@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,143 @@
1
+ /**
2
+ * Deterministic adherence detection, and a hard refusal to guess.
3
+ *
4
+ * Step 5.1 splits a per-asset outcome into five states. The hardest of its
5
+ * rungs to observe is `followed`: whether the work actually conformed to the
6
+ * rule that was loaded. It is tempting to have a model read the transcript and
7
+ * say. That is precisely what this module refuses to do.
8
+ *
9
+ * The rule, from step 5.2: where a rule has an OBSERVABLE FOOTPRINT, adherence
10
+ * is computed from that footprint. Where it has none, adherence is `unknown` —
11
+ * never inferred, never a model's guess. An inferred adherence figure is worse
12
+ * than a missing one, because a missing one is visible in the `unknown` share
13
+ * and an inferred one is not.
14
+ *
15
+ * A DETECTOR ANSWERS FOR ONE CLAUSE, NOT FOR A RULE. Rules in this tree carry
16
+ * many clauses; `think-before-action` alone spans a read set, a verification
17
+ * matrix, and a test-first preference. A detector that observed one clause and
18
+ * reported "rule followed" would be overclaiming by construction, so every
19
+ * registration below names the clause it decides and the registry key is the
20
+ * pair, not the rule id.
21
+ */
22
+
23
+ import type { ActivationState } from './activation_states.js';
24
+
25
+ /** The subset of {@link ActivationState} a detector may return. */
26
+ export type Adherence = Extract<
27
+ ActivationState,
28
+ 'activated-followed' | 'activated-not-followed' | 'unknown'
29
+ >;
30
+
31
+ /**
32
+ * One observed write, reduced to the two things a detector may see: where it
33
+ * went, and in what order relative to the others.
34
+ *
35
+ * Deliberately carries NO content field — a detector that could read a diff
36
+ * body would be a second place for file contents to travel, and the audit
37
+ * privacy floor exists to stop exactly that.
38
+ */
39
+ export interface WriteObservation {
40
+ /** Repository-relative path. */
41
+ path: string;
42
+ /** Monotonic ordering key within one task. Absolute time is not required. */
43
+ seq: number;
44
+ }
45
+
46
+ export interface DetectorInput {
47
+ writes: readonly WriteObservation[];
48
+ }
49
+
50
+ export interface ClauseDetector {
51
+ /** The rule whose clause this decides. */
52
+ rule_id: string;
53
+ /** Which clause — a rule has many, and a detector answers for one. */
54
+ clause: string;
55
+ /** Where that clause is written, so a reader can check the detector against it. */
56
+ source: string;
57
+ detect(input: DetectorInput): Adherence;
58
+ }
59
+
60
+ /** A path is a test path when it sits under a `tests/` segment or is named `*.test.*` / `*.spec.*`. */
61
+ export function isTestPath(p: string): boolean {
62
+ const normalized = p.replace(/\\/g, '/');
63
+ if (/(^|\/)tests?(\/|$)/.test(normalized)) return true;
64
+ return /\.(test|spec)\.[cm]?[jt]sx?$/.test(normalized);
65
+ }
66
+
67
+ /**
68
+ * `think-before-action` § "When behavior can be defined → prefer test-first /
69
+ * TDD" (`dist/agent-src/rules/think-before-action.md:43`).
70
+ *
71
+ * The footprint is write ORDER: a test file written before the production file
72
+ * it covers is test-first; the reverse is not.
73
+ *
74
+ * The `unknown` branch is the load-bearing one and is wider than it first
75
+ * looks. If only test writes are observed, no production change happened and
76
+ * the clause did not apply. If only production writes are observed, it is
77
+ * tempting to answer `activated-not-followed` — and that would be a GUESS: the
78
+ * test may have been written in an earlier task, or the change may be one the
79
+ * clause's own "when behavior can be defined" qualifier excludes. Both
80
+ * one-sided cases are therefore `unknown`, which is the honest answer and the
81
+ * conservative one in the direction that matters — it never manufactures a
82
+ * violation.
83
+ */
84
+ export const testFirstDetector: ClauseDetector = {
85
+ rule_id: 'think-before-action',
86
+ clause: 'prefer test-first / TDD when behavior can be defined',
87
+ source: 'dist/agent-src/rules/think-before-action.md:43',
88
+ detect({ writes }) {
89
+ let firstTest: number | null = null;
90
+ let firstProd: number | null = null;
91
+ for (const w of writes) {
92
+ if (isTestPath(w.path)) {
93
+ if (firstTest === null || w.seq < firstTest) firstTest = w.seq;
94
+ } else if (firstProd === null || w.seq < firstProd) {
95
+ firstProd = w.seq;
96
+ }
97
+ }
98
+ // Either side missing means the order was never observed, not that it
99
+ // was violated. See the note above on why the production-only case is
100
+ // deliberately NOT a violation.
101
+ if (firstTest === null || firstProd === null) return 'unknown';
102
+ return firstTest < firstProd ? 'activated-followed' : 'activated-not-followed';
103
+ },
104
+ };
105
+
106
+ /**
107
+ * The registry. Keyed `${rule_id}#${clause}` so one rule may gain a second
108
+ * detector later without either silently replacing the other.
109
+ */
110
+ const DETECTORS: readonly ClauseDetector[] = [testFirstDetector];
111
+
112
+ export function detectorsFor(ruleId: string): readonly ClauseDetector[] {
113
+ return DETECTORS.filter((d) => d.rule_id === ruleId);
114
+ }
115
+
116
+ /**
117
+ * Adherence for one rule.
118
+ *
119
+ * A rule with NO registered detector returns `unknown`. That is the whole
120
+ * point: 118 of this tree's 119 rules have no observable footprint today, and
121
+ * the honest report says so rather than filling the column with inference.
122
+ *
123
+ * With several detectors registered for one rule, a single observed violation
124
+ * wins over a `followed` — a rule is not adhered to if any decided clause was
125
+ * broken — and `unknown` only survives when nothing was decided at all.
126
+ */
127
+ export function detectAdherence(ruleId: string, input: DetectorInput): Adherence {
128
+ const detectors = detectorsFor(ruleId);
129
+ if (detectors.length === 0) return 'unknown';
130
+
131
+ let sawFollowed = false;
132
+ for (const d of detectors) {
133
+ const verdict = d.detect(input);
134
+ if (verdict === 'activated-not-followed') return 'activated-not-followed';
135
+ if (verdict === 'activated-followed') sawFollowed = true;
136
+ }
137
+ return sawFollowed ? 'activated-followed' : 'unknown';
138
+ }
139
+
140
+ /** Rule ids that have at least one detector. Everything else reports `unknown`. */
141
+ export function rulesWithDetectors(): readonly string[] {
142
+ return [...new Set(DETECTORS.map((d) => d.rule_id))];
143
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Check an arithmetic claim written in prose against itself.
3
+ *
4
+ * The defect this exists for, observed rather than imagined: a fixture states
5
+ * the same number twice — once as a declared `expected` and once inside the
6
+ * rationale that derives it — and the two disagree. A judge then prefers the
7
+ * declared value over the recomputed one and scores a correct response as a
8
+ * loss. Both numbers are in the file, so nothing has to be inferred; they
9
+ * simply were never compared.
10
+ *
11
+ * The scanner reads CHAINS (`a x b / c = d / c = e`), because that is how the
12
+ * derivations in this tree are actually written. Comparing only the outermost
13
+ * pair would mis-read every intermediate step as a claim of its own — the
14
+ * first version of this check did exactly that and produced three false
15
+ * findings on a corpus that was correct.
16
+ *
17
+ * Prose rounding is legitimate and is tolerated to the PRECISION THE PROSE
18
+ * ITSELF USES: `1.333` agrees with 4/3, `1.33` does not have to. A fixed
19
+ * epsilon would either reject honest rounding or accept a real disagreement,
20
+ * depending on the magnitude of the numbers, which is the wrong axis.
21
+ */
22
+
23
+ /** One prose arithmetic chain and what it evaluates to. */
24
+ export interface ArithChain {
25
+ /** The chain as written. */
26
+ text: string;
27
+ /** Every `=`-separated segment that evaluated to a number. */
28
+ values: readonly { segment: string; value: number }[];
29
+ }
30
+
31
+ /** A chain whose segments disagree beyond the precision the prose claims. */
32
+ export interface ArithDisagreement {
33
+ text: string;
34
+ leftSegment: string;
35
+ left: number;
36
+ rightSegment: string;
37
+ right: number;
38
+ /** The tolerance the right-hand segment's own precision earns it. */
39
+ tolerance: number;
40
+ }
41
+
42
+ const CHAIN = /(?:[-+]?[\d,]*\.?\d+[\s\d,.+\-*/x×^()%]*=\s*)+[-+]?[\d,]*\.?\d+/g;
43
+
44
+ /**
45
+ * Evaluate one prose segment, or `null` when it is not pure arithmetic.
46
+ *
47
+ * Deliberately narrow: digits, the four operators, `^`, parentheses and the
48
+ * separators prose uses. Anything else returns `null` rather than being
49
+ * coerced — a segment carrying a word is a sentence, not a claim, and guessing
50
+ * at it is how a checker starts inventing findings.
51
+ */
52
+ export function evaluateSegment(segment: string): number | null {
53
+ const t = segment
54
+ .trim()
55
+ .replace(/,/g, '')
56
+ .replace(/[×x]/g, '*')
57
+ .replace(/%/g, '')
58
+ .replace(/\$/g, '');
59
+ if (!/\d/.test(t)) return null;
60
+ if (!/^[\d\s.+\-*/^()]+$/.test(t)) return null;
61
+ // `^` is exponentiation in prose and XOR in JS — translate before eval.
62
+ const js = t.replace(/\^/g, '**');
63
+ try {
64
+ // Input is whitelisted above to digits, operators and parentheses — a
65
+ // segment carrying anything else returned null before reaching here.
66
+ const fn = new Function(`"use strict"; return (${js});`) as () => unknown;
67
+ const v = fn();
68
+ return typeof v === 'number' && Number.isFinite(v) ? v : null;
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+
74
+ /** Decimal places written in a literal, used as the rounding tolerance. */
75
+ function precisionTolerance(segment: string, magnitude: number): number {
76
+ const m = /\.(\d+)\s*$/.exec(segment.trim());
77
+ const decimals = m?.[1];
78
+ if (decimals !== undefined) return 0.5 * 10 ** -decimals.length;
79
+ // An integer-looking claim is tolerated to half a unit, plus float noise
80
+ // proportional to the magnitude — `20,000` may legitimately stand for
81
+ // 20000.000000001 produced by a chain of divisions.
82
+ return Math.max(0.5, Math.abs(magnitude) * 1e-9);
83
+ }
84
+
85
+ /** Every arithmetic chain in a piece of prose. */
86
+ export function findArithChains(prose: string): ArithChain[] {
87
+ const out: ArithChain[] = [];
88
+ for (const m of prose.matchAll(CHAIN)) {
89
+ const values: { segment: string; value: number }[] = [];
90
+ for (const segment of m[0].split('=')) {
91
+ const value = evaluateSegment(segment);
92
+ if (value !== null) values.push({ segment, value });
93
+ }
94
+ if (values.length >= 2) out.push({ text: m[0], values });
95
+ }
96
+ return out;
97
+ }
98
+
99
+ /**
100
+ * Every chain whose own segments disagree.
101
+ *
102
+ * Each segment is compared against the FIRST evaluable one rather than against
103
+ * its neighbour: a chain is a claim that all of its forms are the same number,
104
+ * and comparing neighbours lets an error accumulate quietly across three steps.
105
+ */
106
+ export function findArithDisagreements(prose: string): ArithDisagreement[] {
107
+ const out: ArithDisagreement[] = [];
108
+ for (const chain of findArithChains(prose)) {
109
+ const first = chain.values[0];
110
+ if (first === undefined) continue;
111
+ for (const later of chain.values.slice(1)) {
112
+ const tolerance = precisionTolerance(later.segment, first.value);
113
+ if (Math.abs(later.value - first.value) > tolerance) {
114
+ out.push({
115
+ text: chain.text,
116
+ leftSegment: first.segment.trim(),
117
+ left: first.value,
118
+ rightSegment: later.segment.trim(),
119
+ right: later.value,
120
+ tolerance,
121
+ });
122
+ break;
123
+ }
124
+ }
125
+ }
126
+ return out;
127
+ }
128
+
129
+ /**
130
+ * Does a prose derivation ever state the declared value?
131
+ *
132
+ * Returns `null` when the prose carries no evaluable chain at all — an absent
133
+ * derivation is not a disagreement, and treating it as one would make the
134
+ * check fire on every fixture that simply cites a source instead.
135
+ */
136
+ export function chainSupportsExpected(
137
+ prose: string,
138
+ expected: number,
139
+ tolerance: number,
140
+ ): boolean | null {
141
+ const chains = findArithChains(prose);
142
+ if (chains.length === 0) return null;
143
+ for (const chain of chains) {
144
+ for (const v of chain.values) {
145
+ const tol = Math.max(tolerance, precisionTolerance(v.segment, expected));
146
+ if (Math.abs(v.value - expected) <= tol) return true;
147
+ if (sameMantissa(v.value, expected, tol)) return true;
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+
153
+ /**
154
+ * Do two numbers agree once a UNIT SCALE is allowed?
155
+ *
156
+ * A measured false positive is what this exists for, not a hypothetical: a
157
+ * fixture derives `4,200,000,000 x 0.006 = 25,200,000 = $25.2M` and declares
158
+ * `expected: 25.2`, because the scenario asks for the figure "in $M". The
159
+ * derivation works in dollars and the expectation in millions. Nothing in the
160
+ * file states the unit in a machine-readable way, so demanding exact agreement
161
+ * reports a correct fixture as broken.
162
+ *
163
+ * **The weakening this accepts, stated rather than buried.** A real defect that
164
+ * happens to be a pure power of ten away — `0.85` written where `8.5` is meant —
165
+ * is no longer caught by THIS check. It is a narrow hole: a transcription or
166
+ * arithmetic error almost always changes the digit sequence, and the
167
+ * self-consistency check above still reads the derivation on its own terms,
168
+ * where no scale tolerance applies at all. The alternative was worse: a check
169
+ * that fires on every unit-scaled fixture is a check that gets suppressed.
170
+ *
171
+ * Bounded to 10^±12 so an unrelated number cannot be dragged into agreement by
172
+ * an arbitrarily large exponent.
173
+ */
174
+ function sameMantissa(a: number, b: number, tolerance: number): boolean {
175
+ if (a === 0 || b === 0) return false;
176
+ const ratio = Math.abs(a / b);
177
+ const exp = Math.round(Math.log10(ratio));
178
+ if (!Number.isFinite(exp) || Math.abs(exp) > 12 || exp === 0) return false;
179
+ const scaled = a / 10 ** exp;
180
+ return Math.abs(scaled - b) <= Math.max(tolerance, Math.abs(b) * 1e-9);
181
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Arm ranking metrics (`road-to-runtime-context-floors` step 4.2).
3
+ *
4
+ * `tokens` stays the default and `cost-per-solved` becomes AVAILABLE — not a
5
+ * replacement. The reason it has to exist at all is the same one behind the
6
+ * mandatory cache fields: an arm can reduce token volume and cost more, because
7
+ * rewriting a prefix pays the cache-write rate rather than the cache-read rate.
8
+ * A comparison that can only rank on tokens cannot see that.
9
+ *
10
+ * The reason it must NOT become the default is the sibling risk: a single
11
+ * mandatory score invites tuning the score. Keeping both available, and keeping
12
+ * the cache ratio diagnostic rather than scored, is what stops this from being
13
+ * the Goodhart shape that `road-to-cache-economy`'s falsified C-5 already
14
+ * demonstrated for token count.
15
+ */
16
+
17
+ export const RANKING_METRICS = ['tokens', 'cost-per-solved'] as const;
18
+ export type RankingMetric = (typeof RANKING_METRICS)[number];
19
+ export const DEFAULT_RANKING_METRIC: RankingMetric = 'tokens';
20
+
21
+ export interface ArmResult {
22
+ arm: string;
23
+ /** Total tokens consumed by the arm across the run. */
24
+ tokens: number;
25
+ /** Total cost in USD. */
26
+ cost_usd: number;
27
+ /** Tasks the arm actually solved — the denominator of cost-per-solved. */
28
+ solved: number;
29
+ }
30
+
31
+ export interface RankedArm extends ArmResult {
32
+ /** The value ranked on. `null` when the metric is undefined for this arm. */
33
+ score: number | null;
34
+ /** Present only when `score` is null. */
35
+ reason?: string;
36
+ }
37
+
38
+ export function isRankingMetric(v: unknown): v is RankingMetric {
39
+ return typeof v === 'string' && (RANKING_METRICS as readonly string[]).includes(v);
40
+ }
41
+
42
+ function score(arm: ArmResult, metric: RankingMetric): { score: number | null; reason?: string } {
43
+ if (metric === 'tokens') return { score: arm.tokens };
44
+ if (arm.solved === 0) {
45
+ // Not Infinity and not 0: an arm that solved nothing has no
46
+ // cost-per-solved, and ranking it as "infinitely expensive" would be a
47
+ // number nobody measured.
48
+ return { score: null, reason: 'the arm solved nothing, so cost-per-solved is undefined' };
49
+ }
50
+ return { score: arm.cost_usd / arm.solved };
51
+ }
52
+
53
+ /**
54
+ * Rank ascending — lower is better under both metrics. Arms with an undefined
55
+ * score sort last, in input order, and carry their reason.
56
+ */
57
+ export function rankArms(arms: readonly ArmResult[], metric: RankingMetric = DEFAULT_RANKING_METRIC): RankedArm[] {
58
+ const scored: RankedArm[] = arms.map((a) => ({ ...a, ...score(a, metric) }));
59
+ const defined = scored.filter((a) => a.score !== null).sort((a, b) => (a.score as number) - (b.score as number) || a.arm.localeCompare(b.arm));
60
+ const undef = scored.filter((a) => a.score === null);
61
+ return [...defined, ...undef];
62
+ }
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Per-asset delivery ledger — which assets produce the standing payload
3
+ * (`road-to-delivered-cost-truth` step 2.1).
4
+ *
5
+ * `check_preamble_payload_budget` measures three BUCKET totals and refuses
6
+ * growth. It cannot say which asset inside a bucket is producing the cost, so a
7
+ * reduction effort has a number and no target. This module ranks the assets.
8
+ *
9
+ * WHAT "DELIVERY" MEANS PER BUCKET, because the two are not the same shape:
10
+ *
11
+ * - a RULE in the standing set delivers its whole body on every spawn;
12
+ * - a SKILL delivers only its catalogue line — `- <name>: <description>` — on
13
+ * every spawn. Its body is loaded on activation, which is not standing cost
14
+ * and is deliberately not counted here.
15
+ *
16
+ * Ranking the two together without that distinction would put every skill body
17
+ * above every rule and send a reduction effort at the wrong half of the tree.
18
+ *
19
+ * MEASUREMENT HONESTY. Each row states whether it was measured with the exact
20
+ * BPE tokenizer or the `chars/4` proxy, and a proxy reading within its own error
21
+ * margin of a threshold is reported `unresolved` rather than classified — the
22
+ * discipline `lint_token_budget_discipline` already applies to the rich band,
23
+ * reused here rather than reinvented.
24
+ */
25
+ import * as fs from 'node:fs';
26
+ import * as path from 'node:path';
27
+
28
+ import { parseFrontmatter } from '../preamble_byte_census.js';
29
+ import { gpt_tokens, gpt_tokens_proxy } from './token_count.js';
30
+
31
+ /** Worst-case per-file error of the character proxy, as measured for the rich band. */
32
+ export const PROXY_ERROR_MARGIN = 0.06;
33
+
34
+ export type AssetKind = 'rule' | 'skill-catalogue-line';
35
+
36
+ export interface AssetRow {
37
+ /** Repo-relative path of the asset. */
38
+ file: string;
39
+ /** Asset name as the preamble names it. */
40
+ name: string;
41
+ kind: AssetKind;
42
+ /** Tokens this asset contributes to EVERY spawn. */
43
+ tokens: number;
44
+ /**
45
+ * The same asset under the `chars/4` proxy.
46
+ *
47
+ * Carried alongside the BPE figure, not instead of it, because the payload
48
+ * gate counts chars/4 and reconciling a BPE total against a chars/4 total
49
+ * measures the METHODS, not the ledger. See {@link reconcile}.
50
+ */
51
+ proxy_tokens: number;
52
+ /** `exact` when the BPE tokenizer resolved, `proxy` otherwise. */
53
+ method: 'exact' | 'proxy';
54
+ /** Share of the ledger total. */
55
+ share: number;
56
+ }
57
+
58
+ export interface LedgerVerdict {
59
+ rows: AssetRow[];
60
+ total_tokens: number;
61
+ /** True only when EVERY row came from the exact tokenizer. */
62
+ exact_throughout: boolean;
63
+ by_kind: Record<AssetKind, { count: number; tokens: number; proxy_tokens: number }>;
64
+ }
65
+
66
+ /**
67
+ * Is a proxy reading too close to `threshold` to be classified?
68
+ *
69
+ * Symmetric, deliberately: the measured proxy error runs in BOTH directions
70
+ * (−5.3 % on the largest rich artifact), so a band that straddles the threshold
71
+ * yields no verdict rather than a confident wrong one.
72
+ */
73
+ export function unresolvedAgainst(tokens: number, method: 'exact' | 'proxy', threshold: number): boolean {
74
+ if (method === 'exact') return false;
75
+ const margin = Math.ceil(tokens * PROXY_ERROR_MARGIN);
76
+ return tokens - margin <= threshold && tokens + margin > threshold;
77
+ }
78
+
79
+ /** Walk the standing rule directory. Each rule delivers its whole body. */
80
+ export function measureRules(rulesDir: string, repoRoot: string): AssetRow[] {
81
+ if (!fs.existsSync(rulesDir)) return [];
82
+ const rows: AssetRow[] = [];
83
+ for (const entry of fs.readdirSync(rulesDir, { withFileTypes: true })) {
84
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue;
85
+ const abs = path.join(rulesDir, entry.name);
86
+ const text = fs.readFileSync(abs, 'utf-8');
87
+ const count = gpt_tokens(text);
88
+ rows.push({
89
+ file: path.relative(repoRoot, abs),
90
+ name: entry.name.replace(/\.md$/, ''),
91
+ kind: 'rule',
92
+ tokens: count.tokens,
93
+ proxy_tokens: gpt_tokens_proxy(text).tokens,
94
+ method: count.exact ? 'exact' : 'proxy',
95
+ share: 0,
96
+ });
97
+ }
98
+ return rows;
99
+ }
100
+
101
+ /**
102
+ * Walk the skill catalogue. Each skill delivers ONE line, not its body.
103
+ *
104
+ * The line is built the same way `censusSkillsCatalog` builds it — `- <name>:
105
+ * <description>\n` — because a ledger that measured the body would rank a skill
106
+ * that is never activated above a rule that is always delivered.
107
+ */
108
+ export function measureSkillCatalogue(skillsDir: string, repoRoot: string): AssetRow[] {
109
+ if (!fs.existsSync(skillsDir)) return [];
110
+ const rows: AssetRow[] = [];
111
+ for (const entry of fs.readdirSync(skillsDir, { withFileTypes: true })) {
112
+ if (!entry.isDirectory()) continue;
113
+ const skillMd = path.join(skillsDir, entry.name, 'SKILL.md');
114
+ let text: string;
115
+ try {
116
+ text = fs.readFileSync(skillMd, 'utf-8');
117
+ } catch {
118
+ continue;
119
+ }
120
+ // The SAME parser the payload census uses, not a second reader of the
121
+ // same bytes. A regex version reconciled 17.1 % low because a folded
122
+ // multi-line `description:` truncates at the first newline.
123
+ const fm = parseFrontmatter(text);
124
+ const name = typeof fm.name === 'string' && fm.name.length > 0 ? fm.name : entry.name;
125
+ const description = typeof fm.description === 'string' ? fm.description : '';
126
+ const line = `- ${name}: ${description}\n`;
127
+ const count = gpt_tokens(line);
128
+ rows.push({
129
+ file: path.relative(repoRoot, skillMd),
130
+ name,
131
+ kind: 'skill-catalogue-line',
132
+ tokens: count.tokens,
133
+ proxy_tokens: gpt_tokens_proxy(line).tokens,
134
+ method: count.exact ? 'exact' : 'proxy',
135
+ share: 0,
136
+ });
137
+ }
138
+ return rows;
139
+ }
140
+
141
+ export function buildLedger(rulesDir: string, skillsDir: string, repoRoot: string): LedgerVerdict {
142
+ const rows = [...measureRules(rulesDir, repoRoot), ...measureSkillCatalogue(skillsDir, repoRoot)];
143
+ const total = rows.reduce((n, r) => n + r.tokens, 0);
144
+ for (const r of rows) r.share = total > 0 ? r.tokens / total : 0;
145
+ rows.sort((a, b) => b.tokens - a.tokens || a.file.localeCompare(b.file));
146
+
147
+ const byKind: Record<AssetKind, { count: number; tokens: number; proxy_tokens: number }> = {
148
+ rule: { count: 0, tokens: 0, proxy_tokens: 0 },
149
+ 'skill-catalogue-line': { count: 0, tokens: 0, proxy_tokens: 0 },
150
+ };
151
+ for (const r of rows) {
152
+ byKind[r.kind].count += 1;
153
+ byKind[r.kind].tokens += r.tokens;
154
+ byKind[r.kind].proxy_tokens += r.proxy_tokens;
155
+ }
156
+ return { rows, total_tokens: total, exact_throughout: rows.every((r) => r.method === 'exact'), by_kind: byKind };
157
+ }
158
+
159
+ export interface Reconciliation {
160
+ kind: AssetKind;
161
+ bucket: string;
162
+ /** The ledger's chars/4 total — the SAME method the bucket uses. */
163
+ ledger_proxy_tokens: number;
164
+ /** The ledger's BPE total, reported for contrast, never reconciled against. */
165
+ ledger_exact_tokens: number;
166
+ bucket_tokens: number;
167
+ /** Absolute relative difference, like-for-like. */
168
+ drift: number;
169
+ within_margin: boolean;
170
+ }
171
+
172
+ /**
173
+ * Reconcile the ledger against the payload gate's own bucket totals.
174
+ *
175
+ * LIKE-FOR-LIKE, and this took a correction. The first cut compared the
176
+ * ledger's BPE total against the gate's `chars/4` bucket and set a 15 % margin
177
+ * to absorb the difference. That margin was GUESSED, which is the exact defect
178
+ * this roadmap's own budget file is careful about — and it did not even work:
179
+ * rules reconciled at 1.6 % while skill catalogue lines drifted 28.1 %, because
180
+ * `chars/4` overestimates short structured lines far more than it does prose.
181
+ * The 15 % would have hidden the rule drift and failed the skill drift, and
182
+ * neither verdict would have been about the ledger.
183
+ *
184
+ * So the ledger carries BOTH figures and reconciles the proxy against the
185
+ * proxy. What remains is real disagreement — a different corpus, a different
186
+ * line format, a missed file — which is what a reconciliation is for. The
187
+ * margin can then be tight, because two implementations of one arithmetic
188
+ * should agree closely or one of them is wrong.
189
+ */
190
+ export const RECONCILE_MARGIN = 0.02;
191
+
192
+ export function reconcile(
193
+ ledger: LedgerVerdict,
194
+ buckets: ReadonlyArray<{ name: string; tokens: number }>,
195
+ ): Reconciliation[] {
196
+ const map: Array<{ kind: AssetKind; bucket: string }> = [
197
+ { kind: 'rule', bucket: 'project-scope rules' },
198
+ { kind: 'skill-catalogue-line', bucket: 'preloaded skills catalog' },
199
+ ];
200
+ const out: Reconciliation[] = [];
201
+ for (const m of map) {
202
+ const bucket = buckets.find((b) => b.name === m.bucket);
203
+ if (bucket === undefined) continue;
204
+ const proxyTokens = ledger.by_kind[m.kind].proxy_tokens;
205
+ const drift =
206
+ bucket.tokens === 0 ? (proxyTokens === 0 ? 0 : 1) : Math.abs(proxyTokens - bucket.tokens) / bucket.tokens;
207
+ out.push({
208
+ kind: m.kind,
209
+ bucket: m.bucket,
210
+ ledger_proxy_tokens: proxyTokens,
211
+ ledger_exact_tokens: ledger.by_kind[m.kind].tokens,
212
+ bucket_tokens: bucket.tokens,
213
+ drift,
214
+ within_margin: drift <= RECONCILE_MARGIN,
215
+ });
216
+ }
217
+ return out;
218
+ }
219
+
220
+ // ─────────────────────────────────────────── growth attribution (step 2.2)
221
+
222
+ export interface GrowthEntry {
223
+ name: string;
224
+ kind: AssetKind;
225
+ /** Positive = this asset grew or arrived. */
226
+ delta: number;
227
+ status: 'added' | 'grew' | 'shrank' | 'removed';
228
+ }
229
+
230
+ export interface GrowthAttribution {
231
+ /** Assets that grew or arrived, largest first. */
232
+ increases: GrowthEntry[];
233
+ /** Assets that shrank or left, largest saving first. */
234
+ decreases: GrowthEntry[];
235
+ net_delta: number;
236
+ }
237
+
238
+ /**
239
+ * Attribute a payload change to the assets that caused it (step 2.2).
240
+ *
241
+ * A ratchet that says "the payload grew past the ceiling" states a fact and
242
+ * leaves the reader to find the cause; the same refusal naming the asset and its
243
+ * token delta reads as a quantified saving instead of an obstacle. That is the
244
+ * whole difference this function buys, and it is a presentation difference with
245
+ * a real consequence: a refusal nobody can act on gets suppressed rather than
246
+ * fixed.
247
+ *
248
+ * Pure and order-independent — both sides are keyed on `kind:name`, so a
249
+ * reordered ledger produces the same attribution.
250
+ */
251
+ export function attributeGrowth(before: readonly AssetRow[], after: readonly AssetRow[]): GrowthAttribution {
252
+ const key = (r: AssetRow): string => `${r.kind}:${r.name}`;
253
+ const b = new Map(before.map((r) => [key(r), r.tokens]));
254
+ const a = new Map(after.map((r) => [key(r), r.tokens]));
255
+
256
+ const entries: GrowthEntry[] = [];
257
+ for (const [k, tokens] of a) {
258
+ const prev = b.get(k);
259
+ const [kind, ...rest] = k.split(':');
260
+ const name = rest.join(':');
261
+ if (prev === undefined) {
262
+ entries.push({ name, kind: kind as AssetKind, delta: tokens, status: 'added' });
263
+ } else if (tokens !== prev) {
264
+ entries.push({ name, kind: kind as AssetKind, delta: tokens - prev, status: tokens > prev ? 'grew' : 'shrank' });
265
+ }
266
+ }
267
+ for (const [k, tokens] of b) {
268
+ if (a.has(k)) continue;
269
+ const [kind, ...rest] = k.split(':');
270
+ entries.push({ name: rest.join(':'), kind: kind as AssetKind, delta: -tokens, status: 'removed' });
271
+ }
272
+
273
+ const increases = entries.filter((e) => e.delta > 0).sort((x, y) => y.delta - x.delta || x.name.localeCompare(y.name));
274
+ const decreases = entries.filter((e) => e.delta < 0).sort((x, y) => x.delta - y.delta || x.name.localeCompare(y.name));
275
+ return { increases, decreases, net_delta: entries.reduce((n, e) => n + e.delta, 0) };
276
+ }
277
+
278
+ /**
279
+ * Render an attribution as the lines a refusing gate appends to its message.
280
+ *
281
+ * Capped, because a refusal listing 400 assets is as unactionable as one listing
282
+ * none — and the cap is stated rather than silent, so a reader knows the list is
283
+ * a head and not the whole story.
284
+ */
285
+ export function renderAttribution(g: GrowthAttribution, limit = 5): string[] {
286
+ if (g.increases.length === 0 && g.decreases.length === 0) return [];
287
+ const out: string[] = [];
288
+ const sign = (n: number): string => (n >= 0 ? `+${String(n)}` : String(n));
289
+ out.push(` net delivered-token change: ${sign(g.net_delta)} tok`);
290
+ if (g.increases.length > 0) {
291
+ out.push(` grew or arrived (top ${String(Math.min(limit, g.increases.length))} of ${String(g.increases.length)}):`);
292
+ for (const e of g.increases.slice(0, limit)) {
293
+ out.push(` ${sign(e.delta).padStart(7)} tok ${e.name} (${e.kind}, ${e.status})`);
294
+ }
295
+ }
296
+ if (g.decreases.length > 0) {
297
+ out.push(` shrank or left (top ${String(Math.min(limit, g.decreases.length))} of ${String(g.decreases.length)}):`);
298
+ for (const e of g.decreases.slice(0, limit)) {
299
+ out.push(` ${sign(e.delta).padStart(7)} tok ${e.name} (${e.kind}, ${e.status})`);
300
+ }
301
+ }
302
+ return out;
303
+ }