@event4u/agent-config 9.7.0 → 9.9.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 (901) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +314 -190
  3. package/CONTRIBUTING.md +23 -7
  4. package/MIGRATION.md +20 -0
  5. package/README.md +57 -11
  6. package/dist/agent-src/commands/agents/audit.md +1 -1
  7. package/dist/agent-src/commands/agents/optimize.md +2 -2
  8. package/dist/agent-src/commands/agents/user.md +1 -1
  9. package/dist/agent-src/commands/agents.md +1 -1
  10. package/dist/agent-src/commands/check-current-md.md +3 -3
  11. package/dist/agent-src/commands/commit/in-chunks.md +8 -0
  12. package/dist/agent-src/commands/commit.md +17 -0
  13. package/dist/agent-src/commands/fix/portability.md +2 -2
  14. package/dist/agent-src/commands/fix/pr-comments-loop.md +136 -0
  15. package/dist/agent-src/commands/fix/refs.md +2 -2
  16. package/dist/agent-src/commands/fix/route.md +4 -3
  17. package/dist/agent-src/commands/fix.md +9 -6
  18. package/dist/agent-src/commands/ghostwriter.md +1 -1
  19. package/dist/agent-src/commands/image.md +1 -1
  20. package/dist/agent-src/commands/optimize/augmentignore.md +1 -1
  21. package/dist/agent-src/commands/optimize/skills.md +2 -2
  22. package/dist/agent-src/commands/post-as.md +1 -1
  23. package/dist/agent-src/commands/pr/create.md +4 -0
  24. package/dist/agent-src/commands/research.md +1 -1
  25. package/dist/agent-src/commands/roadmap/ai-council.md +1 -1
  26. package/dist/agent-src/commands/security-audit-config.md +1 -1
  27. package/dist/agent-src/commands/team/delegate.md +4 -0
  28. package/dist/agent-src/commands/video.md +1 -1
  29. package/dist/agent-src/contexts/authority/kernel-rule-edits.md +1 -1
  30. package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
  31. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +48 -2
  32. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +10 -0
  33. package/dist/agent-src/contexts/execution/subagent-modes-detail.md +109 -8
  34. package/dist/agent-src/contexts/execution/subagent-response-contract.md +20 -0
  35. package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +53 -3
  36. package/dist/agent-src/contexts/override-system.md +75 -5
  37. package/dist/agent-src/rules/code-provenance.md +112 -0
  38. package/dist/agent-src/rules/context-hygiene.md +2 -0
  39. package/dist/agent-src/rules/doc-screenshot-hygiene.md +97 -0
  40. package/dist/agent-src/rules/domain-safety-pii.md +1 -0
  41. package/dist/agent-src/rules/external-code-graph-interop.md +13 -3
  42. package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +2 -0
  43. package/dist/agent-src/rules/git-history-discipline.md +2 -0
  44. package/dist/agent-src/rules/history-discipline.md +80 -0
  45. package/dist/agent-src/rules/language-and-tone.md +2 -0
  46. package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
  47. package/dist/agent-src/rules/media-governance-routing.md +2 -0
  48. package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
  49. package/dist/agent-src/rules/no-roadmap-references.md +4 -1
  50. package/dist/agent-src/rules/non-destructive-by-default.md +20 -0
  51. package/dist/agent-src/rules/onboarding-gate.md +2 -0
  52. package/dist/agent-src/rules/output-discipline.md +2 -0
  53. package/dist/agent-src/rules/persona-governance.md +2 -0
  54. package/dist/agent-src/rules/php-coding.md +1 -0
  55. package/dist/agent-src/rules/preservation-guard.md +3 -0
  56. package/dist/agent-src/rules/reviewer-awareness.md +1 -0
  57. package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
  58. package/dist/agent-src/rules/roadmap-progress-sync.md +9 -0
  59. package/dist/agent-src/rules/scale-discipline.md +92 -0
  60. package/dist/agent-src/rules/secret-vcs-guard.md +106 -0
  61. package/dist/agent-src/rules/security-sensitive-stop.md +11 -1
  62. package/dist/agent-src/rules/session-canary.md +77 -0
  63. package/dist/agent-src/rules/skill-quality.md +2 -0
  64. package/dist/agent-src/rules/source-confidentiality.md +2 -0
  65. package/dist/agent-src/rules/source-of-truth.md +2 -0
  66. package/dist/agent-src/rules/telegraph-speak.md +2 -0
  67. package/dist/agent-src/rules/token-optimizer-maintenance.md +3 -1
  68. package/dist/agent-src/rules/tool-safety.md +4 -1
  69. package/dist/agent-src/rules/untrusted-input-defense.md +10 -0
  70. package/dist/agent-src/rules/verify-before-complete.md +2 -0
  71. package/dist/agent-src/skills/adr-create/SKILL.md +17 -1
  72. package/dist/agent-src/skills/agent-security-review/SKILL.md +1 -1
  73. package/dist/agent-src/skills/code-intelligence/SKILL.md +97 -0
  74. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  75. package/dist/agent-src/skills/corpus-grounding/SKILL.md +0 -3
  76. package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
  77. package/dist/agent-src/skills/design-review/SKILL.md +5 -6
  78. package/dist/agent-src/skills/design-tokens/SKILL.md +0 -1
  79. package/dist/agent-src/skills/gated-reach/SKILL.md +185 -0
  80. package/dist/agent-src/skills/gated-reach/evals/triggers.json +72 -0
  81. package/dist/agent-src/skills/history-design/SKILL.md +101 -0
  82. package/dist/agent-src/skills/history-design/evals/triggers.json +17 -0
  83. package/dist/agent-src/skills/humanizer/SKILL.md +3 -3
  84. package/dist/agent-src/skills/image-analyser/SKILL.md +14 -12
  85. package/dist/agent-src/skills/image-editing/SKILL.md +2 -1
  86. package/dist/agent-src/skills/image-generation/SKILL.md +2 -1
  87. package/dist/agent-src/skills/image-provider-routing/SKILL.md +2 -2
  88. package/dist/agent-src/skills/launch-readiness/SKILL.md +1 -1
  89. package/dist/agent-src/skills/license-compliance-audit/SKILL.md +161 -0
  90. package/dist/agent-src/skills/license-compliance-audit/evals/triggers.json +52 -0
  91. package/dist/agent-src/skills/license-compliance-borrow-check/SKILL.md +145 -0
  92. package/dist/agent-src/skills/license-compliance-borrow-check/evals/triggers.json +52 -0
  93. package/dist/agent-src/skills/license-compliance-credits/SKILL.md +97 -0
  94. package/dist/agent-src/skills/license-compliance-credits/evals/triggers.json +52 -0
  95. package/dist/agent-src/skills/md-language-check/SKILL.md +1 -1
  96. package/dist/agent-src/skills/override-management/SKILL.md +1 -1
  97. package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +7 -7
  98. package/dist/agent-src/skills/project-analysis-core/SKILL.md +1 -1
  99. package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -2
  100. package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +2 -2
  101. package/dist/agent-src/skills/readme-writing/SKILL.md +6 -1
  102. package/dist/agent-src/skills/recursive-verification/SKILL.md +6 -0
  103. package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -3
  104. package/dist/agent-src/skills/roadmap-writing/SKILL.md +4 -0
  105. package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +31 -12
  106. package/dist/agent-src/skills/rule-refactor/SKILL.md +3 -3
  107. package/dist/agent-src/skills/schema-review/SKILL.md +92 -0
  108. package/dist/agent-src/skills/schema-review/evals/triggers.json +17 -0
  109. package/dist/agent-src/skills/screenshot-hygiene/SKILL.md +171 -0
  110. package/dist/agent-src/skills/secrets-management/SKILL.md +43 -1
  111. package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
  112. package/dist/agent-src/skills/security-maturity-assessment/SKILL.md +1 -1
  113. package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -3
  114. package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
  115. package/dist/agent-src/skills/source-discovery/SKILL.md +5 -5
  116. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +45 -85
  117. package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +5 -4
  118. package/dist/agent-src/skills/subagent-orchestration/prompts/adversarial-verification-council.md +29 -0
  119. package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge-two-stage.md +29 -0
  120. package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge.md +29 -0
  121. package/dist/agent-src/skills/subagent-orchestration/prompts/do-competitively.md +29 -0
  122. package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-parallel.md +29 -0
  123. package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-steps.md +29 -0
  124. package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-worktrees.md +29 -0
  125. package/dist/agent-src/skills/subagent-orchestration/prompts/judge-with-debate.md +29 -0
  126. package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
  127. package/dist/agent-src/skills/tailwind-engineer/SKILL.md +2 -3
  128. package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
  129. package/dist/agent-src/skills/verify-repair-loop/SKILL.md +4 -0
  130. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +2 -3
  131. package/dist/agent-src/templates/AGENTS.md +1 -1
  132. package/dist/agent-src/templates/agents/.gitattributes.fragment +25 -6
  133. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  134. package/dist/agent-src/templates/command.md +3 -3
  135. package/dist/agent-src/templates/github-workflows/secret-scan.yml +65 -0
  136. package/dist/agent-src/templates/persona.md +2 -2
  137. package/dist/agent-src/templates/rule.md +2 -2
  138. package/dist/agent-src/templates/scripts/telemetry/engagement.ts +77 -0
  139. package/dist/agent-src/templates/scripts/telemetry_record.ts +33 -1
  140. package/dist/cli/commands/doctorShell.js +20 -0
  141. package/dist/cli/commands/doctorShell.js.map +1 -1
  142. package/dist/cli/commands/rtkDetect.js +58 -0
  143. package/dist/cli/commands/rtkDetect.js.map +1 -0
  144. package/dist/cli/configRoot.js +112 -0
  145. package/dist/cli/configRoot.js.map +1 -0
  146. package/dist/cli/configRoot.test.js +97 -0
  147. package/dist/cli/configRoot.test.js.map +1 -0
  148. package/dist/cli/firstRunNotice.js +70 -0
  149. package/dist/cli/firstRunNotice.js.map +1 -0
  150. package/dist/cli/help.js +4 -1
  151. package/dist/cli/help.js.map +1 -1
  152. package/dist/cli/main.js +54 -5
  153. package/dist/cli/main.js.map +1 -1
  154. package/dist/cli/registry.js +8 -1
  155. package/dist/cli/registry.js.map +1 -1
  156. package/dist/discovery/deprecation-report.md +1 -1
  157. package/dist/discovery/discovery-manifest.json +502 -101
  158. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  159. package/dist/discovery/discovery-manifest.summary.md +6 -4
  160. package/dist/discovery/orphan-report.md +1 -1
  161. package/dist/discovery/packs.json +95 -8
  162. package/dist/discovery/trust-report.md +11 -11
  163. package/dist/discovery/workspaces.json +36 -7
  164. package/dist/hooks/dispatch.js +19475 -0
  165. package/dist/install/agentSwitchDetection.js +75 -0
  166. package/dist/install/agentSwitchDetection.js.map +1 -0
  167. package/dist/install/agentSwitchProfile.js +117 -0
  168. package/dist/install/agentSwitchProfile.js.map +1 -0
  169. package/dist/install/atomic.js +1 -1
  170. package/dist/install/detect.js +1 -1
  171. package/dist/install/install.mjs +791 -486
  172. package/dist/install/rtkDetection.js +213 -0
  173. package/dist/install/rtkDetection.js.map +1 -0
  174. package/dist/install/rule_scope.js +2 -1
  175. package/dist/install/rule_scope.js.map +1 -1
  176. package/dist/install/types.js +2 -2
  177. package/dist/install/wizard-plan.js +1 -1
  178. package/dist/install/wizardDismissals.js +58 -0
  179. package/dist/install/wizardDismissals.js.map +1 -0
  180. package/dist/mcp/awesome-mcp-servers.row.md +1 -1
  181. package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
  182. package/dist/mcp/registry-manifest.json +15 -4
  183. package/dist/mcp/server.json +20 -0
  184. package/dist/mcp/server.mjs +44292 -0
  185. package/dist/router.json +1 -1
  186. package/dist/scripts/condense.js +14 -7
  187. package/dist/scripts/condense.js.map +1 -1
  188. package/dist/server/app.js +12 -0
  189. package/dist/server/app.js.map +1 -1
  190. package/dist/server/io/sharedWriteCheck.js +101 -0
  191. package/dist/server/io/sharedWriteCheck.js.map +1 -0
  192. package/dist/server/routes/ping.js +32 -0
  193. package/dist/server/routes/ping.js.map +1 -1
  194. package/dist/server/routes/settings.js +21 -4
  195. package/dist/server/routes/settings.js.map +1 -1
  196. package/dist/server/routes/userMd.js +21 -4
  197. package/dist/server/routes/userMd.js.map +1 -1
  198. package/dist/server/routes/wizard.js +68 -25
  199. package/dist/server/routes/wizard.js.map +1 -1
  200. package/dist/server/schemas/settings.js +12 -2
  201. package/dist/server/schemas/settings.js.map +1 -1
  202. package/dist/server/serverInfo.js +6 -2
  203. package/dist/server/serverInfo.js.map +1 -1
  204. package/dist/server/token.js +11 -4
  205. package/dist/server/token.js.map +1 -1
  206. package/dist/server/writeRoot.js +15 -1
  207. package/dist/server/writeRoot.js.map +1 -1
  208. package/dist/shared/capabilities.js +33 -0
  209. package/dist/shared/capabilities.js.map +1 -0
  210. package/dist/shared/capabilities.test.js +39 -0
  211. package/dist/shared/capabilities.test.js.map +1 -0
  212. package/dist/ui/assets/index-BpSQt5vm.js +40 -0
  213. package/dist/ui/assets/index-BpSQt5vm.js.map +1 -0
  214. package/dist/ui/assets/index-jSfMW8_t.css +1 -0
  215. package/dist/ui/index.html +15 -6
  216. package/docs/MIGRATION.md +16 -0
  217. package/docs/guidelines/component-oriented-and-oop-development.md +4 -2
  218. package/package.json +22 -9
  219. package/src/agent-src/templates/AGENTS.md +1 -1
  220. package/src/agent-src/templates/scripts/telemetry/engagement.ts +77 -0
  221. package/src/agent-src/templates/scripts/telemetry_record.ts +33 -1
  222. package/src/config/agent-settings.template.yml +94 -13
  223. package/src/config/discovery/packs.yml +22 -0
  224. package/src/config/discovery/workspaces.yml +1 -1
  225. package/src/config/evaluator-budgets.json +47 -0
  226. package/src/config/hook-latency-budget.json +20 -0
  227. package/src/config/reach-channels.yml +217 -0
  228. package/src/config/reach-prescriptions-intake.yml +200 -0
  229. package/src/install/agentSwitchDetection.ts +90 -0
  230. package/src/install/agentSwitchProfile.ts +131 -0
  231. package/src/install/atomic.ts +1 -1
  232. package/src/install/detect.ts +1 -1
  233. package/src/install/rtkDetection.ts +274 -0
  234. package/src/install/rule_scope.ts +9 -2
  235. package/src/install/types.ts +2 -2
  236. package/src/install/wizard-plan.ts +1 -1
  237. package/src/install/wizardDismissals.ts +60 -0
  238. package/src/scripts/_cli/cmd_doctor.ts +62 -1
  239. package/src/scripts/_cli/cmd_memory_get.ts +197 -0
  240. package/src/scripts/_cli/cmd_refresh.ts +30 -0
  241. package/src/scripts/_dispatch.bash +96 -0
  242. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +64 -1
  243. package/src/scripts/_lib/auto_dispatch.ts +76 -0
  244. package/src/scripts/_lib/detect_target_license.ts +714 -0
  245. package/src/scripts/_lib/exec_evidence.ts +251 -0
  246. package/src/scripts/_lib/fixtures/secret-detector/negatives.txt +15 -0
  247. package/src/scripts/_lib/fixtures/secret-detector/positives.txt +15 -0
  248. package/src/scripts/_lib/install_drift.ts +194 -0
  249. package/src/scripts/_lib/kernel_rules.ts +46 -0
  250. package/src/scripts/_lib/memory_fts_index.ts +390 -0
  251. package/src/scripts/_lib/orchestration_record.ts +91 -2
  252. package/src/scripts/_lib/persistence/adapter_raw_sql.ts +443 -0
  253. package/src/scripts/_lib/persistence/detect_audit_coverage.ts +423 -0
  254. package/src/scripts/_lib/persistence/detect_bounded_reads.ts +154 -0
  255. package/src/scripts/_lib/persistence/detect_index_parity.ts +576 -0
  256. package/src/scripts/_lib/persistence/detect_n1_eloquent.ts +226 -0
  257. package/src/scripts/_lib/persistence/detect_offload.ts +441 -0
  258. package/src/scripts/_lib/persistence/detect_prisma_growth.ts +74 -0
  259. package/src/scripts/_lib/persistence/offload_catalog.ts +73 -0
  260. package/src/scripts/_lib/persistence/types.ts +109 -0
  261. package/src/scripts/_lib/reddit_thread_parse.ts +598 -0
  262. package/src/scripts/_lib/risk_paths.ts +160 -0
  263. package/src/scripts/_lib/rtk_allowlist.ts +44 -0
  264. package/src/scripts/_lib/secret_detector.test.ts +108 -0
  265. package/src/scripts/_lib/secret_detector.ts +328 -0
  266. package/src/scripts/_lib/secret_detector_adversarial.test.ts +116 -0
  267. package/src/scripts/_lib/spawn_env.ts +7 -0
  268. package/src/scripts/_lib/sqlite_guard.ts +191 -0
  269. package/src/scripts/_lib/subagent_spawn.ts +53 -0
  270. package/src/scripts/_lib/tool_probe.ts +359 -0
  271. package/src/scripts/_lib/worker_budget.ts +107 -0
  272. package/src/scripts/adr/regenerate_index.ts +81 -6
  273. package/src/scripts/ai_council/blind_review.ts +0 -0
  274. package/src/scripts/ai_council/config.ts +12 -6
  275. package/src/scripts/ai_council/orchestrator.ts +73 -3
  276. package/src/scripts/ai_team/review_gate.ts +8 -10
  277. package/src/scripts/ai_team/team_dispatch.ts +48 -0
  278. package/src/scripts/audit_adr_coverage.ts +33 -0
  279. package/src/scripts/audit_initial_context.ts +1 -1
  280. package/src/scripts/bench_cross_source_eval.ts +400 -0
  281. package/src/scripts/bench_honesty_score.ts +1093 -0
  282. package/src/scripts/bench_hook_latency.ts +218 -0
  283. package/src/scripts/build_mcp_catalog.ts +275 -0
  284. package/src/scripts/build_mcp_registry_manifest.ts +46 -0
  285. package/src/scripts/build_proof.ts +207 -0
  286. package/src/scripts/chat_history.ts +8 -5
  287. package/src/scripts/check_adr_frontmatter.ts +195 -0
  288. package/src/scripts/check_artefact_count_messaging.ts +4 -0
  289. package/src/scripts/check_backstop_debt.ts +210 -0
  290. package/src/scripts/check_bundle_path_leakage.ts +343 -0
  291. package/src/scripts/check_claims.ts +188 -8
  292. package/src/scripts/check_comparison.ts +12 -0
  293. package/src/scripts/check_enforcement_coverage.ts +592 -0
  294. package/src/scripts/check_evaluator_budgets.ts +99 -0
  295. package/src/scripts/check_kernel_rule_bundle.ts +16 -12
  296. package/src/scripts/check_knowledge_sharing.ts +12 -1
  297. package/src/scripts/check_no_external_sources.ts +2 -2
  298. package/src/scripts/check_reach_channels.ts +407 -0
  299. package/src/scripts/check_reach_staleness.ts +364 -0
  300. package/src/scripts/check_secret_leak.test.ts +47 -0
  301. package/src/scripts/check_secret_leak.ts +290 -0
  302. package/src/scripts/check_source_pointer_freshness.ts +28 -3
  303. package/src/scripts/code_graph/build.ts +0 -0
  304. package/src/scripts/code_graph/cli.ts +379 -0
  305. package/src/scripts/code_graph/detect.ts +186 -0
  306. package/src/scripts/code_graph/extract.ts +386 -0
  307. package/src/scripts/code_graph/intent.ts +45 -0
  308. package/src/scripts/code_graph/loader.ts +123 -0
  309. package/src/scripts/code_graph/query.ts +290 -0
  310. package/src/scripts/code_graph/sanitize.ts +30 -0
  311. package/src/scripts/code_graph/sqlite_store.ts +174 -0
  312. package/src/scripts/code_graph/types.ts +83 -0
  313. package/src/scripts/code_graph/validate.ts +63 -0
  314. package/src/scripts/condense.ts +14 -6
  315. package/src/scripts/context_hygiene_hook.ts +8 -5
  316. package/src/scripts/council_cli.ts +126 -12
  317. package/src/scripts/detect_target_license.ts +203 -0
  318. package/src/scripts/evaluator_umbrella.sh +145 -0
  319. package/src/scripts/external_sources_denylist.json +8 -4
  320. package/src/scripts/first_run_gate_hook.ts +9 -5
  321. package/src/scripts/generate_subagent_floor.ts +194 -0
  322. package/src/scripts/hermetic-install.sh +3 -3
  323. package/src/scripts/hook_manifest.yaml +65 -16
  324. package/src/scripts/hooks/block_kernel_rule_writes.ts +212 -0
  325. package/src/scripts/hooks/block_no_verify.ts +9 -9
  326. package/src/scripts/hooks/code_graph_nudge_hook.ts +191 -0
  327. package/src/scripts/hooks/concern_registry.ts +75 -0
  328. package/src/scripts/hooks/design_slop_hook.ts +8 -5
  329. package/src/scripts/hooks/dispatch_entry.ts +37 -0
  330. package/src/scripts/hooks/dispatch_hook.ts +176 -3
  331. package/src/scripts/hooks/hook_stdin.ts +42 -0
  332. package/src/scripts/hooks/rtk_wrap_hook.ts +26 -12
  333. package/src/scripts/hot_context_hook.ts +7 -6
  334. package/src/scripts/injection_scan_hook.ts +9 -1
  335. package/src/scripts/install +6 -6
  336. package/src/scripts/install-hooks.sh +4 -4
  337. package/src/scripts/install.sh +5 -4
  338. package/src/scripts/install.ts +264 -23
  339. package/src/scripts/iron_law_sha.ts +3 -12
  340. package/src/scripts/learning_sidecar.ts +0 -0
  341. package/src/scripts/lint_budget_ownership.ts +89 -0
  342. package/src/scripts/lint_command_routing.ts +35 -5
  343. package/src/scripts/lint_command_verbs.ts +42 -9
  344. package/src/scripts/lint_consumer_internal_refs.ts +211 -0
  345. package/src/scripts/lint_discovery_vocabulary.ts +2 -0
  346. package/src/scripts/lint_documented_commands.ts +670 -0
  347. package/src/scripts/lint_framework_leakage.ts +17 -3
  348. package/src/scripts/lint_framework_leakage_allowlist.json +32 -0
  349. package/src/scripts/lint_governed_writes.ts +335 -0
  350. package/src/scripts/lint_hidden_unicode.ts +6 -0
  351. package/src/scripts/lint_mcp_registry_manifest.ts +26 -1
  352. package/src/scripts/lint_memory_tripwire.ts +186 -0
  353. package/src/scripts/lint_override_kernel_guard.ts +267 -0
  354. package/src/scripts/lint_persistence.ts +171 -0
  355. package/src/scripts/lint_pre_migration_refs.ts +106 -0
  356. package/src/scripts/lint_provenance.ts +547 -0
  357. package/src/scripts/lint_provenance_vocabulary.ts +437 -0
  358. package/src/scripts/lint_spawn_payload.ts +283 -0
  359. package/src/scripts/lint_store_boundary.ts +152 -0
  360. package/src/scripts/lint_versioned_cache.ts +7 -1
  361. package/src/scripts/mcp_server/__main__.ts +7 -2
  362. package/src/scripts/mcp_server/catalog.ts +23 -1
  363. package/src/scripts/mcp_server/consumer_tool_catalog.json +563 -193
  364. package/src/scripts/mcp_server/prompts.ts +6 -0
  365. package/src/scripts/mcp_server/tool_catalog_source.ts +332 -0
  366. package/src/scripts/mcp_server/tools.ts +56 -5
  367. package/src/scripts/mcp_telemetry_query.ts +2 -33
  368. package/src/scripts/mcp_telemetry_store.ts +64 -34
  369. package/src/scripts/measure_rule_budget.ts +3 -12
  370. package/src/scripts/memory_learn_hook.ts +126 -0
  371. package/src/scripts/memory_lookup.ts +0 -0
  372. package/src/scripts/memory_replay_24.ts +312 -0
  373. package/src/scripts/memory_report.ts +16 -3
  374. package/src/scripts/memory_signal.ts +48 -0
  375. package/src/scripts/memory_status.ts +6 -0
  376. package/src/scripts/minimal_safe_diff_hook.ts +8 -5
  377. package/src/scripts/onboarding_gate_hook.ts +8 -5
  378. package/src/scripts/orchestration_record.ts +16 -0
  379. package/src/scripts/pr_url_reminder_hook.ts +8 -1
  380. package/src/scripts/prepack-check.mjs +17 -0
  381. package/src/scripts/prepack_lifecycle_check.d.mts +8 -0
  382. package/src/scripts/prepack_lifecycle_check.mjs +82 -0
  383. package/src/scripts/profile_staleness_hook.ts +8 -7
  384. package/src/scripts/reach_doctor.ts +1651 -0
  385. package/src/scripts/release.ts +24 -3
  386. package/src/scripts/report_install_drift.ts +109 -0
  387. package/src/scripts/risk_escalation_shadow.ts +160 -0
  388. package/src/scripts/roadmap_progress_hook.ts +8 -5
  389. package/src/scripts/schemas/agent-settings.schema.json +23 -0
  390. package/src/scripts/schemas/license-policy.schema.json +89 -0
  391. package/src/scripts/schemas/provenance-borrow.schema.json +46 -0
  392. package/src/scripts/schemas/reach-channels.schema.json +134 -0
  393. package/src/scripts/schemas/reach-doctor-payload.schema.json +220 -0
  394. package/src/scripts/schemas/rule.schema.json +20 -0
  395. package/src/scripts/self_review_gate.test.ts +173 -1
  396. package/src/scripts/self_review_gate.ts +155 -15
  397. package/src/scripts/session_canary_hook.ts +182 -0
  398. package/src/scripts/skill_linter.ts +55 -0
  399. package/src/scripts/smoke_quickstart.ts +2 -2
  400. package/src/scripts/surface_probe_hook.ts +8 -7
  401. package/src/scripts/sync_gitattributes.ts +310 -0
  402. package/src/scripts/team_review_gate_hook.ts +13 -16
  403. package/src/scripts/validate_reach_prescriptions.ts +751 -0
  404. package/src/scripts/verify_before_complete_hook.ts +8 -5
  405. package/src/scripts/wrapper_freshness_hook.ts +8 -7
  406. package/src/server/schemas/settings.ts +24 -2
  407. package/src/shared/capabilities.test.ts +43 -0
  408. package/src/shared/capabilities.ts +75 -0
  409. package/src/templates/minimal/agents-overrides-readme.md +20 -0
  410. package/dist/ui/assets/index-BPFMZWUs.css +0 -1
  411. package/dist/ui/assets/index-BiEZiZ31.js +0 -40
  412. package/dist/ui/assets/index-BiEZiZ31.js.map +0 -1
  413. package/docs/CLAIMS.md +0 -261
  414. package/docs/DISTRIBUTION_CHECKLIST.md +0 -170
  415. package/docs/RELEASE_STORY_TEMPLATE.md +0 -37
  416. package/docs/SKILL_CENSUS.md +0 -352
  417. package/docs/adrs/cost/0001-hard-stop-hook.md +0 -114
  418. package/docs/adrs/cost/README.md +0 -9
  419. package/docs/adrs/memory/0001-consumer-side-snapshot.md +0 -119
  420. package/docs/adrs/memory/README.md +0 -10
  421. package/docs/adrs/router/0001-three-tier-routing.md +0 -119
  422. package/docs/adrs/router/README.md +0 -9
  423. package/docs/adrs/schema/0001-json-schema-frontmatter.md +0 -102
  424. package/docs/adrs/schema/README.md +0 -9
  425. package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +0 -99
  426. package/docs/adrs/smoke/README.md +0 -9
  427. package/docs/adrs/telegraph/0001-default-off-until-bench.md +0 -93
  428. package/docs/adrs/telegraph/README.md +0 -9
  429. package/docs/announcements/2026-05-non-dev-launch.md +0 -79
  430. package/docs/announcements/2026-07-honest-cost-sweep-launch.md +0 -84
  431. package/docs/anti-reskin-gate.md +0 -60
  432. package/docs/architecture/augment-projection.md +0 -70
  433. package/docs/architecture/claude-bundle.md +0 -77
  434. package/docs/architecture/current-onboard-baseline.md +0 -126
  435. package/docs/architecture/current-safety-behavior.md +0 -137
  436. package/docs/architecture/multi-tool-projection.md +0 -125
  437. package/docs/architecture/setup-vs-settings-shared-surface.md +0 -114
  438. package/docs/architecture/source-projection.md +0 -85
  439. package/docs/architecture.md +0 -390
  440. package/docs/archive/CHANGELOG-6.0.0-overview.md +0 -953
  441. package/docs/archive/CHANGELOG-7.0.0.md +0 -419
  442. package/docs/archive/CHANGELOG-pre-2.11.0.md +0 -141
  443. package/docs/archive/CHANGELOG-pre-2.15.0.md +0 -244
  444. package/docs/archive/CHANGELOG-pre-2.16.0.md +0 -48
  445. package/docs/archive/CHANGELOG-pre-2.17.0.md +0 -63
  446. package/docs/archive/CHANGELOG-pre-2.2.0.md +0 -2138
  447. package/docs/archive/CHANGELOG-pre-2.20.0.md +0 -159
  448. package/docs/archive/CHANGELOG-pre-2.25.0.md +0 -191
  449. package/docs/archive/CHANGELOG-pre-2.7.0.md +0 -185
  450. package/docs/archive/CHANGELOG-pre-3.0.0.md +0 -130
  451. package/docs/archive/CHANGELOG-pre-3.1.0.md +0 -167
  452. package/docs/archive/CHANGELOG-pre-3.2.0.md +0 -268
  453. package/docs/archive/CHANGELOG-pre-4.0.0.md +0 -80
  454. package/docs/archive/CHANGELOG-pre-4.5.0.md +0 -289
  455. package/docs/archive/CHANGELOG-pre-5.4.0.md +0 -260
  456. package/docs/archive/CHANGELOG-pre-5.9.0.md +0 -270
  457. package/docs/archive/CHANGELOG-pre-6.0.0.md +0 -473
  458. package/docs/archive/CHANGELOG-pre-7.0.0.md +0 -126
  459. package/docs/archive/CHANGELOG-pre-8.0.0.md +0 -301
  460. package/docs/archive/CHANGELOG-pre-8.1.0.md +0 -316
  461. package/docs/archive/CHANGELOG-pre-8.12.0.md +0 -304
  462. package/docs/archive/CHANGELOG-pre-8.9.0.md +0 -306
  463. package/docs/archive/CHANGELOG-pre-9.2.0.md +0 -276
  464. package/docs/artefact-census.md +0 -71
  465. package/docs/benchmark.md +0 -648
  466. package/docs/benchmark.pinned.yml +0 -31
  467. package/docs/benchmarks.md +0 -85
  468. package/docs/capability-matrix.md +0 -33
  469. package/docs/case-studies/_template.md +0 -60
  470. package/docs/case-studies/frontend-design-positioning.md +0 -86
  471. package/docs/catalog.md +0 -721
  472. package/docs/command-flows.md +0 -272
  473. package/docs/command-naming-audit.md +0 -60
  474. package/docs/comparison.yaml +0 -45
  475. package/docs/contracts/CHANGELOG-conventions.md +0 -149
  476. package/docs/contracts/STABILITY.md +0 -188
  477. package/docs/contracts/adoption-signal-floor.md +0 -127
  478. package/docs/contracts/adr-architectural-consensus-mechanism.md +0 -68
  479. package/docs/contracts/adr-chat-history-split.md +0 -140
  480. package/docs/contracts/adr-command-suggestion.md +0 -144
  481. package/docs/contracts/adr-forecast-construction-shape.md +0 -90
  482. package/docs/contracts/adr-gtm-context-spine.md +0 -116
  483. package/docs/contracts/adr-implement-ticket-runtime.md +0 -121
  484. package/docs/contracts/adr-install-user-type-axis.md +0 -107
  485. package/docs/contracts/adr-layout.md +0 -107
  486. package/docs/contracts/adr-level-6-productization.md +0 -145
  487. package/docs/contracts/adr-mcp-runtime.md +0 -128
  488. package/docs/contracts/adr-product-ui-track.md +0 -381
  489. package/docs/contracts/adr-prompt-driven-execution.md +0 -186
  490. package/docs/contracts/adr-settings-sync-engine.md +0 -128
  491. package/docs/contracts/adr-user-types-axis.md +0 -127
  492. package/docs/contracts/adr-wing4-context-spine.md +0 -126
  493. package/docs/contracts/agent-user-schema.md +0 -170
  494. package/docs/contracts/agents-layout.md +0 -148
  495. package/docs/contracts/agents-md-tech-stack.md +0 -75
  496. package/docs/contracts/ai-council-config.md +0 -829
  497. package/docs/contracts/ai-team-config.md +0 -224
  498. package/docs/contracts/analysis-memory-loop.md +0 -149
  499. package/docs/contracts/at-rest-encryption.md +0 -191
  500. package/docs/contracts/audit-log-v1.md +0 -150
  501. package/docs/contracts/auto-orchestration-v1.md +0 -70
  502. package/docs/contracts/benchmark-ab-contract.md +0 -106
  503. package/docs/contracts/benchmark-corpus-spec.md +0 -125
  504. package/docs/contracts/benchmark-report-schema.md +0 -113
  505. package/docs/contracts/branch-protection-policy.md +0 -139
  506. package/docs/contracts/brand-token-consumption.md +0 -74
  507. package/docs/contracts/capability-boundary.md +0 -69
  508. package/docs/contracts/capability-packs.md +0 -217
  509. package/docs/contracts/ci-cost-budget.md +0 -111
  510. package/docs/contracts/ci-green-floor.md +0 -141
  511. package/docs/contracts/command-category-governance.md +0 -84
  512. package/docs/contracts/command-clusters.md +0 -399
  513. package/docs/contracts/command-surface-tiers.md +0 -176
  514. package/docs/contracts/command-taxonomy.md +0 -137
  515. package/docs/contracts/condensation-default-kill-criterion.md +0 -116
  516. package/docs/contracts/config-presets.md +0 -144
  517. package/docs/contracts/conformance.md +0 -84
  518. package/docs/contracts/consumer-bridge.md +0 -73
  519. package/docs/contracts/context-paths.md +0 -101
  520. package/docs/contracts/context-spine.md +0 -179
  521. package/docs/contracts/cost-dashboard.md +0 -143
  522. package/docs/contracts/cost-enforcement.md +0 -134
  523. package/docs/contracts/cost-profile-defaults.md +0 -110
  524. package/docs/contracts/cost-summary-schema.md +0 -107
  525. package/docs/contracts/creative-pack-boundary.md +0 -63
  526. package/docs/contracts/cross-repo-retrieval.md +0 -64
  527. package/docs/contracts/cross-wing-handoff.md +0 -134
  528. package/docs/contracts/daily-workspace.md +0 -147
  529. package/docs/contracts/decision-engine-gates.md +0 -120
  530. package/docs/contracts/decision-trace-v1.md +0 -177
  531. package/docs/contracts/design-artifact-lifecycle.md +0 -93
  532. package/docs/contracts/design-artifact-verification.md +0 -153
  533. package/docs/contracts/discovery-manifest.md +0 -209
  534. package/docs/contracts/discovery-manifest.schema.json +0 -254
  535. package/docs/contracts/evidence-based-pruning.md +0 -102
  536. package/docs/contracts/explain-modes.md +0 -187
  537. package/docs/contracts/explain-trace.schema.json +0 -144
  538. package/docs/contracts/file-ownership-matrix.json +0 -13941
  539. package/docs/contracts/file-ownership-matrix.md +0 -135
  540. package/docs/contracts/frontmatter-contract.md +0 -140
  541. package/docs/contracts/ghostwriter-schema.md +0 -337
  542. package/docs/contracts/governance-enforcement-projection.md +0 -159
  543. package/docs/contracts/gui-wizard.md +0 -350
  544. package/docs/contracts/harness-expectations.md +0 -128
  545. package/docs/contracts/hook-architecture-v1.md +0 -325
  546. package/docs/contracts/host-agent-protocol.md +0 -110
  547. package/docs/contracts/implement-ticket-flow.md +0 -625
  548. package/docs/contracts/init-telemetry.md +0 -132
  549. package/docs/contracts/injected-block-authenticity-model.md +0 -66
  550. package/docs/contracts/install-layout.md +0 -249
  551. package/docs/contracts/install-scopes.md +0 -82
  552. package/docs/contracts/installed-tools-lockfile.md +0 -144
  553. package/docs/contracts/installer-agent-mode.md +0 -137
  554. package/docs/contracts/iron-law-overrides.txt +0 -26
  555. package/docs/contracts/kernel-membership.md +0 -339
  556. package/docs/contracts/linear-ai-rules-inclusion.md +0 -143
  557. package/docs/contracts/linear-ai-three-layers.md +0 -130
  558. package/docs/contracts/linter-structural-model.md +0 -181
  559. package/docs/contracts/load-context-budget-model.md +0 -259
  560. package/docs/contracts/load-context-schema.md +0 -220
  561. package/docs/contracts/local-analytics.md +0 -160
  562. package/docs/contracts/local-knowledge-ingestion.md +0 -96
  563. package/docs/contracts/local-server-api.md +0 -134
  564. package/docs/contracts/low-impact-corpus-format.md +0 -95
  565. package/docs/contracts/mcp-beta-criteria.md +0 -130
  566. package/docs/contracts/mcp-cloud-scope.md +0 -355
  567. package/docs/contracts/mcp-discovery-phase-notice.md +0 -60
  568. package/docs/contracts/mcp-phase-1-scope.md +0 -272
  569. package/docs/contracts/mcp-registry-manifest.schema.json +0 -129
  570. package/docs/contracts/mcp-tool-inventory.md +0 -64
  571. package/docs/contracts/mcp-tool-stub-envelope.md +0 -78
  572. package/docs/contracts/measurement-baseline.md +0 -103
  573. package/docs/contracts/memory-visibility-v1.md +0 -160
  574. package/docs/contracts/mental-models.md +0 -336
  575. package/docs/contracts/migrate-command.md +0 -233
  576. package/docs/contracts/multi-tool-projection-fidelity.md +0 -134
  577. package/docs/contracts/namespace.md +0 -125
  578. package/docs/contracts/no-runtime-boundary.md +0 -61
  579. package/docs/contracts/one-off-script-lifecycle.md +0 -110
  580. package/docs/contracts/orchestration-dsl-v1.md +0 -153
  581. package/docs/contracts/package-self-orientation.md +0 -163
  582. package/docs/contracts/pilot/agent-authority.md +0 -24
  583. package/docs/contracts/pilot/direct-answers.md +0 -70
  584. package/docs/contracts/pilot/language-and-tone.md +0 -63
  585. package/docs/contracts/plain-language-surface.md +0 -117
  586. package/docs/contracts/prelaunch-diagnostics.md +0 -151
  587. package/docs/contracts/profile-system.md +0 -167
  588. package/docs/contracts/reasoning-discipline-protocol.md +0 -83
  589. package/docs/contracts/release-pr-gating.md +0 -151
  590. package/docs/contracts/release-sizing.md +0 -107
  591. package/docs/contracts/release-trunk-sync.md +0 -105
  592. package/docs/contracts/review-lens-schema.md +0 -64
  593. package/docs/contracts/roadmap-complexity-standard.md +0 -138
  594. package/docs/contracts/role-experience.md +0 -125
  595. package/docs/contracts/router-blending.md +0 -71
  596. package/docs/contracts/rule-classification.md +0 -171
  597. package/docs/contracts/rule-interactions.md +0 -132
  598. package/docs/contracts/rule-interactions.yml +0 -260
  599. package/docs/contracts/rule-priority-hierarchy.md +0 -88
  600. package/docs/contracts/rule-router.md +0 -287
  601. package/docs/contracts/safety-model.md +0 -129
  602. package/docs/contracts/session-profile-overlay.md +0 -157
  603. package/docs/contracts/settings-api.md +0 -264
  604. package/docs/contracts/settings-gui-agent-mode.schema.json +0 -128
  605. package/docs/contracts/settings-sync-yaml-subset.md +0 -139
  606. package/docs/contracts/skill-bundled-assets.md +0 -62
  607. package/docs/contracts/skill-discovery.md +0 -80
  608. package/docs/contracts/skill-distribution-channels.md +0 -66
  609. package/docs/contracts/skill-domains.md +0 -144
  610. package/docs/contracts/skill-dry-run.md +0 -47
  611. package/docs/contracts/skill-family-map.yml +0 -915
  612. package/docs/contracts/smoke-contracts.md +0 -144
  613. package/docs/contracts/subagent-boundary.md +0 -59
  614. package/docs/contracts/surface-agent-contracts.md +0 -153
  615. package/docs/contracts/surface-matrix.md +0 -108
  616. package/docs/contracts/surface-tiers.md +0 -81
  617. package/docs/contracts/telegraph-telemetry.md +0 -83
  618. package/docs/contracts/ticket-bundle-format.md +0 -233
  619. package/docs/contracts/tier-3-contrib-plugin.md +0 -137
  620. package/docs/contracts/trust-and-safety.md +0 -144
  621. package/docs/contracts/ui-stack-extension.md +0 -237
  622. package/docs/contracts/ui-track-flow.md +0 -329
  623. package/docs/contracts/universal-skills.md +0 -91
  624. package/docs/contracts/user-type-schema.md +0 -146
  625. package/docs/contracts/value-dashboard-spec.md +0 -378
  626. package/docs/contracts/value-report-schema.md +0 -168
  627. package/docs/contracts/workflow-packs.md +0 -121
  628. package/docs/contracts/workspace-boundary.md +0 -84
  629. package/docs/contracts/workspace-documents.md +0 -144
  630. package/docs/contracts/write-engine.md +0 -182
  631. package/docs/cookbook.md +0 -152
  632. package/docs/customization.md +0 -685
  633. package/docs/decisions/ADR-001-kernel-swap-deferred.md +0 -109
  634. package/docs/decisions/ADR-002-kernel-bucket-overrides.md +0 -135
  635. package/docs/decisions/ADR-003-flat-cluster-subs-and-colon-syntax.md +0 -137
  636. package/docs/decisions/ADR-004-rule-governance-pruning.md +0 -240
  637. package/docs/decisions/ADR-005-subagent-worktrees.md +0 -120
  638. package/docs/decisions/ADR-006-skill-tools-python-pilot.md +0 -114
  639. package/docs/decisions/ADR-007-agent-discovery-scopes.md +0 -371
  640. package/docs/decisions/ADR-008-installed-tools-manifest.md +0 -160
  641. package/docs/decisions/ADR-009-event4u-namespace.md +0 -188
  642. package/docs/decisions/ADR-010-profile-pack-preset-boundary.md +0 -168
  643. package/docs/decisions/ADR-011-domain-pack-readiness.md +0 -213
  644. package/docs/decisions/ADR-012-typescript-cli-shell.md +0 -162
  645. package/docs/decisions/ADR-013-discovery-frontmatter-contract.md +0 -342
  646. package/docs/decisions/ADR-014-gui-framework-choice.md +0 -136
  647. package/docs/decisions/ADR-015-discovery-manifest-contract.md +0 -146
  648. package/docs/decisions/ADR-016-installer-architecture.md +0 -189
  649. package/docs/decisions/ADR-017-monorepo-physical-layout.md +0 -261
  650. package/docs/decisions/ADR-018-trust-and-safety-layer.md +0 -159
  651. package/docs/decisions/ADR-019-router-json-dist-location.md +0 -124
  652. package/docs/decisions/ADR-020-global-only-consumer-scope.md +0 -213
  653. package/docs/decisions/ADR-021-deployment-shape.md +0 -153
  654. package/docs/decisions/ADR-022-daily-workspace-decomposition.md +0 -140
  655. package/docs/decisions/ADR-023-host-agent-protocol.md +0 -129
  656. package/docs/decisions/ADR-024-workspace-v0-feature-floor.md +0 -126
  657. package/docs/decisions/ADR-025-workspace-chrome.md +0 -119
  658. package/docs/decisions/ADR-026-explain-mode-translation.md +0 -117
  659. package/docs/decisions/ADR-027-changelog-machine-vs-manual.md +0 -129
  660. package/docs/decisions/ADR-028-root-layout.md +0 -147
  661. package/docs/decisions/ADR-029-multi-workspace-deferred.md +0 -122
  662. package/docs/decisions/ADR-030-claude-code-command-projection.md +0 -147
  663. package/docs/decisions/ADR-031-validation-severity-tiers-and-projection-roundtrip.md +0 -97
  664. package/docs/decisions/ADR-032-linked-projects-scope.md +0 -122
  665. package/docs/decisions/ADR-033-distribution-identity-npm-primary.md +0 -81
  666. package/docs/decisions/ADR-034-per-skill-model-recommendation-transport.md +0 -63
  667. package/docs/decisions/ADR-035-model-capability-tiers.md +0 -104
  668. package/docs/decisions/ADR-036-global-install-browser-wizard-handoff.md +0 -106
  669. package/docs/decisions/ADR-037-cost-profile-untangle.md +0 -117
  670. package/docs/decisions/ADR-038-canonical-settings-path.md +0 -96
  671. package/docs/decisions/ADR-039-claude-skills-untracked.md +0 -139
  672. package/docs/decisions/ADR-040-execution-model-projection-time-filtering.md +0 -244
  673. package/docs/decisions/ADR-041-controlled-command-verbs.md +0 -133
  674. package/docs/decisions/ADR-042-runtime-resolver-decision-gate.md +0 -113
  675. package/docs/decisions/ADR-043-monorepo-collapse-to-src-domains.md +0 -128
  676. package/docs/decisions/ADR-044-command-naming-scheme-hyphenated.md +0 -250
  677. package/docs/decisions/ADR-045-src-source-layout-and-profiles-as-views.md +0 -111
  678. package/docs/decisions/ADR-046-thin-command-principle.md +0 -93
  679. package/docs/decisions/ADR-047-framework-neutral-stack-adaptive-commands.md +0 -94
  680. package/docs/decisions/ADR-048-command-justification-rule.md +0 -106
  681. package/docs/decisions/ADR-049-configuration-trust-boundary.md +0 -132
  682. package/docs/decisions/ADR-050-workspace-vs-package-root-boundary.md +0 -121
  683. package/docs/decisions/ADR-051-uncondensed-source-container-relocation.md +0 -114
  684. package/docs/decisions/ADR-052-packages-removal-and-pack-home-migration.md +0 -86
  685. package/docs/decisions/ADR-053-agents-namespace-dual-role-resolution.md +0 -132
  686. package/docs/decisions/ADR-054-rule-adherence-decay-triggered-restate.md +0 -143
  687. package/docs/decisions/ADR-055-flow-layer-data-model.md +0 -235
  688. package/docs/decisions/ADR-056-unvalidated-video-adapters-disposition.md +0 -124
  689. package/docs/decisions/ADR-057-consolidation-evidence-gate-outcomes.md +0 -135
  690. package/docs/decisions/ADR-058-condensed-output-relocation-to-dist.md +0 -94
  691. package/docs/decisions/ADR-059-render-resume-filesystem-as-state.md +0 -161
  692. package/docs/decisions/ADR-060-comfyui-sandbox-model.md +0 -154
  693. package/docs/decisions/ADR-061-corpus-grounding-layer.md +0 -230
  694. package/docs/decisions/ADR-062-encrypt-at-rest-store-architecture.md +0 -121
  695. package/docs/decisions/ADR-063-append-jsonl-encryption-deferred.md +0 -83
  696. package/docs/decisions/ADR-064-append-jsonl-per-record-encryption.md +0 -113
  697. package/docs/decisions/ADR-065-tier3-inbox-handoff-v0.md +0 -86
  698. package/docs/decisions/ADR-066-skill-body-prerendering.md +0 -93
  699. package/docs/decisions/ADR-067-glama-registry-listing.md +0 -259
  700. package/docs/decisions/ADR-068-host-tier-detection.md +0 -90
  701. package/docs/decisions/ADR-069-prompt-renderer.md +0 -97
  702. package/docs/decisions/ADR-070-tier1-drive-loop.md +0 -94
  703. package/docs/decisions/ADR-071-launch-drive-integration.md +0 -95
  704. package/docs/decisions/ADR-072-codex-gemini-drive-configs.md +0 -90
  705. package/docs/decisions/ADR-073-drive-health-kill-switch.md +0 -81
  706. package/docs/decisions/ADR-074-drive-kill-switch-auto-recovery.md +0 -84
  707. package/docs/decisions/ADR-075-workspace-gui-drive.md +0 -83
  708. package/docs/decisions/ADR-076-workspace-multi-turn.md +0 -92
  709. package/docs/decisions/ADR-077-workspace-followup-gui.md +0 -70
  710. package/docs/decisions/ADR-078-drive-health-panel.md +0 -70
  711. package/docs/decisions/ADR-079-workspace-host-picker.md +0 -81
  712. package/docs/decisions/ADR-080-host-session-expired-410.md +0 -92
  713. package/docs/decisions/ADR-081-drive-health-reset-and-410-affordance.md +0 -71
  714. package/docs/decisions/ADR-082-410-one-click-relaunch.md +0 -58
  715. package/docs/decisions/ADR-083-session-thread-and-arbitrary-continuation.md +0 -85
  716. package/docs/decisions/ADR-084-drive-health-refresh-on-drive.md +0 -52
  717. package/docs/decisions/ADR-085-mcp-stdio-end-user-distribution-shape.md +0 -158
  718. package/docs/decisions/ADR-086-read-only-cross-agent-mcp-discovery-helper.md +0 -171
  719. package/docs/decisions/ADR-087-installer-e2e-test-strategy.md +0 -111
  720. package/docs/decisions/ADR-088-no-external-runtime-federation.md +0 -144
  721. package/docs/decisions/ADR-089-lean-local-plugin-install.md +0 -111
  722. package/docs/decisions/ADR-090-visibility-command-frontmatter-field.md +0 -95
  723. package/docs/decisions/ADR-091-split-meta-capability-packs.md +0 -113
  724. package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +0 -117
  725. package/docs/decisions/ADR-093-ai-council-config-user-global.md +0 -117
  726. package/docs/decisions/ADR-094-agent-memory-layer-removal.md +0 -94
  727. package/docs/decisions/ADR-095-workspace-boundary-contract.md +0 -108
  728. package/docs/decisions/ADR-096-analysis-workbench.md +0 -110
  729. package/docs/decisions/ADR-097-mission-recipe-privilege-boundary.md +0 -121
  730. package/docs/decisions/ADR-098-evidence-first-structure-discovery.md +0 -154
  731. package/docs/decisions/ADR-099-file-first-pattern-library.md +0 -87
  732. package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +0 -140
  733. package/docs/decisions/ADR-101-ticket-bundle-emission.md +0 -109
  734. package/docs/decisions/ADR-102-ticket-handoff-paste-and-mcp.md +0 -72
  735. package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +0 -92
  736. package/docs/decisions/ADR-104-ai-council-config-global-only.md +0 -119
  737. package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +0 -108
  738. package/docs/decisions/ADR-106-recursive-verification-benchmark-gate.md +0 -126
  739. package/docs/decisions/ADR-107-legal-domain-pack-adoption.md +0 -70
  740. package/docs/decisions/ADR-108-open-source-forever-no-commercial-tier.md +0 -46
  741. package/docs/decisions/ADR-109-subagent-v1-contract.md +0 -246
  742. package/docs/decisions/ADR-110-discipline-profile-resolution-locus.md +0 -110
  743. package/docs/decisions/ADR-111-canonical-glama-listing.md +0 -97
  744. package/docs/decisions/ADR-112-stdio-lite-stays-read-only.md +0 -92
  745. package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +0 -201
  746. package/docs/decisions/ADR-114-commit-policy-iron-law-override.md +0 -85
  747. package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +0 -91
  748. package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +0 -144
  749. package/docs/decisions/ADR-117-subagents-auto-default-on.md +0 -89
  750. package/docs/decisions/ADR-118-loop-engineering-boundaries.md +0 -162
  751. package/docs/decisions/ADR-119-global-knowledge-default-on.md +0 -104
  752. package/docs/decisions/ADR-120-council-chairman-mode.md +0 -70
  753. package/docs/decisions/ADR-121-knowledge-sensitivity-classes.md +0 -90
  754. package/docs/decisions/ADR-122-adversarial-verification-council.md +0 -166
  755. package/docs/decisions/ADR-123-runtime-security-scope-and-spawn-hardening.md +0 -142
  756. package/docs/decisions/ADR-200-python-to-typescript-migration.md +0 -203
  757. package/docs/decisions/ADR-rule-kernel-and-router.md +0 -133
  758. package/docs/decisions/INDEX.md +0 -136
  759. package/docs/deploy/connector-setup.md +0 -129
  760. package/docs/deploy/env-vars.md +0 -70
  761. package/docs/deploy/policy-cookbook.md +0 -130
  762. package/docs/deploy/quickstart.md +0 -112
  763. package/docs/deploy/small-team-recipe.md +0 -148
  764. package/docs/deploy/team-deployment-posture.md +0 -111
  765. package/docs/design/adversarial-council-eval.md +0 -106
  766. package/docs/design/council-vs-solo-baseline.md +0 -92
  767. package/docs/design/cross-model-parity-eval.md +0 -82
  768. package/docs/design/length-neutral-judge-rerun.md +0 -92
  769. package/docs/design/utilization-window-criteria.md +0 -51
  770. package/docs/development.md +0 -230
  771. package/docs/distribution/awesome-list-submission.md +0 -46
  772. package/docs/distribution/consumer-matrix.md +0 -96
  773. package/docs/distribution/launch-story-draft.md +0 -58
  774. package/docs/distribution/mcp-submission-checklist.md +0 -95
  775. package/docs/distribution/public-install-smoke.md +0 -68
  776. package/docs/distribution/registries.md +0 -117
  777. package/docs/distribution/registry-submissions.md +0 -85
  778. package/docs/distribution/telemetry-privacy.md +0 -128
  779. package/docs/distribution/telemetry-schema.md +0 -174
  780. package/docs/distribution/topics-equivalents-decay-policy.md +0 -51
  781. package/docs/end-to-end-walkthroughs.md +0 -165
  782. package/docs/enforcement-by-host.md +0 -44
  783. package/docs/examples/agent-user.example.md +0 -22
  784. package/docs/experiences/agency.md +0 -49
  785. package/docs/experiences/content_creator.md +0 -49
  786. package/docs/experiences/developer.md +0 -49
  787. package/docs/experiences/finance.md +0 -50
  788. package/docs/experiences/founder.md +0 -49
  789. package/docs/experiences/ops.md +0 -49
  790. package/docs/featured-commands.md +0 -27
  791. package/docs/featured-skills.md +0 -95
  792. package/docs/flows.md +0 -67
  793. package/docs/getting-started-by-role.md +0 -190
  794. package/docs/getting-started-laravel.md +0 -72
  795. package/docs/getting-started-local-stdio.md +0 -77
  796. package/docs/getting-started.md +0 -254
  797. package/docs/github-topics.md +0 -93
  798. package/docs/governance-advantage.md +0 -72
  799. package/docs/governance.md +0 -96
  800. package/docs/guides/cross-repo-linked-projects.md +0 -93
  801. package/docs/guides/cross-repo-retrieval.md +0 -61
  802. package/docs/guides/frontend-design-corpus-refresh.md +0 -83
  803. package/docs/guides/local-analytics.md +0 -125
  804. package/docs/guides/local-knowledge.md +0 -127
  805. package/docs/guides/skill-discovery.md +0 -71
  806. package/docs/guides/skill-preview.md +0 -71
  807. package/docs/hook-payload-capture.md +0 -221
  808. package/docs/install-friction-report.md +0 -66
  809. package/docs/installation.md +0 -959
  810. package/docs/maintainers/branch-protection.md +0 -105
  811. package/docs/maintainers/dev-mode.md +0 -106
  812. package/docs/maintainers/optional-media-deps.md +0 -50
  813. package/docs/maintainers/system-map.md +0 -85
  814. package/docs/mcp-registries.md +0 -94
  815. package/docs/mcp-server.md +0 -175
  816. package/docs/mcp.md +0 -147
  817. package/docs/media/proof-demo.cast +0 -14
  818. package/docs/media/proof-demo.gif +0 -0
  819. package/docs/migration/consumer-template-consumption-model.md +0 -145
  820. package/docs/migration/divergences/README.md +0 -55
  821. package/docs/migration/divergences/_template.md +0 -50
  822. package/docs/migration/divergences/bench-stats-float-precision.md +0 -72
  823. package/docs/migration/divergences/mcp-telemetry-node-sqlite.md +0 -61
  824. package/docs/migration/divergences/pack-mcp-content-gzip-body.md +0 -53
  825. package/docs/migration/divergences/src-scripts-build_cloud_bundle.md +0 -63
  826. package/docs/migration/divergences/src-scripts-check_memory.md +0 -91
  827. package/docs/migration/divergences/src-scripts-inventory_abstraction_budget.md +0 -65
  828. package/docs/migration/divergences/src-scripts-lint_marketplace.md +0 -57
  829. package/docs/migration/divergences/src-scripts-lint_mcp_registry_manifest.md +0 -70
  830. package/docs/migration/divergences/src-scripts-spotcheck_thin_root.md +0 -60
  831. package/docs/migration/divergences/src-scripts-validate_agent_settings.md +0 -58
  832. package/docs/migration/node-floor.md +0 -86
  833. package/docs/migration/v1-to-v2.md +0 -156
  834. package/docs/migration/yaml-roundtrip-spike.md +0 -163
  835. package/docs/migrations/commands-1.15.0.md +0 -117
  836. package/docs/parity/bench-external.json +0 -58
  837. package/docs/parity/bench.json +0 -41
  838. package/docs/parity/command-frontmatter.md +0 -60
  839. package/docs/parity/external-runtime.md +0 -46
  840. package/docs/personas.md +0 -178
  841. package/docs/positioning-evidence.md +0 -86
  842. package/docs/profiles.md +0 -108
  843. package/docs/proof.md +0 -176
  844. package/docs/quality.md +0 -98
  845. package/docs/readme-split-plan.md +0 -102
  846. package/docs/recruits/_template.md +0 -81
  847. package/docs/release-runbook.md +0 -141
  848. package/docs/role-experiences.md +0 -19
  849. package/docs/safety.md +0 -30
  850. package/docs/second-brain-scope.md +0 -116
  851. package/docs/self-review-gate.md +0 -65
  852. package/docs/setup/enterprise-and-offline.md +0 -201
  853. package/docs/setup/mcp-client-config.md +0 -358
  854. package/docs/setup/mcp-cloud-endpoints.md +0 -116
  855. package/docs/setup/mcp-cloud-registry-listing.md +0 -99
  856. package/docs/setup/mcp-cloud-setup.md +0 -183
  857. package/docs/setup/mcp-r2-bootstrap.md +0 -82
  858. package/docs/setup/mcp-server-docker.md +0 -101
  859. package/docs/setup/per-ide/aider.md +0 -48
  860. package/docs/setup/per-ide/antigravity.md +0 -68
  861. package/docs/setup/per-ide/augment.md +0 -89
  862. package/docs/setup/per-ide/claude-code.md +0 -97
  863. package/docs/setup/per-ide/claude-desktop.md +0 -207
  864. package/docs/setup/per-ide/cline.md +0 -43
  865. package/docs/setup/per-ide/codebuddy.md +0 -63
  866. package/docs/setup/per-ide/codex.md +0 -46
  867. package/docs/setup/per-ide/continue.md +0 -68
  868. package/docs/setup/per-ide/copilot.md +0 -80
  869. package/docs/setup/per-ide/cursor.md +0 -125
  870. package/docs/setup/per-ide/droid.md +0 -65
  871. package/docs/setup/per-ide/gemini-cli.md +0 -45
  872. package/docs/setup/per-ide/jetbrains.md +0 -76
  873. package/docs/setup/per-ide/kilocode.md +0 -66
  874. package/docs/setup/per-ide/kiro.md +0 -72
  875. package/docs/setup/per-ide/opencode.md +0 -62
  876. package/docs/setup/per-ide/qoder.md +0 -63
  877. package/docs/setup/per-ide/roocode.md +0 -68
  878. package/docs/setup/per-ide/trae.md +0 -63
  879. package/docs/setup/per-ide/warp.md +0 -63
  880. package/docs/setup/per-ide/windsurf.md +0 -109
  881. package/docs/setup/per-ide/zed.md +0 -73
  882. package/docs/showcase.md +0 -209
  883. package/docs/skill-duplication-findings.md +0 -91
  884. package/docs/skills-catalog.md +0 -265
  885. package/docs/skills-taxonomy.md +0 -90
  886. package/docs/spawn-site-policy.md +0 -72
  887. package/docs/succession.md +0 -80
  888. package/docs/threat-model.md +0 -29
  889. package/docs/troubleshooting.md +0 -155
  890. package/docs/ui-track-mental-model.md +0 -121
  891. package/docs/us-vs-the-category.md +0 -38
  892. package/docs/value.md +0 -86
  893. package/docs/walkthroughs/assets/workspace-role-grid.png +0 -0
  894. package/docs/walkthroughs/assets/workspace-session-started.png +0 -0
  895. package/docs/walkthroughs/assets/workspace-task-picker.png +0 -0
  896. package/docs/walkthroughs/daily-workspace-a11y.md +0 -87
  897. package/docs/walkthroughs/daily-workspace.md +0 -127
  898. package/docs/wedge/production-validator/README.md +0 -81
  899. package/docs/wedge/production-validator/first-run-check.sh +0 -42
  900. package/docs/wedge/production-validator/production-validator.md +0 -83
  901. package/docs/wizard.md +0 -173
@@ -0,0 +1,714 @@
1
+ /**
2
+ * Deterministic SPDX license detection + derived borrow-compatibility policy
3
+ * (road-to-provenance-and-license-governance S1.2).
4
+ *
5
+ * NO LLM classification (cut C3, same cut as lean-init's own C3) — every id
6
+ * is matched via a fixed, deterministic marker table, and the borrow policy
7
+ * comes from a closed compatibility matrix, never free text. Design
8
+ * principle #1 (unknown escalates, never down-guessed) governs every gap in
9
+ * this file: sources that disagree, a repo license outside the matrix's five
10
+ * target rows, or a source class the matrix leaves unstated for a given
11
+ * target all resolve to the STRICTEST available action (escalate or deny),
12
+ * never a guessed allow.
13
+ *
14
+ * This module does real filesystem I/O (reading LICENSE/package.json/
15
+ * composer.json from a target repo) — that IS the detection job, so unlike a
16
+ * typical "_lib is pure" split, only the CLI's argv parsing, stdout/stderr
17
+ * shaping, and file-write side effect live in the sibling
18
+ * `../detect_target_license.ts` wrapper.
19
+ *
20
+ * Deviations from a literal transcription of the roadmap (recorded here so
21
+ * the trace is auditable, not silent):
22
+ *
23
+ * - SSPL's real SPDX identifier is `SSPL-1.0` (there is no bare "SSPL" entry
24
+ * in the SPDX license list) — the roadmap prose shorthand is used
25
+ * everywhere it says "SSPL"; this module uses the real id so its output
26
+ * is directly usable by SPDX-aware consumers.
27
+ * - The top-level `deny` array (flattened to concrete SPDX ids) is NOT in
28
+ * the roadmap's literal S1.2 text — it exists because
29
+ * `src/scripts/lint_provenance.ts` (landed concurrently, S1.3) already
30
+ * reads `license-policy.yaml`'s top-level `deny: [<SPDX id>, ...]`
31
+ * directly (`resolveDenyPolicy()`). Emitting it keeps this file usable as
32
+ * that linter's override surface without touching S1.3's code. The
33
+ * roadmap-mandated class-keyed verdict still lives at `policy.{allow,
34
+ * conditional,deny}` — the top-level array is a derived view of
35
+ * `policy.deny`, expanded to concrete ids for that one downstream reader.
36
+ * - Workspace-license escalation (v1 scope-limiter): a divergent workspace
37
+ * SPDX id escalates through the SAME disagreement path as root-source
38
+ * disagreement — no second mechanism. Full per-workspace policy
39
+ * derivation is explicitly out of scope for v1 (see
40
+ * agents/roadmaps/later/road-to-per-workspace-license-policy.md).
41
+ */
42
+
43
+ import * as crypto from 'node:crypto';
44
+ import * as fs from 'node:fs';
45
+ import * as path from 'node:path';
46
+
47
+ // ─── SPDX ids (the roadmap's closed 15-id detection list) ───────────────────
48
+
49
+ export type SpdxId =
50
+ | 'MIT' | 'Apache-2.0' | 'BSD-2-Clause' | 'BSD-3-Clause' | 'ISC' | '0BSD'
51
+ | 'MPL-2.0' | 'LGPL-2.1' | 'LGPL-3.0' | 'GPL-2.0' | 'GPL-3.0' | 'AGPL-3.0'
52
+ | 'SSPL-1.0' | 'Unlicense' | 'CC0-1.0';
53
+
54
+ export const KNOWN_SPDX_IDS: readonly SpdxId[] = [
55
+ 'MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', '0BSD',
56
+ 'MPL-2.0', 'LGPL-2.1', 'LGPL-3.0', 'GPL-2.0', 'GPL-3.0', 'AGPL-3.0',
57
+ 'SSPL-1.0', 'Unlicense', 'CC0-1.0',
58
+ ];
59
+
60
+ const SPDX_ID_LOOKUP: ReadonlyMap<string, SpdxId> = new Map(
61
+ KNOWN_SPDX_IDS.map((id) => [id.toLowerCase(), id]),
62
+ );
63
+
64
+ // ─── Source / target classes ────────────────────────────────────────────────
65
+
66
+ /** The bucket a BORROWED (source) snippet's license falls into. */
67
+ export type SourceClass =
68
+ | 'permissive' | 'public-domain' | 'weak-copyleft'
69
+ | 'gpl-2.0' | 'gpl-3.0' | 'agpl' | 'sspl' | 'unknown';
70
+
71
+ export const SOURCE_CLASSES: readonly SourceClass[] = [
72
+ 'permissive', 'public-domain', 'weak-copyleft',
73
+ 'gpl-2.0', 'gpl-3.0', 'agpl', 'sspl', 'unknown',
74
+ ];
75
+
76
+ /** The TARGET repo's own license class — one row of the S1.2 matrix. */
77
+ export type TargetClass = 'permissive' | 'weak-copyleft' | 'gpl-2.0' | 'gpl-3.0' | 'agpl' | 'none';
78
+
79
+ export const TARGET_CLASSES: readonly TargetClass[] = [
80
+ 'permissive', 'weak-copyleft', 'gpl-2.0', 'gpl-3.0', 'agpl', 'none',
81
+ ];
82
+
83
+ export type Verdict = 'allow' | 'conditional' | 'deny';
84
+ export type DetectionSourceKind = 'license-file' | 'package-json' | 'composer-json' | 'none' | 'manual';
85
+ export type WorkspaceScope = 'single' | 'homogeneous-multi';
86
+
87
+ /** Maps a detected SPDX id to the source class it represents when BORROWED. */
88
+ const SPDX_TO_SOURCE_CLASS: Record<SpdxId, SourceClass> = {
89
+ MIT: 'permissive', 'Apache-2.0': 'permissive', 'BSD-2-Clause': 'permissive',
90
+ 'BSD-3-Clause': 'permissive', ISC: 'permissive', '0BSD': 'permissive',
91
+ Unlicense: 'public-domain', 'CC0-1.0': 'public-domain',
92
+ 'MPL-2.0': 'weak-copyleft', 'LGPL-2.1': 'weak-copyleft', 'LGPL-3.0': 'weak-copyleft',
93
+ 'GPL-2.0': 'gpl-2.0', 'GPL-3.0': 'gpl-3.0', 'AGPL-3.0': 'agpl',
94
+ 'SSPL-1.0': 'sspl',
95
+ };
96
+
97
+ /**
98
+ * Maps a detected SPDX id to a TARGET row, when the matrix has one.
99
+ * SSPL-1.0 / Unlicense / CC0-1.0 have NO target row in the roadmap's 5-row
100
+ * matrix — deliberately absent (principle #1): a repo licensed under one of
101
+ * these falls back to the strictest default ('none') plus a warning, rather
102
+ * than a guessed row.
103
+ */
104
+ const SPDX_TO_TARGET_CLASS: Partial<Record<SpdxId, TargetClass>> = {
105
+ MIT: 'permissive', 'Apache-2.0': 'permissive', 'BSD-2-Clause': 'permissive',
106
+ 'BSD-3-Clause': 'permissive', ISC: 'permissive', '0BSD': 'permissive',
107
+ 'MPL-2.0': 'weak-copyleft', 'LGPL-2.1': 'weak-copyleft', 'LGPL-3.0': 'weak-copyleft',
108
+ 'GPL-2.0': 'gpl-2.0', 'GPL-3.0': 'gpl-3.0', 'AGPL-3.0': 'agpl',
109
+ };
110
+
111
+ /**
112
+ * The source-class of a concrete detected SPDX id — the counterpart Phase 2
113
+ * (`lint_code_provenance.ts`, S2.1) needs to classify a BORROWED snippet's
114
+ * detected license against this module's target-derived policy.
115
+ */
116
+ export function sourceClassOfSpdxId(id: SpdxId): SourceClass {
117
+ return SPDX_TO_SOURCE_CLASS[id];
118
+ }
119
+
120
+ /** Reverse of SPDX_TO_SOURCE_CLASS — used to flatten policy.deny to concrete ids. */
121
+ const SOURCE_CLASS_TO_SPDX_IDS: Record<SourceClass, readonly SpdxId[]> = {
122
+ permissive: ['MIT', 'Apache-2.0', 'BSD-2-Clause', 'BSD-3-Clause', 'ISC', '0BSD'],
123
+ 'public-domain': ['Unlicense', 'CC0-1.0'],
124
+ 'weak-copyleft': ['MPL-2.0', 'LGPL-2.1', 'LGPL-3.0'],
125
+ 'gpl-2.0': ['GPL-2.0'],
126
+ 'gpl-3.0': ['GPL-3.0'],
127
+ agpl: ['AGPL-3.0'],
128
+ sspl: ['SSPL-1.0'],
129
+ unknown: [],
130
+ };
131
+
132
+ export function expandSourceClassesToSpdxIds(classes: readonly SourceClass[]): SpdxId[] {
133
+ const ids = new Set<SpdxId>();
134
+ for (const cls of classes) {
135
+ for (const id of SOURCE_CLASS_TO_SPDX_IDS[cls]) ids.add(id);
136
+ }
137
+ return [...ids].sort();
138
+ }
139
+
140
+ // ─── The closed compatibility matrix (S1.2, transcribed verbatim per row) ───
141
+
142
+ export interface ClassBuckets {
143
+ allow: SourceClass[];
144
+ conditional: SourceClass[];
145
+ deny: SourceClass[];
146
+ }
147
+
148
+ /**
149
+ * The EXPLICIT cells from the roadmap's matrix, one entry per target row.
150
+ * A source class this module knows about but a given row does NOT name is
151
+ * intentionally absent from all three buckets here — `classifyBorrow()`
152
+ * folds any such gap into 'conditional' (escalate), never a silent
153
+ * allow/deny. This keeps the matrix itself a byte-for-byte transcription
154
+ * (no invented rows) while still producing an exhaustive, closed-enum
155
+ * output policy via `derivePolicyBuckets()`.
156
+ *
157
+ * The one interpretive call this file makes: the roadmap's GPL row deny
158
+ * cell reads "AGPL (for GPL-2.0), SSPL, unknown" — the parenthetical scopes
159
+ * the AGPL denial to a GPL-2.0 target only. For a GPL-3.0 target, AGPL-3.0
160
+ * is therefore folded into the row's "permissive + GPL-compatible copyleft"
161
+ * allow cell instead (AGPL-3.0 is one-directionally GPL-3.0-compatible per
162
+ * FSF guidance) — hence the GPL row is modeled as two target buckets
163
+ * (`gpl-2.0`, `gpl-3.0`) rather than one, reproducing the same cell content
164
+ * the roadmap wrote, not inventing new content.
165
+ */
166
+ export const COMPATIBILITY_MATRIX: Record<TargetClass, ClassBuckets> = {
167
+ permissive: {
168
+ allow: ['permissive', 'public-domain'],
169
+ conditional: ['weak-copyleft'],
170
+ deny: ['gpl-2.0', 'gpl-3.0', 'agpl', 'sspl', 'unknown'],
171
+ },
172
+ 'weak-copyleft': {
173
+ allow: ['permissive', 'weak-copyleft'],
174
+ conditional: ['gpl-2.0', 'gpl-3.0'],
175
+ deny: ['agpl', 'sspl', 'unknown'],
176
+ },
177
+ 'gpl-2.0': {
178
+ allow: ['permissive', 'gpl-2.0', 'gpl-3.0'],
179
+ conditional: ['weak-copyleft'],
180
+ deny: ['agpl', 'sspl', 'unknown'],
181
+ },
182
+ 'gpl-3.0': {
183
+ allow: ['permissive', 'gpl-2.0', 'gpl-3.0', 'agpl'],
184
+ conditional: ['weak-copyleft'],
185
+ deny: ['sspl', 'unknown'],
186
+ },
187
+ agpl: {
188
+ allow: ['permissive', 'gpl-2.0', 'gpl-3.0', 'weak-copyleft'],
189
+ conditional: [],
190
+ deny: ['sspl', 'unknown'],
191
+ },
192
+ none: {
193
+ allow: ['permissive'],
194
+ conditional: ['public-domain'],
195
+ deny: ['weak-copyleft', 'gpl-2.0', 'gpl-3.0', 'agpl', 'sspl', 'unknown'],
196
+ },
197
+ };
198
+
199
+ /**
200
+ * Classify ONE borrow: target repo's class -> the verdict for a source of
201
+ * the given class. `unknown` is an absolute invariant — always deny,
202
+ * regardless of target — matching every row's explicit "…, unknown" deny
203
+ * entry. Any source class a row does not explicitly place is 'conditional'
204
+ * (escalate), never a silent allow or deny.
205
+ */
206
+ export function classifyBorrow(target: TargetClass, source: SourceClass): Verdict {
207
+ if (source === 'unknown') return 'deny';
208
+ const buckets = COMPATIBILITY_MATRIX[target];
209
+ if (buckets.allow.includes(source)) return 'allow';
210
+ if (buckets.deny.includes(source)) return 'deny';
211
+ return 'conditional';
212
+ }
213
+
214
+ /** The exhaustive, closed-enum policy for a target class — every SourceClass placed. */
215
+ export function derivePolicyBuckets(target: TargetClass): ClassBuckets {
216
+ const buckets: ClassBuckets = { allow: [], conditional: [], deny: [] };
217
+ for (const source of SOURCE_CLASSES) {
218
+ const verdict = classifyBorrow(target, source);
219
+ buckets[verdict === 'allow' ? 'allow' : verdict === 'deny' ? 'deny' : 'conditional'].push(source);
220
+ }
221
+ return buckets;
222
+ }
223
+
224
+ // ─── SPDX detection from LICENSE-file text ──────────────────────────────────
225
+
226
+ const SPDX_TAG_RE = /SPDX-License-Identifier:\s*([A-Za-z0-9.+-]+)/i;
227
+
228
+ function normalize(text: string): string {
229
+ return text.toLowerCase().replace(/\s+/g, ' ').trim();
230
+ }
231
+
232
+ /**
233
+ * Deterministic marker-phrase detection — NO LLM (cut C3). Priority order:
234
+ * (1) an explicit `SPDX-License-Identifier:` tag — exact, unambiguous, no
235
+ * heuristics needed; (2) header/body marker phrases, most-specific first so
236
+ * a superstring license (AGPL/LGPL vs bare GPL) never gets misread as its
237
+ * substring cousin.
238
+ *
239
+ * Known limitation (stated, not hidden): 0BSD's body text overlaps almost
240
+ * exactly with ISC's short grant clause, so 0BSD is detected via its header
241
+ * only ("BSD Zero Clause License" / "Zero-Clause BSD"); a 0BSD file with
242
+ * neither that header nor an SPDX tag is undetectable here and falls back
243
+ * to the "nothing detectable" strictest-default path — never guessed as ISC.
244
+ */
245
+ export function detectSpdxFromText(rawText: string): SpdxId | null {
246
+ const tagMatch = SPDX_TAG_RE.exec(rawText);
247
+ if (tagMatch?.[1] !== undefined) {
248
+ const canonical = SPDX_ID_LOOKUP.get(tagMatch[1].toLowerCase());
249
+ if (canonical !== undefined) return canonical;
250
+ }
251
+
252
+ const t = normalize(rawText);
253
+
254
+ if (t.includes('gnu affero general public license')) return 'AGPL-3.0';
255
+ if (t.includes('gnu lesser general public license')) {
256
+ if (t.includes('version 2.1')) return 'LGPL-2.1';
257
+ if (t.includes('version 3')) return 'LGPL-3.0';
258
+ return null; // version unstated -> ambiguous, never guess
259
+ }
260
+ if (t.includes('gnu general public license')) {
261
+ if (t.includes('version 2')) return 'GPL-2.0';
262
+ if (t.includes('version 3')) return 'GPL-3.0';
263
+ return null;
264
+ }
265
+ if (t.includes('server side public license')) return 'SSPL-1.0';
266
+ if (t.includes('mozilla public license')) return 'MPL-2.0';
267
+ if (t.includes('bsd zero clause license') || t.includes('zero-clause bsd')) return '0BSD';
268
+ if (t.includes('bsd 3-clause license')) return 'BSD-3-Clause';
269
+ if (t.includes('bsd 2-clause license')) return 'BSD-2-Clause';
270
+ if (t.includes('redistributions of source code must retain the above copyright')) {
271
+ return t.includes('neither the name') ? 'BSD-3-Clause' : 'BSD-2-Clause';
272
+ }
273
+ if (t.includes('isc license')) return 'ISC';
274
+ if (t.includes('permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that')) return 'ISC';
275
+ if (t.includes('apache license') && t.includes('version 2.0')) return 'Apache-2.0';
276
+ if (t.includes('mit license')) return 'MIT';
277
+ if (t.includes('permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files')) return 'MIT';
278
+ if (t.includes('this is free and unencumbered software released into the public domain')) return 'Unlicense';
279
+ if (t.includes('unlicense')) return 'Unlicense';
280
+ if (t.includes('cc0 1.0 universal')) return 'CC0-1.0';
281
+ if (t.includes('creative commons legal code') && t.includes('cc0')) return 'CC0-1.0';
282
+
283
+ return null;
284
+ }
285
+
286
+ function isPlainRecord(v: unknown): v is Record<string, unknown> {
287
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
288
+ }
289
+
290
+ /**
291
+ * SPDX id from a package.json/composer.json `license` field. Compound SPDX
292
+ * expressions ("(MIT OR Apache-2.0)") are intentionally NOT resolved —
293
+ * picking a branch would be exactly the down-guess principle #1 forbids;
294
+ * they surface as undetectable (null) via this source instead.
295
+ */
296
+ export function detectSpdxFromManifestLicenseField(value: unknown): SpdxId | null {
297
+ if (typeof value === 'string') {
298
+ return SPDX_ID_LOOKUP.get(value.trim().toLowerCase()) ?? null;
299
+ }
300
+ if (isPlainRecord(value) && typeof value.type === 'string') {
301
+ return detectSpdxFromManifestLicenseField(value.type); // legacy npm { type, url } shape
302
+ }
303
+ if (Array.isArray(value)) {
304
+ const ids = value
305
+ .map((v) => detectSpdxFromManifestLicenseField(v))
306
+ .filter((v): v is SpdxId => v !== null);
307
+ const distinct = new Set(ids);
308
+ if (distinct.size === 1) {
309
+ const [only] = distinct;
310
+ return only ?? null;
311
+ }
312
+ return null; // empty or ambiguous multi-license array -> undetectable, never guessed
313
+ }
314
+ return null;
315
+ }
316
+
317
+ function readJsonIfExists(filePath: string): unknown {
318
+ if (!fs.existsSync(filePath)) return undefined;
319
+ try {
320
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
321
+ } catch {
322
+ return undefined;
323
+ }
324
+ }
325
+
326
+ // ─── Directory-level signal scan (reused for root AND each workspace) ──────
327
+
328
+ const LICENSE_FILENAMES = ['LICENSE', 'LICENSE.md', 'COPYING'] as const;
329
+
330
+ export interface FileLicenseSignal {
331
+ source: 'license-file' | 'package-json' | 'composer-json';
332
+ spdxId: SpdxId | null; // present but undetectable -> null
333
+ filePath: string | null;
334
+ }
335
+
336
+ export interface DirectoryScan {
337
+ signals: FileLicenseSignal[];
338
+ licenseFilePath: string | null;
339
+ licenseSha256: string | null;
340
+ }
341
+
342
+ /** Scan one directory (repo root or a workspace dir) in roadmap precedence order. */
343
+ export function scanDirectoryLicenseSignals(dirAbs: string, dirRel: string): DirectoryScan {
344
+ let licenseFilePath: string | null = null;
345
+ let licenseSha256: string | null = null;
346
+ const signals: FileLicenseSignal[] = [];
347
+
348
+ for (const name of LICENSE_FILENAMES) {
349
+ const abs = path.join(dirAbs, name);
350
+ if (fs.existsSync(abs) && fs.statSync(abs).isFile()) {
351
+ const content = fs.readFileSync(abs, 'utf8');
352
+ licenseFilePath = path.join(dirRel, name).split(path.sep).join('/');
353
+ licenseSha256 = crypto.createHash('sha256').update(content, 'utf8').digest('hex');
354
+ signals.push({ source: 'license-file', spdxId: detectSpdxFromText(content), filePath: licenseFilePath });
355
+ break; // first found by precedence — LICENSE, then LICENSE.md, then COPYING
356
+ }
357
+ }
358
+
359
+ const pkgJson = readJsonIfExists(path.join(dirAbs, 'package.json'));
360
+ if (isPlainRecord(pkgJson) && 'license' in pkgJson) {
361
+ signals.push({ source: 'package-json', spdxId: detectSpdxFromManifestLicenseField(pkgJson.license), filePath: null });
362
+ }
363
+
364
+ const composerJson = readJsonIfExists(path.join(dirAbs, 'composer.json'));
365
+ if (isPlainRecord(composerJson) && 'license' in composerJson) {
366
+ signals.push({ source: 'composer-json', spdxId: detectSpdxFromManifestLicenseField(composerJson.license), filePath: null });
367
+ }
368
+
369
+ return { signals, licenseFilePath, licenseSha256 };
370
+ }
371
+
372
+ export interface ResolvedLicense {
373
+ spdxId: SpdxId | null;
374
+ detectionSource: DetectionSourceKind;
375
+ licenseFilePath: string | null;
376
+ licenseSha256: string | null;
377
+ /** Non-null when 2+ distinct SPDX ids were found across sources — never auto-picked. */
378
+ disagreement: { source: string; spdxId: SpdxId }[] | null;
379
+ }
380
+
381
+ /** Fold a directory's raw signals into one verdict, applying the disagree-escalate rule. */
382
+ export function resolveLicenseSignals(scan: DirectoryScan): ResolvedLicense {
383
+ const withIds = scan.signals.filter(
384
+ (s): s is FileLicenseSignal & { spdxId: SpdxId } => s.spdxId !== null,
385
+ );
386
+ const distinctIds = new Set(withIds.map((s) => s.spdxId));
387
+
388
+ if (distinctIds.size > 1) {
389
+ return {
390
+ spdxId: null,
391
+ detectionSource: 'none',
392
+ licenseFilePath: scan.licenseFilePath,
393
+ licenseSha256: scan.licenseSha256,
394
+ disagreement: withIds.map((s) => ({ source: s.source, spdxId: s.spdxId })),
395
+ };
396
+ }
397
+
398
+ const winner = withIds[0];
399
+ if (winner === undefined) {
400
+ return {
401
+ spdxId: null,
402
+ detectionSource: 'none',
403
+ licenseFilePath: scan.licenseFilePath,
404
+ licenseSha256: scan.licenseSha256,
405
+ disagreement: null,
406
+ };
407
+ }
408
+
409
+ return {
410
+ spdxId: winner.spdxId,
411
+ detectionSource: winner.source,
412
+ licenseFilePath: scan.licenseFilePath,
413
+ licenseSha256: scan.licenseSha256,
414
+ disagreement: null,
415
+ };
416
+ }
417
+
418
+ // ─── Workspace-license escalation (v1 scope-limiter) ────────────────────────
419
+
420
+ export interface WorkspaceDivergence {
421
+ workspaceDir: string;
422
+ spdxId: SpdxId | null;
423
+ }
424
+
425
+ export interface WorkspaceScanResult {
426
+ scope: WorkspaceScope;
427
+ workspaceDirs: string[];
428
+ divergent: WorkspaceDivergence[];
429
+ }
430
+
431
+ /**
432
+ * Resolve declared workspace directories from `package.json` `workspaces`
433
+ * (array of globs, or `{packages: [...]}`). Only a trailing `/*` glob
434
+ * segment is expanded (to the directory's real first-level subdirectories,
435
+ * per the addendum's literal scope) — no general glob engine. Composer has
436
+ * no native "workspaces" concept, so it is deliberately not modeled here:
437
+ * absent a `workspaces` field, this whole check is a no-op.
438
+ */
439
+ function resolveWorkspaceDirs(repoRoot: string): string[] {
440
+ const pkg = readJsonIfExists(path.join(repoRoot, 'package.json'));
441
+ const raw: unknown = isPlainRecord(pkg) ? pkg.workspaces : undefined;
442
+
443
+ let patterns: string[] = [];
444
+ if (Array.isArray(raw)) {
445
+ patterns = raw.filter((v): v is string => typeof v === 'string');
446
+ } else if (isPlainRecord(raw) && Array.isArray(raw.packages)) {
447
+ patterns = raw.packages.filter((v): v is string => typeof v === 'string');
448
+ }
449
+
450
+ const dirs = new Set<string>();
451
+ for (const pattern of patterns) {
452
+ if (pattern.endsWith('/*')) {
453
+ const base = pattern.slice(0, -2);
454
+ const baseAbs = path.join(repoRoot, base);
455
+ if (fs.existsSync(baseAbs) && fs.statSync(baseAbs).isDirectory()) {
456
+ for (const entry of fs.readdirSync(baseAbs, { withFileTypes: true })) {
457
+ if (entry.isDirectory()) dirs.add(path.join(base, entry.name));
458
+ }
459
+ }
460
+ } else if (fs.existsSync(path.join(repoRoot, pattern)) && fs.statSync(path.join(repoRoot, pattern)).isDirectory()) {
461
+ dirs.add(pattern);
462
+ }
463
+ }
464
+ return [...dirs].sort();
465
+ }
466
+
467
+ /**
468
+ * Scan every declared workspace for its OWN license and compare to the
469
+ * root's resolved SPDX id. Any workspace resolving to a DIFFERENT id is
470
+ * `divergent` — the caller escalates through the same disagree path as
471
+ * root-source disagreement (no second mechanism). When the root itself is
472
+ * undetected (`rootSpdxId === null`) there is nothing to diverge FROM, so
473
+ * this v1 scope-limiter skips the comparison rather than guessing what
474
+ * "different from unknown" would even mean.
475
+ */
476
+ function scanWorkspaces(repoRoot: string, rootSpdxId: SpdxId | null): WorkspaceScanResult {
477
+ const workspaceDirs = resolveWorkspaceDirs(repoRoot);
478
+ if (workspaceDirs.length === 0) {
479
+ return { scope: 'single', workspaceDirs: [], divergent: [] };
480
+ }
481
+
482
+ const divergent: WorkspaceDivergence[] = [];
483
+ if (rootSpdxId !== null) {
484
+ for (const rel of workspaceDirs) {
485
+ const scan = scanDirectoryLicenseSignals(path.join(repoRoot, rel), rel);
486
+ const resolved = resolveLicenseSignals(scan);
487
+ if (resolved.spdxId !== null && resolved.spdxId !== rootSpdxId) {
488
+ divergent.push({ workspaceDir: rel, spdxId: resolved.spdxId });
489
+ }
490
+ }
491
+ }
492
+
493
+ return { scope: 'homogeneous-multi', workspaceDirs, divergent };
494
+ }
495
+
496
+ // ─── Root-level detection (the public entry point) ──────────────────────────
497
+
498
+ export interface DetectionResult {
499
+ spdxId: SpdxId | null;
500
+ targetClass: TargetClass;
501
+ detectionSource: DetectionSourceKind;
502
+ licenseFilePath: string | null;
503
+ licenseSha256: string | null;
504
+ escalate: boolean;
505
+ escalateReason: string | null;
506
+ warnings: string[];
507
+ workspaceScope: WorkspaceScope;
508
+ workspaceDirs: string[];
509
+ }
510
+
511
+ export function detectTargetLicense(repoRoot: string): DetectionResult {
512
+ const rootScan = scanDirectoryLicenseSignals(repoRoot, '.');
513
+ const rootResolved = resolveLicenseSignals(rootScan);
514
+
515
+ const warnings: string[] = [];
516
+ let escalate = false;
517
+ let escalateReason: string | null = null;
518
+
519
+ if (rootResolved.disagreement) {
520
+ escalate = true;
521
+ escalateReason =
522
+ 'license sources disagree: ' +
523
+ rootResolved.disagreement.map((d) => `${d.source}=${d.spdxId}`).join(' vs ') +
524
+ ' — never auto-pick, escalate to a human';
525
+ } else if (rootResolved.spdxId === null) {
526
+ warnings.push(
527
+ 'no LICENSE/LICENSE.md/COPYING file, package.json license field, or composer.json ' +
528
+ 'license field detected in the target repo — the repo itself has no discoverable ' +
529
+ 'license; defaulting to the strictest policy (target_class: none)',
530
+ );
531
+ }
532
+
533
+ let targetClass: TargetClass = 'none';
534
+ if (rootResolved.spdxId !== null) {
535
+ const mapped = SPDX_TO_TARGET_CLASS[rootResolved.spdxId];
536
+ if (mapped !== undefined) {
537
+ targetClass = mapped;
538
+ } else {
539
+ warnings.push(
540
+ `detected license '${rootResolved.spdxId}' has no row in the S1.2 compatibility ` +
541
+ 'matrix (permissive / weak-copyleft / gpl-2.0 / gpl-3.0 / agpl / none) — defaulting ' +
542
+ 'to the strictest policy (target_class: none) per the unknown-escalates-never-' +
543
+ 'down-guessed principle',
544
+ );
545
+ }
546
+ }
547
+
548
+ const workspace = scanWorkspaces(repoRoot, rootResolved.spdxId);
549
+ if (workspace.divergent.length > 0 && !escalate) {
550
+ escalate = true;
551
+ escalateReason =
552
+ 'workspace license diverges from root: ' +
553
+ workspace.divergent.map((d) => `${d.workspaceDir}=${d.spdxId ?? 'undetected'}`).join(', ') +
554
+ ` vs root=${rootResolved.spdxId ?? 'undetected'} — heterogeneous monorepo, escalate ` +
555
+ 'rather than silently derive a root-wide policy (v1 scope-limiter; full per-workspace ' +
556
+ 'derivation is a later/ note)';
557
+ }
558
+
559
+ return {
560
+ spdxId: rootResolved.spdxId,
561
+ targetClass,
562
+ detectionSource: rootResolved.spdxId === null ? 'none' : rootResolved.detectionSource,
563
+ licenseFilePath: rootResolved.licenseFilePath,
564
+ licenseSha256: rootResolved.licenseSha256,
565
+ escalate,
566
+ escalateReason,
567
+ warnings,
568
+ workspaceScope: workspace.scope,
569
+ workspaceDirs: workspace.workspaceDirs,
570
+ };
571
+ }
572
+
573
+ // ─── The derived policy document (license-policy.yaml) ─────────────────────
574
+
575
+ export interface LicensePolicyDoc {
576
+ schema_version: 1;
577
+ derived_from: SpdxId | 'none-detected' | 'manual';
578
+ detection: {
579
+ source: DetectionSourceKind;
580
+ license_sha256: string | null;
581
+ };
582
+ target_class: TargetClass;
583
+ workspace_scope: WorkspaceScope;
584
+ policy: ClassBuckets;
585
+ /** Flattened concrete-SPDX-id view of policy.deny — see file header. */
586
+ deny: SpdxId[];
587
+ warnings: string[];
588
+ derived_at: string;
589
+ }
590
+
591
+ /** Refuses on an escalating detection — callers must resolve escalation before deriving. */
592
+ export function buildLicensePolicyDocument(detection: DetectionResult, nowIso: string): LicensePolicyDoc {
593
+ if (detection.escalate) {
594
+ throw new Error(
595
+ 'buildLicensePolicyDocument: refused — detection requires escalation ' +
596
+ `(${detection.escalateReason ?? 'unspecified'}); never auto-derive a policy over it`,
597
+ );
598
+ }
599
+ const policy = derivePolicyBuckets(detection.targetClass);
600
+ return {
601
+ schema_version: 1,
602
+ derived_from: detection.spdxId ?? 'none-detected',
603
+ detection: { source: detection.detectionSource, license_sha256: detection.licenseSha256 },
604
+ target_class: detection.targetClass,
605
+ workspace_scope: detection.workspaceScope,
606
+ policy,
607
+ deny: expandSourceClassesToSpdxIds(policy.deny),
608
+ warnings: detection.warnings,
609
+ derived_at: nowIso,
610
+ };
611
+ }
612
+
613
+ // ─── Invalidation (SHA mismatch -> re-derive; downgrade -> escalate) ───────
614
+
615
+ export type InvalidationAction = 'unchanged' | 're-derive' | 'escalate';
616
+
617
+ export interface InvalidationVerdict {
618
+ action: InvalidationAction;
619
+ reason: string;
620
+ }
621
+
622
+ /**
623
+ * Compares a previously-written policy against a fresh detection. A manual
624
+ * file is never invalidated. A LICENSE-content SHA change that leaves the
625
+ * target class unchanged is a harmless re-derive (refreshes the recorded
626
+ * SHA). A SHA change that ALSO moves the target class is only safe to
627
+ * re-derive when no previously-denied source class becomes allowed under
628
+ * the new class — that specific transition is the downgrade this function
629
+ * exists to catch, and it escalates instead of silently loosening.
630
+ */
631
+ export function checkInvalidation(existing: LicensePolicyDoc, fresh: DetectionResult): InvalidationVerdict {
632
+ if (existing.derived_from === 'manual') {
633
+ return { action: 'unchanged', reason: 'derived_from: manual — consumer-owned, never auto-invalidated' };
634
+ }
635
+
636
+ if (existing.detection.license_sha256 === fresh.licenseSha256) {
637
+ return { action: 'unchanged', reason: 'LICENSE file content unchanged (SHA match)' };
638
+ }
639
+
640
+ if (existing.target_class === fresh.targetClass) {
641
+ return {
642
+ action: 're-derive',
643
+ reason: 'LICENSE file content changed but target_class unchanged — re-deriving to refresh the recorded SHA',
644
+ };
645
+ }
646
+
647
+ const oldBuckets = derivePolicyBuckets(existing.target_class);
648
+ const newBuckets = derivePolicyBuckets(fresh.targetClass);
649
+ const downgraded = oldBuckets.deny.filter((s) => newBuckets.allow.includes(s));
650
+ if (downgraded.length > 0) {
651
+ return {
652
+ action: 'escalate',
653
+ reason:
654
+ `target_class moved from '${existing.target_class}' to '${fresh.targetClass}', and ` +
655
+ `previously-denied source class(es) [${downgraded.join(', ')}] would become allowed — ` +
656
+ 'a downgrade must never silently re-derive',
657
+ };
658
+ }
659
+ return {
660
+ action: 're-derive',
661
+ reason: `target_class moved from '${existing.target_class}' to '${fresh.targetClass}' — no ` +
662
+ 'previously-denied source became allowed, safe to re-derive',
663
+ };
664
+ }
665
+
666
+ // ─── Override merge (tighten accepted; loosen rejected without manual) ────
667
+
668
+ const VERDICT_STRICTNESS: Record<Verdict, number> = { deny: 0, conditional: 1, allow: 2 };
669
+
670
+ function verdictOf(buckets: ClassBuckets, source: SourceClass): Verdict {
671
+ if (buckets.allow.includes(source)) return 'allow';
672
+ if (buckets.deny.includes(source)) return 'deny';
673
+ return 'conditional';
674
+ }
675
+
676
+ export interface OverrideMergeResult {
677
+ doc: LicensePolicyDoc;
678
+ rejectedOverrides: string[];
679
+ }
680
+
681
+ /**
682
+ * Merges a consumer-edited `existing` policy against the freshly-derived
683
+ * `fresh` one, per source class: tightening (existing stricter than
684
+ * derived) is honored; loosening (existing looser than derived) is
685
+ * rejected and reverted to the derived value — UNLESS `existing.derived_from
686
+ * === 'manual'`, which hands the file entirely to the consumer.
687
+ */
688
+ export function mergeOverride(existing: LicensePolicyDoc, fresh: LicensePolicyDoc): OverrideMergeResult {
689
+ if (existing.derived_from === 'manual') {
690
+ return { doc: existing, rejectedOverrides: [] };
691
+ }
692
+
693
+ const rejected: string[] = [];
694
+ const merged: ClassBuckets = { allow: [], conditional: [], deny: [] };
695
+
696
+ for (const source of SOURCE_CLASSES) {
697
+ const existingVerdict = verdictOf(existing.policy, source);
698
+ const freshVerdict = verdictOf(fresh.policy, source);
699
+ const isLoosen = VERDICT_STRICTNESS[existingVerdict] > VERDICT_STRICTNESS[freshVerdict];
700
+ const finalVerdict = isLoosen ? freshVerdict : existingVerdict;
701
+ if (isLoosen) {
702
+ rejected.push(
703
+ `${source}: existing policy loosens '${freshVerdict}' -> '${existingVerdict}' without ` +
704
+ `derived_from: manual — rejected, reverted to '${freshVerdict}'`,
705
+ );
706
+ }
707
+ merged[finalVerdict === 'allow' ? 'allow' : finalVerdict === 'deny' ? 'deny' : 'conditional'].push(source);
708
+ }
709
+
710
+ return {
711
+ doc: { ...fresh, policy: merged, deny: expandSourceClassesToSpdxIds(merged.deny) },
712
+ rejectedOverrides: rejected,
713
+ };
714
+ }