@event4u/agent-config 14.12.0 → 14.13.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 (466) 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 +234 -0
  5. package/CONTRIBUTING.md +38 -0
  6. package/CREDITS.md +135 -0
  7. package/NOTICE +13 -0
  8. package/README.md +8 -6
  9. package/dist/agent-src/commands/analyze/inbox.md +109 -2
  10. package/dist/agent-src/commands/feature/dev.md +7 -2
  11. package/dist/agent-src/commands/ghostwriter/write.md +5 -1
  12. package/dist/agent-src/commands/implement-ticket.md +1 -1
  13. package/dist/agent-src/commands/mission/upgrade.md +1 -1
  14. package/dist/agent-src/commands/orchestrate.md +2 -2
  15. package/dist/agent-src/commands/post-as/me.md +5 -1
  16. package/dist/agent-src/commands/pr/merge.md +19 -6
  17. package/dist/agent-src/commands/roadmap/process-full.md +19 -5
  18. package/dist/agent-src/commands/tdd/red.md +12 -2
  19. package/dist/agent-src/commands/work.md +1 -1
  20. package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +40 -0
  21. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -2
  22. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +21 -0
  23. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -0
  24. package/dist/agent-src/contexts/execution/subagent-response-contract.md +40 -0
  25. package/dist/agent-src/contexts/execution/subagent-steering.md +13 -1
  26. package/dist/agent-src/contexts/execution/terminal-states.md +96 -0
  27. package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
  28. package/dist/agent-src/rules/broken-access-control.md +4 -0
  29. package/dist/agent-src/rules/code-provenance.md +4 -0
  30. package/dist/agent-src/rules/design-fidelity.md +1 -1
  31. package/dist/agent-src/rules/domain-safety-pii.md +4 -0
  32. package/dist/agent-src/rules/domain-safety-retention.md +4 -0
  33. package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
  34. package/dist/agent-src/rules/external-code-graph-interop.md +14 -12
  35. package/dist/agent-src/rules/improve-before-implement.md +6 -8
  36. package/dist/agent-src/rules/laravel-routing.md +1 -1
  37. package/dist/agent-src/rules/lethal-trifecta-guard.md +4 -0
  38. package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
  39. package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
  40. package/dist/agent-src/rules/source-confidentiality.md +12 -34
  41. package/dist/agent-src/rules/symfony-routing.md +1 -1
  42. package/dist/agent-src/rules/token-efficiency.md +14 -0
  43. package/dist/agent-src/rules/tool-safety.md +4 -0
  44. package/dist/agent-src/rules/untrusted-input-defense.md +4 -0
  45. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +247 -3
  46. package/dist/agent-src/scripts/guarded_baseline.ts +328 -0
  47. package/dist/agent-src/scripts/roadmap_set_step.ts +253 -0
  48. package/dist/agent-src/scripts/stub_queue.ts +82 -0
  49. package/dist/agent-src/scripts/stubs_due.ts +400 -0
  50. package/dist/agent-src/scripts/ticket_bundles.ts +84 -0
  51. package/dist/agent-src/scripts/update_roadmap_progress.ts +8 -4
  52. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +17 -4
  53. package/dist/agent-src/skills/brand-audit/SKILL.md +33 -0
  54. package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -0
  55. package/dist/agent-src/skills/database/SKILL.md +8 -0
  56. package/dist/agent-src/skills/dependency-upgrade/SKILL.md +27 -0
  57. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  58. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +17 -1
  60. package/dist/agent-src/skills/feature-planning/SKILL.md +9 -2
  61. package/dist/agent-src/skills/frontend-render-security/SKILL.md +44 -0
  62. package/dist/agent-src/skills/git-workflow/SKILL.md +1 -1
  63. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +38 -4
  64. package/dist/agent-src/skills/judge-artifact-completeness/rubrics/ticket-quality-score.json +2 -2
  65. package/dist/agent-src/skills/laravel/SKILL.md +35 -0
  66. package/dist/agent-src/skills/laravel-migration/SKILL.md +57 -3
  67. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +26 -0
  68. package/dist/agent-src/skills/playbook-authoring/SKILL.md +28 -1
  69. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
  70. package/dist/agent-src/skills/skill-writing/references/procedure.md +5 -5
  71. package/dist/agent-src/skills/sql-writing/SKILL.md +7 -1
  72. package/dist/agent-src/skills/standards-from-config/SKILL.md +27 -4
  73. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +11 -4
  74. package/dist/agent-src/skills/symfony-workflow/SKILL.md +35 -0
  75. package/dist/agent-src/skills/test-case-discovery/SKILL.md +85 -0
  76. package/dist/agent-src/skills/test-driven-development/SKILL.md +33 -9
  77. package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
  78. package/dist/agent-src/skills/testing-anti-patterns/process-anti-patterns.md +37 -0
  79. package/dist/agent-src/skills/ui-component-architect/SKILL.md +45 -2
  80. package/dist/agent-src/skills/upstream-contribute/SKILL.md +38 -1
  81. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +9 -4
  82. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  83. package/dist/agent-src/templates/roadmaps.md +97 -3
  84. package/dist/agent-src/templates/scripts/check_memory.ts +258 -17
  85. package/dist/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  86. package/dist/agent-src/templates/scripts/memory_lookup.ts +86 -0
  87. package/dist/agent-src/templates/scripts/memory_signal.ts +44 -0
  88. package/dist/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  89. package/dist/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  90. package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  91. package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  92. package/dist/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  93. package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  94. package/dist/cli/commands/uiAudit.js +124 -5
  95. package/dist/cli/registry.js +2 -0
  96. package/dist/cli-delegate/{chunk-HYWSRNUX.js → chunk-2DK2VJWG.js} +3 -3
  97. package/dist/cli-delegate/{chunk-JCW7ZLHK.js → chunk-4FS4WRXP.js} +5 -0
  98. package/dist/cli-delegate/{chunk-OMIVVCG2.js → chunk-A4QJ6ENT.js} +24 -0
  99. package/dist/cli-delegate/{chunk-JJA2QEWA.js → chunk-AKJGUQNO.js} +466 -285
  100. package/dist/cli-delegate/{chunk-E46KPN6K.js → chunk-BTWUYDGN.js} +424 -363
  101. package/dist/cli-delegate/{chunk-K4FFODTS.js → chunk-ILMUPLGW.js} +1227 -316
  102. package/dist/cli-delegate/{chunk-FF7WAFMR.js → chunk-KPBWFOPZ.js} +5 -1
  103. package/dist/cli-delegate/cmd_adr_effective.js +102 -0
  104. package/dist/cli-delegate/cmd_conformance.js +42 -10
  105. package/dist/cli-delegate/cmd_doctor.js +3 -3
  106. package/dist/cli-delegate/cmd_export.js +3 -2
  107. package/dist/cli-delegate/cmd_fleet.js +2 -2
  108. package/dist/cli-delegate/cmd_handoff.js +1 -1
  109. package/dist/cli-delegate/cmd_preflight.js +2 -2
  110. package/dist/cli-delegate/cmd_prune.js +3 -2
  111. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  112. package/dist/cli-delegate/cmd_settings_migrate.js +3 -2
  113. package/dist/cli-delegate/cmd_settings_set.js +2 -2
  114. package/dist/cli-delegate/cmd_sync.js +3 -2
  115. package/dist/cli-delegate/cmd_uninstall.js +3 -2
  116. package/dist/cli-delegate/cmd_validate.js +3 -2
  117. package/dist/discovery/deprecation-report.md +1 -1
  118. package/dist/discovery/discovery-manifest.json +57 -57
  119. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  120. package/dist/discovery/discovery-manifest.summary.md +1 -1
  121. package/dist/discovery/dormancy-report.md +1 -1
  122. package/dist/discovery/orphan-report.md +1 -1
  123. package/dist/discovery/packs.json +2 -2
  124. package/dist/discovery/trust-report.md +1 -1
  125. package/dist/discovery/workspaces.json +2 -2
  126. package/dist/hooks/dispatch.js +1836 -712
  127. package/dist/install/claudeRuleRewrite.js +254 -0
  128. package/dist/install/detect.js +29 -16
  129. package/dist/install/detect_php_shape.js +159 -0
  130. package/dist/install/install.mjs +634 -428
  131. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  132. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  133. package/dist/mcp/registry-manifest.json +3 -3
  134. package/dist/mcp/server.json +3 -3
  135. package/dist/mcp/server.mjs +4818 -2645
  136. package/dist/server/schemas/settings.js +3 -0
  137. package/dist/ui/assets/index-D0jaCRPM.js +29 -0
  138. package/dist/ui/index.html +1 -1
  139. package/docs/MIGRATION.md +1 -1
  140. package/docs/THIRD-PARTY-NOTICES.md +25 -0
  141. package/docs/contracts/provider-lifecycle.md +1 -1
  142. package/docs/contracts/settings-classes.md +10 -4
  143. package/docs/guidelines/agent-infra/5w2h-analysis.md +3 -3
  144. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +1 -0
  145. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +58 -3
  146. package/docs/guidelines/agent-infra/ask-when-uncertain-demos.md +2 -2
  147. package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +3 -3
  148. package/docs/guidelines/agent-infra/break-glass-usage.md +7 -7
  149. package/docs/guidelines/agent-infra/carve-out-predicates.md +3 -3
  150. package/docs/guidelines/agent-infra/comparison-matrix.md +1 -1
  151. package/docs/guidelines/agent-infra/critical-thinking.md +4 -4
  152. package/docs/guidelines/agent-infra/direct-answers-demos.md +2 -2
  153. package/docs/guidelines/agent-infra/domain-eval-anti-pattern.md +1 -1
  154. package/docs/guidelines/agent-infra/engineering-memory-data-format.md +3 -3
  155. package/docs/guidelines/agent-infra/existence-question-verdicts.md +80 -0
  156. package/docs/guidelines/agent-infra/first-principles.md +2 -2
  157. package/docs/guidelines/agent-infra/framework-neutrality-patterns.md +2 -2
  158. package/docs/guidelines/agent-infra/guarded-baseline.md +39 -0
  159. package/docs/guidelines/agent-infra/inversion-thinking.md +5 -5
  160. package/docs/guidelines/agent-infra/language-and-tone-examples.md +1 -1
  161. package/docs/guidelines/agent-infra/layered-settings.md +10 -10
  162. package/docs/guidelines/agent-infra/memory-access.md +1 -1
  163. package/docs/guidelines/agent-infra/mental-models.md +3 -3
  164. package/docs/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
  165. package/docs/guidelines/agent-infra/review-routing-data-format.md +7 -7
  166. package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +13 -7
  167. package/docs/guidelines/agent-infra/role-contracts.md +2 -2
  168. package/docs/guidelines/agent-infra/role-mode-router.md +3 -3
  169. package/docs/guidelines/agent-infra/rule-type-governance.md +2 -2
  170. package/docs/guidelines/agent-infra/scqa-framework.md +5 -5
  171. package/docs/guidelines/agent-infra/self-improvement-pipeline.md +6 -6
  172. package/docs/guidelines/agent-infra/simplicity-and-goal-demos.md +2 -2
  173. package/docs/guidelines/agent-infra/six-hats.md +3 -3
  174. package/docs/guidelines/agent-infra/skill-quality-checklist.md +6 -6
  175. package/docs/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
  176. package/docs/guidelines/agent-infra/systems-thinking.md +1 -1
  177. package/docs/guidelines/agent-infra/verify-before-complete-demos.md +2 -2
  178. package/docs/guidelines/augment-portability-patterns.md +6 -4
  179. package/docs/guidelines/component-oriented-and-oop-development.md +3 -2
  180. package/docs/guidelines/cross-role-handoff.md +2 -2
  181. package/docs/guidelines/design-fidelity-mechanics.md +1 -1
  182. package/docs/guidelines/docs/readme-size-and-splitting.md +3 -3
  183. package/docs/guidelines/php/database.md +90 -7
  184. package/docs/guidelines/prompt-templates.md +7 -7
  185. package/llms.txt +2 -2
  186. package/package.json +5 -2
  187. package/src/agent-src/scripts/archive_completed_roadmaps.ts +247 -3
  188. package/src/agent-src/scripts/guarded_baseline.ts +328 -0
  189. package/src/agent-src/scripts/roadmap_set_step.ts +253 -0
  190. package/src/agent-src/scripts/stub_queue.ts +82 -0
  191. package/src/agent-src/scripts/stubs_due.ts +400 -0
  192. package/src/agent-src/scripts/ticket_bundles.ts +84 -0
  193. package/src/agent-src/scripts/update_roadmap_progress.ts +8 -4
  194. package/src/agent-src/templates/scripts/check_memory.ts +258 -17
  195. package/src/agent-src/templates/scripts/check_memory_proposal.ts +9 -3
  196. package/src/agent-src/templates/scripts/memory_lookup.ts +86 -0
  197. package/src/agent-src/templates/scripts/memory_signal.ts +44 -0
  198. package/src/agent-src/templates/scripts/work_engine/directives/backend/implement.ts +90 -0
  199. package/src/agent-src/templates/scripts/work_engine/directives/backend/index.ts +11 -0
  200. package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +23 -1
  201. package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +10 -1
  202. package/src/agent-src/templates/scripts/work_engine/directives/mixed/stitch.ts +5 -1
  203. package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +102 -14
  204. package/src/config/agent-settings.template.yml +32 -3
  205. package/src/config/assurance-capability-registry.json +21 -9
  206. package/src/config/canonical-terms.yml +119 -0
  207. package/src/config/capability-native-outcome-bars.json +81 -0
  208. package/src/config/ci-local-parity.yml +80 -0
  209. package/src/config/drafting-channels.yml +43 -0
  210. package/src/config/estate-count-budget.json +38 -36
  211. package/src/config/evaluator-budgets.json +14 -14
  212. package/src/config/gate-coverage.yml +579 -63
  213. package/src/config/gate-reachability-exemptions.json +21 -0
  214. package/src/config/gate-violation-baselines.json +37 -19
  215. package/src/config/harness-evolution-budget.json +25 -0
  216. package/src/config/lapsed-beta-baseline.json +96 -0
  217. package/src/config/memory-twin-verdicts.yml +129 -0
  218. package/src/config/metric-registry.yml +124 -0
  219. package/src/config/pack-size-budget.json +16 -15
  220. package/src/config/preamble-payload-budget.json +8 -8
  221. package/src/config/profiles/balanced.ini +27 -0
  222. package/src/config/profiles/full.ini +27 -0
  223. package/src/config/profiles/minimal.ini +27 -0
  224. package/src/config/publish-surface.json +9 -2
  225. package/src/config/routing-coverage-seed.json +16 -2
  226. package/src/config/rule-stub-ceilings.json +126 -112
  227. package/src/config/turnaround-budget.json +66 -0
  228. package/src/install/claudeRuleRewrite.ts +288 -0
  229. package/src/install/detect.ts +60 -17
  230. package/src/install/detect_php_shape.ts +191 -0
  231. package/src/scripts/_cli/cmd_doctor.ts +27 -112
  232. package/src/scripts/_dispatch.bash +27 -0
  233. package/src/scripts/_lib/activation_ladder.ts +211 -0
  234. package/src/scripts/_lib/activation_states.ts +88 -0
  235. package/src/scripts/_lib/adherence_detectors.ts +143 -0
  236. package/src/scripts/_lib/arith_claims.ts +181 -0
  237. package/src/scripts/_lib/arm_ranking.ts +62 -0
  238. package/src/scripts/_lib/asset_delivery_ledger.ts +303 -0
  239. package/src/scripts/_lib/audit_field_provenance.ts +54 -0
  240. package/src/scripts/_lib/auto_dispatch.ts +32 -3
  241. package/src/scripts/_lib/bench_ab_size_claim.ts +98 -4
  242. package/src/scripts/_lib/benchmark_cache_fields.ts +158 -0
  243. package/src/scripts/_lib/candidate_proposer.ts +402 -0
  244. package/src/scripts/_lib/candidate_record.ts +601 -0
  245. package/src/scripts/_lib/capture_rate.ts +319 -0
  246. package/src/scripts/_lib/collector_denominator.ts +771 -0
  247. package/src/scripts/_lib/collector_record.ts +340 -0
  248. package/src/scripts/_lib/collector_store.ts +664 -0
  249. package/src/scripts/_lib/collector_supervision.ts +827 -0
  250. package/src/scripts/_lib/concern_estate.ts +77 -0
  251. package/src/scripts/_lib/config_chain.ts +477 -0
  252. package/src/scripts/_lib/config_cost.ts +209 -0
  253. package/src/scripts/_lib/council_settings_block.ts +6 -0
  254. package/src/scripts/_lib/curator_ops.ts +172 -0
  255. package/src/scripts/_lib/delivery_arm_experiment.ts +266 -0
  256. package/src/scripts/_lib/delivery_criticality.ts +126 -0
  257. package/src/scripts/_lib/doctor_runtime_checks.ts +70 -0
  258. package/src/scripts/_lib/empty_cycles.ts +88 -0
  259. package/src/scripts/_lib/eval_publication.ts +354 -0
  260. package/src/scripts/_lib/evaluation_vector.ts +283 -0
  261. package/src/scripts/_lib/evaluator_promotion.ts +225 -0
  262. package/src/scripts/_lib/evidence_basis.ts +118 -0
  263. package/src/scripts/_lib/exec_evidence.ts +8 -0
  264. package/src/scripts/_lib/experience_card.ts +272 -0
  265. package/src/scripts/_lib/experience_report.ts +151 -0
  266. package/src/scripts/_lib/experiment_freeze.ts +112 -0
  267. package/src/scripts/_lib/generated_by.ts +66 -0
  268. package/src/scripts/_lib/harness_evolution_guards.ts +229 -0
  269. package/src/scripts/_lib/hook_effect_probe.ts +141 -0
  270. package/src/scripts/_lib/host_denominator.ts +687 -0
  271. package/src/scripts/_lib/host_projection_reach.ts +157 -0
  272. package/src/scripts/_lib/ignored_blocker.ts +148 -0
  273. package/src/scripts/_lib/install_reach_checks.ts +495 -0
  274. package/src/scripts/_lib/json_duplicate_keys.ts +150 -0
  275. package/src/scripts/_lib/judge_hygiene.ts +228 -0
  276. package/src/scripts/_lib/legacy_boundary_map.ts +228 -0
  277. package/src/scripts/_lib/loop_guards.ts +299 -0
  278. package/src/scripts/_lib/loss_class.ts +129 -0
  279. package/src/scripts/_lib/machine_wake.ts +51 -0
  280. package/src/scripts/_lib/md_prose_lines.ts +177 -0
  281. package/src/scripts/_lib/minimality_tiebreak.ts +168 -0
  282. package/src/scripts/_lib/one_resolver_invariant.ts +449 -0
  283. package/src/scripts/_lib/orchestration_record.ts +253 -5
  284. package/src/scripts/_lib/outcome_envelope.ts +227 -0
  285. package/src/scripts/_lib/outcome_vocabularies.ts +125 -0
  286. package/src/scripts/_lib/paired_stats.ts +245 -0
  287. package/src/scripts/_lib/paired_verdict.ts +226 -0
  288. package/src/scripts/_lib/prefix_stable_surfaces.ts +123 -0
  289. package/src/scripts/_lib/privacy_class.ts +47 -0
  290. package/src/scripts/_lib/regression_neighbourhood.ts +259 -0
  291. package/src/scripts/_lib/repeated_failure.ts +105 -0
  292. package/src/scripts/_lib/repo_root.ts +111 -0
  293. package/src/scripts/_lib/review_skipped_record.ts +37 -0
  294. package/src/scripts/_lib/role_split.ts +338 -0
  295. package/src/scripts/_lib/runtime_journal.ts +1499 -0
  296. package/src/scripts/_lib/runtime_wiring_checks.ts +430 -0
  297. package/src/scripts/_lib/skill_catalogue.ts +11 -1
  298. package/src/scripts/_lib/source_digest.ts +200 -0
  299. package/src/scripts/_lib/source_redact.ts +260 -0
  300. package/src/scripts/_lib/source_shape.ts +502 -0
  301. package/src/scripts/_lib/source_snapshot_dedup.ts +199 -0
  302. package/src/scripts/_lib/strip_comments.d.mts +8 -0
  303. package/src/scripts/_lib/strip_comments.mjs +65 -0
  304. package/src/scripts/_lib/test_red_state.ts +154 -0
  305. package/src/scripts/_lib/trigger_shift.ts +144 -0
  306. package/src/scripts/_lib/untrusted_content.ts +82 -7
  307. package/src/scripts/adoption_report.ts +1 -1
  308. package/src/scripts/adr/evidence_census.ts +1 -1
  309. package/src/scripts/adr/regenerate_index.ts +26 -2
  310. package/src/scripts/adr_cite_check.ts +164 -1
  311. package/src/scripts/ai_council/blind_review.ts +11 -1
  312. package/src/scripts/ai_council/clients.ts +4 -0
  313. package/src/scripts/ai_council/config.ts +3 -0
  314. package/src/scripts/ai_council/consensus.ts +28 -8
  315. package/src/scripts/ai_council/consensus_round.ts +226 -0
  316. package/src/scripts/ai_council/disagreement_signal.ts +338 -0
  317. package/src/scripts/ai_council/inline_findings.ts +340 -0
  318. package/src/scripts/ai_council/judge_position_bias.ts +197 -0
  319. package/src/scripts/ai_council/orchestrator.ts +16 -203
  320. package/src/scripts/ai_council/orchestrator_results.ts +23 -3
  321. package/src/scripts/ai_council/pricing.ts +106 -0
  322. package/src/scripts/ai_council/prompts.ts +74 -7
  323. package/src/scripts/ai_council/provider_leakage_bench.ts +327 -0
  324. package/src/scripts/ai_council/py_format.ts +14 -3
  325. package/src/scripts/ai_council/recouncil_guard.ts +293 -0
  326. package/src/scripts/ai_council/response_record.ts +50 -0
  327. package/src/scripts/ai_council/topology_bench_manifest.ts +860 -0
  328. package/src/scripts/ai_council/topology_vocabulary.ts +175 -0
  329. package/src/scripts/asset_delivery_ledger.ts +157 -0
  330. package/src/scripts/attest_artifact.ts +193 -0
  331. package/src/scripts/audit_auto_rules.ts +2 -1
  332. package/src/scripts/audit_command_surface.ts +1 -1
  333. package/src/scripts/audit_mcp_tools.ts +2 -2
  334. package/src/scripts/audit_skill_overlap_allowlist.json +6 -1
  335. package/src/scripts/audit_user_type_axis.ts +2 -1
  336. package/src/scripts/bench_ab_clone.ts +215 -10
  337. package/src/scripts/bench_ab_integrity.ts +119 -5
  338. package/src/scripts/bench_ab_v2_stats.ts +40 -213
  339. package/src/scripts/build_archive_index.ts +1 -1
  340. package/src/scripts/build_linear_digest.ts +1 -1
  341. package/src/scripts/build_proof.ts +11 -4
  342. package/src/scripts/build_rule_trigger_matrix.ts +1 -1
  343. package/src/scripts/build_source_digests.ts +157 -0
  344. package/src/scripts/cache_realization_report.ts +103 -0
  345. package/src/scripts/check_adr_frontmatter.ts +126 -0
  346. package/src/scripts/check_benchmark_report_fields.ts +207 -0
  347. package/src/scripts/check_beta_review_markers.ts +235 -6
  348. package/src/scripts/check_bundle_path_leakage.ts +34 -1
  349. package/src/scripts/check_claims.ts +576 -6
  350. package/src/scripts/check_composite_arming.ts +35 -0
  351. package/src/scripts/check_concern_admissions.ts +365 -0
  352. package/src/scripts/check_enforcement_coverage.ts +28 -0
  353. package/src/scripts/check_estate_count.ts +142 -1
  354. package/src/scripts/check_gate_reachability.ts +383 -0
  355. package/src/scripts/check_host_loadability.ts +104 -0
  356. package/src/scripts/check_loss_class_declared.ts +308 -0
  357. package/src/scripts/check_md_language.ts +15 -98
  358. package/src/scripts/check_memory_proposal.ts +18 -11
  359. package/src/scripts/check_no_currency_in_cost_surfaces.ts +225 -0
  360. package/src/scripts/check_no_external_sources.ts +281 -6
  361. package/src/scripts/check_pack_conformance_fixture.ts +11 -1
  362. package/src/scripts/check_pack_size.ts +59 -2
  363. package/src/scripts/check_preamble_payload_budget.ts +114 -3
  364. package/src/scripts/check_prefix_stable_mutation.ts +372 -0
  365. package/src/scripts/check_publish_surface.ts +10 -1
  366. package/src/scripts/check_references.ts +24 -1
  367. package/src/scripts/check_rule_evidence_declaration.ts +316 -0
  368. package/src/scripts/check_rule_projection_integrity.ts +14 -4
  369. package/src/scripts/check_score_contract.ts +26 -3
  370. package/src/scripts/check_static_parity.ts +285 -0
  371. package/src/scripts/check_supervision_claim_atomicity.ts +498 -0
  372. package/src/scripts/check_suppression_hygiene.ts +15 -0
  373. package/src/scripts/ci_settle.ts +39 -2
  374. package/src/scripts/code_graph/build.ts +60 -3
  375. package/src/scripts/code_graph/extract.ts +44 -0
  376. package/src/scripts/code_graph/types.ts +24 -0
  377. package/src/scripts/collector_daemon.ts +714 -0
  378. package/src/scripts/config_cost_report.ts +302 -0
  379. package/src/scripts/consumer_matrix.ts +74 -1
  380. package/src/scripts/cost/budget.mjs +10 -3
  381. package/src/scripts/cost/track.mjs +10 -3
  382. package/src/scripts/council_cli.ts +37 -54
  383. package/src/scripts/dispatch_r2_reviewer.ts +46 -5
  384. package/src/scripts/evolution_lab.ts +980 -0
  385. package/src/scripts/external_sources_denylist.json +5 -18
  386. package/src/scripts/extract_audit_patterns.ts +21 -0
  387. package/src/scripts/fold_intake.ts +12 -1
  388. package/src/scripts/gen_discovery_baseline.ts +1 -1
  389. package/src/scripts/generate_capabilities_index.ts +12 -5
  390. package/src/scripts/generate_capability_matrix.ts +3 -3
  391. package/src/scripts/generate_command_flows.ts +2 -2
  392. package/src/scripts/generate_cookbook.ts +2 -2
  393. package/src/scripts/generate_index.ts +8 -1
  394. package/src/scripts/generate_knowledge_index.ts +2 -1
  395. package/src/scripts/generate_ownership_matrix.ts +2 -1
  396. package/src/scripts/generate_pack_manifests.ts +10 -2
  397. package/src/scripts/generate_role_experiences_catalog.ts +2 -2
  398. package/src/scripts/generate_settings_reference.ts +1 -1
  399. package/src/scripts/git_authorization_hook.ts +22 -1
  400. package/src/scripts/grade_target_readiness.ts +48 -5
  401. package/src/scripts/hook_effect_doctor.ts +261 -0
  402. package/src/scripts/hook_manifest.json +1 -1
  403. package/src/scripts/hook_manifest.yaml +98 -11
  404. package/src/scripts/hooks/block_speaking_inbox_dir.ts +271 -0
  405. package/src/scripts/hooks/block_unauthorized_git.ts +18 -0
  406. package/src/scripts/hooks/concern_registry.ts +4 -0
  407. package/src/scripts/hooks/dispatch_hook.ts +32 -0
  408. package/src/scripts/hooks/journal_record_hook.ts +345 -0
  409. package/src/scripts/hooks/prepush_metadata_sources.sh +110 -0
  410. package/src/scripts/hooks/run_continuation_hook.ts +44 -9
  411. package/src/scripts/hooks/shims/install.sh +61 -0
  412. package/src/scripts/hooks/shims/php +101 -0
  413. package/src/scripts/hooks/suggestion_capture_hook.ts +10 -0
  414. package/src/scripts/hot_context_hook.ts +16 -0
  415. package/src/scripts/install-hooks.sh +20 -5
  416. package/src/scripts/install.ts +17 -35
  417. package/src/scripts/inventory_frontmatter.ts +2 -1
  418. package/src/scripts/lint_adr_sweep_routing.ts +12 -0
  419. package/src/scripts/lint_canonical_terms.ts +604 -0
  420. package/src/scripts/lint_composition_review.ts +619 -0
  421. package/src/scripts/lint_consolidation_lineage.ts +666 -0
  422. package/src/scripts/lint_eval_specs.ts +593 -0
  423. package/src/scripts/lint_hook_manifest.ts +19 -0
  424. package/src/scripts/lint_host_portability.ts +373 -0
  425. package/src/scripts/lint_judge_prompt_expectation.ts +27 -1
  426. package/src/scripts/lint_memory_twin_parity.ts +335 -0
  427. package/src/scripts/lint_metric_consumers.ts +355 -0
  428. package/src/scripts/lint_skill_descriptions.ts +18 -1
  429. package/src/scripts/lint_skill_frontmatter_safety.ts +58 -0
  430. package/src/scripts/lint_skill_link_reach.ts +252 -0
  431. package/src/scripts/lint_skill_trigger_corpus.ts +426 -23
  432. package/src/scripts/lint_ticket_buildable.ts +17 -1
  433. package/src/scripts/mcp_server/consumer_tool_catalog.json +1 -1
  434. package/src/scripts/mcp_server/tool_catalog_source.ts +1 -1
  435. package/src/scripts/measure_docs_dead_links.ts +348 -0
  436. package/src/scripts/measure_host_capture.ts +461 -0
  437. package/src/scripts/migration_status.ts +3 -3
  438. package/src/scripts/model_rule_injection.ts +60 -0
  439. package/src/scripts/orchestration_record.ts +14 -0
  440. package/src/scripts/preamble_byte_census.ts +139 -10
  441. package/src/scripts/prepack-check.mjs +4 -1
  442. package/src/scripts/probe_turnaround.ts +384 -0
  443. package/src/scripts/probe_unblocked_ask.ts +279 -0
  444. package/src/scripts/render_benchmark_md.ts +1 -1
  445. package/src/scripts/rule_activation_census.ts +70 -9
  446. package/src/scripts/rule_backlinks.ts +1 -1
  447. package/src/scripts/run_lifecycle_suite.ts +208 -0
  448. package/src/scripts/run_skill_evals.ts +40 -0
  449. package/src/scripts/schemas/delivery-manifest.schema.json +43 -0
  450. package/src/scripts/schemas/drafting-channels.schema.json +43 -0
  451. package/src/scripts/schemas/evals.schema.json +24 -1
  452. package/src/scripts/schemas/rule.schema.json +52 -1
  453. package/src/scripts/schemas/skill.schema.json +17 -2
  454. package/src/scripts/skill_overlap.ts +7 -1
  455. package/src/scripts/skill_tools/run_block_d_eval.ts +8 -1
  456. package/src/scripts/skill_tools/score_skill_relevance.ts +84 -3
  457. package/src/scripts/skill_tools/suggest_skill_for_task.ts +8 -1
  458. package/src/scripts/skill_usage_report.ts +2 -1
  459. package/src/scripts/stubs_due.ts +211 -0
  460. package/src/scripts/sweep_source_surfaces.ts +678 -0
  461. package/src/scripts/sync_pr_branch.ts +3 -3
  462. package/src/scripts/trigger_eval_grandfather.json +202 -222
  463. package/src/scripts/utilization_report.ts +22 -2
  464. package/src/server/schemas/settings.ts +5 -0
  465. package/src/templates/marketing-copy.yml +1 -1
  466. package/dist/ui/assets/index-Cxgy63kz.js +0 -26
@@ -0,0 +1,666 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Consolidation-lineage checker — an incomplete lineage becomes a finding.
4
+ *
5
+ * A *consolidating* artifact declares itself a master over sibling proposals.
6
+ * It presents its content as adjudicated: parents named, conflicts resolved, a
7
+ * kill register saying what was rejected and why. When a parent is missing from
8
+ * that list its content is not killed, it is **undiscussed** — and nothing in
9
+ * the artifact distinguishes those two states. This gate makes the omission
10
+ * visible; it never judges whether the omission was correct.
11
+ *
12
+ * Measured population that motivated it:
13
+ * `agents/evidence/analysis/consolidation-lineage-census-2026-08-26.md` — four
14
+ * inbox folders carrying a declared consolidation, four with an incomplete
15
+ * lineage. n=4 is a lower bound on a population found by grep, not a rate.
16
+ *
17
+ * ## What it reads
18
+ *
19
+ * The canonical declaration is the frontmatter list `consolidates:`. Five
20
+ * legacy spellings are recognised so a legacy artifact parses to the SAME
21
+ * parent set rather than to an empty one — an empty set would report every
22
+ * legacy consolidation as declaring zero parents, which is a finding storm that
23
+ * discredits the check on its first run:
24
+ *
25
+ * 1. `consolidates:` frontmatter list (canonical)
26
+ * 2. `supersedes_analysis:` frontmatter list
27
+ * 3. `**Inputs consolidated:**` prose bullet list
28
+ * 4. `**Ersetzt als führendes Proposal:**` prose bullet list
29
+ * 5. `Master-Konsolidierung` + table markdown table, first column a name
30
+ * 6. prose supersession sentence "supersedes ... `a` and `b`"
31
+ *
32
+ * Shape 6 is a correction to the roadmap's count of five, recorded rather than
33
+ * applied silently. The five are the shapes the census GREPPED for, i.e. the
34
+ * ways an artifact announces "I am a consolidation". Shape 6 announces the same
35
+ * relation in prose and is invisible to that grep — but the overlap finding
36
+ * (F3) needs it, because in `evolve/` the omitted sibling declares supersession
37
+ * over exactly the master's two parents in a prose sentence and in no field.
38
+ * Without shape 6 the F3 verification the roadmap asks for cannot be met on its
39
+ * own evidence.
40
+ *
41
+ * ## Findings
42
+ *
43
+ * - `omitted-sibling` a `road-to-*.md` in the same folder that the declared
44
+ * parent set does not name.
45
+ * - `missing-parent` a declared parent with no matching file — the lineage
46
+ * names something nobody can open.
47
+ * - `overlapping-sets` two artifacts in one folder declaring overlapping parent
48
+ * sets while neither names the other: an artifact that
49
+ * reads as the settled answer while a peer of equal
50
+ * standing exists.
51
+ * - `claims-without-field` consolidation vocabulary and no parseable
52
+ * declaration. The trigger is the claim, not the filename:
53
+ * two of the four census omissions were in files with no
54
+ * `-master` in the name.
55
+ *
56
+ * ## Exit codes
57
+ *
58
+ * 0 = clean, or findings in report mode (the default). 1 = findings in
59
+ * `--strict`. 2 = a usage or scope error. Report-mode-by-default is the risk-1
60
+ * mitigation from the roadmap: an inbox folder routinely holds files that are
61
+ * not parents, and a finding that is usually wrong gets ignored.
62
+ */
63
+
64
+ import * as fs from 'node:fs';
65
+ import * as os from 'node:os';
66
+ import * as path from 'node:path';
67
+ import { fileURLToPath, pathToFileURL } from 'node:url';
68
+
69
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
70
+ import { reportScanned } from './_lib/scan_scope.js';
71
+
72
+ const _HERE = fileURLToPath(import.meta.url);
73
+ const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
74
+
75
+ const FM_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
76
+ const ROADMAP_RE = /^road-to-.*\.md$/i;
77
+
78
+ /**
79
+ * Vocabulary that makes an artifact CLAIM to be a consolidation — restricted to
80
+ * a SELF-declaration in the header.
81
+ *
82
+ * Deliberately narrow, and the narrowness is risk 1 from the roadmap: an inbox
83
+ * folder routinely holds files that merely *discuss* consolidation, and a
84
+ * finding that is usually wrong gets ignored — worse than no finding.
85
+ *
86
+ * Measured on the four census folders, 2026-08-27: an unrestricted
87
+ * `/consolidat|konsolidier|supersedes/i` over the whole file body fired
88
+ * `claims-without-field` on **9** files, and **all 9 were parents** discussing
89
+ * the consolidation rather than masters declaring one — a 9/9 false-positive
90
+ * rate on its first run. Restricting to a self-referential phrase within the
91
+ * first `HEADER_LINES` lines drops it to 0 on those folders while still
92
+ * catching the one real case in the wider corpus
93
+ * (`database-structure/road-to-database-mastery-master.md`, whose declaration
94
+ * is an inline prose clause with no list).
95
+ */
96
+ const CLAIM_RE =
97
+ /Master-Konsolidierung|Inputs consolidated|Ersetzt als führendes Proposal|deliberately supersedes|^\s*(consolidates|supersedes_analysis)\s*:/im;
98
+
99
+ /** How much of a file counts as its header for CLAIM_RE. */
100
+ const HEADER_LINES = 40;
101
+
102
+ export type DeclKind =
103
+ | 'consolidates'
104
+ | 'supersedes_analysis'
105
+ | 'inputs-consolidated'
106
+ | 'ersetzt-als-fuehrendes-proposal'
107
+ | 'master-konsolidierung-table'
108
+ | 'prose-supersession'
109
+ | 'none';
110
+
111
+ export interface Declaration {
112
+ kind: DeclKind;
113
+ /** Normalised parent stems, e.g. `road-to-shared-spine`. */
114
+ parents: string[];
115
+ /**
116
+ * True when the artifact claims a consolidation but no shape yielded a
117
+ * parent set. Deliberately distinct from `parents: []` with `kind: 'none'`,
118
+ * which means "makes no claim".
119
+ */
120
+ unparseable: boolean;
121
+ }
122
+
123
+ export interface Finding {
124
+ code: 'omitted-sibling' | 'missing-parent' | 'overlapping-sets' | 'claims-without-field';
125
+ folder: string;
126
+ file: string;
127
+ detail: string;
128
+ }
129
+
130
+ /**
131
+ * Normalise a declared parent to a comparable stem.
132
+ *
133
+ * Legacy lists carry the name in five different dresses: backticked or bare,
134
+ * with or without `.md`, with a download-collision `(1)` suffix, and followed by
135
+ * arbitrary prose (`road-to-gated-self-evolution` v3 (Claude-Session) — P0–P7).
136
+ * Everything after the first token is commentary.
137
+ */
138
+ export function normalizeParent(raw: string): string {
139
+ let s = raw.trim();
140
+ s = s.replace(/^[-*]\s+/, '');
141
+ const tick = /`([^`]+)`/.exec(s);
142
+ if (tick) {
143
+ s = tick[1] as string;
144
+ } else {
145
+ s = (s.split(/[\s—–,;|]/)[0] ?? '').trim();
146
+ }
147
+ s = s.replace(/^.*\//, '');
148
+ s = s.replace(/\.md$/i, '');
149
+ s = s.replace(/\(\d+\)$/, '');
150
+ s = s.trim();
151
+ // A glob is a description of a file set, not a file. Prose that mentions
152
+ // `road-to-*.md` is talking ABOUT roadmaps, not declaring a parent.
153
+ if (/[*?\[\]]/.test(s)) return '';
154
+ return s;
155
+ }
156
+
157
+ function frontmatterList(fm: string, key: string): string[] | null {
158
+ const lines = fm.split(/\r?\n/);
159
+ const head = new RegExp(`^${key}:\\s*(.*)$`);
160
+ for (let i = 0; i < lines.length; i += 1) {
161
+ const m = head.exec(lines[i] as string);
162
+ if (!m) continue;
163
+ const inline = (m[1] ?? '').trim();
164
+ if (inline.startsWith('[')) {
165
+ return inline
166
+ .slice(1, inline.lastIndexOf(']') === -1 ? undefined : inline.lastIndexOf(']'))
167
+ .split(',')
168
+ .map((x) => normalizeParent(x.replace(/["']/g, '')))
169
+ .filter(Boolean);
170
+ }
171
+ const out: string[] = [];
172
+ for (let j = i + 1; j < lines.length; j += 1) {
173
+ const line = lines[j] as string;
174
+ if (/^\s*#/.test(line)) continue;
175
+ const item = /^\s+-\s+(.*)$/.exec(line);
176
+ if (item) {
177
+ out.push(normalizeParent((item[1] as string).replace(/#.*$/, '')));
178
+ continue;
179
+ }
180
+ if (line.trim() === '') continue;
181
+ break;
182
+ }
183
+ return out.filter(Boolean);
184
+ }
185
+ return null;
186
+ }
187
+
188
+ /** A prose `**Label:**` heading followed by a `- ` bullet list. */
189
+ function proseList(body: string, label: RegExp): string[] | null {
190
+ const lines = body.split(/\r?\n/);
191
+ for (let i = 0; i < lines.length; i += 1) {
192
+ if (!label.test(lines[i] as string)) continue;
193
+ const out: string[] = [];
194
+ for (let j = i + 1; j < lines.length; j += 1) {
195
+ const line = lines[j] as string;
196
+ const item = /^\s*[->]*\s*[-*]\s+(.*)$/.exec(line);
197
+ if (item) {
198
+ const n = normalizeParent(item[1] as string);
199
+ if (n.startsWith('road-to-')) out.push(n);
200
+ continue;
201
+ }
202
+ if (line.trim() === '' || /^>\s*$/.test(line)) continue;
203
+ break;
204
+ }
205
+ if (out.length > 0) return out;
206
+ }
207
+ return null;
208
+ }
209
+
210
+ /** A `Master-Konsolidierung` marker followed by a table whose rows name files. */
211
+ function konsolidierungTable(body: string): string[] | null {
212
+ if (!/Master-Konsolidierung/i.test(body)) return null;
213
+ const out: string[] = [];
214
+ for (const line of body.split(/\r?\n/)) {
215
+ if (!line.trimStart().startsWith('|')) continue;
216
+ for (const cell of line.split('|')) {
217
+ const tick = /`(road-to-[^`]+)`/.exec(cell);
218
+ if (tick) out.push(normalizeParent(tick[1] as string));
219
+ }
220
+ }
221
+ return out.length > 0 ? out : null;
222
+ }
223
+
224
+ /** "…supersedes the planning shape of `a` and `b`…" spread over wrapped lines. */
225
+ function proseSupersession(body: string): string[] | null {
226
+ const flat = body.replace(/\r?\n/g, ' ');
227
+ const m = /supersedes\b([^.]*)\./i.exec(flat);
228
+ if (!m) return null;
229
+ const out: string[] = [];
230
+ for (const tick of (m[1] as string).matchAll(/`(road-to-[^`]+)`/g)) {
231
+ out.push(normalizeParent(tick[1] as string));
232
+ }
233
+ return out.length > 0 ? out : null;
234
+ }
235
+
236
+ export function parseDeclaration(text: string, surface: Surface = 'inbox'): Declaration {
237
+ const fmMatch = FM_RE.exec(text);
238
+ const fm = fmMatch ? (fmMatch[1] as string) : '';
239
+ // A declaration is a HEADER statement. Reading the whole body finds the
240
+ // word "supersedes" a thousand lines into a discussion and turns a sentence
241
+ // about roadmaps into a parent set — measured on this repository's own
242
+ // estate, where two roadmaps were flagged for prose that merely discusses a
243
+ // consolidation. Same window, and same reason, as CLAIM_RE.
244
+ const body = (fmMatch ? text.slice(fmMatch[0].length) : text)
245
+ .split(/\r?\n/)
246
+ .slice(0, HEADER_LINES)
247
+ .join('\n');
248
+
249
+ const shapes: ReadonlyArray<readonly [DeclKind, () => string[] | null]> = [
250
+ ['consolidates', () => frontmatterList(fm, 'consolidates')],
251
+ ['supersedes_analysis', () => frontmatterList(fm, 'supersedes_analysis')],
252
+ ['inputs-consolidated', () => proseList(body, /\*\*Inputs consolidated:?\*\*/i)],
253
+ [
254
+ 'ersetzt-als-fuehrendes-proposal',
255
+ () => proseList(body, /\*\*Ersetzt als führendes Proposal:?\*\*/i),
256
+ ],
257
+ ['master-konsolidierung-table', () => konsolidierungTable(body)],
258
+ ['prose-supersession', () => proseSupersession(body)],
259
+ ];
260
+
261
+ const active = surface === 'estate' ? shapes.slice(0, 2) : shapes;
262
+ for (const [kind, read] of active) {
263
+ const parents = read();
264
+ if (parents && parents.length > 0) {
265
+ return { kind, parents: [...new Set(parents)].sort(), unparseable: false };
266
+ }
267
+ }
268
+ // A claim with no readable set is UNPARSEABLE, never an empty declaration.
269
+ if (CLAIM_RE.test(text.split(/\r?\n/).slice(0, HEADER_LINES).join('\n'))) {
270
+ return { kind: 'none', parents: [], unparseable: true };
271
+ }
272
+ return { kind: 'none', parents: [], unparseable: false };
273
+ }
274
+
275
+ interface Artifact {
276
+ stem: string;
277
+ file: string;
278
+ decl: Declaration;
279
+ }
280
+
281
+ /**
282
+ * Which surface a folder belongs to. One axis, because the two properties it
283
+ * decides are not independent in practice.
284
+ *
285
+ * `inbox` — a drafting folder holding one session's sibling proposals
286
+ * (`agents/tmp/`, `agents/tmp.old/`). Every `road-to-*.md` beside the master is
287
+ * a candidate parent, so an omission is a finding; and the declaration may be
288
+ * in any of the six spellings, because these artifacts predate the canonical
289
+ * field. This is the shape the census measured.
290
+ *
291
+ * `estate` — the active roadmap set under `agents/roadmaps/`. Two differences,
292
+ * both measured on this repository rather than assumed:
293
+ *
294
+ * 1. **No sibling inference.** Estate files are unrelated by construction, so
295
+ * inferring parents from neighbours makes every roadmap a mandatory parent
296
+ * of every other — 11 omitted-sibling findings on a 12-file estate, all
297
+ * wrong. That is the roadmap's risk 1 and the hazard the deciding council
298
+ * named: "blocking on an unvalidated universe risks turning legitimate
299
+ * neighboring files into mandatory parents".
300
+ * 2. **Frontmatter declarations only.** The four prose spellings read a
301
+ * header window, and an estate roadmap's header routinely *describes* an
302
+ * inbox consolidation it was authored from. Measured: prose recognition
303
+ * turned two such descriptions into parent sets, one of them a `road-to-*`
304
+ * glob. A tracked roadmap declares its lineage in the canonical field —
305
+ * that is what step 1.1 of the roadmap fixed the field name FOR — so the
306
+ * legacy readers are scoped to the artifacts that need them.
307
+ */
308
+ export type Surface = 'inbox' | 'estate';
309
+
310
+ export function analyseFolder(
311
+ dir: string,
312
+ folderLabel = path.basename(dir),
313
+ surface: Surface = 'inbox',
314
+ ): Finding[] {
315
+ const findings: Finding[] = [];
316
+ let names: string[];
317
+ try {
318
+ names = fs.readdirSync(dir).filter((n) => ROADMAP_RE.test(n)).sort();
319
+ } catch {
320
+ return findings;
321
+ }
322
+ const artifacts: Artifact[] = names.map((n) => ({
323
+ stem: n.replace(/\.md$/i, ''),
324
+ file: n,
325
+ decl: parseDeclaration(fs.readFileSync(path.join(dir, n), 'utf-8'), surface),
326
+ }));
327
+ const present = new Set(artifacts.map((a) => a.stem));
328
+
329
+ for (const a of artifacts) {
330
+ if (a.decl.unparseable) {
331
+ findings.push({
332
+ code: 'claims-without-field',
333
+ folder: folderLabel,
334
+ file: a.file,
335
+ detail: 'declares a consolidation in prose but no parent set could be parsed — add `consolidates:`',
336
+ });
337
+ continue;
338
+ }
339
+ if (a.decl.parents.length === 0) continue;
340
+
341
+ for (const p of a.decl.parents) {
342
+ if (!present.has(p)) {
343
+ findings.push({
344
+ code: 'missing-parent',
345
+ folder: folderLabel,
346
+ file: a.file,
347
+ detail: `declares parent \`${p}\` but no such file is present`,
348
+ });
349
+ }
350
+ }
351
+ const omitted =
352
+ surface === 'inbox'
353
+ ? artifacts.filter((b) => b.stem !== a.stem && !a.decl.parents.includes(b.stem)).map((b) => b.stem)
354
+ : [];
355
+ if (omitted.length > 0) {
356
+ findings.push({
357
+ code: 'omitted-sibling',
358
+ folder: folderLabel,
359
+ file: a.file,
360
+ detail: `declared ${a.decl.parents.length}, present ${artifacts.length - 1}, omitted ${omitted
361
+ .map((o) => `\`${o}\``)
362
+ .join(', ')}`,
363
+ });
364
+ }
365
+ }
366
+
367
+ const declaring = artifacts.filter((a) => a.decl.parents.length > 0);
368
+ for (let i = 0; i < declaring.length; i += 1) {
369
+ for (let j = i + 1; j < declaring.length; j += 1) {
370
+ const a = declaring[i] as Artifact;
371
+ const b = declaring[j] as Artifact;
372
+ const shared = a.decl.parents.filter((p) => b.decl.parents.includes(p));
373
+ if (shared.length === 0) continue;
374
+ if (a.decl.parents.includes(b.stem) || b.decl.parents.includes(a.stem)) continue;
375
+ findings.push({
376
+ code: 'overlapping-sets',
377
+ folder: folderLabel,
378
+ file: `${a.file} + ${b.file}`,
379
+ detail: `both declare ${shared.map((s) => `\`${s}\``).join(', ')} and neither names the other`,
380
+ });
381
+ }
382
+ }
383
+ return findings;
384
+ }
385
+
386
+ interface Args {
387
+ roots: string[];
388
+ surface: Surface;
389
+ flat: boolean;
390
+ strict: boolean;
391
+ json: boolean;
392
+ quiet: boolean;
393
+ selfTest: boolean;
394
+ }
395
+
396
+ function parseArgs(argv: string[]): Args {
397
+ const a: Args = {
398
+ roots: [],
399
+ surface: 'inbox',
400
+ flat: false,
401
+ strict: false,
402
+ json: false,
403
+ quiet: false,
404
+ selfTest: false,
405
+ };
406
+ let surfaceGiven = false;
407
+ for (let i = 0; i < argv.length; i += 1) {
408
+ const v = argv[i] as string;
409
+ if (v === '--flat') a.flat = true;
410
+ else if (v === '--strict') a.strict = true;
411
+ else if (v === '--json') a.json = true;
412
+ else if (v === '--quiet') a.quiet = true;
413
+ else if (v === '--self-test') a.selfTest = true;
414
+ else if (v === '--root') a.roots.push(path.resolve(REPO_ROOT, argv[++i] as string));
415
+ else if (v.startsWith('--root=')) a.roots.push(path.resolve(REPO_ROOT, v.slice('--root='.length)));
416
+ else if (v === '--surface' || v.startsWith('--surface=')) {
417
+ const raw = v.startsWith('--surface=') ? v.slice('--surface='.length) : (argv[++i] as string);
418
+ if (raw !== 'inbox' && raw !== 'estate') {
419
+ process.stderr.write(`lint_consolidation_lineage: --surface must be inbox|estate\n`);
420
+ process.exit(2);
421
+ }
422
+ a.surface = raw;
423
+ surfaceGiven = true;
424
+ }
425
+ else {
426
+ process.stderr.write(`lint_consolidation_lineage: unrecognized argument: ${v}\n`);
427
+ process.exit(2);
428
+ }
429
+ }
430
+ if (a.roots.length === 0) {
431
+ // The default root is the ACTIVE ESTATE, whose files are unrelated by
432
+ // construction — so sibling inference is off unless asked for.
433
+ a.roots.push(path.join(REPO_ROOT, 'agents', 'roadmaps'));
434
+ a.flat = true;
435
+ if (!surfaceGiven) a.surface = 'estate';
436
+ }
437
+ return a;
438
+ }
439
+
440
+ /**
441
+ * Prove the gate can go red without touching the tree.
442
+ *
443
+ * A gate whose corpus is legitimately clean reports "0 findings" whether it
444
+ * works or not. The self-test builds the census's own shapes in a temp dir and
445
+ * asserts each finding fires, so a green run on the real tree is a statement
446
+ * about the tree rather than about the checker.
447
+ */
448
+ export function selfTest(): number {
449
+ const tmp = fs.mkdtempSync(path.join(fs.realpathSync(os.tmpdir()), 'lineage-'));
450
+ const w = (rel: string, body: string): void => {
451
+ const abs = path.join(tmp, rel);
452
+ fs.mkdirSync(path.dirname(abs), { recursive: true });
453
+ fs.writeFileSync(abs, body);
454
+ };
455
+ const cli = (dir: string, extra: readonly string[]): number =>
456
+ runGateCli(REPO_ROOT, 'src/scripts/lint_consolidation_lineage.ts', ['--root', path.join(tmp, dir), '--strict', ...extra], REPO_ROOT);
457
+
458
+ // Rejection is measured under `--strict`. The gate ships in REPORT mode by
459
+ // council decision, so its default exit code carries no verdict — asserting
460
+ // against it would prove nothing. `--strict` is the same detection with a
461
+ // non-zero exit, which is what a self-test needs and what a canary cannot
462
+ // supply for a report-mode gate.
463
+ w('cmp/road-to-master.md', '---\nconsolidates:\n - road-to-a.md\n - road-to-ghost.md\n---\n# m\n');
464
+ w('cmp/road-to-a.md', '# a\n');
465
+ w('cmp/road-to-b.md', '---\nsupersedes_analysis:\n - road-to-a\n---\n# b\n');
466
+ w('claim/road-to-a.md', '# a\n');
467
+ w('claim/road-to-m.md', '# m\n\n**Status: ersetzt als führendes Proposal:** `road-to-a` v1, prose only.\n');
468
+ w('clean/road-to-a.md', '# a\n');
469
+ w('clean/road-to-b.md', '# b\n');
470
+ w('clean/road-to-m.md', '---\nconsolidates:\n - road-to-a.md\n - road-to-b.md\n---\n# m\n');
471
+ w('estate-ghost/road-to-m.md', '---\nconsolidates:\n - road-to-never-existed.md\n---\n# m\n');
472
+ w(
473
+ 'estate-describes/road-to-m.md',
474
+ '---\nstatus: draft\n---\n# m\n\n> **Source:** an inbox folder. The master there\n> supersedes — `road-to-x` v3 and `road-to-y`.\n\nA set difference over `road-to-*.md`.\n',
475
+ );
476
+ w('estate-describes/road-to-other.md', '# unrelated estate work\n');
477
+
478
+ const spellings: ReadonlyArray<readonly [DeclKind, string]> = [
479
+ ['consolidates', '---\nconsolidates:\n - road-to-a.md\n - road-to-b.md\n---\n#\n'],
480
+ ['supersedes_analysis', '---\nsupersedes_analysis:\n - road-to-a\n - road-to-b\n---\n#\n'],
481
+ ['inputs-consolidated', '# m\n\n**Inputs consolidated:**\n- `road-to-a(1).md`\n- `road-to-b.md`\n'],
482
+ [
483
+ 'ersetzt-als-fuehrendes-proposal',
484
+ '# m\n\n> **Ersetzt als führendes Proposal:**\n> - `road-to-a` v3 — P0\n> - `road-to-b` — Phase 0\n',
485
+ ],
486
+ [
487
+ 'master-konsolidierung-table',
488
+ '# m — Master-Konsolidierung\n\n| ID | Roadmap |\n|---|---|\n| R1 | `road-to-a` |\n| R2 | `road-to-b` |\n',
489
+ ],
490
+ [
491
+ 'prose-supersession',
492
+ '# m\n\n> This document deliberately supersedes the planning shape of\n> `road-to-a` and `road-to-b`.\n',
493
+ ],
494
+ ];
495
+
496
+ const cases: SelfTestCase[] = [
497
+ {
498
+ name: 'a folder with an omitted sibling, a ghost parent and two competing masters',
499
+ expect: 'reject',
500
+ run: () => cli('cmp', []),
501
+ },
502
+ {
503
+ name: 'a consolidation claim with no readable parent list',
504
+ expect: 'reject',
505
+ run: () => cli('claim', []),
506
+ },
507
+ {
508
+ name: 'a complete folder stays silent — without this, a checker returning every code would pass',
509
+ expect: 'accept',
510
+ run: () => cli('clean', []),
511
+ },
512
+ {
513
+ name: 'estate surface still reports a canonical declaration naming a file that does not exist',
514
+ expect: 'reject',
515
+ run: () => cli('estate-ghost', ['--surface', 'estate']),
516
+ },
517
+ {
518
+ name: 'estate surface does NOT read a roadmap describing a consolidation as declaring one',
519
+ expect: 'accept',
520
+ run: () => cli('estate-describes', ['--surface', 'estate']),
521
+ },
522
+ {
523
+ name: 'all six declaration spellings parse to the identical parent set',
524
+ expect: 'accept',
525
+ run: () => {
526
+ for (const [kind, body] of spellings) {
527
+ const d = parseDeclaration(body);
528
+ if (d.kind !== kind || d.parents.join(',') !== 'road-to-a,road-to-b' || d.unparseable) {
529
+ process.stdout.write(
530
+ ` spelling ${kind} parsed as ${d.kind} -> [${d.parents.join(', ')}]\n`,
531
+ );
532
+ return 1;
533
+ }
534
+ }
535
+ return 0;
536
+ },
537
+ },
538
+ ];
539
+
540
+ const code = runSelfTest({
541
+ gate: 'lint_consolidation_lineage',
542
+ cases,
543
+ minCases: 6,
544
+ minRejectCases: 3,
545
+ });
546
+ fs.rmSync(tmp, { recursive: true, force: true });
547
+ return code;
548
+ }
549
+
550
+ export function main(argv: string[] | null = null): number {
551
+ const args = parseArgs(argv ?? process.argv.slice(2));
552
+ if (args.selfTest) return selfTest();
553
+
554
+ const folders: Array<{ dir: string; label: string }> = [];
555
+ // ENUMERATION, not a count, is this gate's dead-scope invariant.
556
+ //
557
+ // AI council 2026-08-30, anthropic + openai, convergent on option (b). The
558
+ // count floor that used to guard this scope (`min_scanned: 5` in
559
+ // gate-coverage.yml) sat over a population under deliberate drawdown to
560
+ // ZERO, so every successful drain red the build and the only available
561
+ // move was lowering the number again — a treadmill that would have ended at
562
+ // a floor of 0, which is no floor at all.
563
+ //
564
+ // What replaced it has to live HERE rather than in check_gate_coverage, and
565
+ // that placement is the load-bearing half of the verdict: an independent
566
+ // `existsSync` in the coverage gate would observe a directory without
567
+ // proving that THIS linter enumerated it. openai's seat put the invariant
568
+ // as "successful enumeration of the exact declared root by the production
569
+ // linter invocation". So a declared root that is missing, is not a
570
+ // directory, or cannot be read is a hard failure naming the resolved path —
571
+ // where the old code silently `continue`d, contributed zero, and passed
572
+ // under the `allowEmpty` reason below.
573
+ //
574
+ // What this deliberately does NOT claim, because openai's seat listed the
575
+ // gap and it would be dishonest to imply otherwise: it detects a missing or
576
+ // renamed root. It does not detect a glob narrowed to match nothing,
577
+ // enumeration in the wrong working directory, or roadmaps moved into nested
578
+ // directories the walk no longer traverses. Those remain uncovered.
579
+ for (const root of args.roots) {
580
+ let stat: fs.Stats;
581
+ try {
582
+ stat = fs.statSync(root);
583
+ } catch (err) {
584
+ process.stderr.write(
585
+ `❌ lint_consolidation_lineage: declared scan root does not exist: ${root}\n`
586
+ + ` (${(err as Error).message})\n`
587
+ + ' A root that cannot be resolved is a DEAD SCOPE, not an empty estate.\n',
588
+ );
589
+ return 1;
590
+ }
591
+ if (!stat.isDirectory()) {
592
+ process.stderr.write(
593
+ `❌ lint_consolidation_lineage: declared scan root is not a directory: ${root}\n`,
594
+ );
595
+ return 1;
596
+ }
597
+ try {
598
+ fs.readdirSync(root);
599
+ } catch (err) {
600
+ process.stderr.write(
601
+ `❌ lint_consolidation_lineage: declared scan root cannot be enumerated: ${root}\n`
602
+ + ` (${(err as Error).message})\n`
603
+ + ' Unreadable is not empty — a permission error must not read as a clean scan.\n',
604
+ );
605
+ return 1;
606
+ }
607
+ // A root that directly holds roadmaps IS a folder. Without this, a
608
+ // `--root` pointed at a single consolidation folder scans zero files
609
+ // and exits green — the "gates that scan nothing exit green" shape the
610
+ // roadmap's risk 3 names, arriving through the CLI instead of CI.
611
+ const direct = fs.readdirSync(root).filter((n) => ROADMAP_RE.test(n)).length > 0;
612
+ if (direct || args.flat) {
613
+ folders.push({ dir: root, label: path.relative(REPO_ROOT, root) || '.' });
614
+ }
615
+ if (args.flat) continue;
616
+ for (const e of fs.readdirSync(root, { withFileTypes: true }).sort((x, y) => x.name.localeCompare(y.name))) {
617
+ if (e.isDirectory()) folders.push({ dir: path.join(root, e.name), label: e.name });
618
+ }
619
+ }
620
+
621
+ const findings: Finding[] = [];
622
+ let scanned = 0;
623
+ for (const f of folders) {
624
+ const before = findings.length;
625
+ findings.push(...analyseFolder(f.dir, f.label, args.surface));
626
+ void before;
627
+ try {
628
+ scanned += fs.readdirSync(f.dir).filter((n) => ROADMAP_RE.test(n)).length;
629
+ } catch {
630
+ /* unreadable folder contributes nothing */
631
+ }
632
+ }
633
+
634
+ if (args.json) {
635
+ process.stdout.write(`${JSON.stringify({ scanned, findings }, null, 2)}\n`);
636
+ }
637
+
638
+ reportScanned({
639
+ gate: 'lint_consolidation_lineage',
640
+ scanned,
641
+ units: 'roadmap file(s)',
642
+ roots: args.roots.map((r) => path.relative(REPO_ROOT, r)),
643
+ allowEmpty:
644
+ 'The active roadmap estate is under deliberate drawdown and may legitimately ' +
645
+ 'reach zero files; an empty estate is a completed drain, not a dead scan root. ' +
646
+ 'The count is published on every run so an empty scan stays visible rather ' +
647
+ 'than silently green.',
648
+ });
649
+
650
+ if (!args.json && !args.quiet) {
651
+ for (const f of findings) {
652
+ process.stdout.write(` ${f.code} · ${f.folder}/${f.file}: ${f.detail}\n`);
653
+ }
654
+ const verb = args.strict ? 'error' : 'finding';
655
+ process.stdout.write(
656
+ findings.length === 0
657
+ ? `✅ lint_consolidation_lineage: ${scanned} roadmap file(s), no lineage ${verb}s.\n`
658
+ : `⚠️ lint_consolidation_lineage: ${findings.length} lineage ${verb}(s) across ${folders.length} folder(s).\n`,
659
+ );
660
+ }
661
+ return args.strict && findings.length > 0 ? 1 : 0;
662
+ }
663
+
664
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
665
+ process.exit(main());
666
+ }