@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,168 @@
1
+ /**
2
+ * Minimality tie-break — the FOUR criteria of decision E5, in its committed order.
3
+ *
4
+ * `road-to-governed-harness-evolution` Phase 4, step 4.5.
5
+ *
6
+ * > *Order per E5: the FOUR criteria `tokens → artifacts → scope →
7
+ * > precedence`. The fifth criterion the skipped parent added — simpler
8
+ * > mechanism — is OUT.*
9
+ * > verify: **two candidates with identical vectors resolve deterministically
10
+ * > under the committed order.**
11
+ *
12
+ * ## Why four and not five
13
+ *
14
+ * E5 (decided 2026-08-30, AI council, anthropic + openai, 2/2) rejected
15
+ * "simpler mechanism" on a timing argument rather than a taste one: by the time
16
+ * this function runs, both candidates have survived selection evaluation and
17
+ * every hygiene check, so there is **no outcome signal left to measure
18
+ * simplicity against**. Admitting it converts a mechanical decision into a
19
+ * reviewer's vote. The four that survive are all countable off candidate
20
+ * metadata — token count, artifact count, a scope enum, a precedence rank —
21
+ * which is what makes the result reproducible rather than argued.
22
+ *
23
+ * This module therefore has no place to put a fifth criterion:
24
+ * {@link MINIMALITY_ORDER} is the whole list, {@link breakTie} walks exactly it,
25
+ * and `tests/scripts/minimality_tiebreak.test.ts` pins both the order and its
26
+ * arity. A future fifth criterion has to move E5 first.
27
+ *
28
+ * ## The order is load-bearing, which is why inverting it is tested
29
+ *
30
+ * The two source proposals ordered these criteria in opposite directions, so
31
+ * the same pair of candidates resolves to different winners depending on the
32
+ * choice. The test asserts a fixture whose winner FLIPS under the inverted
33
+ * order — a tie-break whose order did not matter would not need a council
34
+ * decision behind it.
35
+ *
36
+ * ## Identity is a stabiliser, not a criterion
37
+ *
38
+ * {@link breakTie} returns `winner: null` when all four criteria tie: that is
39
+ * the honest answer, and it is deterministic. {@link orderByMinimality} needs a
40
+ * TOTAL order to sort with, so it falls back to the candidate id — an arbitrary
41
+ * but stable stabiliser that expresses no preference about the candidates and
42
+ * is deliberately NOT a fifth criterion. A caller that needs to know whether a
43
+ * decision was made reads {@link breakTie}, never the sort position.
44
+ */
45
+
46
+ /** The committed order. E5, option A. Walked left to right; first difference decides. */
47
+ export const MINIMALITY_ORDER = ['tokens', 'artifacts', 'scope', 'precedence'] as const;
48
+
49
+ export type MinimalityCriterion = (typeof MINIMALITY_ORDER)[number];
50
+
51
+ /**
52
+ * Blast-radius scope, narrowest first. Lower rank wins — a candidate that
53
+ * touches one artifact is more minimal than one that touches a whole pack.
54
+ */
55
+ export const SCOPE_ORDER = ['single-artifact', 'module', 'pack', 'repo'] as const;
56
+ export type CandidateScope = (typeof SCOPE_ORDER)[number];
57
+
58
+ /**
59
+ * Precedence rank, least binding first. Lower rank wins — a candidate that
60
+ * lands as advisory guidance is more minimal than one that lands in the kernel,
61
+ * because the kernel binds every session whether or not the change helps it.
62
+ */
63
+ export const PRECEDENCE_ORDER = ['advisory', 'auto', 'always', 'kernel'] as const;
64
+ export type CandidatePrecedence = (typeof PRECEDENCE_ORDER)[number];
65
+
66
+ /** Everything the tie-break reads. All four fields are counted, none is judged. */
67
+ export interface MinimalityMetadata {
68
+ candidate_id: string;
69
+ /** Tokens the candidate adds to the surface it lands on. */
70
+ tokens: number;
71
+ /** Artifacts the candidate creates or edits. */
72
+ artifacts: number;
73
+ scope: CandidateScope;
74
+ precedence: CandidatePrecedence;
75
+ }
76
+
77
+ export interface TieBreakResult {
78
+ /** The winning candidate id, or `null` when all four criteria tie. */
79
+ winner: string | null;
80
+ /** Which criterion decided, or `null` when none did. */
81
+ decided_by: MinimalityCriterion | null;
82
+ /** One sentence a report can print verbatim. */
83
+ reason: string;
84
+ }
85
+
86
+ export class MinimalityMetadataError extends Error {
87
+ constructor(message: string) {
88
+ super(message);
89
+ this.name = 'MinimalityMetadataError';
90
+ }
91
+ }
92
+
93
+ function rankOf<T extends string>(order: readonly T[], value: T, field: string): number {
94
+ const i = order.indexOf(value);
95
+ if (i < 0) {
96
+ throw new MinimalityMetadataError(`unknown ${field}: '${String(value)}'`);
97
+ }
98
+ return i;
99
+ }
100
+
101
+ /** The four criterion values as comparable integers, lower being more minimal. */
102
+ function costs(m: MinimalityMetadata): Record<MinimalityCriterion, number> {
103
+ if (!Number.isFinite(m.tokens) || !Number.isFinite(m.artifacts)) {
104
+ throw new MinimalityMetadataError(`${m.candidate_id}: tokens and artifacts must be finite`);
105
+ }
106
+ return {
107
+ tokens: m.tokens,
108
+ artifacts: m.artifacts,
109
+ scope: rankOf(SCOPE_ORDER, m.scope, 'scope'),
110
+ precedence: rankOf(PRECEDENCE_ORDER, m.precedence, 'precedence'),
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Walk {@link MINIMALITY_ORDER} and return at the first criterion that differs.
116
+ *
117
+ * `order` exists for the test that proves the order is load-bearing; production
118
+ * callers pass nothing and get E5's committed order.
119
+ */
120
+ export function breakTie(
121
+ a: MinimalityMetadata,
122
+ b: MinimalityMetadata,
123
+ order: readonly MinimalityCriterion[] = MINIMALITY_ORDER,
124
+ ): TieBreakResult {
125
+ const ca = costs(a);
126
+ const cb = costs(b);
127
+ for (const criterion of order) {
128
+ if (ca[criterion] === cb[criterion]) continue;
129
+ const winner = ca[criterion] < cb[criterion] ? a : b;
130
+ return {
131
+ winner: winner.candidate_id,
132
+ decided_by: criterion,
133
+ reason:
134
+ `${winner.candidate_id} wins on ${criterion} ` +
135
+ `(${String(ca[criterion])} vs ${String(cb[criterion])}); ` +
136
+ `criteria before it tied under the committed order ${order.join(' -> ')}`,
137
+ };
138
+ }
139
+ return {
140
+ winner: null,
141
+ decided_by: null,
142
+ reason:
143
+ `${a.candidate_id} and ${b.candidate_id} tie on all ${String(order.length)} criteria ` +
144
+ `(${order.join(' -> ')}); E5 admits no further criterion, so this escalates rather than resolving`,
145
+ };
146
+ }
147
+
148
+ /**
149
+ * A deterministic TOTAL order over candidates.
150
+ *
151
+ * Ties on all four criteria fall back to the candidate id — see the header:
152
+ * that is a sort stabiliser, not a fifth criterion, and it never appears in a
153
+ * {@link TieBreakResult}.
154
+ */
155
+ export function orderByMinimality(
156
+ candidates: readonly MinimalityMetadata[],
157
+ ): readonly MinimalityMetadata[] {
158
+ return [...candidates].sort((a, b) => {
159
+ const ca = costs(a);
160
+ const cb = costs(b);
161
+ for (const criterion of MINIMALITY_ORDER) {
162
+ if (ca[criterion] !== cb[criterion]) {
163
+ return ca[criterion] - cb[criterion];
164
+ }
165
+ }
166
+ return a.candidate_id < b.candidate_id ? -1 : a.candidate_id > b.candidate_id ? 1 : 0;
167
+ });
168
+ }
@@ -0,0 +1,449 @@
1
+ /**
2
+ * The one-resolver invariant, made checkable.
3
+ *
4
+ * `judgment_ladder.ts` is the ONE task-side resolver that decides which
5
+ * dispatch rung a task takes, council included. Its own module docstring says
6
+ * so, and says two further things that were equally unenforced: that no fourth
7
+ * parallel classifier may be "bolted on beside it", and that it is
8
+ * "deliberately independent of `ai_council/necessity.ts`", the council's OWN
9
+ * council-internal necessity gate.
10
+ *
11
+ * All three were prose. A docstring cannot fail, so a second task-side council
12
+ * router could land beside the ladder and every gate in this tree would stay
13
+ * green — which is what
14
+ * `road-to-inbox-harvest-2026-08-e-council-topology-evidence` step 0.5 asks to
15
+ * be closed: "lock the one-resolver invariant in documentation AND in a test".
16
+ *
17
+ * ## This file PARSES. It does not lex. That is the whole point.
18
+ *
19
+ * Three successive R2 review rounds killed three successive text-scanning
20
+ * implementations, each defect introduced by the repair for the last:
21
+ *
22
+ * | Round | Approach | What a fresh reviewer measured |
23
+ * |---|---|---|
24
+ * | 1 | no comment/string handling | false POSITIVE: a router name in a comment or string counted as a declaration |
25
+ * | 2 | ordered regexes, block comments first | false NEGATIVE: a line comment containing a glob opened a spurious block comment. 12 files under `src/` lost top-level exports |
26
+ * | 3 | hand-written character scanner | false NEGATIVE, worse: a backtick inside a REGEX LITERAL read as a template opener, and templates do not end at a newline. **54 files, 231 exports lost** |
27
+ *
28
+ * Round 3's trigger was ordinary, not exotic: `check_portability.ts:741`
29
+ * contains a regex with a backtick in it.
30
+ *
31
+ * The AI council was asked for guidance at the N=3 budget and **split** —
32
+ * one seat for withdrawing the guard, one for parsing properly. Both seats
33
+ * classified withdrawal and narrowing as OWNER-RESERVED and both refused
34
+ * another hand-lexing round, so parsing is the only option a council may
35
+ * execute. Both recorded the same principle:
36
+ *
37
+ * > A gate must not implement a partial lexer or parser for a language when an
38
+ * > authoritative parser for it is already a dependency.
39
+ *
40
+ * and a second, which is the one that explains three rounds of near-misses:
41
+ *
42
+ * > A repair is tested against the violated PROPERTY and representative
43
+ * > mutations, never against the reproducer's literal spelling.
44
+ *
45
+ * `typescript` is already a dependency. Comments, strings, template literals
46
+ * and regex literals are therefore the parser's problem, which is where they
47
+ * belong.
48
+ *
49
+ * **That sentence used to end "and the entire defect class above is gone rather
50
+ * than relocated", and round 5 showed it was false by one line.** Round 4
51
+ * widened WHICH files are read to include `.tsx` without widening HOW they are
52
+ * parsed — every file was handed over as non-JSX TypeScript — so a `.tsx`
53
+ * module's JSX text tokenized as ordinary TS and rounds 2 and 3's defect
54
+ * classes were both live again inside the repair that added the extension. The
55
+ * script kind is now derived from the extension. For that class the claim is
56
+ * true; the limits below are what remains, and they are limits rather than
57
+ * defects.
58
+ *
59
+ * ## The frozen claim — what this guard does and does not assert
60
+ *
61
+ * **Asserts, syntactically:** no module outside the sanctioned resolver
62
+ * EXPORTS A BINDING WHOSE NAME matches a router pattern, in any export form the
63
+ * TypeScript parser recognises; the sanctioned resolver itself exports one; and
64
+ * the sanctioned resolver names no council-internal module in any import,
65
+ * re-export, dynamic import or `require` specifier.
66
+ *
67
+ * **Does NOT assert, and no reading of it should:** anything requiring symbol
68
+ * resolution or a module graph. A router exported under an unrelated name, or
69
+ * reached through an alias chain this file cannot follow, is outside the claim.
70
+ *
71
+ * **Known limits, recorded by R2 round 5 and deliberately NOT repaired**, because
72
+ * each repair moved the review scope and forced another round; the loop was
73
+ * terminated on the one blocking finding. All are measured, none is a hole a
74
+ * second resolver reaches by accident:
75
+ *
76
+ * 1. A **dotted** namespace (`namespace A.B { … }`) nests a `ModuleDeclaration`
77
+ * where the block form nests a `ModuleBlock`, so its body is unwalked while
78
+ * the block form is covered.
79
+ * 2. {@link exportsRouterFunction} never received the namespace walk
80
+ * {@link exportedNames} got, so the two disagree on a router declared inside
81
+ * a namespace in the sanctioned file.
82
+ * 3. `export declare function classifyLadder(…)` — an AMBIENT declaration with
83
+ * no body — is accepted as a callable resolver.
84
+ * 4. A namespace the module does not export has its members counted as module
85
+ * exports, so a non-exported `namespace Internal { export class … }` reports
86
+ * `second-resolver`. A false positive.
87
+ * 5. A **symlinked directory** is invisible (`isDirectory()` is false for a
88
+ * symlink), and a UTF-16 file is counted in `scanned` while being read as
89
+ * mojibake. Both are silent-green.
90
+ *
91
+ * Limits 1-4 are syntactic and repairable; 5 is discovery. They are written
92
+ * here rather than in a helper's docstring because a buried caveat is not a
93
+ * disclosure. */
94
+ import fs from 'node:fs';
95
+ import path from 'node:path';
96
+ import ts from 'typescript';
97
+
98
+ /** The single sanctioned task-side resolver, repo-relative. */
99
+ export const SANCTIONED_RESOLVER = path.join('src', 'scripts', '_lib', 'judgment_ladder.ts');
100
+
101
+ /** The council's own internal surface. The resolver does not import from it. */
102
+ export const COUNCIL_INTERNAL_DIR = path.join('src', 'scripts', 'ai_council');
103
+
104
+ /** Root walked by {@link checkOneResolver}, repo-relative. */
105
+ export const SCAN_ROOT = 'src';
106
+
107
+ /**
108
+ * Module file extensions walked. R2 round-4 finding: `.ts` alone left the
109
+ * tree's 28 `.tsx` modules invisible, and the gap was MASKED — `src/ui/`
110
+ * contributes `.ts` paths to `scanned`, so the directory looked covered while
111
+ * two thirds of its modules were never read. The anti-vacuity discriminator
112
+ * cannot see a per-extension gap, which is the third time in four rounds that
113
+ * a blind spot hid behind a non-empty `scanned`.
114
+ */
115
+ const SCANNED_EXTS = ['.ts', '.tsx', '.mts', '.cts'] as const;
116
+ const TEST_SUFFIXES = ['.test.ts', '.test.tsx', '.test.mts', '.test.cts'] as const;
117
+
118
+ /** Directory names never walked. */
119
+ const SKIP_DIRS = new Set(['node_modules', '__tests__', 'dist', '.git']);
120
+
121
+ /** Names that mark an exported binding as a task-side council/dispatch ROUTER. */
122
+ export const ROUTER_NAMES: readonly RegExp[] = [
123
+ /^\w*Council\w*Router$/,
124
+ /^classifyLadder$/,
125
+ /^\w*resolveCouncilRoute\w*$/,
126
+ ];
127
+
128
+ export interface Violation {
129
+ readonly file: string;
130
+ readonly kind:
131
+ | 'second-resolver'
132
+ | 'council-import-in-resolver'
133
+ | 'resolver-missing'
134
+ | 'resolver-is-not-a-resolver';
135
+ readonly detail: string;
136
+ }
137
+
138
+ export interface ScanResult {
139
+ readonly violations: readonly Violation[];
140
+ /** Every file actually read, repo-relative. Empty means the scan found nothing to look at. */
141
+ readonly scanned: readonly string[];
142
+ }
143
+
144
+ /**
145
+ * Parse one module.
146
+ *
147
+ * `fileName` is not cosmetic. R2 round-5 finding, and it is the sharpest of the
148
+ * five rounds: the previous version hard-coded `'probe.ts'` and
149
+ * `ts.ScriptKind.TS` for EVERY file, while round 4 had just widened
150
+ * {@link SCANNED_EXTS} to admit `.tsx`. A `.tsx` module was therefore parsed as
151
+ * non-JSX TypeScript, so JSX text tokenized as ordinary TS — resurrecting the
152
+ * two defect classes that killed rounds 2 and 3, inside the repair that
153
+ * introduced the extension. The script kind is now derived from the extension,
154
+ * so the parser is told what it is reading.
155
+ */
156
+ function parse(text: string, fileName = 'probe.ts'): ts.SourceFile {
157
+ const kind = fileName.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
158
+ return ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true, kind);
159
+ }
160
+
161
+ function hasExportModifier(node: ts.Node): boolean {
162
+ const mods = (node as { modifiers?: readonly ts.ModifierLike[] }).modifiers;
163
+ return (mods ?? []).some((m) => m.kind === ts.SyntaxKind.ExportKeyword);
164
+ }
165
+
166
+ /** Every name a binding pattern introduces, so `export const { X } = mod` is seen. */
167
+ function bindingNames(name: ts.BindingName, out: string[]): void {
168
+ if (ts.isIdentifier(name)) {
169
+ out.push(name.text);
170
+ return;
171
+ }
172
+ for (const el of name.elements) {
173
+ if (ts.isBindingElement(el)) bindingNames(el.name, out);
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Every identifier this module exports, by any syntax the parser recognises:
179
+ * declarations with an `export` modifier, `export default`, named exports,
180
+ * aliased exports, re-exports, type-only exports, and `export * as NS from`.
181
+ */
182
+ export function exportedNames(text: string, fileName?: string): string[] {
183
+ const names: string[] = [];
184
+ collect(parse(text, fileName).statements, names);
185
+ return names;
186
+ }
187
+
188
+ function collect(statements: readonly ts.Statement[], names: string[]): void {
189
+ for (const st of statements) {
190
+ if (ts.isVariableStatement(st) && hasExportModifier(st)) {
191
+ for (const d of st.declarationList.declarations) bindingNames(d.name, names);
192
+ continue;
193
+ }
194
+ if (
195
+ (ts.isClassDeclaration(st) ||
196
+ ts.isInterfaceDeclaration(st) ||
197
+ ts.isTypeAliasDeclaration(st) ||
198
+ ts.isEnumDeclaration(st) ||
199
+ ts.isFunctionDeclaration(st) ||
200
+ ts.isModuleDeclaration(st)) &&
201
+ hasExportModifier(st)
202
+ ) {
203
+ if (st.name && ts.isIdentifier(st.name)) names.push(st.name.text);
204
+ }
205
+ // R2 round-4 finding: a namespace's own name was read and its BODY was
206
+ // not, so `export namespace Dispatch { export class CouncilTopologyRouter {} }`
207
+ // was invisible while the enclosing form was caught. The visitor listed
208
+ // `isModuleDeclaration` and only half-honoured it, which is worse than
209
+ // omitting it — a reader sees the kind in the list and assumes cover.
210
+ if (ts.isModuleDeclaration(st) && st.body && ts.isModuleBlock(st.body)) {
211
+ collect(st.body.statements, names);
212
+ continue;
213
+ }
214
+ if (ts.isModuleDeclaration(st)) continue;
215
+ if (ts.isExportAssignment(st)) {
216
+ if (ts.isIdentifier(st.expression)) names.push(st.expression.text);
217
+ if (ts.isClassExpression(st.expression) && st.expression.name) names.push(st.expression.name.text);
218
+ continue;
219
+ }
220
+ if (ts.isExportDeclaration(st)) {
221
+ const clause = st.exportClause;
222
+ if (clause === undefined) continue;
223
+ if (ts.isNamespaceExport(clause)) {
224
+ names.push(clause.name.text);
225
+ continue;
226
+ }
227
+ for (const el of clause.elements) names.push(el.name.text);
228
+ }
229
+ }
230
+ }
231
+
232
+ /**
233
+ * Does this file export a router-named binding that is actually CALLABLE?
234
+ *
235
+ * R2 round-3 finding: the previous resolver check tested for the identifier
236
+ * `classifyLadder` in an export position, so `export const classifyLadder =
237
+ * "moved"` scanned green while round 2's own reproducer `export const NOTE =
238
+ * "moved"` was caught — the two differ only in whether the stub kept the name.
239
+ * That is repairing to the reproducer rather than to the property, which is one
240
+ * of the two principles the council asked to be recorded.
241
+ *
242
+ * The property is "this file still holds a resolver", so the declaration KIND
243
+ * is what is checked: a function, a class, or a binding initialised to one. A
244
+ * string, a number or an object literal is a stub whatever it is called.
245
+ *
246
+ * Still syntactic, and deliberately so — a function that merely `return`s a
247
+ * constant satisfies this. Closing THAT needs semantic analysis and is outside
248
+ * the frozen claim.
249
+ */
250
+ export function exportsRouterFunction(text: string, fileName?: string): boolean {
251
+ const sf = parse(text, fileName);
252
+ const named = (n: string): boolean => ROUTER_NAMES.some((re) => re.test(n));
253
+
254
+ /** Peel `(x)`, `x as T`, `x satisfies T`, `x!` — none of them changes the value. */
255
+ const unwrap = (e: ts.Expression): ts.Expression => {
256
+ let cur = e;
257
+ for (;;) {
258
+ if (ts.isParenthesizedExpression(cur) || ts.isAsExpression(cur) || ts.isNonNullExpression(cur)) {
259
+ cur = cur.expression;
260
+ } else if (ts.isSatisfiesExpression(cur)) {
261
+ cur = cur.expression;
262
+ } else {
263
+ return cur;
264
+ }
265
+ }
266
+ };
267
+ const callableExpr = (e: ts.Expression | undefined): boolean => {
268
+ if (e === undefined) return false;
269
+ const inner = unwrap(e);
270
+ return ts.isArrowFunction(inner) || ts.isFunctionExpression(inner) || ts.isClassExpression(inner);
271
+ };
272
+
273
+ // Local declarations, so an export STATEMENT can be resolved to what it
274
+ // actually exports. R2 round-4 finding: the previous version recognised a
275
+ // router only when declared INLINE, so `function classifyLadder() {}` +
276
+ // `export { classifyLadder }` — and `export default classifyLadder`, and an
277
+ // `as`- or `satisfies`-wrapped arrow — were all reported as
278
+ // `resolver-is-not-a-resolver` on the sanctioned file, with a diagnostic
279
+ // asserting the resolver had "gone somewhere this guard does not look"
280
+ // while the function sat two lines above. `exportedNames` already accepted
281
+ // those exact forms, so one syntax counted as "declares a router" for every
282
+ // other file and as "is not a resolver" for this one.
283
+ const localCallable = new Map<string, boolean>();
284
+ for (const st of sf.statements) {
285
+ if (ts.isFunctionDeclaration(st) && st.name) localCallable.set(st.name.text, true);
286
+ else if (ts.isClassDeclaration(st) && st.name) localCallable.set(st.name.text, true);
287
+ else if (ts.isVariableStatement(st)) {
288
+ for (const d of st.declarationList.declarations) {
289
+ if (ts.isIdentifier(d.name)) localCallable.set(d.name.text, callableExpr(d.initializer));
290
+ }
291
+ }
292
+ }
293
+ const resolves = (local: string): boolean => localCallable.get(local) === true;
294
+
295
+ for (const st of sf.statements) {
296
+ // Inline: `export function X`, `export class X`, `export const X = () => …`
297
+ if (ts.isFunctionDeclaration(st) && hasExportModifier(st) && st.name && named(st.name.text)) return true;
298
+ if (ts.isClassDeclaration(st) && hasExportModifier(st) && st.name && named(st.name.text)) return true;
299
+ if (ts.isVariableStatement(st) && hasExportModifier(st)) {
300
+ for (const d of st.declarationList.declarations) {
301
+ if (ts.isIdentifier(d.name) && named(d.name.text) && callableExpr(d.initializer)) return true;
302
+ }
303
+ }
304
+ // `export default X` / `export default function X() {}`
305
+ if (ts.isExportAssignment(st) && !st.isExportEquals) {
306
+ const e = unwrap(st.expression);
307
+ if (ts.isIdentifier(e) && named(e.text) && resolves(e.text)) return true;
308
+ if (ts.isFunctionExpression(e) && e.name && named(e.name.text)) return true;
309
+ if (ts.isClassExpression(e) && e.name && named(e.name.text)) return true;
310
+ }
311
+ // `export { X }` / `export { local as X }` — LOCAL only. A re-export
312
+ // carrying a module specifier resolves to another file and is still
313
+ // refused, which is what R2 round 3 asked for.
314
+ if (ts.isExportDeclaration(st) && st.moduleSpecifier === undefined) {
315
+ const clause = st.exportClause;
316
+ if (clause !== undefined && ts.isNamedExports(clause)) {
317
+ for (const el of clause.elements) {
318
+ if (!named(el.name.text)) continue;
319
+ if (resolves((el.propertyName ?? el.name).text)) return true;
320
+ }
321
+ }
322
+ }
323
+ }
324
+ return false;
325
+ }
326
+
327
+ /** Does this file declare a task-side council/dispatch router? */
328
+ export function declaresRouter(text: string, fileName?: string): boolean {
329
+ return exportedNames(text, fileName).some((n) => ROUTER_NAMES.some((re) => re.test(n)));
330
+ }
331
+
332
+ /**
333
+ * Every module specifier this file names — static import (side-effect included),
334
+ * re-export, dynamic `import()`, and `require()`. String and
335
+ * no-substitution-template specifiers both count; a specifier built by
336
+ * interpolation is outside the frozen claim and is not guessed at.
337
+ */
338
+ export function moduleSpecifiers(text: string, fileName?: string): string[] {
339
+ const sf = parse(text, fileName);
340
+ const out: string[] = [];
341
+ const literal = (n: ts.Node | undefined): void => {
342
+ if (n === undefined) return;
343
+ if (ts.isStringLiteralLike(n)) out.push(n.text);
344
+ };
345
+ const visit = (n: ts.Node): void => {
346
+ if (ts.isImportDeclaration(n) || ts.isExportDeclaration(n)) literal(n.moduleSpecifier);
347
+ else if (ts.isImportEqualsDeclaration(n) && ts.isExternalModuleReference(n.moduleReference)) {
348
+ literal(n.moduleReference.expression);
349
+ } else if (ts.isCallExpression(n)) {
350
+ const isDynamic = n.expression.kind === ts.SyntaxKind.ImportKeyword;
351
+ const isRequire = ts.isIdentifier(n.expression) && n.expression.text === 'require';
352
+ if (isDynamic || isRequire) literal(n.arguments[0]);
353
+ }
354
+ ts.forEachChild(n, visit);
355
+ };
356
+ visit(sf);
357
+ return out;
358
+ }
359
+
360
+ /** Does this file import from the council's internal directory? */
361
+ export function importsCouncilInternal(text: string, fileName?: string): boolean {
362
+ return moduleSpecifiers(text, fileName).some((spec) => /(?:^|\/)ai_council(?:\/|$)/.test(spec));
363
+ }
364
+
365
+ /** Walk `.ts` files under a directory, skipping tests and build output. */
366
+ function walk(dir: string, out: string[] = []): string[] {
367
+ if (!fs.existsSync(dir)) return out;
368
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
369
+ const p = path.join(dir, entry.name);
370
+ if (entry.isDirectory()) {
371
+ if (SKIP_DIRS.has(entry.name)) continue;
372
+ walk(p, out);
373
+ } else if (SCANNED_EXTS.some((e) => entry.name.endsWith(e)) && !TEST_SUFFIXES.some((t) => entry.name.endsWith(t))) {
374
+ out.push(p);
375
+ }
376
+ }
377
+ return out;
378
+ }
379
+
380
+ /**
381
+ * Check the invariant over a tree.
382
+ *
383
+ * @param root repository root to scan
384
+ * @returns the violations AND the files actually read. A caller that only
385
+ * checks `violations.length === 0` cannot tell a clean tree from an
386
+ * unscanned one — which is finding 1, and why `scanned` is returned
387
+ * rather than logged.
388
+ */
389
+ export function checkOneResolver(root: string): ScanResult {
390
+ const violations: Violation[] = [];
391
+ const scanned: string[] = [];
392
+
393
+ // Finding 4: the POSITIVE half. Without this the invariant is satisfied by
394
+ // deleting the resolver, which is not "one resolver" — it is none.
395
+ const resolverAbs = path.join(root, SANCTIONED_RESOLVER);
396
+ if (!fs.existsSync(resolverAbs)) {
397
+ violations.push({
398
+ file: SANCTIONED_RESOLVER,
399
+ kind: 'resolver-missing',
400
+ detail:
401
+ 'the sanctioned task-side resolver does not exist. "Exactly one" is ' +
402
+ 'violated by zero as well as by two; a tree with no resolver must not scan green.',
403
+ });
404
+ } else if (!exportsRouterFunction(fs.readFileSync(resolverAbs, 'utf-8'), SANCTIONED_RESOLVER)) {
405
+ // R2 round-2 finding: existence is not identity. A `judgment_ladder.ts`
406
+ // gutted to `export const NOTE = "moved"` passed the existence check,
407
+ // so `rm` was caught and hollowing-out was not.
408
+ violations.push({
409
+ file: SANCTIONED_RESOLVER,
410
+ kind: 'resolver-is-not-a-resolver',
411
+ detail:
412
+ 'the sanctioned path exists but exports no CALLABLE binding whose name ' +
413
+ 'matches a router pattern. Presence is not identity and neither is the name: ' +
414
+ 'a stub such as `export const classifyLadder = \"moved\"` keeps both. If the ' +
415
+ 'resolver was RENAMED rather than gutted, update ROUTER_NAMES in the same ' +
416
+ 'change — this check reports the name it can see, not a conclusion about ' +
417
+ 'where the resolver went.',
418
+ });
419
+ }
420
+
421
+ for (const abs of walk(path.join(root, SCAN_ROOT))) {
422
+ const rel = path.relative(root, abs);
423
+ // The council's own internals are not task-side and are out of scope.
424
+ if (rel.startsWith(COUNCIL_INTERNAL_DIR + path.sep)) continue;
425
+ scanned.push(rel);
426
+ const text = fs.readFileSync(abs, 'utf-8');
427
+ if (rel !== SANCTIONED_RESOLVER && declaresRouter(text, rel)) {
428
+ violations.push({
429
+ file: rel,
430
+ kind: 'second-resolver',
431
+ detail:
432
+ `declares a task-side council/dispatch router beside ` +
433
+ `${SANCTIONED_RESOLVER}. There is exactly one task-side resolver; ` +
434
+ `a second one makes routing depend on which classifier ran first.`,
435
+ });
436
+ }
437
+ if (rel === SANCTIONED_RESOLVER && importsCouncilInternal(text, rel)) {
438
+ violations.push({
439
+ file: rel,
440
+ kind: 'council-import-in-resolver',
441
+ detail:
442
+ `imports from ${COUNCIL_INTERNAL_DIR}/. The resolver is deliberately ` +
443
+ `independent of the council's own necessity gate; an import merges the ` +
444
+ `task-side and council-internal surfaces the ladder's docstring keeps apart.`,
445
+ });
446
+ }
447
+ }
448
+ return { violations, scanned };
449
+ }