@event4u/agent-config 14.20.0 → 14.22.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 (242) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +441 -0
  5. package/CREDITS.md +37 -0
  6. package/README.md +1 -1
  7. package/agents/templates/.ai-council.yml.example +10 -0
  8. package/dist/agent-src/commands/analyze/inbox.md +381 -6
  9. package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +251 -34
  10. package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
  11. package/dist/agent-src/commands/analyze.md +12 -7
  12. package/dist/agent-src/commands/feature/plan.md +55 -16
  13. package/dist/agent-src/commands/feature/refactor.md +8 -3
  14. package/dist/agent-src/commands/humanize.md +48 -4
  15. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
  16. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
  17. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
  18. package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
  19. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
  20. package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
  21. package/dist/agent-src/rules/evaluator-independence.md +4 -4
  22. package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
  23. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  24. package/dist/agent-src/rules/token-efficiency.md +26 -3
  25. package/dist/agent-src/scripts/_cli_entry.ts +51 -0
  26. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
  27. package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
  28. package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
  29. package/dist/agent-src/scripts/stubs_due.ts +4 -18
  30. package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
  31. package/dist/agent-src/skills/code-intelligence/SKILL.md +25 -0
  32. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  33. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
  34. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
  35. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  36. package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
  37. package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
  38. package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
  39. package/dist/agent-src/templates/agent-settings.md +0 -1
  40. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  41. package/dist/agent-src/templates/features.md +5 -0
  42. package/dist/agent-src/templates/roadmaps.md +15 -0
  43. package/dist/cli-delegate/_cli_entry.js +8 -0
  44. package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
  45. package/dist/cli-delegate/archival_sweep.js +8 -0
  46. package/dist/cli-delegate/archive_completed_roadmaps.js +494 -0
  47. package/dist/cli-delegate/check_update_banner.js +923 -0
  48. package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
  49. package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
  50. package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
  51. package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
  52. package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-BIXEDQPZ.js} +7 -1
  53. package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
  54. package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
  55. package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
  56. package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
  57. package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
  58. package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-TGIPZBCW.js} +1 -1
  59. package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
  60. package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
  61. package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
  62. package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
  63. package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
  64. package/dist/cli-delegate/cmd_conformance.js +19 -5
  65. package/dist/cli-delegate/cmd_doctor.js +3 -3
  66. package/dist/cli-delegate/cmd_export.js +2 -2
  67. package/dist/cli-delegate/cmd_handoff.js +27 -3
  68. package/dist/cli-delegate/cmd_prune.js +2 -2
  69. package/dist/cli-delegate/cmd_session_recycle.js +2 -2
  70. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  71. package/dist/cli-delegate/cmd_sync.js +2 -2
  72. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  73. package/dist/cli-delegate/cmd_validate.js +2 -2
  74. package/dist/cli-delegate/dashboard_mode.js +16 -0
  75. package/dist/cli-delegate/gate_budget.js +24 -0
  76. package/dist/cli-delegate/gate_execute.js +25 -0
  77. package/dist/cli-delegate/guarded_baseline.js +18 -0
  78. package/dist/cli-delegate/resume_probe.js +22 -0
  79. package/dist/cli-delegate/roadmap_gates.js +638 -0
  80. package/dist/cli-delegate/roadmap_set_step.js +173 -0
  81. package/dist/cli-delegate/stub_queue.js +47 -0
  82. package/dist/cli-delegate/stubs_due.js +38 -0
  83. package/dist/cli-delegate/ticket_bundles.js +59 -0
  84. package/dist/cli-delegate/update_roadmap_progress.js +60 -0
  85. package/dist/discovery/deprecation-report.md +1 -1
  86. package/dist/discovery/discovery-manifest.json +60 -29
  87. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  88. package/dist/discovery/discovery-manifest.summary.md +3 -3
  89. package/dist/discovery/dormancy-report.md +1 -1
  90. package/dist/discovery/orphan-report.md +1 -1
  91. package/dist/discovery/packs.json +7 -6
  92. package/dist/discovery/trust-report.md +2 -2
  93. package/dist/discovery/workspaces.json +6 -5
  94. package/dist/hooks/dispatch.js +1522 -1008
  95. package/dist/install/claudeLayerCarriage.js +237 -0
  96. package/dist/install/claudePathsPlan.js +79 -0
  97. package/dist/install/hostLayerFingerprint.js +1 -1
  98. package/dist/install/install.mjs +10 -1
  99. package/dist/install/partitionEligibility.js +231 -117
  100. package/dist/install/ruleLayerPartition.js +10 -7
  101. package/dist/mcp/registry-manifest.json +3 -3
  102. package/dist/mcp/server.json +2 -2
  103. package/dist/mcp/server.mjs +39376 -38902
  104. package/dist/scripts/ai_council/content_ceiling.js +63 -0
  105. package/dist/scripts/ai_council/seat_policy.js +106 -0
  106. package/dist/scripts/ai_council/transport_resolver.js +21 -0
  107. package/dist/scripts/condense.js +32 -25
  108. package/docs/MIGRATION.md +49 -1
  109. package/docs/contracts/settings-classes.md +1 -1
  110. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
  111. package/llms.txt +14 -14
  112. package/package.json +7 -4
  113. package/src/agent-src/scripts/_cli_entry.ts +51 -0
  114. package/src/agent-src/scripts/archive_completed_roadmaps.ts +14 -23
  115. package/src/agent-src/scripts/roadmap_gates.ts +4 -20
  116. package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
  117. package/src/agent-src/scripts/stubs_due.ts +4 -18
  118. package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
  119. package/src/config/continuity-surface.json +509 -0
  120. package/src/config/estate-count-budget.json +1 -1
  121. package/src/config/gate-coverage.yml +29 -8
  122. package/src/config/gate-violation-baselines.json +8 -8
  123. package/src/config/gitignore-block.txt +19 -0
  124. package/src/config/host-injection-effect.json +73 -0
  125. package/src/config/packed-binary-manifest.json +33 -0
  126. package/src/config/preamble-payload-budget.json +112 -105
  127. package/src/config/publish-surface.json +4 -2
  128. package/src/config/release-gate-locality.yml +4 -3
  129. package/src/install/claudeLayerCarriage.ts +273 -0
  130. package/src/install/claudePathsPlan.ts +76 -0
  131. package/src/install/hostLayerFingerprint.ts +1 -1
  132. package/src/install/partitionEligibility.ts +261 -127
  133. package/src/install/ruleLayerPartition.ts +19 -23
  134. package/src/scripts/_dispatch.bash +77 -27
  135. package/src/scripts/_lib/auto_dispatch.ts +2 -2
  136. package/src/scripts/_lib/blocked_by_marker.ts +189 -0
  137. package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
  138. package/src/scripts/_lib/continuity_surface.ts +296 -0
  139. package/src/scripts/_lib/corpus_manifest.ts +26 -9
  140. package/src/scripts/_lib/hook_settings.ts +1 -1
  141. package/src/scripts/_lib/host_capability.ts +38 -1
  142. package/src/scripts/_lib/injection_effect.ts +151 -0
  143. package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
  144. package/src/scripts/_lib/packed_binary_predicate.ts +225 -0
  145. package/src/scripts/_lib/pin_resolver.ts +13 -0
  146. package/src/scripts/_lib/regression_neighbourhood.ts +1 -1
  147. package/src/scripts/_lib/release_highlights.ts +19 -211
  148. package/src/scripts/_lib/release_material.ts +27 -77
  149. package/src/scripts/_lib/retired_status.ts +74 -0
  150. package/src/scripts/_lib/skill_catalogue.ts +156 -21
  151. package/src/scripts/_lib/structured_ask.ts +117 -0
  152. package/src/scripts/_lib/subagent_capsule.ts +117 -8
  153. package/src/scripts/_lib/tty_prompt.ts +76 -0
  154. package/src/scripts/ai_council/api_transport.ts +168 -0
  155. package/src/scripts/ai_council/clients.ts +58 -63
  156. package/src/scripts/ai_council/config.ts +13 -0
  157. package/src/scripts/ai_council/content_ceiling.ts +79 -0
  158. package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
  159. package/src/scripts/ai_council/jury_aggregate.ts +178 -0
  160. package/src/scripts/ai_council/prompts.ts +9 -0
  161. package/src/scripts/ai_council/seat_policy.ts +137 -0
  162. package/src/scripts/ai_council/status_surface.ts +79 -0
  163. package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
  164. package/src/scripts/ai_council/transport_resolver.ts +59 -1
  165. package/src/scripts/ai_tells_rules.ts +446 -5
  166. package/src/scripts/ask_block_census.ts +499 -0
  167. package/src/scripts/bench_humanizer_eval.ts +264 -40
  168. package/src/scripts/bench_prose_tells_fp.ts +198 -0
  169. package/src/scripts/capture_skill_catalogue.ts +24 -11
  170. package/src/scripts/check_branch_work_committed.ts +234 -0
  171. package/src/scripts/check_candidate_lines.ts +499 -0
  172. package/src/scripts/check_continuity_surface.ts +475 -0
  173. package/src/scripts/check_dependency_floors.ts +13 -6
  174. package/src/scripts/check_estate_count.ts +40 -79
  175. package/src/scripts/check_pack_size.ts +263 -24
  176. package/src/scripts/check_release_highlights.ts +0 -157
  177. package/src/scripts/check_roadmap_trackable.ts +19 -16
  178. package/src/scripts/check_rule_layer_partition.ts +54 -34
  179. package/src/scripts/check_single_delivery.ts +97 -31
  180. package/src/scripts/check_update_banner.ts +13 -0
  181. package/src/scripts/code_graph/build.ts +138 -15
  182. package/src/scripts/code_graph/cli.ts +76 -8
  183. package/src/scripts/code_graph/extract.ts +11 -0
  184. package/src/scripts/code_graph/grammar_probe.ts +43 -0
  185. package/src/scripts/code_graph/loader.ts +88 -14
  186. package/src/scripts/code_graph/query.ts +121 -11
  187. package/src/scripts/code_graph/resolution_tiers.ts +238 -0
  188. package/src/scripts/code_graph/sqlite_store.ts +247 -6
  189. package/src/scripts/code_graph/types.ts +66 -1
  190. package/src/scripts/code_graph/validate.ts +23 -0
  191. package/src/scripts/condense.ts +34 -22
  192. package/src/scripts/corpus_manifest.ts +5 -5
  193. package/src/scripts/council_cli.ts +29 -39
  194. package/src/scripts/count_intent_disagreement.ts +426 -0
  195. package/src/scripts/detect_ai_tells.ts +142 -20
  196. package/src/scripts/handoff_context_hook.ts +79 -0
  197. package/src/scripts/harvest_reference_tokens.ts +698 -0
  198. package/src/scripts/hook_effect_doctor.ts +82 -1
  199. package/src/scripts/hook_manifest.json +1 -1
  200. package/src/scripts/hook_manifest.yaml +94 -12
  201. package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
  202. package/src/scripts/hooks/code_graph_context_hook.ts +212 -0
  203. package/src/scripts/hooks/concern_registry.ts +6 -2
  204. package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
  205. package/src/scripts/hooks/host_semantics.ts +1 -1
  206. package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
  207. package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
  208. package/src/scripts/hooks/rule_inject_hook.ts +1 -1
  209. package/src/scripts/hooks/skill_route_hook.ts +11 -4
  210. package/src/scripts/inbox_source_census.ts +322 -0
  211. package/src/scripts/install-hooks.sh +134 -0
  212. package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
  213. package/src/scripts/lint_plan_risk_register.ts +13 -17
  214. package/src/scripts/lint_roadmap_blockers.ts +51 -1
  215. package/src/scripts/mcp_server/suggest_skill.ts +72 -0
  216. package/src/scripts/mcp_server/tools.ts +2 -56
  217. package/src/scripts/media/lib/adapter-contract.md +7 -2
  218. package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
  219. package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
  220. package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
  221. package/src/scripts/migrate_command_suggestions.ts +1 -1
  222. package/src/scripts/probe_unblocked_ask.ts +135 -2
  223. package/src/scripts/rdp_candidates_delta.ts +276 -0
  224. package/src/scripts/rdp_quality_eval.ts +217 -19
  225. package/src/scripts/release.ts +26 -71
  226. package/src/scripts/release_drill.ts +6 -10
  227. package/src/scripts/release_env.ts +1 -0
  228. package/src/scripts/release_mix_taxonomy.json +3 -2
  229. package/src/scripts/release_publication.ts +34 -29
  230. package/src/scripts/report_host_injection_effect.ts +189 -0
  231. package/src/scripts/report_layer_overlap.ts +41 -0
  232. package/src/scripts/report_skill_menu_census.ts +341 -0
  233. package/src/scripts/self_review_gate.ts +409 -15
  234. package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
  235. package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
  236. package/src/vendor/grammars/README.md +61 -0
  237. package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
  238. package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
  239. package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
  240. package/src/scripts/_lib/carrier_status.ts +0 -118
  241. package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
  242. package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
@@ -126,6 +126,7 @@ import {
126
126
  derive_category_hits,
127
127
  render_derived_head_values,
128
128
  } from './_lib/release_highlights.js';
129
+ import { canPrompt, promptLine } from './_lib/tty_prompt.js';
129
130
  import { preflightPosition } from './_lib/release_position.js';
130
131
  import {
131
132
  RELEASE_HEAD_DEFAULT,
@@ -554,8 +555,8 @@ function render_changelog_entry(
554
555
 
555
556
  // The curated head sits above the generated log, which stays unchanged.
556
557
  const body_lines: string[] = [...render_release_head(opts.head ?? {})];
557
- // Under the head and outside it — rationale on `MIX_RESPONSE_PLACEHOLDER`.
558
- if (opts.mix?.triggered) {
558
+ // Under the head, outside it; any resolved reading — see `MixObligation`.
559
+ if (opts.mix) {
559
560
  body_lines.push('');
560
561
  body_lines.push(...render_mix_response(opts.mix.level));
561
562
  }
@@ -933,74 +934,12 @@ function print_preview(plan: Plan): void {
933
934
  }
934
935
 
935
936
  function confirm(prompt: string): boolean | null {
936
- const ans = _input(`${prompt} [y/N] `);
937
+ const ans = promptLine(`${prompt} [y/N] `);
937
938
  if (ans === null) return null; // no usable controlling terminal
938
939
  const norm = ans.trim().toLowerCase();
939
940
  return norm === 'y' || norm === 'yes';
940
941
  }
941
942
 
942
- /** Can we prompt at all — is fd 0 a TTY, or is a controlling terminal openable? */
943
- function _canPrompt(): boolean {
944
- if (process.env.CI) return false; // CI is non-interactive by contract → require --yes
945
- if (process.stdin.isTTY) return true;
946
- try {
947
- const fd = fs.openSync('/dev/tty', 'r');
948
- fs.closeSync(fd);
949
- return true;
950
- } catch {
951
- return false;
952
- }
953
- }
954
-
955
- /**
956
- * Mirror of Python `input(prompt)` — write the prompt, read one line from the
957
- * controlling terminal. Returns `null` when no terminal is reachable.
958
- *
959
- * The answer is ALWAYS read from a freshly opened, blocking `/dev/tty`
960
- * (`openSync(..., 'rs')`), NEVER from fd 0. `task release` / `./scripts-run`
961
- * run this under go-task's `interactive: true`, which leaves fd 0 a TTY
962
- * (`process.stdin.isTTY === true`) but one Node treats as NON-blocking — so a
963
- * bare `readSync(0)` throws `EAGAIN`, the catch swallows it, and the `[y/N]`
964
- * prompt "auto-aborts" without ever waiting (the exact failure this fixes;
965
- * reproduced with `isTTY === true`). A fresh blocking `/dev/tty` descriptor
966
- * blocks for real input regardless of how the script was invoked — the proven
967
- * synchronous-prompt pattern.
968
- */
969
- function _input(prompt: string): string | null {
970
- process.stdout.write(prompt);
971
- let fd: number;
972
- try {
973
- // 'rs' → O_RDONLY | O_SYNC: a fresh, blocking descriptor on the
974
- // controlling terminal, unaffected by Node's non-blocking fd 0.
975
- fd = fs.openSync('/dev/tty', 'rs');
976
- } catch {
977
- return null; // no controlling terminal (true non-interactive)
978
- }
979
- try {
980
- const buf = Buffer.alloc(1);
981
- const chars: number[] = [];
982
- for (;;) {
983
- let bytesRead: number;
984
- try {
985
- bytesRead = fs.readSync(fd, buf, 0, 1, null);
986
- } catch (e) {
987
- const code = (e as NodeJS.ErrnoException).code;
988
- // A blocking /dev/tty should not yield these, but inherited
989
- // descriptor flags can — retry rather than abort the prompt.
990
- if (code === 'EAGAIN' || code === 'EINTR') continue;
991
- break; // EOF / EIO → EOFError analogue; return what we have.
992
- }
993
- if (bytesRead === 0) break;
994
- const b = buf[0] as number;
995
- if (b === 0x0a) break; // newline terminates the line (stripped, like input()).
996
- chars.push(b);
997
- }
998
- return Buffer.from(chars).toString('utf-8');
999
- } finally {
1000
- fs.closeSync(fd);
1001
- }
1002
- }
1003
-
1004
943
  export interface ConfirmVerdict {
1005
944
  proceed: boolean;
1006
945
  /** Message to surface when not proceeding (no trailing newline). */
@@ -1024,7 +963,7 @@ export function confirmGate(target: string, yes: boolean): ConfirmVerdict {
1024
963
  'No terminal available for the [y/N] confirmation (non-interactive shell). ' +
1025
964
  'Re-run with --yes to confirm, e.g. `task release -- --yes`.',
1026
965
  };
1027
- if (!_canPrompt()) return noTerminal;
966
+ if (!canPrompt()) return noTerminal;
1028
967
  const answer = confirm(`Proceed with release ${target}?`);
1029
968
  // null = the controlling terminal vanished between the probe and the read;
1030
969
  // surface the actionable --yes guidance, never a bare silent "aborted.".
@@ -1684,17 +1623,33 @@ function main(argv: readonly string[] | null = null): number {
1684
1623
 
1685
1624
  const bump = resolve_bump(args.bump_override, commits);
1686
1625
 
1687
- // Resume mode: prefer an existing `release/X.Y.Z` over computed bump,
1688
- // so we don't accidentally start a 1.16.0 release while 1.15.0 is
1689
- // still in flight. Explicit --version still wins.
1690
- const in_flight = args.resume ? _detect_in_flight_target() : null;
1626
+ // An in-flight release is a STATE, never a flag — so the probe runs
1627
+ // unconditionally and `--resume` no longer decides what is being released.
1628
+ //
1629
+ // The comment this replaces already stated the right intent ("so we don't
1630
+ // accidentally start a 1.16.0 release while 1.15.0 is still in flight") and
1631
+ // the `args.resume ?` gate narrowed it to resumed runs only. Measured on
1632
+ // 14.20.0: `guard_release_curation` refuses after step 2 has bumped
1633
+ // `package.json`, so a plain re-run computed the bump from the ALREADY
1634
+ // BUMPED version, landed on 14.21.0, and then refused its own branch with
1635
+ // `release must run from 'main' or 'release/14.21.0', currently on
1636
+ // 'release/14.20.0'`. The 2026-09-07 start-position fix could not help: it
1637
+ // was asked about the wrong target.
1638
+ //
1639
+ // Neither branch of `_detect_in_flight_target` needs the flag — HEAD
1640
+ // sitting on `release/X.Y.Z`, and a `package.json` version whose tag is not
1641
+ // PUBLISHED, are both facts about the repository. On a clean `main` after a
1642
+ // completed release the second branch returns null (the tag is published),
1643
+ // so a fresh release is unaffected; what changes is that an abandoned bump
1644
+ // can no longer be silently released as the NEXT version.
1645
+ const in_flight = _detect_in_flight_target();
1691
1646
  let target: string;
1692
1647
  if (args.explicit) {
1693
1648
  target = args.explicit;
1694
1649
  } else if (in_flight) {
1695
1650
  target = in_flight;
1696
1651
  process.stdout.write(
1697
- `(resume) in-flight target ${in_flight} (package.json version with no tag yet)\n`,
1652
+ `in-flight target ${in_flight} (package.json version with no published tag yet)\n`,
1698
1653
  );
1699
1654
  } else {
1700
1655
  target = bump_version(current, bump);
@@ -244,17 +244,13 @@ export function defaultChangelogFixture(target: string): string {
244
244
  '',
245
245
  '- fixture entry for the release drill',
246
246
  '',
247
- // Both lines are section-level publication obligations
248
- // (`section_publication_blockers`), so a fixture without them refuses
249
- // at the curation guard and every sequencing scenario dies at step 2 —
250
- // the same "fixture trips the guard the drill exists to sequence"
251
- // failure this builder's docstring already warns about, for the two
252
- // obligations added 2026-09-05. The mix answer is written OUT rather
253
- // than measured: measuring would couple every scenario to the live
254
- // repository's commit span, which is the coupling `changelog` was
255
- // introduced to remove.
247
+ // The mix line is written OUT rather than measured: measuring would
248
+ // couple every scenario to the live repository's commit span, which is
249
+ // the coupling `changelog` was introduced to remove. It is no longer a
250
+ // publication obligation (ADR-261 deleted the written answer it used to
251
+ // carry) — it stays because the drill exercises the shape the writer
252
+ // actually renders.
256
253
  '> **Governance mix:** governance-only 2 vs consumer-only 1 (taxonomy 1.0.0).',
257
- '> Next cycle ships the fixture consumer work, tracked in the drill.',
258
254
  '',
259
255
  'Tests: 1000 (+0 since 0.0.0)',
260
256
  '',
@@ -124,6 +124,7 @@ function _set_changelog_reader(fn: ((file: string) => string) | null): void {
124
124
  function read_changelog_text(): string {
125
125
  return _changelog_reader ? _changelog_reader(CHANGELOG) : fs.readFileSync(CHANGELOG, 'utf-8');
126
126
  }
127
+
127
128
  const MAIN_BRANCH = 'main';
128
129
  const REMOTE = 'origin';
129
130
  const REPO_SLUG = 'event4u-app/agent-config';
@@ -1,6 +1,6 @@
1
1
  {
2
- "taxonomy_version": "1.0.0",
3
- "updated": "2026-09-04",
2
+ "taxonomy_version": "1.1.0",
3
+ "updated": "2026-09-08",
4
4
  "contract": "docs/decisions/ADR-253-per-pr-user-artifact-gate-declined.md",
5
5
  "reader": "src/scripts/measure_release_mix.ts",
6
6
  "note": [
@@ -62,6 +62,7 @@
62
62
  { "prefix": "src/install/", "category": "consumer" },
63
63
  { "prefix": "src/server/", "category": "consumer" },
64
64
  { "prefix": "src/ui/", "category": "consumer" },
65
+ { "prefix": "src/vendor/grammars/", "category": "consumer" },
65
66
  { "prefix": "src/shared/", "category": "unclassified" },
66
67
 
67
68
  { "prefix": "src/scripts/", "category": "maintenance" },
@@ -458,15 +458,14 @@ export function local_release_gate_argv(): string[] {
458
458
  /**
459
459
  * Measure the governance-versus-product level for the span under release.
460
460
  *
461
- * The push guard's fourth site reads the SAME obligation the release PR reads,
462
- * which until 2026-09-05 it did not: `publication_blockers` knew two sentinels
463
- * and not this one, so the earliest refusal was the pull request. That gap was
464
- * documented in `docs/contracts/CHANGELOG-conventions.md` and it is what turned
465
- * 14.17.0 red after a branch, a PR and a CI run had already been spent.
461
+ * REPORTING ONLY since ADR-261. It feeds exactly one consumer: the `>
462
+ * **Governance mix:** …` line the writer renders into the section. No guard
463
+ * reads it, nothing refuses over it, and `triggered` gates nothing — the
464
+ * written-answer obligation it used to serve is deleted.
466
465
  *
467
466
  * Returns `null` on any measurement failure — a shallow clone or a missing tag
468
- * is an environment fact, and the contract is explicit that it degrades to a
469
- * warning rather than to a refusal. `null` produces no blocker downstream.
467
+ * is an environment fact, and a release must not stop over one. `null` means
468
+ * the line is simply not rendered.
470
469
  */
471
470
  export function measure_mix_obligation(
472
471
  version: string,
@@ -478,19 +477,18 @@ export function measure_mix_obligation(
478
477
  const reading = measureRange(from, 'HEAD', loadTaxonomy(), version, REPO_ROOT);
479
478
  const o = reading.response_obligation;
480
479
  return {
481
- triggered: o.triggered,
482
480
  level:
483
481
  `governance-only ${String(o.governance_only)} vs consumer-only ` +
484
482
  `${String(o.consumer_only)} (taxonomy ${reading.taxonomy_version})`,
485
483
  };
486
484
  } catch (err) {
487
- // The CI side prints `⚠️ governance mix not measured`; swallowing it
488
- // here made a broken taxonomy file disable BOTH local guards in
489
- // silence while CI kept refusing — the exact asymmetry this change
490
- // exists to remove, restored one layer down.
485
+ // Printed rather than swallowed. Nothing refuses over the reading any
486
+ // more (ADR-261), so this is not a blocked release — it is a section
487
+ // that will publish without its `> **Governance mix:** …` line, and the
488
+ // operator is entitled to know why the line is missing.
491
489
  process.stderr.write(
492
490
  `⚠️ governance mix not measured for ${version}: ${(err as Error).message}\n` +
493
- ' Both local guards are inert for this obligation; the release PR still refuses.\n',
491
+ ' The section will publish without the measured line. Nothing is blocked.\n',
494
492
  );
495
493
  return null;
496
494
  }
@@ -539,12 +537,7 @@ export function guard_release_branch_push(branch: string): void {
539
537
  if (!section) {
540
538
  return;
541
539
  }
542
- const blockers = section_publication_blockers(
543
- section.body,
544
- version,
545
- `\`${branch}\``,
546
- measure_mix_obligation(version),
547
- );
540
+ const blockers = section_publication_blockers(section.body, version, `\`${branch}\``);
548
541
  if (blockers.length === 0) {
549
542
  return;
550
543
  }
@@ -685,6 +678,13 @@ export function integrate_main_if_behind(branch: string, say: (msg: string) => v
685
678
  run(['git', 'merge', `${REMOTE}/${MAIN_BRANCH}`, '--no-edit']);
686
679
  }
687
680
 
681
+ /** Everything this site refuses over: the head-level blockers, nothing else. */
682
+ function curation_blockers(version: string): string[] {
683
+ const section = extract_changelog_section(read_changelog_text(), version);
684
+ if (section === null) return [];
685
+ return publication_blockers(section.body, version, `\`release/${version}\``);
686
+ }
687
+
688
688
  export function guard_release_curation(version: string, prMerged = false): void {
689
689
  if (prMerged) {
690
690
  return;
@@ -693,8 +693,7 @@ export function guard_release_curation(version: string, prMerged = false): void
693
693
  if (!section) {
694
694
  return;
695
695
  }
696
- // The head-level blockers plus the mix obligation — deliberately NOT the
697
- // tests footer.
696
+ // The head-level blockers only — deliberately NOT the tests footer.
698
697
  //
699
698
  // This site runs BEFORE `prepend_changelog`'s output is committed, and
700
699
  // `--resume` skips the whole bump block once `package.json` already carries
@@ -707,17 +706,23 @@ export function guard_release_curation(version: string, prMerged = false): void
707
706
  //
708
707
  // `guard_release_branch_push` carries it instead: by then the section is
709
708
  // committed and editable on the branch, and nothing has left the machine.
710
- const blockers = [
711
- ...publication_blockers(
712
- section.body,
713
- version,
714
- `\`release/${version}\``,
715
- measure_mix_obligation(version),
716
- ),
717
- ];
709
+ //
710
+ // What is NOT here any more (ADR-261): the governance-versus-product
711
+ // written answer, the next-cycle promise, the read-back of the previous
712
+ // one, the `## [Unreleased]` staging channel that fed them and the
713
+ // interactive prompt that asked for them. This guard refuses the
714
+ // generator's own unrewritten draft — a claim no human has looked at — and
715
+ // nothing else. It never asks the operator to write prose about a future
716
+ // cycle to get a version out.
717
+ const blockers = curation_blockers(version);
718
718
  if (blockers.length === 0) {
719
719
  return;
720
720
  }
721
+ // Read once. This used to re-read the file here, because
722
+ // `consume_staged_answers` / `collect_curation_answers` could rewrite the
723
+ // section between the two reads; both write paths are gone (ADR-261) and
724
+ // `curation_blockers` is read-only, so a second read could only ever
725
+ // return the same bytes.
721
726
  const marked = section.body
722
727
  .split('\n')
723
728
  .filter((l) => l.includes(DERIVED_MARKER))
@@ -0,0 +1,189 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * The per-host injection-effect census — one line per host, provenance or none.
4
+ *
5
+ * `road-to-delivery-on-hook-hosts` steps 1.2 and 1.3. It renders the committed
6
+ * record at `src/config/host-injection-effect.json` and adds the one thing the
7
+ * record deliberately does not hold: the COMPUTED half, read fresh from the hook
8
+ * manifest, so a reader sees both axes side by side and can see that they
9
+ * disagree.
10
+ *
11
+ * WHY BOTH COLUMNS ARE PRINTED, ALWAYS
12
+ * ------------------------------------
13
+ * `cowork` is the row that makes this table worth generating: eight slots bound
14
+ * and a measured `observed-false`. Printing only the observation would hide the
15
+ * near-miss; printing only the binding would produce the wrong verdict for the
16
+ * one host we have actually measured. The table exists so the gap between "could
17
+ * emit" and "did arrive" is visible per host rather than argued in prose.
18
+ *
19
+ * It computes NO verdict. `admissibleUnderE3` is a one-line read of the observed
20
+ * column and is applied by the caller that would change a projection, never here:
21
+ * a report that also decided admission would be the single place where an
22
+ * inference could quietly replace an observation.
23
+ *
24
+ * Usage:
25
+ * ./scripts-run src/scripts/report_host_injection_effect
26
+ * ./scripts-run src/scripts/report_host_injection_effect --emit --pin <sha>
27
+ *
28
+ * Deterministic: hosts sort by id, no timestamp, the pin is an argument.
29
+ */
30
+
31
+ import { execFileSync, spawnSync } from 'node:child_process';
32
+ import * as fs from 'node:fs';
33
+ import * as path from 'node:path';
34
+ import { fileURLToPath, pathToFileURL } from 'node:url';
35
+
36
+ import {
37
+ concernBound,
38
+ loadRecord,
39
+ recordProblems,
40
+ type InjectionRecord,
41
+ } from './_lib/injection_effect.js';
42
+
43
+ const _HERE = fileURLToPath(import.meta.url);
44
+ export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
45
+ export const ARTIFACT_REL = path.join(
46
+ 'agents', 'evidence', 'analysis', 'host-injection-effect-2026-09.md',
47
+ );
48
+
49
+ /** Platform → slot → concern ids, straight out of the manifest. */
50
+ export function loadPlatforms(root: string): Record<string, Record<string, string[]>> {
51
+ const raw = fs.readFileSync(path.join(root, 'src', 'scripts', 'hook_manifest.yaml'), 'utf-8');
52
+ const out = spawnSync(
53
+ process.execPath,
54
+ [
55
+ '-e',
56
+ 'const y=require("js-yaml");let s="";process.stdin.on("data",d=>s+=d).on("end",()=>{' +
57
+ 'const d=y.load(s);process.stdout.write(JSON.stringify(d.platforms||{}))})',
58
+ ],
59
+ { input: raw, encoding: 'utf-8', cwd: root, maxBuffer: 64 * 1024 * 1024 },
60
+ );
61
+ if (out.status !== 0 || !out.stdout) {
62
+ throw new Error(`hook_manifest.yaml could not be parsed: ${out.stderr || 'no output'}`);
63
+ }
64
+ return JSON.parse(out.stdout) as Record<string, Record<string, string[]>>;
65
+ }
66
+
67
+ /**
68
+ * Keys under a platform that are NOT lifecycle slots.
69
+ *
70
+ * `fallback_only` is a marker saying this package binds nothing here, and
71
+ * counting it as a slot made copilot read 1 against the 0 that
72
+ * `docs/enforcement-by-host.md` records — a one-row disagreement with the host
73
+ * table for a nameable reason, which is the drift this repository gates
74
+ * elsewhere. Caught by comparing the generated table against that doc rather
75
+ * than by a test, so it is named here.
76
+ */
77
+ const NON_SLOT_KEYS: ReadonlySet<string> = new Set(['fallback_only']);
78
+
79
+ /** Lifecycle slots bound for a host, on the host table's own definition. */
80
+ export function boundSlotCount(platforms: Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>, host: string): number {
81
+ return Object.keys(platforms[host] ?? {}).filter((k) => !NON_SLOT_KEYS.has(k)).length;
82
+ }
83
+
84
+ /** Host ids that are real rows, in sort order. Underscore keys are annotations. */
85
+ export function hostIds(rec: InjectionRecord): string[] {
86
+ return Object.keys(rec).filter((k) => !k.startsWith('_')).sort();
87
+ }
88
+
89
+ export function renderArtifact(root: string, pin: string): string {
90
+ const rec = loadRecord(root);
91
+ const platforms = loadPlatforms(root);
92
+ const L: string[] = [];
93
+ L.push('<!-- evidence-type: analysis -->');
94
+ L.push('# Host injection effect — does a delivered rule body reach the model?');
95
+ L.push('');
96
+ L.push(`Pinned to commit \`${pin}\`. Generated by`);
97
+ L.push(`\`./scripts-run src/scripts/report_host_injection_effect --emit --pin ${pin}\`.`);
98
+ L.push('Observations come from `src/config/host-injection-effect.json`; the binding column is');
99
+ L.push('read fresh from `src/scripts/hook_manifest.yaml` on every run.');
100
+ L.push('');
101
+ L.push('## The two columns, and why neither is the other');
102
+ L.push('');
103
+ L.push('**Slots bound** is computed and cheap. **Body reaches the model** is observed and');
104
+ L.push('expensive, and owner ruling E3 admits a host to `lean_projection.hosts` on the second');
105
+ L.push('only: one transcript in which a model visibly acts on a delivered body. Documentation');
106
+ L.push('of a host\'s hook API is not admission.');
107
+ L.push('');
108
+ L.push('`cowork` is why the distinction is not pedantry: **eight slots bound, and measured');
109
+ L.push('`observed-false`** — its trampoline discards dispatcher output and exits 0. A table');
110
+ L.push('with only the binding column would admit it.');
111
+ L.push('');
112
+ L.push('## Per host');
113
+ L.push('');
114
+ L.push('| Host | Slots bound | `rule-inject` on `user_prompt_submit` | Body reaches the model | Citation |');
115
+ L.push('|---|---:|:-:|---|---|');
116
+ for (const host of hostIds(rec)) {
117
+ const obs = rec[host];
118
+ const slots = boundSlotCount(platforms, host);
119
+ const bound = concernBound(platforms, host, 'user_prompt_submit', 'rule-inject');
120
+ const cite =
121
+ obs !== undefined && obs.transcript !== null && obs.date !== null
122
+ ? `\`${obs.transcript}\` (${obs.date})`
123
+ : '—';
124
+ L.push(
125
+ `| \`${host}\` | ${String(slots)} | ${bound ? 'yes' : 'no'} | \`${obs?.state ?? 'unobserved'}\` | ${cite} |`,
126
+ );
127
+ }
128
+ L.push('');
129
+ L.push('## Why each row reads as it does');
130
+ L.push('');
131
+ for (const host of hostIds(rec)) {
132
+ L.push(`- **\`${host}\`** — ${rec[host]?.reason ?? 'no row'}`);
133
+ }
134
+ const mdTree = rec['_cursor_md_tree_loaded'];
135
+ if (mdTree !== undefined) {
136
+ L.push('');
137
+ L.push('## `cursor.md_tree_loaded` (step 1.3)');
138
+ L.push('');
139
+ L.push(`\`cursor.md_tree_loaded: ${mdTree.state}\` — ${mdTree.reason}`);
140
+ }
141
+ L.push('');
142
+ L.push('## What this census does NOT do');
143
+ L.push('');
144
+ L.push('It computes no verdict. Admission under E3 is a one-line read of the observed column');
145
+ L.push('and is applied by whatever would change a projection, never here — a report that also');
146
+ L.push('decided admission would be the one place an inference could quietly replace an');
147
+ L.push('observation.');
148
+ L.push('');
149
+ L.push('`unobserved` is a RESULT, not a gap: the host keeps its full rule bodies and loses');
150
+ L.push('nothing while it waits, which is the roadmap\'s K6 and is asserted per PR by');
151
+ L.push('`check_host_tree_parity` in the predecessor.');
152
+ L.push('');
153
+ return L.join('\n');
154
+ }
155
+
156
+ function headSha(root: string): string {
157
+ return execFileSync('git', ['-C', root, 'rev-parse', 'HEAD'], { encoding: 'utf-8' }).trim();
158
+ }
159
+
160
+ export function main(argv: readonly string[]): number {
161
+ const root = REPO_ROOT;
162
+ const rec = loadRecord(root);
163
+ const problems = recordProblems(rec);
164
+ if (problems.length > 0) {
165
+ process.stderr.write('❌ the observation record is not admissible:\n');
166
+ for (const p of problems) process.stderr.write(` · ${p.host}: ${p.message}\n`);
167
+ return 1;
168
+ }
169
+ const pinIdx = argv.indexOf('--pin');
170
+ const pin = pinIdx !== -1 && pinIdx + 1 < argv.length ? (argv[pinIdx + 1] as string) : headSha(root);
171
+ if (argv.includes('--emit')) {
172
+ const out = path.join(root, ARTIFACT_REL);
173
+ fs.mkdirSync(path.dirname(out), { recursive: true });
174
+ fs.writeFileSync(out, renderArtifact(root, pin), 'utf-8');
175
+ process.stdout.write(`wrote ${ARTIFACT_REL} · pin ${pin}\n`);
176
+ }
177
+ const hosts = hostIds(rec);
178
+ const byState = new Map<string, number>();
179
+ for (const h of hosts) byState.set(rec[h]!.state, (byState.get(rec[h]!.state) ?? 0) + 1);
180
+ process.stdout.write(`scanned: ${String(hosts.length)} host(s)\n`);
181
+ for (const [state, n] of [...byState].sort()) {
182
+ process.stdout.write(` ${state.padEnd(16)} ${String(n)}\n`);
183
+ }
184
+ return 0;
185
+ }
186
+
187
+ if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) {
188
+ process.exit(main(process.argv.slice(2)));
189
+ }
@@ -24,6 +24,7 @@ import * as path from 'node:path';
24
24
  import process from 'node:process';
25
25
  import { fileURLToPath, pathToFileURL } from 'node:url';
26
26
 
27
+ import { resolveHostLayerVerdict, setPartitionAnnounce } from '../install/partitionEligibility.js';
27
28
  import { warnLayerOverlap } from './_lib/layer_overlap_notice.js';
28
29
 
29
30
  // ledger-exempt: this is a REPORTER, not a gate — it always exits 0 by construction
@@ -59,6 +60,46 @@ export function main(argv?: readonly string[]): number {
59
60
  return 1;
60
61
  }
61
62
  }
63
+ // The projection line, and where it lives is a correction. **Both council seats
64
+ // (2026-08-20, 2/2) required that generation PRINT the mode it selected** rather
65
+ // than partition silently. The 2026-09-07 change moved the withhold off
66
+ // `installed.lock` and, in doing so, removed `condense.ts`'s only call into the
67
+ // resolver — so the emitter it installed became dead and generation withheld
68
+ // ~299 skills and 29 personas while printing nothing about the layer it
69
+ // withheld against. A neutral review found it the same day.
70
+ //
71
+ // It prints from HERE rather than from `condense.ts` because that file is ~1,200
72
+ // lines past the source-size ceiling, where the ratchet counts every added line
73
+ // and its test blocks on `baseline == live`. This step already runs in the same
74
+ // `generate-tools` chain, one line below the generator, so the operator sees the
75
+ // two lines together.
76
+ // Silence the resolver's OWN one-liner first: it writes to stdout by default,
77
+ // and leaving it on printed the verdict twice — once bare, once with the
78
+ // remediation text below. One line, the useful one.
79
+ setPartitionAnnounce(() => undefined);
80
+ const verdict = resolveHostLayerVerdict(projectRoot);
81
+ // Three states, not two. Corrected after a second neutral review: the
82
+ // unverified branch printed "a stale global layer is fixed by
83
+ // `agent-config install`" on a machine with NO global layer at all — a fresh
84
+ // checkout, where nothing is withheld, nothing is stale, and the remediation
85
+ // named does not apply. It fired on every `task generate-tools` there.
86
+ //
87
+ // What this line reports is the VERIFICATION state of the layer withheld
88
+ // against. The per-artefact withhold COUNTS are the generator's own summary
89
+ // one line above (`skills=N`, `command_skills=N (M withheld …)`), which is
90
+ // where a reader sees what was actually held back.
91
+ const noLayer = verdict.reason.includes('no host-global layer');
92
+ process.stdout.write(
93
+ verdict.verified
94
+ ? ` ℹ️ project layer carries only what ~/.claude lacks — ${verdict.reason}\n`
95
+ : noLayer
96
+ ? ' ℹ️ no host-global layer on this machine — nothing is withheld, so the\n' +
97
+ ' project layer carries the full projection by construction.\n'
98
+ : ` ⚠️ host layer UNVERIFIED — ${verdict.reason}\n` +
99
+ ' The project layer still carries only what ~/.claude lacks (per-artefact,\n' +
100
+ ' ADR-236 amendment 2026-09-07). A stale global layer is fixed by\n' +
101
+ ' `agent-config install`, not by re-running generate-tools.\n',
102
+ );
62
103
  warnLayerOverlap(projectRoot, (m) => process.stdout.write(`${m}\n`));
63
104
  return 0;
64
105
  }