@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
@@ -48,6 +48,7 @@ import { censusRuleDir, type RuleDirCensus } from './_lib/carrier_divergence.js'
48
48
  import { DEFAULT_PROJECTS_ROOT, scanTranscripts } from './_lib/cc_transcript.js';
49
49
  import { censusDuplicateScope } from './_lib/duplicate_scope_census.js';
50
50
  import { computeColdStarts } from './cache_realization_report.js';
51
+ import type { EvidenceBasis } from './_lib/evidence_basis.js';
51
52
 
52
53
  const _HERE = fileURLToPath(import.meta.url);
53
54
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
@@ -81,6 +82,72 @@ function readCharsIfExists(p: string): number {
81
82
  // scope, and this file uses both below.
82
83
  export { censusRuleDir, type RuleDirCensus };
83
84
 
85
+ /**
86
+ * The user-scope rule payload, DERIVED from the projection instead of read off
87
+ * the developer's machine.
88
+ *
89
+ * `road-to-agent-turnaround` 4.1. `src/config/preamble-payload-budget.json`
90
+ * excludes the user-scope bucket as *"machine-dependent, not CI-checkable"*.
91
+ * That reason is falsifiable and this function falsifies it: the layer is
92
+ * written by this package's own installer out of `dist/agent-src/rules/`, and
93
+ * which rules go to the GLOBAL layer rather than the project one is decided by
94
+ * frontmatter — `workspaces:` naming the package's own maintainer workspace —
95
+ * not by anything on the machine.
96
+ *
97
+ * Verified exactly on 2026-08-30, which is why this is a partition and not an
98
+ * estimate: 119 projected rules, 15 carrying `workspaces: [agent-config-maintainer]`,
99
+ * and 119 − 15 = 104 = the file count of the installed `~/.claude/rules/`. The
100
+ * 15 split further into 13 written to the project `.claude/rules/` and 2 that
101
+ * are `type: manual` and written nowhere — which is why `packageOnly` is
102
+ * reported rather than folded away.
103
+ *
104
+ * The predicate is deliberately narrow: a rule is package-only when its
105
+ * `workspaces:` list exists and contains ONLY the maintainer workspace. A rule
106
+ * with no `workspaces:` key ships everywhere, which is the common case.
107
+ */
108
+ export interface DeliveredRuleCensus {
109
+ /** Rules the global layer receives — the machine-independent user-scope figure. */
110
+ delivered: RuleDirCensus;
111
+ /** Rules restricted to this package's own workspace. */
112
+ packageOnly: RuleDirCensus;
113
+ }
114
+
115
+ const MAINTAINER_WORKSPACE = 'agent-config-maintainer';
116
+
117
+ export function censusDeliveredRulePayload(projectionDir: string): DeliveredRuleCensus {
118
+ const empty = (): RuleDirCensus => ({ files: 0, chars: 0 });
119
+ const delivered = empty();
120
+ const packageOnly = empty();
121
+ let names: string[];
122
+ try {
123
+ names = fs.readdirSync(projectionDir).filter((f) => f.endsWith('.md'));
124
+ } catch {
125
+ return { delivered, packageOnly };
126
+ }
127
+ for (const name of names) {
128
+ const abs = path.join(projectionDir, name);
129
+ let text: string;
130
+ try {
131
+ text = fs.readFileSync(abs, 'utf8');
132
+ } catch {
133
+ continue;
134
+ }
135
+ const m = /^workspaces:\s*\[([^\]]*)\]/m.exec(text);
136
+ const listed =
137
+ m === null
138
+ ? []
139
+ : m[1]!
140
+ .split(',')
141
+ .map((x) => x.trim())
142
+ .filter((x) => x !== '');
143
+ const isPackageOnly = listed.length > 0 && listed.every((w) => w === MAINTAINER_WORKSPACE);
144
+ const bucket = isPackageOnly ? packageOnly : delivered;
145
+ bucket.files += 1;
146
+ bucket.chars += Buffer.byteLength(text, 'utf8');
147
+ }
148
+ return { delivered, packageOnly };
149
+ }
150
+
84
151
  export interface PerRuleCost {
85
152
  file: string;
86
153
  chars: number;
@@ -187,12 +254,19 @@ export interface SkillsCatalogCensus {
187
254
  chars: number;
188
255
  }
189
256
 
190
- interface RawSkillFrontmatter {
257
+ export interface RawSkillFrontmatter {
191
258
  name?: unknown;
192
259
  description?: unknown;
193
260
  }
194
261
 
195
- function parseFrontmatter(text: string): RawSkillFrontmatter {
262
+ /**
263
+ * Exported since `road-to-delivered-cost-truth` 2.1: the per-asset ledger must
264
+ * build the catalogue line the SAME way this census does, or the two disagree
265
+ * about the same corpus. A second regex-based reader was tried and reconciled
266
+ * 17.1 % low, because a `[^\n]+` capture truncates every folded multi-line
267
+ * `description:` — the reconciliation caught it, which is what it is for.
268
+ */
269
+ export function parseFrontmatter(text: string): RawSkillFrontmatter {
196
270
  if (!text.startsWith('---')) return {};
197
271
  const end = text.indexOf('\n---', 3);
198
272
  if (end === -1) return {};
@@ -242,14 +316,40 @@ export interface ByteCensusSource {
242
316
  files: number;
243
317
  chars: number;
244
318
  tokens_estimate: number;
245
- /** Estimate vs residual — see module doc comment. */
246
- provenance: 'measured_local_file' | 'residual';
319
+ /**
320
+ * Migrated onto the shared evidence-basis vocabulary
321
+ * (`road-to-delivered-cost-truth` 4.1). `measured_local_file` was this
322
+ * file's private spelling of `measured`, and `residual` was its spelling of
323
+ * a figure derived by subtraction — which the contract calls `estimated`.
324
+ * Two names for one idea is how a vocabulary forks; the old literals are
325
+ * gone rather than aliased.
326
+ */
327
+ provenance: Extract<EvidenceBasis, 'measured' | 'estimated'>;
247
328
  /** How this figure was derived — mandatory for anything not a plain file-size sum. */
248
329
  basis: string;
249
330
  }
250
331
 
251
332
  export interface ByteCensus {
252
333
  sources: ByteCensusSource[];
334
+ /**
335
+ * The projection-derived view of the user-scope bucket — road-to-agent-turnaround 4.1.
336
+ *
337
+ * DELIBERATELY NOT IN `sources`, and the reason is a defect this change made
338
+ * and caught: `sources` is SUMMED into `measurable_tokens_total`, and this is
339
+ * a second VIEW of the user-scope row already in it, not additional payload.
340
+ * Putting it there added ~123k phantom tokens to the total and reddened
341
+ * `check_preamble_payload_budget` on the first run. A non-additive figure
342
+ * belongs beside the sum, never inside it.
343
+ */
344
+ derived_user_scope: {
345
+ delivered_files: number;
346
+ delivered_chars: number;
347
+ delivered_tokens: number;
348
+ package_only_files: number;
349
+ package_only_chars: number;
350
+ package_only_tokens: number;
351
+ basis: string;
352
+ };
253
353
  measurable_tokens_total: number;
254
354
  /** Includes the residual bucket — always ≈ measured_cold_start_median by construction (see module doc comment). */
255
355
  grand_total_tokens: number;
@@ -338,6 +438,7 @@ export function buildByteCensus(opts: {
338
438
  skillsCatalog: SkillsCatalogCensus;
339
439
  topRules: PerRuleCost[];
340
440
  duplicateScope: ReturnType<typeof censusDuplicateScope>;
441
+ deliveredRules: DeliveredRuleCensus;
341
442
  measuredColdStartMedian: number | null;
342
443
  coldStartLegs: number;
343
444
  }): ByteCensus {
@@ -358,7 +459,7 @@ export function buildByteCensus(opts: {
358
459
  files: opts.userRules.files,
359
460
  chars: opts.userRules.chars,
360
461
  tokens_estimate: tokensFromChars(opts.userRules.chars),
361
- provenance: 'measured_local_file',
462
+ provenance: 'measured',
362
463
  basis: 'Σ byte size of every .md file in the user-scope rules directory, chars/4.',
363
464
  },
364
465
  {
@@ -366,7 +467,7 @@ export function buildByteCensus(opts: {
366
467
  files: opts.projectRules.files,
367
468
  chars: opts.projectRules.chars,
368
469
  tokens_estimate: tokensFromChars(opts.projectRules.chars),
369
- provenance: 'measured_local_file',
470
+ provenance: 'measured',
370
471
  basis: 'Σ byte size of every .md file in the project-scope rules directory, chars/4.',
371
472
  },
372
473
  {
@@ -374,7 +475,7 @@ export function buildByteCensus(opts: {
374
475
  files: claudeMdFiles,
375
476
  chars: claudeMdChars,
376
477
  tokens_estimate: tokensFromChars(claudeMdChars),
377
- provenance: 'measured_local_file',
478
+ provenance: 'measured',
378
479
  basis:
379
480
  'Byte size of project CLAUDE.md + project CLAUDE.local.md (if present) + user ~/.claude/CLAUDE.md ' +
380
481
  '+ every file pulled in by a top-level "@<path>" import line in the user CLAUDE.md, chars/4.',
@@ -384,7 +485,7 @@ export function buildByteCensus(opts: {
384
485
  files: opts.globalProfile.present ? 1 : 0,
385
486
  chars: opts.globalProfile.chars,
386
487
  tokens_estimate: tokensFromChars(opts.globalProfile.chars),
387
- provenance: 'measured_local_file',
488
+ provenance: 'measured',
388
489
  basis: opts.globalProfile.present
389
490
  ? `Byte size of ${opts.globalProfile.path as string}, chars/4.`
390
491
  : 'Layer not written yet on this machine (resolveGlobalProfilePath() returned null) — treated as 0, per the roadmap\'s own honesty caveat.',
@@ -394,7 +495,7 @@ export function buildByteCensus(opts: {
394
495
  files: opts.skillsCatalog.skills,
395
496
  chars: opts.skillsCatalog.chars,
396
497
  tokens_estimate: tokensFromChars(opts.skillsCatalog.chars),
397
- provenance: 'measured_local_file',
498
+ provenance: 'measured',
398
499
  basis:
399
500
  'Σ "- <name>: <description>\\n" over every SKILL.md frontmatter, chars/4 — the description-text ' +
400
501
  'component only; the host\'s exact on-wire catalog format is not independently file-measurable.',
@@ -411,7 +512,7 @@ export function buildByteCensus(opts: {
411
512
  files: 0,
412
513
  chars: 0,
413
514
  tokens_estimate: residualTokens,
414
- provenance: 'residual',
515
+ provenance: 'estimated',
415
516
  basis:
416
517
  'measured_cold_start_median minus the sum of the file-measurable buckets above. No local transcript ' +
417
518
  'or file carries the request\'s raw tool-definition JSON or the per-spawn dispatch-prompt text ' +
@@ -441,6 +542,21 @@ export function buildByteCensus(opts: {
441
542
 
442
543
  return {
443
544
  sources,
545
+ derived_user_scope: {
546
+ delivered_files: opts.deliveredRules.delivered.files,
547
+ delivered_chars: opts.deliveredRules.delivered.chars,
548
+ delivered_tokens: tokensFromChars(opts.deliveredRules.delivered.chars),
549
+ package_only_files: opts.deliveredRules.packageOnly.files,
550
+ package_only_chars: opts.deliveredRules.packageOnly.chars,
551
+ package_only_tokens: tokensFromChars(opts.deliveredRules.packageOnly.chars),
552
+ basis:
553
+ 'Σ byte size of every dist/agent-src/rules/*.md whose `workspaces:` does not restrict it to ' +
554
+ `the ${MAINTAINER_WORKSPACE} workspace, chars/4 — computed without reading ~/.claude at all. ` +
555
+ 'This is the bucket src/config/preamble-payload-budget.json excludes as "machine-dependent, ' +
556
+ 'not CI-checkable", and computing it here is what falsifies that reason. A gap against the ' +
557
+ 'measured user-scope row is INSTALL DRIFT (the machine carries an older release), never a ' +
558
+ 'defect in either number.',
559
+ },
444
560
  measurable_tokens_total: measurableTokensTotal,
445
561
  grand_total_tokens: grandTotal,
446
562
  measured_cold_start_median: median,
@@ -471,6 +587,7 @@ export function buildReport(opts: Options): ByteCensus {
471
587
  const skillsCatalog = censusSkillsCatalog(opts.skillsDir);
472
588
  const topRules = topRulesByCost(opts.projectRulesDir, 10);
473
589
  const duplicateScope = censusDuplicateScope(opts.userRulesDir, opts.projectRulesDir);
590
+ const deliveredRules = censusDeliveredRulePayload(opts.projectRulesDir);
474
591
 
475
592
  const scan = scanTranscripts({ root: opts.root, maxAgeDays: opts.maxAgeDays });
476
593
  const coldStart = computeColdStarts(scan.records);
@@ -484,6 +601,7 @@ export function buildReport(opts: Options): ByteCensus {
484
601
  skillsCatalog,
485
602
  topRules,
486
603
  duplicateScope,
604
+ deliveredRules,
487
605
  measuredColdStartMedian,
488
606
  coldStartLegs: coldStart.legs,
489
607
  });
@@ -504,6 +622,17 @@ export function renderText(r: ByteCensus): string {
504
622
  out.push(` basis: ${s.basis}`);
505
623
  }
506
624
  out.push('');
625
+ const d = r.derived_user_scope;
626
+ out.push('');
627
+ out.push('User-scope bucket, DERIVED from the projection (NOT summed — a second view of a row above):');
628
+ out.push(
629
+ ` delivered to the global layer: files=${String(d.delivered_files)} chars=${String(d.delivered_chars)} tokens_estimate=${String(d.delivered_tokens)}`,
630
+ );
631
+ out.push(
632
+ ` package-only (never delivered): files=${String(d.package_only_files)} chars=${String(d.package_only_chars)} tokens_estimate=${String(d.package_only_tokens)}`,
633
+ );
634
+ out.push(` basis: ${d.basis}`);
635
+ out.push('');
507
636
  out.push(`measurable_tokens_total (excludes residual): ${Math.round(r.measurable_tokens_total)}`);
508
637
  out.push(`grand_total_tokens (includes residual): ${Math.round(r.grand_total_tokens)}`);
509
638
  if (r.measured_cold_start_median !== null) {
@@ -76,6 +76,8 @@ process.stderr.write(`prepack-check: ${BIN} OK\n`);
76
76
  // the shipped set is derived from the package.json `files` whitelist.
77
77
  // ---------------------------------------------------------------------------
78
78
  import { readdirSync } from 'node:fs';
79
+
80
+ import { stripCommentsMjs } from './_lib/strip_comments.mjs';
79
81
  import { dirname, join, relative, sep } from 'node:path';
80
82
 
81
83
  const pkg = JSON.parse(readFileSync(resolve('package.json'), 'utf8'));
@@ -109,6 +111,7 @@ function* walk(dir) {
109
111
 
110
112
  const IMPORT_RE = /(?:from\s+|import\s*\(\s*|require\s*\(\s*)['"](\.\.?\/[^'"]+)['"]/g;
111
113
 
114
+
112
115
  function resolveCandidates(fromDir, spec) {
113
116
  const base = join(fromDir, spec);
114
117
  return [
@@ -126,7 +129,7 @@ const importErrors = [];
126
129
  const scanRoots = shippedPrefixes.filter((p) => p.startsWith('src/'));
127
130
  for (const root of scanRoots) {
128
131
  for (const file of walk(resolve(root))) {
129
- const text = readFileSync(file, 'utf8');
132
+ const text = stripCommentsMjs(readFileSync(file, 'utf8'));
130
133
  for (const m of text.matchAll(IMPORT_RE)) {
131
134
  const spec = m[1];
132
135
  const candidates = resolveCandidates(dirname(file), spec);
@@ -0,0 +1,384 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Turnaround instrument — round-trips, batch size, blocking waits, context floor.
4
+ *
5
+ * WHY IT EXISTS. `agents/evidence/analysis/agent-turnaround-2026-08-30.md`
6
+ * measured that a user request in this package costs 42.6 API round-trips and a
7
+ * file change costs 58 tool calls, and its fifth finding is that NONE of it was
8
+ * measurable before: no instrument in the tree reported round-trips per request,
9
+ * tool-call batch size, or the blocking-wait tail, so no gate could have caught
10
+ * any of them growing. That analysis was a throwaway script. This is the
11
+ * committed form of it — `road-to-agent-turnaround` Phase 1.1.
12
+ *
13
+ * THE UNIT IS `requestId`, NOT THE ROW. The host writes `thinking`, `text` and
14
+ * `tool_use` as SEPARATE JSONL rows that share one `requestId`, so counting rows
15
+ * inflates every per-call figure. That was the first wrong answer the original
16
+ * measurement produced, and it is why this file groups before it counts.
17
+ *
18
+ * WHAT IT REPORTS, per corpus:
19
+ * - API calls per real user request (F1)
20
+ * - mean tool-call batch size (F1 — measured 1.00, i.e. fully serial)
21
+ * - the >60 s blocking tail and its share of tool time (F2)
22
+ * - the first-call context floor (F3 cross-check)
23
+ *
24
+ * A MEASUREMENT, NOT A GATE — by default. `--against-baseline` compares against
25
+ * `src/config/turnaround-budget.json` and is the only mode that can exit
26
+ * non-zero. Whether that mode may run in CI is answered in the config's own
27
+ * `_comment`, because a transcript store is machine-local and a fresh clone has
28
+ * none: a CI gate over it would be green-because-empty, the exact
29
+ * gate-that-scans-nothing-exits-green failure this repository names elsewhere.
30
+ *
31
+ * Usage:
32
+ * ./scripts-run src/scripts/probe_turnaround [--store PATH] [--limit N]
33
+ * ./scripts-run src/scripts/probe_turnaround --json
34
+ * ./scripts-run src/scripts/probe_turnaround --against-baseline
35
+ *
36
+ * The MEASURING session is excluded by default — see `recentSessions`.
37
+ *
38
+ * Exit codes: 0 = reported (or within baseline) · 1 = baseline regression, or an
39
+ * empty corpus under `--against-baseline` · 2 = usage error.
40
+ */
41
+ import * as fs from 'node:fs';
42
+ import * as path from 'node:path';
43
+ import { fileURLToPath } from 'node:url';
44
+
45
+ import { defaultStore, isInjectedBody, userText } from './conformance_scan.js';
46
+
47
+ const _FILE = fileURLToPath(import.meta.url);
48
+ const REPO_ROOT = path.resolve(path.dirname(_FILE), '..', '..');
49
+ export const BUDGET_REL = 'src/config/turnaround-budget.json';
50
+
51
+ /** The blocking-tail threshold, in seconds. Fixed by the source measurement. */
52
+ export const BLOCKING_SECONDS = 60;
53
+
54
+ type Entry = Record<string, unknown>;
55
+
56
+ function isObj(v: unknown): v is Record<string, unknown> {
57
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
58
+ }
59
+
60
+ export interface Turnaround {
61
+ sessions: number;
62
+ userRequests: number;
63
+ apiCalls: number;
64
+ callsPerRequest: number;
65
+ toolCalls: number;
66
+ /** Requests that carried at least one tool_use block. */
67
+ toolUsingRequests: number;
68
+ meanBatchSize: number;
69
+ toolSeconds: number;
70
+ blockingCalls: number;
71
+ blockingSeconds: number;
72
+ blockingShare: number;
73
+ contextFloorMin: number;
74
+ contextFloorMax: number;
75
+ }
76
+
77
+ const ZERO: Turnaround = {
78
+ sessions: 0,
79
+ userRequests: 0,
80
+ apiCalls: 0,
81
+ callsPerRequest: 0,
82
+ toolCalls: 0,
83
+ toolUsingRequests: 0,
84
+ meanBatchSize: 0,
85
+ toolSeconds: 0,
86
+ blockingCalls: 0,
87
+ blockingSeconds: 0,
88
+ blockingShare: 0,
89
+ contextFloorMin: 0,
90
+ contextFloorMax: 0,
91
+ };
92
+
93
+ function ms(iso: unknown): number | null {
94
+ if (typeof iso !== 'string') return null;
95
+ const t = Date.parse(iso);
96
+ return Number.isNaN(t) ? null : t;
97
+ }
98
+
99
+ /**
100
+ * The N most recently modified transcript files under `store`, excluding the
101
+ * MEASURING session.
102
+ *
103
+ * The exclusion is not hygiene, it is correctness, and it was found by the
104
+ * instrument failing its own baseline. Two consecutive runs minutes apart
105
+ * reported `calls_per_request` 81.42 then 81.61: the measuring session was
106
+ * adding API calls to the newest transcript while its user-request denominator
107
+ * stayed at one, so the probe was reading its own execution as a regression.
108
+ * The source analysis excluded the measuring session for the same reason and
109
+ * said so; this reproduces that rather than rediscovering it a third time.
110
+ *
111
+ * Identified by `CLAUDE_CODE_SESSION_ID` where the host exports it — the file
112
+ * is `<sessionId>.jsonl` — and otherwise by dropping the single
113
+ * most-recently-modified file, which is the same session by construction on any
114
+ * machine actually running this. `--include-current` opts back in for an
115
+ * offline corpus where nothing is being written.
116
+ */
117
+ export function recentSessions(store: string, limit: number, includeCurrent = false): string[] {
118
+ if (!fs.existsSync(store)) return [];
119
+ const all = fs
120
+ .readdirSync(store)
121
+ .filter((f) => f.endsWith('.jsonl'))
122
+ .map((f) => ({ f, m: fs.statSync(path.join(store, f)).mtimeMs }))
123
+ .sort((a, b) => b.m - a.m);
124
+ if (!includeCurrent && all.length > 0) {
125
+ const sid = process.env['CLAUDE_CODE_SESSION_ID'];
126
+ const idx =
127
+ sid !== undefined && sid !== ''
128
+ ? all.findIndex((x) => x.f === `${sid}.jsonl`)
129
+ : 0;
130
+ if (idx >= 0) all.splice(idx, 1);
131
+ }
132
+ return all.slice(0, limit).map(({ f }) => path.join(store, f));
133
+ }
134
+
135
+ export function measure(files: readonly string[]): Turnaround {
136
+ const requestIds = new Set<string>();
137
+ /** requestId → tool_use blocks in it. */
138
+ const batch = new Map<string, number>();
139
+ /** tool_use id → the timestamp the assistant emitted it. */
140
+ const emittedAt = new Map<string, number>();
141
+ const durations: number[] = [];
142
+ const contextFloors: number[] = [];
143
+ let userRequests = 0;
144
+ let sessions = 0;
145
+
146
+ for (const file of files) {
147
+ let sawFloor = false;
148
+ let sawAny = false;
149
+ for (const line of fs.readFileSync(file, 'utf8').split('\n')) {
150
+ if (!line.trim()) continue;
151
+ let e: Entry;
152
+ try {
153
+ e = JSON.parse(line) as Entry;
154
+ } catch {
155
+ continue;
156
+ }
157
+ if (e['isSidechain'] === true) continue;
158
+ sawAny = true;
159
+ const msg = e['message'];
160
+
161
+ if (e['type'] === 'user') {
162
+ // The SAME predicate the conformance scan uses, imported rather
163
+ // than re-typed: a skill body and a slash-command header arrive
164
+ // in the user role and are not requests. Counting them would
165
+ // deflate calls-per-request by inventing a denominator.
166
+ const text = userText(e);
167
+ if (text !== null && !isInjectedBody(text)) userRequests += 1;
168
+ if (isObj(msg) && Array.isArray(msg['content'])) {
169
+ const at = ms(e['timestamp']);
170
+ for (const b of msg['content']) {
171
+ if (!isObj(b) || b['type'] !== 'tool_result') continue;
172
+ const id = String(b['tool_use_id'] ?? '');
173
+ const started = emittedAt.get(id);
174
+ if (started === undefined || at === null) continue;
175
+ durations.push((at - started) / 1000);
176
+ emittedAt.delete(id);
177
+ }
178
+ }
179
+ continue;
180
+ }
181
+
182
+ if (e['type'] !== 'assistant') continue;
183
+ const rid = e['requestId'];
184
+ if (typeof rid === 'string' && rid !== '') requestIds.add(rid);
185
+
186
+ if (!sawFloor && isObj(msg) && isObj(msg['usage'])) {
187
+ const u = msg['usage'] as Record<string, unknown>;
188
+ const floor =
189
+ Number(u['input_tokens'] ?? 0) +
190
+ Number(u['cache_read_input_tokens'] ?? 0) +
191
+ Number(u['cache_creation_input_tokens'] ?? 0);
192
+ if (floor > 0) {
193
+ contextFloors.push(floor);
194
+ sawFloor = true;
195
+ }
196
+ }
197
+
198
+ if (!isObj(msg) || !Array.isArray(msg['content'])) continue;
199
+ const at = ms(e['timestamp']);
200
+ let n = 0;
201
+ for (const b of msg['content']) {
202
+ if (!isObj(b) || b['type'] !== 'tool_use') continue;
203
+ n += 1;
204
+ const id = String(b['id'] ?? '');
205
+ if (id !== '' && at !== null) emittedAt.set(id, at);
206
+ }
207
+ if (n > 0 && typeof rid === 'string' && rid !== '') {
208
+ batch.set(rid, (batch.get(rid) ?? 0) + n);
209
+ }
210
+ }
211
+ if (sawAny) sessions += 1;
212
+ }
213
+
214
+ const toolCalls = [...batch.values()].reduce((a, b) => a + b, 0);
215
+ const toolSeconds = durations.reduce((a, b) => a + b, 0);
216
+ const blocking = durations.filter((d) => d > BLOCKING_SECONDS);
217
+ const blockingSeconds = blocking.reduce((a, b) => a + b, 0);
218
+ const round = (x: number, p = 2): number => Math.round(x * 10 ** p) / 10 ** p;
219
+
220
+ return {
221
+ sessions,
222
+ userRequests,
223
+ apiCalls: requestIds.size,
224
+ callsPerRequest: userRequests === 0 ? 0 : round(requestIds.size / userRequests),
225
+ toolCalls,
226
+ toolUsingRequests: batch.size,
227
+ meanBatchSize: batch.size === 0 ? 0 : round(toolCalls / batch.size),
228
+ toolSeconds: round(toolSeconds, 1),
229
+ blockingCalls: blocking.length,
230
+ blockingSeconds: round(blockingSeconds, 1),
231
+ blockingShare: toolSeconds === 0 ? 0 : round(blockingSeconds / toolSeconds, 4),
232
+ contextFloorMin: contextFloors.length === 0 ? 0 : Math.min(...contextFloors),
233
+ contextFloorMax: contextFloors.length === 0 ? 0 : Math.max(...contextFloors),
234
+ };
235
+ }
236
+
237
+ export interface Budget {
238
+ baseline: {
239
+ calls_per_request: number;
240
+ mean_batch_size: number;
241
+ blocking_share: number;
242
+ context_floor_max: number;
243
+ };
244
+ empty_corpus: 'fail' | 'report';
245
+ }
246
+
247
+ export function readBudget(root = REPO_ROOT): Budget | null {
248
+ const p = path.join(root, BUDGET_REL);
249
+ if (!fs.existsSync(p)) return null;
250
+ return JSON.parse(fs.readFileSync(p, 'utf8')) as Budget;
251
+ }
252
+
253
+ /**
254
+ * Baseline comparison.
255
+ *
256
+ * `calls_per_request` is deliberately ABSENT — see the comment in the body.
257
+ * Direction is DOWN for `blocking_share`, and UP for
258
+ * `mean_batch_size` — a batch of 1.00 is the floor being fixed, so a higher
259
+ * number is the improvement and the ratchet must not read it as a regression.
260
+ * `context_floor_max` is DOWN. Getting one of these directions backwards would
261
+ * make the gate reward the defect, which is why they are listed rather than
262
+ * inferred from a shared helper.
263
+ */
264
+ export function compare(t: Turnaround, b: Budget): string[] {
265
+ const out: string[] = [];
266
+ // `calls_per_request` is REPORTED, never compared. Twice measured unstable
267
+ // for reasons that are not regressions: it fell 81.42 -> 72.67 when the
268
+ // measuring session was excluded, then drifted 72.67 -> 73.73 within the
269
+ // same afternoon because the corpus is an MTIME WINDOW and other sessions
270
+ // in the same project store keep sliding into it. A ratchet on a number
271
+ // that moves when nothing changed teaches the reader to ignore the gate,
272
+ // which is the failure this repository records for gates that can only
273
+ // block. The three metrics below are stable across the same runs.
274
+ if (t.meanBatchSize < b.baseline.mean_batch_size) {
275
+ out.push(
276
+ `mean batch size ${String(t.meanBatchSize)} < baseline ${String(b.baseline.mean_batch_size)} (direction: UP — 1.00 is fully serial)`,
277
+ );
278
+ }
279
+ if (t.blockingShare > b.baseline.blocking_share) {
280
+ out.push(
281
+ `blocking share ${String(t.blockingShare)} > baseline ${String(b.baseline.blocking_share)} (direction: DOWN)`,
282
+ );
283
+ }
284
+ if (t.contextFloorMax > b.baseline.context_floor_max) {
285
+ out.push(
286
+ `context floor max ${String(t.contextFloorMax)} > baseline ${String(b.baseline.context_floor_max)} (direction: DOWN)`,
287
+ );
288
+ }
289
+ return out;
290
+ }
291
+
292
+ function render(t: Turnaround, store: string): string {
293
+ const pct = (x: number): string => `${String(Math.round(x * 1000) / 10)} %`;
294
+ return [
295
+ `probe:turnaround · ${String(t.sessions)} session(s) · ${store}`,
296
+ ` API calls per user request ${String(t.callsPerRequest)} (${String(t.apiCalls)} calls / ${String(t.userRequests)} requests)`,
297
+ ` mean tool-call batch size ${String(t.meanBatchSize)} (${String(t.toolCalls)} tool calls / ${String(t.toolUsingRequests)} tool-using requests)`,
298
+ ` blocking tail (>${String(BLOCKING_SECONDS)}s) ${String(t.blockingCalls)} call(s), ${String(Math.round(t.blockingSeconds / 60))} min = ${pct(t.blockingShare)} of ${String(Math.round(t.toolSeconds / 60))} min tool time`,
299
+ ` first-call context floor ${String(t.contextFloorMin)}–${String(t.contextFloorMax)} tokens`,
300
+ '',
301
+ ].join('\n');
302
+ }
303
+
304
+ export function main(argv: string[] = process.argv.slice(2)): number {
305
+ const arg = (flag: string, dflt: string): string => {
306
+ const i = argv.indexOf(flag);
307
+ return i >= 0 && argv[i + 1] !== undefined ? (argv[i + 1] as string) : dflt;
308
+ };
309
+ for (const a of argv) {
310
+ if (a.startsWith('-') && !['--store', '--limit', '--json', '--against-baseline', '--include-current', '-h', '--help'].includes(a)) {
311
+ if (!argv.includes('--store') && !argv.includes('--limit')) {
312
+ process.stderr.write(`probe_turnaround: unrecognized argument: ${a}\n`);
313
+ return 2;
314
+ }
315
+ }
316
+ }
317
+ if (argv.includes('-h') || argv.includes('--help')) {
318
+ process.stdout.write(
319
+ 'usage: probe_turnaround [--store PATH] [--limit N] [--json] [--against-baseline] [--include-current]\n',
320
+ );
321
+ return 0;
322
+ }
323
+ const store = arg('--store', defaultStore(process.cwd()));
324
+ const limit = Number(arg('--limit', '10'));
325
+ if (!Number.isFinite(limit) || limit < 1) {
326
+ process.stderr.write('probe_turnaround: --limit needs a positive integer\n');
327
+ return 2;
328
+ }
329
+ const files = recentSessions(store, limit, argv.includes('--include-current'));
330
+ const t = files.length === 0 ? ZERO : measure(files);
331
+ const gating = argv.includes('--against-baseline');
332
+
333
+ if (argv.includes('--json')) {
334
+ process.stdout.write(`${JSON.stringify({ store, ...t }, null, 2)}\n`);
335
+ } else {
336
+ process.stdout.write(render(t, store));
337
+ }
338
+
339
+ if (!gating) {
340
+ if (files.length === 0) {
341
+ // A REPORT over an empty store is honest and exits 0; only the
342
+ // gating mode treats emptiness as a failure. The distinction is the
343
+ // whole of step 1.3.
344
+ process.stdout.write(
345
+ `probe:turnaround · no transcript store at ${store} — nothing measured (report mode; ` +
346
+ 'use --against-baseline for the fail-closed form)\n',
347
+ );
348
+ }
349
+ return 0;
350
+ }
351
+
352
+ const budget = readBudget();
353
+ if (budget === null) {
354
+ process.stderr.write(`❌ probe_turnaround: ${BUDGET_REL} is missing — nothing to compare against.\n`);
355
+ return 1;
356
+ }
357
+ if (files.length === 0) {
358
+ if (budget.empty_corpus === 'fail') {
359
+ process.stderr.write(
360
+ `❌ probe_turnaround: empty corpus at ${store}. A baseline comparison over zero ` +
361
+ 'sessions certifies nothing, and the config records `empty_corpus: "fail"` so it ' +
362
+ 'refuses rather than reporting green.\n',
363
+ );
364
+ return 1;
365
+ }
366
+ process.stdout.write('probe:turnaround · empty corpus, `empty_corpus: "report"` — no verdict.\n');
367
+ return 0;
368
+ }
369
+ const regressions = compare(t, budget);
370
+ if (regressions.length > 0) {
371
+ process.stderr.write('❌ probe_turnaround: regression against the recorded baseline:\n');
372
+ for (const r of regressions) process.stderr.write(` ${r}\n`);
373
+ process.stderr.write(
374
+ ` → raising a baseline in ${BUDGET_REL} needs the reason as a sentence in the same commit.\n`,
375
+ );
376
+ return 1;
377
+ }
378
+ process.stdout.write('✅ probe_turnaround: within the recorded baseline on all four metrics.\n');
379
+ return 0;
380
+ }
381
+
382
+ if (process.argv[1] !== undefined && process.argv[1].includes('probe_turnaround')) {
383
+ process.exit(main());
384
+ }