diff --git a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md index 4c49e9d4..9467958b 100644 --- a/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md +++ b/docs/CHATWOOT_PARITY_DEVELOPMENT_PLAN.md @@ -16,9 +16,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc ## Current Baseline -- Latest implementation checkpoint: `ef3a909 feat(csat): send surveys on resolved conversations`. -- Latest documentation checkpoint before this update: `bda5ad5 docs: expand parity execution plan`. -- Worktree status at this planning checkpoint: clean after `ef3a909`; next active slice is B8 CSAT download/export parity. +- Latest implementation checkpoint: `b36cf07 feat(csat): align report download csv`. +- Latest documentation checkpoint before this update: `5c771d8 docs: record csat public send checkpoint`. +- Worktree status at this planning checkpoint: clean after `b36cf07`; next active slice is B9 automation and macro side-effect parity. - `go test ./...` passes. - Route dump succeeds with `TOTAL: 830` after adding the Chatwoot-compatible applied-SLA index route. - Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`. @@ -45,7 +45,7 @@ Next ordered checkpoints: | Order | Slice | Required outcome | Primary verification | | --- | --- | --- | --- | | 1 | B7.6 | SLA business-hours timer math and breach notification fan-out match Chatwoot or have named job-boundary gaps. | Focused applied-SLA lifecycle/notification tests, service package tests, `go test ./...`. | -| 2 | B8 | CSAT account/public/report/send behavior matches Chatwoot enough for the reused frontend. | CSAT handler/service/listener tests, public update tests, CSV/report tests. | +| 2 | B8 | CSAT account/public/report/send behavior matches Chatwoot enough for the reused frontend. | Done by `b36cf07`; B8 moves to Review with WhatsApp/Twilio external template dispatch tracked under Phase 5. | | 3 | B9 | Automation rules and macros mutate conversations through real side effects with execution logs. | Rule listener tests, macro execute tests, action-service retry/log tests. | | 4 | B10 | Audit, CustomRole, and remaining InboxLimit surfaces enforce Chatwoot admin behavior. | Permission matrix tests, audit writer/list tests, inbox/account limit tests. | | 5 | B11 | Captain/Copilot enterprise screens have real persistence and safe LLM feature gates. | Captain/Copilot handler/service fixtures and disabled-state tests. | @@ -59,7 +59,7 @@ Next ordered checkpoints: | Phase 1 | Meilisearch search engine | Review | B6 payload parity, optional live gate, and DB-fallback hardening are implemented; an actual live Meilisearch run is optional and environment-dependent | | Phase 2 | Route and controller parity audit | Doing | Ruby/Bundler unavailable, so Chatwoot route extraction currently uses static `routes.rb` fallback | | Phase 3 | Data and serializer parity | Doing | JSON fixture coverage is partial and still endpoint-family based | -| Phase 4 | Enterprise feature completion | Doing | B7 is in Review; B8 account-side CSAT report payloads, public submit/update idempotency, and resolve-triggered survey send are implemented; CSAT download depth remains active | +| Phase 4 | Enterprise feature completion | Doing | B7 and B8 are in Review; B9 automation/macros are active next | | Phase 5 | Background jobs and integrations | Planned | durable worker choice and job parity are open | | Phase 6 | Core placeholder burn-down | Doing | account/contact/conversation/message/inbox placeholder groups remain broad | | Phase 7 | Verification harness | Planned | search live gate and reused-frontend smoke harness are not complete | @@ -132,12 +132,14 @@ This ledger records the committed parity checkpoints that future slices should b | `fca6838 docs: record csat report checkpoint` | Recorded the B8.1 CSAT report checkpoint in this tracker, moved B8 to Doing, and made B8.2/B8.3 the next active implementation slices. | Documentation-only checkpoint after `f441680`; `git diff --check` passed before commit. | Continue B8 public CSAT submit/update, resolve-triggered survey send idempotency, and CSAT download parity. | | `bda5ad5 docs: expand parity execution plan` | Expanded this tracker with executable B8-B12 breakdowns for CSAT, automation/macros, audit/custom roles/limits, Captain/Copilot, and reused frontend smoke validation. | Documentation-only checkpoint; `git diff --check HEAD~1 HEAD` passed. | Continue B8 public CSAT submit/update and resolve-triggered send parity. | | `ef3a909 feat(csat): send surveys on resolved conversations` | Advanced B8 public/send parity: public CSAT update now accepts the reused survey frontend's object-shaped `message.submitted_values`, keeps one message-linked CSAT response per `input_csat` message across repeated submissions, and still enforces the 14-day message-date lock. The resolve listener now loads the resolved conversation, checks inbox CSAT enablement and survey label rules, creates one Chatwoot-style `input_csat` template message with inbox CSAT content/display type, and no longer creates pending rating-0 responses before customer submission. Message-updated events also build CSAT responses from `submitted_values` when present. | `go test ./internal/automation -run 'CsatSurveyListener\|CsatSurveyService' -count=1`; `go test ./internal/handler/api/v1 -run CsatSurvey -count=1`; `go test ./internal/handler/widget -run 'CSAT\|Csat\|PublicInbox' -count=1`; `go test ./internal/service -run Widget -count=1`; `go test ./internal/automation -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 830`. | Continue B8.4 CSAT download CSV parity and then review channel-specific WhatsApp/Twilio template send hooks as broader integration work. | +| `5c771d8 docs: record csat public send checkpoint` | Recorded the B8.2/B8.3 public CSAT and resolve-send checkpoint, made B8.4 the next active task, and kept external WhatsApp/Twilio template delivery as a named Phase 5 integration follow-up. | Documentation-only checkpoint after `ef3a909`; `git diff --check` passed before commit. | Continue B8.4 CSAT download CSV parity. | +| `b36cf07 feat(csat): align report download csv` | Completed B8.4 CSAT CSV download parity for the reused reports screen: `/csat_survey_responses/download` now writes real CSV via `encoding/csv`, uses Chatwoot's report headers, includes enterprise review notes, emits absolute conversation links using conversation display IDs, appends the reporting-period row, and applies the same account/date/agent/inbox/team/rating filters as list/metrics. | `go test ./internal/handler/api/v1 -run CsatSurvey -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./internal/automation -count=1`; `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 830`. | B8 moves to Review. Continue B9 automation and macro side-effect parity. | ## Next Slice Contract Completed implementation slice: B5.1-B5.5 now cover inbox serializer shape, Chatwoot frontend create/update binding, working-hours persistence, out-of-office behavior, inbox member assignment payload/mutation semantics, channel-specific config depth, AgentCapacityPolicy/InboxCapacityLimit API data contracts, and dedicated Email/Twilio/LINE channel route response shapes. B6 is now in Review after `a16c23c` and `f08c743`: Chatwoot search response envelopes, frontend query params, Meilisearch sender filters, mocked hit serialization, env-gated live Meilisearch validation, release-mode DB fallback rejection, and reindex Meilisearch-only guard are covered. B7 is now in Review after `a98dc2c`, `95224fa`, `a11bb96`, `213bf2b`, `4f85ef1`, and `d23f3f0`, enforcing per-inbox capacity limits, aligning SLA policy CRUD payloads, persisting applied SLA records, making core FRT/NRT/RT breach events idempotent, aligning the applied-SLA reports API, and creating SLA miss notifications. B3 and B4 remain in review for deeper side effects and browser validation. -Next implementation slice: continue B8 with CSAT download/export parity. +Next implementation slice: start B9 automation and macro side-effect parity. | Step | Required result | Reference source | Verification | | --- | --- | --- | --- | @@ -149,7 +151,7 @@ Next implementation slice: continue B8 with CSAT download/export parity. | N6 | Keep SLA timer and breach lifecycle as current SLA lifecycle baseline. | `reference/chatwoot/enterprise/app/services/sla/evaluate_applied_sla_service.rb`, `SlaEvent`, SLA processing jobs. | Done by `213bf2b`; active applied SLAs create idempotent FRT/NRT/RT miss events, transition to `active_with_misses`, `hit`, or `missed`, and preserve status on repeated evaluation. | | N7 | Keep applied-SLA report/list payload parity as the current SLA reports baseline. | `reference/chatwoot/enterprise/app/controllers/api/v1/accounts/applied_slas_controller.rb`, applied-SLA index/metrics/download views, dashboard SLA reports client. | Done by `4f85ef1`; account-side applied-SLA list/report filters return frontend-compatible payloads and metrics/download shapes. | | N8 | Keep B7.6 SLA notification and timing review as current SLA closeout baseline. | `Sla::EvaluateAppliedSlaService`, `SlaEvent#create_notifications`, SLA processing jobs. | Done by `d23f3f0`; SLA miss notifications fan out to participants/admins/assignee, repeated evaluation is idempotent, and current reference timing for `only_during_business_hours` is covered. | -| N9 | Keep B8.1-B8.3 CSAT account/public/send parity as the current CSAT baseline. | `CsatSurveyResponsesController`, public CSAT controller/views, `CsatSurveyService`, `CsatSurveyListener`, dashboard reports and survey frontend clients. | Done by `f441680` and `ef3a909`; list/metrics/review-note payloads, public object-shaped submission, lock window, response idempotency, and resolve-triggered message send are covered. | +| N9 | Keep B8 CSAT account/public/send/download parity as the current CSAT baseline. | `CsatSurveyResponsesController`, public CSAT controller/views, `CsatSurveyService`, `CsatSurveyListener`, dashboard reports and survey frontend clients. | Done by `f441680`, `ef3a909`, and `b36cf07`; list/metrics/review-note payloads, public object-shaped submission, lock window, response idempotency, resolve-triggered message send, and CSV download are covered. | | N10 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. | Current B2 profile checkpoint: @@ -370,7 +372,7 @@ Upcoming enterprise task boards: | B8 | B8.1 | Align account-side CSAT survey response index, metrics, filters, pagination, and review-note payloads. | `reference/chatwoot/app/controllers/api/v1/accounts/csat_survey_responses_controller.rb`, CSAT Jbuilder/views, dashboard reports clients. | Handler/service tests for list, metrics, review note update, date/agent/inbox/team/rating filters. | Done by `f441680` | | B8 | B8.2 | Align public CSAT submit/update flow, lock window, message linkage, and one-response-per-message behavior. | `reference/chatwoot/app/controllers/public/api/v1/csat_survey_controller.rb`, CSAT response model/services. | Public handler tests for submit, update within window, update after lock, duplicate response rejection. | Done by `ef3a909` | | B8 | B8.3 | Wire resolve-triggered CSAT survey send with idempotency and channel-aware message creation. | Chatwoot CSAT listener/jobs and inbox CSAT settings. | Listener tests prove one CSAT message per resolved conversation and no send when disabled. | Done by `ef3a909`; WhatsApp/Twilio external template dispatch remains integration follow-up | -| B8 | B8.4 | Align CSAT downloads/export payloads used by reports screens. | CSAT report/download controller paths and frontend report API. | CSV/download tests cover filters and frontend column names. | Todo | +| B8 | B8.4 | Align CSAT downloads/export payloads used by reports screens. | CSAT report/download controller paths and frontend report API. | CSV/download tests cover filters and frontend column names. | Done by `b36cf07` | | B9 | B9.1 | Align automation rule CRUD payloads, validation, condition groups, and event names. | Chatwoot automation rule controllers/models and dashboard automation builder. | Handler/service tests for create/update/list/show/delete and validation errors. | Todo | | B9 | B9.2 | Implement automation action side effects for labels, status, priority, assignee/team, custom attributes, private notes, and snooze. | Chatwoot automation action services/listeners. | Rule listener tests assert real conversation mutations and stop-on-match behavior. | Todo | | B9 | B9.3 | Add durable execution logs, delayed actions, webhook delivery, and email transcript delivery boundaries. | Chatwoot automation jobs and webhook/transcript actions. | Worker/action tests cover retryable external actions and observable failures. | Todo | @@ -393,7 +395,7 @@ B8 CSAT execution breakdown: | B8.2c | Make response creation/update message-linked and idempotent: one CSAT response per `input_csat` message, updates mutate the existing response, and repeated public submissions do not create extra rows. | `CsatSurveys::ResponseBuilder`, `Message#csat_survey_response`, CSAT response model uniqueness. | Service tests count rows after repeated public updates and assert message/contact/conversation/assignee linkage. | Done by `ef3a909` | | B8.3a | Replace the current resolve listener placeholder with Chatwoot-style survey message creation when an inbox has CSAT enabled. | `reference/chatwoot/app/listeners/csat_survey_listener.rb`, `CsatSurveyService`, inbox CSAT settings. | Listener tests prove disabled inboxes do not send, enabled inboxes create exactly one `input_csat` outgoing message, and repeated resolve events are idempotent. | Done by `ef3a909` | | B8.3b | Keep CSAT survey sending inside a clear job boundary even if execution remains synchronous for now, so durable worker migration can happen under Phase 5 without changing behavior. | Chatwoot listener/job boundary and existing Go channel dispatcher. | Unit tests cover enqueue/perform boundary or documented synchronous fallback with idempotency. | Review; `ef3a909` isolates behavior in `SendSurveyForConversation`, durable queue remains Phase 5 | -| B8.4a | Align CSAT download CSV with Chatwoot report filters and columns, including review notes where the frontend exposes them. | CSAT report/download controller, dashboard reports API. | CSV tests cover date, agent, inbox, team, rating filters and expected column names/order. | Todo | +| B8.4a | Align CSAT download CSV with Chatwoot report filters and columns, including review notes where the frontend exposes them. | CSAT report/download controller, dashboard reports API. | CSV tests cover date, agent, inbox, team, rating filters and expected column names/order. | Done by `b36cf07` | B9 automation and macro execution breakdown: @@ -518,7 +520,7 @@ Work proceeds top-down unless a failing test or frontend blocker forces a narrow | B5 | Inbox/channel behavior fixtures. | Chatwoot inbox/channel controllers and channel models. | Fixture tests for inbox CRUD, settings, business hours, members, avatar, channel config. | Done | | B6 | Meilisearch live-shape review. | Chatwoot frontend search usage and search controllers. | Meilisearch-backed response fixtures plus optional live integration gate. | Review | | B7 | SLA and assignment capacity. | Chatwoot enterprise SLA and assignment policy behavior. | Unit/integration tests for SLA state, breach, assignment capacity, policy selection. | Review | -| B8 | CSAT account/public/send completion. | Chatwoot CSAT survey responses, reports, downloads, listeners. | Metrics/list/review/download/send idempotency tests. | Doing | +| B8 | CSAT account/public/send completion. | Chatwoot CSAT survey responses, reports, downloads, listeners. | Metrics/list/review/download/send idempotency tests. | Review | | B9 | Automation/macros durable side effects. | Chatwoot automation/macro services and jobs. | Action execution, logs, webhook/email transcript retry tests. | Todo | | B10 | Audit, CustomRole, InboxLimit. | Chatwoot enterprise admin behavior and policies. | Authorization, audit emission, limits enforcement, admin payload fixtures. | Todo | | B11 | Captain/Copilot deep behavior. | Chatwoot Captain/Copilot controllers, services, frontend clients. | Assistant/tool/document/scenario/copilot thread/task tests and feature gates. | Todo | @@ -530,7 +532,7 @@ Remaining slice landing plan: | --- | --- | --- | --- | | B6 | Compare current Meilisearch document fields and global/entity search payloads against Chatwoot frontend consumers. | Add optional live Meilisearch integration gate, tighten account-scoped filters, and document DB fallback as development-only. | Done only after mocked tests and live-shape review prove search payloads work without DB-only assumptions. | | B7 | Done: assignment capacity enforcement, SLA policy CRUD payload parity, applied-SLA conversation persistence, idempotent SLA breach lifecycle, applied-SLA report/list payload parity, and SLA miss notification fan-out. | Capacity-aware assignment policy binding review and durable SLA job scheduling remain broader Phase 5/P4.9 follow-ups, not hidden SLA gaps. | Review after `d23f3f0`; move to Done only after assignment policy binding review is either implemented or split out of B7. | -| B8 | Done: account-side CSAT list/metrics/review-note payload parity, public submit/update idempotency, and resolve-triggered generic survey message send. | CSAT download filters/CSV and channel-specific WhatsApp/Twilio template send hooks. | Done only after account APIs, public APIs, resolve listener, and CSV/download behavior agree on one message-linked response model; external template dispatch may remain a named Phase 5 integration gap. | +| B8 | Done: account-side CSAT list/metrics/review-note payload parity, public submit/update idempotency, resolve-triggered generic survey message send, and CSV download filters/columns. | Channel-specific WhatsApp/Twilio template send hooks and durable queueing are tracked under Phase 5. | Review after `b36cf07`; move to Done only after channel-specific send hooks are either implemented or formally split as external integration scope. | | B9 | Macro execution side effects for labels, status, assignee/team, priority, private notes, and attachments. | Automation rule condition/action parity, execution logs, webhook delivery retry, email transcript delivery, stop-on-match semantics. | Done only after actions mutate real conversations and async external actions are retryable or explicitly feature-gated. | | B10 | Audit list payload and audit writer boundary for representative mutating core resources. | CustomRole permission-key parity, AccountUser permission resolution, InboxLimit enforcement in inbox/channel creation paths. | Done only after authorization tests prove admin-only surfaces and non-admin denial shapes match Chatwoot. | | B11 | Captain Assistant CRUD, inbox binding, responses, documents, and custom tools payload fixtures. | Copilot threads/messages/tasks, playground/tool-call behavior, document sync/embedding feature gates, streaming fallback. | Done only after LLM-dependent behavior is either implemented behind config or safely stubbed with frontend-compatible disabled states. | @@ -812,7 +814,7 @@ Enterprise tracking table: | P4.4 | Agent capacity | `internal/model/agent_capacity_policy.go`, `internal/service/agent_capacity_policy_service.go`, `internal/handler/api/v1/agent_capacity_handler.go`, `internal/autoassignment/*` | First align AgentCapacityPolicy API, serializers, nested users, and `InboxCapacityLimit` data contracts; then enforce capacity in manual/auto assignment. | Review | | P4.5 | Inbox limits | `internal/model/agent_capacity_policy.go` `InboxCapacityLimit`, plus legacy `internal/model/inbox_limit.go`, `internal/service/inbox_limit_service.go`, `internal/repository/inbox_limit_repo.go`, `internal/handler/api/v1/inbox_limit_handler.go` | Chatwoot enterprise nested `agent_capacity_policies/:policy_id/inbox_limits` and assignment-path enforcement are covered; next review account-level create-limit behavior and legacy route compatibility. | Review | | P4.6 | Captain/Copilot | `internal/model/captain_models.go`, `internal/model/copilot_models.go`, `internal/service/captain_*`, `internal/service/copilot_*`, `internal/handler/api/v1/captain_*`, `internal/handler/api/v1/copilot_handler.go` | Complete assistant, tools, scenarios, documents, responses, inbox bindings, suggestions, and streaming compatibility. | Todo | -| P4.7 | CSAT | `internal/csat/*`, `internal/automation/csat_survey_*`, `internal/handler/api/v1/csat_*`, `internal/service/csat_metrics_service.go` | Account-side list/metrics/review-note payloads, public update depth, and resolve-triggered generic survey send are aligned; next wire download CSV parity and channel-specific template send hooks. | Doing | +| P4.7 | CSAT | `internal/csat/*`, `internal/automation/csat_survey_*`, `internal/handler/api/v1/csat_*`, `internal/service/csat_metrics_service.go` | Account-side list/metrics/review-note payloads, public update depth, resolve-triggered generic survey send, and download CSV are aligned; channel-specific template send hooks remain Phase 5 integration work. | Review | | P4.8 | Automation and macros | `internal/automation/*`, `internal/handler/api/v1/automation_rule_handler.go`, `internal/handler/api/v1/macro_handler.go` | Finish action side effects, execution logs, webhook/email transcript delivery, and rule trigger coverage. | Todo | | P4.9 | Assignment policies | `internal/autoassignment/*`, `internal/automation/agent_bot_rule_listener.go` | Match Chatwoot assignment policy behavior and availability/capacity rules. | Doing | @@ -822,7 +824,7 @@ Enterprise work package breakdown: | --- | --- | --- | --- | | SLA | Policy CRUD parity, conversation SLA assignment, first-response/next-response/resolution timers, business-hours handling, breach events, notifications. | Policy payloads, applied-SLA attach, seconds-based thresholds, core state transitions, breach idempotency, report payloads, reference `only_during_business_hours` timing, and notification fan-out are covered. Durable scheduling remains Phase 5. | Review | | Assignment and capacity | Assignment policy CRUD, inbox policy binding, round-robin/availability/capacity selection, manual assignment limits, fallback behavior. | Manual and automatic assignment respect policy, availability, team/inbox membership, and limits. | Review | -| CSAT account/public side | Survey send on resolve, response list, metrics, filters, downloads, review notes, resend/idempotency, public lock. | Account report list/metrics/review-note payload fixtures are covered by `f441680`; public object-shaped submit/update, one-response-per-message idempotency, and resolve-triggered generic survey message send are covered by `ef3a909`; download CSV remains. | Doing | +| CSAT account/public side | Survey send on resolve, response list, metrics, filters, downloads, review notes, resend/idempotency, public lock. | Account report list/metrics/review-note payload fixtures are covered by `f441680`; public object-shaped submit/update, one-response-per-message idempotency, and resolve-triggered generic survey message send are covered by `ef3a909`; download CSV is covered by `b36cf07`. | Review | | Automation rules | Condition/action parity, event listener coverage, delayed actions, execution logs, stop-on-match behavior, webhook and transcript actions. | Rule trigger tests for conversation/contact/message events and durable retry for external actions. | Todo | | Macros | Macro CRUD, availability by account/user, execute side effects, validation, audit/log output. | Execute changes conversation labels/status/assignee/team/notes/custom attributes exactly as frontend expects. | Todo | | Audit | Audit model parity, mutating action coverage, request metadata, filters/pagination, admin endpoint payloads. | Representative mutations across core and enterprise features emit audit records. | Todo | @@ -1047,3 +1049,4 @@ Verification milestone gates: - 2026-06-05: B7 SLA notification checkpoint committed as `d23f3f0 feat(sla): notify users on sla misses`; new FRT/NRT/RT SLA miss events now create Chatwoot notification types for conversation participants, account administrators, and conversation assignee, with de-duplication and account membership filtering. Repeated evaluation stays idempotent. Current `reference/chatwoot` timing was verified to keep `only_during_business_hours` as a stored policy flag without changing elapsed-second thresholds, and a regression test records that behavior. Focused applied-SLA tests, service package tests, full `go test ./...`, and `git diff --check` passed. B7 moves to Review; next slice is B8 CSAT account/public/report/send parity. - 2026-06-05: B8 account-side CSAT checkpoint committed as `f441680 feat(csat): align account report payloads`; CSAT report list now returns the raw Chatwoot response array with nested contact, assigned agent, reviewer, display conversation ID, Unix timestamps, and message ID. Filters now cover frontend `since/until`, `user_ids`, `inbox_id`, `team_id`, and `rating`; metrics returns `total_count`, `ratings_count`, and `total_sent_messages_count`; update/review-note actions return the Chatwoot CSAT serializer. Focused CSAT handler/service tests, handler and automation package tests, full `go test ./...`, and `git diff --check` passed. Next B8 checkpoint is public CSAT submit/update and resolve-triggered send parity. - 2026-06-05: B8 public/send CSAT checkpoint committed as `ef3a909 feat(csat): send surveys on resolved conversations`; public CSAT update now accepts object-shaped `message.submitted_values`, repeated submissions update the same message-linked response, and the 14-day lock still follows the survey message creation date. The resolve listener now creates one `input_csat` template message only when the resolved conversation's inbox has CSAT enabled and survey rules allow it, skips duplicates, and no longer creates pending rating-0 responses. Message-updated events build responses from submitted CSAT values. Focused CSAT listener/service/handler/widget tests, automation and API handler package tests, full `go test ./...`, and `git diff --check` passed. Next B8 checkpoint is CSAT download CSV parity. +- 2026-06-05: B8 CSAT download checkpoint committed as `b36cf07 feat(csat): align report download csv`; CSAT report downloads now match Chatwoot CSV headers, filters, absolute conversation links by display ID, enterprise review notes, and the final reporting-period row. Focused CSAT handler tests, handler and automation package tests, full `go test ./...`, and `git diff --check` passed. B8 moves to Review; next slice is B9 automation and macro side-effect parity.