Skip to content

[AI-2095] Render SessionStart coordination-notices terminal-delivery lane - #609

Merged
realtonyyoung merged 3 commits into
mainfrom
claude-tyoung/coordination-notices-rendering
Aug 19, 2026
Merged

realtonyyoung merged 3 commits into
mainfrom
claude-tyoung/coordination-notices-rendering

Conversation

@realtonyyoung

Copy link
Copy Markdown
Collaborator

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:

  1. Advertises the capability — injects coordination_notices: "v1" onto the /hooks/session-start POST 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 import posts the /hooks/session-start/{vendor} routes with origin=historical and never reaches this code; the server also refuses a historical origin.
  2. Renders the response — when the server returns coordination_notices: [{ "text": … }, …] (a bounded list, optionally with a +N more in the notification centre tail entry), the CLI injects them under a ## Coordination notices heading into the SessionStart additionalContext envelope, right next to the team-memory index. New CoordinationNoticesEmitter, modelled on SessionGuidelinesEmitter (reads the hook response, returns plain text, fail-open → null).
  3. Honours an opt-out — new disable_coordination_notices profile setting that mirrors disable_memory_index (new Profile field + ConfigCommand set 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 for KCAP_URL/--server-url users too (matching the sibling guidelines opt-out and every non-Claude harness's memory read).

Safety

  • Additive & fail-open everywhere: any JSON parse/build error leaves the hook unaffected; a malformed coordination_notices field renders nothing and never fails the hook.
  • AOT-safe: JsonNode + the existing source-generated Profile context; no reflection. Builds warning-free with PublishAot/IsAotCompatible analyzers on.
  • No Linear IDs in .cs sources (scripts/check-linear-ids.sh clean).

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_notices set true/false/invalid.

All targeted unit classes pass locally (macOS); the integration class is Linux-gated (assembly RunOn(OS.Linux), like the sibling ClaudeHookStdoutTests) and was verified locally under a temporary gate widen (reverted).

🤖 Generated with Claude Code

…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>
@linear-code

linear-code Bot commented Aug 19, 2026

Copy link
Copy Markdown

AI-2095

AI-2008

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Render coordination notices in SessionStart context

✨ Enhancement 🧪 Tests ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Advertises live SessionStart coordination-notices support and renders overlap warnings into agent
 context.
• Adds a profile opt-out suppressing both capability negotiation and notice rendering.
• Covers successful, disabled, empty, and malformed responses with unit and integration tests.
Diagram

sequenceDiagram
    participant P as Profile Config
    participant H as Claude Hook
    participant S as Session API
    participant E as Notices Emitter
    participant A as Context Envelope
    participant C as Claude Context
    P->>H: Opt-out setting
    alt Notices enabled
        H->>S: POST capability v1
        S-->>H: Notice list
        H->>E: Build fragment
        E-->>H: Markdown bullets
    else Notices disabled
        H->>S: POST without capability
    end
    H->>A: Combine fragments
    A-->>C: additionalContext
Loading
High-Level Assessment

The current approach is appropriate: capability negotiation keeps older clients and historical replays inert, piggybacking notices on the existing SessionStart response avoids another network request, and a dedicated emitter matches existing fragment composition patterns. Omitting the capability when disabled, plus suppressing rendering defensively, provides a strong opt-out while fail-open parsing protects hook execution.

Files changed (9) +478 / -1

Enhancement (2) +99 / -1
ClaudeHookCommand.csNegotiate and render SessionStart coordination notices +32/-1

Negotiate and render SessionStart coordination notices

• Conditionally adds the v1 capability to live SessionStart requests after the backlog guard, using the effective profile preference. It converts returned notices into a fragment and includes it in the shared additionalContext envelope while remaining fail-open.

src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs

CoordinationNoticesEmitter.csAdd fail-open coordination notice renderer +67/-0

Add fail-open coordination notice renderer

• Introduces the v1 capability token and a renderer for bounded coordination_notices response arrays. Valid text entries become bullets under a dedicated heading, while disabled, absent, empty, or malformed content produces no fragment.

src/Capacitor.Cli/CoordinationNoticesEmitter.cs

Tests (4) +365 / -0
SessionStartCoordinationNoticesTests.csExercise the coordination-notices lane end to end +134/-0

Exercise the coordination-notices lane end to end

• Adds WireMock integration coverage for capability advertisement, response rendering, opt-out suppression, and malformed-response handling. Tests preserve global profile configuration between runs.

test/Capacitor.Cli.Tests.Integration/SessionStartCoordinationNoticesTests.cs

ConfigCommandTests.csTest coordination-notices configuration parsing +26/-0

Test coordination-notices configuration parsing

• Verifies true and false updates for disable_coordination_notices and confirms invalid values throw an argument exception.

test/Capacitor.Cli.Tests.Unit/Commands/ConfigCommandTests.cs

ClaudeHookCommandTests.csTest hook capability and rendering behavior +84/-0

Test hook capability and rendering behavior

• Covers default capability advertisement, opt-out omission, notice rendering, defense-in-depth render suppression, and malformed-field fail-open behavior in the Claude SessionStart hook.

test/Capacitor.Cli.Tests.Unit/Commands/Harness/ClaudeHookCommandTests.cs

CoordinationNoticesEmitterTests.csTest coordination notice fragment generation +121/-0

Test coordination notice fragment generation

• Validates headings, bullets, tail entries, whitespace filtering, opt-out behavior, and null handling. It also confirms malformed arrays and non-string text are skipped without creating a separate JSON envelope.

test/Capacitor.Cli.Tests.Unit/CoordinationNoticesEmitterTests.cs

Documentation (1) +1 / -0
help-config.txtDocument the coordination-notices opt-out +1/-0

Document the coordination-notices opt-out

• Adds disable_coordination_notices to the supported configuration key reference with its SessionStart behavior.

src/Capacitor.Cli.Core/Resources/help-config.txt

Other (2) +13 / -0
ProfileConfig.csAdd coordination-notices profile preference +11/-0

Add coordination-notices profile preference

• Adds the nullable disable_coordination_notices profile field. Its documentation explains that opting out leaves notices available through other delivery channels and remains independent from other SessionStart injections.

src/Capacitor.Cli.Core/Config/ProfileConfig.cs

ConfigCommand.csSupport setting the coordination-notices preference +2/-0

Support setting the coordination-notices preference

• Extends config set handling to accept true or false for disable_coordination_notices and reject invalid values.

src/Capacitor.Cli/Commands/ConfigCommand.cs

@qodo-code-review

qodo-code-review Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (1) 📜 Skill insights (0)

Grey Divider


Action required

1. README omits new config key 📘 Rule violation ⚙ Maintainability
Description
The user-facing disable_coordination_notices config option is added to the CLI and help resource
without a corresponding README.md update. Users relying on the primary documentation will not
discover the new opt-out.
Code

src/Capacitor.Cli/Commands/ConfigCommand.cs[R170-171]

+            "disable_coordination_notices" when bool.TryParse(value, out var b) => profile with { DisableCoordinationNotices = b },
+            "disable_coordination_notices" => throw new ArgumentException($"Invalid value for disable_coordination_notices: '{value}'. Must be true or false."),
Evidence
Compliance rule 14 requires README updates for new user-facing CLI settings. The changed command and
help resource expose disable_coordination_notices, while the PR branch README contains no
occurrence of that key.

CLAUDE.md: User-Facing CLI Changes Must Update README.md in the Same PR
src/Capacitor.Cli/Commands/ConfigCommand.cs[170-171]
src/Capacitor.Cli.Core/Resources/help-config.txt[20-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new user-facing `disable_coordination_notices` configuration key is documented in CLI help but absent from `README.md`.

## Issue Context
PR Compliance ID 14 requires user-visible CLI changes to update the README in the same PR. Document the setting in the relevant quick-start and CLI configuration sections, including its default behavior and usage example.

## Fix Focus Areas
- README.md[1-1]
- src/Capacitor.Cli.Core/Resources/help-config.txt[20-20]
- src/Capacitor.Cli/Commands/ConfigCommand.cs[170-171]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Comments narrate obvious implementation ✓ Resolved 📘 Rule violation ⚙ Maintainability
Description
Several added documentation and inline-comment blocks verbosely restate the wire format, control
flow, and nearby code rather than explaining only non-obvious constraints. This conflicts with the
requirement that comments remain minimal and rationale-focused.
Code

src/Capacitor.Cli/CoordinationNoticesEmitter.cs[R6-9]

+/// <summary>
+/// Builds the SessionStart "coordination notices" text fragment from a server
+/// <c>/hooks/session-start</c> response body. Returns plain text, not a JSON
+/// envelope — the caller (see <c>SessionStartAdditionalContext</c>) joins this
Evidence
Rule 8 requires short comments focused on non-obvious reasons. The cited additions describe the
response body, rendering steps, opt-out behavior, and request flow at length even though those
mechanics are directly represented by the adjacent implementation.

CLAUDE.md: Comments Must Be Minimal and Explain “Why”, Not Restate Code
src/Capacitor.Cli/CoordinationNoticesEmitter.cs[6-25]
src/Capacitor.Cli.Core/Config/ProfileConfig.cs[73-80]
src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[582-592]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The added comments extensively narrate implementation details that are already evident from class names, conditions, and method calls.

## Issue Context
Retain only concise comments that explain non-obvious constraints, such as why capability advertisement occurs after the backlog return or why failures are intentionally ignored. Remove descriptions of ordinary data flow and wire fields where names and types are sufficient.

## Fix Focus Areas
- src/Capacitor.Cli.Core/Config/ProfileConfig.cs[73-80]
- src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[582-592]
- src/Capacitor.Cli/CoordinationNoticesEmitter.cs[6-25]
- src/Capacitor.Cli/CoordinationNoticesEmitter.cs[27-41]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Replay drops coordination notices ✓ Resolved 🐞 Bug ☼ Reliability
Description
The capability is added to body, and that same body is spooled after a transient POST failure;
replay posts it verbatim but never renders the returned notices. A retry can therefore cause the
server to claim terminal-delivery notices while the CLI silently discards them.
Code

src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[R598-599]

+                        node["coordination_notices"] = CoordinationNoticesEmitter.CapabilityVersion;
+                        body                          = node.ToJsonString();
Evidence
The new code mutates the live request body with the capability, and the transient-failure path later
persists that same body. HookSpool supplies stored payloads verbatim to the replay poster, whose
response handling only inspects session-end responses; unlike the live success path, it never builds
a coordination-notices fragment.

src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[593-639]
src/Capacitor.Cli.Core/HookSpool.cs[190-203]
src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[997-1021]
src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[649-713]
src/Capacitor.Cli/CoordinationNoticesEmitter.cs[28-31]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
SessionStart retry payloads retain `coordination_notices: "v1"`, but spool replay ignores the response. This can claim and discard terminal-delivery notices during recovery from a transient failure.

## Issue Context
Keep a replay-safe body without the capability, or explicitly remove the field before every post-capability spool append. Replay must not advertise a response capability it cannot consume.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[593-639]
- src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[997-1021]
- src/Capacitor.Cli.Core/HookSpool.cs[190-203]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View high (1)
4. Claude logic escapes vendor directory ✗ Dismissed 📘 Rule violation ⚙ Maintainability
Description
The new CoordinationNoticesEmitter implements Claude SessionStart response rendering at the
project root rather than under Harness/Claude/. Its only production integration shown by the
change is ClaudeHookCommand, leaving vendor-specific behavior in shared space.
Code

src/Capacitor.Cli/CoordinationNoticesEmitter.cs[R26-29]

+static class CoordinationNoticesEmitter {
+    /// <summary>
+    /// The capability token the CLI advertises on the SessionStart request
+    /// (<c>coordination_notices: "v1"</c>). An old/opted-out CLI sends nothing and
Evidence
Rule 2 requires vendor-specific implementation code under the corresponding vendor directory. The
emitter explicitly builds a Claude Code SessionStart envelope fragment and is integrated by
ClaudeHookCommand, but the new file is placed directly under src/Capacitor.Cli/.

CLAUDE.md: Vendor-Specific Harness Code Must Live Only Under That Vendor’s Harness Directory
src/Capacitor.Cli/CoordinationNoticesEmitter.cs[6-26]
src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[701-702]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Claude-specific SessionStart rendering logic was introduced outside the vendor harness directory.

## Issue Context
PR Compliance ID 2 requires vendor implementations to live under `Harness/<Vendor>/`, while genuinely cross-vendor code remains shared. Move the emitter to the Claude harness and update its namespace/imports, or extract a genuinely vendor-neutral abstraction while keeping Claude integration in the vendor directory.

## Fix Focus Areas
- src/Capacitor.Cli/CoordinationNoticesEmitter.cs[1-67]
- src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs[701-702]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

5. Setting missing from usage ✓ Resolved 🐞 Bug ⚙ Maintainability
Description
ApplySet accepts disable_coordination_notices, but the command-specific kcap config set usage
omits it from the supported key list. Users reaching that usage output cannot discover the new
setting even though the general config help advertises it.
Code

src/Capacitor.Cli/Commands/ConfigCommand.cs[R170-171]

+            "disable_coordination_notices" when bool.TryParse(value, out var b) => profile with { DisableCoordinationNotices = b },
+            "disable_coordination_notices" => throw new ArgumentException($"Invalid value for disable_coordination_notices: '{value}'. Must be true or false."),
Evidence
The setter switch recognizes the new key and the general help resource lists it, while the complete
inline SetUsage key list skips directly from other SessionStart controls to unrelated settings
without including it.

src/Capacitor.Cli/Commands/ConfigCommand.cs[155-183]
src/Capacitor.Cli/Commands/ConfigCommand.cs[196-212]
src/Capacitor.Cli.Core/Resources/help-config.txt[17-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new config key is implemented but absent from the `kcap config set` usage list, leaving the two config help surfaces inconsistent.

## Issue Context
Add `disable_coordination_notices` alongside the related SessionStart opt-outs in `SetUsage`, using wording consistent with `help-config.txt`.

## Fix Focus Areas
- src/Capacitor.Cli/Commands/ConfigCommand.cs[196-212]
- src/Capacitor.Cli.Core/Resources/help-config.txt[17-21]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
Review mode: ⚖️ Balanced: Downgraded extended -> standard: change is below the extended eligibility bar (hunks 10/18, lines 479/200; both must reach the floor). Router rationale: This is a behavior-changing cross-cutting feature spanning hook request mutation, response parsing/rendering, profile/config opt-out handling, and integration paths, with enough independent logic sites for redundant review to catch subtle defects.

Grey Divider

Tip of the day
💡 Did you know, you can show, collapse, or hide each part of a finding: code, evidence, and all

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread src/Capacitor.Cli/Commands/ConfigCommand.cs
Comment thread src/Capacitor.Cli/CoordinationNoticesEmitter.cs Outdated
Comment thread src/Capacitor.Cli/CoordinationNoticesEmitter.cs
Comment thread src/Capacitor.Cli/Commands/Harness/ClaudeHookCommand.cs Outdated
Comment thread src/Capacitor.Cli/Commands/ConfigCommand.cs
realtonyyoung and others added 2 commits August 19, 2026 13:09
- 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>
@realtonyyoung
realtonyyoung merged commit 43eeb6d into main Aug 19, 2026
6 checks passed
@realtonyyoung
realtonyyoung deleted the claude-tyoung/coordination-notices-rendering branch August 19, 2026 18:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant