Repository navigation
[AI-2095] Render SessionStart coordination-notices terminal-delivery lane - #609
Conversation
…lane
Client half of the kcap-server U3 coordination-notices lane (server shipped
first, capability-gated and inert until this lands). On a live Claude/generic
SessionStart the CLI now:
- advertises the `coordination_notices: "v1"` capability on the POST body, only
on the live path (injected after the ordering-guard/backlog return, so a
spooled body never carries it; `kcap import` uses the vendor routes with
origin=historical and never reaches here);
- renders a returned `coordination_notices: [{text}]` list into the SessionStart
additionalContext envelope, next to the team-memory index, via a new
CoordinationNoticesEmitter (modelled on SessionGuidelinesEmitter);
- honours a new `disable_coordination_notices` profile opt-out that mirrors
`disable_memory_index` (Profile field, ConfigCommand set arm, help-config),
read from the effective profile so it holds for KCAP_URL users too; when set,
the capability is not sent at all (notices stay in the bell / Slack).
Additive and fail-open throughout: any parse/build error leaves the hook
unaffected. AOT-safe (JsonNode + the existing source-gen Profile context; no
reflection). Unit + integration tests cover capability send/omit, render,
opt-out suppression and malformed-field fail-open.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
PR Summary by QodoRender coordination notices in SessionStart context
AI Description
Diagram
High-Level Assessment
Files changed (9)
|
Code Review by Qodo
1. README omits new config key
|
- Fix (qodo #4): the coordination-notices capability was injected into `body`, which the transient-POST-failure path also spools — so a replay would make the server mark notices delivered that the replay never renders. Inject into a separate POST-only `postBody`; the spool now keeps the capability-free `body`. New test pins that a spooled session-start body carries no coordination_notices while the live POST still advertises it. - Docs (qodo #1, #5): add a README SessionStart coordination-notices bullet (opt-out + capability-gating) next to the sibling injections, and list disable_coordination_notices in `kcap config set` usage. - Comments (qodo #2): trim narration in ProfileConfig / ClaudeHookCommand / CoordinationNoticesEmitter, keeping the non-obvious rationale. - qodo #3 (move emitter under Harness/Claude/): declined — every sibling SessionStart-fragment emitter lives at src/Capacitor.Cli/ root. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (kiro nit) string.IsNullOrWhiteSpace is [NotNullWhen(false)], so after the guard the compiler already flows `text` as non-null — the `!` was redundant. Still builds warning-free without it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
Client (kcap-cli) half of the kcap-server U3 coordination-notices terminal-delivery lane (part of AI-2008). The server side already shipped (kcap-server PR #1494, merged) and is capability-gated + completely inert until this CLI change ships — server-before-CLI, so this is safe to merge on its own schedule.
On a live Claude/generic
SessionStart, the CLI now:coordination_notices: "v1"onto the/hooks/session-startPOST body. It is added only on the live path — after the ordering-guard/backlog return (so a spooled body never carries the promise, matching the memory-index "don't pay for what a replay won't use" rule).kcap importposts the/hooks/session-start/{vendor}routes withorigin=historicaland never reaches this code; the server also refuses a historical origin.coordination_notices: [{ "text": … }, …](a bounded list, optionally with a+N more in the notification centretail entry), the CLI injects them under a## Coordination noticesheading into the SessionStartadditionalContextenvelope, right next to the team-memory index. NewCoordinationNoticesEmitter, modelled onSessionGuidelinesEmitter(reads the hook response, returns plain text, fail-open → null).disable_coordination_noticesprofile setting that mirrorsdisable_memory_index(newProfilefield +ConfigCommandset arm +help-config.txt). When set, the capability is not sent at all, so the server does nothing and the notices stay in the notification centre / Slack. Read from the effective profile so it is honoured forKCAP_URL/--server-urlusers too (matching the sibling guidelines opt-out and every non-Claude harness's memory read).Safety
coordination_noticesfield renders nothing and never fails the hook.JsonNode+ the existing source-generatedProfilecontext; no reflection. Builds warning-free withPublishAot/IsAotCompatibleanalyzers on..cssources (scripts/check-linear-ids.shclean).Tests
CoordinationNoticesEmitterTests(unit) — render, grouping, opt-out, empty/missing/malformed (string-not-array, number-text) fail-open, not-a-JSON-envelope.ClaudeHookCommandTests(unit) — capability sent by default, omitted when disabled, response rendered, opt-out suppresses both capability and render (non-vacuous control), malformed field doesn't fail the hook.SessionStartCoordinationNoticesTests(integration, WireMock) — end-to-end advertise+render, opt-out suppression, malformed fail-open.ConfigCommandTests(unit) —disable_coordination_noticesset true/false/invalid.All targeted unit classes pass locally (macOS); the integration class is Linux-gated (assembly
RunOn(OS.Linux), like the siblingClaudeHookStdoutTests) and was verified locally under a temporary gate widen (reverted).🤖 Generated with Claude Code