@event4u/agent-config 9.32.0 → 9.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +85 -72
  3. package/dist/agent-src/commands/agent-handoff.md +15 -3
  4. package/dist/agent-src/commands/review/routing.md +3 -4
  5. package/dist/agent-src/contexts/communication/rules-auto/reply-close-mechanics.md +65 -0
  6. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +3 -0
  7. package/dist/agent-src/rules/delegation-policy.md +6 -0
  8. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
  9. package/dist/agent-src/scripts/roadmap_gates.ts +107 -3
  10. package/dist/agent-src/skills/corpus-grounding/SKILL.md +21 -12
  11. package/dist/agent-src/skills/design-tokens/SKILL.md +15 -0
  12. package/dist/agent-src/skills/docker/SKILL.md +11 -0
  13. package/dist/agent-src/skills/laravel/SKILL.md +11 -0
  14. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +24 -0
  15. package/dist/agent-src/skills/review-routing/SKILL.md +23 -6
  16. package/dist/agent-src/skills/rule-writing/SKILL.md +23 -0
  17. package/dist/agent-src/skills/skill-writing/SKILL.md +80 -2
  18. package/dist/agent-src/skills/systematic-debugging/SKILL.md +5 -3
  19. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +24 -0
  20. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  21. package/dist/agent-src/templates/scripts/ownership-map.example.yml +1 -1
  22. package/dist/cli/registry.js +4 -4
  23. package/dist/cli/registry.js.map +1 -1
  24. package/dist/cli-delegate/chunk-HTIU3W56.js +66 -0
  25. package/dist/cli-delegate/{chunk-2WELHO3P.js → chunk-I5EAFRII.js} +35 -0
  26. package/dist/cli-delegate/{chunk-ITIQSGOQ.js → chunk-MA4GFWDY.js} +7 -4
  27. package/dist/cli-delegate/{chunk-PFBV6XRT.js → chunk-Q26HT4QT.js} +1 -63
  28. package/dist/cli-delegate/{chunk-542DZR4P.js → chunk-REMEZSVP.js} +1660 -146
  29. package/dist/cli-delegate/{chunk-3EWYC3YK.js → chunk-UEZ7D35A.js} +4 -3
  30. package/dist/cli-delegate/{chunk-LVRYBKTI.js → chunk-YPAWXXXR.js} +158 -0
  31. package/dist/cli-delegate/cmd_conformance.js +256 -226
  32. package/dist/cli-delegate/cmd_doctor.js +3 -2
  33. package/dist/cli-delegate/cmd_export.js +1 -1
  34. package/dist/cli-delegate/cmd_handoff.js +21 -28
  35. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  36. package/dist/cli-delegate/cmd_prune.js +1 -1
  37. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  38. package/dist/cli-delegate/cmd_session_recycle.js +20 -5
  39. package/dist/cli-delegate/cmd_settings_get.js +4 -2
  40. package/dist/cli-delegate/cmd_settings_migrate.js +1 -1
  41. package/dist/cli-delegate/cmd_settings_set.js +5 -3
  42. package/dist/cli-delegate/cmd_sync.js +1 -1
  43. package/dist/cli-delegate/cmd_uninstall.js +1 -1
  44. package/dist/cli-delegate/cmd_validate.js +1 -1
  45. package/dist/discovery/deprecation-report.md +1 -1
  46. package/dist/discovery/discovery-manifest.json +17 -17
  47. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  48. package/dist/discovery/discovery-manifest.summary.md +2 -2
  49. package/dist/discovery/dormancy-report.md +17 -0
  50. package/dist/discovery/orphan-report.md +1 -1
  51. package/dist/discovery/packs.json +3 -3
  52. package/dist/discovery/trust-report.md +1 -1
  53. package/dist/discovery/workspaces.json +3 -3
  54. package/dist/hooks/dispatch.js +114 -7
  55. package/dist/install/install.mjs +7 -4
  56. package/dist/mcp/registry-manifest.json +3 -3
  57. package/dist/mcp/server.json +2 -2
  58. package/dist/mcp/server.mjs +3755 -1168
  59. package/docs/guidelines/agent-infra/failure-signatures.md +47 -12
  60. package/package.json +2 -2
  61. package/src/agent-src/scripts/archive_completed_roadmaps.ts +35 -1
  62. package/src/agent-src/scripts/roadmap_gates.ts +107 -3
  63. package/src/agent-src/templates/scripts/ownership-map.example.yml +1 -1
  64. package/src/config/cost-parity-budget.json +167 -0
  65. package/src/config/gate-coverage.yml +44 -5
  66. package/src/config/quorum-attendance-budget.json +39 -0
  67. package/src/scripts/_cli/cmd_doctor.ts +69 -1
  68. package/src/scripts/_cli/cmd_session_recycle.ts +25 -4
  69. package/src/scripts/_dispatch.bash +5 -3
  70. package/src/scripts/_lib/env_kill_switch.ts +37 -0
  71. package/src/scripts/_lib/orchestration_record.ts +45 -0
  72. package/src/scripts/_lib/security_lint.ts +18 -4
  73. package/src/scripts/_lib/self_repair.ts +60 -0
  74. package/src/scripts/_lib/self_repair_store.ts +65 -5
  75. package/src/scripts/_lib/subagent_capsule.ts +78 -1
  76. package/src/scripts/ai_council/clients.ts +31 -0
  77. package/src/scripts/ai_council/events_log.ts +130 -8
  78. package/src/scripts/ai_council/orchestrator.ts +50 -3
  79. package/src/scripts/ai_council/pricing.ts +48 -1
  80. package/src/scripts/ai_council/prompts.ts +165 -3
  81. package/src/scripts/ai_council/quorum.ts +30 -0
  82. package/src/scripts/ai_council/session.ts +11 -0
  83. package/src/scripts/ai_team/review_gate.ts +2 -2
  84. package/src/scripts/ask_transport.ts +20 -1
  85. package/src/scripts/build_discovery_manifest.ts +165 -0
  86. package/src/scripts/cache_realization_report.ts +38 -0
  87. package/src/scripts/check_claims.ts +51 -2
  88. package/src/scripts/check_release_highlights.ts +7 -1
  89. package/src/scripts/ci_time_ratio.ts +3 -1
  90. package/src/scripts/conformance_scan.ts +105 -0
  91. package/src/scripts/cost/track.mjs +39 -0
  92. package/src/scripts/cost_summary.ts +104 -1
  93. package/src/scripts/council_cli.ts +131 -13
  94. package/src/scripts/discovery_graph.ts +201 -27
  95. package/src/scripts/dispatch_economy_report.ts +5 -2
  96. package/src/scripts/dispatch_r2_reviewer.ts +102 -1
  97. package/src/scripts/install.ts +20 -6
  98. package/src/scripts/lint_budget_ownership.ts +81 -0
  99. package/src/scripts/lint_handoffs.ts +92 -2
  100. package/src/scripts/lint_hedge_words.ts +317 -0
  101. package/src/scripts/lint_mcp_config_security.ts +1 -1
  102. package/src/scripts/lint_skill_frontmatter_safety.ts +56 -2
  103. package/src/scripts/lint_workflow_paths.ts +384 -0
  104. package/src/scripts/preamble_byte_census.ts +9 -1
  105. package/src/scripts/report_carrier_divergence.ts +10 -0
  106. package/src/scripts/report_conformance_funnel.ts +40 -0
  107. package/src/scripts/schemas/subagent.schema.json +3 -2
  108. package/src/scripts/self_repair_cli.ts +26 -1
  109. package/src/scripts/skill_linter.ts +58 -2
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "metadata": {
8
8
  "description": "Bootstrap shim for the event4u agent-config suite. This plugin ships ONLY the dispatcher hooks plus one install-pointer skill — ALL content (skills, rules, commands) is installed via `npx -y @event4u/agent-config init`, which writes the file projection AND registers the same hooks in ~/.claude/settings.json. DEPRECATED as a content channel; kept so the marketplace listing stays discoverable and existing installs keep working hooks.",
9
- "version": "9.32.0",
9
+ "version": "9.34.0",
10
10
  "keywords": [
11
11
  "agent-config",
12
12
  "skills",
package/CHANGELOG.md CHANGED
@@ -314,107 +314,120 @@ Entry-shape contract: [`docs/contracts/CHANGELOG-conventions.md`](docs/contracts
314
314
  > keeps the active era under the 250-line drift cap enforced by
315
315
  > `tests/test_changelog_eras.py`.
316
316
 
317
- # Era: 9.31.x — current
317
+ # Era: pre-9.34.0 — archived
318
318
 
319
- > Started at `9.31.0`. Full entries live inline below.
319
+ > All entries before `9.34.0` live in
320
+ > [`docs/archive/CHANGELOG-pre-9.34.0.md`](docs/archive/CHANGELOG-pre-9.34.0.md).
321
+ > The archive is read-only; git tags remain the canonical
322
+ > source for what shipped. Splitting them out of the main file
323
+ > keeps the active era under the 250-line drift cap enforced by
324
+ > `tests/test_changelog_eras.py`.
325
+
326
+ # Era: 9.34.x — current
327
+
328
+ > Started at `9.34.0`. Full entries live inline below.
320
329
  > The drift test caps this era at 250 lines of entry body; growth past
321
- > that forces a new era split (`# Era: 9.32.x`, etc.) — see
330
+ > that forces a new era split (`# Era: 9.35.x`, etc.) — see
322
331
  > [`docs/contracts/CHANGELOG-conventions.md § Era splits`](docs/contracts/CHANGELOG-conventions.md).
323
332
 
324
- ## [9.32.0](https://github.com/event4u-app/agent-config/compare/9.31.0...9.32.0) (2026-08-10)
333
+ ## [9.34.0](https://github.com/event4u-app/agent-config/compare/9.33.0...9.34.0) (2026-08-11)
325
334
 
326
335
  ### Release highlights
327
336
 
328
337
  <!-- Curated head: fill before merge, keep it under 10 lines, and leave `_none_` where it is genuinely the answer. The generated log below is unchanged. -->
329
- - **Behaviour changes:** _auto-derived, rewrite before merge:_ rule/schema diffs, breaking commits or removed public surface in 1f01490, e05de77.
330
- - **Default changes + migration:** _none_
331
- - **Security and correctness:** _none_
338
+ - **Behaviour changes:** _none_
339
+ - **Default changes + migration:** _auto-derived, rewrite before merge:_ commits naming a default, migration or migrate in 4420340.
340
+ - **Security and correctness:** _auto-derived, rewrite before merge:_ security-scoped commits in 4420340, b2fc429.
332
341
  - **Honest nulls:** _none_
333
342
  - **Known limitations:** _none_
334
343
 
335
344
  ### Features
336
345
 
337
- * **work-engine:** route both red-check lanes through the self-fix loop ([10c8f7e](https://github.com/event4u-app/agent-config/commit/10c8f7eafbf21fc03a5f301163c941a20df4a885))
338
- * **work-engine:** bounded self-fix loop primitive + executable dod schema ([1f01490](https://github.com/event4u-app/agent-config/commit/1f0149035c1a27729b729dab1dedd861ccdf9812))
339
- * **envelope:** drift anchor, scripted grounding, verbatim error strings ([6c9187e](https://github.com/event4u-app/agent-config/commit/6c9187e30f41c87553764759096a3f7f4d19a126))
340
- * **envelope:** successor tailoring, mandatory failed_approaches, data-never-instruction boundary ([b03beac](https://github.com/event4u-app/agent-config/commit/b03beac1ad1325253c89a63eac9abc9f051269d3))
341
- * **orchestration:** report per-field provenance for host capabilities ([f4958de](https://github.com/event4u-app/agent-config/commit/f4958decf4a70b445a38747bff768ab449877d52))
346
+ * **handoff-envelope:** a checkable off-limits path list, and a validated reversibility tag ([33fd14c](https://github.com/event4u-app/agent-config/commit/33fd14ca8c8aa3ade0081e5f5bf33b6baef6f3f7))
347
+ * **r2-dispatch:** tell the three stale-artefact states apart, and route each to its contract path ([ca417a1](https://github.com/event4u-app/agent-config/commit/ca417a13ebd7890bbefedec89399add96a3f8eca))
348
+ * **lint-handoffs:** make a blank Open-questions section a finding, not a pass ([a22534b](https://github.com/event4u-app/agent-config/commit/a22534bbf8e80eb5e32d7e547651b8efa55a47dc))
349
+ * **self-repair:** bound record creation per source, and count what the cap refuses ([76aa7b5](https://github.com/event4u-app/agent-config/commit/76aa7b56dd53212f0b4f5d556cae27054bb2a358))
350
+ * **discovery:** add a dormancy report that names an unavailable signal ([99cfa2f](https://github.com/event4u-app/agent-config/commit/99cfa2f632320b7e1300e0d539292e1c038437a6))
351
+ * **discovery-graph:** add a zero-inbound query, per-pass stats and error containment ([ed3d532](https://github.com/event4u-app/agent-config/commit/ed3d5325285476e86fae62c679b6d20cbbff923f))
352
+ * **lint-hedge-words:** diff-scoped advisory hedge lint with a declared stage ([364ef70](https://github.com/event4u-app/agent-config/commit/364ef707dce9e29917d4bc9f5d2b2aeedc4572da))
353
+ * **claims:** bind six external research cites, gate the successor pointer ([37cc8fe](https://github.com/event4u-app/agent-config/commit/37cc8fee84343b8cb92edf7778b97ce235086dc8))
354
+ * **skill-linter:** gate Security constraints on script-bearing skills ([b2fc429](https://github.com/event4u-app/agent-config/commit/b2fc4292540fe140ca9ead90b32bebaac133d2d0))
355
+ * **council:** check a synthesis verdict against its own stance tally ([37103c3](https://github.com/event4u-app/agent-config/commit/37103c3e68efd749dd1d386f3402b8bc0679255d))
342
356
 
343
357
  ### Bug Fixes
344
358
 
345
- * **gates:** stop the R2 review-input copy from laundering fixture secrets ([c63d350](https://github.com/event4u-app/agent-config/commit/c63d3502013e7bc261096d02e2c102a52e2f1ec7))
346
- * **golden:** teach the replay the delegated bad-verdict surface, re-lock baselines ([1ff480c](https://github.com/event4u-app/agent-config/commit/1ff480c6adeb88af32198a19d41838e2f9f5dcc0))
347
- * **envelope:** close the eight R2 review findings ([b0d153c](https://github.com/event4u-app/agent-config/commit/b0d153cb3084dac2a20093b73d28a3ce3c12324c))
348
- * **tests:** type the self-fix state helpers against the wire types ([60d2724](https://github.com/event4u-app/agent-config/commit/60d272419b07b5abea1ce6ffa430faf127fcde4a))
349
- * **handoff:** stop offering the caller its own empty session ([b72f772](https://github.com/event4u-app/agent-config/commit/b72f772a03207aa4feeded3acd5b68823e3fd79c))
350
- * **tests:** narrow regex captures in the capability parity block ([6f27615](https://github.com/event4u-app/agent-config/commit/6f276154b83781404d6551113c6d43e5cf040423))
351
- * **carrier:** a paths: disagreement is a delivery defect, not inert metadata ([318fb81](https://github.com/event4u-app/agent-config/commit/318fb818eb543f27fc4c3b138ce762dc8c9e9640))
352
-
353
- ### Documentation
354
-
355
- * **review:** re-bind the findings artefact to the post-fix scope ([7a9bbbb](https://github.com/event4u-app/agent-config/commit/7a9bbbb2d7f4f04786771914bf7b3b68aba23e10))
356
- * **review:** record the R2 completion-review findings before fixing them ([27322e8](https://github.com/event4u-app/agent-config/commit/27322e876c76f96d6a67131f9e96a1759290a8f5))
357
- * **roadmap:** P2.2 build half shipped, its threshold unevaluated with a blocker ([1e478fd](https://github.com/event4u-app/agent-config/commit/1e478fdc317fdf12df001c1b98ef264df54a3742))
358
- * **contracts:** the flow contract documents the self-fix loop and the dod slot ([0ebaaba](https://github.com/event4u-app/agent-config/commit/0ebaabae9e12c75446efefc5be6f6b33b9d0435f))
359
- * **roadmap:** capability-answerability Phase 1 closed, blocker narrowed to one question ([c37af04](https://github.com/event4u-app/agent-config/commit/c37af045ecbf62b31e6878befdc88d763498d7a9))
360
- * **rules:** delegation-policy names the capability provenance check ([e05de77](https://github.com/event4u-app/agent-config/commit/e05de771d74e8284e2e64e91e33be66b1bd15e23))
361
- * **contracts:** the host-capability contract documents six fields, not five ([566b984](https://github.com/event4u-app/agent-config/commit/566b984681d1a1a5ef76643fa6f90f297c284e81))
362
-
363
- ### Tests
364
-
365
- * **work-engine:** pin the loop contract and update the changed halt shapes ([eb88aa6](https://github.com/event4u-app/agent-config/commit/eb88aa695121a01c0c1e10de9804f1df3424c334))
359
+ * **tests:** satisfy exactOptionalPropertyTypes in the two new spec files ([4f9cd9b](https://github.com/event4u-app/agent-config/commit/4f9cd9b46370920daa0a39aa5cb10a65b70e70a4))
360
+ * **security-lint:** report the corpus a gate actually walked, not the shared default ([4420340](https://github.com/event4u-app/agent-config/commit/4420340cba6f5f091d2b836f21e747878fd71ed3))
361
+ * **roadmap-archival:** never rewrite path strings inside frozen records ([8805814](https://github.com/event4u-app/agent-config/commit/88058140884f14c275737e0e612daee005db2bd7))
362
+ * **review:** re-align the round-3 dispositions and escape the table pipes ([c569ba5](https://github.com/event4u-app/agent-config/commit/c569ba54c95e0d16ab315cb7a5223a33d34f3268))
363
+ * **review:** close the round-3 findings, including four npm ci sites the verify regex could not see ([57867df](https://github.com/event4u-app/agent-config/commit/57867dfd5dbb2d00bbc02deace9c0312e999b423))
364
+ * **review:** repair the round-2 findings, including a self-falsifying cost table ([90cb3ca](https://github.com/event4u-app/agent-config/commit/90cb3caff641d4bb4ef515813b7ec44af92d6636))
365
+ * **lint-hedge-words:** harden the gate scope so the coverage ratchet holds ([d0c9d9e](https://github.com/event4u-app/agent-config/commit/d0c9d9e9b6812d59a7a89e8287bc4b89800aa28d))
366
+ * **council:** let the reserved split label yield to a real option ([30328d8](https://github.com/event4u-app/agent-config/commit/30328d8811bd65f35f2bf42bc6b4fbe8cc76fa4c))
367
+ * **council:** address the R2 completion-review findings ([02c786c](https://github.com/event4u-app/agent-config/commit/02c786c28646da19e8c9cee94ab49b826f91a611))
368
+ * **review:** repair the seven completion-review findings ([efa7cc1](https://github.com/event4u-app/agent-config/commit/efa7cc1d150e0679678db22252dedbbcb838051f))
369
+ * address the nine R2 completion-review findings ([02cdfbb](https://github.com/event4u-app/agent-config/commit/02cdfbb28f04e432cccff7c14ba9f12e177feb0e))
370
+ * **ci-time-ratio:** correct the output-path docstring and register a task target ([1e28e18](https://github.com/event4u-app/agent-config/commit/1e28e1884ea8be49f6b8412e3b1c2d8c7240e9c9))
366
371
 
367
- ### Other
368
-
369
- * **carrier:** re-bind to the post-merge scope — the fixed point ([884104c](https://github.com/event4u-app/agent-config/commit/884104c15d8257579a320932c671b489d96e7b06))
370
- * **carrier:** re-bind the R2 round to the shipping content, all 6 terminal ([9bbfbc3](https://github.com/event4u-app/agent-config/commit/9bbfbc35388f54c77fdae27315025843a6ea79f5))
371
- * **carrier:** R2 completion review round 1 — 6 findings, all open ([75be940](https://github.com/event4u-app/agent-config/commit/75be940f4f9bcbf71e1f1248467637fc51c6b63b))
372
+ ### Performance
372
373
 
373
- Tests: 12818 (+89 since 9.31.0)
374
+ * **tests:** render the proof page once in build_proof.test.ts ([c4d699b](https://github.com/event4u-app/agent-config/commit/c4d699ba61c4ad5a1a19ca292b4d3218748069b5))
374
375
 
375
- ## [9.31.0](https://github.com/event4u-app/agent-config/compare/9.30.0...9.31.0) (2026-08-10)
376
-
377
- ### Release highlights
378
-
379
- <!-- Curated head: fill before merge, keep it under 10 lines, and leave `_none_` where it is genuinely the answer. The generated log below is unchanged. -->
380
- - **Behaviour changes:** _none_
381
- - **Default changes + migration:** _none_
382
- - **Security and correctness:** _none_
383
- - **Honest nulls:** _none_
384
- - **Known limitations:** _none_
385
-
386
- ### Features
376
+ ### Documentation
387
377
 
388
- * **settings:** give the scripts read path the template-defaults layer ([a67a640](https://github.com/event4u-app/agent-config/commit/a67a640cbf64262d07026853b7b5ead21b509e5d))
378
+ * **dispatch-safety:** close 3.1, 3.2 and 3.3, and record why 3.4 waits for producers ([9efe07e](https://github.com/event4u-app/agent-config/commit/9efe07e11b40eb5231d267f89968045af87163d9))
379
+ * **dispatch-safety:** ADR-109 amendment 4, and close 1.3, 1.4 and 4.1 ([5f7e83a](https://github.com/event4u-app/agent-config/commit/5f7e83ad61488c1098ce5696731664b24a13ac2f))
380
+ * **review:** declare the no-code-surface skip for the org-pack decision ([4a573c0](https://github.com/event4u-app/agent-config/commit/4a573c0565854f0f561de8258eb776b1f51eb1fd))
381
+ * **council:** record the gate-scoped solo-attendance floor as ADR-224 ([686c23d](https://github.com/event4u-app/agent-config/commit/686c23df83b84c33f5bfac5257d1b187a0174f73))
382
+ * **roadmap:** close estate-lifecycle Phases 2-4 and record the two refusals ([f64b7fc](https://github.com/event4u-app/agent-config/commit/f64b7fc87fd6c0701ff5601676f067af2fe024fe))
383
+ * **governance:** separate the two lifecycle vocabularies and answer the deferred field ([212a38c](https://github.com/event4u-app/agent-config/commit/212a38c45622b9ee2329fe2929db2949a2c612de))
384
+ * **decision:** decline the external pack source root ([87f8943](https://github.com/event4u-app/agent-config/commit/87f894310ec1b07d64d86cef7d9b8a3cb5595a0a))
385
+ * **review:** disposition the round-3 findings ([c78afd5](https://github.com/event4u-app/agent-config/commit/c78afd510497d08b3684fc5fe42230b7a82b2af3))
386
+ * **review:** disposition the round-2 findings and preserve both rounds ([59b4561](https://github.com/event4u-app/agent-config/commit/59b45613fdd7eb3ffef275b5062f36c366428015))
387
+ * **review:** re-bind the findings after the gate-hardening fix ([0928422](https://github.com/event4u-app/agent-config/commit/092842250cb6b861e9cc217e0708bc63d411513b))
388
+ * **review:** disposition the seven findings against the repair commit ([910f7c4](https://github.com/event4u-app/agent-config/commit/910f7c4e85886f194f214ceeafe06c2b479b154e))
389
+ * **review:** record the completion-review findings for ci-economy ([d5e1fbd](https://github.com/event4u-app/agent-config/commit/d5e1fbdd3184adc4cd0c4f2a88776ae475e99422))
390
+ * **roadmap:** resolve the council-integrity deferral into a READY follow-up ([94f25c2](https://github.com/event4u-app/agent-config/commit/94f25c25d7911ef9fc177db21dba0e294e7e143a))
391
+ * **review:** re-bind the completion-review findings to the post-fix scope ([0208175](https://github.com/event4u-app/agent-config/commit/0208175e677af8db7632be58e7384209dceae45e))
392
+ * **roadmap:** close 15 of 16 ci-economy steps, cancel the build fan-out ([3a3523d](https://github.com/event4u-app/agent-config/commit/3a3523de29b269b9b580fd9bd071d94cafa0a2cd))
393
+ * **adr:** ADR-223 records no required-check demotion on cost grounds ([6e0d45a](https://github.com/event4u-app/agent-config/commit/6e0d45ae673f2cd8fa8c0e36f09110b4a573de7e))
394
+ * **development:** correct the testing section and document the in-process runner ([53ce1f6](https://github.com/event4u-app/agent-config/commit/53ce1f6fa2ef56ff3bc1737c987dd6e918c81928))
395
+ * **ci-cost-budget:** re-anchor the baseline to CI-recorded figures ([2972545](https://github.com/event4u-app/agent-config/commit/2972545e966319ebf4c632e1931ebc8c084b8816))
396
+ * **review:** R2 completion-review findings for the authoring-contract branch ([d97c428](https://github.com/event4u-app/agent-config/commit/d97c42860ea1ce4b6b63a347c824f00a536d853f))
397
+ * **failure-signatures:** stable ids, per-row drills, capability-claim row ([c18295e](https://github.com/event4u-app/agent-config/commit/c18295e0108955418eabf064502b591432cdeb1a))
398
+ * **skill-writing:** reclassify two unenforced sections, add three patterns ([07d8ce1](https://github.com/event4u-app/agent-config/commit/07d8ce1198699833f0154112831ae7fcf4703ba0))
399
+ * **roadmap:** close council-integrity 2.1 and 3.1, record two premise corrections ([088f7ab](https://github.com/event4u-app/agent-config/commit/088f7ab87de41ecbe21c46d7efefef1ed5f04a60))
400
+
401
+ ### Refactoring
402
+
403
+ * one definition of the env kill-switch predicate ([689cc55](https://github.com/event4u-app/agent-config/commit/689cc5558a55ccab4728175955459973a5b03e4e))
389
404
 
390
- ### Bug Fixes
405
+ ### Build
391
406
 
392
- * **carrier:** a frontmatter-only difference is not body divergence ([c48b6c8](https://github.com/event4u-app/agent-config/commit/c48b6c88cf274e3e24b0795d8fb4d19be7637557))
393
- * **hooks:** replace the fence regex with a scanner, and stop shipping a moving figure ([2fe01e7](https://github.com/event4u-app/agent-config/commit/2fe01e79804c37380e8c968b6efdf71b7d9d33fa))
394
- * **hooks:** per-session pin marker, atomic write, enforced read cap ([67abbb6](https://github.com/event4u-app/agent-config/commit/67abbb63028ab79fa6d04083523fbb6ffb9033c3))
395
- * **scripts:** measure the gate per TURN, and withdraw the figures that were not ([313e665](https://github.com/event4u-app/agent-config/commit/313e665354c1dd0512aceb21475dbc532f6b9ba5))
396
- * **hooks:** turn-end-gate — key the guard on the turn, not the prompt text ([b3392e6](https://github.com/event4u-app/agent-config/commit/b3392e60ceeae500b639040e8849b801fc661008))
407
+ * **toolchain:** enable tsc incremental buildinfo and the eslint cache ([344bf97](https://github.com/event4u-app/agent-config/commit/344bf979e7c355c1a97d7407706f198fd5bf78ee))
397
408
 
398
- ### Documentation
409
+ ### CI
399
410
 
400
- * **carrier:** the header says three classes and there are now four ([1955234](https://github.com/event4u-app/agent-config/commit/195523465db8fabb84be3f4ad27f8293cf5e4ac5))
401
- * **evidence:** classify the 109 carrier divergences, and correct two records ([c2fc109](https://github.com/event4u-app/agent-config/commit/c2fc109f7ca30f621455a9a754803055a0b1a0d9))
402
- * **evidence:** map the scripts settings read path against the server one ([858a256](https://github.com/event4u-app/agent-config/commit/858a25646352fd3603800e5657527591a00e16ae))
403
- * **review:** record the R2 findings on the fix pass, before touching it ([65aee68](https://github.com/event4u-app/agent-config/commit/65aee68691d85bdba928b4de65f4e28025c80852))
404
- * **review:** record the R2 findings for turn-end-gate before fixing anything ([9f204c5](https://github.com/event4u-app/agent-config/commit/9f204c55a73a45bada0baa7925bc000df7bee912))
411
+ * **workflows:** add PR-scoped concurrency and flag the bare npm ci calls ([522c052](https://github.com/event4u-app/agent-config/commit/522c05283ea2f5d4b7ca65c5862d21520291f0db))
405
412
 
406
- ### Build
413
+ ### Chores
407
414
 
408
- * **install:** rebuild the bundle without the worktree symlink poison ([741806c](https://github.com/event4u-app/agent-config/commit/741806c91a9774b1b521ae34487d271d0c5dee93))
409
- * **install:** refresh the committed install bundle ([7bbcfdc](https://github.com/event4u-app/agent-config/commit/7bbcfdce9355708ef6785ec640af64d23a5ac4a0))
415
+ * **roadmap:** regenerate the dashboard after the second main merge ([92bf6fb](https://github.com/event4u-app/agent-config/commit/92bf6fb58e50170176a76d5892098b0d19d21e8c))
416
+ * **dist:** project the reversibility-tag paragraphs into the command tree ([2d33951](https://github.com/event4u-app/agent-config/commit/2d339519eee0dd84b4b876de8222e5503c08d04c))
417
+ * **tests:** drop two dead symbols left by the py2ts parity teardown ([cfecb9d](https://github.com/event4u-app/agent-config/commit/cfecb9dd1466af91791ad60fcdb768a85ab5cf4f))
418
+ * **roadmap:** close council-integrity-followup, open the implementation plan ([6efce85](https://github.com/event4u-app/agent-config/commit/6efce8541f6e3ec48789d2e4dc6333adcfb8148d))
419
+ * **roadmap:** archive install-lifecycle and re-depth its moved links ([6790d85](https://github.com/event4u-app/agent-config/commit/6790d857c0869cf2d08c8d74c149d94dcf0a346b))
420
+ * **tests:** drop the dead tail in the discovery-manifest test ([fe258f9](https://github.com/event4u-app/agent-config/commit/fe258f9da91b594f06837694619a5f6934e5a0dc))
421
+ * **roadmap:** archive the authoring-contract roadmap ([4aea8f7](https://github.com/event4u-app/agent-config/commit/4aea8f7a241a063ed371edd1f53fad31e56340a4))
410
422
 
411
- ### Chores
423
+ ### Other
412
424
 
413
- * **roadmap:** close carrier-layer-convergence Phases 1-2 on the measurement ([bc42809](https://github.com/event4u-app/agent-config/commit/bc42809689634578e52501470f299822624e15ae))
414
- * **roadmap:** archive road-to-scripts-settings-defaults, complete ([e3af4c5](https://github.com/event4u-app/agent-config/commit/e3af4c56c1149a96254f1e7e3c1807a8f53744b5))
415
- * **budget:** re-baseline the packed-tarball cap, 6.9 to 7.8, maintainer-decided ([04ca7d8](https://github.com/event4u-app/agent-config/commit/04ca7d84f93c33999b02361c9baa4e61a181a07c))
425
+ * **council-integrity:** re-bind after merging origin/main a second time ([dd58e0c](https://github.com/event4u-app/agent-config/commit/dd58e0c7c46c27ec0784621096c7f091fd29c44e))
426
+ * **council-integrity:** re-bind after merging origin/main ([eb84ecb](https://github.com/event4u-app/agent-config/commit/eb84ecb6c0bee33c4d9ec43b85b50493badf4b26))
427
+ * **council-integrity:** re-bind the findings artefact to the fixed scope ([267ff3f](https://github.com/event4u-app/agent-config/commit/267ff3f3b4dae46a82bc38a4d1399d93b0304e57))
428
+ * **council-integrity:** record R2 completion-review findings before any fix ([ccf6650](https://github.com/event4u-app/agent-config/commit/ccf6650bbe7287a92e58177d6fe9ef8fc1cbb6a3))
416
429
 
417
- Tests: 12729 (+38 since 9.30.0)
430
+ Tests: 13068 (+133 since 9.33.0)
418
431
 
419
432
  # Era: pre-4.0.0 — archived
420
433
 
@@ -167,9 +167,11 @@ Markdown, no host API). Required fields, in order:
167
167
  ## Contract owed
168
168
  {what the current phase must produce before yielding}
169
169
  ## Decisions
170
- - {decisions taken, with one-line rationale}
170
+ - {decision taken, with one-line rationale} {optionally close the line with
171
+ `[reversible]` or `[irreversible]` — those two spellings exactly}
171
172
  ## Open questions
172
- - {unresolved items the next session must not silently drop}
173
+ - {unresolved items the next session must not silently drop — each as a
174
+ question ending in `?`; write `none` when there genuinely are none}
173
175
  ## Next command
174
176
  {the single command or step to run first on resume}
175
177
  ```
@@ -177,7 +179,11 @@ Markdown, no host API). Required fields, in order:
177
179
  **Resume rule:** a workflow skill's step 0 checks for this file and resumes
178
180
  from its contract (mode-inference table) instead of re-deriving state; a
179
181
  long phase refreshes the file before yielding. Validated by
180
- `lint_handoffs.ts` when present — a missing required field is red.
182
+ `lint_handoffs.ts` when present — a missing required field is red, and so is an
183
+ `## Open questions` section that answers neither way (blank, or a bare `TBD` /
184
+ `TODO` / `...`). A `?`-terminated question passes; so does an explicit `none` —
185
+ the check exists to stop a blank section reading as an all-clear, not to force a
186
+ question where there is none.
181
187
 
182
188
  **Critical-planning-file safety protocol** (applies to HANDOFF.md and agent
183
189
  roadmap edits): read the current file FIRST; take a timestamped backup copy
@@ -225,6 +231,12 @@ do not auto-execute.
225
231
  - **Branch name is critical** — always include it.
226
232
  - **Open tasks are critical** — the new chat needs to know what's left.
227
233
  - **Decisions are important** — prevents the new chat from re-asking settled questions.
234
+ A decision line may close with `[reversible]` or `[irreversible]`, which tells the
235
+ successor "we picked A over B, easy to revisit" apart from "we already migrated the
236
+ data". The tag is optional; those two spellings are the only accepted ones, and a
237
+ near-miss (`[reversble]`, `[Irreversible]`) is a validation error rather than a
238
+ silently untagged line. No claim is made that tagging improves resumption — that
239
+ stays the registered, unmeasured `envelope_resume_success` metric.
228
240
  - **File list is optional** — only include if the new chat will need to edit specific files.
229
241
  - **NEVER render a printed handoff as live markdown** — see Iron Law above.
230
242
 
@@ -59,9 +59,8 @@ changed-file list. The skill:
59
59
  4. Computes overall severity (`high` wins).
60
60
  5. Returns the structured block.
61
61
 
62
- If neither data file exists, the skill returns the generic fallback
63
- from [`reviewer-awareness`](../../rules/reviewer-awareness.md). That is
64
- valid output — do not invent owners.
62
+ If neither data file exists, the skill returns the generic fallback from
63
+ its own common role vocabulary. That is valid output — do not invent owners.
65
64
 
66
65
  ### 3. Present the result
67
66
 
@@ -121,7 +120,7 @@ After the block, ask:
121
120
  ## See also
122
121
 
123
122
  - [`review-routing`](../../skills/review-routing/SKILL.md) — the resolver
124
- - [`reviewer-awareness`](../../rules/reviewer-awareness.md) — role vocabulary + data-source rules
123
+ - [`reviewer-awareness`](../../rules/reviewer-awareness.md) — the rule that routes here: paths and risk over seniority, primary + secondary on medium/high
125
124
  - [`review-routing-data-format`](../../../docs/guidelines/agent-infra/review-routing-data-format.md)
126
125
  — YAML schemas
127
126
  - [`create-pr-description`](../../skills/create-pr:description-only/SKILL.md) —
@@ -23,6 +23,53 @@ hunts for it. One PR → one URL line.
23
23
  - Only when a PR was actually created/updated **this turn**. No PR → no link line.
24
24
  - The raw URL (not a masked markdown link) so it is unmistakable and clickable.
25
25
 
26
+ ## Blocker handover — when the reply leaves work stuck
27
+
28
+ A work reply that stops because something is waiting on the **user** closes
29
+ with the blocker, in the same one summary, above the PR-URL line. Not a second
30
+ summary — this is what "what remains" looks like when what remains is not yours
31
+ to do.
32
+
33
+ `agent-config gates --reply` renders it. Call it, paste what it returns, and
34
+ mirror the labels into the user's language. It prints **nothing** when nothing
35
+ is owned by the user, so it is safe to call unconditionally: the "no blocker →
36
+ no block" rule is a property of the command, not a judgement you have to make.
37
+
38
+ Three obligations the command cannot carry for you:
39
+
40
+ - **The options ARE the blocker decision — never a choice of substitute work.**
41
+ This is the measured failure, not a hypothetical one: a session that ended
42
+ every reply with well-formed numbered options still left its real blockers
43
+ unresolved for nine rounds, because each option block asked *"which work
44
+ should I do while your decision sits there"* (option 3 was reliably "nothing
45
+ more autonomously"). The user answered every one of them with a digit and
46
+ decided nothing. Offering the agent's next task is not a handover.
47
+ - **The action travels, not a pointer to it.** "The three readings are in the
48
+ roadmap" is not a decidable option — the user cannot answer without reading
49
+ first. Where a real choice exists, write the alternatives out, one line each
50
+ for what speaks for them, then the single recommendation line
51
+ ([`user-interaction`](../../../rules/user-interaction.md)).
52
+ - **Owner ≠ agent.** If you could clear it with a tool or a command, it is not
53
+ a blocker, it is unfinished work — do it now. A blocker whose owner is the
54
+ agent, announced instead of executed, is the promissory close that
55
+ [`verify-before-complete`](../../../rules/verify-before-complete.md) forbids.
56
+
57
+ ## Why the blocker handover is not a trailing checklist
58
+
59
+ [`mandated-lines`](../../execution/mandated-lines.md) bans the end-of-reply
60
+ checklist, and this shape has to answer that ban rather than sit beside it.
61
+
62
+ - **It fires only when owed.** Nothing waiting on the user ⇒ nothing emitted,
63
+ enforced by the renderer. It never appears as evidence that blockers were
64
+ checked for — which is exactly the ceremony the ban targets.
65
+ - **The end of the reply IS the decision point here.** The turn stops because
66
+ the decision is the user's. A line emitted where the decision lands is the
67
+ rule's own criterion, not an exception to it.
68
+ - **One blocker in full, the rest as a count.** The blocking one is written
69
+ out; everything else is `N other decisions also wait on you` plus the
70
+ command. A roster is the failure mode — "twelve decisions across two
71
+ documents, ~850 lines" produced no decision at all.
72
+
26
73
  ## Why it does not collide with sibling rules
27
74
 
28
75
  - **`language-and-tone`** last-line rule: a raw URL is language-neutral, so it
@@ -34,6 +81,15 @@ hunts for it. One PR → one URL line.
34
81
  last among interactive elements; the PR URL sits below it as the final line and
35
82
  is not an "open question," so it does not trip the no-trailing-open-question
36
83
  rule.
84
+ - **`ask-when-uncertain`** (one question per turn): reporting N blockers is
85
+ **zero** questions — it is a report. What the turn may carry is at most ONE
86
+ numbered-options block, for the one blocking decision, with one recommendation
87
+ line. Per-blocker questions are the violation that rule names.
88
+ - **`no-cheap-questions`** IL4: the handover ends on `Done when:` — a decidable
89
+ signal, not "shall I continue once you've sorted this out?".
90
+ - **`scope-control`** post-artifact hard stop: the steps address the **user**.
91
+ A handover that routes a slash-command back to the agent ("Blocker X — shall I
92
+ start Phase 1?") is the execution menu that rule forbids, wearing a new label.
37
93
 
38
94
  ## Failure modes
39
95
 
@@ -43,3 +99,12 @@ hunts for it. One PR → one URL line.
43
99
  scroll/search.
44
100
  - Masking the URL behind link text so it is not obviously the PR.
45
101
  - Adding a PR-link line when no PR was created this turn.
102
+ - Closing on a blocker while offering the agent's next task instead of the
103
+ decision the blocker actually needs.
104
+ - Handing over options as a count ("four options, see the template") or as a
105
+ file reference — the user cannot answer either without reading first.
106
+ - Listing every open blocker flat, leaving the user to work out which one is
107
+ the one holding the chain.
108
+ - Reporting the same blocker again, turn after turn, with no resolution path —
109
+ a repeat with nothing moved is itself the finding, and belongs in the reply
110
+ as such.
@@ -68,6 +68,9 @@ v1 forward-compat rule on unknown fields).
68
68
  | `rules_used` | int \| null | **Lean-init extension (L6 demand signal).** Rules the worker actually applied/cited. Validation: never exceeds `rules_carried`. `null` = not measured. Counts only. |
69
69
  | `work_tokens` | int \| null | **Dispatch-economy extension.** Tokens the worker consumed AFTER init — the delta from first worker turn to envelope close. With `init_tokens`, yields the `dispatch_floor` ratio (`src/config/dispatch-economy-metrics.json`). Sourced from the transcript ledger (`_lib/cc_transcript.ts` `billable_input` semantics) by the report/backfill, never model-estimated without the tag below. `null` = not measured. |
70
70
  | `floor_provenance` | enum \| null | **Dispatch-economy extension.** Provenance of the `init_tokens`/`work_tokens` pair: `measured` (transcript ledger) · `estimated`. Auto-defaults to `estimated` when either field is present untagged — mirrors `token_delta_provenance`. `null` = neither field present. |
71
+ | `model_requested` | string \| null | **Served-model-truth extension (`inbox-harvest-2026-08-b-ledger-truth` Phase 1).** The model id that was ASKED for — the id `tier_chosen`, `tier_source`, `session_tier` and the downshift cost-% are all derived from. `null` = not recorded. Id only, never free-form. |
72
+ | `model_served` | string \| null | **Served-model-truth extension.** The model id the provider reported ANSWERING with. `''`/`null` when the transport reports none (every CLI client) — absent is the honest value, not a defect. Attribution only: never route or tier on it. |
73
+ | `model_divergent` | bool \| null | **Served-model-truth extension.** DERIVED, never supplied: `true` when both ids are present and differ (an alias or provider substitution — every tier-derived figure on this line attributes to a model that never ran), `false` when both are present and match. `null` when either is absent, because a comparison that never happened must not read as "checked, and they matched". |
71
74
  | `return_channel_chars` | int \| null | **Dispatch-economy extension (Phase 6.3).** Serialized chars of the tool result returned into the orchestrator context (sync completions, hook-derived count — content never enters the record). Detector for "isolation win refunded through the return channel"; committed cap: `MAX_ENVELOPE_CHARS` (12,000) via `return_channel` in `dispatch-economy-metrics.json`. `null` = async ack. |
72
75
 
73
76
  These routing + cost fields are additive and optional — a line without them is
@@ -66,6 +66,12 @@ primitive → run in-session (clean no-op).
66
66
  2. **Tier-size** each slice (`lite|medium|high`) per the classification context —
67
67
  never the orchestrator's own session tier (the cost win is the per-call
68
68
  downsize; see [`subagent-routing`](../contexts/execution/subagent-routing.md)).
69
+ Where `subagents.model_ceiling` caps the tier and the slice does not fit under
70
+ it, the worker **escalates and never silently delivers the degraded result**;
71
+ the orchestrator re-slices, runs in-session, or surfaces the ceiling to the
72
+ human, and never raises the ceiling itself (class C — `settings:set` refuses
73
+ it by construction). Absent is *uncapped*, never a low cap. Full contract:
74
+ [`subagent-boundary § The model ceiling`](../docs/contracts/subagent-boundary.md).
69
75
  3. **Dispatch** via the form gate + matching `subagent-orchestration` mode
70
76
  (independent → do-in-parallel; ordered → do-in-steps; risk/correctness →
71
77
  + judge; UI-heavy → live-app judge, efficacy-gated).
@@ -128,12 +128,42 @@ function _branch_touched_paths(root: string, base: string): Set<string> | null {
128
128
  return out;
129
129
  }
130
130
 
131
+ /**
132
+ * Paths where a repo-relative path is DATA, not a live reference — so
133
+ * rewriting it falsifies a record instead of keeping a link resolvable.
134
+ *
135
+ * Two classes, and the second is why this predicate exists at all:
136
+ *
137
+ * - `agents/evidence/**` — a committed record of what was true, or of what a
138
+ * reviewer was shown, at one point in time. Retroactively editing it makes it
139
+ * claim something that did not happen, which is the failure
140
+ * `evaluator-independence` exists to prevent ("the prompt ships with the
141
+ * verdict" is worthless if a later sweep can edit the prompt).
142
+ * - `*.patch` anywhere — a patch's `diff --git a/… b/…` and `+++ b/…` lines are
143
+ * the payload. Rewriting them detaches the patch from the commit it records
144
+ * AND makes `git apply` target a path the diff never created.
145
+ *
146
+ * Measured 2026-08-11: archiving one roadmap rewrote four frozen artefacts
147
+ * under `agents/evidence/reviews/`, including the `diff.patch` headers of a
148
+ * recorded review input. Nothing caught it — the sweep reported it as
149
+ * "4 ref(s) migrated", which reads like success.
150
+ */
151
+ const _FROZEN_RECORD_PREFIXES: readonly string[] = ['agents/evidence/'];
152
+
153
+ /** Is `rel` a frozen record whose path strings must not be rewritten? */
154
+ export function _is_frozen_record(rel: string): boolean {
155
+ const norm = rel.split(path.sep).join('/');
156
+ return _FROZEN_RECORD_PREFIXES.some((p) => norm.startsWith(p)) || norm.endsWith('.patch');
157
+ }
158
+
131
159
  /**
132
160
  * Rewrite full-path references `old_rel` → `new_rel` in tracked files.
133
161
  *
134
162
  * Only the exact repo-relative path is rewritten (bare-filename mentions are
135
163
  * left alone). The archived file's own path never matches because the search
136
- * string is the un-archived path.
164
+ * string is the un-archived path. Frozen records are skipped — see
165
+ * `_is_frozen_record`; a link left dangling in a historical record is correct,
166
+ * because the record describes a tree that no longer exists.
137
167
  */
138
168
  function _inbound_ref_rewrite(
139
169
  root: string,
@@ -165,6 +195,10 @@ function _inbound_ref_rewrite(
165
195
  // skip the roadmap file itself
166
196
  continue;
167
197
  }
198
+ if (_is_frozen_record(rel)) {
199
+ // A record of a past state — rewriting it falsifies the record.
200
+ continue;
201
+ }
168
202
  const fp = path.join(root, rel);
169
203
  let text: string;
170
204
  try {
@@ -18,10 +18,34 @@
18
18
  * Blocking a pipeline on "the user has an open decision" would turn a
19
19
  * visibility aid into a second thing to fight.
20
20
  *
21
+ * `--reply` is the same projection shaped for the END OF A CHAT REPLY rather
22
+ * than a terminal pane, and it exists because the pull channel above is only
23
+ * read by someone who already knows to ask. Transcript forensics over three
24
+ * consumer sessions found five "what do I do now?" follow-ups, every one of
25
+ * them next to a blocker the agent HAD reported — as a file reference, as a
26
+ * bare option count, or as a choice of substitute work. What the same corpus
27
+ * also shows is that a tool's output gets carried into the reply verbatim
28
+ * (the dashboard's step count appears word-for-word inside eight option
29
+ * blocks). So the fix is not to make the agent format better from memory; it
30
+ * is to hand it the finished text. See ADR-222.
31
+ *
32
+ * Two properties carry the contract mechanically:
33
+ * - Nothing owned by the user open ⇒ EMPTY output. "No blocker → no block"
34
+ * stops being a judgement call, so the command is safe to call
35
+ * unconditionally at reply-close.
36
+ * - Exactly ONE blocker is rendered in full — the one that unblocks most.
37
+ * Every other one is a single trailing line. The user is never handed a
38
+ * flat list to weigh themselves.
39
+ *
40
+ * Labels are English like the rest of the CLI surface; the agent mirrors them
41
+ * into the user's language when it lifts them into a reply, exactly as it does
42
+ * for `Recommendation:` / `Empfehlung:`.
43
+ *
21
44
  * Invocation (from project root):
22
45
  * ./agent-config gates # decisions owned by the user
23
46
  * ./agent-config gates --all # every open blocker, grouped by owner
24
47
  * ./agent-config gates --json # machine-readable
48
+ * ./agent-config gates --reply # reply-close form; empty when none
25
49
  */
26
50
 
27
51
  import * as path from 'node:path';
@@ -209,6 +233,76 @@ function render(entries: readonly Entry[], all: boolean): string {
209
233
  return lines.join('\n') + '\n';
210
234
  }
211
235
 
236
+ /**
237
+ * Reply-close form: the one blocking decision in full, the rest as one line.
238
+ *
239
+ * Deliberately narrower than `render()`. `Status` and `Owner` are roadmap-file
240
+ * metadata that a reader of a reply cannot act on, so they are dropped; what
241
+ * survives is what the user has to DO and how they will know it is done. The
242
+ * five-field shape stays where it is enforced — in the roadmap file — and does
243
+ * not follow the blocker into prose it would only pad.
244
+ *
245
+ * Returns '' when nothing is owned by the user, so the caller can append the
246
+ * result unconditionally and get silence when silence is correct.
247
+ */
248
+ function renderReply(entries: readonly Entry[]): string {
249
+ const mine = entries.filter((e) => needsUser(e.blocker.owner));
250
+ if (mine.length === 0) {
251
+ return '';
252
+ }
253
+
254
+ // `collectEntries` already sorted by unblocking weight, so the first entry
255
+ // IS the one that matters most. Picking it here rather than asking the
256
+ // caller to choose is the point: a flat list is the failure mode.
257
+ const lead = mine[0] as Entry;
258
+ const rest = mine.slice(1);
259
+ const lines: string[] = [];
260
+
261
+ lines.push(`Blocked: ${lead.blocker.blocks} — ${lead.roadmapRel}`);
262
+
263
+ const steps = regroupTodo(lead.blocker.todo);
264
+ const doLabel = isLegacy(lead.blocker) ? 'Blocked until:' : 'Do:';
265
+ if (steps.length === 0) {
266
+ lines.push(
267
+ `${doLabel} (no steps recorded — the blocker entry needs a **What to do:** list)`,
268
+ );
269
+ } else if (steps.length === 1) {
270
+ lines.push(...wrap(`${doLabel} ${steps[0] as string}`, WIDTH, ' '));
271
+ } else {
272
+ // Emitted as authored, never re-numbered: real blocker entries already
273
+ // carry their own `1.` / `2.` markers (the CI-enforced `What to do:`
274
+ // shape), so adding our own would produce "1. 1. …". An entry that
275
+ // arrives unnumbered stays unnumbered — that is a fact about the
276
+ // roadmap entry, and papering over it here would hide it from the
277
+ // person who could fix it.
278
+ lines.push(`${doLabel}`);
279
+ for (const s of steps) {
280
+ lines.push(...wrap(` ${s}`, WIDTH, ' '));
281
+ }
282
+ }
283
+ lines.push(...wrap(`Done when: ${lead.blocker.resolvedWhen}`, WIDTH, ' '));
284
+
285
+ if (rest.length > 0) {
286
+ // A count, not a roster. Naming all of them reproduces the exact
287
+ // failure this form exists to end — "zwölf Entscheidungen bei Dir …
288
+ // rund 850 Zeilen", which the transcript shows produced no decision at
289
+ // all. The one that blocks is written out above; the rest are one
290
+ // command away, and saying so is shorter than listing them and more
291
+ // honest than hiding them.
292
+ lines.push('');
293
+ lines.push(
294
+ ...wrap(
295
+ `${rest.length} other decision${rest.length !== 1 ? 's' : ''} also wait${rest.length === 1 ? 's' : ''} on you, ` +
296
+ 'none blocking this — `agent-config gates`.',
297
+ WIDTH,
298
+ ' ',
299
+ ),
300
+ );
301
+ }
302
+
303
+ return lines.join('\n') + '\n';
304
+ }
305
+
212
306
  function renderJson(entries: readonly Entry[], all: boolean): string {
213
307
  const pick = all ? entries : entries.filter((e) => needsUser(e.blocker.owner));
214
308
  return (
@@ -270,18 +364,28 @@ function main(argv?: readonly string[]): number {
270
364
  const args = argv ?? process.argv.slice(2);
271
365
  const all = args.includes('--all');
272
366
  const json = args.includes('--json');
367
+ const reply = args.includes('--reply');
273
368
 
274
369
  const repoRoot = _resolveRepoRoot(process.cwd());
275
370
  const roadmapRoot = path.join(repoRoot, 'agents', 'roadmaps');
276
371
  if (!_isDir(roadmapRoot)) {
372
+ // `--reply` stays silent here too: a project without roadmaps has no
373
+ // blocker to hand over, and a reply-close line saying so would be the
374
+ // ceremony the form exists to avoid.
277
375
  process.stdout.write(
278
- json ? '{"needsYou":0,"other":0,"blockers":[]}\n' : 'No roadmaps directory — nothing to report.\n',
376
+ reply
377
+ ? ''
378
+ : json
379
+ ? '{"needsYou":0,"other":0,"blockers":[]}\n'
380
+ : 'No roadmaps directory — nothing to report.\n',
279
381
  );
280
382
  return 0;
281
383
  }
282
384
 
283
385
  const entries = collectEntries(roadmapRoot);
284
- process.stdout.write(json ? renderJson(entries, all) : render(entries, all));
386
+ process.stdout.write(
387
+ reply ? renderReply(entries) : json ? renderJson(entries, all) : render(entries, all),
388
+ );
285
389
  return 0;
286
390
  }
287
391
 
@@ -308,5 +412,5 @@ if (_isCliEntry()) {
308
412
  process.exitCode = main();
309
413
  }
310
414
 
311
- export { main, needsUser, regroupTodo, wrap, collectEntries, render, renderJson };
415
+ export { main, needsUser, regroupTodo, wrap, collectEntries, render, renderJson, renderReply };
312
416
  export type { Entry };