@event4u/agent-config 16.3.0 → 16.4.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/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +251 -0
- package/README.md +2 -2
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +10 -7
- package/dist/agent-src/commands/bug/investigate.md +1 -1
- package/dist/agent-src/commands/commit/in-chunks.md +40 -31
- package/dist/agent-src/commands/commit.md +72 -33
- package/dist/agent-src/commands/condense.md +2 -8
- package/dist/agent-src/commands/council/debate.md +11 -7
- package/dist/agent-src/commands/council/default.md +10 -9
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/estimate-ticket.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +3 -3
- package/dist/agent-src/commands/feature/plan.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +17 -3
- package/dist/agent-src/commands/fix/commit-messages.md +48 -23
- package/dist/agent-src/commands/fix/pr-comments.md +3 -6
- package/dist/agent-src/commands/implement-ticket.md +3 -3
- package/dist/agent-src/commands/memory/add.md +4 -16
- package/dist/agent-src/commands/memory/load.md +5 -6
- package/dist/agent-src/commands/memory/promote.md +3 -16
- package/dist/agent-src/commands/mission/upgrade.md +3 -2
- package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
- package/dist/agent-src/commands/optimize/deep.md +4 -2
- package/dist/agent-src/commands/optimize/rtk.md +4 -26
- package/dist/agent-src/commands/pr/create/description-only.md +18 -15
- package/dist/agent-src/commands/pr/create.md +42 -34
- package/dist/agent-src/commands/pr/merge.md +136 -9
- package/dist/agent-src/commands/prepare-for-review.md +13 -11
- package/dist/agent-src/commands/project/analyze.md +7 -35
- package/dist/agent-src/commands/refine-ticket.md +1 -1
- package/dist/agent-src/commands/review/changes.md +14 -19
- package/dist/agent-src/commands/roadmap/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +52 -38
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
- package/dist/agent-src/commands/worktree/create.md +7 -3
- package/dist/agent-src/commands/worktree/verify.md +2 -1
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
- package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
- package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
- package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +67 -52
- package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
- package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +4 -10
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +0 -1
- package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
- package/dist/agent-src/rules/architecture.md +0 -1
- package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
- package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
- package/dist/agent-src/rules/autonomous-execution.md +0 -1
- package/dist/agent-src/rules/brand-consistency.md +0 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
- package/dist/agent-src/rules/broken-access-control.md +0 -1
- package/dist/agent-src/rules/cli-output-handling.md +0 -1
- package/dist/agent-src/rules/code-comment-discipline.md +0 -1
- package/dist/agent-src/rules/code-provenance.md +0 -1
- package/dist/agent-src/rules/command-suggestion-policy.md +0 -1
- package/dist/agent-src/rules/commit-conventions.md +1 -2
- package/dist/agent-src/rules/communication-through-line.md +1 -2
- package/dist/agent-src/rules/content-quoting-floor.md +1 -2
- package/dist/agent-src/rules/context-hygiene.md +5 -5
- package/dist/agent-src/rules/copilot-routing.md +1 -2
- package/dist/agent-src/rules/council-availability.md +4 -4
- package/dist/agent-src/rules/cross-source-consistency.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +2 -3
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +0 -1
- package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
- package/dist/agent-src/rules/devcontainer-routing.md +1 -2
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +0 -1
- package/dist/agent-src/rules/docker-commands.md +0 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
- package/dist/agent-src/rules/domain-safety-disclaimer.md +0 -1
- package/dist/agent-src/rules/domain-safety-pii.md +0 -1
- package/dist/agent-src/rules/domain-safety-retention.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -2
- package/dist/agent-src/rules/finance-safety-floor.md +0 -1
- package/dist/agent-src/rules/fix-what-you-see.md +6 -32
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
- package/dist/agent-src/rules/git-history-discipline.md +1 -2
- package/dist/agent-src/rules/guidelines.md +0 -1
- package/dist/agent-src/rules/history-discipline.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +0 -1
- package/dist/agent-src/rules/image-likeness-and-rights.md +0 -1
- package/dist/agent-src/rules/improve-before-implement.md +0 -1
- package/dist/agent-src/rules/invite-challenge.md +0 -1
- package/dist/agent-src/rules/laravel-routing.md +1 -2
- package/dist/agent-src/rules/laravel-translations.md +0 -1
- package/dist/agent-src/rules/legal-safety-floor.md +0 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +1 -2
- package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
- package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
- package/dist/agent-src/rules/missing-tool-handling.md +0 -1
- package/dist/agent-src/rules/model-recommendation.md +0 -1
- package/dist/agent-src/rules/neighbour-precedence.md +64 -0
- package/dist/agent-src/rules/no-attribution-footers.md +0 -1
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
- package/dist/agent-src/rules/no-roadmap-references.md +0 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
- package/dist/agent-src/rules/onboarding-gate.md +0 -1
- package/dist/agent-src/rules/output-discipline.md +0 -1
- package/dist/agent-src/rules/package-ci-checks.md +0 -1
- package/dist/agent-src/rules/persona-governance.md +2 -3
- package/dist/agent-src/rules/php-coding.md +0 -1
- package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
- package/dist/agent-src/rules/preservation-guard.md +0 -1
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -3
- package/dist/agent-src/rules/question-not-instruction.md +0 -1
- package/dist/agent-src/rules/reviewer-awareness.md +0 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +3 -6
- package/dist/agent-src/rules/role-mode-adherence.md +0 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -1
- package/dist/agent-src/rules/runtime-safety.md +0 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
- package/dist/agent-src/rules/self-repair-loop.md +0 -1
- package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
- package/dist/agent-src/rules/session-canary.md +0 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +5 -6
- package/dist/agent-src/rules/size-enforcement.md +0 -1
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
- package/dist/agent-src/rules/skill-quality.md +0 -1
- package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
- package/dist/agent-src/rules/source-confidentiality.md +0 -1
- package/dist/agent-src/rules/source-discovery-gate.md +0 -1
- package/dist/agent-src/rules/source-of-truth.md +0 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +0 -1
- package/dist/agent-src/rules/symfony-routing.md +1 -2
- package/dist/agent-src/rules/think-before-action.md +0 -1
- package/dist/agent-src/rules/token-budget-discipline.md +12 -46
- package/dist/agent-src/rules/token-efficiency.md +0 -1
- package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
- package/dist/agent-src/rules/tool-safety.md +0 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -1
- package/dist/agent-src/rules/upstream-proposal.md +0 -1
- package/dist/agent-src/rules/user-interaction.md +0 -1
- package/dist/agent-src/rules/user-interrupt-priority.md +0 -1
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
- package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
- package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
- package/dist/agent-src/skills/database/data/manifest.json +2 -2
- package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +46 -129
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +6 -1
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli-delegate/check_update_banner.js +195 -119
- package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
- package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
- package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
- package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-AED2VQGM.js} +65 -11
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
- package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
- package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
- package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
- package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
- package/dist/cli-delegate/cmd_analyze_session.js +223 -43
- package/dist/cli-delegate/cmd_brand_status.js +3 -2
- package/dist/cli-delegate/cmd_conformance.js +105 -35
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +15 -12
- package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
- package/dist/cli-delegate/cmd_explain.js +43 -19
- package/dist/cli-delegate/cmd_export.js +10 -10
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +40 -21
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +4 -3
- package/dist/cli-delegate/cmd_prune.js +10 -10
- package/dist/cli-delegate/cmd_route_audit.js +7 -7
- package/dist/cli-delegate/cmd_route_explain.js +5 -5
- package/dist/cli-delegate/cmd_session_recycle.js +9 -7
- package/dist/cli-delegate/cmd_settings_check.js +54 -7
- package/dist/cli-delegate/cmd_settings_get.js +18 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +10 -10
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -10
- package/dist/cli-delegate/cmd_uninstall.js +10 -10
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +7 -7
- package/dist/cli-delegate/cmd_validate.js +10 -10
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +129 -90
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +24 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +2151 -1820
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/install/install.mjs +462 -431
- package/dist/install/installThinLayer.js +40 -12
- package/dist/install/installedRuleLinks.js +105 -19
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +5 -5
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +2702 -1995
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +15 -120
- package/dist/scripts/_lib/compile_time_toggles.js +12 -21
- package/dist/scripts/_lib/git_convention.js +232 -0
- package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
- package/dist/scripts/_lib/installed_layer.js +344 -18
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_law_section.js +1 -1
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +80 -3
- package/dist/server/app.js +2 -1
- package/dist/server/gitKeysGate.js +105 -0
- package/dist/server/routes/settings.js +67 -8
- package/dist/server/routes/wizard.js +4 -0
- package/dist/server/schemas/settings.js +23 -81
- package/dist/shared/skillRanking.js +30 -4
- package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +72 -1
- package/docs/contracts/settings-classes.md +35 -65
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/docs/guidelines/agent-infra/layered-settings.md +13 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +1 -1
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/src/config/agent-settings.template.yml +79 -312
- package/src/config/agents-paths.yml +16 -0
- package/src/config/assurance-capability-registry.json +7 -3
- package/src/config/ci-local-parity.yml +3 -0
- package/src/config/continuity-surface.json +3 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +19 -6
- package/src/config/gate-coverage.yml +65 -23
- package/src/config/gate-violation-baselines.json +53 -13
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +1 -1
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +48 -6
- package/src/config/rule-law-ceilings.json +12 -6
- package/src/config/rule-obligations.json +833 -0
- package/src/install/installThinLayer.ts +76 -10
- package/src/install/installedRuleLinks.ts +105 -17
- package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
- package/src/scripts/_cli/cmd_conformance.ts +13 -2
- package/src/scripts/_cli/cmd_doctor.ts +10 -10
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -0
- package/src/scripts/_cli/cmd_explain.ts +72 -14
- package/src/scripts/_cli/cmd_git_convention.ts +637 -0
- package/src/scripts/_cli/cmd_settings_check.ts +66 -8
- package/src/scripts/_cli/cmd_settings_get.ts +18 -3
- package/src/scripts/_cli/doctor_execution.ts +72 -1
- package/src/scripts/_dispatch.bash +37 -2
- package/src/scripts/_lib/agent_settings.ts +15 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
- package/src/scripts/_lib/git_base_ref.ts +92 -0
- package/src/scripts/_lib/git_convention.ts +291 -0
- package/src/scripts/_lib/git_convention_carrier.ts +389 -0
- package/src/scripts/_lib/git_convention_grammar.ts +246 -0
- package/src/scripts/_lib/git_convention_measure.ts +344 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +74 -0
- package/src/scripts/_lib/graph_feeder_record.ts +236 -13
- package/src/scripts/_lib/installed_layer.ts +448 -23
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
- package/src/scripts/_lib/ratification_artifact.ts +220 -1
- package/src/scripts/_lib/ratification_subject.ts +92 -0
- package/src/scripts/_lib/release_findings_ingest.ts +15 -7
- package/src/scripts/_lib/release_highlights.ts +5 -0
- package/src/scripts/_lib/rule_law_section.ts +1 -1
- package/src/scripts/_lib/rule_obligations.ts +205 -0
- package/src/scripts/_lib/self_repair.ts +1 -1
- package/src/scripts/_lib/settings_removed_keys.ts +173 -0
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +83 -3
- package/src/scripts/_lib/touched_file_quality.ts +142 -6
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/config.ts +7 -7
- package/src/scripts/ai_council/modification_review.ts +100 -0
- package/src/scripts/ai_council/modification_review_questions.ts +90 -0
- package/src/scripts/ai_council/orchestrator.ts +5 -5
- package/src/scripts/ai_council/spend_gate.ts +18 -7
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/bench_ab_clone.ts +1 -2
- package/src/scripts/bench_concern_spawn_path.ts +129 -0
- package/src/scripts/bench_graph_feeder_latency.ts +584 -0
- package/src/scripts/check_agent_artifact_location.ts +36 -0
- package/src/scripts/check_branch_freshness.ts +147 -24
- package/src/scripts/check_enforcement_coverage.ts +130 -0
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +88 -25
- package/src/scripts/check_gate_coverage.ts +4 -3
- package/src/scripts/check_gate_reachability.ts +48 -1
- package/src/scripts/check_generator_sync.ts +48 -0
- package/src/scripts/check_hook_bundle_composition.ts +107 -0
- package/src/scripts/check_kernel_edit_ratified.ts +86 -6
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_standing_rule_delivery.ts +96 -0
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/ci_settle.ts +42 -5
- package/src/scripts/classify_merge_risk.ts +320 -0
- package/src/scripts/closure_scan.ts +91 -1
- package/src/scripts/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/consumer_matrix.ts +102 -0
- package/src/scripts/context_hygiene_hook.ts +13 -23
- package/src/scripts/cost/preflight.mjs +12 -1
- package/src/scripts/council_cli.ts +7 -7
- package/src/scripts/dispatch_r2_reviewer.ts +24 -11
- package/src/scripts/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +20 -3
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +25 -6
- package/src/scripts/hooks/block_config_weakening.ts +48 -5
- package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +44 -3
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
- package/src/scripts/hooks/turn_end_gate_hook.ts +1 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +7 -31
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_pack_boundaries.ts +406 -28
- package/src/scripts/lint_roadmap_blockers.ts +282 -29
- package/src/scripts/lint_roadmap_complexity.ts +4 -2
- package/src/scripts/lint_rule_law_section.ts +151 -21
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +12 -1
- package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
- package/src/scripts/model_rule_injection.ts +3 -3
- package/src/scripts/onboarding_gate_hook.ts +7 -6
- package/src/scripts/print_required_checks.ts +15 -3
- package/src/scripts/probe_host_compliance.ts +33 -5
- package/src/scripts/probe_turnaround.ts +183 -3
- package/src/scripts/ratification_header.ts +77 -0
- package/src/scripts/refusal_envelope.ts +138 -0
- package/src/scripts/release_publication.ts +29 -3
- package/src/scripts/report_dispatch_import_closure.ts +76 -0
- package/src/scripts/report_installed_rule_links.ts +118 -1
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +16 -18
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_skill_ranker_confusion.ts +103 -2
- package/src/scripts/report_standing_payload_by_host.ts +1 -1
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +39 -2
- package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -16
- package/src/server/schemas/settings.ts +33 -177
- package/src/shared/skillRanking.ts +53 -4
- package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
- package/src/scripts/_lib/config_chain.ts +0 -477
- package/src/scripts/_lib/conformance_report.ts +0 -403
- package/src/scripts/_lib/eval_discrimination.ts +0 -95
- package/src/scripts/_lib/evaluator_promotion.ts +0 -225
- package/src/scripts/_lib/file_slicer.ts +0 -117
- package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
- package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
- package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
- package/src/scripts/_lib/role_split.ts +0 -338
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
* definition. It runs the same contract as the review-side canary in
|
|
31
31
|
* `docs/contracts/adversarial-review-protocol.md` § 6 (rotating class, sealed
|
|
32
32
|
* record, NEVER SHIPS) and is deliberately kept OFF the default CI path: it
|
|
33
|
-
* mutates the tree, so it is
|
|
33
|
+
* mutates the tree, so it is a scheduled experiment (`.github/workflows/gate-canary.yml`,
|
|
34
|
+
* monthly, ledger uploaded as a run artifact) or an operator run, never a
|
|
34
35
|
* per-PR gate. Every plant is reverted in a `finally`.
|
|
35
36
|
*
|
|
36
37
|
* CLI:
|
|
@@ -415,8 +416,8 @@ function report_negative_control_inventory(specs: readonly GateSpec[]): void {
|
|
|
415
416
|
// The arithmetic is the guard against skimming: two numbers that reconcile
|
|
416
417
|
// against the enforced total cannot be read as "all of them checked".
|
|
417
418
|
process.stdout.write(
|
|
418
|
-
` ⚠️ declared, not run — the mutating --canary path is
|
|
419
|
-
`so no negative control ran here.\n`,
|
|
419
|
+
` ⚠️ declared, not run — the mutating --canary path is off the per-PR workflow (it runs monthly ` +
|
|
420
|
+
`in .github/workflows/gate-canary.yml), so no negative control ran here.\n`,
|
|
420
421
|
);
|
|
421
422
|
for (const s of without) {
|
|
422
423
|
process.stdout.write(` · ${s.id}: ${s.no_canary_reason ?? '(no reason recorded)'}\n`);
|
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
resetSourceCache,
|
|
43
43
|
type InventoryVerdict,
|
|
44
44
|
} from './_lib/loop_surfaces.js';
|
|
45
|
+
import { analyseModuleReach, groupCounts } from './_lib/module_reach.js';
|
|
45
46
|
|
|
46
47
|
const REPO_ROOT = process.cwd();
|
|
47
48
|
const TASKFILES_DIR = 'taskfiles';
|
|
@@ -312,6 +313,10 @@ function runGate(root: string): number {
|
|
|
312
313
|
units: 'exemption row(s) + unreachable target(s) + declared loop instrument(s)',
|
|
313
314
|
roots: [EXEMPTIONS_REL, INVENTORY_REL, ROOT_TASKFILE, TASKFILES_DIR],
|
|
314
315
|
});
|
|
316
|
+
// Phase 3.1 of road-to-modules-that-something-calls.md: five numbers beside
|
|
317
|
+
// this gate's existing verdicts, printed whether the gate passes or fails —
|
|
318
|
+
// reported, enforcing nothing, the way the estate gate prints its draft count.
|
|
319
|
+
process.stdout.write(moduleReachLine(moduleReachSummary(root)));
|
|
315
320
|
const inventoryClean = inv.shape.length === 0 && inv.reach.length === 0;
|
|
316
321
|
if (v.unreasoned.length === 0 && v.stale.length === 0 && inventoryClean) {
|
|
317
322
|
process.stdout.write(
|
|
@@ -506,13 +511,54 @@ function selfTest(): number {
|
|
|
506
511
|
}
|
|
507
512
|
}
|
|
508
513
|
|
|
514
|
+
export interface ModuleReachSummary {
|
|
515
|
+
/** The four group counts — the roadmap's "four group counts", over the unreferenced-by-name set only. */
|
|
516
|
+
groups: Record<'contract-test' | 'open-or-deferred-step' | 'named-outside-open-step' | 'named-in-none', number>;
|
|
517
|
+
/** "the total of modules reached by nothing" (import edges + run-by-path) — the fifth number. */
|
|
518
|
+
reachedByNothing: number;
|
|
519
|
+
/**
|
|
520
|
+
* Not one of the five — the size of the unreferenced-by-name set the four
|
|
521
|
+
* groups partition. Kept on the summary because it is a free cross-check
|
|
522
|
+
* (it must equal the sum of `groups`); never printed as a sixth number.
|
|
523
|
+
*/
|
|
524
|
+
unreferencedTotal: number;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* The five module-reach numbers (road-to-modules-that-something-calls.md
|
|
529
|
+
* Phase 3.1) — reported beside this gate's own verdicts, the way the estate
|
|
530
|
+
* gate prints its draft-roadmap count: a number that moves in a change's own
|
|
531
|
+
* gate output, enforcing nothing.
|
|
532
|
+
*/
|
|
533
|
+
export function moduleReachSummary(root: string): ModuleReachSummary {
|
|
534
|
+
const { modules } = analyseModuleReach(root);
|
|
535
|
+
const unreferenced = modules.filter((m) => !m.namedByProduction);
|
|
536
|
+
return {
|
|
537
|
+
groups: groupCounts(unreferenced),
|
|
538
|
+
reachedByNothing: modules.filter((m) => !m.reachedByImportOrPath).length,
|
|
539
|
+
unreferencedTotal: unreferenced.length,
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
export function moduleReachLine(s: ModuleReachSummary): string {
|
|
544
|
+
return (
|
|
545
|
+
`module reach (reported, not gated), src/scripts/_lib: ` +
|
|
546
|
+
`contract-test ${String(s.groups['contract-test'])} · ` +
|
|
547
|
+
`open-or-deferred-step ${String(s.groups['open-or-deferred-step'])} · ` +
|
|
548
|
+
`named-outside-open-step ${String(s.groups['named-outside-open-step'])} · ` +
|
|
549
|
+
`named-in-none ${String(s.groups['named-in-none'])} · ` +
|
|
550
|
+
`REACHED-BY-NOTHING total ${String(s.reachedByNothing)}\n`
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
509
554
|
export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT): number {
|
|
510
555
|
if (argv.includes('--self-test')) return selfTest();
|
|
511
556
|
if (argv.includes('--gate')) return runGate(root);
|
|
512
557
|
const r = analyse(root);
|
|
513
558
|
const inv = inventoryVerdict(root);
|
|
559
|
+
const moduleReach = moduleReachSummary(root);
|
|
514
560
|
if (argv.includes('--json')) {
|
|
515
|
-
process.stdout.write(`${JSON.stringify({ ...r, inventory: inv }, null, 2)}\n`);
|
|
561
|
+
process.stdout.write(`${JSON.stringify({ ...r, inventory: inv, moduleReach }, null, 2)}\n`);
|
|
516
562
|
return 0;
|
|
517
563
|
}
|
|
518
564
|
// Emitted on the DEFAULT invocation too, not only under `--gate`: the
|
|
@@ -538,6 +584,7 @@ export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT):
|
|
|
538
584
|
`prose-bound surfaces ${String(inv.proseBound.length)}\n`,
|
|
539
585
|
);
|
|
540
586
|
for (const f of inv.reach) process.stdout.write(` · ${f.id} — ${f.detail}\n`);
|
|
587
|
+
process.stdout.write(moduleReachLine(moduleReach));
|
|
541
588
|
return 0;
|
|
542
589
|
}
|
|
543
590
|
|
|
@@ -546,11 +546,59 @@ const HOST_LOWERING_TRIPLE: Triple = {
|
|
|
546
546
|
},
|
|
547
547
|
};
|
|
548
548
|
|
|
549
|
+
const GRAMMAR_REFERENCE = 'src/skills/git-workflow/references/commit-subject.md';
|
|
550
|
+
const GRAMMAR_GENERATOR = 'src/scripts/generate_git_convention_grammar.ts';
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* The commit-subject reference is the fallback a command reads when the
|
|
554
|
+
* `agent-config` binary cannot run, so its grammar block has to be the grammar
|
|
555
|
+
* the binary runs. The page itself is a source too: a hand edit to the block is
|
|
556
|
+
* the drift this row exists to catch.
|
|
557
|
+
*/
|
|
558
|
+
const GIT_CONVENTION_GRAMMAR_TRIPLE: Triple = {
|
|
559
|
+
id: 'git-convention-grammar',
|
|
560
|
+
output: GRAMMAR_REFERENCE,
|
|
561
|
+
remedy: './scripts-run src/scripts/generate_git_convention_grammar',
|
|
562
|
+
why: 'the reference carries a grammar and measurement block rendered from the modules the git:convention verb runs',
|
|
563
|
+
sourcesOf() {
|
|
564
|
+
return {
|
|
565
|
+
ok: true,
|
|
566
|
+
sources: [
|
|
567
|
+
{ kind: 'file', value: 'src/scripts/_lib/git_convention_grammar.ts' },
|
|
568
|
+
{ kind: 'file', value: 'src/scripts/_lib/git_convention_measure.ts' },
|
|
569
|
+
{ kind: 'file', value: GRAMMAR_GENERATOR },
|
|
570
|
+
{ kind: 'file', value: GRAMMAR_REFERENCE },
|
|
571
|
+
],
|
|
572
|
+
};
|
|
573
|
+
},
|
|
574
|
+
regenerate(root, workDir) {
|
|
575
|
+
const outFile = path.join(workDir, 'commit-subject.md');
|
|
576
|
+
const tsx = path.join(root, 'node_modules', '.bin', process.platform === 'win32' ? 'tsx.cmd' : 'tsx');
|
|
577
|
+
if (!fs.existsSync(tsx)) {
|
|
578
|
+
return { ok: false, reason: `no tsx at ${path.relative(root, tsx)} — run \`npm ci\` before this gate` };
|
|
579
|
+
}
|
|
580
|
+
const res = spawnSync(tsx, [path.join(root, GRAMMAR_GENERATOR), '--out', outFile], {
|
|
581
|
+
cwd: root,
|
|
582
|
+
encoding: 'utf8',
|
|
583
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
584
|
+
});
|
|
585
|
+
if (res.status !== 0) {
|
|
586
|
+
return { ok: false, reason: `generate_git_convention_grammar exited ${String(res.status)}: ${(res.stderr ?? '').trim()}` };
|
|
587
|
+
}
|
|
588
|
+
try {
|
|
589
|
+
return { ok: true, text: fs.readFileSync(outFile, 'utf8') };
|
|
590
|
+
} catch (e) {
|
|
591
|
+
return { ok: false, reason: `generate_git_convention_grammar reported success but wrote no readable file (${String(e)})` };
|
|
592
|
+
}
|
|
593
|
+
},
|
|
594
|
+
};
|
|
595
|
+
|
|
549
596
|
export const REGISTRY: readonly Triple[] = [
|
|
550
597
|
CENSUS_TRIPLE,
|
|
551
598
|
INSTALL_BUNDLE_TRIPLE,
|
|
552
599
|
HOOK_MANIFEST_TRIPLE,
|
|
553
600
|
HOST_LOWERING_TRIPLE,
|
|
601
|
+
GIT_CONVENTION_GRAMMAR_TRIPLE,
|
|
554
602
|
];
|
|
555
603
|
|
|
556
604
|
// ---------------------------------------------------------------------------
|
|
@@ -191,6 +191,95 @@ export function verdict(a: Analysis, budget: Budget): string[] {
|
|
|
191
191
|
return findings;
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
+
/**
|
|
195
|
+
* The raise rule, enforced against the base revision.
|
|
196
|
+
*
|
|
197
|
+
* `road-to-a-ratification-fence-that-follows-its-imports` 3.2. The budget's
|
|
198
|
+
* `shrink_only_note` used to say a raise needs a `raise_log` entry and that
|
|
199
|
+
* "the gate prints the rule but cannot enforce it, because a raise and a
|
|
200
|
+
* lowering are the same edit to a number". Against the base revision they are
|
|
201
|
+
* not: a working `max_bytes` above the base's is a raise, and it passes only if
|
|
202
|
+
* `raise_log` gained an entry — one absent from the base's log — whose `to`
|
|
203
|
+
* equals the new ceiling. A lowering needs nothing. The comparison is the
|
|
204
|
+
* merged value against the base, because that is what ships; a ceiling raised
|
|
205
|
+
* and lowered inside one branch is judged by where it ends.
|
|
206
|
+
*
|
|
207
|
+
* `baseRaw` is the base revision's budget file, or `null` when it has none
|
|
208
|
+
* (the registration diff), which is not a raise.
|
|
209
|
+
*/
|
|
210
|
+
export function raiseFindings(baseRaw: string | null, workingRaw: string): string[] {
|
|
211
|
+
if (baseRaw === null) {
|
|
212
|
+
return [];
|
|
213
|
+
}
|
|
214
|
+
const base = JSON.parse(baseRaw) as Record<string, unknown>;
|
|
215
|
+
const working = JSON.parse(workingRaw) as Record<string, unknown>;
|
|
216
|
+
const baseMax = base['max_bytes'];
|
|
217
|
+
const workingMax = working['max_bytes'];
|
|
218
|
+
if (typeof baseMax !== 'number' || typeof workingMax !== 'number' || workingMax <= baseMax) {
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
const baseLog = Array.isArray(base['raise_log']) ? (base['raise_log'] as unknown[]) : [];
|
|
222
|
+
const workingLog = Array.isArray(working['raise_log']) ? (working['raise_log'] as unknown[]) : [];
|
|
223
|
+
const seen = new Set(baseLog.map((e) => JSON.stringify(e)));
|
|
224
|
+
const gained = workingLog.filter((e) => !seen.has(JSON.stringify(e)));
|
|
225
|
+
const matching = gained.find((e) => isRecord(e) && e['to'] === workingMax);
|
|
226
|
+
if (matching === undefined) {
|
|
227
|
+
return [
|
|
228
|
+
`max_bytes raised from ${String(baseMax)} to ${String(workingMax)} against the base revision ` +
|
|
229
|
+
`with no new raise_log entry whose \`to\` is ${String(workingMax)}. A raise needs a recorded ` +
|
|
230
|
+
`reason in ${BUDGET_REL}'s raise_log naming what was added and why it could not be avoided.`,
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
const problems = raiseEntryProblems(matching as Record<string, unknown>, baseMax);
|
|
234
|
+
return problems.length === 0
|
|
235
|
+
? []
|
|
236
|
+
: [`the raise_log entry for ${String(workingMax)} is incomplete: ${problems.join('; ')}.`];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function isRecord(v: unknown): v is Record<string, unknown> {
|
|
240
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/** The prose fields an entry must carry, matching the entry the log already holds. */
|
|
244
|
+
const RAISE_REASON_FIELDS = ['what_was_added', 'why_it_could_not_be_avoided'] as const;
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* What a raise entry must say. A bare `{ "to": N }` matched the first version
|
|
248
|
+
* of this rule, which a ratification reviewer refused on: a log entry with no
|
|
249
|
+
* reason is the empty log the rule exists to refuse, wearing a key.
|
|
250
|
+
*/
|
|
251
|
+
export function raiseEntryProblems(entry: Record<string, unknown>, baseMax: number): string[] {
|
|
252
|
+
const problems: string[] = [];
|
|
253
|
+
if (entry['from'] !== baseMax) {
|
|
254
|
+
problems.push(`\`from\` must be the base ceiling ${String(baseMax)}`);
|
|
255
|
+
}
|
|
256
|
+
if (typeof entry['date'] !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(entry['date'])) {
|
|
257
|
+
problems.push('`date` must be YYYY-MM-DD');
|
|
258
|
+
}
|
|
259
|
+
for (const field of RAISE_REASON_FIELDS) {
|
|
260
|
+
const v = entry[field];
|
|
261
|
+
if (typeof v !== 'string' || v.trim().length < 20) {
|
|
262
|
+
problems.push(`\`${field}\` must say what happened, in at least a sentence`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return problems;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/** The base revision's budget file, `null` when absent there, `undefined` when no base resolves. */
|
|
269
|
+
function readBaseBudget(root: string, explicit: string | undefined): string | null | undefined {
|
|
270
|
+
const candidates = explicit !== undefined ? [explicit] : ['origin/main', 'main'];
|
|
271
|
+
for (const ref of candidates) {
|
|
272
|
+
const ok = spawnSync('git', ['rev-parse', '--verify', '--quiet', ref], {
|
|
273
|
+
cwd: root,
|
|
274
|
+
stdio: ['ignore', 'ignore', 'ignore'],
|
|
275
|
+
});
|
|
276
|
+
if (ok.status !== 0) continue;
|
|
277
|
+
const shown = spawnSync('git', ['show', `${ref}:${BUDGET_REL}`], { cwd: root, encoding: 'utf-8' });
|
|
278
|
+
return shown.status === 0 ? shown.stdout : null;
|
|
279
|
+
}
|
|
280
|
+
return undefined;
|
|
281
|
+
}
|
|
282
|
+
|
|
194
283
|
/**
|
|
195
284
|
* Build the hook bundle to a throwaway path and return its metafile JSON.
|
|
196
285
|
*
|
|
@@ -331,6 +420,24 @@ export function main(argv: readonly string[] = process.argv.slice(2), root: stri
|
|
|
331
420
|
}
|
|
332
421
|
|
|
333
422
|
const findings = verdict(analysis, budget);
|
|
423
|
+
const baseAt = argv.indexOf('--base-ref');
|
|
424
|
+
const baseBudget = readBaseBudget(root, baseAt >= 0 ? argv[baseAt + 1] : undefined);
|
|
425
|
+
if (baseBudget === undefined) {
|
|
426
|
+
// In CI an unresolvable base is a broken checkout, not a registration
|
|
427
|
+
// diff: refusing there is the difference between a rule and a notice.
|
|
428
|
+
// Locally a missing base is ordinary (a fresh clone without origin), so
|
|
429
|
+
// it is said rather than failed.
|
|
430
|
+
if (process.env['GITHUB_ACTIONS'] === 'true') {
|
|
431
|
+
process.stderr.write(
|
|
432
|
+
'check_hook_bundle_composition: no base revision resolved in CI — the max_bytes raise ' +
|
|
433
|
+
'rule cannot be evaluated, and this gate fails closed rather than skip it\n',
|
|
434
|
+
);
|
|
435
|
+
return 2;
|
|
436
|
+
}
|
|
437
|
+
process.stdout.write('raise rule: no base revision resolved — the max_bytes raise check was not evaluated\n');
|
|
438
|
+
} else {
|
|
439
|
+
findings.push(...raiseFindings(baseBudget, fs.readFileSync(path.join(root, BUDGET_REL), 'utf-8')));
|
|
440
|
+
}
|
|
334
441
|
// Reported on BOTH paths, red included: a census that goes quiet exactly
|
|
335
442
|
// when the gate fires is the shape `reportScanned` exists to prevent.
|
|
336
443
|
reportScanned({
|
|
@@ -33,8 +33,10 @@
|
|
|
33
33
|
* hook plumbing — the YAML sources `hook_manifest.yaml` and
|
|
34
34
|
* `host_lowering.yaml`, their compiled siblings `hook_manifest.json` and
|
|
35
35
|
* `host_lowering.json`, the dispatcher `hooks/dispatch_hook.ts`, the
|
|
36
|
-
* kernel-rule list `_lib/kernel_rules.ts`, a `*-dispatcher.sh`,
|
|
37
|
-
* budget
|
|
36
|
+
* kernel-rule list `_lib/kernel_rules.ts`, a `*-dispatcher.sh`, the token,
|
|
37
|
+
* latency and bundle budget files (see `PLUMBING_SOURCE_RE`), and every module
|
|
38
|
+
* the dispatcher imports that decides an exit code (derived at run time, see
|
|
39
|
+
* `derivedPlumbing`) — or this gate itself, so it cannot
|
|
38
40
|
* be weakened without its own record. Such a diff must carry a ratification
|
|
39
41
|
* artifact under the ratifications directory with `verdict: ratified`, valid
|
|
40
42
|
* per the ratification-artifact contract.
|
|
@@ -73,6 +75,7 @@ import * as path from 'node:path';
|
|
|
73
75
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
74
76
|
|
|
75
77
|
import { GateLedger } from './_lib/gate_ledger.js';
|
|
78
|
+
import { dispatchImportClosure, verdictModules } from './_lib/dispatch_import_closure.js';
|
|
76
79
|
import { is_kernel_rule } from './_lib/kernel_rules.js';
|
|
77
80
|
import { reportScanned } from './_lib/scan_scope.js';
|
|
78
81
|
import {
|
|
@@ -92,6 +95,13 @@ export const SELF_PATH = 'src/scripts/check_kernel_edit_ratified.ts';
|
|
|
92
95
|
/** The reader this gate shares with the artifact contract, also self-watched. */
|
|
93
96
|
export const READER_PATH = 'src/scripts/_lib/ratification_artifact.ts';
|
|
94
97
|
|
|
98
|
+
/**
|
|
99
|
+
* The reader that derives the dispatcher's verdict-deciding imports. It decides
|
|
100
|
+
* part of this gate's reach, so narrowing it is narrowing the gate — watched as
|
|
101
|
+
* part of the mechanism for the reason `READER_PATH` is.
|
|
102
|
+
*/
|
|
103
|
+
export const CLOSURE_READER_PATH = 'src/scripts/_lib/dispatch_import_closure.ts';
|
|
104
|
+
|
|
95
105
|
/** The in-repo quorum policy. Present on every runner; absent means refuse. */
|
|
96
106
|
export const POLICY_PATH = 'src/config/ratification-policy.json';
|
|
97
107
|
|
|
@@ -185,7 +195,67 @@ const GOVERNANCE_HOOK_RE = /^src\/scripts\/hooks\/block_[a-z0-9_]+\.ts$/;
|
|
|
185
195
|
* is the diff where only the JSON moves.
|
|
186
196
|
*/
|
|
187
197
|
const PLUMBING_SOURCE_RE =
|
|
188
|
-
/^(?:src\/scripts\/hook_manifest\.(?:yaml|json)|src\/scripts\/hooks\/host_lowering\.(?:yaml|json)|src\/scripts\/hooks\/dispatch_hook\.ts|src\/scripts\/_lib\/kernel_rules\.ts|src\/scripts\/hooks\/[a-z0-9-]+-dispatcher\.sh|src\/config\/hook-(?:token|latency)-budget\.json)$/;
|
|
198
|
+
/^(?:src\/scripts\/hook_manifest\.(?:yaml|json)|src\/scripts\/hooks\/host_lowering\.(?:yaml|json)|src\/scripts\/hooks\/dispatch_hook\.ts|src\/scripts\/_lib\/kernel_rules\.ts|src\/scripts\/hooks\/[a-z0-9-]+-dispatcher\.sh|src\/config\/hook-(?:token|latency|bundle)-budget\.json)$/;
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* The derived half of the plumbing set: every module in the static import
|
|
202
|
+
* closure of `dispatch_hook.ts` that decides an exit code or a refusal, read by
|
|
203
|
+
* `_lib/dispatch_import_closure.ts`.
|
|
204
|
+
*
|
|
205
|
+
* `road-to-a-ratification-fence-that-follows-its-imports` 2.2. The pattern
|
|
206
|
+
* above stayed correct until `_resolve_execution_failure` moved from the
|
|
207
|
+
* dispatcher into `concern_failure_policy.ts` — release finding `21900086c1a0`:
|
|
208
|
+
* a diff deciding whether a crashed blocking concern refuses then carried no
|
|
209
|
+
* record. A hand list fails the same way the next time a function moves, so
|
|
210
|
+
* this half is computed from the import graph at run time, and a module the
|
|
211
|
+
* dispatcher newly imports that names an exit code joins it with no edit here.
|
|
212
|
+
* Concerns stay outside: the walk stops at the concern table, and a governance
|
|
213
|
+
* concern is already gated by `GOVERNANCE_HOOK_RE`.
|
|
214
|
+
*
|
|
215
|
+
* `hook-bundle-budget.json` joins the pattern for D3: its ceiling was raised on
|
|
216
|
+
* its own registration day, and a raised ceiling is a looser budget reached one
|
|
217
|
+
* file earlier than the bundle it bounds.
|
|
218
|
+
*/
|
|
219
|
+
let _repoDerived: readonly string[] | null = null;
|
|
220
|
+
export function derivedPlumbing(root: string = REPO): readonly string[] {
|
|
221
|
+
if (root === REPO) {
|
|
222
|
+
_repoDerived ??= verdictModules(REPO);
|
|
223
|
+
return _repoDerived;
|
|
224
|
+
}
|
|
225
|
+
return verdictModules(root);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Modules that ARRIVE in the dispatcher's closure: non-concern modules present
|
|
230
|
+
* in the closure under `root` and absent from the closure under `baseRoot`.
|
|
231
|
+
*
|
|
232
|
+
* The classifier reads names, so a verdict-deciding module with neutral names
|
|
233
|
+
* (`acceptsExecution(): boolean`, no exit constant) reads as `payload` or
|
|
234
|
+
* `neither` — the gap the first ratification round of this change refused it
|
|
235
|
+
* on. A name-based classifier cannot close that gap; what can is watching the
|
|
236
|
+
* one moment such a module becomes reachable. So any module newly reachable
|
|
237
|
+
* from the dispatcher is gated on the diff that makes it reachable, whatever
|
|
238
|
+
* its class, and a reviewer reads it once. Modules already in the base closure
|
|
239
|
+
* are judged by class, so routine edits to a payload helper stay record-free.
|
|
240
|
+
*
|
|
241
|
+
* The other direction needs no extra rule: a module the base classes as
|
|
242
|
+
* `verdict` stays watched through the union in `evaluate` even when the head
|
|
243
|
+
* strips the names that classed it, so declassifying one is itself a gated
|
|
244
|
+
* edit of it.
|
|
245
|
+
*
|
|
246
|
+
* Meaningful only where the two trees differ — in CI, where the gate's code is
|
|
247
|
+
* the base revision's and `root` is the head. A local run with `root` equal to
|
|
248
|
+
* the gate's own tree sees no arrivals, and says nothing it cannot measure.
|
|
249
|
+
*/
|
|
250
|
+
export function closureArrivals(root: string, baseRoot: string = REPO): string[] {
|
|
251
|
+
if (path.resolve(root) === path.resolve(baseRoot)) {
|
|
252
|
+
return [];
|
|
253
|
+
}
|
|
254
|
+
const base = new Set(dispatchImportClosure(baseRoot).map((e) => e.path));
|
|
255
|
+
return dispatchImportClosure(root)
|
|
256
|
+
.filter((e) => e.class !== 'concern' && !base.has(e.path))
|
|
257
|
+
.map((e) => e.path);
|
|
258
|
+
}
|
|
189
259
|
|
|
190
260
|
/**
|
|
191
261
|
* A kernel rule, in the source tree or in any projection.
|
|
@@ -212,7 +282,11 @@ export interface GatedPaths {
|
|
|
212
282
|
}
|
|
213
283
|
|
|
214
284
|
/** Partition a changed-file list into the surfaces this gate watches. */
|
|
215
|
-
export function classifyPaths(
|
|
285
|
+
export function classifyPaths(
|
|
286
|
+
files: readonly string[],
|
|
287
|
+
derived: readonly string[] = derivedPlumbing(),
|
|
288
|
+
): GatedPaths {
|
|
289
|
+
const derivedSet = new Set(derived);
|
|
216
290
|
const kernelRules: string[] = [];
|
|
217
291
|
const governanceHooks: string[] = [];
|
|
218
292
|
const plumbing: string[] = [];
|
|
@@ -225,6 +299,7 @@ export function classifyPaths(files: readonly string[]): GatedPaths {
|
|
|
225
299
|
if (
|
|
226
300
|
p === SELF_PATH ||
|
|
227
301
|
p === READER_PATH ||
|
|
302
|
+
p === CLOSURE_READER_PATH ||
|
|
228
303
|
p === POLICY_PATH ||
|
|
229
304
|
p === WORKFLOW_PATH ||
|
|
230
305
|
ANCHOR_PATHS.includes(p)
|
|
@@ -241,7 +316,7 @@ export function classifyPaths(files: readonly string[]): GatedPaths {
|
|
|
241
316
|
governanceHooks.push(p);
|
|
242
317
|
continue;
|
|
243
318
|
}
|
|
244
|
-
if (PLUMBING_SOURCE_RE.test(p)) {
|
|
319
|
+
if (PLUMBING_SOURCE_RE.test(p) || derivedSet.has(p)) {
|
|
245
320
|
plumbing.push(p);
|
|
246
321
|
}
|
|
247
322
|
}
|
|
@@ -319,7 +394,12 @@ export function evaluate(
|
|
|
319
394
|
providerCount: number | null,
|
|
320
395
|
): GateResult {
|
|
321
396
|
const lines: string[] = [];
|
|
322
|
-
|
|
397
|
+
// The union of the closure read from the tree under review and from this
|
|
398
|
+
// script's own tree: under CI the gate code is the base revision's and
|
|
399
|
+
// `root` is the head, so a module present on either side is watched.
|
|
400
|
+
const gated = classifyPaths(files, [
|
|
401
|
+
...new Set([...derivedPlumbing(), ...derivedPlumbing(root), ...closureArrivals(root)]),
|
|
402
|
+
]);
|
|
323
403
|
const scanned = files.length;
|
|
324
404
|
|
|
325
405
|
// Every changed path is a planned target: the gate's denominator is the
|
|
@@ -332,7 +332,7 @@ export function payloadFingerprint(entryPath: string, size: number): string {
|
|
|
332
332
|
* convention and the class is empty by observation.
|
|
333
333
|
*/
|
|
334
334
|
const BOUND_PAYLOAD_EXCEPTIONS: readonly BoundPayloadException[] = [
|
|
335
|
-
{ fingerprint: '
|
|
335
|
+
{ fingerprint: '6de9a60d02af4883ab8f09171673a44924ca78ddadd55518f743e95651d3d257', path: 'agents/templates/.ai-council.yml.example', size: 31548, why: 'shipped council config TEMPLATE — a dotfile because the file it is copied to is one. RE-PINNED 2026-10-06, 30,561 -> 31,548 bytes: the exception is path-AND-size-bound by design, so step 3.1 of road-to-a-spend-bound-only-where-one-was-set moving the template cost_budget and debate ceilings to 0 (ADR-279), removing the stale "always asks before invoking" sentence and explaining both invalidated it and the gate refused the entry as unaccounted. The re-pin excepts the SAME path for the SAME reason at its measured new size — it does not widen the class, raise a cap, or except anything the gate was not already carrying. (Previously RE-PINNED 2026-09-07, 29,872 -> 30,561 bytes, for a `disabled_reason` honest-null block on the gemini seat.)' },
|
|
336
336
|
{ fingerprint: 'd36a53951c184db7481139235056e90b53a853714b3a68b31ba1349ecb95ac1d', path: 'agents/templates/.ai-video.xml.example', size: 9209, why: 'shipped video-provider config template — same reason as the council template' },
|
|
337
337
|
{ fingerprint: 'f83229412cf8cceaadd0cb1934140a0c7c8277df43edc590f370572f6df6da98', path: 'dist/agent-src/templates/agents/.gitattributes.fragment', size: 2140, why: 'gitattributes fragment the installer appends to a consumer repo' },
|
|
338
338
|
{ fingerprint: 'a4f51dba3a66c0f2c3548e77961271ed4d7b5ec8a6661ea359ff044758f0d921', path: 'src/templates/minimal/.agent-settings.yml', size: 1042, why: 'the minimal-install settings template; the dot is the consumer-side filename' },
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Release-manifest content check — the content half beside
|
|
4
|
+
* `check_release_pr_shape`.
|
|
5
|
+
*
|
|
6
|
+
* The shape check lets a release PR carry `package.json`, `package-lock.json`
|
|
7
|
+
* and the three plugin manifests, and it reads only their NAMES. Smoke is
|
|
8
|
+
* skipped on release heads (`docs/contracts/release-pr-gating.md`), so an added
|
|
9
|
+
* script, a new dependency or a changed `bin` path could ride a release PR with
|
|
10
|
+
* no gate reading it. This compares each manifest against the base and fails on
|
|
11
|
+
* any difference outside the version fields the release writes.
|
|
12
|
+
*
|
|
13
|
+
* Version fields are named per file, never matched by key name anywhere: in
|
|
14
|
+
* `package-lock.json` every dependency also has a `version`, and a key-name
|
|
15
|
+
* rule would wave a dependency bump through as a version bump.
|
|
16
|
+
*
|
|
17
|
+
* Usage: check_release_manifest_content [--base REF] [--root DIR]
|
|
18
|
+
* --base REF ref the head is compared against (default: origin/main)
|
|
19
|
+
* --root DIR repository root to read the head and run git in (testing)
|
|
20
|
+
*
|
|
21
|
+
* Exit codes: 0 = only version fields differ · 1 = other difference · 2 = usage/env.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { spawnSync } from 'node:child_process';
|
|
25
|
+
import * as fs from 'node:fs';
|
|
26
|
+
import * as path from 'node:path';
|
|
27
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
28
|
+
|
|
29
|
+
import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
30
|
+
|
|
31
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
32
|
+
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
33
|
+
|
|
34
|
+
type Segment = string;
|
|
35
|
+
/** A JSON path pattern; `*` matches any array index. */
|
|
36
|
+
type Pattern = readonly Segment[];
|
|
37
|
+
|
|
38
|
+
/** The manifests a release PR may carry, and the fields the release writes in each. */
|
|
39
|
+
export const VERSION_FIELDS: Readonly<Record<string, readonly Pattern[]>> = {
|
|
40
|
+
'package.json': [['version']],
|
|
41
|
+
'package-lock.json': [['version'], ['packages', '', 'version']],
|
|
42
|
+
'.claude-plugin/marketplace.json': [['version'], ['metadata', 'version'], ['plugins', '*', 'version']],
|
|
43
|
+
'.augment-plugin/plugin.json': [['version']],
|
|
44
|
+
'.augment-plugin/marketplace.json': [['version'], ['metadata', 'version'], ['plugins', '*', 'version']],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function _matches(p: readonly Segment[], pat: Pattern): boolean {
|
|
48
|
+
if (p.length !== pat.length) return false;
|
|
49
|
+
return pat.every((seg, i) => seg === p[i] || (seg === '*' && /^\d+$/u.test(p[i] ?? '')));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function _render(p: readonly Segment[]): string {
|
|
53
|
+
return p.length === 0 ? '(root)' : p.map((s) => (/^\d+$/u.test(s) ? `[${s}]` : JSON.stringify(s))).join('.');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function _isObject(v: unknown): v is Record<string, unknown> {
|
|
57
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Every JSON path at which `a` and `b` differ, leaf-first where both are containers. */
|
|
61
|
+
function _diffPaths(a: unknown, b: unknown, at: Segment[] = []): Segment[][] {
|
|
62
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
63
|
+
if (a.length !== b.length) return [at];
|
|
64
|
+
return a.flatMap((v, i) => _diffPaths(v, b[i], [...at, String(i)]));
|
|
65
|
+
}
|
|
66
|
+
if (_isObject(a) && _isObject(b)) {
|
|
67
|
+
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
|
|
68
|
+
const out: Segment[][] = [];
|
|
69
|
+
for (const k of keys) {
|
|
70
|
+
if (!(k in a) || !(k in b)) {
|
|
71
|
+
out.push([...at, k]);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
out.push(..._diffPaths(a[k], b[k], [...at, k]));
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
return JSON.stringify(a) === JSON.stringify(b) ? [] : [at];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The differences between `base` and `head` text of one manifest that are NOT a
|
|
83
|
+
* version-string change in a declared version field. Empty means in shape.
|
|
84
|
+
*/
|
|
85
|
+
export function manifestDifferences(file: string, base: string | null, head: string | null): string[] {
|
|
86
|
+
if (base === null && head === null) return [];
|
|
87
|
+
if (base === null) return [`${file}: added — absent at the base`];
|
|
88
|
+
if (head === null) return [`${file}: removed — present at the base`];
|
|
89
|
+
let a: unknown;
|
|
90
|
+
let b: unknown;
|
|
91
|
+
try {
|
|
92
|
+
a = JSON.parse(base);
|
|
93
|
+
b = JSON.parse(head);
|
|
94
|
+
} catch (err) {
|
|
95
|
+
return [`${file}: not parseable JSON (${(err as Error).message})`];
|
|
96
|
+
}
|
|
97
|
+
const allowed = VERSION_FIELDS[file] ?? [];
|
|
98
|
+
const out: string[] = [];
|
|
99
|
+
for (const p of _diffPaths(a, b)) {
|
|
100
|
+
const atVersion = allowed.some((pat) => _matches(p, pat));
|
|
101
|
+
let before: unknown = a;
|
|
102
|
+
let after: unknown = b;
|
|
103
|
+
for (const s of p) {
|
|
104
|
+
before = (before as Record<string, unknown> | undefined)?.[s];
|
|
105
|
+
after = (after as Record<string, unknown> | undefined)?.[s];
|
|
106
|
+
}
|
|
107
|
+
if (atVersion && typeof before === 'string' && typeof after === 'string') continue;
|
|
108
|
+
out.push(`${file}: ${_render(p)} differs outside the version fields`);
|
|
109
|
+
}
|
|
110
|
+
return out;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _git(args: readonly string[], cwd: string): { status: number; stdout: string } {
|
|
114
|
+
const r = spawnSync('git', args, { cwd, encoding: 'utf8' });
|
|
115
|
+
return { status: r.status ?? 1, stdout: r.stdout ?? '' };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function _readHead(root: string, file: string): string | null {
|
|
119
|
+
const abs = path.join(root, file);
|
|
120
|
+
return fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
124
|
+
let base = 'origin/main';
|
|
125
|
+
let root = REPO_ROOT;
|
|
126
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
127
|
+
const a = argv[i];
|
|
128
|
+
if (a === '--base' || a === '--root') {
|
|
129
|
+
const v = argv[i + 1];
|
|
130
|
+
if (v === undefined || v === '') {
|
|
131
|
+
process.stderr.write(`error: ${a} needs a value\n`);
|
|
132
|
+
return 2;
|
|
133
|
+
}
|
|
134
|
+
if (a === '--base') base = v;
|
|
135
|
+
else root = path.resolve(v);
|
|
136
|
+
i += 1;
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
process.stderr.write(`error: unknown argument ${String(a)}\n`);
|
|
140
|
+
return 2;
|
|
141
|
+
}
|
|
142
|
+
if (_git(['rev-parse', '--verify', `${base}^{commit}`], root).status !== 0) {
|
|
143
|
+
process.stderr.write(`error: base \`${base}\` does not resolve to a commit in ${root}\n`);
|
|
144
|
+
return 2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const findings: string[] = [];
|
|
148
|
+
let scanned = 0;
|
|
149
|
+
for (const file of Object.keys(VERSION_FIELDS)) {
|
|
150
|
+
const shown = _git(['show', `${base}:${file}`], root);
|
|
151
|
+
const before = shown.status === 0 ? shown.stdout : null;
|
|
152
|
+
const after = _readHead(root, file);
|
|
153
|
+
if (before !== null || after !== null) scanned += 1;
|
|
154
|
+
findings.push(...manifestDifferences(file, before, after));
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
reportScanned({ gate: 'check_release_manifest_content', scanned, units: 'manifests', roots: [root] });
|
|
158
|
+
} catch (err) {
|
|
159
|
+
if (err instanceof DeadScopeError) {
|
|
160
|
+
process.stderr.write(`${err.message}\n`);
|
|
161
|
+
return 2;
|
|
162
|
+
}
|
|
163
|
+
throw err;
|
|
164
|
+
}
|
|
165
|
+
if (findings.length > 0) {
|
|
166
|
+
process.stdout.write(
|
|
167
|
+
`❌ release manifests differ from ${base} outside their version fields:\n` +
|
|
168
|
+
findings.map((f) => ` · ${f}`).join('\n') +
|
|
169
|
+
'\n A release PR carries version bumps only. Land the other change on main via its own PR.\n',
|
|
170
|
+
);
|
|
171
|
+
return 1;
|
|
172
|
+
}
|
|
173
|
+
process.stdout.write(`✅ release manifests differ from ${base} in version fields only (${String(scanned)} read)\n`);
|
|
174
|
+
return 0;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function _isCliEntry(): boolean {
|
|
178
|
+
if (process.argv[1] === undefined) return false;
|
|
179
|
+
if (import.meta.url === pathToFileURL(path.resolve(process.argv[1])).href) return true;
|
|
180
|
+
try {
|
|
181
|
+
return fs.realpathSync(_HERE) === fs.realpathSync(path.resolve(process.argv[1]));
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
if (_isCliEntry()) {
|
|
188
|
+
process.exit(main());
|
|
189
|
+
}
|