@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +314 -190
- package/CONTRIBUTING.md +23 -7
- package/MIGRATION.md +20 -0
- package/README.md +57 -11
- package/dist/agent-src/commands/agents/audit.md +1 -1
- package/dist/agent-src/commands/agents/optimize.md +2 -2
- package/dist/agent-src/commands/agents/user.md +1 -1
- package/dist/agent-src/commands/agents.md +1 -1
- package/dist/agent-src/commands/check-current-md.md +3 -3
- package/dist/agent-src/commands/commit/in-chunks.md +8 -0
- package/dist/agent-src/commands/commit.md +17 -0
- package/dist/agent-src/commands/fix/portability.md +2 -2
- package/dist/agent-src/commands/fix/pr-comments-loop.md +136 -0
- package/dist/agent-src/commands/fix/refs.md +2 -2
- package/dist/agent-src/commands/fix/route.md +4 -3
- package/dist/agent-src/commands/fix.md +9 -6
- package/dist/agent-src/commands/ghostwriter.md +1 -1
- package/dist/agent-src/commands/image.md +1 -1
- package/dist/agent-src/commands/optimize/augmentignore.md +1 -1
- package/dist/agent-src/commands/optimize/skills.md +2 -2
- package/dist/agent-src/commands/post-as.md +1 -1
- package/dist/agent-src/commands/pr/create.md +4 -0
- package/dist/agent-src/commands/research.md +1 -1
- package/dist/agent-src/commands/roadmap/ai-council.md +1 -1
- package/dist/agent-src/commands/security-audit-config.md +1 -1
- package/dist/agent-src/commands/team/delegate.md +4 -0
- package/dist/agent-src/commands/video.md +1 -1
- package/dist/agent-src/contexts/authority/kernel-rule-edits.md +1 -1
- package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +48 -2
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +10 -0
- package/dist/agent-src/contexts/execution/subagent-modes-detail.md +109 -8
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +20 -0
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +53 -3
- package/dist/agent-src/contexts/override-system.md +75 -5
- package/dist/agent-src/rules/code-provenance.md +112 -0
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +97 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +13 -3
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +2 -0
- package/dist/agent-src/rules/git-history-discipline.md +2 -0
- package/dist/agent-src/rules/history-discipline.md +80 -0
- package/dist/agent-src/rules/language-and-tone.md +2 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +3 -0
- package/dist/agent-src/rules/media-governance-routing.md +2 -0
- package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
- package/dist/agent-src/rules/no-roadmap-references.md +4 -1
- package/dist/agent-src/rules/non-destructive-by-default.md +20 -0
- package/dist/agent-src/rules/onboarding-gate.md +2 -0
- package/dist/agent-src/rules/output-discipline.md +2 -0
- package/dist/agent-src/rules/persona-governance.md +2 -0
- package/dist/agent-src/rules/php-coding.md +1 -0
- package/dist/agent-src/rules/preservation-guard.md +3 -0
- package/dist/agent-src/rules/reviewer-awareness.md +1 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +9 -0
- package/dist/agent-src/rules/scale-discipline.md +92 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +106 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +11 -1
- package/dist/agent-src/rules/session-canary.md +77 -0
- package/dist/agent-src/rules/skill-quality.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +2 -0
- package/dist/agent-src/rules/source-of-truth.md +2 -0
- package/dist/agent-src/rules/telegraph-speak.md +2 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +3 -1
- package/dist/agent-src/rules/tool-safety.md +4 -1
- package/dist/agent-src/rules/untrusted-input-defense.md +10 -0
- package/dist/agent-src/rules/verify-before-complete.md +2 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +17 -1
- package/dist/agent-src/skills/agent-security-review/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +97 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +0 -3
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/design-review/SKILL.md +5 -6
- package/dist/agent-src/skills/design-tokens/SKILL.md +0 -1
- package/dist/agent-src/skills/gated-reach/SKILL.md +185 -0
- package/dist/agent-src/skills/gated-reach/evals/triggers.json +72 -0
- package/dist/agent-src/skills/history-design/SKILL.md +101 -0
- package/dist/agent-src/skills/history-design/evals/triggers.json +17 -0
- package/dist/agent-src/skills/humanizer/SKILL.md +3 -3
- package/dist/agent-src/skills/image-analyser/SKILL.md +14 -12
- package/dist/agent-src/skills/image-editing/SKILL.md +2 -1
- package/dist/agent-src/skills/image-generation/SKILL.md +2 -1
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/launch-readiness/SKILL.md +1 -1
- package/dist/agent-src/skills/license-compliance-audit/SKILL.md +161 -0
- package/dist/agent-src/skills/license-compliance-audit/evals/triggers.json +52 -0
- package/dist/agent-src/skills/license-compliance-borrow-check/SKILL.md +145 -0
- package/dist/agent-src/skills/license-compliance-borrow-check/evals/triggers.json +52 -0
- package/dist/agent-src/skills/license-compliance-credits/SKILL.md +97 -0
- package/dist/agent-src/skills/license-compliance-credits/evals/triggers.json +52 -0
- package/dist/agent-src/skills/md-language-check/SKILL.md +1 -1
- package/dist/agent-src/skills/override-management/SKILL.md +1 -1
- package/dist/agent-src/skills/prediction-pool-optimizer/SKILL.md +7 -7
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +1 -1
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -2
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +2 -2
- package/dist/agent-src/skills/readme-writing/SKILL.md +6 -1
- package/dist/agent-src/skills/recursive-verification/SKILL.md +6 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -3
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +4 -0
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +31 -12
- package/dist/agent-src/skills/rule-refactor/SKILL.md +3 -3
- package/dist/agent-src/skills/schema-review/SKILL.md +92 -0
- package/dist/agent-src/skills/schema-review/evals/triggers.json +17 -0
- package/dist/agent-src/skills/screenshot-hygiene/SKILL.md +171 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +43 -1
- package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/security-maturity-assessment/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -3
- package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/source-discovery/SKILL.md +5 -5
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +45 -85
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +5 -4
- package/dist/agent-src/skills/subagent-orchestration/prompts/adversarial-verification-council.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge-two-stage.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-and-judge.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-competitively.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-parallel.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-steps.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/do-in-worktrees.md +29 -0
- package/dist/agent-src/skills/subagent-orchestration/prompts/judge-with-debate.md +29 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +2 -3
- package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +4 -0
- package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +2 -3
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agents/.gitattributes.fragment +25 -6
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/command.md +3 -3
- package/dist/agent-src/templates/github-workflows/secret-scan.yml +65 -0
- package/dist/agent-src/templates/persona.md +2 -2
- package/dist/agent-src/templates/rule.md +2 -2
- package/dist/agent-src/templates/scripts/telemetry/engagement.ts +77 -0
- package/dist/agent-src/templates/scripts/telemetry_record.ts +33 -1
- package/dist/cli/commands/doctorShell.js +20 -0
- package/dist/cli/commands/doctorShell.js.map +1 -1
- package/dist/cli/commands/rtkDetect.js +58 -0
- package/dist/cli/commands/rtkDetect.js.map +1 -0
- package/dist/cli/configRoot.js +112 -0
- package/dist/cli/configRoot.js.map +1 -0
- package/dist/cli/configRoot.test.js +97 -0
- package/dist/cli/configRoot.test.js.map +1 -0
- package/dist/cli/firstRunNotice.js +70 -0
- package/dist/cli/firstRunNotice.js.map +1 -0
- package/dist/cli/help.js +4 -1
- package/dist/cli/help.js.map +1 -1
- package/dist/cli/main.js +54 -5
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/registry.js +8 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +502 -101
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +6 -4
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +95 -8
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +36 -7
- package/dist/hooks/dispatch.js +19475 -0
- package/dist/install/agentSwitchDetection.js +75 -0
- package/dist/install/agentSwitchDetection.js.map +1 -0
- package/dist/install/agentSwitchProfile.js +117 -0
- package/dist/install/agentSwitchProfile.js.map +1 -0
- package/dist/install/atomic.js +1 -1
- package/dist/install/detect.js +1 -1
- package/dist/install/install.mjs +791 -486
- package/dist/install/rtkDetection.js +213 -0
- package/dist/install/rtkDetection.js.map +1 -0
- package/dist/install/rule_scope.js +2 -1
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/install/types.js +2 -2
- package/dist/install/wizard-plan.js +1 -1
- package/dist/install/wizardDismissals.js +58 -0
- package/dist/install/wizardDismissals.js.map +1 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +1 -1
- package/dist/mcp/registry-manifest.json +15 -4
- package/dist/mcp/server.json +20 -0
- package/dist/mcp/server.mjs +44292 -0
- package/dist/router.json +1 -1
- package/dist/scripts/condense.js +14 -7
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/app.js +12 -0
- package/dist/server/app.js.map +1 -1
- package/dist/server/io/sharedWriteCheck.js +101 -0
- package/dist/server/io/sharedWriteCheck.js.map +1 -0
- package/dist/server/routes/ping.js +32 -0
- package/dist/server/routes/ping.js.map +1 -1
- package/dist/server/routes/settings.js +21 -4
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/userMd.js +21 -4
- package/dist/server/routes/userMd.js.map +1 -1
- package/dist/server/routes/wizard.js +68 -25
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/server/schemas/settings.js +12 -2
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/server/serverInfo.js +6 -2
- package/dist/server/serverInfo.js.map +1 -1
- package/dist/server/token.js +11 -4
- package/dist/server/token.js.map +1 -1
- package/dist/server/writeRoot.js +15 -1
- package/dist/server/writeRoot.js.map +1 -1
- package/dist/shared/capabilities.js +33 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/capabilities.test.js +39 -0
- package/dist/shared/capabilities.test.js.map +1 -0
- package/dist/ui/assets/index-BpSQt5vm.js +40 -0
- package/dist/ui/assets/index-BpSQt5vm.js.map +1 -0
- package/dist/ui/assets/index-jSfMW8_t.css +1 -0
- package/dist/ui/index.html +15 -6
- package/docs/MIGRATION.md +16 -0
- package/docs/guidelines/component-oriented-and-oop-development.md +4 -2
- package/package.json +22 -9
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/telemetry/engagement.ts +77 -0
- package/src/agent-src/templates/scripts/telemetry_record.ts +33 -1
- package/src/config/agent-settings.template.yml +94 -13
- package/src/config/discovery/packs.yml +22 -0
- package/src/config/discovery/workspaces.yml +1 -1
- package/src/config/evaluator-budgets.json +47 -0
- package/src/config/hook-latency-budget.json +20 -0
- package/src/config/reach-channels.yml +217 -0
- package/src/config/reach-prescriptions-intake.yml +200 -0
- package/src/install/agentSwitchDetection.ts +90 -0
- package/src/install/agentSwitchProfile.ts +131 -0
- package/src/install/atomic.ts +1 -1
- package/src/install/detect.ts +1 -1
- package/src/install/rtkDetection.ts +274 -0
- package/src/install/rule_scope.ts +9 -2
- package/src/install/types.ts +2 -2
- package/src/install/wizard-plan.ts +1 -1
- package/src/install/wizardDismissals.ts +60 -0
- package/src/scripts/_cli/cmd_doctor.ts +62 -1
- package/src/scripts/_cli/cmd_memory_get.ts +197 -0
- package/src/scripts/_cli/cmd_refresh.ts +30 -0
- package/src/scripts/_dispatch.bash +96 -0
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +64 -1
- package/src/scripts/_lib/auto_dispatch.ts +76 -0
- package/src/scripts/_lib/detect_target_license.ts +714 -0
- package/src/scripts/_lib/exec_evidence.ts +251 -0
- package/src/scripts/_lib/fixtures/secret-detector/negatives.txt +15 -0
- package/src/scripts/_lib/fixtures/secret-detector/positives.txt +15 -0
- package/src/scripts/_lib/install_drift.ts +194 -0
- package/src/scripts/_lib/kernel_rules.ts +46 -0
- package/src/scripts/_lib/memory_fts_index.ts +390 -0
- package/src/scripts/_lib/orchestration_record.ts +91 -2
- package/src/scripts/_lib/persistence/adapter_raw_sql.ts +443 -0
- package/src/scripts/_lib/persistence/detect_audit_coverage.ts +423 -0
- package/src/scripts/_lib/persistence/detect_bounded_reads.ts +154 -0
- package/src/scripts/_lib/persistence/detect_index_parity.ts +576 -0
- package/src/scripts/_lib/persistence/detect_n1_eloquent.ts +226 -0
- package/src/scripts/_lib/persistence/detect_offload.ts +441 -0
- package/src/scripts/_lib/persistence/detect_prisma_growth.ts +74 -0
- package/src/scripts/_lib/persistence/offload_catalog.ts +73 -0
- package/src/scripts/_lib/persistence/types.ts +109 -0
- package/src/scripts/_lib/reddit_thread_parse.ts +598 -0
- package/src/scripts/_lib/risk_paths.ts +160 -0
- package/src/scripts/_lib/rtk_allowlist.ts +44 -0
- package/src/scripts/_lib/secret_detector.test.ts +108 -0
- package/src/scripts/_lib/secret_detector.ts +328 -0
- package/src/scripts/_lib/secret_detector_adversarial.test.ts +116 -0
- package/src/scripts/_lib/spawn_env.ts +7 -0
- package/src/scripts/_lib/sqlite_guard.ts +191 -0
- package/src/scripts/_lib/subagent_spawn.ts +53 -0
- package/src/scripts/_lib/tool_probe.ts +359 -0
- package/src/scripts/_lib/worker_budget.ts +107 -0
- package/src/scripts/adr/regenerate_index.ts +81 -6
- package/src/scripts/ai_council/blind_review.ts +0 -0
- package/src/scripts/ai_council/config.ts +12 -6
- package/src/scripts/ai_council/orchestrator.ts +73 -3
- package/src/scripts/ai_team/review_gate.ts +8 -10
- package/src/scripts/ai_team/team_dispatch.ts +48 -0
- package/src/scripts/audit_adr_coverage.ts +33 -0
- package/src/scripts/audit_initial_context.ts +1 -1
- package/src/scripts/bench_cross_source_eval.ts +400 -0
- package/src/scripts/bench_honesty_score.ts +1093 -0
- package/src/scripts/bench_hook_latency.ts +218 -0
- package/src/scripts/build_mcp_catalog.ts +275 -0
- package/src/scripts/build_mcp_registry_manifest.ts +46 -0
- package/src/scripts/build_proof.ts +207 -0
- package/src/scripts/chat_history.ts +8 -5
- package/src/scripts/check_adr_frontmatter.ts +195 -0
- package/src/scripts/check_artefact_count_messaging.ts +4 -0
- package/src/scripts/check_backstop_debt.ts +210 -0
- package/src/scripts/check_bundle_path_leakage.ts +343 -0
- package/src/scripts/check_claims.ts +188 -8
- package/src/scripts/check_comparison.ts +12 -0
- package/src/scripts/check_enforcement_coverage.ts +592 -0
- package/src/scripts/check_evaluator_budgets.ts +99 -0
- package/src/scripts/check_kernel_rule_bundle.ts +16 -12
- package/src/scripts/check_knowledge_sharing.ts +12 -1
- package/src/scripts/check_no_external_sources.ts +2 -2
- package/src/scripts/check_reach_channels.ts +407 -0
- package/src/scripts/check_reach_staleness.ts +364 -0
- package/src/scripts/check_secret_leak.test.ts +47 -0
- package/src/scripts/check_secret_leak.ts +290 -0
- package/src/scripts/check_source_pointer_freshness.ts +28 -3
- package/src/scripts/code_graph/build.ts +0 -0
- package/src/scripts/code_graph/cli.ts +379 -0
- package/src/scripts/code_graph/detect.ts +186 -0
- package/src/scripts/code_graph/extract.ts +386 -0
- package/src/scripts/code_graph/intent.ts +45 -0
- package/src/scripts/code_graph/loader.ts +123 -0
- package/src/scripts/code_graph/query.ts +290 -0
- package/src/scripts/code_graph/sanitize.ts +30 -0
- package/src/scripts/code_graph/sqlite_store.ts +174 -0
- package/src/scripts/code_graph/types.ts +83 -0
- package/src/scripts/code_graph/validate.ts +63 -0
- package/src/scripts/condense.ts +14 -6
- package/src/scripts/context_hygiene_hook.ts +8 -5
- package/src/scripts/council_cli.ts +126 -12
- package/src/scripts/detect_target_license.ts +203 -0
- package/src/scripts/evaluator_umbrella.sh +145 -0
- package/src/scripts/external_sources_denylist.json +8 -4
- package/src/scripts/first_run_gate_hook.ts +9 -5
- package/src/scripts/generate_subagent_floor.ts +194 -0
- package/src/scripts/hermetic-install.sh +3 -3
- package/src/scripts/hook_manifest.yaml +65 -16
- package/src/scripts/hooks/block_kernel_rule_writes.ts +212 -0
- package/src/scripts/hooks/block_no_verify.ts +9 -9
- package/src/scripts/hooks/code_graph_nudge_hook.ts +191 -0
- package/src/scripts/hooks/concern_registry.ts +75 -0
- package/src/scripts/hooks/design_slop_hook.ts +8 -5
- package/src/scripts/hooks/dispatch_entry.ts +37 -0
- package/src/scripts/hooks/dispatch_hook.ts +176 -3
- package/src/scripts/hooks/hook_stdin.ts +42 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +26 -12
- package/src/scripts/hot_context_hook.ts +7 -6
- package/src/scripts/injection_scan_hook.ts +9 -1
- package/src/scripts/install +6 -6
- package/src/scripts/install-hooks.sh +4 -4
- package/src/scripts/install.sh +5 -4
- package/src/scripts/install.ts +264 -23
- package/src/scripts/iron_law_sha.ts +3 -12
- package/src/scripts/learning_sidecar.ts +0 -0
- package/src/scripts/lint_budget_ownership.ts +89 -0
- package/src/scripts/lint_command_routing.ts +35 -5
- package/src/scripts/lint_command_verbs.ts +42 -9
- package/src/scripts/lint_consumer_internal_refs.ts +211 -0
- package/src/scripts/lint_discovery_vocabulary.ts +2 -0
- package/src/scripts/lint_documented_commands.ts +670 -0
- package/src/scripts/lint_framework_leakage.ts +17 -3
- package/src/scripts/lint_framework_leakage_allowlist.json +32 -0
- package/src/scripts/lint_governed_writes.ts +335 -0
- package/src/scripts/lint_hidden_unicode.ts +6 -0
- package/src/scripts/lint_mcp_registry_manifest.ts +26 -1
- package/src/scripts/lint_memory_tripwire.ts +186 -0
- package/src/scripts/lint_override_kernel_guard.ts +267 -0
- package/src/scripts/lint_persistence.ts +171 -0
- package/src/scripts/lint_pre_migration_refs.ts +106 -0
- package/src/scripts/lint_provenance.ts +547 -0
- package/src/scripts/lint_provenance_vocabulary.ts +437 -0
- package/src/scripts/lint_spawn_payload.ts +283 -0
- package/src/scripts/lint_store_boundary.ts +152 -0
- package/src/scripts/lint_versioned_cache.ts +7 -1
- package/src/scripts/mcp_server/__main__.ts +7 -2
- package/src/scripts/mcp_server/catalog.ts +23 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +563 -193
- package/src/scripts/mcp_server/prompts.ts +6 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +332 -0
- package/src/scripts/mcp_server/tools.ts +56 -5
- package/src/scripts/mcp_telemetry_query.ts +2 -33
- package/src/scripts/mcp_telemetry_store.ts +64 -34
- package/src/scripts/measure_rule_budget.ts +3 -12
- package/src/scripts/memory_learn_hook.ts +126 -0
- package/src/scripts/memory_lookup.ts +0 -0
- package/src/scripts/memory_replay_24.ts +312 -0
- package/src/scripts/memory_report.ts +16 -3
- package/src/scripts/memory_signal.ts +48 -0
- package/src/scripts/memory_status.ts +6 -0
- package/src/scripts/minimal_safe_diff_hook.ts +8 -5
- package/src/scripts/onboarding_gate_hook.ts +8 -5
- package/src/scripts/orchestration_record.ts +16 -0
- package/src/scripts/pr_url_reminder_hook.ts +8 -1
- package/src/scripts/prepack-check.mjs +17 -0
- package/src/scripts/prepack_lifecycle_check.d.mts +8 -0
- package/src/scripts/prepack_lifecycle_check.mjs +82 -0
- package/src/scripts/profile_staleness_hook.ts +8 -7
- package/src/scripts/reach_doctor.ts +1651 -0
- package/src/scripts/release.ts +24 -3
- package/src/scripts/report_install_drift.ts +109 -0
- package/src/scripts/risk_escalation_shadow.ts +160 -0
- package/src/scripts/roadmap_progress_hook.ts +8 -5
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/license-policy.schema.json +89 -0
- package/src/scripts/schemas/provenance-borrow.schema.json +46 -0
- package/src/scripts/schemas/reach-channels.schema.json +134 -0
- package/src/scripts/schemas/reach-doctor-payload.schema.json +220 -0
- package/src/scripts/schemas/rule.schema.json +20 -0
- package/src/scripts/self_review_gate.test.ts +173 -1
- package/src/scripts/self_review_gate.ts +155 -15
- package/src/scripts/session_canary_hook.ts +182 -0
- package/src/scripts/skill_linter.ts +55 -0
- package/src/scripts/smoke_quickstart.ts +2 -2
- package/src/scripts/surface_probe_hook.ts +8 -7
- package/src/scripts/sync_gitattributes.ts +310 -0
- package/src/scripts/team_review_gate_hook.ts +13 -16
- package/src/scripts/validate_reach_prescriptions.ts +751 -0
- package/src/scripts/verify_before_complete_hook.ts +8 -5
- package/src/scripts/wrapper_freshness_hook.ts +8 -7
- package/src/server/schemas/settings.ts +24 -2
- package/src/shared/capabilities.test.ts +43 -0
- package/src/shared/capabilities.ts +75 -0
- package/src/templates/minimal/agents-overrides-readme.md +20 -0
- package/dist/ui/assets/index-BPFMZWUs.css +0 -1
- package/dist/ui/assets/index-BiEZiZ31.js +0 -40
- package/dist/ui/assets/index-BiEZiZ31.js.map +0 -1
- package/docs/CLAIMS.md +0 -261
- package/docs/DISTRIBUTION_CHECKLIST.md +0 -170
- package/docs/RELEASE_STORY_TEMPLATE.md +0 -37
- package/docs/SKILL_CENSUS.md +0 -352
- package/docs/adrs/cost/0001-hard-stop-hook.md +0 -114
- package/docs/adrs/cost/README.md +0 -9
- package/docs/adrs/memory/0001-consumer-side-snapshot.md +0 -119
- package/docs/adrs/memory/README.md +0 -10
- package/docs/adrs/router/0001-three-tier-routing.md +0 -119
- package/docs/adrs/router/README.md +0 -9
- package/docs/adrs/schema/0001-json-schema-frontmatter.md +0 -102
- package/docs/adrs/schema/README.md +0 -9
- package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +0 -99
- package/docs/adrs/smoke/README.md +0 -9
- package/docs/adrs/telegraph/0001-default-off-until-bench.md +0 -93
- package/docs/adrs/telegraph/README.md +0 -9
- package/docs/announcements/2026-05-non-dev-launch.md +0 -79
- package/docs/announcements/2026-07-honest-cost-sweep-launch.md +0 -84
- package/docs/anti-reskin-gate.md +0 -60
- package/docs/architecture/augment-projection.md +0 -70
- package/docs/architecture/claude-bundle.md +0 -77
- package/docs/architecture/current-onboard-baseline.md +0 -126
- package/docs/architecture/current-safety-behavior.md +0 -137
- package/docs/architecture/multi-tool-projection.md +0 -125
- package/docs/architecture/setup-vs-settings-shared-surface.md +0 -114
- package/docs/architecture/source-projection.md +0 -85
- package/docs/architecture.md +0 -390
- package/docs/archive/CHANGELOG-6.0.0-overview.md +0 -953
- package/docs/archive/CHANGELOG-7.0.0.md +0 -419
- package/docs/archive/CHANGELOG-pre-2.11.0.md +0 -141
- package/docs/archive/CHANGELOG-pre-2.15.0.md +0 -244
- package/docs/archive/CHANGELOG-pre-2.16.0.md +0 -48
- package/docs/archive/CHANGELOG-pre-2.17.0.md +0 -63
- package/docs/archive/CHANGELOG-pre-2.2.0.md +0 -2138
- package/docs/archive/CHANGELOG-pre-2.20.0.md +0 -159
- package/docs/archive/CHANGELOG-pre-2.25.0.md +0 -191
- package/docs/archive/CHANGELOG-pre-2.7.0.md +0 -185
- package/docs/archive/CHANGELOG-pre-3.0.0.md +0 -130
- package/docs/archive/CHANGELOG-pre-3.1.0.md +0 -167
- package/docs/archive/CHANGELOG-pre-3.2.0.md +0 -268
- package/docs/archive/CHANGELOG-pre-4.0.0.md +0 -80
- package/docs/archive/CHANGELOG-pre-4.5.0.md +0 -289
- package/docs/archive/CHANGELOG-pre-5.4.0.md +0 -260
- package/docs/archive/CHANGELOG-pre-5.9.0.md +0 -270
- package/docs/archive/CHANGELOG-pre-6.0.0.md +0 -473
- package/docs/archive/CHANGELOG-pre-7.0.0.md +0 -126
- package/docs/archive/CHANGELOG-pre-8.0.0.md +0 -301
- package/docs/archive/CHANGELOG-pre-8.1.0.md +0 -316
- package/docs/archive/CHANGELOG-pre-8.12.0.md +0 -304
- package/docs/archive/CHANGELOG-pre-8.9.0.md +0 -306
- package/docs/archive/CHANGELOG-pre-9.2.0.md +0 -276
- package/docs/artefact-census.md +0 -71
- package/docs/benchmark.md +0 -648
- package/docs/benchmark.pinned.yml +0 -31
- package/docs/benchmarks.md +0 -85
- package/docs/capability-matrix.md +0 -33
- package/docs/case-studies/_template.md +0 -60
- package/docs/case-studies/frontend-design-positioning.md +0 -86
- package/docs/catalog.md +0 -721
- package/docs/command-flows.md +0 -272
- package/docs/command-naming-audit.md +0 -60
- package/docs/comparison.yaml +0 -45
- package/docs/contracts/CHANGELOG-conventions.md +0 -149
- package/docs/contracts/STABILITY.md +0 -188
- package/docs/contracts/adoption-signal-floor.md +0 -127
- package/docs/contracts/adr-architectural-consensus-mechanism.md +0 -68
- package/docs/contracts/adr-chat-history-split.md +0 -140
- package/docs/contracts/adr-command-suggestion.md +0 -144
- package/docs/contracts/adr-forecast-construction-shape.md +0 -90
- package/docs/contracts/adr-gtm-context-spine.md +0 -116
- package/docs/contracts/adr-implement-ticket-runtime.md +0 -121
- package/docs/contracts/adr-install-user-type-axis.md +0 -107
- package/docs/contracts/adr-layout.md +0 -107
- package/docs/contracts/adr-level-6-productization.md +0 -145
- package/docs/contracts/adr-mcp-runtime.md +0 -128
- package/docs/contracts/adr-product-ui-track.md +0 -381
- package/docs/contracts/adr-prompt-driven-execution.md +0 -186
- package/docs/contracts/adr-settings-sync-engine.md +0 -128
- package/docs/contracts/adr-user-types-axis.md +0 -127
- package/docs/contracts/adr-wing4-context-spine.md +0 -126
- package/docs/contracts/agent-user-schema.md +0 -170
- package/docs/contracts/agents-layout.md +0 -148
- package/docs/contracts/agents-md-tech-stack.md +0 -75
- package/docs/contracts/ai-council-config.md +0 -829
- package/docs/contracts/ai-team-config.md +0 -224
- package/docs/contracts/analysis-memory-loop.md +0 -149
- package/docs/contracts/at-rest-encryption.md +0 -191
- package/docs/contracts/audit-log-v1.md +0 -150
- package/docs/contracts/auto-orchestration-v1.md +0 -70
- package/docs/contracts/benchmark-ab-contract.md +0 -106
- package/docs/contracts/benchmark-corpus-spec.md +0 -125
- package/docs/contracts/benchmark-report-schema.md +0 -113
- package/docs/contracts/branch-protection-policy.md +0 -139
- package/docs/contracts/brand-token-consumption.md +0 -74
- package/docs/contracts/capability-boundary.md +0 -69
- package/docs/contracts/capability-packs.md +0 -217
- package/docs/contracts/ci-cost-budget.md +0 -111
- package/docs/contracts/ci-green-floor.md +0 -141
- package/docs/contracts/command-category-governance.md +0 -84
- package/docs/contracts/command-clusters.md +0 -399
- package/docs/contracts/command-surface-tiers.md +0 -176
- package/docs/contracts/command-taxonomy.md +0 -137
- package/docs/contracts/condensation-default-kill-criterion.md +0 -116
- package/docs/contracts/config-presets.md +0 -144
- package/docs/contracts/conformance.md +0 -84
- package/docs/contracts/consumer-bridge.md +0 -73
- package/docs/contracts/context-paths.md +0 -101
- package/docs/contracts/context-spine.md +0 -179
- package/docs/contracts/cost-dashboard.md +0 -143
- package/docs/contracts/cost-enforcement.md +0 -134
- package/docs/contracts/cost-profile-defaults.md +0 -110
- package/docs/contracts/cost-summary-schema.md +0 -107
- package/docs/contracts/creative-pack-boundary.md +0 -63
- package/docs/contracts/cross-repo-retrieval.md +0 -64
- package/docs/contracts/cross-wing-handoff.md +0 -134
- package/docs/contracts/daily-workspace.md +0 -147
- package/docs/contracts/decision-engine-gates.md +0 -120
- package/docs/contracts/decision-trace-v1.md +0 -177
- package/docs/contracts/design-artifact-lifecycle.md +0 -93
- package/docs/contracts/design-artifact-verification.md +0 -153
- package/docs/contracts/discovery-manifest.md +0 -209
- package/docs/contracts/discovery-manifest.schema.json +0 -254
- package/docs/contracts/evidence-based-pruning.md +0 -102
- package/docs/contracts/explain-modes.md +0 -187
- package/docs/contracts/explain-trace.schema.json +0 -144
- package/docs/contracts/file-ownership-matrix.json +0 -13941
- package/docs/contracts/file-ownership-matrix.md +0 -135
- package/docs/contracts/frontmatter-contract.md +0 -140
- package/docs/contracts/ghostwriter-schema.md +0 -337
- package/docs/contracts/governance-enforcement-projection.md +0 -159
- package/docs/contracts/gui-wizard.md +0 -350
- package/docs/contracts/harness-expectations.md +0 -128
- package/docs/contracts/hook-architecture-v1.md +0 -325
- package/docs/contracts/host-agent-protocol.md +0 -110
- package/docs/contracts/implement-ticket-flow.md +0 -625
- package/docs/contracts/init-telemetry.md +0 -132
- package/docs/contracts/injected-block-authenticity-model.md +0 -66
- package/docs/contracts/install-layout.md +0 -249
- package/docs/contracts/install-scopes.md +0 -82
- package/docs/contracts/installed-tools-lockfile.md +0 -144
- package/docs/contracts/installer-agent-mode.md +0 -137
- package/docs/contracts/iron-law-overrides.txt +0 -26
- package/docs/contracts/kernel-membership.md +0 -339
- package/docs/contracts/linear-ai-rules-inclusion.md +0 -143
- package/docs/contracts/linear-ai-three-layers.md +0 -130
- package/docs/contracts/linter-structural-model.md +0 -181
- package/docs/contracts/load-context-budget-model.md +0 -259
- package/docs/contracts/load-context-schema.md +0 -220
- package/docs/contracts/local-analytics.md +0 -160
- package/docs/contracts/local-knowledge-ingestion.md +0 -96
- package/docs/contracts/local-server-api.md +0 -134
- package/docs/contracts/low-impact-corpus-format.md +0 -95
- package/docs/contracts/mcp-beta-criteria.md +0 -130
- package/docs/contracts/mcp-cloud-scope.md +0 -355
- package/docs/contracts/mcp-discovery-phase-notice.md +0 -60
- package/docs/contracts/mcp-phase-1-scope.md +0 -272
- package/docs/contracts/mcp-registry-manifest.schema.json +0 -129
- package/docs/contracts/mcp-tool-inventory.md +0 -64
- package/docs/contracts/mcp-tool-stub-envelope.md +0 -78
- package/docs/contracts/measurement-baseline.md +0 -103
- package/docs/contracts/memory-visibility-v1.md +0 -160
- package/docs/contracts/mental-models.md +0 -336
- package/docs/contracts/migrate-command.md +0 -233
- package/docs/contracts/multi-tool-projection-fidelity.md +0 -134
- package/docs/contracts/namespace.md +0 -125
- package/docs/contracts/no-runtime-boundary.md +0 -61
- package/docs/contracts/one-off-script-lifecycle.md +0 -110
- package/docs/contracts/orchestration-dsl-v1.md +0 -153
- package/docs/contracts/package-self-orientation.md +0 -163
- package/docs/contracts/pilot/agent-authority.md +0 -24
- package/docs/contracts/pilot/direct-answers.md +0 -70
- package/docs/contracts/pilot/language-and-tone.md +0 -63
- package/docs/contracts/plain-language-surface.md +0 -117
- package/docs/contracts/prelaunch-diagnostics.md +0 -151
- package/docs/contracts/profile-system.md +0 -167
- package/docs/contracts/reasoning-discipline-protocol.md +0 -83
- package/docs/contracts/release-pr-gating.md +0 -151
- package/docs/contracts/release-sizing.md +0 -107
- package/docs/contracts/release-trunk-sync.md +0 -105
- package/docs/contracts/review-lens-schema.md +0 -64
- package/docs/contracts/roadmap-complexity-standard.md +0 -138
- package/docs/contracts/role-experience.md +0 -125
- package/docs/contracts/router-blending.md +0 -71
- package/docs/contracts/rule-classification.md +0 -171
- package/docs/contracts/rule-interactions.md +0 -132
- package/docs/contracts/rule-interactions.yml +0 -260
- package/docs/contracts/rule-priority-hierarchy.md +0 -88
- package/docs/contracts/rule-router.md +0 -287
- package/docs/contracts/safety-model.md +0 -129
- package/docs/contracts/session-profile-overlay.md +0 -157
- package/docs/contracts/settings-api.md +0 -264
- package/docs/contracts/settings-gui-agent-mode.schema.json +0 -128
- package/docs/contracts/settings-sync-yaml-subset.md +0 -139
- package/docs/contracts/skill-bundled-assets.md +0 -62
- package/docs/contracts/skill-discovery.md +0 -80
- package/docs/contracts/skill-distribution-channels.md +0 -66
- package/docs/contracts/skill-domains.md +0 -144
- package/docs/contracts/skill-dry-run.md +0 -47
- package/docs/contracts/skill-family-map.yml +0 -915
- package/docs/contracts/smoke-contracts.md +0 -144
- package/docs/contracts/subagent-boundary.md +0 -59
- package/docs/contracts/surface-agent-contracts.md +0 -153
- package/docs/contracts/surface-matrix.md +0 -108
- package/docs/contracts/surface-tiers.md +0 -81
- package/docs/contracts/telegraph-telemetry.md +0 -83
- package/docs/contracts/ticket-bundle-format.md +0 -233
- package/docs/contracts/tier-3-contrib-plugin.md +0 -137
- package/docs/contracts/trust-and-safety.md +0 -144
- package/docs/contracts/ui-stack-extension.md +0 -237
- package/docs/contracts/ui-track-flow.md +0 -329
- package/docs/contracts/universal-skills.md +0 -91
- package/docs/contracts/user-type-schema.md +0 -146
- package/docs/contracts/value-dashboard-spec.md +0 -378
- package/docs/contracts/value-report-schema.md +0 -168
- package/docs/contracts/workflow-packs.md +0 -121
- package/docs/contracts/workspace-boundary.md +0 -84
- package/docs/contracts/workspace-documents.md +0 -144
- package/docs/contracts/write-engine.md +0 -182
- package/docs/cookbook.md +0 -152
- package/docs/customization.md +0 -685
- package/docs/decisions/ADR-001-kernel-swap-deferred.md +0 -109
- package/docs/decisions/ADR-002-kernel-bucket-overrides.md +0 -135
- package/docs/decisions/ADR-003-flat-cluster-subs-and-colon-syntax.md +0 -137
- package/docs/decisions/ADR-004-rule-governance-pruning.md +0 -240
- package/docs/decisions/ADR-005-subagent-worktrees.md +0 -120
- package/docs/decisions/ADR-006-skill-tools-python-pilot.md +0 -114
- package/docs/decisions/ADR-007-agent-discovery-scopes.md +0 -371
- package/docs/decisions/ADR-008-installed-tools-manifest.md +0 -160
- package/docs/decisions/ADR-009-event4u-namespace.md +0 -188
- package/docs/decisions/ADR-010-profile-pack-preset-boundary.md +0 -168
- package/docs/decisions/ADR-011-domain-pack-readiness.md +0 -213
- package/docs/decisions/ADR-012-typescript-cli-shell.md +0 -162
- package/docs/decisions/ADR-013-discovery-frontmatter-contract.md +0 -342
- package/docs/decisions/ADR-014-gui-framework-choice.md +0 -136
- package/docs/decisions/ADR-015-discovery-manifest-contract.md +0 -146
- package/docs/decisions/ADR-016-installer-architecture.md +0 -189
- package/docs/decisions/ADR-017-monorepo-physical-layout.md +0 -261
- package/docs/decisions/ADR-018-trust-and-safety-layer.md +0 -159
- package/docs/decisions/ADR-019-router-json-dist-location.md +0 -124
- package/docs/decisions/ADR-020-global-only-consumer-scope.md +0 -213
- package/docs/decisions/ADR-021-deployment-shape.md +0 -153
- package/docs/decisions/ADR-022-daily-workspace-decomposition.md +0 -140
- package/docs/decisions/ADR-023-host-agent-protocol.md +0 -129
- package/docs/decisions/ADR-024-workspace-v0-feature-floor.md +0 -126
- package/docs/decisions/ADR-025-workspace-chrome.md +0 -119
- package/docs/decisions/ADR-026-explain-mode-translation.md +0 -117
- package/docs/decisions/ADR-027-changelog-machine-vs-manual.md +0 -129
- package/docs/decisions/ADR-028-root-layout.md +0 -147
- package/docs/decisions/ADR-029-multi-workspace-deferred.md +0 -122
- package/docs/decisions/ADR-030-claude-code-command-projection.md +0 -147
- package/docs/decisions/ADR-031-validation-severity-tiers-and-projection-roundtrip.md +0 -97
- package/docs/decisions/ADR-032-linked-projects-scope.md +0 -122
- package/docs/decisions/ADR-033-distribution-identity-npm-primary.md +0 -81
- package/docs/decisions/ADR-034-per-skill-model-recommendation-transport.md +0 -63
- package/docs/decisions/ADR-035-model-capability-tiers.md +0 -104
- package/docs/decisions/ADR-036-global-install-browser-wizard-handoff.md +0 -106
- package/docs/decisions/ADR-037-cost-profile-untangle.md +0 -117
- package/docs/decisions/ADR-038-canonical-settings-path.md +0 -96
- package/docs/decisions/ADR-039-claude-skills-untracked.md +0 -139
- package/docs/decisions/ADR-040-execution-model-projection-time-filtering.md +0 -244
- package/docs/decisions/ADR-041-controlled-command-verbs.md +0 -133
- package/docs/decisions/ADR-042-runtime-resolver-decision-gate.md +0 -113
- package/docs/decisions/ADR-043-monorepo-collapse-to-src-domains.md +0 -128
- package/docs/decisions/ADR-044-command-naming-scheme-hyphenated.md +0 -250
- package/docs/decisions/ADR-045-src-source-layout-and-profiles-as-views.md +0 -111
- package/docs/decisions/ADR-046-thin-command-principle.md +0 -93
- package/docs/decisions/ADR-047-framework-neutral-stack-adaptive-commands.md +0 -94
- package/docs/decisions/ADR-048-command-justification-rule.md +0 -106
- package/docs/decisions/ADR-049-configuration-trust-boundary.md +0 -132
- package/docs/decisions/ADR-050-workspace-vs-package-root-boundary.md +0 -121
- package/docs/decisions/ADR-051-uncondensed-source-container-relocation.md +0 -114
- package/docs/decisions/ADR-052-packages-removal-and-pack-home-migration.md +0 -86
- package/docs/decisions/ADR-053-agents-namespace-dual-role-resolution.md +0 -132
- package/docs/decisions/ADR-054-rule-adherence-decay-triggered-restate.md +0 -143
- package/docs/decisions/ADR-055-flow-layer-data-model.md +0 -235
- package/docs/decisions/ADR-056-unvalidated-video-adapters-disposition.md +0 -124
- package/docs/decisions/ADR-057-consolidation-evidence-gate-outcomes.md +0 -135
- package/docs/decisions/ADR-058-condensed-output-relocation-to-dist.md +0 -94
- package/docs/decisions/ADR-059-render-resume-filesystem-as-state.md +0 -161
- package/docs/decisions/ADR-060-comfyui-sandbox-model.md +0 -154
- package/docs/decisions/ADR-061-corpus-grounding-layer.md +0 -230
- package/docs/decisions/ADR-062-encrypt-at-rest-store-architecture.md +0 -121
- package/docs/decisions/ADR-063-append-jsonl-encryption-deferred.md +0 -83
- package/docs/decisions/ADR-064-append-jsonl-per-record-encryption.md +0 -113
- package/docs/decisions/ADR-065-tier3-inbox-handoff-v0.md +0 -86
- package/docs/decisions/ADR-066-skill-body-prerendering.md +0 -93
- package/docs/decisions/ADR-067-glama-registry-listing.md +0 -259
- package/docs/decisions/ADR-068-host-tier-detection.md +0 -90
- package/docs/decisions/ADR-069-prompt-renderer.md +0 -97
- package/docs/decisions/ADR-070-tier1-drive-loop.md +0 -94
- package/docs/decisions/ADR-071-launch-drive-integration.md +0 -95
- package/docs/decisions/ADR-072-codex-gemini-drive-configs.md +0 -90
- package/docs/decisions/ADR-073-drive-health-kill-switch.md +0 -81
- package/docs/decisions/ADR-074-drive-kill-switch-auto-recovery.md +0 -84
- package/docs/decisions/ADR-075-workspace-gui-drive.md +0 -83
- package/docs/decisions/ADR-076-workspace-multi-turn.md +0 -92
- package/docs/decisions/ADR-077-workspace-followup-gui.md +0 -70
- package/docs/decisions/ADR-078-drive-health-panel.md +0 -70
- package/docs/decisions/ADR-079-workspace-host-picker.md +0 -81
- package/docs/decisions/ADR-080-host-session-expired-410.md +0 -92
- package/docs/decisions/ADR-081-drive-health-reset-and-410-affordance.md +0 -71
- package/docs/decisions/ADR-082-410-one-click-relaunch.md +0 -58
- package/docs/decisions/ADR-083-session-thread-and-arbitrary-continuation.md +0 -85
- package/docs/decisions/ADR-084-drive-health-refresh-on-drive.md +0 -52
- package/docs/decisions/ADR-085-mcp-stdio-end-user-distribution-shape.md +0 -158
- package/docs/decisions/ADR-086-read-only-cross-agent-mcp-discovery-helper.md +0 -171
- package/docs/decisions/ADR-087-installer-e2e-test-strategy.md +0 -111
- package/docs/decisions/ADR-088-no-external-runtime-federation.md +0 -144
- package/docs/decisions/ADR-089-lean-local-plugin-install.md +0 -111
- package/docs/decisions/ADR-090-visibility-command-frontmatter-field.md +0 -95
- package/docs/decisions/ADR-091-split-meta-capability-packs.md +0 -113
- package/docs/decisions/ADR-092-defer-command-tier-alias-removal.md +0 -117
- package/docs/decisions/ADR-093-ai-council-config-user-global.md +0 -117
- package/docs/decisions/ADR-094-agent-memory-layer-removal.md +0 -94
- package/docs/decisions/ADR-095-workspace-boundary-contract.md +0 -108
- package/docs/decisions/ADR-096-analysis-workbench.md +0 -110
- package/docs/decisions/ADR-097-mission-recipe-privilege-boundary.md +0 -121
- package/docs/decisions/ADR-098-evidence-first-structure-discovery.md +0 -154
- package/docs/decisions/ADR-099-file-first-pattern-library.md +0 -87
- package/docs/decisions/ADR-100-global-knowledge-card-sharing.md +0 -140
- package/docs/decisions/ADR-101-ticket-bundle-emission.md +0 -109
- package/docs/decisions/ADR-102-ticket-handoff-paste-and-mcp.md +0 -72
- package/docs/decisions/ADR-103-global-knowledge-default-off-until-measured.md +0 -92
- package/docs/decisions/ADR-104-ai-council-config-global-only.md +0 -119
- package/docs/decisions/ADR-105-automatic-subagent-orchestration.md +0 -108
- package/docs/decisions/ADR-106-recursive-verification-benchmark-gate.md +0 -126
- package/docs/decisions/ADR-107-legal-domain-pack-adoption.md +0 -70
- package/docs/decisions/ADR-108-open-source-forever-no-commercial-tier.md +0 -46
- package/docs/decisions/ADR-109-subagent-v1-contract.md +0 -246
- package/docs/decisions/ADR-110-discipline-profile-resolution-locus.md +0 -110
- package/docs/decisions/ADR-111-canonical-glama-listing.md +0 -97
- package/docs/decisions/ADR-112-stdio-lite-stays-read-only.md +0 -92
- package/docs/decisions/ADR-113-ci-native-release-label-trigger.md +0 -201
- package/docs/decisions/ADR-114-commit-policy-iron-law-override.md +0 -85
- package/docs/decisions/ADR-115-command-cluster-consolidation-phase-4.md +0 -91
- package/docs/decisions/ADR-116-memory-tripwire-activation-path.md +0 -144
- package/docs/decisions/ADR-117-subagents-auto-default-on.md +0 -89
- package/docs/decisions/ADR-118-loop-engineering-boundaries.md +0 -162
- package/docs/decisions/ADR-119-global-knowledge-default-on.md +0 -104
- package/docs/decisions/ADR-120-council-chairman-mode.md +0 -70
- package/docs/decisions/ADR-121-knowledge-sensitivity-classes.md +0 -90
- package/docs/decisions/ADR-122-adversarial-verification-council.md +0 -166
- package/docs/decisions/ADR-123-runtime-security-scope-and-spawn-hardening.md +0 -142
- package/docs/decisions/ADR-200-python-to-typescript-migration.md +0 -203
- package/docs/decisions/ADR-rule-kernel-and-router.md +0 -133
- package/docs/decisions/INDEX.md +0 -136
- package/docs/deploy/connector-setup.md +0 -129
- package/docs/deploy/env-vars.md +0 -70
- package/docs/deploy/policy-cookbook.md +0 -130
- package/docs/deploy/quickstart.md +0 -112
- package/docs/deploy/small-team-recipe.md +0 -148
- package/docs/deploy/team-deployment-posture.md +0 -111
- package/docs/design/adversarial-council-eval.md +0 -106
- package/docs/design/council-vs-solo-baseline.md +0 -92
- package/docs/design/cross-model-parity-eval.md +0 -82
- package/docs/design/length-neutral-judge-rerun.md +0 -92
- package/docs/design/utilization-window-criteria.md +0 -51
- package/docs/development.md +0 -230
- package/docs/distribution/awesome-list-submission.md +0 -46
- package/docs/distribution/consumer-matrix.md +0 -96
- package/docs/distribution/launch-story-draft.md +0 -58
- package/docs/distribution/mcp-submission-checklist.md +0 -95
- package/docs/distribution/public-install-smoke.md +0 -68
- package/docs/distribution/registries.md +0 -117
- package/docs/distribution/registry-submissions.md +0 -85
- package/docs/distribution/telemetry-privacy.md +0 -128
- package/docs/distribution/telemetry-schema.md +0 -174
- package/docs/distribution/topics-equivalents-decay-policy.md +0 -51
- package/docs/end-to-end-walkthroughs.md +0 -165
- package/docs/enforcement-by-host.md +0 -44
- package/docs/examples/agent-user.example.md +0 -22
- package/docs/experiences/agency.md +0 -49
- package/docs/experiences/content_creator.md +0 -49
- package/docs/experiences/developer.md +0 -49
- package/docs/experiences/finance.md +0 -50
- package/docs/experiences/founder.md +0 -49
- package/docs/experiences/ops.md +0 -49
- package/docs/featured-commands.md +0 -27
- package/docs/featured-skills.md +0 -95
- package/docs/flows.md +0 -67
- package/docs/getting-started-by-role.md +0 -190
- package/docs/getting-started-laravel.md +0 -72
- package/docs/getting-started-local-stdio.md +0 -77
- package/docs/getting-started.md +0 -254
- package/docs/github-topics.md +0 -93
- package/docs/governance-advantage.md +0 -72
- package/docs/governance.md +0 -96
- package/docs/guides/cross-repo-linked-projects.md +0 -93
- package/docs/guides/cross-repo-retrieval.md +0 -61
- package/docs/guides/frontend-design-corpus-refresh.md +0 -83
- package/docs/guides/local-analytics.md +0 -125
- package/docs/guides/local-knowledge.md +0 -127
- package/docs/guides/skill-discovery.md +0 -71
- package/docs/guides/skill-preview.md +0 -71
- package/docs/hook-payload-capture.md +0 -221
- package/docs/install-friction-report.md +0 -66
- package/docs/installation.md +0 -959
- package/docs/maintainers/branch-protection.md +0 -105
- package/docs/maintainers/dev-mode.md +0 -106
- package/docs/maintainers/optional-media-deps.md +0 -50
- package/docs/maintainers/system-map.md +0 -85
- package/docs/mcp-registries.md +0 -94
- package/docs/mcp-server.md +0 -175
- package/docs/mcp.md +0 -147
- package/docs/media/proof-demo.cast +0 -14
- package/docs/media/proof-demo.gif +0 -0
- package/docs/migration/consumer-template-consumption-model.md +0 -145
- package/docs/migration/divergences/README.md +0 -55
- package/docs/migration/divergences/_template.md +0 -50
- package/docs/migration/divergences/bench-stats-float-precision.md +0 -72
- package/docs/migration/divergences/mcp-telemetry-node-sqlite.md +0 -61
- package/docs/migration/divergences/pack-mcp-content-gzip-body.md +0 -53
- package/docs/migration/divergences/src-scripts-build_cloud_bundle.md +0 -63
- package/docs/migration/divergences/src-scripts-check_memory.md +0 -91
- package/docs/migration/divergences/src-scripts-inventory_abstraction_budget.md +0 -65
- package/docs/migration/divergences/src-scripts-lint_marketplace.md +0 -57
- package/docs/migration/divergences/src-scripts-lint_mcp_registry_manifest.md +0 -70
- package/docs/migration/divergences/src-scripts-spotcheck_thin_root.md +0 -60
- package/docs/migration/divergences/src-scripts-validate_agent_settings.md +0 -58
- package/docs/migration/node-floor.md +0 -86
- package/docs/migration/v1-to-v2.md +0 -156
- package/docs/migration/yaml-roundtrip-spike.md +0 -163
- package/docs/migrations/commands-1.15.0.md +0 -117
- package/docs/parity/bench-external.json +0 -58
- package/docs/parity/bench.json +0 -41
- package/docs/parity/command-frontmatter.md +0 -60
- package/docs/parity/external-runtime.md +0 -46
- package/docs/personas.md +0 -178
- package/docs/positioning-evidence.md +0 -86
- package/docs/profiles.md +0 -108
- package/docs/proof.md +0 -176
- package/docs/quality.md +0 -98
- package/docs/readme-split-plan.md +0 -102
- package/docs/recruits/_template.md +0 -81
- package/docs/release-runbook.md +0 -141
- package/docs/role-experiences.md +0 -19
- package/docs/safety.md +0 -30
- package/docs/second-brain-scope.md +0 -116
- package/docs/self-review-gate.md +0 -65
- package/docs/setup/enterprise-and-offline.md +0 -201
- package/docs/setup/mcp-client-config.md +0 -358
- package/docs/setup/mcp-cloud-endpoints.md +0 -116
- package/docs/setup/mcp-cloud-registry-listing.md +0 -99
- package/docs/setup/mcp-cloud-setup.md +0 -183
- package/docs/setup/mcp-r2-bootstrap.md +0 -82
- package/docs/setup/mcp-server-docker.md +0 -101
- package/docs/setup/per-ide/aider.md +0 -48
- package/docs/setup/per-ide/antigravity.md +0 -68
- package/docs/setup/per-ide/augment.md +0 -89
- package/docs/setup/per-ide/claude-code.md +0 -97
- package/docs/setup/per-ide/claude-desktop.md +0 -207
- package/docs/setup/per-ide/cline.md +0 -43
- package/docs/setup/per-ide/codebuddy.md +0 -63
- package/docs/setup/per-ide/codex.md +0 -46
- package/docs/setup/per-ide/continue.md +0 -68
- package/docs/setup/per-ide/copilot.md +0 -80
- package/docs/setup/per-ide/cursor.md +0 -125
- package/docs/setup/per-ide/droid.md +0 -65
- package/docs/setup/per-ide/gemini-cli.md +0 -45
- package/docs/setup/per-ide/jetbrains.md +0 -76
- package/docs/setup/per-ide/kilocode.md +0 -66
- package/docs/setup/per-ide/kiro.md +0 -72
- package/docs/setup/per-ide/opencode.md +0 -62
- package/docs/setup/per-ide/qoder.md +0 -63
- package/docs/setup/per-ide/roocode.md +0 -68
- package/docs/setup/per-ide/trae.md +0 -63
- package/docs/setup/per-ide/warp.md +0 -63
- package/docs/setup/per-ide/windsurf.md +0 -109
- package/docs/setup/per-ide/zed.md +0 -73
- package/docs/showcase.md +0 -209
- package/docs/skill-duplication-findings.md +0 -91
- package/docs/skills-catalog.md +0 -265
- package/docs/skills-taxonomy.md +0 -90
- package/docs/spawn-site-policy.md +0 -72
- package/docs/succession.md +0 -80
- package/docs/threat-model.md +0 -29
- package/docs/troubleshooting.md +0 -155
- package/docs/ui-track-mental-model.md +0 -121
- package/docs/us-vs-the-category.md +0 -38
- package/docs/value.md +0 -86
- package/docs/walkthroughs/assets/workspace-role-grid.png +0 -0
- package/docs/walkthroughs/assets/workspace-session-started.png +0 -0
- package/docs/walkthroughs/assets/workspace-task-picker.png +0 -0
- package/docs/walkthroughs/daily-workspace-a11y.md +0 -87
- package/docs/walkthroughs/daily-workspace.md +0 -127
- package/docs/wedge/production-validator/README.md +0 -81
- package/docs/wedge/production-validator/first-run-check.sh +0 -42
- package/docs/wedge/production-validator/production-validator.md +0 -83
- 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
|
+
}
|