@event4u/agent-config 14.9.0 → 14.11.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 (426) 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 +384 -0
  5. package/README.md +1 -1
  6. package/dist/agent-src/commands/design.md +88 -0
  7. package/dist/agent-src/commands/project/analyze.md +47 -0
  8. package/dist/agent-src/commands/review/changes.md +67 -6
  9. package/dist/agent-src/commands/roadmap/create.md +49 -0
  10. package/dist/agent-src/commands/roadmap/next.md +27 -11
  11. package/dist/agent-src/commands/roadmap/process-full.md +14 -0
  12. package/dist/agent-src/commands/video/from-script.md +60 -3
  13. package/dist/agent-src/commands/video/stitch.md +14 -0
  14. package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
  16. package/dist/agent-src/rules/active-remediation.md +3 -3
  17. package/dist/agent-src/rules/code-provenance.md +2 -2
  18. package/dist/agent-src/rules/context-hygiene.md +13 -33
  19. package/dist/agent-src/rules/council-availability.md +2 -2
  20. package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
  21. package/dist/agent-src/rules/design-fidelity.md +18 -0
  22. package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
  23. package/dist/agent-src/rules/evaluator-independence.md +56 -44
  24. package/dist/agent-src/rules/fix-what-you-see.md +9 -1
  25. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
  26. package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
  27. package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
  28. package/dist/agent-src/rules/playbook-precedence.md +105 -0
  29. package/dist/agent-src/rules/recurring-criticism.md +3 -1
  30. package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
  31. package/dist/agent-src/rules/scale-discipline.md +24 -0
  32. package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
  33. package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
  34. package/dist/agent-src/rules/ui-audit-gate.md +27 -15
  35. package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
  36. package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
  37. package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
  38. package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
  39. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
  40. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
  41. package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
  42. package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
  43. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +17 -0
  44. package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
  45. package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
  46. package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
  47. package/dist/agent-src/skills/brand/SKILL.md +3 -0
  48. package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
  49. package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
  50. package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
  51. package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
  52. package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
  53. package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
  54. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  55. package/dist/agent-src/skills/code-review/SKILL.md +11 -0
  56. package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
  57. package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
  58. package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
  59. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
  60. package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
  61. package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
  62. package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
  63. package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
  64. package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
  65. package/dist/agent-src/skills/design-review/SKILL.md +80 -2
  66. package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
  67. package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
  68. package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
  69. package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
  70. package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
  71. package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
  72. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  73. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  74. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  75. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  76. package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
  77. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
  78. package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
  79. package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
  80. package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
  81. package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
  82. package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
  83. package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
  84. package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
  85. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  86. package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
  87. package/dist/agent-src/skills/iconography/SKILL.md +3 -0
  88. package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
  89. package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
  90. package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
  91. package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
  92. package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
  93. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +17 -0
  94. package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
  95. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  96. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  97. package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
  98. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  99. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
  100. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  101. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  102. package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
  103. package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
  104. package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
  105. package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
  106. package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
  107. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  108. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  109. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  110. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  111. package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
  112. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
  113. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
  114. package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
  115. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  116. package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
  117. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
  118. package/dist/agent-src/skills/override-management/SKILL.md +3 -0
  119. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  120. package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
  121. package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
  122. package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
  123. package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
  124. package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
  125. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
  126. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  127. package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
  128. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
  129. package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
  130. package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
  131. package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
  132. package/dist/agent-src/skills/receiving-code-review/SKILL.md +3 -3
  133. package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
  134. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
  135. package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
  136. package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
  137. package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
  138. package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
  139. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
  140. package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
  141. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  142. package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
  143. package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
  144. package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
  145. package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -0
  146. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
  147. package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
  148. package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
  149. package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
  150. package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
  151. package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
  152. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -0
  153. package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
  154. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
  155. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
  156. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
  157. package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
  158. package/dist/agent-src/skills/terraform/SKILL.md +3 -0
  159. package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
  160. package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
  161. package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
  162. package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
  163. package/dist/agent-src/skills/ui-component-architect/SKILL.md +9 -1
  164. package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
  165. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  166. package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
  167. package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
  168. package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
  169. package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
  170. package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
  171. package/dist/agent-src/skills/workspace-link/evals/triggers.json +17 -0
  172. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  173. package/dist/agent-src/templates/playbook.md +75 -0
  174. package/dist/agent-src/templates/roadmaps.md +37 -0
  175. package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  176. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  177. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  178. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  179. package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  180. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  181. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  182. package/dist/cli/commands/uiAudit.js +221 -0
  183. package/dist/cli/commands/uiAudit.js.map +1 -0
  184. package/dist/cli/commands/uiRender.js +206 -0
  185. package/dist/cli/commands/uiRender.js.map +1 -0
  186. package/dist/cli/main.js +22 -1
  187. package/dist/cli/main.js.map +1 -1
  188. package/dist/cli/mcp/content.js +64 -1
  189. package/dist/cli/mcp/content.js.map +1 -1
  190. package/dist/cli/mcp/dispatch.js +175 -10
  191. package/dist/cli/mcp/dispatch.js.map +1 -1
  192. package/dist/cli/registry.js +3 -0
  193. package/dist/cli/registry.js.map +1 -1
  194. package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
  195. package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
  196. package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
  197. package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
  198. package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
  199. package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
  200. package/dist/cli-delegate/cmd_brand_status.js +8 -6
  201. package/dist/cli-delegate/cmd_conformance.js +3 -3
  202. package/dist/cli-delegate/cmd_doctor.js +3 -3
  203. package/dist/cli-delegate/cmd_export.js +4 -4
  204. package/dist/cli-delegate/cmd_handoff.js +1 -1
  205. package/dist/cli-delegate/cmd_preflight.js +2 -2
  206. package/dist/cli-delegate/cmd_prune.js +4 -4
  207. package/dist/cli-delegate/cmd_session_recycle.js +70 -9
  208. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  209. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  210. package/dist/cli-delegate/cmd_sync.js +4 -4
  211. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  212. package/dist/cli-delegate/cmd_validate.js +4 -4
  213. package/dist/discovery/deprecation-report.md +1 -1
  214. package/dist/discovery/discovery-manifest.json +380 -145
  215. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  216. package/dist/discovery/discovery-manifest.summary.md +4 -4
  217. package/dist/discovery/dormancy-report.md +1 -1
  218. package/dist/discovery/orphan-report.md +1 -1
  219. package/dist/discovery/packs.json +18 -8
  220. package/dist/discovery/trust-report.md +3 -3
  221. package/dist/discovery/workspaces.json +16 -6
  222. package/dist/hooks/dispatch.js +2337 -1436
  223. package/dist/install/install.mjs +795 -708
  224. package/dist/mcp/registry-manifest.json +3 -3
  225. package/dist/mcp/server.json +2 -2
  226. package/dist/mcp/server.mjs +1685 -1250
  227. package/dist/router.json +1 -1
  228. package/dist/scripts/_lib/lean_projection_mode.js +40 -0
  229. package/dist/scripts/_lib/lean_projection_mode.js.map +1 -0
  230. package/dist/scripts/_lib/scoped_projection.js +84 -0
  231. package/dist/scripts/_lib/scoped_projection.js.map +1 -1
  232. package/dist/scripts/_lib/ui_surface.js +89 -0
  233. package/dist/scripts/_lib/ui_surface.js.map +1 -0
  234. package/dist/scripts/condense.js +6 -10
  235. package/dist/scripts/condense.js.map +1 -1
  236. package/dist/scripts/project_thin_rules.js +40 -2
  237. package/dist/scripts/project_thin_rules.js.map +1 -1
  238. package/dist/server/schemas/settings.js +3 -0
  239. package/dist/server/schemas/settings.js.map +1 -1
  240. package/dist/shared/skillRanking.js +159 -0
  241. package/dist/shared/skillRanking.js.map +1 -0
  242. package/docs/contracts/provider-lifecycle.md +47 -18
  243. package/docs/contracts/settings-classes.md +14 -4
  244. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
  245. package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
  246. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
  247. package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
  248. package/docs/guidelines/design-antipatterns.md +4 -2
  249. package/docs/guidelines/design-asset-discipline.md +38 -0
  250. package/docs/guidelines/design-fidelity-mechanics.md +45 -29
  251. package/docs/guidelines/design-handover-extraction.md +35 -5
  252. package/docs/guidelines/design-modes.md +41 -1
  253. package/docs/guidelines/monorepo-antipatterns.md +141 -0
  254. package/llms.txt +50 -39
  255. package/package.json +1 -1
  256. package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
  257. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
  258. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
  259. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
  260. package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
  261. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
  262. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
  263. package/src/config/agent-settings.template.yml +28 -0
  264. package/src/config/assurance-capability-registry.json +223 -0
  265. package/src/config/assurance-threshold-budget.json +65 -0
  266. package/src/config/ci-local-parity.yml +14 -0
  267. package/src/config/discovery/unassigned-artefacts.yml +2 -0
  268. package/src/config/dispatch-economy-metrics.json +4 -2
  269. package/src/config/evaluator-budgets.json +14 -14
  270. package/src/config/gate-coverage.yml +227 -8
  271. package/src/config/gate-violation-baselines.json +76 -72
  272. package/src/config/hook-latency-budget.json +1 -0
  273. package/src/config/hook-token-budget.json +137 -125
  274. package/src/config/pack-size-budget.json +4 -2
  275. package/src/config/preamble-payload-budget.json +71 -67
  276. package/src/config/quorum-attendance-budget.json +12 -4
  277. package/src/config/rule-activation-census.json +7 -3
  278. package/src/config/rule-enforcement-baseline.json +0 -1
  279. package/src/config/rule-stub-ceilings.json +112 -91
  280. package/src/scripts/_cli/cmd_doctor.ts +15 -16
  281. package/src/scripts/_cli/doctor_install_mode.ts +49 -0
  282. package/src/scripts/_cli/doctor_overrides.ts +154 -0
  283. package/src/scripts/_dispatch.bash +17 -0
  284. package/src/scripts/_lib/as_of.ts +412 -0
  285. package/src/scripts/_lib/exec_evidence.ts +7 -0
  286. package/src/scripts/_lib/hook_settings.ts +33 -0
  287. package/src/scripts/_lib/host_listing_model.ts +192 -0
  288. package/src/scripts/_lib/lean_projection_mode.ts +44 -0
  289. package/src/scripts/_lib/mcp_bridge.ts +199 -0
  290. package/src/scripts/_lib/review_independence.ts +62 -10
  291. package/src/scripts/_lib/rule_injection.ts +239 -0
  292. package/src/scripts/_lib/run_checkpoint.ts +54 -1
  293. package/src/scripts/_lib/scoped_projection.ts +96 -0
  294. package/src/scripts/_lib/session_register.ts +44 -2
  295. package/src/scripts/_lib/skill_catalogue.ts +38 -2
  296. package/src/scripts/_lib/transcript_reads.ts +207 -0
  297. package/src/scripts/_lib/ui_authority.test.ts +251 -0
  298. package/src/scripts/_lib/ui_authority.ts +343 -0
  299. package/src/scripts/ai-video/adapters/fal.sh +8 -3
  300. package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
  301. package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
  302. package/src/scripts/ai-video/adapters/kling.sh +10 -0
  303. package/src/scripts/ai-video/adapters/sora.sh +10 -0
  304. package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
  305. package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
  306. package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
  307. package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
  308. package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
  309. package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
  310. package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
  311. package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
  312. package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
  313. package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
  314. package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
  315. package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
  316. package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
  317. package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
  318. package/src/scripts/ai-video/smoke-trace.sh +450 -2
  319. package/src/scripts/ai-video/stitch.sh +191 -10
  320. package/src/scripts/ai-video/test-pipeline.sh +84 -6
  321. package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
  322. package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
  323. package/src/scripts/ai_council/clients.ts +3 -2
  324. package/src/scripts/ai_council/consensus.ts +63 -6
  325. package/src/scripts/ai_council/events_log.ts +46 -1
  326. package/src/scripts/ai_council/orchestrator.ts +103 -108
  327. package/src/scripts/ai_council/orchestrator_results.ts +166 -0
  328. package/src/scripts/ai_council/prompts.ts +12 -5
  329. package/src/scripts/ai_council/quorum.ts +82 -1
  330. package/src/scripts/ai_council/quorum_wiring.ts +82 -5
  331. package/src/scripts/ai_council/seating.ts +211 -0
  332. package/src/scripts/ai_council/stance_tally.ts +22 -0
  333. package/src/scripts/bench_hook_latency.ts +62 -0
  334. package/src/scripts/build_proof.ts +12 -1
  335. package/src/scripts/capture_skill_catalogue.ts +21 -0
  336. package/src/scripts/check_always_budget.ts +2 -1
  337. package/src/scripts/check_augmentignore.ts +2 -1
  338. package/src/scripts/check_beta_review_markers.ts +2 -1
  339. package/src/scripts/check_composite_arming.ts +364 -0
  340. package/src/scripts/check_corpus_staleness.ts +2 -1
  341. package/src/scripts/check_council_pin_staleness.ts +3 -1
  342. package/src/scripts/check_enforcement_coverage.ts +116 -3
  343. package/src/scripts/check_enforcement_denominator.ts +365 -0
  344. package/src/scripts/check_gate_coverage.ts +2 -1
  345. package/src/scripts/check_hook_bundle_freshness.ts +13 -1
  346. package/src/scripts/check_knowledge_cards.ts +2 -1
  347. package/src/scripts/check_knowledge_pages.ts +3 -2
  348. package/src/scripts/check_memory.ts +2 -1
  349. package/src/scripts/check_no_automerge_key.ts +287 -0
  350. package/src/scripts/check_pack_conformance_fixture.ts +565 -0
  351. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  352. package/src/scripts/check_proposal.ts +2 -1
  353. package/src/scripts/check_reach_staleness.ts +2 -1
  354. package/src/scripts/check_release_adjacent_health.ts +2 -1
  355. package/src/scripts/check_release_pr_shape.ts +5 -0
  356. package/src/scripts/check_review_schema.ts +34 -1
  357. package/src/scripts/check_roadmap_trackable.ts +48 -3
  358. package/src/scripts/check_source_size_budget.ts +2 -1
  359. package/src/scripts/check_standing_payload_delta.ts +505 -0
  360. package/src/scripts/check_trigger_evals.ts +2 -1
  361. package/src/scripts/classify_change_risk.ts +281 -0
  362. package/src/scripts/compute_skill_tiers.ts +186 -0
  363. package/src/scripts/condense.ts +8 -13
  364. package/src/scripts/council_attendance_metrics.ts +183 -0
  365. package/src/scripts/council_cli.ts +10 -8
  366. package/src/scripts/council_parse_rate.ts +116 -0
  367. package/src/scripts/derive_playbooks.ts +406 -0
  368. package/src/scripts/design_slop_rules.test.ts +7 -0
  369. package/src/scripts/design_slop_rules.ts +56 -2
  370. package/src/scripts/dispatch_r2_reviewer.ts +16 -0
  371. package/src/scripts/frontend_corpus_hash.ts +116 -0
  372. package/src/scripts/generate_pack_manifests.ts +78 -0
  373. package/src/scripts/grade_target_readiness.ts +478 -0
  374. package/src/scripts/hook_manifest.json +1 -1
  375. package/src/scripts/hook_manifest.yaml +97 -16
  376. package/src/scripts/hooks/concern_registry.ts +4 -0
  377. package/src/scripts/hooks/design_pass_hook.ts +356 -0
  378. package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
  379. package/src/scripts/hooks/rule_inject_hook.ts +318 -0
  380. package/src/scripts/hot_context_hook.ts +74 -7
  381. package/src/scripts/install.ts +40 -89
  382. package/src/scripts/lint_adapter_tier.ts +663 -0
  383. package/src/scripts/lint_archived_skills.ts +93 -0
  384. package/src/scripts/lint_budget_ownership.ts +2 -1
  385. package/src/scripts/lint_deterministic_time.ts +470 -0
  386. package/src/scripts/lint_framework_leakage.ts +5 -0
  387. package/src/scripts/lint_handoffs.ts +525 -1
  388. package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
  389. package/src/scripts/lint_marketplace.ts +88 -0
  390. package/src/scripts/lint_media_policy_linkage.ts +140 -1
  391. package/src/scripts/lint_one_off_age.ts +6 -1
  392. package/src/scripts/lint_override_kernel_guard.ts +87 -5
  393. package/src/scripts/lint_roadmap_complexity.ts +56 -0
  394. package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
  395. package/src/scripts/lint_rule_norm_pin.ts +321 -0
  396. package/src/scripts/lint_spawn_payload.ts +42 -5
  397. package/src/scripts/lint_symptom_intake.ts +2 -1
  398. package/src/scripts/mcp_parity_smoke.ts +30 -4
  399. package/src/scripts/mcp_render.ts +66 -1
  400. package/src/scripts/mcp_tool_fingerprint.ts +241 -0
  401. package/src/scripts/measure_risk_class_drift.ts +140 -0
  402. package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
  403. package/src/scripts/media/lib/adapter-common.sh +211 -1
  404. package/src/scripts/media/lib/adapter-contract.md +74 -6
  405. package/src/scripts/model_rule_injection.ts +907 -0
  406. package/src/scripts/project_thin_rules.ts +45 -2
  407. package/src/scripts/release.ts +66 -860
  408. package/src/scripts/release_drill.ts +59 -3
  409. package/src/scripts/release_env.ts +298 -0
  410. package/src/scripts/release_publication.ts +720 -0
  411. package/src/scripts/roadmap_context.ts +778 -0
  412. package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
  413. package/src/scripts/schemas/pack.schema.json +62 -4
  414. package/src/scripts/schemas/rule.schema.json +24 -2
  415. package/src/scripts/schemas/skill.schema.json +41 -2
  416. package/src/scripts/schemas/ui-authority.schema.json +114 -0
  417. package/src/scripts/session_register_hook.ts +29 -3
  418. package/src/scripts/sessions_cli.ts +67 -3
  419. package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
  420. package/src/scripts/tighten_skill_descriptions.ts +264 -0
  421. package/src/scripts/token_sink_report.ts +239 -0
  422. package/src/scripts/trigger_coverage.ts +122 -3
  423. package/src/scripts/update_counts.ts +7 -3
  424. package/src/scripts/validate_frontmatter.ts +66 -0
  425. package/src/server/schemas/settings.ts +5 -0
  426. package/src/shared/skillRanking.ts +183 -0
@@ -37,8 +37,9 @@ import {
37
37
  type QuorumCommand,
38
38
  type QuorumDispatch,
39
39
  type QuorumEventPhase,
40
+ type StanceAgreement,
40
41
  } from './events_log.js';
41
- import { evaluateQuorum, SOLO_FLOOR_MIN_PRESENT, type QuorumResult } from './quorum.js';
42
+ import { evaluateQuorum, formatAttendanceCaveats, SOLO_FLOOR_MIN_PRESENT, withUnparsed, type QuorumResult } from './quorum.js';
42
43
  import { absentReasonFromCliFailure, classifyCliFailure } from './transport_resolver.js';
43
44
 
44
45
  type Dict = Record<string, unknown>;
@@ -117,6 +118,12 @@ export function _emitQuorumEvent(
117
118
  * absence is what the `false` on every line honestly records.
118
119
  */
119
120
  minPresent?: number;
121
+ /**
122
+ * Phase 3 — whether the seats agreed, when a stance tally ran.
123
+ * Omitted by every caller that ran none, which `appendQuorumEvent`
124
+ * records as `not_tallied` rather than as a bare `false`.
125
+ */
126
+ stanceAgreement?: StanceAgreement;
120
127
  },
121
128
  ): void {
122
129
  appendQuorumEvent({
@@ -128,6 +135,7 @@ export function _emitQuorumEvent(
128
135
  configuredTotal: ctx.configuredTotal ?? quorum.total,
129
136
  result: quorum,
130
137
  ...(ctx.minPresent !== undefined ? { minPresent: ctx.minPresent } : {}),
138
+ ...(ctx.stanceAgreement !== undefined ? { stanceAgreement: ctx.stanceAgreement } : {}),
131
139
  absent: absent.map(
132
140
  (a): QuorumAbsence => ({
133
141
  member: String(a['member'] ?? ''),
@@ -154,9 +162,11 @@ export function _postRunQuorum(
154
162
  members: ExternalAIClient[],
155
163
  responses: CouncilResponse[],
156
164
  ai_cfg: Dict,
165
+ parse_outcomes?: ReadonlyMap<string, string>,
157
166
  ): { quorum: QuorumResult; absent: Dict[] } {
158
167
  const absent: Dict[] = [];
159
168
  let present = 0;
169
+ let unparsed = 0;
160
170
  for (let i = 0; i < members.length; i++) {
161
171
  const m = members[i] as ExternalAIClient;
162
172
  const r = responses[i];
@@ -167,7 +177,33 @@ export function _postRunQuorum(
167
177
  // banner printed `2/2 present` — a single-voice verdict presented as
168
178
  // convergence, on a paid run. An empty answer contributes nothing to a
169
179
  // quorum by definition, whatever the transport thought of it.
180
+ // Step 2.3 — the same argument, one rung further in. The byte check
181
+ // below settles "did the transport deliver anything"; it cannot settle
182
+ // "was what it delivered usable content", and a prose refusal passes it
183
+ // unchanged. So a member whose findings answer reached `parse_failed`
184
+ // is pulled out of `present` into its own bucket rather than folded
185
+ // into `N/N present`. It is NOT merged with the error cases: those are
186
+ // transport failures with an `AbsentReason` taxonomy the absent-member
187
+ // table renders, and re-labelling one of them `unparsed` would lose the
188
+ // auth/timeout/quota distinction. Hence the error branch below wins for
189
+ // a member that both errored and parsed badly — the transport failure
190
+ // is the stronger fact.
191
+ //
192
+ // `parse_outcomes` is optional because the run path derives it AFTER
193
+ // the post-run attendance event is emitted: the event stays a
194
+ // transport-level reading by design, and the rendered artefact is where
195
+ // AC-2 asks for the distinction. A caller with no map gets the exact
196
+ // pre-2.3 behaviour, key-for-key.
170
197
  if (r !== undefined && !r.error && r.text.trim() !== '') {
198
+ if (parse_outcomes?.get(m.name) === 'parse_failed') {
199
+ unparsed += 1;
200
+ absent.push({
201
+ member: m.name,
202
+ reason: 'unparsed',
203
+ detail: 'answer present, no parser could read it',
204
+ });
205
+ continue;
206
+ }
171
207
  present += 1;
172
208
  continue;
173
209
  }
@@ -179,7 +215,46 @@ export function _postRunQuorum(
179
215
  detail: raw,
180
216
  });
181
217
  }
182
- return { quorum: evaluateQuorum(members.length, present, _quorum_setting_from(ai_cfg)), absent };
218
+ return { quorum: evaluateQuorum(members.length, present, _quorum_setting_from(ai_cfg), unparsed), absent };
219
+ }
220
+
221
+ /**
222
+ * Which of the three agreement states a stance tally represents.
223
+ *
224
+ * `undefined` when no tally ran — the emitter records that as `not_tallied`,
225
+ * which is a fact ("nobody asked"), not a `false` that reads as disagreement.
226
+ * Returned as an optional rather than a nullable so the caller can spread it
227
+ * under `exactOptionalPropertyTypes`, where an explicit `undefined` and an
228
+ * absent key are different things.
229
+ */
230
+ export function stanceAgreementOf(
231
+ tally: { consensus: unknown } | null,
232
+ ): StanceAgreement | undefined {
233
+ if (tally === null) {
234
+ return undefined;
235
+ }
236
+ return tally.consensus !== null ? 'consensus' : 'split';
237
+ }
238
+
239
+ /**
240
+ * Re-derive the rendered attendance line once the findings parser has spoken.
241
+ *
242
+ * Lives here rather than at the call site because it is the second half of the
243
+ * split `withUnparsed` documents: the post-run event stays a TRANSPORT-level
244
+ * reading so every rate over `events.log` keeps its denominator, and the
245
+ * rendered artefact is where AC-2 asks for the unparsed distinction. Counting
246
+ * `parse_failed` outcomes is the whole derivation; keeping it next to
247
+ * `_postRunQuorum` keeps both readings in one file.
248
+ */
249
+ export function annotateRenderedQuorum(
250
+ quorum: QuorumResult | null,
251
+ parse_outcomes: ReadonlyMap<string, string> | null | undefined,
252
+ ): QuorumResult | null {
253
+ if (quorum === null || parse_outcomes === null || parse_outcomes === undefined) {
254
+ return quorum;
255
+ }
256
+ const unparsed = Array.from(parse_outcomes.values()).filter((o) => o === 'parse_failed').length;
257
+ return withUnparsed(quorum, unparsed);
183
258
  }
184
259
 
185
260
  /**
@@ -205,7 +280,9 @@ export function _format_quorum_line(q: QuorumResult, phase?: QuorumEventPhase):
205
280
  // a solo answer concludes, and nothing in the line distinguishes "both
206
281
  // members agreed" from "one member answered". The counts were always there;
207
282
  // what was missing is the word that stops a reader inferring convergence.
208
- const degraded =
209
- q.present < q.total ? ` ⚠️ DEGRADED — ${String(q.total - q.present)} member(s) did not answer; this is not convergence.` : '';
210
- return `council:quorum · ${tag}${q.present}/${q.total} present, needed ${q.threshold} — ${verdict}.${degraded}`;
283
+ // Step 2.3 moved the wording into `formatAttendanceCaveats` so this line and
284
+ // its mirror in `orchestrator.ts` cannot drift apart, and added the
285
+ // present-unparsed clause there — see that function for why the two counts
286
+ // are disjoint.
287
+ return `council:quorum · ${tag}${q.present}/${q.total} present, needed ${q.threshold} — ${verdict}.${formatAttendanceCaveats(q)}`;
211
288
  }
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Who is seated for a run, decided once and frozen.
3
+ *
4
+ * Selection today is config-static ask-all: every enabled member, every
5
+ * question, no per-question seating. This adds an OPTIONAL declaration of what
6
+ * a run wants from its seats, and computes the answer once.
7
+ *
8
+ * FAMILY DIVERSITY ONLY — no band ordering. `blocker: b-ladder-order-benchmark-spend`
9
+ * was resolved (b) on 2026-08-22: nothing in this tree establishes that a higher
10
+ * capability band reviews better, the archived benchmark substrate would have to
11
+ * be re-run to support it, and the council that would have decided otherwise had
12
+ * no quota. So `tier` keeps its single documented meaning — a capability rank
13
+ * read only as a chairman-selection input — and nothing here reads it. A seat
14
+ * declaration carries MISSION, never RANK.
15
+ *
16
+ * "Family" is the PROVIDER, not a capability band. For a council, diversity means
17
+ * independent providers: two Claude bands share a vendor, a training pipeline and
18
+ * a failure mode, so seating `opus` beside `sonnet` buys none of what a second
19
+ * opinion is for. (`MODEL_FAMILIES` in the orchestration-record hook enumerates
20
+ * Claude bands for a different purpose — token attribution — and is deliberately
21
+ * not reused here.)
22
+ *
23
+ * Absent declaration ⇒ today's behaviour, byte-identical. That is asserted by a
24
+ * test rather than intended.
25
+ */
26
+ import type { MemberConfig } from './config.js';
27
+
28
+ /** A member's provider family. The map key IS the provider in this config shape. */
29
+ export function familyOf(member: MemberConfig): string {
30
+ return member.name;
31
+ }
32
+
33
+ /**
34
+ * Read the optional `seat_constraints:` block out of a raw council config.
35
+ *
36
+ * DELIBERATELY NOT A FIELD ON `CouncilConfig`, and the reason is a ratchet
37
+ * rather than a design preference. `config.ts` is 2,205 lines — 705 over the
38
+ * 1,500-line source-size line — and `check_source_size_budget` counts only the
39
+ * excess above that line, so the 39 lines this feature first added there were a
40
+ * straight +39 against a shrink-only baseline. The gate's own message is
41
+ * explicit that raising the baseline is "a defect, not a fix". Extraction is the
42
+ * sanctioned repair, and this file is 169 lines, so the same code costs nothing
43
+ * here.
44
+ *
45
+ * THE TRADE-OFF, stated rather than buried: validation moves from LOAD time to
46
+ * READ time. A malformed `min_families` no longer fails the moment the config
47
+ * is parsed; it fails when a caller asks for the constraints. Today that
48
+ * difference is unobservable — Phase 2 ships the DECLARATION and no caller
49
+ * reads it yet — but it is a real change in when a bad config is caught, and
50
+ * whoever wires the first caller should call this at run start so the failure
51
+ * lands before any seat is used.
52
+ *
53
+ * Fails CLOSED on a malformed value rather than falling back to absent: a run
54
+ * that declared a diversity floor and silently got none is the exact silent
55
+ * fallback the degradation line exists to prevent.
56
+ */
57
+ export function readSeatConstraints(raw: unknown, label = 'seat_constraints'): SeatConstraints {
58
+ if (raw === null || raw === undefined) return {};
59
+ if (typeof raw !== 'object' || Array.isArray(raw)) {
60
+ throw new TypeError(`${label} must be a mapping (got ${typeof raw}).`);
61
+ }
62
+ const block = (raw as Record<string, unknown>)['seat_constraints'] ?? raw;
63
+ if (block === null || block === undefined) return {};
64
+ if (typeof block !== 'object' || Array.isArray(block)) {
65
+ throw new TypeError(`${label} must be a mapping (got ${typeof block}).`);
66
+ }
67
+ const mf = (block as Record<string, unknown>)['min_families'];
68
+ if (mf === null || mf === undefined) return {};
69
+ if (typeof mf !== 'number' || !Number.isInteger(mf) || mf < 1) {
70
+ throw new TypeError(`${label}.min_families must be an integer >= 1 when set (got ${String(mf)}).`);
71
+ }
72
+ return { min_families: mf };
73
+ }
74
+
75
+ export interface SeatConstraints {
76
+ /**
77
+ * Minimum distinct provider families the run wants seated.
78
+ *
79
+ * Absent (or 1) ⇒ no constraint and no degradation line: ask-all, unchanged.
80
+ */
81
+ readonly min_families?: number;
82
+ }
83
+
84
+ export interface Seating {
85
+ /** The seated members, in deterministic config order. */
86
+ readonly seats: readonly MemberConfig[];
87
+ /** Distinct families actually seated. */
88
+ readonly families: readonly string[];
89
+ /**
90
+ * Exactly ONE line when the constraint could not be met, `null` otherwise.
91
+ *
92
+ * One line, not a wall: a degradation nobody reads is the same as a silent
93
+ * fallback, and N warnings is how a reader learns to skip the header.
94
+ */
95
+ readonly degraded: string | null;
96
+ }
97
+
98
+ /**
99
+ * Compute the seating once.
100
+ *
101
+ * Deterministic and side-effect-free: the same members and constraints always
102
+ * produce the same seats, which is what makes freezing it meaningful. Seating
103
+ * that changed between round 1 and round 3 would silently invalidate the
104
+ * anonymised-peer-reply contract the council skill depends on — a "reviewer B"
105
+ * in round 3 that is not the "reviewer B" of round 1 makes every cross-reference
106
+ * in the synthesis wrong while looking fine.
107
+ */
108
+ export function resolveSeating(
109
+ members: readonly MemberConfig[],
110
+ constraints: SeatConstraints = {},
111
+ ): Seating {
112
+ const enabled = members.filter((m) => m.enabled);
113
+ const families = [...new Set(enabled.map(familyOf))];
114
+ const want = constraints.min_families ?? 1;
115
+
116
+ if (want <= 1 || families.length >= want) {
117
+ return { seats: enabled, families, degraded: null };
118
+ }
119
+ return {
120
+ seats: enabled,
121
+ families,
122
+ degraded:
123
+ `tier-degraded: asked for ${String(want)} model families, seated ${String(families.length)} ` +
124
+ `(${families.join(', ')}). The run proceeds with what is enabled — a council that refused to ` +
125
+ `convene on a diversity shortfall would fail closed on the common single-provider install, ` +
126
+ `which is worse than a stated shortfall.`,
127
+ };
128
+ }
129
+
130
+ /**
131
+ * Freeze a seating so a later config mutation cannot change it.
132
+ *
133
+ * Returns a structurally frozen snapshot. This is the mechanism behind "resolve
134
+ * once per run": the caller resolves at run start, freezes, and passes the
135
+ * snapshot down — so there is no path by which round 3 re-reads the config.
136
+ */
137
+ export function freezeSeating(s: Seating): Seating {
138
+ return Object.freeze({
139
+ seats: Object.freeze([...s.seats]),
140
+ families: Object.freeze([...s.families]),
141
+ degraded: s.degraded,
142
+ });
143
+ }
144
+
145
+ /**
146
+ * Vendor sentinels and "latest in band" aliases — an id that cannot go stale.
147
+ *
148
+ * Read from the providers' own CLI surfaces on 2026-08-22, never from recall:
149
+ * `claude --help` documents 'fable', 'opus' and 'sonnet' as aliases for the
150
+ * latest model in their band, and `codex-default` is the openai transport's
151
+ * documented "let the CLI choose".
152
+ */
153
+ export const ADMISSIBLE_SENTINELS: ReadonlySet<string> = new Set([
154
+ 'codex-default',
155
+ 'fable',
156
+ 'opus',
157
+ 'sonnet',
158
+ 'haiku',
159
+ ]);
160
+
161
+ export interface AdmissibilityResult {
162
+ readonly admissible: boolean;
163
+ readonly reason: string | null;
164
+ }
165
+
166
+ /**
167
+ * Is this member's `model:` id from an admissible SOURCE?
168
+ *
169
+ * THE CRITERION AS WRITTEN COULD NOT BE IMPLEMENTED, and the deviation is
170
+ * recorded here rather than papered over. The roadmap step says a declared seat
171
+ * "resolves through the shipped `model_tier` bands; a model id that no tier maps
172
+ * to is rejected at config load". But `TIER_TO_CLAUDE_MODEL`
173
+ * (`_lib/model_tier.ts:36-41`) maps tiers onto CLAUDE aliases only — there is no
174
+ * entry any `gemini-*`, `grok-*` or `sonar-*` id could resolve through. Taken
175
+ * literally, that rule rejects every non-anthropic member in the shipped starter
176
+ * config, i.e. it fails closed on a valid config.
177
+ *
178
+ * What the step is actually protecting is stated in its own title: "including
179
+ * the host's own recall". The failure mode is a plausible-sounding model id
180
+ * TYPED FROM MEMORY, which is indistinguishable from a real one at review time
181
+ * and would silently undo the pin refresh. So admissibility is checked against
182
+ * the SOURCE of the id, over the two things this tree can actually verify:
183
+ *
184
+ * 1. a vendor sentinel or documented "latest in band" alias — cannot go stale,
185
+ * and read from the provider's own surface; or
186
+ * 2. a dated pin carrying a `verified_at` stamp — which records that a human
187
+ * looked at the provider's surface on a named date, and which
188
+ * `check_council_pin_staleness` ages out.
189
+ *
190
+ * An id that is neither is exactly the recall case: a bare dated id nobody
191
+ * recorded checking.
192
+ */
193
+ export function checkModelAdmissibility(member: MemberConfig): AdmissibilityResult {
194
+ const id = member.model.trim().toLowerCase();
195
+ if (ADMISSIBLE_SENTINELS.has(id)) {
196
+ return { admissible: true, reason: null };
197
+ }
198
+ if (member.verified_at !== null && member.verified_at !== undefined) {
199
+ return { admissible: true, reason: null };
200
+ }
201
+ return {
202
+ admissible: false,
203
+ reason:
204
+ `members.${member.name}.model is '${member.model}' — a dated id with no verified_at stamp. ` +
205
+ `An id must come from a source this tree can check: a vendor sentinel or documented ` +
206
+ `"latest in band" alias (${[...ADMISSIBLE_SENTINELS].join(', ')} — see src/scripts/_lib/model_tier.ts ` +
207
+ `for the tier vocabulary these bands come from), or a pin carrying verified_at: "YYYY-MM-DD" ` +
208
+ `recorded from the provider's own surface. A model id remembered rather than read is not an ` +
209
+ `admissible source, and is indistinguishable from a real one at review time.`,
210
+ };
211
+ }
@@ -218,6 +218,28 @@ export function tally_stances(
218
218
  * Render the **Vote Tally** verdict section: one line per option, the threshold,
219
219
  * and a cleared-or-escalated line. Deterministic — pure projection of the tally.
220
220
  */
221
+ /**
222
+ * Tally the stance lines of a finished round, or `null` when tallying is off.
223
+ *
224
+ * Wraps the member-shape mapping so a caller does not re-derive it: an errored
225
+ * response has no stance line to read, and the member key is
226
+ * `provider:model` because that is what `parse_stance_line` records. Exists so
227
+ * ONE tally is computed per pass and reused — two tallies over the same
228
+ * responses are two chances to disagree, and the attendance event and the
229
+ * handoff envelope both consume this one.
230
+ */
231
+ export function tallyFromResponses(
232
+ responses: ReadonlyArray<{ provider: string; model: string; text: string; error?: string | null }>,
233
+ enabled: boolean,
234
+ ): StanceTallyResult | null {
235
+ if (!enabled) {
236
+ return null;
237
+ }
238
+ return tally_stances(
239
+ responses.filter((r) => !r.error).map((r) => ({ member: `${r.provider}:${r.model}`, text: r.text })),
240
+ );
241
+ }
242
+
221
243
  export function render_vote_tally(result: StanceTallyResult): string {
222
244
  const lines: string[] = ['### Vote Tally'];
223
245
  if (result.options.length === 0) {
@@ -580,6 +580,58 @@ export function perTurnComposite(
580
580
  };
581
581
  }
582
582
 
583
+ /**
584
+ * A1.1 — append ONE composite reading to the durable store.
585
+ *
586
+ * The bench used to print the composite and discard it, which made
587
+ * `per_turn_composite.arming_precondition` ("at least 10 CI gate readings … from
588
+ * at least 2 distinct runner sessions") impossible to evaluate at all. This is
589
+ * the write half; `check_composite_arming.ts` is the read half.
590
+ *
591
+ * THE SESSION DISCRIMINATOR IS THE LOAD-BEARING FIELD, not the count. The
592
+ * precondition asks for two distinct runner SESSIONS because the instability it
593
+ * excludes was measured on ONE machine — a sibling metric read 44-157 % at n=12
594
+ * and 69-74 % at n=50 there — so twelve readings from one session is exactly the
595
+ * shape the floor rejects, and a bare counter cannot see the difference.
596
+ *
597
+ * A null composite is written as null rather than skipped. Dropping it silently
598
+ * would make the store's own length a lie about how many runs contributed, and
599
+ * the reader needs to distinguish "no run" from "a run whose slots were
600
+ * incomplete" — `perTurnComposite` returns null exactly because a composite over
601
+ * a subset reads LOW, and low is the direction that makes a ceiling look met.
602
+ */
603
+ export function composeReadingRecord(
604
+ composite: { ms: number; parts: Record<string, number> } | null,
605
+ tool_calls: number,
606
+ env: NodeJS.ProcessEnv = process.env,
607
+ ): Record<string, unknown> {
608
+ // In CI a session is the workflow run on a given runner; locally it is the
609
+ // host plus the process's own start, so two consecutive local runs are
610
+ // distinguishable while one run's records stay grouped.
611
+ const ciRun = env['GITHUB_RUN_ID'];
612
+ const session =
613
+ ciRun !== undefined && ciRun !== ''
614
+ ? `gh-${ciRun}-${env['RUNNER_OS'] ?? os.platform()}-${env['GITHUB_RUN_ATTEMPT'] ?? '1'}`
615
+ : `local-${os.hostname()}-${String(process.pid)}`;
616
+ return {
617
+ composite_ms: composite === null ? null : composite.ms,
618
+ parts: composite === null ? null : composite.parts,
619
+ tool_calls,
620
+ session,
621
+ platform: `${os.platform()}-${os.arch()}`,
622
+ node: process.version,
623
+ commit: env['GITHUB_SHA'] ?? null,
624
+ run_id: ciRun ?? null,
625
+ recorded_at: new Date().toISOString(),
626
+ };
627
+ }
628
+
629
+ /** Append one JSONL record. Creates the file and its directory if absent. */
630
+ export function appendReading(storePath: string, record: Record<string, unknown>): void {
631
+ fs.mkdirSync(path.dirname(storePath), { recursive: true });
632
+ fs.appendFileSync(storePath, `${JSON.stringify(record)}\n`, 'utf-8');
633
+ }
634
+
583
635
  interface HistoryEntry {
584
636
  recorded_at: string;
585
637
  invocation_path: InvocationPath;
@@ -895,6 +947,16 @@ export function main(argv: string[] = process.argv.slice(2)): number {
895
947
  : perTurnComposite(results, compositeCfg.tool_calls ?? 10);
896
948
  if (compositeCfg !== undefined) {
897
949
  const calls = compositeCfg.tool_calls ?? 10;
950
+ // A1.1: persist the reading BEFORE the ceiling comparison below, so a run
951
+ // that fails the gate still contributes its number. A store that only
952
+ // gets the passing runs is biased in the direction that makes a ceiling
953
+ // look met, which is the same bias the null-composite rule guards.
954
+ const recIdx = argv.indexOf('--record-composite');
955
+ if (recIdx >= 0) {
956
+ const dest = argv[recIdx + 1] ?? path.join(REPO_ROOT, 'agents', 'evidence', 'hook-composite-readings.jsonl');
957
+ appendReading(dest, composeReadingRecord(composite, calls));
958
+ process.stdout.write(`recorded composite reading → ${path.relative(REPO_ROOT, dest)}\n`);
959
+ }
898
960
  if (composite === null) {
899
961
  process.stdout.write(
900
962
  `ℹ️ per-turn composite not computed — a slot the definition needs is missing from this run\n`,
@@ -24,7 +24,11 @@ import * as path from 'node:path';
24
24
  import { fileURLToPath, pathToFileURL } from 'node:url';
25
25
 
26
26
  import { REPO, LEDGER_REL, load_ledger, pointer_unresolved } from './check_claims.js';
27
- import { collect as collectEnforcement, summarise as summariseEnforcement } from './check_enforcement_coverage.js';
27
+ import {
28
+ collect as collectEnforcement,
29
+ denominator_line as enforcementDenominatorLine,
30
+ summarise as summariseEnforcement,
31
+ } from './check_enforcement_coverage.js';
28
32
  import { collectSkillGaps } from './check_skill_gaps.js';
29
33
  import { loadRows as loadComparisonRows } from './check_comparison.js';
30
34
  import { computeStatus as domainSoundnessStatus } from './domain_soundness_status.js';
@@ -415,6 +419,13 @@ function render(): string {
415
419
  `${s.local_only} local-only · ${s.undeclared} undeclared (no \`enforced_by\` yet).`,
416
420
  );
417
421
  L.push('');
422
+ // The denominator, WITH the frame that produced it. Until 2026-08-23 the
423
+ // tree published five figures for this one property because every one was
424
+ // quoted without saying which population it was taken over. This line is
425
+ // the single sanctioned statement of it, and `check_enforcement_denominator`
426
+ // reds when a published doc restates a count instead of citing this block.
427
+ L.push(`\`${enforcementDenominatorLine(s.frames)}\``);
428
+ L.push('');
418
429
  L.push('| Rule | Effective level | Declared backstop(s) |');
419
430
  L.push('|---|---|---|');
420
431
  const declared = rows
@@ -434,11 +434,32 @@ function runLimitsMode(): number {
434
434
  /** The package's two projection-mode counts, one walk. Shared by both modes. */
435
435
  function projectionModeCounts(): ProjectionModeCounts {
436
436
  const stats = scoped_projection_stats(REPO, iter_skills());
437
+ // The tier split is a per-machine runtime artefact (gitignored), so absent is
438
+ // the normal state and must read as "unmeasured", never as zero.
439
+ let tierACount: number | null = null;
440
+ let tierBCount: number | null = null;
441
+ const tiersFile = path.join(REPO, 'agents', 'runtime', 'state', 'skill-tiers.json');
442
+ if (fs.existsSync(tiersFile)) {
443
+ try {
444
+ const parsed = JSON.parse(fs.readFileSync(tiersFile, 'utf-8')) as {
445
+ tier_a?: unknown;
446
+ tier_b?: unknown;
447
+ };
448
+ if (Array.isArray(parsed.tier_a) && Array.isArray(parsed.tier_b)) {
449
+ tierACount = parsed.tier_a.length;
450
+ tierBCount = parsed.tier_b.length;
451
+ }
452
+ } catch {
453
+ /* a malformed split is an absent split — never a partial count */
454
+ }
455
+ }
437
456
  return {
438
457
  scoped: stats.projected,
439
458
  legacyAll: stats.total,
440
459
  prunedUnderScoped: stats.pruned,
441
460
  activePacks: stats.active_packs,
461
+ tierACount,
462
+ tierBCount,
442
463
  };
443
464
  }
444
465
 
@@ -29,6 +29,7 @@ import process from 'node:process';
29
29
  import { fileURLToPath, pathToFileURL } from 'node:url';
30
30
  import YAML from 'yaml';
31
31
 
32
+ import { asOf } from './_lib/as_of.js';
32
33
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
33
34
 
34
35
  // src/scripts/check_always_budget.ts → parents[2] is the repo root
@@ -435,7 +436,7 @@ function recordTrend(totalExt: number, sizes: Array<[string, number, number]>):
435
436
 
436
437
  /** datetime.now(timezone.utc).isoformat(timespec="seconds") → ...+00:00. */
437
438
  function isoSeconds(): string {
438
- const d = new Date();
439
+ const d = asOf();
439
440
  const pad = (v: number): string => String(v).padStart(2, '0');
440
441
  return (
441
442
  `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())}` +
@@ -24,6 +24,7 @@ import * as fs from 'node:fs';
24
24
  import * as path from 'node:path';
25
25
  import { fileURLToPath, pathToFileURL } from 'node:url';
26
26
 
27
+ import { asOfMs } from './_lib/as_of.js';
27
28
  import { assertWatchlistResolves, DeadScopeError } from './_lib/scan_scope.js';
28
29
 
29
30
  const QUIET = process.argv.slice(2).includes('--quiet');
@@ -76,7 +77,7 @@ function check(): number {
76
77
  }
77
78
 
78
79
  const mtimeMs = fs.statSync(target).mtimeMs;
79
- const age_days = (Date.now() - mtimeMs) / 86400000;
80
+ const age_days = (asOfMs() - mtimeMs) / 86400000;
80
81
  if (age_days > STALE_DAYS) {
81
82
  notes.push(
82
83
  `ℹ️ .augmentignore is ${Math.trunc(age_days)} days old (threshold: ${STALE_DAYS}) — ` +
@@ -19,6 +19,7 @@ import * as fs from 'node:fs';
19
19
  import * as path from 'node:path';
20
20
  import { fileURLToPath, pathToFileURL } from 'node:url';
21
21
 
22
+ import { asOf } from './_lib/as_of.js';
22
23
  import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
23
24
 
24
25
  const _HERE = fileURLToPath(import.meta.url);
@@ -185,7 +186,7 @@ function parse_args(argv: readonly string[]): ParsedArgs {
185
186
 
186
187
  /** Today's date as an ordinal, in local time (mirrors date.today()). */
187
188
  function _todayOrdinal(): number {
188
- const now = new Date();
189
+ const now = asOf();
189
190
  return _dateOrdinal(now.getFullYear(), now.getMonth() + 1, now.getDate());
190
191
  }
191
192