@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
@@ -0,0 +1,63 @@
1
+ /**
2
+ * One binary content ceiling per seat — road-to-admissible-council-seats 2.2.
3
+ *
4
+ * Redaction today is a SECRETS floor and says so
5
+ * (`src/skills/ai-council/references/cost-and-redaction.md`). Nothing above it
6
+ * could express "this project's diff may not go to that seat", which is a
7
+ * different question with a different answer: a diff carrying no secret at all
8
+ * is still the consumer's private source.
9
+ *
10
+ * Deliberately BINARY. A graded scale invites a middle value nobody can define
11
+ * and every caller reads differently; two values force the decision at
12
+ * authoring time, where it belongs.
13
+ *
14
+ * public-artifact — repo-owned artefacts: skill descriptions, the trigger
15
+ * corpus, rule text, bench fixtures. Already public.
16
+ * project-content — everything a consumer supplies. The DEFAULT, so an
17
+ * unclassified payload is treated as private rather than
18
+ * as publishable.
19
+ *
20
+ * A seat's ceiling is the most sensitive class it may receive. `public-artifact`
21
+ * is therefore the NARROWER seat: it admits public artefacts only.
22
+ */
23
+ export const CONTENT_CLASSES = ['public-artifact', 'project-content'];
24
+ /** The default for anything not explicitly classified: private, not public. */
25
+ export const DEFAULT_CONTENT_CLASS = 'project-content';
26
+ /** The default seat ceiling: unchanged behaviour for every existing seat. */
27
+ export const DEFAULT_SEAT_CEILING = 'project-content';
28
+ const _SET = new Set(CONTENT_CLASSES);
29
+ /**
30
+ * Any configured value → a content class. Unrecognised input becomes
31
+ * `project-content`, the RESTRICTIVE value: a typo must narrow what a seat may
32
+ * receive, never widen it.
33
+ */
34
+ export function classifyContentClass(raw, fallback = DEFAULT_CONTENT_CLASS) {
35
+ if (typeof raw === 'string' && _SET.has(raw)) {
36
+ return raw;
37
+ }
38
+ return fallback;
39
+ }
40
+ /**
41
+ * Does a seat whose ceiling is `seatCeiling` admit a payload of `contentClass`?
42
+ *
43
+ * The one refusal is `project-content` into a `public-artifact` seat. Every
44
+ * other pairing is admitted, including public artefacts into a
45
+ * `project-content` seat — a seat cleared for private content is trivially
46
+ * cleared for public content, and refusing that would be a nonsense that
47
+ * disables the default seat for its own repo's fixtures.
48
+ *
49
+ * Refusal is a GREEN path (this roadmap's 2.2 verify): the caller drops the
50
+ * seat and the run continues. It is not an error, because a policy working as
51
+ * designed is not a failure.
52
+ */
53
+ export function admitsContent(seatCeiling, contentClass) {
54
+ if (seatCeiling === 'public-artifact' && contentClass === 'project-content') {
55
+ return {
56
+ admitted: false,
57
+ reason: "seat ceiling is 'public-artifact'; the payload is classified " +
58
+ "'project-content' (consumer-supplied)",
59
+ };
60
+ }
61
+ return { admitted: true, reason: null };
62
+ }
63
+ //# sourceMappingURL=content_ceiling.js.map
@@ -0,0 +1,106 @@
1
+ import { classifyContentClass as _classifyContentClass, DEFAULT_SEAT_CEILING as _DEFAULT_SEAT_CEILING, } from './content_ceiling.js';
2
+ /**
3
+ * Seat policy — the vocabulary for refusing a route that IS reachable.
4
+ *
5
+ * road-to-admissible-council-seats 2.1. `AbsentReason` was exactly four values
6
+ * — `no_binary | no_auth | timeout | quota` — and every one of them means "the
7
+ * route is not reachable". A route that is reachable and **must not be used**
8
+ * had no name at all, so the resolver could not fail closed on terms, privacy,
9
+ * unproven cost, or a served model that is not the requested one. The absence
10
+ * of a word was the absence of the refusal: `resolveTransport`'s `api`/`cli`
11
+ * branches return `available: true` unconditionally, so anything the four
12
+ * reachability reasons could not express resolved OPEN.
13
+ *
14
+ * Fail closed on the unknown, which is the whole point.
15
+ * `classifyPolicyExclusion` maps anything it does not recognise to
16
+ * `policy_unknown`, and `policy_unknown` is an ABSENT reason like every other
17
+ * member of this union. An unrecognised policy token is the case where the
18
+ * config is trying to say something the code cannot understand; resolving it
19
+ * to "available" would make a typo an authorisation. This is the roadmap's own
20
+ * Risk 3 — "a new refusal vocabulary defaults open instead of closed".
21
+ *
22
+ * What this is NOT. It is not a secrets floor. Redaction (`skills/ai-council/references/
23
+ * cost-and-redaction.md`) still runs underneath and is not replaced. It is not
24
+ * an authorisation either: nothing here ever makes a seat available that was
25
+ * not already available — every value in the union only ever subtracts.
26
+ */
27
+ /** Reachable-but-refused reasons. Every value means ABSENT. */
28
+ export const POLICY_EXCLUSIONS = [
29
+ /** The provider's terms forbid this use of the route. */
30
+ 'policy_terms',
31
+ /** A privacy constraint forbids sending to this route. */
32
+ 'policy_privacy',
33
+ /** The route's cost property is asserted rather than proven. */
34
+ 'policy_unproven_cost',
35
+ /** The route serves a model other than the one requested. */
36
+ 'policy_model_mismatch',
37
+ /** 2.2 — the payload's content class exceeds the seat's ceiling. */
38
+ 'policy_content_ceiling',
39
+ /** The config named an exclusion this build does not understand. */
40
+ 'policy_unknown',
41
+ ];
42
+ const _SET = new Set(POLICY_EXCLUSIONS);
43
+ /** True for a value this build can act on. `policy_unknown` included: it is a
44
+ * real, actionable verdict (refuse), not a parse failure. */
45
+ export function isPolicyExclusion(v) {
46
+ return typeof v === 'string' && _SET.has(v);
47
+ }
48
+ /**
49
+ * Any configured value → a verdict. Unrecognised input, including `null` used
50
+ * where a string was meant, becomes `policy_unknown` — never `null`, because a
51
+ * `null` return would put the caller back in the position of deciding what an
52
+ * unreadable policy means, which is where the open default came from.
53
+ *
54
+ * `undefined` and `null` are the two ways of saying "no exclusion configured"
55
+ * and are the ONLY inputs that return `null`. Everything else is a refusal.
56
+ */
57
+ export function classifyPolicyExclusion(raw) {
58
+ if (raw === undefined || raw === null) {
59
+ return null;
60
+ }
61
+ return isPolicyExclusion(raw) ? raw : 'policy_unknown';
62
+ }
63
+ /** One-line human reason for a refusal. `council:status` prints this. */
64
+ export function policyExclusionReason(p, provider) {
65
+ const who = provider === undefined ? 'this seat' : provider;
66
+ switch (p) {
67
+ case 'policy_terms':
68
+ return `${who} is excluded by policy: the route's terms forbid this use`;
69
+ case 'policy_privacy':
70
+ return `${who} is excluded by policy: a privacy constraint forbids this route`;
71
+ case 'policy_unproven_cost':
72
+ return `${who} is excluded by policy: the route's cost property is asserted, not proven`;
73
+ case 'policy_model_mismatch':
74
+ return `${who} is excluded by policy: the route serves a model other than the one requested`;
75
+ case 'policy_content_ceiling':
76
+ return `${who} is excluded by policy: the payload exceeds the seat's content ceiling`;
77
+ case 'policy_unknown':
78
+ return `${who} is excluded by policy: an exclusion this build does not understand (failing closed)`;
79
+ }
80
+ }
81
+ export function parseSeatFields(name, raw) {
82
+ const dr = raw['disabled_reason'];
83
+ let disabled_reason = null;
84
+ if (dr !== undefined && dr !== null) {
85
+ if (typeof dr !== 'string' || dr.trim() === '') {
86
+ return {
87
+ error: `members.${name}.disabled_reason must be a non-empty string when set ` +
88
+ `(got ${JSON.stringify(dr)}).`,
89
+ };
90
+ }
91
+ disabled_reason = dr.trim();
92
+ }
93
+ // Both of the next two fail CLOSED on an unreadable value: an unknown
94
+ // exclusion token becomes `policy_unknown` (which resolves ABSENT) and an
95
+ // unknown ceiling falls back to the RESTRICTIVE one. Throwing instead would
96
+ // be defensible, but a config typo would then take the whole council down
97
+ // rather than the one seat it names.
98
+ return {
99
+ fields: {
100
+ disabled_reason,
101
+ policy_exclusion: classifyPolicyExclusion(raw['policy_exclusion']),
102
+ content_ceiling: _classifyContentClass(raw['content_ceiling'], _DEFAULT_SEAT_CEILING),
103
+ },
104
+ };
105
+ }
106
+ //# sourceMappingURL=seat_policy.js.map
@@ -40,6 +40,8 @@
40
40
  */
41
41
  import { classifyBilling, isCommunityCli, strongestAuth, PROVIDER_CLI_META, } from '../_lib/environment_detector.js';
42
42
  import { resolve_mode } from './modes.js';
43
+ import { admitsContent, DEFAULT_SEAT_CEILING, } from './content_ceiling.js';
44
+ import { policyExclusionReason } from './seat_policy.js';
43
45
  /** The accepted values of the `mode` key, including the resolver-only `auto`. */
44
46
  export const VALID_TRANSPORT_MODES = new Set([
45
47
  'api',
@@ -84,6 +86,22 @@ export function resolveTransport(opts) {
84
86
  const auth = strongestAuth(report, provider);
85
87
  const authSource = auth?.source ?? null;
86
88
  const billing = classifyBilling(provider, authSource);
89
+ // Policy first (2.1). Before the mode dispatch, because `api` and `cli`
90
+ // below return `available: true` UNCONDITIONALLY — a refusal placed after
91
+ // them would be unreachable for exactly the seats it exists to stop.
92
+ const exclusion = opts.policyExclusion ?? null;
93
+ if (exclusion !== null) {
94
+ return unavailable(provider, billing, policyExclusionReason(exclusion, provider), exclusion);
95
+ }
96
+ // Content ceiling (2.2). Only runs when the caller declared what it would
97
+ // send; an undeclared pass is unchanged, not silently refused.
98
+ const contentClass = opts.contentClass ?? null;
99
+ if (contentClass !== null) {
100
+ const verdict = admitsContent(opts.seatCeiling ?? DEFAULT_SEAT_CEILING, contentClass);
101
+ if (!verdict.admitted) {
102
+ return unavailable(provider, billing, `${provider}: ${verdict.reason ?? 'content ceiling refused the payload'}`, 'policy_content_ceiling');
103
+ }
104
+ }
87
105
  if (mode === 'manual') {
88
106
  return {
89
107
  available: true,
@@ -173,6 +191,9 @@ export function resolveMemberTransport(opts) {
173
191
  // optional-boolean field, so the key is omitted entirely rather than
174
192
  // forwarded as `apiKeyPresent: undefined`.
175
193
  ...(opts.apiKeyPresent !== undefined ? { apiKeyPresent: opts.apiKeyPresent } : {}),
194
+ policyExclusion: opts.policyExclusion ?? null,
195
+ seatCeiling: opts.seatCeiling ?? null,
196
+ contentClass: opts.contentClass ?? null,
176
197
  });
177
198
  return { ...resolved, configuredMode };
178
199
  }
@@ -232,9 +232,9 @@ export { rule_in_scope } from '../install/ruleInScope.js';
232
232
  export { CLAUDE_PATHS_PATTERN_BUDGET, _claude_paths_plan, _escape_claude_bracket, _expanded_pattern_count, _has_non_path_trigger, _is_unresolved_placeholder, derive_trigger_globs, } from '../install/claudePathsPlan.js';
233
233
  import { rule_in_scope } from '../install/ruleInScope.js';
234
234
  import { pruneEmptyDirs } from './_lib/prune_empty_dirs.js';
235
- import { commandsWithheld, partitionActive, personaPartition, setPartitionAnnounce } from '../install/partitionEligibility.js'; // ADR-236
235
+ import { claudeLayerHolds, commandWithheld, keepInProjectLayer, personaPartition } from '../install/partitionEligibility.js'; // ADR-236
236
236
  import { dedupableRules, partitionRulesForDir } from '../install/ruleLayerPartition.js'; // ADR-236, per-host evidence
237
- import { _claude_paths_plan, derive_trigger_globs } from '../install/claudePathsPlan.js';
237
+ import { _claude_paths_plan, applies_when, derive_trigger_globs } from '../install/claudePathsPlan.js';
238
238
  const _HERE = path.dirname(fileURLToPath(import.meta.url)); // <repo>/src/scripts
239
239
  const _DEFAULT_PROJECT_ROOT = path.resolve(_HERE, '..', '..');
240
240
  function _deriveState(root) {
@@ -263,7 +263,6 @@ function _deriveState(root) {
263
263
  };
264
264
  }
265
265
  export const MODULE_STATE = _deriveState(_DEFAULT_PROJECT_ROOT);
266
- setPartitionAnnounce(success); // ADR-236: the mode line must be visible at the default level
267
266
  /**
268
267
  * Test seam — reassign one or more module-state fields, mirroring the pytest
269
268
  * monkeypatch of `condense.<NAME> = ...`. When `PROJECT_ROOT` is overridden,
@@ -1149,7 +1148,7 @@ export function _emit_cursor_mdc(source, target) {
1149
1148
  const globs = always_apply ? [] : derive_trigger_globs(meta);
1150
1149
  const lines = [
1151
1150
  '---',
1152
- `description: ${_yaml_scalar(description)}`,
1151
+ `description: ${_yaml_scalar(always_apply ? description : applies_when(description, meta))}`,
1153
1152
  `globs: ${globs.join(',')}`,
1154
1153
  `alwaysApply: ${always_apply ? 'true' : 'false'}`,
1155
1154
  '---',
@@ -1170,7 +1169,7 @@ export function _emit_windsurf_rule(source, target) {
1170
1169
  const lines = [
1171
1170
  '---',
1172
1171
  `trigger: ${trigger}`,
1173
- `description: ${_yaml_scalar(description)}`,
1172
+ `description: ${_yaml_scalar(always_apply ? description : applies_when(description, meta))}`,
1174
1173
  `globs: ${globs.join(',')}`,
1175
1174
  '---',
1176
1175
  '',
@@ -1448,23 +1447,21 @@ function _render_native_model_md(src_md, tier) {
1448
1447
  return text.replace(_MODEL_TIER_RE, `model: ${model}`);
1449
1448
  }
1450
1449
  export function generate_claude_skills(active_skill_names = null) {
1451
- if (partitionActive(MODULE_STATE.PROJECT_ROOT))
1452
- active_skill_names = new Set();
1453
1450
  if (!_exists(MODULE_STATE.SKILLS_SOURCE)) {
1454
1451
  process.stderr.write(' ⚠️ dist/agent-src/skills/ not found — skipping skills\n');
1455
1452
  return 0;
1456
1453
  }
1457
- let skills = _iterdirSorted(MODULE_STATE.SKILLS_SOURCE)
1454
+ const all_skills = _iterdirSorted(MODULE_STATE.SKILLS_SOURCE)
1458
1455
  .filter((p) => _isDir(p))
1459
1456
  .map((p) => path.basename(p))
1460
1457
  .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
1458
+ // ADR-236: withhold every skill `~/.claude/skills` holds — see `keepInProjectLayer`.
1459
+ let skills = keepInProjectLayer('skills', all_skills);
1461
1460
  if (active_skill_names !== null) {
1462
1461
  skills = skills.filter((s) => active_skill_names.has(s));
1463
1462
  }
1464
1463
  const skill_set = new Set(skills);
1465
- const command_slugs = partitionActive(MODULE_STATE.PROJECT_ROOT)
1466
- ? new Set()
1467
- : new Set([...iterCommands()].map(([, slug]) => slug));
1464
+ const command_slugs = _emitted_wrapper_slugs(new Set(all_skills));
1468
1465
  _mkdirp(MODULE_STATE.CLAUDE_SKILLS_DIR);
1469
1466
  const auto = _read_model_auto_switch() === 'auto';
1470
1467
  for (const item of _iterdirSorted(MODULE_STATE.CLAUDE_SKILLS_DIR)) {
@@ -1549,6 +1546,17 @@ function _nested_command_subpath(source_file) {
1549
1546
  const sub = logical.slice('commands/'.length).replace(/\.md$/, '');
1550
1547
  return sub.includes('/') ? sub : null;
1551
1548
  }
1549
+ /** The emitter's four skips, in one place — see `emittedWrapperSlugs`. */
1550
+ const _wrapper_is_emitted = (f, s, skills) => !skills.has(s) &&
1551
+ _nested_command_subpath(f) === null &&
1552
+ !is_claude_builtin_name(s) &&
1553
+ !claudeLayerHolds('skills', s);
1554
+ /** Wrapper slugs the command emitter really writes — see `emittedWrapperSlugs`. */
1555
+ function _emitted_wrapper_slugs(skill_names) {
1556
+ return new Set([...iterCommands()]
1557
+ .filter(([f, s]) => _wrapper_is_emitted(f, s, skill_names))
1558
+ .map(([, slug]) => slug));
1559
+ }
1552
1560
  /**
1553
1561
  * Project-scope `.claude/commands/<cluster>/<sub>.md` — the colon form.
1554
1562
  *
@@ -1561,15 +1569,12 @@ function _nested_command_subpath(source_file) {
1561
1569
  *
1562
1570
  * Emitting the nested form here lets `generate_claude_commands` stop wrapping
1563
1571
  * the clustered commands: one listing, same reachability, and reachability no
1564
- * longer depends on a global deploy. Dedup AND precedence: `commandsWithheld`.
1572
+ * longer depends on a global deploy. Dedup AND precedence: `commandWithheld`.
1565
1573
  *
1566
1574
  * ADR-003 (colon canonical for clusters) and ADR-044 (flat commands stay
1567
1575
  * hyphenated) both hold: flat commands are untouched and keep their wrapper.
1568
1576
  */
1569
1577
  export function generate_claude_project_commands(active_command_slugs = null) {
1570
- if (commandsWithheld(MODULE_STATE.PROJECT_ROOT))
1571
- active_command_slugs = new Set();
1572
- // Returns before the stale sweep — invariant, see `commandsWithheld`.
1573
1578
  if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
1574
1579
  return 0;
1575
1580
  }
@@ -1579,7 +1584,7 @@ export function generate_claude_project_commands(active_command_slugs = null) {
1579
1584
  continue;
1580
1585
  }
1581
1586
  const sub = _nested_command_subpath(source_file);
1582
- if (sub !== null) {
1587
+ if (sub !== null && !commandWithheld(`${sub}.md`)) {
1583
1588
  nested.push([source_file, sub]);
1584
1589
  }
1585
1590
  }
@@ -1613,8 +1618,6 @@ export function generate_claude_project_commands(active_command_slugs = null) {
1613
1618
  return count;
1614
1619
  }
1615
1620
  export function generate_claude_commands(active_command_slugs = null) {
1616
- if (partitionActive(MODULE_STATE.PROJECT_ROOT))
1617
- active_command_slugs = new Set();
1618
1621
  if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
1619
1622
  process.stderr.write(' ⚠️ src/domains/ not found — skipping commands\n');
1620
1623
  return 0;
@@ -1630,6 +1633,7 @@ export function generate_claude_commands(active_command_slugs = null) {
1630
1633
  let count = 0;
1631
1634
  let skipped = 0;
1632
1635
  let reserved = 0;
1636
+ let withheld = 0;
1633
1637
  let rendered = 0;
1634
1638
  const auto = _read_model_auto_switch() === 'auto';
1635
1639
  for (const [source_file, slug] of iterCommands()) {
@@ -1658,6 +1662,12 @@ export function generate_claude_commands(active_command_slugs = null) {
1658
1662
  reserved += 1;
1659
1663
  continue;
1660
1664
  }
1665
+ // ADR-236 per-name: the INSTALLER re-projects visible flat commands as
1666
+ // `~/.claude/skills/<slug>/SKILL.md` (install.ts:_apply_claude_flat_command_wrappers).
1667
+ if (claudeLayerHolds('skills', slug)) {
1668
+ withheld += 1;
1669
+ continue;
1670
+ }
1661
1671
  current_slugs.add(slug);
1662
1672
  const skill_dir = path.join(MODULE_STATE.CLAUDE_SKILLS_DIR, slug);
1663
1673
  _mkdirp(skill_dir);
@@ -1706,6 +1716,9 @@ export function generate_claude_commands(active_command_slugs = null) {
1706
1716
  if (reserved) {
1707
1717
  msg += ` (${reserved} withheld — Claude Code built-in name)`;
1708
1718
  }
1719
+ if (withheld > 0) {
1720
+ msg += ` (${withheld} withheld — ~/.claude/skills carries the wrapper)`;
1721
+ }
1709
1722
  if (removed_dirs) {
1710
1723
  msg += ` (${removed_dirs} stale dirs removed)`;
1711
1724
  }
@@ -2019,19 +2032,13 @@ export function generate_persona_symlinks() {
2019
2032
  _print(' ⚠️ dist/agent-src/personas/ not found — skipping personas');
2020
2033
  return 0;
2021
2034
  }
2022
- const personas = _rglobSorted(MODULE_STATE.PERSONAS_SOURCE, '*.md')
2023
- .filter((p) => _isFile(p) && path.basename(p, '.md') !== 'README')
2024
- .map((p) => path.basename(p))
2025
- // glob('*.md') is non-recursive in Python; mirror that — only top level.
2026
- .filter((name) => _isFile(path.join(MODULE_STATE.PERSONAS_SOURCE, name)));
2027
2035
  // Python uses PERSONAS_SOURCE.glob("*.md") (non-recursive). Re-derive
2028
2036
  // from a direct listing to match exactly.
2029
2037
  const direct = _iterdirSorted(MODULE_STATE.PERSONAS_SOURCE)
2030
2038
  .filter((p) => p.endsWith('.md') && _isFile(p) && path.basename(p, '.md') !== 'README')
2031
2039
  .map((p) => path.basename(p))
2032
2040
  .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
2033
- void personas;
2034
- const partition = personaPartition(MODULE_STATE.PROJECT_ROOT, direct);
2041
+ const partition = personaPartition(direct);
2035
2042
  const tool_dirs = _filter_tool_dirs(PERSONA_TOOL_DIRS);
2036
2043
  let total = 0;
2037
2044
  for (const [tool_dir, rel_prefix] of Object.entries(tool_dirs)) {
package/docs/MIGRATION.md CHANGED
@@ -17,7 +17,7 @@ releaser here on every `release:major`.
17
17
  | Surface | Committed | Deprecation notice due | Removal due | Reversal condition |
18
18
  |---|---|---|---|---|
19
19
  | Free-text `compatibility` skill-frontmatter field (`src/scripts/schemas/skill.schema.json`) | 2026-08-14 | shipped 2026-08-14 — `deprecated: true` + superseded-by note in the schema; `harness_compat` lands beside it, additively | next major after the notice — **not pinned here** (removal date is maintainer-owned, same stance as the manifest `tier` entry, whose `sunset` shipped `null`) | The public Agent-Skills spec makes `compatibility` load-bearing for cross-host portability, or an external consumer is found reading it. `harness_compat` is package-local; `compatibility` mirrors the public spec, so keeping the mirror stays cheaply reversible. Superseded by `harness_compat` (class) + `runtime_requires` (probeable detail); both users (`docx-authoring`, `pdf-tools`) already carry both fields, so nothing breaks on removal day. |
20
- | `code_graph` native code-graph engine (`code_graph.enabled`, the `code-intelligence` skill's native arm, `code_graph_nudge_hook`) | 2026-07-28 | next major after 9.x | **REVISED 2026-08-15 — no removal date; see below** | A consumer case the graph answers and disciplined grep cannot. Measured null: recall 0.365 vs grep 0.797 (`docs/CLAIMS.md` `code-graph-retrieval-null`), measured 2026-07-28 against a build predating the 2026-08-22 extractor repair. `enabled: false` stays the DEFAULT — "permanent" is retracted here on 2026-08-26, because this row's own revision below withdraws the removal commitment and a withdrawn schedule cannot leave a permanence behind it. **Revision (ADR-232 track, maintainer decision 2026-08-15).** The original row committed removal to 11.0; the tree reached 12.0.0 with the runtime paths still registered, i.e. the commitment was missed. Rather than quietly re-dating it, the commitment is withdrawn with its reason recorded, because the measurement changed what removal is worth: **the payload this deprecation existed for has already shipped** — the parser pair (`web-tree-sitter@0.24.7` / `tree-sitter-wasms@0.1.13`, ~51 MB unpacked) moved from `dependencies` to `devDependencies` ahead of schedule, so no consumer installs it. What source removal would still free is ~112 K against a 27 M tree (0.4 %). Against that it costs a breaking change across four consumer-visible surfaces (CLI verb, `code-intelligence` skill, `external-code-graph-interop` rule, `hooks.code_graph.enabled`) plus one surface that is re-plumbing rather than deletion: `_lib/auto_dispatch.ts` routes the `definition` / `references` lookup classes to `primitive: 'code-graph-query'`, and `_lib/judgment_ladder.ts` calls that live at Rung 0. There is also no cheap middle — `code_graph/detect.ts` handles the `consumer` / `scip` / `native` source kinds in one type union, so stripping only the native engine while keeping consumer-index interop is a redesign. **New commitment:** the surfaces stay registered and disabled; removal happens when a concrete reason appears (a maintenance cost that bites, a conflicting redesign, or a consumer asking), not on a date. This row stays in the table so the decision is visible rather than forgotten — a withdrawn commitment recorded is not the folklore this table exists to prevent; an unrecorded one would be. |
20
+ | `code_graph` native code-graph engine (`code_graph.enabled`, the `code-intelligence` skill's native arm, `code_graph_nudge_hook`) | 2026-07-28 | next major after 9.x | **REVISED 2026-08-15 — no removal date; see below** | A consumer case the graph answers and disciplined grep cannot. Measured null: recall 0.365 vs grep 0.797 (`docs/CLAIMS.md` `code-graph-retrieval-null`), measured 2026-07-28 against a build predating the 2026-08-22 extractor repair. `enabled: false` stays the DEFAULT — "permanent" is retracted here on 2026-08-26, because this row's own revision below withdraws the removal commitment and a withdrawn schedule cannot leave a permanence behind it. **Revision (ADR-232 track, maintainer decision 2026-08-15).** The original row committed removal to 11.0; the tree reached 12.0.0 with the runtime paths still registered, i.e. the commitment was missed. Rather than quietly re-dating it, the commitment is withdrawn with its reason recorded, because the measurement changed what removal is worth: **the payload this deprecation existed for has already shipped** — the parser pair (`web-tree-sitter@0.24.7` / `tree-sitter-wasms@0.1.13`, ~51 MB unpacked) moved from `dependencies` to `devDependencies` ahead of schedule, so no consumer installed it. **REVERSED 2026-09-07 by ADR-259 (as amended) — this clause no longer holds and is kept only so the reversal is legible.** Delivery and measurement are decoupled: `web-tree-sitter` is a runtime `dependency` again and the three loadable grammars are vendored at `src/vendor/grammars/`, so a consumer now DOES install the engine. The measured null above is untouched and `enabled: false` stays the default — what changed is that the reopen trigger ("a consumer case the graph answers") became reachable at all, which it was not while no consumer could receive the engine. The install delta is recorded below under "14.21.x — the code-graph engine ships to consumers". What source removal would still free is ~112 K against a 27 M tree (0.4 %). Against that it costs a breaking change across four consumer-visible surfaces (CLI verb, `code-intelligence` skill, `external-code-graph-interop` rule, `hooks.code_graph.enabled`) plus one surface that is re-plumbing rather than deletion: `_lib/auto_dispatch.ts` routes the `definition` / `references` lookup classes to `primitive: 'code-graph-query'`, and `_lib/judgment_ladder.ts` calls that live at Rung 0. There is also no cheap middle — `code_graph/detect.ts` handles the `consumer` / `scip` / `native` source kinds in one type union, so stripping only the native engine while keeping consumer-index interop is a redesign. **New commitment:** the surfaces stay registered and disabled; removal happens when a concrete reason appears (a maintenance cost that bites, a conflicting redesign, or a consumer asking), not on a date. This row stays in the table so the decision is visible rather than forgotten — a withdrawn commitment recorded is not the folklore this table exists to prevent; an unrecorded one would be. |
21
21
  | `telegraph-speak` condenser (`telegraph.speak`, `src/rules/telegraph-speak.md`, `src/scripts/_lib/compile_time_toggles.ts`, `src/scripts/validate_telegraph_carveouts.ts`) | 2026-07-29 | shipped 2026-07-29 — dormant by default, `speak` defaults `false` | **not pinned here** — removal is authorized in principle and deliberately not executed (ADR `telegraph/0002` § Decision part 3), pending a `prose_only` bench (~$0.80). The date is maintainer-owned, same stance as the `compatibility` row above | An output-side bench clears the kill-criterion bar, at which point `telegraph.speak: true` restores the feature intact. Measured basis for dormancy: median vs_terse **−9.27 %** (API) / −5.47 % (exact `cl100k_base`) — the condenser emits MORE than a plain "be terse". Tracked as a row rather than left dormant-and-unlisted: dormancy without a row is untracked in *both* directions, neither scheduled nor recorded as a keep. Deliberately NOT a due version (that would invent a commitment ADR 0002 declined to make) and NOT a permanent keep (that would contradict its authorized removal). |
22
22
 
23
23
  ### Row status — what is currently late, and who owns it
@@ -42,6 +42,54 @@ reason, which no arithmetic can.
42
42
  or that promises a future breaking removal, gets a row here in the same commit
43
43
  that makes the promise. A promise with no row is not tracked and will be missed.
44
44
 
45
+ ## 14.21.x — the code-graph engine ships to consumers
46
+
47
+ 💡 advisory · 🔄 automatic — no action required.
48
+
49
+ `npm install @event4u/agent-config` now carries everything the native
50
+ code-graph engine needs, so `agent-config code-graph build` works on the next
51
+ command with no manual parser install (ADR-259, as amended 2026-09-07).
52
+
53
+ ### What changed
54
+
55
+ - `web-tree-sitter@0.24.7` moved from `devDependencies` to `dependencies`.
56
+ - Three compiled tree-sitter grammars are **vendored** in the published tarball
57
+ at `src/vendor/grammars/` — php, typescript (which also serves `.tsx`), and
58
+ javascript. These are the three `GRAMMAR_WASM` entries the engine can load.
59
+ - `tree-sitter-wasms` stays a `devDependency`. It is the source the vendored
60
+ copies are refreshed from, not a runtime dependency, so consumers do **not**
61
+ receive the 36-grammar pack.
62
+
63
+ ### Install delta, measured
64
+
65
+ Measured on 2026-09-07 with `npm pack --dry-run --json --ignore-scripts`,
66
+ against the same tree with the vendored directory excluded from `files[]`:
67
+
68
+ | Axis | Before | After | Delta |
69
+ |---|---|---|---|
70
+ | Compressed tarball | 9,821,600 B | 10,195,522 B | **+373,922 B** (+0.357 MiB) |
71
+ | Vendored grammars, on disk | 0 B | 3,802,618 B | **+3,802,618 B** (+3.63 MiB) |
72
+ | `web-tree-sitter` in `node_modules` | 0 B (dev-only) | ~389,120 B | **+~380 KiB** |
73
+
74
+ Per-grammar, uncompressed: `tree-sitter-php.wasm` 812,594 B ·
75
+ `tree-sitter-typescript.wasm` 2,342,690 B · `tree-sitter-javascript.wasm`
76
+ 647,334 B.
77
+
78
+ For contrast, the two rejected shapes: depending on `tree-sitter-wasms`
79
+ directly would have delivered all 36 grammars (51,765,657 B apparent, 49 MiB on
80
+ disk), and the 13-grammar set ADR-259 originally named measures 9,085,798 B
81
+ apparent / 1,016,804 B compressed — the latter would have put the tarball at
82
+ ~10.84 MB. Only three grammars are loadable today, so the other ten would have
83
+ been payload with no reader.
84
+
85
+ ### Rolling back
86
+
87
+ Set `hooks.code_graph.enabled: false` — which is already the default; the
88
+ engine ships disabled. Nothing runs the parsers unless you build a graph. To
89
+ drop the payload entirely, remove `src/vendor/grammars/` from `files[]` and
90
+ move `web-tree-sitter` back to `devDependencies`; the loader falls back to the
91
+ `tree-sitter-wasms` pack, which is this repository's own dev path.
92
+
45
93
  ## 8.x → 9.0.0 — consumer rule projection scoped by default
46
94
 
47
95
  9.0.0 flips the consumer rule-projection default to **scoped**: fresh
@@ -558,7 +558,7 @@ Rows follow template order, so a diff against the template reads straight down.
558
558
  | `hooks.runtime_journal.enabled` | C | `false` | configures code that runs on every dispatched hook event | consent |
559
559
  | `hooks.ui_route_nudge.enabled` | C | `false` | configures code that runs on every tool call | derivable — the UI-surface predicate plus the hook's own two-nudges-per-session cap |
560
560
  | `hooks.design_pass.enabled` | C | `false` | C-test 1 — it configures code that runs on every tool call and at every turn end, and unlike its two `hooks.*` neighbours its stop pass can BLOCK rather than warn | policy — whether an operator accepts a gate that can refuse a turn is a risk preference, not a fact the tree can compute. The two neighbours are `derivable` because they only ever warn; the block is the discriminator. `road-to-frontend-power` transfers the default flip to the owner for the same reason |
561
- | `hooks.code_graph.enabled` | C | `false` | configures code that runs on every tool call | derivable — the index-detection probe the nudge already runs; no index means silence |
561
+ | `hooks.code_graph.enabled` | C | `false` | INERT since 2026-09-07 — the nudge it gated was replaced by `code-graph-context`, which reads no flag. The KEY stays registered because `docs/MIGRATION.md` commits to exactly that ("the surfaces stay registered and disabled"): removing it is a breaking change for any consumer who set it, and the row was briefly marked REMOVED, which left the template's own leaf unclassified. Classified, not deleted. | derivable — the index-detection probe the replacement hook already runs; no index means silence |
562
562
  | `hooks.suggestion_capture.enabled` | C | `false` | configures code that runs at every turn end and every prompt | consent |
563
563
  | `decision_engine.surface_traces` | C | `false` | the decision engine’s own black box; the agent must not be able to close it | derivable — the engine's own active-gate state; there is nothing to surface when no gate fired |
564
564
  | `decision_engine.min_confidence` | C | `"off"` | the confidence gate | derivable — the confidence band the scoring engine already computes at the plan phase |
@@ -166,3 +166,76 @@ reviewer has to untangle.
166
166
 
167
167
  - `minimal-safe-diff` (rule) — Iron Law, the rule, the pre-fix shared-path repair clause, own-orphan cleanup. The pre-diff checklist, the red-flag catalog and the when-in-doubt line live HERE since A1.
168
168
  - [`active-remediation-mechanics`](active-remediation-mechanics.md) — the fix-now / note+ask / follow-up-PR ladder detail.
169
+
170
+ ## Re-derive the requirement, don't inherit the shape
171
+
172
+ ```
173
+ A REPAIR INHERITS THE SHAPE OF WHAT IT REPAIRS UNLESS YOU ASK OTHERWISE.
174
+ STATE THE REQUIREMENT IN ONE LINE, THEN ASK WHETHER THE CURRENT FORM
175
+ *CAN* EXPRESS IT. A FORM THAT CANNOT IS NOT A PARAMETER BUG.
176
+ ```
177
+
178
+ Applies to a downstream repair — clearing a red, or fixing what your own diff
179
+ broke — where the smallest change is a different VALUE and the form is
180
+ unexamined.
181
+
182
+ ### Why this is a guideline section and not a rule clause
183
+
184
+ It was drafted as a clause in `minimal-safe-diff` on 2026-09-07 and moved here
185
+ the same day, on the gate's own evidence rather than on preference.
186
+ `check_preamble_payload_budget` measured the clause at +460 delivered tokens,
187
+ then +271, then +180 after two rounds of cutting — a floor set by the Iron-Law
188
+ block itself. That payload is re-written on EVERY subagent spawn, and the rule it
189
+ would have lived in is `type: auto`: it fires on prompt keywords, not on tool
190
+ output, so a red test nobody mentioned never loads it.
191
+
192
+ Certain per-spawn cost against an activation path that misses the case the clause
193
+ was written for is a bad trade, and the deterministic half does not depend on it
194
+ — see § Honest limit below. The rule already routes here
195
+ (`routes_to: guideline:agent-infra/minimal-safe-diff-mechanics`), which is the
196
+ same reachability every other migrated body in this repository has.
197
+
198
+ ### Worked failure — 2026-09-07
199
+
200
+ A change emptied `<repo>/.claude/skills` of skills and left ~49 flat-command
201
+ wrappers behind. `resolveSkillsRoot` tried two workspace-relative roots and took
202
+ the first non-empty one, so it resolved the wrappers, and the skill ranker ranked
203
+ 49 command wrappers instead of 299 skills.
204
+
205
+ The repair chosen was to swap the ORDER of the two roots. The requirement was
206
+ *"rank every catalogue the session can reach"*, which **no ordering of a
207
+ first-hit loop can satisfy** — and the host-global root the whole task was about
208
+ appeared in neither position. The evidence had been measured an hour earlier and
209
+ sat in the same session's own notes: `~/.claude/skills` 307, `.claude/skills` 49,
210
+ `src/skills` 299.
211
+
212
+ Two things made it easy to miss, and both are the shape of the trap rather than
213
+ this instance of it:
214
+
215
+ - **The docstring framed the question.** It read *"First existing catalogue root
216
+ … `.claude/skills` before `src/skills` because a CONSUMER install carries the
217
+ former"* — i.e. it asked *which root wins*, and that question was answered. A
218
+ docstring records what the code decided; it never records whether that was the
219
+ decision to make.
220
+ - **The same docstring contained the premise of the correct answer.** If
221
+ consumers carry their own skills *and* the package ships skills, both must be
222
+ read. The sentence was read, used to justify an ordering, and its consequence
223
+ was not drawn.
224
+
225
+ ### Why the pre-work rules do not cover it
226
+
227
+ [`improve-before-implement`](../../../src/rules/improve-before-implement.md) does
228
+ not activate for bug fixes ("the problem is already defined") and
229
+ [`invite-challenge`](../../../src/rules/invite-challenge.md) excludes "evidenced
230
+ bug fixes". Both exclusions are right for the case they were written for and
231
+ wrong for a downstream repair, which presents as a defined problem while its
232
+ definition came from the broken code.
233
+
234
+ ### Honest limit
235
+
236
+ The rule is `type: auto`, so it fires on prompt keywords and not on tool output:
237
+ a red test nobody prompted about does not load it. The clause is the
238
+ model-carried half. The deterministic half is a comparison of the two lists that
239
+ diverged — `tests/contracts/catalogue_layer_parity.test.ts`, which reds when a
240
+ directory the delivery side treats as an artifact source is not a root the reader
241
+ reads, and which was measured to catch this exact defect on the pre-change tree.