docs: record capacity enforcement checkpoint

This commit is contained in:
2026-06-05 06:58:14 +08:00
parent a98dc2ca42
commit be9c8f1f93
+28 -15
View File
@@ -16,9 +16,9 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc
## Current Baseline
- Latest implementation checkpoint: `f08c743 test(search): add meilisearch live gate`.
- Latest documentation checkpoint before this update: `f36512e docs: record search payload checkpoint`.
- Worktree status at this planning checkpoint: clean after the matching implementation checkpoint; next active slice is either running the optional live Meilisearch gate or starting B7 SLA/assignment capacity.
- Latest implementation checkpoint: `a98dc2c feat(capacity): enforce inbox assignment limits`.
- Latest documentation checkpoint before this update: `5033cf1 docs: record search live gate checkpoint`.
- Worktree status at this planning checkpoint: clean after the matching implementation checkpoint; next active slice is B7 SLA policy CRUD and applied-SLA persistence.
- `go test ./...` passes.
- Route dump succeeds with `TOTAL: 829` after adding Chatwoot-compatible nested AgentCapacityPolicy users and inbox-limit routes.
- Route parity artifacts now exist under `docs/parity/` and are generated by `cmd/route_parity`.
@@ -35,7 +35,7 @@ Build GoChat as a Go backend that can directly reuse the frontend from `referenc
| 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 | excluded SSO family must stay out of scope; all other enterprise features remain included |
| Phase 4 | Enterprise feature completion | Doing | B7 assignment capacity enforcement has started; SLA policy and applied-SLA lifecycle remain active gaps |
| 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 |
@@ -95,19 +95,21 @@ This ledger records the committed parity checkpoints that future slices should b
| `f04a03b feat(channels): align channel route inbox payloads` | Completed the final B5.5 channel route-response cleanup: Email, Twilio SMS, and LINE dedicated channel create/get/update/list routes now return Chatwoot frontend-compatible raw inbox payloads or `{ payload: [...] }` lists instead of `{ channel }`, `{ channels }`, `{ channel, inbox }`, or success-message envelopes. Delete routes now return empty `200 OK`, and channel creation binds the dedicated channel ID/config back onto the inbox before serialization. | `go test ./internal/handler/api/v1 -run 'Test(Email\|Twilio\|LINE)Channel' -count=1`; `go test ./internal/service -run Inbox -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | B5 inbox/channel API parity moves to Done for current frontend-critical scope; next active slice is B6 Meilisearch live-shape review. |
| `a16c23c feat(search): align chatwoot search payloads` | Advanced B6 search live-shape parity: global and entity search endpoints now return Chatwoot frontend `payload` envelopes, search filters accept Chatwoot `since`, `until`, and `from=contact:id/agent:id` params, message sender IDs are indexed/filterable in Meilisearch and DB fallback, SearchController default page size is aligned to 15, and Meilisearch hit maps are serialized into frontend message/contact/conversation/article shapes. | `go test ./internal/handler/api/v1 -run SearchHandler -count=1`; `go test ./internal/search -run 'SearchFilter\|Meili\|Engine' -count=1`; `go test ./internal/repository -run Search -count=1`; `go test ./internal/handler/api/v1 -count=1`; `go test ./internal/search -count=1`; `go test ./internal/repository -count=1`; sandboxed `go test ./...` failed on local socket restrictions; escalated `go test ./...` passed; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | B6.1 and B6.3 are Done; B6.2 is in Review with mocked Meilisearch coverage. Continue B6.4 live integration gate and B6.5 DB-fallback hardening before moving B6 to Review/Done. |
| `f08c743 test(search): add meilisearch live gate` | Completed B6 gate/hardening work: added an env-gated live Meilisearch test that bootstraps isolated indexes, indexes message/contact documents, verifies account-scoped searches and sender filters, rejects `search.engine=db` in release mode, and prevents `cmd/reindex_search` from silently running against the DB fallback. | `go test ./internal/config -count=1`; `go test ./cmd/reindex_search -count=1`; `go test ./internal/search -count=1`; `go test ./...`; `git diff --check`. Live gate is skipped unless `GOCHAT_LIVE_MEILI_HOST` is set. No route changes; route dump remains `TOTAL: 829`. | B6 moves to Review. Optional next verification is running `GOCHAT_LIVE_MEILI_HOST=http://localhost:7700 GOCHAT_LIVE_MEILI_API_KEY=... go test ./internal/search -run TestLiveMeiliSearchEngineIndexesAndSearchesChatwootShapes -count=1`; otherwise continue B7 SLA/assignment capacity. |
| `a98dc2c feat(capacity): enforce inbox assignment limits` | Started B7 assignment/capacity enforcement: manual assignment, team assignment with explicit agent, team overflow fallback, and auto-assignment now respect `account_users.agent_capacity_policy_id` plus matching `inbox_capacity_limits.conversation_limit`; only open conversations in the same inbox count toward capacity and resolved conversations do not block new assignments. | `go test ./internal/service -run 'AssignAgent\|AssignTeam' -count=1`; `go test ./internal/autoassignment -run Capacity -count=1` escalated after sandbox socket denial; `go test ./internal/service -count=1`; `go test ./internal/autoassignment -count=1` escalated after sandbox socket denial; `go test ./internal/handler/api/v1 -run 'Conversation\|AgentCapacity' -count=1`; escalated `go test ./...`; `git diff --check`. No route changes; route dump remains `TOTAL: 829`. | Continue B7 with SLA policy CRUD payload review, applied-SLA persistence for new conversations, and timer/breach lifecycle tests. |
## 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. B3 and B4 remain in review for deeper side effects and browser validation.
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 has started with `a98dc2c`, enforcing per-inbox capacity limits in manual and automatic assignment paths. B3 and B4 remain in review for deeper side effects and browser validation.
Next implementation slice: start B7 SLA/assignment capacity unless an operator wants to run the optional live Meilisearch gate first.
Next implementation slice: continue B7 with SLA policy CRUD payload review and applied-SLA persistence for new conversations.
| Step | Required result | Reference source | Verification |
| --- | --- | --- | --- |
| N1 | Keep B6 search payload/gate checkpoint as the current baseline. | `SearchController`, `search.js`, `conversationSearch.js`, search Jbuilder views, `cmd/reindex_search`. | Done by `a16c23c` and `f08c743`; response envelopes, Chatwoot params, sender filters, mocked hit shape, live gate, and fallback guards are covered. |
| N2 | Optionally run the live Meilisearch gate when a local Meilisearch instance is available. | `internal/search/engine_meili_live_test.go`. | `GOCHAT_LIVE_MEILI_HOST=http://localhost:7700 GOCHAT_LIVE_MEILI_API_KEY=... go test ./internal/search -run TestLiveMeiliSearchEngineIndexesAndSearchesChatwootShapes -count=1`. |
| N3 | Start B7 SLA and assignment capacity. | Chatwoot enterprise SLA policies, assignment policies, capacity enforcement. | First B7 checkpoint must include routes/data/service tests before deeper timer/report behavior. |
| N4 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. |
| N3 | Keep B7 assignment capacity enforcement as current capacity baseline. | `AssignmentHandler`, `AutoAssignmentHandler`, `AgentCapacityPolicy`, `InboxCapacityLimit`. | Done by `a98dc2c`; manual/team/auto assignment now skip or reject agents at per-inbox capacity. |
| N4 | Add SLA policy CRUD payload review and applied-SLA persistence. | `reference/chatwoot/app/controllers/api/v1/accounts/sla_policies_controller.rb`, `applied_slas` routes/services. | New conversations receive the right policy and store an applied SLA record; frontend SLA settings payloads match reference serializers. |
| N5 | Update this tracker after every implementation checkpoint. | This document. | `git diff --check`; `go test ./...` for Go changes. |
Current B2 profile checkpoint:
@@ -285,6 +287,16 @@ B6 current checkpoint:
- The optional live gate is implemented but not run by default. It requires `GOCHAT_LIVE_MEILI_HOST` and validates isolated live indexes, account-scoped contact/message search, and message sender filters.
- DB fallback remains available for explicit local development, but release config validation and `reindex_search` now refuse it so production parity stays Meilisearch-first.
Active B7 task board:
| ID | Task | Reference source | Status | Exit gate |
| --- | --- | --- | --- | --- |
| B7.1 | Enforce AgentCapacityPolicy/InboxCapacityLimit during manual and automatic assignment. | `AssignmentHandler`, `AutoAssignmentHandler`, `AgentAssignmentService`, `AgentCapacityPolicy`, `InboxCapacityLimit`. | Done | `a98dc2c`; manual assignment, team assignment, overflow fallback, and auto-assignment respect per-inbox open-conversation limits. |
| B7.2 | Align SLA policy CRUD payloads and validation. | `sla_policies_controller.rb`, SLA frontend store/API, SLA policy views/entities. | Todo | Handler tests prove Chatwoot request wrapper, raw payload/list shapes, validation, and delete status. |
| B7.3 | Persist applied SLA on new/open conversations. | Chatwoot applied SLA model/services and conversation create/status callbacks. | Todo | Conversation creation and status changes select the right SLA policy and create/update applied SLA state. |
| B7.4 | Implement SLA timer and breach lifecycle. | First response, next response, resolution targets, business-hours behavior, SLA events/notifications. | Todo | Lifecycle tests cover active, hit, missed, and active-with-misses transitions with idempotent breach events. |
| B7.5 | Add SLA report/list payload parity. | `applied_slas` routes, SLA report frontend. | Todo | Account-side applied-SLA list/report filters return frontend-compatible payloads. |
Active B4 task board:
| ID | Task | Reference source | Status | Exit gate |
@@ -315,7 +327,7 @@ This is the ordered queue for the next implementation slices. Do not skip the ro
| Q4 | Start serializer parity fixtures for auth/session, conversations/messages, contacts/companies, inboxes, notifications, and search. | Phase 3 | Each area has at least one reference fixture and Go response test. | Doing |
| Q5 | Review Meilisearch document shape and endpoint payloads against Chatwoot frontend consumers. | Phase 1 and Phase 3 | Search remains Meilisearch-first and payload mismatches are fixed or tracked. | Review |
| Q6 | Implement provider-specific webhook ingress for Chatwoot public webhook paths. | Phase 6 | Generic webhook placeholder no longer masks provider gaps; Telegram, LINE, SMS/Twilio, WhatsApp, Instagram/Twitter/TikTok routes resolve and verify like Chatwoot where supported. | Done |
| Q7 | Burn down enterprise gaps in this order: SLA, assignment policy and capacity, CSAT, automation/macros, Audit, CustomRole, InboxLimit, Captain/Copilot. | Phase 4 and Phase 5 | Each feature passes route, persistence, auth, side-effect, response, and test checks. | Todo |
| Q7 | Burn down enterprise gaps in this order: SLA, assignment policy and capacity, CSAT, automation/macros, Audit, CustomRole, InboxLimit, Captain/Copilot. | Phase 4 and Phase 5 | Each feature passes route, persistence, auth, side-effect, response, and test checks. | Doing |
| Q8 | Add frontend smoke harness using the reused Chatwoot frontend once core API flows boot end-to-end. | Phase 7 | Login, inbox list, conversation list/detail, message send, contact view, and widget init run without frontend adapters. | Todo |
## Current Decision Ledger
@@ -341,7 +353,7 @@ These milestones are the tracking spine for the remaining Chatwoot frontend reus
| M2 | Route parity expansion for frontend-critical routes. | Tracked route set has zero missing routes and every new route group has router boot coverage. | Doing |
| M3 | Serializer parity for frontend API families. | Fixture tests cover auth/profile, accounts/users, inboxes, conversations/messages, contacts/companies, notifications, reports, widget/public, and search. | Doing |
| M4 | Core handler placeholder burn-down. | Account/contact/conversation/message/inbox/webhook handlers are repository-backed and no frontend-critical route returns placeholder JSON. | Doing |
| M5 | Paid feature parity excluding SSO family. | SLA, Audit, CustomRole, AgentCapacity, Captain/Copilot, CSAT, InboxLimit, automation/macros, and assignment policies pass route, persistence, auth, side-effect, serializer, and tests. | Todo |
| M5 | Paid feature parity excluding SSO family. | SLA, Audit, CustomRole, AgentCapacity, Captain/Copilot, CSAT, InboxLimit, automation/macros, and assignment policies pass route, persistence, auth, side-effect, serializer, and tests. | Doing |
| M6 | Durable jobs and external integrations. | Search indexing, CSAT send, automation actions, notifications, webhooks, and external deliveries are queued, retryable, logged, and idempotent. | Todo |
| M7 | Reused Chatwoot frontend smoke validation. | Chatwoot frontend boots against GoChat for login, inbox list, conversation detail, message send, contact view, widget init/message, public CSAT, and key enterprise screens without adapters. | Todo |
@@ -357,7 +369,7 @@ Work proceeds top-down unless a failing test or frontend blocker forces a narrow
| B4 | Contact/company behavior fixtures. | Chatwoot contact/company controllers, merge/import/export/notes/labels. | Fixture tests for CRUD/search/merge/relation/import-export shells and frontend CRM API smoke. | Review |
| 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. | Todo |
| B7 | SLA and assignment capacity. | Chatwoot enterprise SLA and assignment policy behavior. | Unit/integration tests for SLA state, breach, assignment capacity, policy selection. | Doing |
| B8 | CSAT account-side completion. | Chatwoot CSAT survey responses, reports, downloads, listeners. | Metrics/list/review/download/send idempotency tests. | Todo |
| 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 |
@@ -648,25 +660,25 @@ Enterprise tracking table:
| P4.1 | SLA policies/events | `internal/model/sla_policy.go`, `internal/model/sla_event.go`, `internal/service/sla_policy_service.go`, `internal/service/sla_event_service.go`, `internal/handler/api/v1/sla_policy_handler.go` | Compare with Chatwoot SLA behavior; wire lifecycle events and breach tracking; add frontend payload tests. | Todo |
| P4.2 | Audit logs | `internal/model/audit.go`, `internal/service/audit_service.go`, `internal/repository/audit_repo.go`, `internal/handler/api/v1/audit_handler.go` | Ensure every mutating enterprise/core action emits audit events and filters match Chatwoot. | Todo |
| P4.3 | Custom roles/permissions | `internal/model/custom_role.go`, `internal/service/custom_role_service.go`, `internal/middleware/role_check.go`, `internal/handler/api/v1/custom_role_handler.go` | Align permission keys, inherited roles, authorization failures, and admin UX payloads. | Todo |
| 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. | Doing |
| 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` | Implement Chatwoot enterprise nested `agent_capacity_policies/:policy_id/inbox_limits` first; keep legacy `/inboxes/:inbox_id/inbox_limits` from blocking but do not treat it as parity. | Doing |
| 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` | Wire resolve-triggered survey send, public update flow, metrics, review notes, filters. | Todo |
| 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. | Todo |
| P4.9 | Assignment policies | `internal/autoassignment/*`, `internal/automation/agent_bot_rule_listener.go` | Match Chatwoot assignment policy behavior and availability/capacity rules. | Doing |
Enterprise work package breakdown:
| Package | Subtasks | Must verify | Status |
| --- | --- | --- | --- |
| SLA | Policy CRUD parity, conversation SLA assignment, first-response/next-response/resolution timers, business-hours handling, breach events, notifications. | Field/unit parity, timer state transitions, breach idempotency, report payloads. | Todo |
| 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. | Doing |
| 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 side | Survey send on resolve, response list, metrics, filters, downloads, review notes, resend/idempotency, public lock already implemented. | Account API payload fixtures, metrics math, 14-day lock, one response per CSAT message. | Todo |
| 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 |
| Custom roles | Permission-key parity, account-user role resolution, policy middleware, create/update/delete behavior. | Permission matrix tests and frontend admin payload fixtures. | Todo |
| Inbox limits | Account/inbox limit models, create/update enforcement, UI-readable limit responses, admin overrides. | Chatwoot nested capacity-limit API is done; assignment-path enforcement and any account-level create limits remain for B7/P4.4. | Review |
| Inbox limits | Account/inbox limit models, create/update enforcement, UI-readable limit responses, admin overrides. | Chatwoot nested capacity-limit API and assignment-path enforcement are covered; account-level create limits remain for later review. | Review |
| Captain/Copilot | Assistants, inbox bindings, scenarios, responses, documents, tools, copilot threads/messages, tasks, streaming/tool-call behavior. | Route fixtures, persistence tests, feature gates for external LLM dependencies, frontend smoke screens. | Todo |
Enterprise acceptance gates:
@@ -877,3 +889,4 @@ Verification milestone gates:
- 2026-06-05: B5.5 final channel route-response cleanup committed as `f04a03b feat(channels): align channel route inbox payloads`; Email, Twilio SMS, and LINE dedicated channel routes now return raw Chatwoot-compatible inbox payloads for create/get/update, `{ payload: [...] }` for list, and empty `200 OK` for delete. `InboxService.BindChannel` keeps dedicated channel IDs/configs reflected in inbox serialization. Focused Email/Twilio/LINE channel tests, service inbox tests, handler package tests, full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 829`; next active slice is B6 Meilisearch live-shape review.
- 2026-06-05: B6 search payload checkpoint committed as `a16c23c feat(search): align chatwoot search payloads`; global/entity search endpoints now return Chatwoot `payload` envelopes, default to 15 results, accept `since`/`until`/`from` frontend params, serialize model and Meilisearch-hit data into frontend-ready conversation/contact/message/article shapes, and filter message sender IDs through both Meilisearch and DB fallback. Focused handler/search/repository tests, handler/search/repository package tests, escalated full `go test ./...`, and `git diff --check` passed. Route dump unchanged at `TOTAL: 829`; B6.4 live Meilisearch gate and B6.5 fallback hardening remain active.
- 2026-06-05: B6 live-gate/fallback checkpoint committed as `f08c743 test(search): add meilisearch live gate`; added an env-gated live Meilisearch test for isolated bootstrap/index/search of contacts and messages, kept it skipped unless `GOCHAT_LIVE_MEILI_HOST` is set, rejected `search.engine=db` in release validation, and made `cmd/reindex_search` fail fast if configured with DB fallback. Focused config/reindex/search tests, full `go test ./...`, and `git diff --check` passed. B6 moves to Review; next implementation slice is B7 SLA/assignment capacity unless the optional live gate is explicitly run first.
- 2026-06-05: B7 capacity enforcement checkpoint committed as `a98dc2c feat(capacity): enforce inbox assignment limits`; manual agent assignment, team assignment with explicit agent, team overflow fallback, and auto-assignment now apply AgentCapacityPolicy/InboxCapacityLimit per-inbox open-conversation limits. Resolved conversations do not count against capacity. Focused assignment/capacity tests, handler smoke for conversation/capacity routes, escalated full `go test ./...`, and `git diff --check` passed. Next B7 checkpoint is SLA policy CRUD payload review and applied-SLA persistence.