@event4u/agent-config 14.11.0 → 14.12.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 (350) 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 +156 -538
  5. package/README.md +1 -1
  6. package/dist/agent-src/contexts/execution/evidence-discipline.md +26 -0
  7. package/dist/agent-src/skills/code-refactoring/SKILL.md +45 -1
  8. package/dist/agent-src/skills/code-review/SKILL.md +65 -6
  9. package/dist/agent-src/skills/command-writing/SKILL.md +35 -0
  10. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +8 -1
  11. package/dist/agent-src/skills/project-analysis-core/SKILL.md +23 -0
  12. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +29 -12
  13. package/dist/agent-src/skills/skill-writing/references/procedure.md +24 -2
  14. package/dist/agent-src/skills/storybook-workshop/SKILL.md +9 -0
  15. package/dist/agent-src/skills/ui-component-architect/SKILL.md +8 -0
  16. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  17. package/dist/cli-delegate/{chunk-L43NCYPB.js → chunk-JCW7ZLHK.js} +5 -0
  18. package/dist/cli-delegate/{chunk-236PNQZK.js → chunk-JJA2QEWA.js} +1 -1
  19. package/dist/cli-delegate/cmd_export.js +2 -2
  20. package/dist/cli-delegate/cmd_prune.js +2 -2
  21. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  22. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  23. package/dist/cli-delegate/cmd_sync.js +2 -2
  24. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  25. package/dist/cli-delegate/cmd_validate.js +2 -2
  26. package/dist/discovery/deprecation-report.md +1 -1
  27. package/dist/discovery/discovery-manifest.json +10 -10
  28. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  29. package/dist/discovery/discovery-manifest.summary.md +1 -1
  30. package/dist/discovery/dormancy-report.md +1 -1
  31. package/dist/discovery/orphan-report.md +1 -1
  32. package/dist/discovery/packs.json +2 -2
  33. package/dist/discovery/trust-report.md +1 -1
  34. package/dist/discovery/workspaces.json +2 -2
  35. package/dist/hooks/dispatch.js +1081 -879
  36. package/dist/install/install.mjs +5 -0
  37. package/dist/mcp/registry-manifest.json +2 -2
  38. package/dist/mcp/server.json +2 -2
  39. package/dist/server/schemas/settings.js +3 -0
  40. package/docs/contracts/settings-classes.md +24 -4
  41. package/docs/guidelines/agent-infra/downstream-changes-mechanics.md +91 -0
  42. package/docs/guidelines/code-clarity.md +21 -0
  43. package/docs/guidelines/redundancy-taxonomy.md +180 -0
  44. package/package.json +6 -2
  45. package/src/config/agent-settings.template.yml +16 -0
  46. package/src/config/budgets.yml +19 -0
  47. package/src/config/ci-local-parity.yml +15 -0
  48. package/src/config/discovery/prompt-patterns.yml +56 -0
  49. package/src/config/estate-count-budget.json +8 -3
  50. package/src/config/gate-coverage.yml +191 -6
  51. package/src/config/gate-violation-baselines.json +3 -3
  52. package/src/config/pack-size-budget.json +39 -12
  53. package/src/config/preamble-payload-budget.json +97 -71
  54. package/src/config/routing-coverage-seed.json +29 -0
  55. package/src/config/suggestion-capture.json +28 -0
  56. package/src/config/surface-matrix.yml +21 -2
  57. package/src/config/web-launch-readiness.json +188 -0
  58. package/src/scripts/_lib/gate_baseline.ts +150 -1
  59. package/src/scripts/_lib/skill_estate.ts +115 -0
  60. package/src/scripts/ai_council/argument_exhaustion.ts +130 -0
  61. package/src/scripts/check_bundle_path_leakage.ts +162 -7
  62. package/src/scripts/check_command_examples.ts +557 -0
  63. package/src/scripts/check_estate_count.ts +122 -3
  64. package/src/scripts/check_preamble_payload_budget.ts +30 -3
  65. package/src/scripts/check_routing_coverage.ts +352 -0
  66. package/src/scripts/check_score_contract.ts +505 -0
  67. package/src/scripts/check_skill_admissions.ts +514 -0
  68. package/src/scripts/check_web_launch_readiness.ts +705 -0
  69. package/src/scripts/description_route_check.ts +494 -0
  70. package/src/scripts/hook_manifest.json +1 -1
  71. package/src/scripts/hook_manifest.yaml +29 -2
  72. package/src/scripts/hooks/concern_registry.ts +2 -0
  73. package/src/scripts/hooks/suggestion_capture_hook.ts +368 -0
  74. package/src/scripts/lint_budget_ownership.ts +54 -4
  75. package/src/scripts/lint_skill_trigger_corpus.ts +349 -0
  76. package/src/scripts/pack_install_smoke.ts +363 -0
  77. package/src/scripts/pr_conflict_census.ts +187 -0
  78. package/src/scripts/report_pack_delta.ts +204 -0
  79. package/src/scripts/select_analysis_mode.ts +112 -0
  80. package/src/scripts/sync_pr_branch.ts +40 -1
  81. package/src/server/schemas/settings.ts +5 -0
  82. package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +0 -15
  83. package/dist/agent-src/skills/agent-security-review/evals/triggers.json +0 -52
  84. package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +0 -52
  85. package/dist/agent-src/skills/alerting-doctrine/evals/triggers.json +0 -17
  86. package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +0 -13
  87. package/dist/agent-src/skills/brand/evals/triggers.json +0 -17
  88. package/dist/agent-src/skills/brand-asset-generation/evals/triggers.json +0 -17
  89. package/dist/agent-src/skills/brand-audit/evals/triggers.json +0 -17
  90. package/dist/agent-src/skills/brand-identity/evals/triggers.json +0 -17
  91. package/dist/agent-src/skills/brand-strategy/evals/triggers.json +0 -17
  92. package/dist/agent-src/skills/brand-to-tokens/evals/triggers.json +0 -17
  93. package/dist/agent-src/skills/bug-analyzer/evals/evals.json +0 -13
  94. package/dist/agent-src/skills/canvas-design/evals/triggers.json +0 -17
  95. package/dist/agent-src/skills/character-consistency/evals/evals.json +0 -16
  96. package/dist/agent-src/skills/code-intelligence/evals/triggers.json +0 -52
  97. package/dist/agent-src/skills/code-review/evals/evals.json +0 -100
  98. package/dist/agent-src/skills/command-routing/evals/evals.json +0 -14
  99. package/dist/agent-src/skills/complexity-first-planning/evals/triggers.json +0 -17
  100. package/dist/agent-src/skills/contract-review/evals/triggers.json +0 -17
  101. package/dist/agent-src/skills/conventional-commits-writing/evals/evals.json +0 -15
  102. package/dist/agent-src/skills/corpus-grounding/evals/triggers.json +0 -17
  103. package/dist/agent-src/skills/dashboard-design/evals/evals.json +0 -16
  104. package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +0 -29
  105. package/dist/agent-src/skills/dcf-modeling/evals/evals.json +0 -20
  106. package/dist/agent-src/skills/decision-record/evals/evals.json +0 -20
  107. package/dist/agent-src/skills/decision-record/evals/triggers.json +0 -87
  108. package/dist/agent-src/skills/design-intelligence/evals/evals.json +0 -14
  109. package/dist/agent-src/skills/design-intelligence/evals/triggers.json +0 -17
  110. package/dist/agent-src/skills/design-system-capture/evals/evals.json +0 -15
  111. package/dist/agent-src/skills/design-tokens/evals/triggers.json +0 -17
  112. package/dist/agent-src/skills/design-variations/evals/triggers.json +0 -17
  113. package/dist/agent-src/skills/developer-like-execution/evals/evals.json +0 -16
  114. package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +0 -16
  115. package/dist/agent-src/skills/doc-coauthoring/evals/triggers.json +0 -17
  116. package/dist/agent-src/skills/docx-authoring/evals/triggers.json +0 -52
  117. package/dist/agent-src/skills/dpa-review/evals/triggers.json +0 -17
  118. package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +0 -16
  119. package/dist/agent-src/skills/editorial-calendar/evals/evals.json +0 -16
  120. package/dist/agent-src/skills/eloquent/evals/triggers.json +0 -17
  121. package/dist/agent-src/skills/estimate-ticket/evals/evals.json +0 -16
  122. package/dist/agent-src/skills/estimate-ticket/evals/output-schema.yml +0 -20
  123. package/dist/agent-src/skills/estimate-ticket/evals/triggers.json +0 -19
  124. package/dist/agent-src/skills/evaluate-llm-feature/evals/triggers.json +0 -52
  125. package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +0 -47
  126. package/dist/agent-src/skills/experiment-loop/evals/output-schema.yml +0 -27
  127. package/dist/agent-src/skills/experiment-loop/evals/triggers.json +0 -17
  128. package/dist/agent-src/skills/fe-design/evals/triggers.json +0 -67
  129. package/dist/agent-src/skills/forecasting/evals/domain-truth.json +0 -29
  130. package/dist/agent-src/skills/forecasting/evals/evals.json +0 -20
  131. package/dist/agent-src/skills/forensics-report/evals/triggers.json +0 -17
  132. package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +0 -52
  133. package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +0 -16
  134. package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +0 -16
  135. package/dist/agent-src/skills/gated-reach/evals/triggers.json +0 -72
  136. package/dist/agent-src/skills/github-ci/evals/evals.json +0 -25
  137. package/dist/agent-src/skills/history-design/evals/triggers.json +0 -17
  138. package/dist/agent-src/skills/html-deck/evals/triggers.json +0 -17
  139. package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +0 -57
  140. package/dist/agent-src/skills/humanizer/evals/triggers.json +0 -17
  141. package/dist/agent-src/skills/iconography/evals/triggers.json +0 -17
  142. package/dist/agent-src/skills/image-analyser/evals/triggers.json +0 -17
  143. package/dist/agent-src/skills/image-creator/evals/triggers.json +0 -17
  144. package/dist/agent-src/skills/image-editing/evals/triggers.json +0 -17
  145. package/dist/agent-src/skills/image-generation/evals/triggers.json +0 -17
  146. package/dist/agent-src/skills/image-provider-routing/evals/triggers.json +0 -17
  147. package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +0 -16
  148. package/dist/agent-src/skills/incident-commander/evals/evals.json +0 -20
  149. package/dist/agent-src/skills/js-library-packaging/evals/triggers.json +0 -17
  150. package/dist/agent-src/skills/judge-artifact-completeness/evals/triggers.json +0 -17
  151. package/dist/agent-src/skills/judge-injection-defense/evals/triggers.json +0 -17
  152. package/dist/agent-src/skills/judge-synthesis/evals/triggers.json +0 -17
  153. package/dist/agent-src/skills/launch-readiness/evals/evals.json +0 -21
  154. package/dist/agent-src/skills/learning-tutor/evals/triggers.json +0 -52
  155. package/dist/agent-src/skills/legal-intake-triage/evals/triggers.json +0 -17
  156. package/dist/agent-src/skills/legal-practice-profile/evals/triggers.json +0 -17
  157. package/dist/agent-src/skills/license-compliance-audit/evals/triggers.json +0 -52
  158. package/dist/agent-src/skills/license-compliance-borrow-check/evals/triggers.json +0 -52
  159. package/dist/agent-src/skills/license-compliance-credits/evals/triggers.json +0 -52
  160. package/dist/agent-src/skills/llm-provider-knowledge/evals/triggers.json +0 -20
  161. package/dist/agent-src/skills/logging-monitoring/evals/evals.json +0 -16
  162. package/dist/agent-src/skills/logo-generation/evals/triggers.json +0 -17
  163. package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +0 -16
  164. package/dist/agent-src/skills/messaging-architecture/evals/evals.json +0 -16
  165. package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +0 -17
  166. package/dist/agent-src/skills/nda-triage/evals/triggers.json +0 -17
  167. package/dist/agent-src/skills/operational-readiness/evals/triggers.json +0 -17
  168. package/dist/agent-src/skills/pdf-tools/evals/triggers.json +0 -52
  169. package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +0 -16
  170. package/dist/agent-src/skills/persona-improvement/evals/triggers.json +0 -52
  171. package/dist/agent-src/skills/php-coder/evals/triggers.json +0 -17
  172. package/dist/agent-src/skills/prediction-pool-optimizer/evals/triggers.json +0 -21
  173. package/dist/agent-src/skills/prompt-engineering-image/evals/triggers.json +0 -17
  174. package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +0 -16
  175. package/dist/agent-src/skills/prompt-validator/evals/triggers.json +0 -17
  176. package/dist/agent-src/skills/quality-tools/evals/evals.json +0 -15
  177. package/dist/agent-src/skills/reasoning-orchestrator/evals/triggers.json +0 -17
  178. package/dist/agent-src/skills/refine-prompt/evals/evals.json +0 -16
  179. package/dist/agent-src/skills/refine-ticket/evals/evals.json +0 -16
  180. package/dist/agent-src/skills/refine-ticket/evals/output-schema.yml +0 -16
  181. package/dist/agent-src/skills/refine-ticket/evals/triggers.json +0 -17
  182. package/dist/agent-src/skills/release-comms/evals/evals.json +0 -16
  183. package/dist/agent-src/skills/rice-prioritization/evals/evals.json +0 -20
  184. package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +0 -29
  185. package/dist/agent-src/skills/runway-cognition/evals/evals.json +0 -20
  186. package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +0 -29
  187. package/dist/agent-src/skills/scenario-modeling/evals/evals.json +0 -20
  188. package/dist/agent-src/skills/schema-review/evals/triggers.json +0 -17
  189. package/dist/agent-src/skills/screenshot-hygiene/evals/triggers.json +0 -52
  190. package/dist/agent-src/skills/security-maturity-assessment/evals/triggers.json +0 -52
  191. package/dist/agent-src/skills/server-hardening/evals/triggers.json +0 -17
  192. package/dist/agent-src/skills/skill-writing/evals/triggers.json +0 -17
  193. package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +0 -47
  194. package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +0 -17
  195. package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +0 -60
  196. package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +0 -52
  197. package/dist/agent-src/skills/systematic-debugging/evals/evals.json +0 -20
  198. package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +0 -52
  199. package/dist/agent-src/skills/test-driven-development/evals/evals.json +0 -16
  200. package/dist/agent-src/skills/threat-modeling/evals/evals.json +0 -21
  201. package/dist/agent-src/skills/typography-system/evals/evals.json +0 -14
  202. package/dist/agent-src/skills/typography-system/evals/triggers.json +0 -17
  203. package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +0 -40
  204. package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +0 -24
  205. package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +0 -16
  206. package/dist/agent-src/skills/verify-repair-loop/evals/output-schema.yml +0 -20
  207. package/dist/agent-src/skills/verify-repair-loop/evals/triggers.json +0 -17
  208. package/dist/agent-src/skills/vision-articulation/evals/evals.json +0 -16
  209. package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +0 -16
  210. package/dist/agent-src/skills/wireframe/evals/triggers.json +0 -17
  211. package/dist/agent-src/skills/workspace-link/evals/triggers.json +0 -17
  212. package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +0 -38
  213. package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +0 -52
  214. package/dist/cli/agent-config.js.map +0 -1
  215. package/dist/cli/bash/runBash.js.map +0 -1
  216. package/dist/cli/commands/commands.js.map +0 -1
  217. package/dist/cli/commands/config.js.map +0 -1
  218. package/dist/cli/commands/doctorShell.js.map +0 -1
  219. package/dist/cli/commands/mcpServer.js.map +0 -1
  220. package/dist/cli/commands/packs.js.map +0 -1
  221. package/dist/cli/commands/profiles.js.map +0 -1
  222. package/dist/cli/commands/recordTriggerEval.js.map +0 -1
  223. package/dist/cli/commands/rtkDetect.js.map +0 -1
  224. package/dist/cli/commands/settings.js.map +0 -1
  225. package/dist/cli/commands/uiAudit.js.map +0 -1
  226. package/dist/cli/commands/uiRender.js.map +0 -1
  227. package/dist/cli/commands/uiServe.js.map +0 -1
  228. package/dist/cli/commands/versions.js.map +0 -1
  229. package/dist/cli/commands/workspaces.js.map +0 -1
  230. package/dist/cli/configRoot.js.map +0 -1
  231. package/dist/cli/discovery/loadManifest.js.map +0 -1
  232. package/dist/cli/firstRunNotice.js.map +0 -1
  233. package/dist/cli/help.js.map +0 -1
  234. package/dist/cli/initRouting.js.map +0 -1
  235. package/dist/cli/log/logger.js.map +0 -1
  236. package/dist/cli/main.js.map +0 -1
  237. package/dist/cli/mcp/content.js.map +0 -1
  238. package/dist/cli/mcp/dispatch.js.map +0 -1
  239. package/dist/cli/mcp/stdio.js.map +0 -1
  240. package/dist/cli/paths.js.map +0 -1
  241. package/dist/cli/python/knowledge_ingest.js.map +0 -1
  242. package/dist/cli/python/workspace_analytics.js.map +0 -1
  243. package/dist/cli/python/workspace_crypto.js.map +0 -1
  244. package/dist/cli/python/workspace_documents.js.map +0 -1
  245. package/dist/cli/python/workspace_drive.js.map +0 -1
  246. package/dist/cli/python/workspace_drive_health.js.map +0 -1
  247. package/dist/cli/python/workspace_explain.js.map +0 -1
  248. package/dist/cli/python/workspace_hosts.js.map +0 -1
  249. package/dist/cli/python/workspace_inbox.js.map +0 -1
  250. package/dist/cli/python/workspace_render.js.map +0 -1
  251. package/dist/cli/python/workspace_roles.js.map +0 -1
  252. package/dist/cli/python/workspace_secrets.js.map +0 -1
  253. package/dist/cli/python/workspace_sessions.js.map +0 -1
  254. package/dist/cli/python/workspace_skills.js.map +0 -1
  255. package/dist/cli/registry.js.map +0 -1
  256. package/dist/install/agentSwitchDetection.js.map +0 -1
  257. package/dist/install/agentSwitchProfile.js.map +0 -1
  258. package/dist/install/atomic.js.map +0 -1
  259. package/dist/install/claudePathsPlan.js.map +0 -1
  260. package/dist/install/conflict.js.map +0 -1
  261. package/dist/install/detect.js.map +0 -1
  262. package/dist/install/emit_host_rules_cli.js.map +0 -1
  263. package/dist/install/globalRuleLayers.js.map +0 -1
  264. package/dist/install/hostLayerFingerprint.js.map +0 -1
  265. package/dist/install/partitionEligibility.js.map +0 -1
  266. package/dist/install/paths.js.map +0 -1
  267. package/dist/install/plan.js.map +0 -1
  268. package/dist/install/preflight.js.map +0 -1
  269. package/dist/install/rtkDetection.js.map +0 -1
  270. package/dist/install/ruleInScope.js.map +0 -1
  271. package/dist/install/ruleLayerPartition.js.map +0 -1
  272. package/dist/install/rule_scope.js.map +0 -1
  273. package/dist/install/rule_scope_cli.js.map +0 -1
  274. package/dist/install/selectedTools.js.map +0 -1
  275. package/dist/install/toolDetection.js.map +0 -1
  276. package/dist/install/txlog.js.map +0 -1
  277. package/dist/install/types.js.map +0 -1
  278. package/dist/install/wizard-plan.js.map +0 -1
  279. package/dist/install/wizardDismissals.js.map +0 -1
  280. package/dist/scripts/_lib/agent_settings.js.map +0 -1
  281. package/dist/scripts/_lib/agent_src.js.map +0 -1
  282. package/dist/scripts/_lib/claude_builtin_names.js.map +0 -1
  283. package/dist/scripts/_lib/claude_plugin.js.map +0 -1
  284. package/dist/scripts/_lib/claude_settings_hooks.js.map +0 -1
  285. package/dist/scripts/_lib/compile_time_toggles.js.map +0 -1
  286. package/dist/scripts/_lib/environment_detector.js.map +0 -1
  287. package/dist/scripts/_lib/host_launch.js.map +0 -1
  288. package/dist/scripts/_lib/install_layout.js.map +0 -1
  289. package/dist/scripts/_lib/installed_lock.js.map +0 -1
  290. package/dist/scripts/_lib/lean_projection_mode.js.map +0 -1
  291. package/dist/scripts/_lib/model_tier.js.map +0 -1
  292. package/dist/scripts/_lib/prune_empty_dirs.js.map +0 -1
  293. package/dist/scripts/_lib/scoped_projection.js.map +0 -1
  294. package/dist/scripts/_lib/script_output.js.map +0 -1
  295. package/dist/scripts/_lib/spawn_env.js.map +0 -1
  296. package/dist/scripts/_lib/surface_tiers.js.map +0 -1
  297. package/dist/scripts/_lib/token_count.js.map +0 -1
  298. package/dist/scripts/_lib/trigger_eval_floors.js.map +0 -1
  299. package/dist/scripts/_lib/ui_surface.js.map +0 -1
  300. package/dist/scripts/_lib/user_global_paths.js.map +0 -1
  301. package/dist/scripts/ai_council/modes.js.map +0 -1
  302. package/dist/scripts/ai_council/transport_resolver.js.map +0 -1
  303. package/dist/scripts/condense.js.map +0 -1
  304. package/dist/scripts/project_thin_rules.js.map +0 -1
  305. package/dist/server/app.js.map +0 -1
  306. package/dist/server/io/atomicMultiWrite.js.map +0 -1
  307. package/dist/server/io/atomicWrite.js.map +0 -1
  308. package/dist/server/io/sharedWriteCheck.js.map +0 -1
  309. package/dist/server/io/substituteTemplate.js.map +0 -1
  310. package/dist/server/io/yamlIO.js.map +0 -1
  311. package/dist/server/nicknameResolver.js.map +0 -1
  312. package/dist/server/port.js.map +0 -1
  313. package/dist/server/routes/discovery.js.map +0 -1
  314. package/dist/server/routes/install.js.map +0 -1
  315. package/dist/server/routes/ping.js.map +0 -1
  316. package/dist/server/routes/schema.js.map +0 -1
  317. package/dist/server/routes/settings.js.map +0 -1
  318. package/dist/server/routes/settingsChanges.js.map +0 -1
  319. package/dist/server/routes/userMd.js.map +0 -1
  320. package/dist/server/routes/wizard.js.map +0 -1
  321. package/dist/server/routes/workspace.js.map +0 -1
  322. package/dist/server/schemas/settings.js.map +0 -1
  323. package/dist/server/serverInfo.js.map +0 -1
  324. package/dist/server/token.js.map +0 -1
  325. package/dist/server/writeRoot.js.map +0 -1
  326. package/dist/shared/capabilities.js.map +0 -1
  327. package/dist/shared/interactiveContext.js.map +0 -1
  328. package/dist/shared/nicknamePrefill.js.map +0 -1
  329. package/dist/shared/settingsAsks.js.map +0 -1
  330. package/dist/shared/settingsCarveOut.js.map +0 -1
  331. package/dist/shared/settingsClasses.js.map +0 -1
  332. package/dist/shared/settingsConsent.js.map +0 -1
  333. package/dist/shared/settingsSurface.js.map +0 -1
  334. package/dist/shared/skillRanking.js.map +0 -1
  335. package/dist/shared/userMd/formAdapter.js.map +0 -1
  336. package/dist/shared/userMd/schema.js.map +0 -1
  337. package/dist/shared/userMd/utils.js.map +0 -1
  338. package/dist/ui/assets/index-Cxgy63kz.js.map +0 -1
  339. package/src/scripts/_lib/adr_frontmatter.test.ts +0 -365
  340. package/src/scripts/_lib/adversarial_council_gate.test.ts +0 -112
  341. package/src/scripts/_lib/adversarial_reconcile.test.ts +0 -197
  342. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +0 -150
  343. package/src/scripts/_lib/secret_detector.test.ts +0 -108
  344. package/src/scripts/_lib/secret_detector_adversarial.test.ts +0 -116
  345. package/src/scripts/_lib/ui_authority.test.ts +0 -251
  346. package/src/scripts/_lib/untrusted_content.test.ts +0 -175
  347. package/src/scripts/check_secret_leak.test.ts +0 -90
  348. package/src/scripts/design_slop_rules.test.ts +0 -246
  349. package/src/scripts/self_review_gate.test.ts +0 -296
  350. package/src/shared/capabilities.test.ts +0 -43
@@ -0,0 +1,505 @@
1
+ #!/usr/bin/env tsx
2
+ /**
3
+ * Score-contract gate — an unreferenced prose "10" becomes impossible.
4
+ *
5
+ * `agents/evidence/ac-capability-scorecard.yaml` records, per rubric category,
6
+ * the current claim, the evidence classes that would support it, and a status.
7
+ * This gate makes the status a DERIVED field: a row whose status disagrees with
8
+ * its own evidence arrays is a finding, so nobody can hand-award a `ten` and no
9
+ * closed roadmap checkbox can buy one.
10
+ *
11
+ * Register class 4 of 4 — `agents/evidence/README.md` says what the other three
12
+ * are (`docs/CLAIMS.md`, `provenance/borrows.jsonl`,
13
+ * `provenance/harvests.jsonl`) and why this is none of them. It is modelled on
14
+ * `check_claims.ts`: same "a claim may not stand without a resolvable evidence
15
+ * reference" culture, applied to an external reviewer's rubric axes instead of
16
+ * to public marketing claims.
17
+ *
18
+ * WHAT IT DELIBERATELY DOES NOT DO — the whole point of the contract:
19
+ * It never judges outcome QUALITY. A threshold applied to how good an outcome
20
+ * was is a judgement behind a gate, which is score theatre. Quality belongs in
21
+ * a report on the green path, gating nothing. Emptiness and resolvability are
22
+ * mechanical; "is this evidence good enough" is not, and is not attempted.
23
+ *
24
+ * Checks:
25
+ * 1. Shape — the file parses, carries `rubric:` and `categories:`, and every
26
+ * row has the closed field set with a `status` from the six-value enum.
27
+ * 2. Manifest honesty — `recovered + missing == reported`; `state: complete`
28
+ * is REFUSED while `authority: unavailable-external-review`; the row count
29
+ * equals `recovered_category_count`; no duplicate category ids; no row id
30
+ * appearing in `excluded_from_manifest`.
31
+ * 3. Evidence resolvability — every non-empty URI resolves, at every status.
32
+ * 4. The class rule, per status (see STATUS_RULES). `ten` needs every
33
+ * required class non-empty and resolvable plus a production window;
34
+ * `max-boundary` needs a standing constraint and nothing else may carry
35
+ * one; `measured-null` needs a window and resolvable outcome evidence.
36
+ * 5. Stale pins — a `path@<sha>` URI whose sha is not an ancestor of HEAD.
37
+ *
38
+ * Evidence-URI grammar (deliberately narrower than check_claims'):
39
+ * <repo-path> → the path exists
40
+ * <repo-path>:<line> → the path exists (line advisory)
41
+ * <repo-path>#<substring> → the path exists AND contains <substring>
42
+ * <repo-path>@<sha> → the path exists AND <sha> is an ancestor of HEAD
43
+ * https://… (YYYY-MM-DD) → external cite with a dated stamp (never fetched)
44
+ * fixture:<repo-path> → explicitly a FIXTURE; may never satisfy the
45
+ * adoption, production, or outcome classes
46
+ *
47
+ * `fixture:` exists because the contract's own definition of 10 says synthetic
48
+ * fixtures can never satisfy adoption, production or outcome. Without a marker
49
+ * the gate could not tell a fixture path from a production one, and the rule
50
+ * would be prose only.
51
+ *
52
+ * Usage:
53
+ * ./scripts-run src/scripts/check_score_contract
54
+ * ./scripts-run src/scripts/check_score_contract --quiet
55
+ * ./scripts-run src/scripts/check_score_contract --file <path>
56
+ *
57
+ * Exit codes: 0 = clean · 2 = any finding OR usage error.
58
+ */
59
+ import * as fs from 'node:fs';
60
+ import * as path from 'node:path';
61
+ import { execFileSync } from 'node:child_process';
62
+ import { fileURLToPath, pathToFileURL } from 'node:url';
63
+ import * as os from 'node:os';
64
+ import { parse as parseYaml } from 'yaml';
65
+ import { runGateCli, runSelfTest, type SelfTestCase } from './_lib/gate_self_test.js';
66
+
67
+ const _FILE = fileURLToPath(import.meta.url);
68
+ const REPO = path.resolve(path.dirname(_FILE), '..', '..');
69
+ const DEFAULT_REL = path.join('agents', 'evidence', 'ac-capability-scorecard.yaml');
70
+ /** This script's repo-relative path, for the self-test's real CLI invocations. */
71
+ const SELF = path.join('src', 'scripts', 'check_score_contract.ts');
72
+ const TWIN_DIR = path.join('tests', 'fixtures', 'score-contract', 'twins');
73
+ const SELF_TEST_MIN_CASES = 8;
74
+ const SELF_TEST_MIN_REJECT = 6;
75
+
76
+ class ExitCode extends Error {
77
+ constructor(readonly code: number) {
78
+ super(`exit ${code}`);
79
+ }
80
+ }
81
+
82
+ /** The six legal statuses. `max-boundary` and `measured-null` are TERMINAL. */
83
+ export const STATUSES = [
84
+ 'missing-mechanism',
85
+ 'missing-adoption',
86
+ 'missing-proof',
87
+ 'measured-null',
88
+ 'max-boundary',
89
+ 'ten',
90
+ ] as const;
91
+ export type Status = (typeof STATUSES)[number];
92
+
93
+ /** Classes a `fixture:` URI may never satisfy — from the frozen definition of 10. */
94
+ const FIXTURE_FORBIDDEN = ['adoption_evidence', 'production_window', 'outcome_evidence'] as const;
95
+
96
+ const EVIDENCE_ARRAYS = [
97
+ 'mechanism_evidence',
98
+ 'adoption_evidence',
99
+ 'negative_control_evidence',
100
+ 'outcome_evidence',
101
+ 'non_regression_evidence',
102
+ ] as const;
103
+
104
+ const ROW_FIELDS = [
105
+ 'category',
106
+ 'baseline',
107
+ 'claim',
108
+ 'closing_path',
109
+ ...EVIDENCE_ARRAYS,
110
+ 'production_window',
111
+ 'status',
112
+ ] as const;
113
+
114
+ export interface Finding {
115
+ readonly where: string;
116
+ readonly code: string;
117
+ readonly detail: string;
118
+ }
119
+
120
+ /* ── evidence URIs ─────────────────────────────────────────────────────────── */
121
+
122
+ const _isAncestor = (sha: string): boolean => {
123
+ try {
124
+ execFileSync('git', ['merge-base', '--is-ancestor', sha, 'HEAD'], {
125
+ cwd: REPO,
126
+ stdio: 'ignore',
127
+ });
128
+ return true;
129
+ } catch {
130
+ return false;
131
+ }
132
+ };
133
+
134
+ /** Resolve one URI. Returns null when it resolves, else the reason it does not. */
135
+ export function resolveUri(uri: string): string | null {
136
+ const raw = uri.trim();
137
+ if (raw === '') return 'empty URI';
138
+
139
+ if (raw.startsWith('https://') || raw.startsWith('http://')) {
140
+ // Never fetched — an undated external cite cannot be re-verified later.
141
+ return /\(\d{4}-\d{2}-\d{2}\)\s*$/.test(raw) ? null : 'external cite carries no (YYYY-MM-DD) stamp';
142
+ }
143
+
144
+ const body = raw.startsWith('fixture:') ? raw.slice('fixture:'.length).trim() : raw;
145
+
146
+ let rel = body;
147
+ let needle: string | null = null;
148
+ let sha: string | null = null;
149
+
150
+ const hash = body.indexOf('#');
151
+ const at = body.lastIndexOf('@');
152
+ if (hash !== -1) {
153
+ rel = body.slice(0, hash);
154
+ needle = body.slice(hash + 1);
155
+ } else if (at > 0) {
156
+ rel = body.slice(0, at);
157
+ sha = body.slice(at + 1);
158
+ } else {
159
+ rel = body.replace(/:\d+$/, '');
160
+ }
161
+
162
+ if (rel === '' || path.isAbsolute(rel) || rel.split('/').includes('..')) {
163
+ return `path must be repo-relative and inside the repo: ${rel}`;
164
+ }
165
+ const abs = path.join(REPO, rel);
166
+ if (!fs.existsSync(abs)) return `path does not exist: ${rel}`;
167
+
168
+ if (needle !== null && needle !== '') {
169
+ let text = '';
170
+ try {
171
+ text = fs.readFileSync(abs, 'utf8');
172
+ } catch {
173
+ return `path is not readable as text: ${rel}`;
174
+ }
175
+ if (!text.includes(needle)) return `\`${rel}\` does not contain \`${needle}\``;
176
+ }
177
+ if (sha !== null) {
178
+ if (!/^[0-9a-f]{7,40}$/.test(sha)) return `not a sha: ${sha}`;
179
+ if (!_isAncestor(sha)) return `stale pin: ${sha} is not an ancestor of HEAD`;
180
+ }
181
+ return null;
182
+ }
183
+
184
+ const nonEmpty = (v: unknown): boolean =>
185
+ Array.isArray(v) ? v.length > 0 : typeof v === 'string' ? v.trim() !== '' : v !== null && v !== undefined;
186
+
187
+ /* ── the class rule ────────────────────────────────────────────────────────── */
188
+
189
+ type Row = Record<string, unknown>;
190
+
191
+ /**
192
+ * Per-status requirements. `require`/`forbid` name row fields that must be
193
+ * non-empty / empty. The precedence is deliberate: a status is a CLAIM about
194
+ * which class is the first one missing, so `missing-adoption` asserts mechanism
195
+ * evidence exists — otherwise the row would be `missing-mechanism` and two
196
+ * statuses would describe the same evidence shape.
197
+ */
198
+ export const STATUS_RULES: Record<Status, { require: string[]; forbid: string[] }> = {
199
+ 'missing-mechanism': { require: [], forbid: ['mechanism_evidence', 'standing_constraint'] },
200
+ 'missing-adoption': {
201
+ require: ['mechanism_evidence'],
202
+ forbid: ['adoption_evidence', 'standing_constraint'],
203
+ },
204
+ 'missing-proof': {
205
+ require: ['mechanism_evidence', 'adoption_evidence'],
206
+ forbid: ['standing_constraint'],
207
+ },
208
+ 'measured-null': {
209
+ require: ['production_window', 'outcome_evidence'],
210
+ forbid: ['standing_constraint'],
211
+ },
212
+ 'max-boundary': { require: ['standing_constraint'], forbid: [] },
213
+ ten: {
214
+ require: [
215
+ 'mechanism_evidence',
216
+ 'adoption_evidence',
217
+ 'negative_control_evidence',
218
+ 'production_window',
219
+ 'outcome_evidence',
220
+ 'non_regression_evidence',
221
+ ],
222
+ forbid: ['standing_constraint'],
223
+ },
224
+ };
225
+
226
+ export function checkRow(row: Row, where: string): Finding[] {
227
+ const out: Finding[] = [];
228
+ const push = (code: string, detail: string): void => void out.push({ where, code, detail });
229
+
230
+ for (const f of ROW_FIELDS) {
231
+ if (!(f in row)) push('missing_field', `row has no \`${f}\``);
232
+ }
233
+ const status = row['status'];
234
+ if (typeof status !== 'string' || !(STATUSES as readonly string[]).includes(status)) {
235
+ push('bad_status', `\`${String(status)}\` is not one of ${STATUSES.join(' | ')}`);
236
+ return out; // every later rule keys on the status
237
+ }
238
+
239
+ // 3. every non-empty URI resolves, at every status
240
+ for (const key of EVIDENCE_ARRAYS) {
241
+ const arr = row[key];
242
+ if (arr === undefined || arr === null) continue;
243
+ if (!Array.isArray(arr)) {
244
+ push('bad_shape', `\`${key}\` must be a list`);
245
+ continue;
246
+ }
247
+ for (const uri of arr) {
248
+ if (typeof uri !== 'string') {
249
+ push('bad_shape', `\`${key}\` holds a non-string entry`);
250
+ continue;
251
+ }
252
+ const why = resolveUri(uri);
253
+ if (why !== null) push('unresolvable_evidence', `${key}: ${why}`);
254
+ if (uri.trim().startsWith('fixture:') && (FIXTURE_FORBIDDEN as readonly string[]).includes(key)) {
255
+ push('fixture_in_production_class', `${key} may not be satisfied by a fixture: ${uri}`);
256
+ }
257
+ }
258
+ }
259
+ const window_ = row['production_window'];
260
+ if (typeof window_ === 'string' && window_.trim() !== '') {
261
+ const why = resolveUri(window_);
262
+ if (why !== null) push('unresolvable_evidence', `production_window: ${why}`);
263
+ if (window_.trim().startsWith('fixture:')) {
264
+ push('fixture_in_production_class', `production_window may not be a fixture: ${window_}`);
265
+ }
266
+ }
267
+
268
+ // 4. the class rule
269
+ const rule = STATUS_RULES[status as Status];
270
+ for (const f of rule.require) {
271
+ if (!nonEmpty(row[f])) push('class_rule', `status \`${status}\` requires a non-empty \`${f}\``);
272
+ }
273
+ for (const f of rule.forbid) {
274
+ if (nonEmpty(row[f])) push('class_rule', `status \`${status}\` forbids a non-empty \`${f}\``);
275
+ }
276
+ return out;
277
+ }
278
+
279
+ /* ── manifest honesty ──────────────────────────────────────────────────────── */
280
+
281
+ export function checkRubric(rubric: Row, rowCount: number, ids: string[]): Finding[] {
282
+ const out: Finding[] = [];
283
+ const where = 'rubric';
284
+ const push = (code: string, detail: string): void => void out.push({ where, code, detail });
285
+
286
+ const num = (k: string): number | null => (typeof rubric[k] === 'number' ? (rubric[k] as number) : null);
287
+ const reported = num('reported_category_count');
288
+ const recovered = num('recovered_category_count');
289
+ const missing = num('missing_category_count');
290
+ const state = rubric['state'];
291
+ const authority = rubric['authority'];
292
+
293
+ if (reported === null || recovered === null || missing === null) {
294
+ push('missing_field', 'rubric needs numeric reported/recovered/missing counts');
295
+ } else if (recovered + missing !== reported) {
296
+ push('bad_arithmetic', `${recovered} recovered + ${missing} missing != ${reported} reported`);
297
+ }
298
+ if (recovered !== null && rowCount !== recovered) {
299
+ push('row_count_mismatch', `${rowCount} rows present, rubric declares ${recovered} recovered`);
300
+ }
301
+ // The one that matters: incompleteness must not be silently redeclared away.
302
+ if (state === 'complete' && authority === 'unavailable-external-review') {
303
+ push(
304
+ 'false_completeness',
305
+ 'state `complete` is refused while authority is `unavailable-external-review` — ' +
306
+ 'an authoritative manifest must be present first',
307
+ );
308
+ }
309
+ if (state !== 'complete' && state !== 'incomplete') {
310
+ push('bad_state', `state must be \`complete\` or \`incomplete\`, got \`${String(state)}\``);
311
+ }
312
+ const excluded = Array.isArray(rubric['excluded_from_manifest']) ? (rubric['excluded_from_manifest'] as unknown[]) : [];
313
+ for (const id of ids) {
314
+ if (excluded.includes(id)) push('excluded_row_present', `\`${id}\` is both a row and excluded_from_manifest`);
315
+ }
316
+ const seen = new Set<string>();
317
+ for (const id of ids) {
318
+ if (seen.has(id)) push('duplicate_category', `\`${id}\` appears more than once`);
319
+ seen.add(id);
320
+ }
321
+ return out;
322
+ }
323
+
324
+ /* ── driver ────────────────────────────────────────────────────────────────── */
325
+
326
+ export function check(rel: string): Finding[] {
327
+ // An absolute `--file` must be honoured as given. path.join(REPO, '/abs')
328
+ // silently produces REPO + '/abs', which reports `missing_file` for a file
329
+ // that exists — the shape a test using a temp copy hits first.
330
+ const abs = path.isAbsolute(rel) ? rel : path.join(REPO, rel);
331
+ if (!fs.existsSync(abs)) {
332
+ return [{ where: rel, code: 'missing_file', detail: 'scorecard does not exist' }];
333
+ }
334
+ let doc: unknown;
335
+ try {
336
+ doc = parseYaml(fs.readFileSync(abs, 'utf8'));
337
+ } catch (exc) {
338
+ return [{ where: rel, code: 'unparseable', detail: String(exc) }];
339
+ }
340
+ if (doc === null || typeof doc !== 'object') {
341
+ return [{ where: rel, code: 'unparseable', detail: 'top level is not a mapping' }];
342
+ }
343
+ const top = doc as Row;
344
+ const rubric = top['rubric'];
345
+ const cats = top['categories'];
346
+ if (rubric === undefined || typeof rubric !== 'object' || rubric === null) {
347
+ return [{ where: rel, code: 'missing_field', detail: 'no `rubric:` block' }];
348
+ }
349
+ if (!Array.isArray(cats)) {
350
+ return [{ where: rel, code: 'missing_field', detail: '`categories:` must be a list' }];
351
+ }
352
+ const rows = cats as Row[];
353
+ const ids = rows.map((r) => String(r['category'] ?? '<unnamed>'));
354
+ const findings = [...checkRubric(rubric as Row, rows.length, ids)];
355
+ rows.forEach((r, i) => {
356
+ findings.push(...checkRow(r, `${rel} → ${ids[i] ?? `row ${i}`}`));
357
+ });
358
+ return findings;
359
+ }
360
+
361
+ /**
362
+ * Prove, through the real binary, that the rejections still fire.
363
+ *
364
+ * Unit tests here call `check()` in-process. That cannot catch the failure this
365
+ * repository has actually had: a CLI whose argv parsing or entry guard silently
366
+ * no-ops while the imported function still works. So every case below shells out
367
+ * to this file's own CLI.
368
+ *
369
+ * The committed twins ARE the fixtures — six of the eight cases point at them
370
+ * rather than re-authoring the same defects inline, so a twin that drifts breaks
371
+ * the self-test too instead of only the unit suite.
372
+ */
373
+ export function selfTest(): number {
374
+ const made: string[] = [];
375
+ const runFile = (rel: string): number => runGateCli(REPO, SELF, ['--quiet', '--file', rel], REPO);
376
+ const runText = (text: string): number => {
377
+ const d = fs.mkdtempSync(path.join(os.tmpdir(), 'score-selftest-'));
378
+ made.push(d);
379
+ const f = path.join(d, 'scorecard.yaml');
380
+ fs.writeFileSync(f, text, 'utf8');
381
+ return runFile(f);
382
+ };
383
+
384
+ const twin = (name: string): number => runFile(path.join(TWIN_DIR, `${name}.yaml`));
385
+
386
+ const cases: SelfTestCase[] = [
387
+ { name: 'accepts the real seeded scorecard', expect: 'accept', run: () => runFile(DEFAULT_REL) },
388
+ { name: 'rejects a `ten` with an empty required class', expect: 'reject', run: () => twin('a-ten-with-empty-class') },
389
+ { name: 'rejects a stale pin', expect: 'reject', run: () => twin('b-stale-pin') },
390
+ { name: 'rejects a fixture as the production window', expect: 'reject', run: () => twin('c-fixture-as-production') },
391
+ { name: 'rejects an unresolvable evidence path', expect: 'reject', run: () => twin('d-unresolvable-path') },
392
+ { name: 'rejects incompleteness redeclared as complete', expect: 'reject', run: () => twin('e-false-completeness') },
393
+ { name: 'rejects max-boundary naming no standing constraint', expect: 'reject', run: () => twin('f-max-boundary-no-constraint') },
394
+ {
395
+ // The dead-scan-root case: a moved scorecard must exit 2, never green.
396
+ // This is the shape gate-coverage.yml exists to catch, and the one a
397
+ // twin cannot express.
398
+ name: 'rejects a missing scorecard rather than reporting clean',
399
+ expect: 'reject',
400
+ run: () => runFile(path.join('agents', 'evidence', 'no-such-scorecard.yaml')),
401
+ },
402
+ {
403
+ name: 'accepts a minimal hand-built scorecard with one seeded row',
404
+ expect: 'accept',
405
+ run: () =>
406
+ runText(
407
+ 'schema_version: 1\nrubric:\n state: incomplete\n' +
408
+ ' authority: unavailable-external-review\n reported_category_count: 32\n' +
409
+ ' recovered_category_count: 1\n missing_category_count: 31\n' +
410
+ ' manifest_version: baseline_v1\n recovery_source: agents/roadmaps/road-to-ten-across-the-board.md\n' +
411
+ ' excluded_from_manifest: []\ncategories:\n - category: security\n' +
412
+ ' baseline: 9.6\n claim: "x"\n closing_path: "x"\n' +
413
+ ' mechanism_evidence: []\n adoption_evidence: []\n' +
414
+ ' negative_control_evidence: []\n production_window: null\n' +
415
+ ' outcome_evidence: []\n non_regression_evidence: []\n' +
416
+ ' status: missing-mechanism\n',
417
+ ),
418
+ },
419
+ ];
420
+ try {
421
+ return runSelfTest({
422
+ gate: 'check_score_contract',
423
+ cases,
424
+ minCases: SELF_TEST_MIN_CASES,
425
+ minRejectCases: SELF_TEST_MIN_REJECT,
426
+ });
427
+ } finally {
428
+ for (const d of made) fs.rmSync(d, { recursive: true, force: true });
429
+ }
430
+ }
431
+
432
+ /** Rows the gate actually inspected — 0 when the file is missing or unparseable. */
433
+ export function countRows(rel: string): number {
434
+ const abs = path.isAbsolute(rel) ? rel : path.join(REPO, rel);
435
+ try {
436
+ const doc = parseYaml(fs.readFileSync(abs, 'utf8')) as Row;
437
+ const cats = doc['categories'];
438
+ return Array.isArray(cats) ? cats.length : 0;
439
+ } catch {
440
+ return 0;
441
+ }
442
+ }
443
+
444
+ export function main(argv: readonly string[] = process.argv.slice(2)): number {
445
+ if (argv.includes('--self-test')) return selfTest();
446
+ const quiet = argv.includes('--quiet');
447
+ const fi = argv.indexOf('--file');
448
+ const rel = fi !== -1 ? argv[fi + 1] : DEFAULT_REL;
449
+ if (rel === undefined) {
450
+ process.stderr.write('usage: check_score_contract [--quiet] [--file <path>]\n');
451
+ return 2;
452
+ }
453
+ const findings = check(rel);
454
+ // Rule 1 of the gate-coverage contract: exactly one `scanned: <N>` line,
455
+ // emitted on the red path too — a gate that only reports coverage when it
456
+ // passes cannot be caught going blind at the moment it matters.
457
+ process.stdout.write(`scanned: ${countRows(rel)}\n`);
458
+ if (findings.length > 0) {
459
+ process.stderr.write(`\n❌ ${findings.length} score-contract finding(s):\n\n`);
460
+ for (const f of findings) {
461
+ process.stderr.write(` 🔴 ${f.where} — [${f.code}] ${f.detail}\n`);
462
+ }
463
+ return 2;
464
+ }
465
+ if (!quiet) {
466
+ // A gate that scans nothing and exits green is indistinguishable from a
467
+ // broken one, so the green path prints what it actually examined.
468
+ const shown = path.isAbsolute(rel) ? rel : path.join(REPO, rel);
469
+ const doc = parseYaml(fs.readFileSync(shown, 'utf8')) as Row;
470
+ const rubric = doc['rubric'] as Row;
471
+ const rows = doc['categories'] as Row[];
472
+ const byStatus = new Map<string, number>();
473
+ for (const r of rows) byStatus.set(String(r['status']), (byStatus.get(String(r['status'])) ?? 0) + 1);
474
+ const tally = [...byStatus.entries()].map(([s, n]) => `${s}=${n}`).join(' · ');
475
+ process.stdout.write(
476
+ `✅ ${rel} — ${rows.length} row(s) · ${tally}\n` +
477
+ ` rubric: ${String(rubric['state'])} — ${String(rubric['recovered_category_count'])} recovered, ` +
478
+ `${String(rubric['missing_category_count'])} of ${String(rubric['reported_category_count'])} unknown ` +
479
+ `(authority: ${String(rubric['authority'])})\n` +
480
+ ` quality is NOT judged here: emptiness and resolvability only.\n`,
481
+ );
482
+ }
483
+ return 0;
484
+ }
485
+
486
+ function _isCliEntry(): boolean {
487
+ const a = process.argv[1];
488
+ if (!a) return false;
489
+ if (a === _FILE || pathToFileURL(path.resolve(a)).href === import.meta.url) return true;
490
+ try {
491
+ return fs.realpathSync(a) === fs.realpathSync(_FILE);
492
+ } catch {
493
+ return false;
494
+ }
495
+ }
496
+ if (_isCliEntry()) {
497
+ try {
498
+ process.exit(main());
499
+ } catch (exc) {
500
+ if (exc instanceof ExitCode) process.exit(exc.code);
501
+ throw exc;
502
+ }
503
+ }
504
+
505
+ export { REPO, DEFAULT_REL, ExitCode };