Skip to content

feat(kiro-ide): read the 1.x stdin hook context channel - #615

Merged
leandrodamascena merged 4 commits into
awslabs:v2from
csotomon:feat/kiro-ide-stdin-context
Jul 29, 2026
Merged

feat(kiro-ide): read the 1.x stdin hook context channel#615
leandrodamascena merged 4 commits into
awslabs:v2from
csotomon:feat/kiro-ide-stdin-context

Conversation

@csotomon

@csotomon csotomon commented Jul 20, 2026

Copy link
Copy Markdown

Summary

Closes the payload boundary #614 left open on Kiro IDE 1.x. That generation delivers hook context as snake_case JSON on stdin with an empty USER_PROMPT, while 0.12 uses a camelCase USER_PROMPT and leaves stdin open forever. The adapter now serves both without penalising either, so artifact audit rows, sensor firing, and subagent tracking work again on current IDE builds.

Behaviour

Channel acquisition. A non-empty USER_PROMPT is consumed immediately (0.12). Only when it is empty does the adapter read stdin (1.x), bounded by a broken-channel ceiling. Both field spellings are accepted; the explicit toolSuccess: false failed-write guard (#417) is preserved. Acquisition is gated to audit-and-sensors and log-subagent — every other target, including the per-tool-call block floor, touches neither channel.

Subagent completions. IDE 1.x sends subagent_<agent>, not the 0.12 invoke_sub_agent, each preceded by an empty subagent_response shell. The registration matcher is therefore broad (^(subagent_.+|invoke_sub_agent)$) so any delegate name reaches the adapter — including fork-added agents that do not follow the aidlc-*-agent convention — and the adapter drops subagent_response, which carries prose but no identity and would otherwise be logged as Agent Type: unknown. That split is deliberate: the direct and dispatcher entry points bypass the matcher entirely.

Broken-channel visibility. When neither channel yields a payload, audit-and-sensors now records a visible hook drop instead of exiting silently, so /aidlc --doctor can surface exactly the decay this harness exists to eliminate.

Dispatcher parity. The public aidlc adapter kiro-ide <target> route follows the same contract; previously it forced input = "".

Test seam

AIDLC_IDE_STDIN_TIMEOUT_MS overrides the stdin ceiling. The latency cases raise it well above any plausible CI scheduling delay and assert the process finishes far inside that window, so "did this path probe stdin at all?" is decided by a wide margin rather than a millisecond budget near the production ceiling. Verified the seam is genuinely honoured: a payload target parks at exactly the ceiling given (557 ms at 500, 4043 ms at 4000) while mint exits in 48 ms.

Coverage

Case Pins
N4 USER_PROMPT wins without probing stdin
N5a / N5b subagent_<agent> accepted and identity recovered; invoke_sub_agent compat; subagent_response dropped
N6 / N7 / N7b No stdin probe on payload-independent targets, the 0.12 env path, and the dispatcher
N8 / N9 Dispatcher forwards the 1.x payload; broken-channel ceiling releases held-open stdin
N10 Empty context records a visible drop
N11 Restores the held-open-stdin coverage for stop / session-end deleted with 12b (#639)
t245 Registration matcher reaches every observed delegate name; documents that the exclusion belongs in the adapter

Validation

  • bun scripts/package.ts --check: clean across all five harnesses
  • bun run typecheck, bun run lint: clean
  • t218 + t245: 71 tests, 271 assertions, green
  • t68, t149, t239, t221: 99 tests green
  • Full default suite: 280 files, 5732 assertions, no regressions from this PR

Two integration files fail on this branch and fail identically on bare upstream/v2 — verified in a clean worktree (15 failures there too): t66 and t89 break on #647's new claim-sources sensor, whose golden export fixture and sensor-resolution fixtures were not updated. Neither file is touched here. t19 is a live preflight that only needs authenticated credentials; it passes 2/2 once a valid profile is used.

Not verified here

A real delegate completion on IDE 1.0.165 has not been captured, so the subagent_<agent> naming rests on the 1.0.89-1.0.138 evidence in #543. The matcher is broad enough to cover either naming, but the live confirmation is still open.

Upgrade

mkdir -p your-project/.kiro && cp -R dist/kiro-ide/.kiro/. your-project/.kiro/

Version

Bumps to 2.5.12. Note that #646 and #535 also claim 2.5.12; per the repo changelog policy the second PR to merge rebases and re-bumps.

References

csotomon pushed a commit to csotomon/aidlc-workflows that referenced this pull request Jul 22, 2026
…ce (2.5.4)

Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format,
leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165).
Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0.

- Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside
  the existing legacy .kiro.hook files; same adapter targets and commands
- Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire)
- Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and
  session-end fire for observability/audit, not enforcement
- Known boundary: payload-dependent targets (audit-and-sensors, log-subagent)
  fire but no-op on 1.x until the stdin context channel lands (awslabs#615)
- Five remaining hooks (mint, block, session-start, runtime-compile,
  sync-statusline) are payload-independent and fully functional on 1.x
- Docs updated: kiro-ide guide context-channel caveat, README both formats,
  14-claude-features Automation row; version 2.5.4 + CHANGELOG

Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
csotomon pushed a commit to csotomon/aidlc-workflows that referenced this pull request Jul 23, 2026
…ce (2.5.4)

Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format,
leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165).
Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0.

- Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside
  the existing legacy .kiro.hook files; same adapter targets and commands
- Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire)
- Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and
  session-end fire for observability/audit, not enforcement
- Known boundary: payload-dependent targets (audit-and-sensors, log-subagent)
  fire but no-op on 1.x until the stdin context channel lands (awslabs#615)
- Five remaining hooks (mint, block, session-start, runtime-compile,
  sync-statusline) are payload-independent and fully functional on 1.x
- Docs updated: kiro-ide guide context-channel caveat, README both formats,
  14-claude-features Automation row; version 2.5.4 + CHANGELOG

Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
@csotomon
csotomon force-pushed the feat/kiro-ide-stdin-context branch from c076013 to db876c3 Compare July 23, 2026 19:02
csotomon pushed a commit to csotomon/aidlc-workflows that referenced this pull request Jul 23, 2026
…ce (2.5.4)

Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format,
leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165).
Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0.

- Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside
  the existing legacy .kiro.hook files; same adapter targets and commands
- Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire)
- Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and
  session-end fire for observability/audit, not enforcement
- Known boundary: payload-dependent targets (audit-and-sensors, log-subagent)
  fire but no-op on 1.x until the stdin context channel lands (awslabs#615)
- Five remaining hooks (mint, block, session-start, runtime-compile,
  sync-statusline) are payload-independent and fully functional on 1.x
- Docs updated: kiro-ide guide context-channel caveat, README both formats,
  14-claude-features Automation row; version 2.5.4 + CHANGELOG

Addresses awslabs#555; the stdin channel fix for awslabs#543 follows in awslabs#615.
leandrodamascena pushed a commit that referenced this pull request Jul 23, 2026
* fix(kiro-ide): ship v2 hook schema alongside legacy for IDE coexistence (2.5.4)

Kiro IDE >= 1.0.1xx silently stopped executing the legacy .kiro.hook format,
leaving dist/kiro-ide installs with no hooks layer (field-proven on 1.0.165).
Ships both formats for coexistence: v2 JSON for IDE >= 1.0, legacy for pre-1.0.

- Add nine .kiro/hooks/aidlc-*.json (v2 schema, PascalCase triggers) alongside
  the existing legacy .kiro.hook files; same adapter targets and commands
- Coexistence safe: pre-1.0 ignores v2 files, 1.x+ ignores legacy (no double-fire)
- Known boundary: Stop trigger is advisory-only on IDE (cannot block); stop and
  session-end fire for observability/audit, not enforcement
- Known boundary: payload-dependent targets (audit-and-sensors, log-subagent)
  fire but no-op on 1.x until the stdin context channel lands (#615)
- Five remaining hooks (mint, block, session-start, runtime-compile,
  sync-statusline) are payload-independent and fully functional on 1.x
- Docs updated: kiro-ide guide context-channel caveat, README both formats,
  14-claude-features Automation row; version 2.5.4 + CHANGELOG

Addresses #555; the stdin channel fix for #543 follows in #615.

* fix(kiro-ide): address round-2 review - correctness, coverage, and docs (2.5.8)

Addresses all 7 items from the leandrodamascena review:

1. Rebase + re-bump to 2.5.8 (2.5.7 taken by #639 which also touched
   the adapter file). CHANGELOG entry above 2.5.7, badge and version.ts
   set explicitly (the auto-merge trap).

2. Install commands use content-copy semantics (mkdir -p + cp -R src/.
   dst/) on both Kiro trees (IDE and CLI) in README.md, kiro-ide.md, and
   kiro-cli.md. The previous cp -r form nested .kiro inside .kiro on
   existing installs.

3. session-start.json trigger changed from UserPromptSubmit to
   SessionStart (the real once-per-session trigger on IDE 1.x). The
   legacy file keeps its per-prompt behavior unchanged for pre-1.0.

4. session-end v2 registration REMOVED: the IDE's Stop trigger fires
   at the end of every assistant turn, not at conversation close, so it
   was appending SESSION_ENDED between prompts. The legacy .kiro.hook
   stays (pre-1.0 behavior unchanged). Documented in kiro-ide.md and
   the CHANGELOG.

5. log-subagent adapter: empty payload (IDE 1.x) now returns null with
   a visible recordHookDrop instead of forwarding a fabricated
   SUBAGENT_COMPLETED row with Agent Type: unknown. Non-empty payload
   without an identity marker keeps the documented unknown fallback
   (t218 S3 still passes).

6. New t244-kiro-ide-hook-registrations.test.ts: table-driven
   structural test covering all 8 v2 JSONs x {version, trigger, matcher,
   command}, the absence of session-end.json, no unexpected extras, and
   all 9 legacy files present. Runs on both authored and dist trees.

7. Docs: aidlc-stop.json description corrected (advisory-only, not
   enforcement); README:115 now says cli.json is CLI-only (the IDE
   ignores it).

Dist regenerated. Drift guard OK (5 harnesses), typecheck green,
smoke+unit 176 files / 4350 assertions / 0 failures.

* fix: round-4 doc fixes + t244->t245 rename

* fix: round-4 - onboarding SESSION_ENDED claim, hook-payload.md dual-channel, t244->t245 rename, rebase to 2.5.10

---------

Co-authored-by: Camilo Soto <csotomon@amazon.com>
@csotomon
csotomon force-pushed the feat/kiro-ide-stdin-context branch 2 times, most recently from 160c54e to f5cab27 Compare July 23, 2026 22:12
@csotomon
csotomon marked this pull request as ready for review July 23, 2026 22:20

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two material issues remain:

  1. [P2] Make log-subagent reachable for the documented Kiro IDE 1.x tool names (harness/kiro-ide/hooks/aidlc-log-subagent.json:7)

The registration matches only invoke_sub_agent, but the live evidence in #543 records subagent_<agent> on IDE 1.0.89, with no invoke_sub_agent event. Test N5 invokes the adapter directly with a synthetic invoke_sub_agent payload, bypassing the registration matcher entirely. Consequently, the changelog claim that subagent tracking is fully restored across Kiro IDE 1.x is not yet supported.

Please accept the observed completion forms, exclude subagent_response, and add a registration-level test using the captured tool names.

  1. [P2] Avoid the mandatory two-second delay on the supported 0.12 channel (harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:451-457)

On IDE 0.12, USER_PROMPT already contains the payload while stdin remains open indefinitely. The adapter nevertheless waits for the full stdin timeout before reading the available environment payload. This adds approximately two seconds to every audit-and-sensors and log-subagent invocation.

The existing N7 test confirms eventual fallback but does not constrain latency; it completed in 2.44 seconds, compared with 204 ms for the non-reading N6 path. Please use a non-empty USER_PROMPT immediately, or otherwise distinguish the legacy channel without imposing the timeout on every payload hook.

Non-blocking follow-ups:

  • aidlc adapter kiro-ide <target> still forces input = "", so the dispatcher route cannot consume the new 1.x stdin payload.
  • Narrow the statement that tool_input is “always empty” to the PostToolUse write/shell events actually captured; #543 records populated PreToolUse and delegation inputs on later 1.x builds.
  • Mark only the hook-registration portion of #555 as resolved; its agent/settings concerns remain open.

Verification: t218 passed 34 tests; t218 + t245 passed 63 tests in three consecutive runs; package parity passed for all harnesses.

@csotomon
csotomon force-pushed the feat/kiro-ide-stdin-context branch from f5cab27 to 66fe3fc Compare July 24, 2026 14:37

@csotomon csotomon left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both P2 findings are fixed. Here is what happened and what changed:


1. log-subagent matcher — our mistake

We failed to validate the registration matcher against the actual tool names documented in #543. The issue explicitly states that completions arrive as subagent_aidlc-product-lead-agent (not invoke_sub_agent) and that subagent_response must be excluded. We took the #603 diff as the specification instead of cross-referencing the linked issue evidence, and our test (N5) invoked the adapter directly — bypassing the registration entirely — so the structural test (t245) pinned the wrong value without catching it.

Fix: matcher is now ^(subagent_.+-agent|invoke_sub_agent)$. The adapter also applies the same gate internally (defense-in-depth for direct/dispatcher invocations). New tests:

  • t245: registration-level regex validation against all captured tool names
  • N5a: subagent_<agent> accepted, identity recovered
  • N5b: invoke_sub_agent compat, subagent_response excluded

2. Mandatory 2s delay on 0.12 — our mistake

We accepted N7's eventual fallback (it asserted correctness but not latency) without noticing that probing stdin first on 0.12 — where it never closes — imposed a deterministic 2s wait on every payload hook. The test completed in ~2.4s and we treated that as success.

Fix: a non-empty USER_PROMPT is now consumed immediately, without touching stdin. The timeout race fires only when USER_PROMPT is empty (the 1.x channel). N7 now asserts elapsedMs < 1900 with a held-open stdin and passes at ~330ms.


Non-blocking items (included in this push)

Dispatcher route

The aidlc adapter kiro-ide <target> route in core/tools/aidlc.ts now mirrors the adapter entry point's dual-generation contract instead of forcing input = "". This was introduced by #639 (merged after #603 was closed) and became semantically stale after this PR's stdin acquisition fix. N8 and N9 pin it.

tool_input scope

Narrowed to "captured PostToolUse write/shell events" in docs, comments, and the hook-payload reference. The follow-up observation from #543 (populated PreToolUse inputs at 1.0.116+, delegation inputs at 1.0.138+) is now cited rather than contradicted.

#555 roadmap claim

Corrected: only the hook-registration portion is marked resolved; the agent/settings surfaces remain open.


Verification

  • package --check: all five harnesses in sync
  • typecheck + lint: green
  • Full default suite: 280 files / 5729 assertions; 3 failures are pre-existing upstream (t19: expired AWS creds; t66/t89: #647's claim-sources sensor broke its own golden fixture — untouched by this PR)
  • t218: 75 tests / 259 assertions (including the 5 new cases)
  • t245: 35 tests / all green with registration-level matcher validation

@csotomon

Copy link
Copy Markdown
Author

CI note: the full default suite shows 3 pre-existing failures on this branch that also reproduce on bare upstream/v2 (5cdcc63):

  • t19InvalidClientTokenId: requires live AWS credentials (environment-specific, not a code issue).
  • t66 / t89Stage "intent-capture" imports unknown sensor id "claim-sources": upstream fix: ground Intent Capture claims (2.5.10) #647 added the claim-sources sensor and a reviewer field to intent-capture, but the t66 golden export fixture and the t89 sensor-resolution test fixtures were not updated in that commit. These tests pass if you regenerate the fixture (bun .claude/tools/aidlc-graph.ts export > tests/fixtures/...), confirming this PR does not touch the affected files.

None of the 3 failures involve files changed by this PR. Happy to open an issue for the t66/t89 fixture drift if helpful.

@apackeer

Copy link
Copy Markdown
Contributor

Reviewed at head f5cab27. The mechanism is sound and well-guarded: stdin-first acquisition raced against a 2s timeout, gated to the two payload-dependent targets, falling back to USER_PROMPT, with both field spellings accepted. The #417 failed-write guard and the #459 identity recovery carry over correctly, and every gate I can run on this head is green (details at the bottom). Three P2 asks and two P3s before this closes #543 cleanly.

P2-1: the dispatcher's adapter kiro-ide route still discards the payload

The stdin acquisition lives only in the adapter's import.meta.main block (harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:443). The dispatcher route enters through run() instead, and core/tools/aidlc.ts:986 still forces input = "" for kiro-ide (the #639 hang fix), so aidlc adapter kiro-ide <target> keeps the 0.12 behavior. The comment above it (core/tools/aidlc.ts:982-985) is now false: "The IDE adapter's run() ignores its input parameter" - after this PR it doesn't.

Reproduced on the head: piping a valid 1.x payload into bun .kiro/tools/aidlc.ts adapter kiro-ide audit-and-sensors logs hasStdinPayload=false and no-ops, while the same payload through the direct adapter entry forwards.

Compounding it, test 12d ("the DISPATCHER adapter route also never reads stdin for kiro-ide", added with #639) is deleted with no replacement, so the two entry points can now diverge unguarded. No shipped hook JSON uses the dispatcher route today, but it is a public grammar verb and the compiled-binary path runs through it.

Ask: apply the same PAYLOAD_TARGETS-gated raced read in runAdapter() (or hoist acquisition into a helper both entries share), fix the stale comment, and restore a 12d-shaped regression test for the route's new contract.

P2-2: invoke_sub_agent is not the name IDE 1.x sends, so log-subagent likely still never fires there

The shipped matcher (harness/kiro-ide/hooks/aidlc-log-subagent.json:7, from #614) matches invoke_sub_agent - the 0.12.333 name from #459. But #543's own "Fix 2" section documents, from a clean 1.0.89 run: every delegate completion arrived as subagent_<agentname> (e.g. subagent_aidlc-product-lead-agent), each preceded by an empty subagent_response shell ("Response recorded."), and invoke_sub_agent did not appear at all. The same author verified the channel on 1.0.116 and 1.0.138. This PR's 1.0.165 live verification covers audit-and-sensors and the mint rows but no subagent completion, and t218 N5 invokes the adapter directly (bypassing the matcher), so "log-subagent fully functional on 1.x" is unproven - if the 1.0.89-138 naming holds on 1.0.165, the hook never fires.

There is a trap in the naive fix: the adapter deliberately carries no tool-name gate on log-subagent ("the hook file's matcher already filters to invoke_sub_agent"), so broadening the matcher alone forwards the subagent_response shell - non-empty prose, no identity marker - and fabricates a SUBAGENT_COMPLETED row with Agent Type: unknown. Reproduced against the head by feeding the shell event straight to the target:

kiro-adapter  invoked  target="log-subagent" hasStdinPayload=true toolName="subagent_response" toolResult="Response recorded."
kiro-adapter  forward  target="log-subagent" hook="aidlc-log-subagent.ts"

Ask: broaden the matcher (#543 ran ^(subagent_.+|invoke_sub_agent)$ live on 1.0.89), add the subagent_response exclusion in the adapter's log-subagent gate, and pin both - a grep-pin on the shipped matcher string plus adapter-level cases for subagent_<name> (forwards, identity recovered) and subagent_response (drops); the IDE's matcher engine itself can't run in CI. Ideally re-verify one real delegate completion on 1.0.165 to settle the naming question.

P2-3: an empty context on audit-and-sensors is a silent no-op

If the stdin race times out and USER_PROMPT is empty, ide stays {} and the flow exits at canonicalWriteTool("") === "" (aidlc-kiro-adapter.ts:272) with no recordHookDrop - while log-subagent's equivalent empty-payload path records a visible drop (aidlc-kiro-adapter.ts:348). Reproduced: an empty-context invocation logs forward: null (no-op) and writes no .drops file, so --doctor cannot surface a broken channel on exactly the path this harness exists to fix.

Ask: record a drop when a payload target ends up with an empty context, before the tool-name check.

P3s

  • 0.12 now pays the full 2s race on every audited write and subagent completion (its stdin never resolves, so the timeout always wins before the USER_PROMPT fallback). Trying the env var first when it parses would make 0.12 instant and cost 1.x nothing (its USER_PROMPT arrives empty). N4 pins stdin-first precedence, so I read this as a deliberate tradeoff - flagging to confirm it is one.
  • Coverage narrowing on the agentStop surface: deleted 12b exercised stop/session-end with a held-open stdin - the exact fix(kiro-ide): never read stdin in adapter entry paths (2.5.7) #639 hang - while new N6 covers only mint as the payload-independent proxy. All non-payload targets share the skip path structurally, but the specific regression surface lost its pin; one agentStop-target case would restore it.

Nits

Verification on f5cab27

  • bun scripts/package.ts --check: clean; harness/dist adapter copies byte-identical
  • bun run typecheck: clean
  • t218: 34/34; t145 + t147: green
  • smoke + unit: 176 files, 4363 assertions, 0 failures
  • integration: 103 files, 1354 assertions, 0 failures
  • Version/badge/CHANGELOG agree on 2.5.11 and the slot is currently free on v2
  • Kiro IDE live e2e is not runnable on my box (Linux; needs macOS + Kiro.app), so the 1.0.165 claims rest on the captures in the PR body - which is why P2-2's missing subagent-completion capture matters.

Close the Kiro IDE 1.x payload boundary left by the v2 hook-schema migration, so artifact audit rows, sensor firing, and subagent tracking work on current IDE builds without regressing pre-1.0.

- Consume a non-empty legacy USER_PROMPT immediately; read the 1.x stdin payload only when it is empty, bounded by a broken-channel ceiling
- Record SUBAGENT_COMPLETED again on 1.x: the registration matcher reaches any delegate name (that generation sends subagent_<agent>, not invoke_sub_agent) while the adapter drops the empty subagent_response shell
- Record a visible hook drop when neither channel yields a payload, so a broken context channel surfaces in --doctor instead of exiting silently
- Apply the same dual-channel contract to the public aidlc adapter kiro-ide dispatcher route
- Preserve both field spellings and the explicit toolSuccess:false write guard
- Add the AIDLC_IDE_STDIN_TIMEOUT_MS seam so latency coverage asserts "never probed stdin" deterministically rather than against a tight millisecond budget
- Restore held-open-stdin coverage for the agentStop targets and pin the dispatcher route
- Scope the empty tool-input documentation to captured PostToolUse events and state that awslabs#543/awslabs#555 remain open pending manual closure

Refs awslabs#543, awslabs#459.
@csotomon
csotomon force-pushed the feat/kiro-ide-stdin-context branch from 66fe3fc to f8d4f52 Compare July 24, 2026 23:50
@csotomon

Copy link
Copy Markdown
Author

@apackeer thanks — you reviewed at f5cab276, before the round-2 push, so P2-1 and P3-1 were already fixed by the time you looked. The two you found that nobody else had are now fixed too. Head is f8d4f527.

P2-2 — I adopted your matcher over mine, and you were right to push back

I had shipped ^(subagent_.+-agent|invoke_sub_agent)$. That was fragile for exactly the reason your suggested form avoids: it silently requires every delegate name to end in -agent. The 14 shipped agents happen to comply, but the framework supports fork-added agents, so subagent_my-custom-reviewer would never have fired. Now ^(subagent_.+|invoke_sub_agent)$.

You also called the trap precisely: broadening alone forwards the subagent_response shell and fabricates Agent Type: unknown. The exclusion lives in the adapter gate, not the matcher — that is the only place that also covers the direct and dispatcher entries. I wrote the division of responsibility into both the adapter comment and the t245 test, with an explicit note that the test pins broad reach and must not be "hardened" into an exclusion, so the next person does not re-narrow it.

P2-3 — fixed, and it was a real asymmetry

Confirmed: audit-and-sensors exited at canon === "" with no recordHookDrop, while log-subagent recorded one on its equivalent path. A drop is now recorded before the tool-name check, distinguishing a broken channel (neither channel produced anything) from a legitimate non-write no-op. Pinned by N10.

P3-2 — restored

You were right that deleting 12b lost the #639 surface and that mint alone is not an equivalent proxy. N11 restores it: stop and session-end, both "" and absent USER_PROMPT, stdin held open, each variant asserted on its own effect (block decision, one SESSION_ENDED per invocation).

P3-1 / your latency nit — fixed together

The 2s cost on 0.12 is gone: a non-empty USER_PROMPT is used directly. And you were right that elapsedMs < 1900 was a latent flake — worse, I had added two more of them. Replaced with an AIDLC_IDE_STDIN_TIMEOUT_MS seam: the latency cases raise the ceiling to 15s and assert completion under 8s, so the discriminator is a wide margin instead of a budget hugging the production ceiling. Verified the seam is actually honoured rather than silently ignored — a payload target parks at exactly the ceiling passed (557ms at 500, 4043ms at 4000) while mint exits in 48ms.

Nits

  • PR body corrected: the 176-file figure was smoke+unit, as you said. Full suite is 280 files / 5732 assertions.
  • Roadmap no longer claims resolution. It now states both issues are OPEN, which half shipped in which version, and that manual closing is required because a Fixes trailer on a v2-targeted PR does not auto-close. Your point that "resolved" was premature for the log-subagent leg was fair.

On your t66/t89 observation

I verified this in a clean upstream/v2 worktree: both fail there identically (15 failures), from #647's claim-sources sensor landing without its export/sensor fixtures updated. Not from this PR.

Still open, and I am not claiming otherwise

No real delegate completion captured on 1.0.165. The matcher now covers either naming, but your ask to settle the naming question live remains unmet — I would rather leave it explicit than assert coverage I have not observed.

One more thing worth flagging since you checked the slot on 2.5.11: 2.5.12 is contested#646 and #535 also bump to it. Per the changelog policy the second to merge re-bumps.

@apackeer

Copy link
Copy Markdown
Contributor

Re-reviewed at head f8d4f527. All four of my round-2 findings are genuinely fixed, and the fixes are better than what I asked for in two places: putting the subagent_response exclusion in the adapter gate (so it covers the direct and dispatcher entries, not just the matcher) is the right seam, and the AIDLC_IDE_STDIN_TIMEOUT_MS seam replaces three latent flakes with a wide-margin discriminator rather than papering over them. Verified independently:

  • P2-1 dispatcher route - core/tools/aidlc.ts:1019-1037 now mirrors the entry-point contract; the old input = "" and its false "run() ignores its input parameter" comment are gone. Re-ran my old repro (piping a 1.x payload through aidlc.ts adapter kiro-ide log-subagent): the row lands now.
  • P2-2 matcher - ^(subagent_.+|invoke_sub_agent)$ matches [Bug]: 2.2.17's Kiro IDE adapter reads USER_PROMPT, empty on IDE 1.0.89 — audit/sensor (#417) and log-subagent (#459) regress #543's live-proven form verbatim (issue body, Fix 2: "I kept the hook matcher broad (^(subagent_.+|invoke_sub_agent)$) and did the subagent_response exclusion in the adapter gate"). t245 pinning broad reach with the explicit do-not-harden note is the right shape.
  • P2-3 / P3-2 / latency - all confirmed fixed and pinned (N10, N11, N6/N7/N7b). I re-measured the held-open-stdin release per target at a 3s ceiling: log-subagent 3046ms, audit-and-sensors 3047ms, block 41ms, mint 41ms, stop 80ms. The gating is real - block and mint never touch the channel.

Two findings remain, both introduced by this round's changes.

P2 - the new tool-name gate makes log-subagent's broken-channel drop unreachable, silently reverting P2-3 on that leg

harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:365-388. The new isSubagentCompletion gate returns at line 369, before the empty-result drop at 381-388. On a broken channel ide.toolName is "", so the gate returns null and the recordHookDrop below it can never run.

Reproduced on this head, held-open stdin with an empty USER_PROMPT and a 500ms ceiling:

log-subagent      -> exit 0, drops file NOT created, no SUBAGENT_COMPLETED row
audit-and-sensors -> exit 0, drops: "audit-and-sensors: empty hook context ... write not audited"

That asymmetry is the exact one P2-3 set out to remove, now pointing the other way: audit-and-sensors reports a broken channel, log-subagent exits silently. Pre-PR the log-subagent drop did fire on this path (5cdcc63f:harness/kiro-ide/hooks/aidlc-kiro-adapter.ts, the empty-result check was the first statement in the case), so this is a regression, not a pre-existing gap.

It is also unguarded: grep -rn "empty tool payload" tests/ returns nothing on this head. The string is shipped but no test asserts it, which is why the suite stays green. N10 covers only audit-and-sensors.

Suggested fix - hoist the broken-channel check above the tool-name gate, keeping the two cases distinct:

const toolName = ide.toolName ?? "";
const result = ide.toolResult ?? "";
// Broken channel: neither channel produced anything. Distinct from a
// legitimate non-delegate tool name, which DOES carry context.
if (toolName === "" && result.trim() === "") {
  recordHookDrop(projectDir, "kiro-adapter",
    "log-subagent: empty hook context (no stdin payload, no USER_PROMPT), SUBAGENT_COMPLETED not recorded");
  return null;
}
const isSubagentCompletion = ...

and extend N10 to both payload targets (a for (const target of ["audit-and-sensors", "log-subagent"]) loop) so the two legs cannot drift apart again.

P2 - Agent Type: unknown on every 1.x delegate whose result lacks a self-identification line, when the tool name carries the identity

aidlc-kiro-adapter.ts:240-247 + :393. extractAgentIdentity reads only the result prose (**Reviewer:** / **Agent:**, the #459 0.12 contract). On IDE 1.x the tool name is subagent_<agent> - the identity is right there in the payload this PR just started reading - but it is used only for the gate and then discarded.

Reproduced on this head:

payload: {"tool_name":"subagent_aidlc-developer-agent","tool_response":"Implementation complete. All 4 files written and tests pass."}
audit:   **Event**: SUBAGENT_COMPLETED
         **Agent Type**: unknown
         **Message**: Implementation complete. All 4 files written and tests pass.

This matters more than a cosmetic label because the identity is what makes the row useful, and it is not hypothetical: of the 14 shipped agents only the reviewer personas emit **Reviewer:** headers. #543 happened to observe aidlc-product-lead-agent (a reviewer, which self-identifies), so the gap is invisible in that evidence - the domain-expert delegates are the ones that will land as unknown. t218 S3 pins the unknown fallback but only via the 0.12 invoke_sub_agent shape, where no identity is recoverable and unknown is genuinely correct.

Suggested fix - prefer the prose, fall back to the tool name on 1.x:

function extractAgentIdentity(toolResult: string, toolName = ""): string {
  // ... existing prose scan (the 0.12 contract, #459) ...
  // IDE 1.x encodes the delegate in the tool name itself (#543); prefer the
  // prose (it survives renames) but do not throw away a known identity.
  if (toolName.startsWith("subagent_") && toolName !== "subagent_response") {
    return toolName.slice("subagent_".length);
  }
  return "unknown";
}

with a case pinning that a 1.x subagent_<agent> payload whose result has no self-identification line still records the real agent type. Happy to treat this one as a follow-up issue rather than a blocker if you would rather land the channel fix - your call, but the fix is ~4 lines and the payload is already in hand.

P2 - a non-string tool_name crashes log-subagent with exit 1, breaking the fail-open contract

aidlc-kiro-adapter.ts:119 casts the field without validating it ((parsed.toolName ?? parsed.tool_name) as string | undefined), so a JSON-valid payload whose tool_name is not a string reaches .startsWith() at :368 and throws. The ?? "" at :365 cannot save it - the value is present, just the wrong type.

Reproduced on this head, every non-string type, on both entry paths and both channels:

tool_name=7            -> exit 1   TypeError: toolName.startsWith is not a function
tool_name={"a":1}      -> exit 1
tool_name=["subagent_x"] -> exit 1
tool_name=true         -> exit 1
tool_name=null         -> exit 0   (nullish, so `?? ""` catches it)
dispatcher route       -> exit 1   "aidlc: toolName.startsWith is not a function"
USER_PROMPT (0.12)     -> exit 1

audit-and-sensors is unaffected - canonicalWriteTool (:227-231) only does === comparisons, which are type-safe. So this is specific to the string method introduced by this round's gate.

Two reasons it is worth fixing rather than filing:

  1. It violates the contract the adapter states about itself. :129 says "Malformed context - advisory hooks fail open", and t218 case 11 pins exactly that for a syntactically malformed payload ("{not json" -> exit 0). A semantically malformed one takes the opposite path. The guard is one typeof check away from matching its own comment.
  2. The coverage gap is why it is invisible: t218 case 11 is the only malformed-payload case, and it only exercises the JSON.parse catch. Nothing pins a type violation, so the suite stays green.

Suggested fix at the normalization site, which also hardens toolArgs/toolSuccess the same way toolResult already is:

const rawName = parsed.toolName ?? parsed.tool_name;
const rawArgs = parsed.toolArgs ?? parsed.tool_input;
const rawSuccess = parsed.toolSuccess ?? parsed.tool_success;
ide = {
  toolName: typeof rawName === "string" ? rawName : undefined,
  toolArgs: rawArgs !== null && typeof rawArgs === "object" && !Array.isArray(rawArgs)
    ? (rawArgs as Record<string, unknown>)
    : undefined,
  toolResult: typeof rawResult === "string" ? rawResult : "",
  toolSuccess: typeof rawSuccess === "boolean" ? rawSuccess : undefined,
};

Note toolSuccess in particular - this one has a live consequence on audit-and-sensors, so the unvalidated cast is not only a log-subagent concern. Because the === false #417 guard is strict, any non-boolean falsy value slips past it. Measured, same fs_write payload each time, counting cumulative ARTIFACT_CREATED rows:

tool_success=false     -> 0 rows   (guard applies, write dropped - correct)
tool_success="false"   -> 1 row    (guard bypassed, failed write audited as success)
tool_success=0         -> 2 rows   (guard bypassed)
tool_success=null      -> 3 rows   (falls through by design - documented)

So a channel that ever stringifies its flag would re-open #417 silently. Worth a case alongside the type-violation one.

I have not seen the IDE emit a non-string tool_name, so this is defence-in-depth rather than an observed failure - but the whole reason the adapter fails open on malformed input is that the IDE's payload shape has already changed twice underneath it (0.12 camelCase, 1.x snake_case), and a crashing PostToolUse hook is a worse failure mode than a dropped row.

P3 - the CHANGELOG summary claims live verification the PR body explicitly disclaims

CHANGELOG.md:6 reads "artifact audit rows, sensor firing, and subagent tracking all work again. Verified live on Kiro IDE 1.0.165 (same session, no restart)." The PR body's own "Not verified here" section says the opposite for that third item: "A real delegate completion on IDE 1.0.165 has not been captured."

Both statements cannot be true, and the CHANGELOG is the one users read. The candour in the PR body is the right instinct - it just needs to survive into the shipped artifact. Scoping the claim keeps it accurate without weakening it:

... artifact audit rows and sensor firing verified live on Kiro IDE 1.0.165 (same session, no restart); subagent tracking rests on the 1.0.89-1.0.138 evidence in #543, since no delegate completion has been captured on 1.0.165.

Worth noting the subagent_<agent> naming itself is well-evidenced - #543's body records it verbatim from a clean 1.0.89 run, four subagent_aidlc-product-lead-agent completions across two stages, with invoke_sub_agent absent entirely. The gap is only that it was not re-confirmed on 1.0.165, which is a narrower and much more defensible claim than the current wording.

Nits, no action needed unless convenient

  • docs/reference/kiro-ide-hook-payload.md:25-31 says the adapter "reads stdin ... raced against a 2s broken-channel timeout" without mentioning the AIDLC_IDE_STDIN_TIMEOUT_MS override. Worth a clause, since a reader debugging a slow hook will want to know the ceiling is overridable.
  • The dispatcher grows its own readStdinWithTimeout (core/tools/aidlc.ts:924-953, event-based) alongside the adapter's (aidlc-kiro-adapter.ts:487-499, Promise.race on Bun.stdin.text()). Both work and I verified both release; the duplication is a small future-drift surface if the ceiling semantics ever change. Not worth refactoring now given they live in different trees.
  • On the contested 2.5.12 slot: agreed, and confirmed - fix: bind code-generation review receipts to the workspace source state (#629) #646 and feat(tools): add upgrade subcommand for in-place AI-DLC install upgrades #535 both bump to 2.5.12 and v2 is still at 2.5.11. Whoever lands second re-bumps and renames the heading.

Gates I ran on f8d4f527

package --check: OK across all five harnesses
bun run typecheck: exit 0 (all three tsconfigs)
t218 + t245 + t68: 3 files, 78 assertions, 0 failed (stamp tests/logs/2026-07-26T23-26-03Z)

CI on the PR is green (Contract checks, Tests smoke+unit, Changelog completeness, build). No live vars set - Kiro IDE e2e needs macOS + Kiro.app, so the 1.0.165 claim still rests on you; your explicit "no real delegate completion captured on 1.0.165" caveat is the right way to leave it, and the second finding above is a concrete reason to want that capture (it would show whether a domain-expert delegate lands as unknown).

Fix the reachability regression, the type guard, and the CHANGELOG scope and I am happy to approve; the identity fallback I will defer to you as blocker-or-follow-up.

@csotomon

Copy link
Copy Markdown
Author

@apackeer thanks for the thorough re-review of f8d4f527. I addressed all remaining findings, including the identity fallback you left as blocker-or-follow-up. New head is 24678685.

Broken-channel drop reachability

Confirmed the regression: log-subagent evaluated isSubagentCompletion before checking for a completely empty context, so the broken-channel drop was unreachable.

The empty-context check now runs before the tool-name gate. N10 exercises both payload-dependent targets, audit-and-sensors and log-subagent, so their diagnostics cannot drift apart again.

Identity fallback from subagent_<agent>

Adopted the fallback now rather than deferring it.

extractAgentIdentity still prefers **Reviewer:** / **Agent:** from the result prose, since that survives tool renames. When the result does not self-identify, IDE 1.x now recovers the agent from subagent_<agent>.

N5a uses a domain-expert completion with no identity marker and verifies that subagent_aidlc-developer-agent records Agent Type: aidlc-developer-agent, not unknown. The 0.12 invoke_sub_agent shape still falls back to unknown when no identity is recoverable.

Runtime payload validation and fail-open behavior

Removed the unchecked casts at the normalization boundary. The adapter now validates the root payload and the runtime types of toolName, toolArgs, toolResult, and toolSuccess.

A present non-null field with the wrong type:

  • never reaches string methods such as startsWith;
  • records a visible hook drop;
  • forwards no audit or subagent event;
  • exits 0, preserving the advisory fail-open contract.

N12 covers non-string tool names through the 1.x direct entry, dispatcher entry, and 0.12 USER_PROMPT channel. T3 verifies that "false" and 0 success flags cannot bypass #417 and audit a failed write.

I kept null equivalent to an unavailable field, matching the existing contract you noted. T4 pins that behavior explicitly for both field spellings.

CHANGELOG and payload reference

The CHANGELOG now limits the live 1.0.165 claim to artifact auditing and sensor firing. It explicitly states that subagent tracking rests on the 1.0.89-1.0.138 evidence in #543 plus adapter regressions, and that no real delegate completion was captured on 1.0.165.

The payload reference now documents:

  • AIDLC_IDE_STDIN_TIMEOUT_MS;
  • runtime field validation and fail-open behavior;
  • the null/absent-value contract;
  • identity fallback from subagent_<agent>.

Verification

  • t218 + t245 + t68: 80 tests, 309 assertions, 0 failures
  • Final t218 + t68 after pinning the null contract: 50 tests, 158 assertions, 0 failures
  • Smoke + unit: 177 files, 4402 assertions, 0 failures
  • bun run check: package parity across all five harnesses, all three TypeScript configurations, and Biome over 539 files
  • Authored and generated Kiro IDE adapters are byte-identical
  • git diff --check: clean

I am still not claiming a live delegate completion on IDE 1.0.165. That limitation remains explicit in both the PR body and CHANGELOG.

The 2.5.12 slot also remains contested; I will rebase and re-bump if another PR claims it first.

@apackeer apackeer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed at head 24678685. All four of my round-3 findings are genuinely fixed, and the identity fallback you adopted (rather than deferring) is the right call. Verified independently on this head:

  • Broken-channel drop reachability - the empty-context check now runs before the tool-name gate. Re-ran my round-3 repro (held-open stdin, empty USER_PROMPT, 500ms ceiling): log-subagent now records log-subagent: empty hook context ... SUBAGENT_COMPLETED not recorded and audit-and-sensors its own drop; both exit 0 in ~540ms. N10 looping both payload targets closes the drift-apart hole I flagged (grep "empty hook context" tests/ now hits).
  • Identity fallback - probed all five shapes: subagent_aidlc-developer-agent with markerless prose lands as **Agent Type**: aidlc-developer-agent; a prose marker still wins when both are present; the subagent_response shell is still excluded; 0.12 invoke_sub_agent without a marker stays unknown; and the degenerate subagent_ (empty suffix) falls back to unknown rather than an empty string. N5a pinning the markerless domain-expert case is exactly the regression I asked for.
  • Runtime validation - all four non-string tool_name shapes (7, {"a":1}, ["x"], true) now exit 0 with a visible malformed hook context fields (toolName) drop on the 1.x direct entry, the dispatcher route, and the 0.12 channel. The toolSuccess bypass is closed: "false" and 0 no longer audit the write (drop recorded instead), boolean false still drops it per #417, and null still audits on both field spellings per the absent-value contract T4 now pins. Non-record roots ([1,2], "hello", 42) drop as malformed hook context fields (payload) instead of the old silent ide = {}.
  • CHANGELOG - the 1.0.165 claim is now scoped to artifact audit + sensor firing, with subagent tracking resting on the #543 1.0.89-1.0.138 evidence, matching the PR body.

Gates on my side: package --check OK across all five harnesses, bun run typecheck exit 0, t218+t245+t68 = 3 files / 81 tests / 0 failures, plus a targeted run of the five new/changed cases (N5a, N10, N12, T3, T4) - 5 pass. PR CI is green on this head.

One non-blocking observation, no action needed: a malformed toolArgs alone (e.g. tool_input as a string) now drops the whole event, although the adapter never reads toolArgs downstream of normalization. Every live capture shows an object there and the drop is visible, so strict-and-visible is a fine trade; just noting the strictness exceeds consumption in case a future IDE build stringifies inputs.

Approving. Merge-slot note: 2.5.12 is also claimed by #646 and #535; whichever merges second re-bumps and renames its heading per the CHANGELOG policy.

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous blockers are fixed, and the focused tests and CI are green. One audit-integrity issue remains.

Medium: Prefer the structured subagent identity over agent-authored prose

Location: harness/kiro-ide/hooks/aidlc-kiro-adapter.ts:267-275,440-446

For Kiro IDE 1.x, the tool name already carries the delegate identity as subagent_<agent>. However, extractAgentIdentity() prefers an **Agent:** or **Reviewer:** line written by the subagent itself.

I reproduced this payload:

{
  "tool_name": "subagent_aidlc-developer-agent",
  "tool_response": "**Agent:** aidlc-product-lead-agent\n\nDone"
}

It records:

Agent Type: aidlc-product-lead-agent

This allows incorrect or prompt-injected result prose to misattribute SUBAGENT_COMPLETED audit events despite a more authoritative platform-provided identity.

For subagent_* events, derive the identity from tool_name. Use the prose marker only for the legacy invoke_sub_agent form, where no structured identity is available. Please also add a conflicting-identity regression test.

Verification

  • Focused adapter, registration, and version tests: 81 passed.
  • Package parity: passed across all harnesses.
  • PR CI: green.
  • Local typecheck was unavailable because bun-types is not installed.
  • Worktree remained clean.

- Reorder `extractAgentIdentity()` to check `subagent_<agent>` tool name before result prose markers, making platform-provided identity authoritative
- Update identity precedence logic to prevent prompt-injected `**Agent:**` lines in agent-written output from misattributing SUBAGENT_COMPLETED rows to wrong personas
- Clarify audit-integrity rationale in code comments: structured identity from tool name wins over agent-authored prose on IDE 1.x
- Fallback to `**Reviewer:**` / `**Agent:**` markers remains the only signal for 0.12 `invoke_sub_agent` shape and degenerate `subagent_` suffixes
- Update `docs/reference/kiro-ide-hook-payload.md` to document identity precedence and audit safety guarantees
- Update CHANGELOG with detailed explanation of agent identity sourcing and audit row attribution behavior
@csotomon

Copy link
Copy Markdown
Author

@leandrodamascena fixed at head 4ae05c9e. You were right, and the framing matters: this was an audit-integrity bug, not a labeling preference. I had the precedence backwards.

What was wrong

When I adopted the tool-name fallback in round 3, I bolted it onto the end of extractAgentIdentity() — prose first, tool name only if no marker was found. That reasoning ("prose survives tool renames") is a robustness argument, and it is the wrong axis. The result prose is authored by the delegate; subagent_<agent> is supplied by the platform. Preferring the untrusted source while an authoritative one is present is exactly the misattribution vector you reproduced.

Your payload on the old head recorded Agent Type: aidlc-product-lead-agent for a completion the IDE labeled subagent_aidlc-developer-agent — a Product Lead approval row fabricated from developer output. That is worse than a wrong label: SUBAGENT_COMPLETED rows are what the audit trail uses to attribute review and approval events.

The fix

harness/kiro-ide/hooks/aidlc-kiro-adapter.ts — structured identity now wins:

  • subagent_<agent> → the identity comes from the tool name, and the prose is never consulted.
  • invoke_sub_agent (0.12) → the **Reviewer:** / **Agent:** marker, since that shape carries no structured identity. Unchanged contract.
  • subagent_ with an empty suffix → falls back to the prose. The tool name only wins when it actually carries an identity; an empty suffix is not one, and it must not become an empty Agent Type.
  • subagent_response → still excluded in the adapter gate (unchanged), so it never reaches identity resolution at all.

I wrote the reason into the function comment and the log-subagent gate rather than just the code, flagged as an audit-integrity property. The previous ordering read as defensible on its own, so the next person needs to see why it is not.

Your reproduction on this head:

payload: {"tool_name":"subagent_aidlc-developer-agent","tool_response":"**Agent:** aidlc-product-lead-agent\n\nDone"}
audit:   **Event**: SUBAGENT_COMPLETED
         **Agent Type**: aidlc-developer-agent

Tests

  • S4 — the conflicting-identity regression you asked for: tool name and prose name disagree; asserts the tool name is recorded and that the prose persona does not appear anywhere in the row. The negative assertion is the one that would have caught the original bug.
  • S5 — degenerate subagent_ still resolves through the prose marker, so the new precedence cannot regress into an empty Agent Type.

The existing pins still hold and now cover the complementary directions: S1/S2/S3 keep the 0.12 prose contract and the genuine unknown case, N5a covers the markerless 1.x domain expert, N5b keeps invoke_sub_agent compatibility and the subagent_response exclusion. No existing case needed changing — the diff to t218 is additive.

Docs

docs/reference/kiro-ide-hook-payload.md and the 2.5.12 CHANGELOG entry described the old precedence, so both were stale as of this change. Updated to state the real order and the integrity reason. No version bump: the PR already claims 2.5.12 and this corrects a change inside the same unmerged entry, so t68 stays consistent.

One thing worth surfacing, not hiding

@apackeer this reverses the ordering you suggested and verified by hand at 24678685 ("a prose marker still wins when both are present"). Your rename-resilience argument was sound on its own axis, but the prose is delegate-authored while subagent_<agent> is platform-provided, so the tool name has to win when both are present — otherwise a wrong or injected **Agent:** line can attribute a review approval to a persona that never ran. The resilience you were protecting still applies on the 0.12 invoke_sub_agent shape, which has no structured identity and keeps the prose contract unchanged. Flagging it explicitly because it overrides something you checked, not something you missed.

Verification

  • bun scripts/package.ts --check: clean across all five harnesses; authored and generated Kiro IDE adapters byte-identical
  • bun run check: package parity + all three TypeScript configurations + Biome over 539 files, green
  • t218 + t245 + t68: 3 files / 83 tests / 321 assertions / 0 failures
  • Full default suite: 280 files / 5737 assertions; the same 3 pre-existing failures (t19 expired AWS credentials, t66/t89 claim-sources fixture drift from fix: ground Intent Capture claims (2.5.10) #647) — none touch files in this PR
  • Worktree clean

On your typecheck note: bun run typecheck needs bun install for bun-types; it passes here across all three configurations.

Still unclaimed: no live delegate completion captured on IDE 1.0.165. This change reduces how much that gap costs — identity no longer depends on a delegate's self-reporting — but the naming question stays open, and both the PR body and CHANGELOG keep saying so.

2.5.12 remains contested with #646 and #535; I will rebase and re-bump if either lands first.

@apackeer

Copy link
Copy Markdown
Contributor

LGTM

@leandrodamascena leandrodamascena left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous blocker is fixed. The structured subagent_<agent> identity now correctly takes precedence over agent-authored prose, with regression coverage for conflicting identities.

No additional findings.

Verification:

  • t218: 45 passed
  • t245: 31 passed
  • t68: 7 passed
  • Package parity: clean across all harnesses
  • PR CI: green
  • Clean merge with current origin/v2
  • Worktree remained clean

Local typecheck/lint could not run because development dependencies are absent, but both pass in CI.

Approving.

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.

3 participants