@event4u/agent-config 16.2.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 +337 -234
- package/README.md +15 -13
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +11 -8
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- 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 +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +18 -4
- 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 +4 -4
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- 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/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +56 -42
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- 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/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- 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 +7 -7
- 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 +99 -54
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- 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 +95 -7
- 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 +1 -2
- 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 +1 -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 +1 -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 +1 -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 +6 -6
- 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 +17 -35
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +12 -16
- 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 +1 -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 +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/domain-safety-retention.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- 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 +2 -2
- package/dist/agent-src/rules/finance-safety-floor.md +1 -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 +2 -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 +1 -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 +3 -3
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +13 -17
- 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 +4 -5
- 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 +3 -4
- package/dist/agent-src/rules/question-not-instruction.md +1 -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 +4 -7
- 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 +1 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
- 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 +7 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +6 -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 +1 -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 +1 -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 +1 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -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 +1 -1
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- 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/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- 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/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- package/dist/agent-src/skills/fe-design/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/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
- 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/directives/ui/apply.ts +48 -6
- 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/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +207 -120
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
- package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- 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-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- 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 +1252 -249
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +19 -9
- package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
- package/dist/cli-delegate/cmd_explain.js +45 -21
- package/dist/cli-delegate/cmd_export.js +13 -10
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +44 -22
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +5 -4
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +14 -11
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +10 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +10 -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 +13 -10
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +14 -11
- package/dist/cli-delegate/cmd_uninstall.js +15 -12
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +9 -8
- package/dist/cli-delegate/cmd_validate.js +15 -12
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +3 -3
- package/dist/cli-delegate/roadmap_gates.js +6 -6
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +232 -141
- 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 +26 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +7 -4
- package/dist/hooks/dispatch.js +5655 -6623
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2336 -1249
- package/dist/install/installThinLayer.js +289 -0
- package/dist/install/installedRuleLinks.js +262 -0
- package/dist/install/wizard-plan.js +24 -0
- 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 +36504 -30986
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +26 -120
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- 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 +704 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +535 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- 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 +29 -81
- package/dist/shared/skillRanking.js +103 -16
- 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 +39 -67
- 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/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- 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/directives/ui/apply.ts +48 -6
- 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/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +141 -334
- 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 +10 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +23 -10
- package/src/config/gate-coverage.yml +138 -3
- package/src/config/gate-violation-baselines.json +64 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +67 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +105 -0
- package/src/config/rule-obligations.json +833 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +378 -0
- package/src/install/installedRuleLinks.ts +308 -0
- package/src/install/wizard-plan.ts +24 -0
- 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 +33 -81
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -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 +53 -4
- package/src/scripts/_lib/agent_settings.ts +26 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -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/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
- package/src/scripts/_lib/graph_feeder_record.ts +437 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +963 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- 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/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- 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/skill_origin.ts +254 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +620 -0
- package/src/scripts/_lib/touched_file_quality.ts +570 -0
- package/src/scripts/_lib/turn_end_refusals.ts +77 -2
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +8 -8
- 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_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/before_complete_hook.ts +243 -12
- 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/bench_touched_file_quality.ts +238 -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_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +196 -21
- 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 +477 -0
- package/src/scripts/check_kernel_edit_ratified.ts +132 -14
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +129 -13
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- 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/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/compile_hook_manifest.ts +33 -1
- package/src/scripts/condense.ts +14 -27
- 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/estate_exemption_shape.ts +202 -0
- 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 +21 -4
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +175 -15
- package/src/scripts/hooks/block_config_weakening.ts +115 -7
- package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +795 -60
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
- package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +78 -215
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- 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_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +465 -29
- 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 +647 -0
- 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 +173 -6
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +55 -8
- 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/project_thin_rules.ts +34 -327
- 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_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +274 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +228 -0
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_ranker_confusion.ts +543 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +101 -9
- package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -21
- package/src/server/schemas/settings.ts +42 -176
- package/src/shared/skillRanking.ts +169 -14
- package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- 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
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Composition gate for the self-hosted hook bundle: ONE YAML reader, and a
|
|
4
|
+
* byte ceiling.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS — measured, not hypothetical.
|
|
7
|
+
*
|
|
8
|
+
* Every concern is inlined into one `dist/hooks/dispatch.js` that every hook
|
|
9
|
+
* event loads, so a dependency added for ONE concern is paid by all of them on
|
|
10
|
+
* every dispatch. On 2026-10-01 the bundle carried two YAML parsers: `yaml`,
|
|
11
|
+
* pulled by six modules including the dispatcher itself, and `js-yaml`, pulled
|
|
12
|
+
* by one call site in `block_config_weakening.ts` — 95,846 bytes, 6.13 % of a
|
|
13
|
+
* 1,564,211-byte bundle. Beside it, `host_lowering.ts`'s own header said the
|
|
14
|
+
* `yaml` reader was "the ONLY remaining YAML reader on that path".
|
|
15
|
+
*
|
|
16
|
+
* Nothing in this tree could see either fact. The freshness gate compares
|
|
17
|
+
* mtimes, the content gate compares digests, and neither has an opinion about
|
|
18
|
+
* what is INSIDE the bundle. So the second parser reached a release and was
|
|
19
|
+
* found by an external rescore counting bytes — a review finding where it
|
|
20
|
+
* should have been a red check. This gate is that red check.
|
|
21
|
+
*
|
|
22
|
+
* WHAT IT CHECKS
|
|
23
|
+
*
|
|
24
|
+
* 1. At most `max_yaml_packages` YAML packages contribute bytes to the
|
|
25
|
+
* bundle. The package set is {@link YAML_PACKAGES} — a closed list, so a
|
|
26
|
+
* third parser arriving under a name nobody listed is NOT caught here, and
|
|
27
|
+
* that limit is stated rather than implied.
|
|
28
|
+
* 2. The bundle's byte count is at or under `max_bytes`.
|
|
29
|
+
*
|
|
30
|
+
* Both bounds live in `src/config/hook-bundle-budget.json`, never in this file:
|
|
31
|
+
* a number a reviewer has to read a script to find is a number nobody reviews.
|
|
32
|
+
*
|
|
33
|
+
* WHY IT BUILDS RATHER THAN READS. `dist/hooks/` is untracked, so a gate that
|
|
34
|
+
* read the bundle on disk would be local-only like `check_hook_bundle_content`
|
|
35
|
+
* — and the thing being bounded is what the SOURCE produces, which is a
|
|
36
|
+
* property of the commit rather than of a machine. So this builds its own
|
|
37
|
+
* bundle with `--metafile` and measures that. It runs identically in CI and
|
|
38
|
+
* locally, and it never touches the live bundle.
|
|
39
|
+
*
|
|
40
|
+
* The esbuild invocation is NOT duplicated here: it is read from
|
|
41
|
+
* `package.json`'s `build:hooks` script with only `--outfile=` rewritten, via
|
|
42
|
+
* the same `rewriteOutfile` helper `rebuild_hook_bundle` and
|
|
43
|
+
* `check_hook_bundle_content` use, so the flag set has exactly one home. The
|
|
44
|
+
* trailing `&& node src/scripts/write_bundle_digest.mjs` is stripped — this
|
|
45
|
+
* gate must not write a sidecar for a probe bundle nobody executes.
|
|
46
|
+
*
|
|
47
|
+
* WHAT IT DOES NOT DO. It says nothing about LATENCY. A smaller bundle is not
|
|
48
|
+
* a measured speed-up; `src/config/hook-latency-budget.json` owns that axis and
|
|
49
|
+
* no number here is derived from it. It says nothing about the install or MCP
|
|
50
|
+
* bundles, which carry their own module graphs.
|
|
51
|
+
*
|
|
52
|
+
* Usage:
|
|
53
|
+
* ./scripts-run src/scripts/check_hook_bundle_composition
|
|
54
|
+
* ./scripts-run src/scripts/check_hook_bundle_composition --metafile <path>
|
|
55
|
+
* ./scripts-run src/scripts/check_hook_bundle_composition --self-test
|
|
56
|
+
*
|
|
57
|
+
* Exit codes: 0 = within budget · 1 = over the ceiling or a second reader ·
|
|
58
|
+
* 2 = the build or the budget file failed.
|
|
59
|
+
*/
|
|
60
|
+
// ledger-exempt: single-artefact transaction — one bundle build yielding one
|
|
61
|
+
// aggregate verdict. The per-source accounting lives in
|
|
62
|
+
// check_hook_bundle_freshness, which carries the ledger.
|
|
63
|
+
import { spawnSync } from 'node:child_process';
|
|
64
|
+
import * as fs from 'node:fs';
|
|
65
|
+
import * as os from 'node:os';
|
|
66
|
+
import * as path from 'node:path';
|
|
67
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
68
|
+
|
|
69
|
+
import { runGateCli, runSelfTest } from './_lib/gate_self_test.js';
|
|
70
|
+
import { reportScanned } from './_lib/scan_scope.js';
|
|
71
|
+
import { buildScript } from './check_hook_bundle_content.js';
|
|
72
|
+
import { rewriteOutfile } from './rebuild_hook_bundle.js';
|
|
73
|
+
|
|
74
|
+
const _HERE = path.dirname(fileURLToPath(import.meta.url));
|
|
75
|
+
export const REPO_ROOT = path.resolve(_HERE, '..', '..');
|
|
76
|
+
export const SELF_REL = path.join('src', 'scripts', 'check_hook_bundle_composition.ts');
|
|
77
|
+
export const BUDGET_REL = path.join('src', 'config', 'hook-bundle-budget.json');
|
|
78
|
+
const LIVE_REL = path.join('dist', 'hooks', 'dispatch.js');
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The YAML readers this tree has ever carried, by package name.
|
|
82
|
+
*
|
|
83
|
+
* A CLOSED LIST, and the consequence is worth stating: a fourth parser arriving
|
|
84
|
+
* under a name not listed here contributes bytes the ceiling catches and the
|
|
85
|
+
* parser cap does not. The alternative — treating any package whose name
|
|
86
|
+
* contains "yaml" as a reader — would match a YAML-adjacent utility that is not
|
|
87
|
+
* a parser, and a cap that fires on the wrong package is worse than one that
|
|
88
|
+
* misses an unlisted one, because the first teaches people to raise the cap.
|
|
89
|
+
*/
|
|
90
|
+
export const YAML_PACKAGES: readonly string[] = ['yaml', 'js-yaml', 'yaml-js', 'yamljs'];
|
|
91
|
+
|
|
92
|
+
export interface Budget {
|
|
93
|
+
readonly maxBytes: number;
|
|
94
|
+
readonly maxYamlPackages: number;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface Analysis {
|
|
98
|
+
/** Bytes of the bundle esbuild produced. */
|
|
99
|
+
readonly bytes: number;
|
|
100
|
+
/** Modules that contributed to it — the unit `scanned:` reports. */
|
|
101
|
+
readonly modules: number;
|
|
102
|
+
/** Bytes in the output, summed per owning npm package. */
|
|
103
|
+
readonly packageBytes: ReadonlyMap<string, number>;
|
|
104
|
+
/** Listed YAML packages contributing more than zero bytes, sorted. */
|
|
105
|
+
readonly yamlPackages: readonly string[];
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* The npm package a bundled module path belongs to, or `null` for this tree's
|
|
110
|
+
* own sources.
|
|
111
|
+
*
|
|
112
|
+
* Reads the INNERMOST `node_modules/` segment: a transitively nested copy
|
|
113
|
+
* (`node_modules/a/node_modules/yaml/…`) is still `yaml`'s bytes, and
|
|
114
|
+
* attributing it to `a` would let a second parser hide one level down.
|
|
115
|
+
*/
|
|
116
|
+
export function owningPackage(modulePath: string): string | null {
|
|
117
|
+
const marker = 'node_modules/';
|
|
118
|
+
const at = modulePath.lastIndexOf(marker);
|
|
119
|
+
if (at < 0) return null;
|
|
120
|
+
const parts = modulePath.slice(at + marker.length).split('/');
|
|
121
|
+
const head = parts[0] ?? '';
|
|
122
|
+
if (head === '') return null;
|
|
123
|
+
return head.startsWith('@') ? `${head}/${parts[1] ?? ''}` : head;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Parse an esbuild metafile and attribute its one entry-point output. */
|
|
127
|
+
export function analyzeMetafile(json: string): Analysis {
|
|
128
|
+
const meta = JSON.parse(json) as {
|
|
129
|
+
outputs?: Record<string, { entryPoint?: string; bytes?: number; inputs?: Record<string, { bytesInOutput?: number }> }>;
|
|
130
|
+
};
|
|
131
|
+
const outputs = Object.values(meta.outputs ?? {});
|
|
132
|
+
const out = outputs.find((o) => typeof o.entryPoint === 'string');
|
|
133
|
+
if (out === undefined) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
'metafile carries no entry-point output — refusing to report a composition for a build that produced nothing',
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
const packageBytes = new Map<string, number>();
|
|
139
|
+
const inputs = out.inputs ?? {};
|
|
140
|
+
for (const [p, v] of Object.entries(inputs)) {
|
|
141
|
+
const pkg = owningPackage(p);
|
|
142
|
+
if (pkg === null) continue;
|
|
143
|
+
packageBytes.set(pkg, (packageBytes.get(pkg) ?? 0) + (v.bytesInOutput ?? 0));
|
|
144
|
+
}
|
|
145
|
+
const yamlPackages = YAML_PACKAGES.filter((p) => (packageBytes.get(p) ?? 0) > 0).sort();
|
|
146
|
+
return {
|
|
147
|
+
bytes: out.bytes ?? 0,
|
|
148
|
+
modules: Object.keys(inputs).length,
|
|
149
|
+
packageBytes,
|
|
150
|
+
yamlPackages,
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Read the committed budget. Throws rather than defaulting — a guessed ceiling bounds nothing. */
|
|
155
|
+
export function readBudget(root: string): Budget {
|
|
156
|
+
const abs = path.join(root, BUDGET_REL);
|
|
157
|
+
const raw = JSON.parse(fs.readFileSync(abs, 'utf-8')) as Record<string, unknown>;
|
|
158
|
+
const maxBytes = raw['max_bytes'];
|
|
159
|
+
const maxYaml = raw['max_yaml_packages'];
|
|
160
|
+
if (typeof maxBytes !== 'number' || !Number.isInteger(maxBytes) || maxBytes <= 0) {
|
|
161
|
+
throw new Error(`${BUDGET_REL}: max_bytes must be a positive integer, got ${JSON.stringify(maxBytes)}`);
|
|
162
|
+
}
|
|
163
|
+
if (typeof maxYaml !== 'number' || !Number.isInteger(maxYaml) || maxYaml < 1) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
`${BUDGET_REL}: max_yaml_packages must be an integer >= 1, got ${JSON.stringify(maxYaml)}`,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
return { maxBytes, maxYamlPackages: maxYaml };
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Findings, one line each. Empty means within budget. */
|
|
172
|
+
export function verdict(a: Analysis, budget: Budget): string[] {
|
|
173
|
+
const findings: string[] = [];
|
|
174
|
+
if (a.yamlPackages.length > budget.maxYamlPackages) {
|
|
175
|
+
const detail = a.yamlPackages
|
|
176
|
+
.map((p) => `${p} (${String(a.packageBytes.get(p) ?? 0)} B)`)
|
|
177
|
+
.join(', ');
|
|
178
|
+
findings.push(
|
|
179
|
+
`${String(a.yamlPackages.length)} YAML readers in the bundle, cap is ` +
|
|
180
|
+
`${String(budget.maxYamlPackages)}: ${detail}. Every dispatch loads all of them. ` +
|
|
181
|
+
'Move the odd caller onto the reader the dispatcher already uses.',
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
if (a.bytes > budget.maxBytes) {
|
|
185
|
+
findings.push(
|
|
186
|
+
`bundle is ${String(a.bytes)} bytes, ceiling is ${String(budget.maxBytes)} ` +
|
|
187
|
+
`(over by ${String(a.bytes - budget.maxBytes)}). Shrink the bundle, or raise ` +
|
|
188
|
+
`max_bytes in ${BUDGET_REL} WITH an entry in its raise_log naming what was added.`,
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
return findings;
|
|
192
|
+
}
|
|
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
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Build the hook bundle to a throwaway path and return its metafile JSON.
|
|
285
|
+
*
|
|
286
|
+
* Throws on any failure rather than returning a sentinel: a composition read off
|
|
287
|
+
* a build that did not happen is the silent-green this gate exists to replace.
|
|
288
|
+
*/
|
|
289
|
+
export function buildMetafile(root: string): string {
|
|
290
|
+
const script = buildScript(root);
|
|
291
|
+
if (script === null) {
|
|
292
|
+
throw new Error('package.json has no `build:hooks` script — refusing to guess the flag set');
|
|
293
|
+
}
|
|
294
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'hook-bundle-composition-'));
|
|
295
|
+
const outFile = path.join(dir, 'dispatch.js');
|
|
296
|
+
const metaFile = path.join(dir, 'meta.json');
|
|
297
|
+
try {
|
|
298
|
+
const rewritten = rewriteOutfile(script, outFile);
|
|
299
|
+
if (rewritten === null) {
|
|
300
|
+
throw new Error(`\`build:hooks\` no longer writes ${LIVE_REL} — refusing to build to a guessed path`);
|
|
301
|
+
}
|
|
302
|
+
// Strip the digest sidecar step: it would write `dispatch.sha256` beside
|
|
303
|
+
// a probe bundle nobody executes, and on failure it would mask the build
|
|
304
|
+
// error with a file-not-found.
|
|
305
|
+
const esbuildOnly = rewritten.split('&& node ')[0] ?? rewritten;
|
|
306
|
+
const built = spawnSync('sh', ['-c', `${esbuildOnly.trim()} --metafile=${metaFile}`], {
|
|
307
|
+
cwd: root,
|
|
308
|
+
encoding: 'utf-8',
|
|
309
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
310
|
+
env: {
|
|
311
|
+
...process.env,
|
|
312
|
+
PATH: `${path.join(root, 'node_modules', '.bin')}:${process.env['PATH'] ?? ''}`,
|
|
313
|
+
},
|
|
314
|
+
});
|
|
315
|
+
if (built.status !== 0 || !fs.existsSync(metaFile)) {
|
|
316
|
+
throw new Error(`esbuild failed (exit ${String(built.status)})\n${built.stderr ?? ''}`);
|
|
317
|
+
}
|
|
318
|
+
return fs.readFileSync(metaFile, 'utf-8');
|
|
319
|
+
} finally {
|
|
320
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function selfTest(): number {
|
|
325
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'hook-bundle-composition-selftest-'));
|
|
326
|
+
const write = (name: string, body: unknown): string => {
|
|
327
|
+
const p = path.join(dir, name);
|
|
328
|
+
fs.writeFileSync(p, JSON.stringify(body));
|
|
329
|
+
return p;
|
|
330
|
+
};
|
|
331
|
+
const meta = (bytes: number, inputs: Record<string, number>): unknown => ({
|
|
332
|
+
inputs: {},
|
|
333
|
+
outputs: {
|
|
334
|
+
'dist/hooks/dispatch.js': {
|
|
335
|
+
entryPoint: 'src/scripts/hooks/dispatch_entry.ts',
|
|
336
|
+
bytes,
|
|
337
|
+
inputs: Object.fromEntries(
|
|
338
|
+
Object.entries(inputs).map(([k, v]) => [k, { bytesInOutput: v }]),
|
|
339
|
+
),
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
const budget = readBudget(REPO_ROOT);
|
|
344
|
+
const clean = write('clean.json', meta(budget.maxBytes, { 'node_modules/yaml/dist/index.js': 10 }));
|
|
345
|
+
const twoReaders = write(
|
|
346
|
+
'two-readers.json',
|
|
347
|
+
meta(1000, {
|
|
348
|
+
'node_modules/yaml/dist/index.js': 600,
|
|
349
|
+
'node_modules/js-yaml/dist/js-yaml.mjs': 400,
|
|
350
|
+
}),
|
|
351
|
+
);
|
|
352
|
+
const oneOver = write('one-over.json', meta(budget.maxBytes + 1, { 'src/x.ts': 1 }));
|
|
353
|
+
const empty = write('empty.json', { inputs: {}, outputs: {} });
|
|
354
|
+
|
|
355
|
+
const drive = (fixture: string): number =>
|
|
356
|
+
runGateCli(REPO_ROOT, SELF_REL, ['--metafile', fixture], REPO_ROOT);
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
return runSelfTest({
|
|
360
|
+
gate: 'check_hook_bundle_composition',
|
|
361
|
+
minCases: 4,
|
|
362
|
+
minRejectCases: 3,
|
|
363
|
+
cases: [
|
|
364
|
+
{
|
|
365
|
+
name: 'one YAML reader, exactly at the ceiling → accepted',
|
|
366
|
+
expect: 'accept',
|
|
367
|
+
run: () => drive(clean),
|
|
368
|
+
},
|
|
369
|
+
{
|
|
370
|
+
name: 'a second YAML reader → rejected',
|
|
371
|
+
expect: 'reject',
|
|
372
|
+
run: () => drive(twoReaders),
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
name: 'one byte over the ceiling → rejected',
|
|
376
|
+
expect: 'reject',
|
|
377
|
+
run: () => drive(oneOver),
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
name: 'a metafile with no entry-point output → rejected, never reported as 0 bytes',
|
|
381
|
+
expect: 'reject',
|
|
382
|
+
run: () => drive(empty),
|
|
383
|
+
},
|
|
384
|
+
],
|
|
385
|
+
});
|
|
386
|
+
} finally {
|
|
387
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
export function main(argv: readonly string[] = process.argv.slice(2), root: string = REPO_ROOT): number {
|
|
392
|
+
if (argv.includes('--self-test')) {
|
|
393
|
+
return selfTest();
|
|
394
|
+
}
|
|
395
|
+
const at = argv.indexOf('--metafile');
|
|
396
|
+
const fixture = at >= 0 ? argv[at + 1] : undefined;
|
|
397
|
+
|
|
398
|
+
let budget: Budget;
|
|
399
|
+
try {
|
|
400
|
+
budget = readBudget(root);
|
|
401
|
+
} catch (e) {
|
|
402
|
+
process.stderr.write(`check_hook_bundle_composition: ${String(e instanceof Error ? e.message : e)}\n`);
|
|
403
|
+
return 2;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
let json: string;
|
|
407
|
+
try {
|
|
408
|
+
json = fixture === undefined ? buildMetafile(root) : fs.readFileSync(fixture, 'utf-8');
|
|
409
|
+
} catch (e) {
|
|
410
|
+
process.stderr.write(`check_hook_bundle_composition: ${String(e instanceof Error ? e.message : e)}\n`);
|
|
411
|
+
return 2;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
let analysis: Analysis;
|
|
415
|
+
try {
|
|
416
|
+
analysis = analyzeMetafile(json);
|
|
417
|
+
} catch (e) {
|
|
418
|
+
process.stderr.write(`check_hook_bundle_composition: ${String(e instanceof Error ? e.message : e)}\n`);
|
|
419
|
+
return 1;
|
|
420
|
+
}
|
|
421
|
+
|
|
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
|
+
}
|
|
441
|
+
// Reported on BOTH paths, red included: a census that goes quiet exactly
|
|
442
|
+
// when the gate fires is the shape `reportScanned` exists to prevent.
|
|
443
|
+
reportScanned({
|
|
444
|
+
gate: 'check_hook_bundle_composition',
|
|
445
|
+
scanned: analysis.modules,
|
|
446
|
+
units: 'bundled module(s)',
|
|
447
|
+
roots: [LIVE_REL],
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
if (findings.length === 0) {
|
|
451
|
+
process.stdout.write(
|
|
452
|
+
`✅ hook bundle: ${String(analysis.bytes)} B / ${String(budget.maxBytes)} B ceiling, ` +
|
|
453
|
+
`${String(analysis.yamlPackages.length)} YAML reader(s) ` +
|
|
454
|
+
`[${analysis.yamlPackages.join(', ')}], ${String(analysis.modules)} modules\n`,
|
|
455
|
+
);
|
|
456
|
+
return 0;
|
|
457
|
+
}
|
|
458
|
+
process.stderr.write('❌ hook bundle composition is out of budget\n');
|
|
459
|
+
for (const f of findings) process.stderr.write(` - ${f}\n`);
|
|
460
|
+
process.stderr.write(` Budget: ${BUDGET_REL}\n`);
|
|
461
|
+
return 1;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function _isCliEntry(): boolean {
|
|
465
|
+
const argv1 = process.argv[1];
|
|
466
|
+
if (argv1 === undefined) return false;
|
|
467
|
+
if (import.meta.url === pathToFileURL(path.resolve(argv1)).href) return true;
|
|
468
|
+
try {
|
|
469
|
+
return fs.realpathSync(fileURLToPath(import.meta.url)) === fs.realpathSync(path.resolve(argv1));
|
|
470
|
+
} catch {
|
|
471
|
+
return false;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (_isCliEntry()) {
|
|
476
|
+
process.exit(main());
|
|
477
|
+
}
|
|
@@ -30,12 +30,16 @@
|
|
|
30
30
|
*
|
|
31
31
|
* Gated: a diff touching a kernel rule under `src/rules/` (the nine of
|
|
32
32
|
* `_lib/kernel_rules.ts`), a governance hook (`src/scripts/hooks/block_*.ts`),
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
33
|
+
* hook plumbing — the YAML sources `hook_manifest.yaml` and
|
|
34
|
+
* `host_lowering.yaml`, their compiled siblings `hook_manifest.json` and
|
|
35
|
+
* `host_lowering.json`, the dispatcher `hooks/dispatch_hook.ts`, the
|
|
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
|
|
40
|
+
* be weakened without its own record. Such a diff must carry a ratification
|
|
41
|
+
* artifact under the ratifications directory with `verdict: ratified`, valid
|
|
42
|
+
* per the ratification-artifact contract.
|
|
39
43
|
*
|
|
40
44
|
* Every kernel diff is gated, not only the authority-EXPANDING ones.
|
|
41
45
|
* ADR-268 § 4 makes the artifact required for authority-expanding edits. Which
|
|
@@ -71,6 +75,7 @@ import * as path from 'node:path';
|
|
|
71
75
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
72
76
|
|
|
73
77
|
import { GateLedger } from './_lib/gate_ledger.js';
|
|
78
|
+
import { dispatchImportClosure, verdictModules } from './_lib/dispatch_import_closure.js';
|
|
74
79
|
import { is_kernel_rule } from './_lib/kernel_rules.js';
|
|
75
80
|
import { reportScanned } from './_lib/scan_scope.js';
|
|
76
81
|
import {
|
|
@@ -90,6 +95,13 @@ export const SELF_PATH = 'src/scripts/check_kernel_edit_ratified.ts';
|
|
|
90
95
|
/** The reader this gate shares with the artifact contract, also self-watched. */
|
|
91
96
|
export const READER_PATH = 'src/scripts/_lib/ratification_artifact.ts';
|
|
92
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
|
+
|
|
93
105
|
/** The in-repo quorum policy. Present on every runner; absent means refuse. */
|
|
94
106
|
export const POLICY_PATH = 'src/config/ratification-policy.json';
|
|
95
107
|
|
|
@@ -141,13 +153,109 @@ const GOVERNANCE_HOOK_RE = /^src\/scripts\/hooks\/block_[a-z0-9_]+\.ts$/;
|
|
|
141
153
|
* and every property the gate already had — base-revision code, in-repo quorum
|
|
142
154
|
* policy, the whole diff as its unit — carries over unchanged.
|
|
143
155
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
147
|
-
*
|
|
156
|
+
* `dist/hooks/dispatch.js` and `hooks/hooks.json` are BUILD OUTPUTS that no
|
|
157
|
+
* reader in this repository opens — they exist only inside an installed
|
|
158
|
+
* consumer tree — and they have no legitimate hand edit at all, so they are
|
|
159
|
+
* refused at tool-call time by `block_plumbing_writes.ts` rather than ratified
|
|
160
|
+
* here: a record for a file whose every hand edit is illegitimate would be a
|
|
161
|
+
* record of a mistake.
|
|
162
|
+
*
|
|
163
|
+
* road-to-blocking-severities 2.1 WIDENS this from "sources only" to
|
|
164
|
+
* "everything the dispatcher serves at runtime", and the distinction above is
|
|
165
|
+
* why the two compiled tables land here while the two build outputs do not.
|
|
166
|
+
* `src/scripts/hook_manifest.json` and `src/scripts/hooks/host_lowering.json`
|
|
167
|
+
* are committed in THIS tree and are READ IN PREFERENCE to the YAML they were
|
|
168
|
+
* compiled from — `dispatch_hook._load_yaml` and `host_lowering.resolveTable`
|
|
169
|
+
* both serve the compiled body whenever its fingerprint matches. A diff that
|
|
170
|
+
* edits only the JSON therefore changes what every host is told about which
|
|
171
|
+
* concern runs and which slot can refuse, while the gated YAML beside it is
|
|
172
|
+
* untouched. That is the same authority change as editing the source, reached
|
|
173
|
+
* one file later, and before this step it carried no record.
|
|
174
|
+
*
|
|
175
|
+
* Two further files join for the same reason rather than by analogy:
|
|
176
|
+
*
|
|
177
|
+
* - `src/scripts/hooks/dispatch_hook.ts` is the dispatcher itself. Every
|
|
178
|
+
* concern's verdict passes through it, so an edit here can suppress a
|
|
179
|
+
* refusal without touching the manifest that declares it.
|
|
180
|
+
* - `src/scripts/_lib/kernel_rules.ts` is the list of the nine kernel rules.
|
|
181
|
+
* THIS GATE resolves `is_kernel_rule` through it, so a diff removing a name
|
|
182
|
+
* from that list removes a rule from this gate's own reach — the one edit
|
|
183
|
+
* that could quietly narrow the gate while every other path kept working.
|
|
184
|
+
*
|
|
185
|
+
* Both are also denied at tool-call time, `dispatch_hook.ts` through the
|
|
186
|
+
* plumbing guard's source set and `kernel_rules.ts` through the kernel-rule
|
|
187
|
+
* write guard, so the pairing of deny-plus-record matches what the YAML sources
|
|
188
|
+
* already had.
|
|
189
|
+
*
|
|
190
|
+
* The compiled tables are REGENERATED, not hand-edited, by
|
|
191
|
+
* `./scripts-run src/scripts/compile_hook_manifest --table manifest|host-lowering`,
|
|
192
|
+
* which runs as a script rather than as an edit tool call and so is not what
|
|
193
|
+
* either mechanism refuses. A regeneration lands in the same diff as the YAML
|
|
194
|
+
* change that caused it, which already requires a record; the case this closes
|
|
195
|
+
* is the diff where only the JSON moves.
|
|
148
196
|
*/
|
|
149
197
|
const PLUMBING_SOURCE_RE =
|
|
150
|
-
/^(?:src\/scripts\/hook_manifest\.yaml|src\/scripts\/hooks\/host_lowering\.yaml|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
|
+
}
|
|
151
259
|
|
|
152
260
|
/**
|
|
153
261
|
* A kernel rule, in the source tree or in any projection.
|
|
@@ -174,7 +282,11 @@ export interface GatedPaths {
|
|
|
174
282
|
}
|
|
175
283
|
|
|
176
284
|
/** Partition a changed-file list into the surfaces this gate watches. */
|
|
177
|
-
export function classifyPaths(
|
|
285
|
+
export function classifyPaths(
|
|
286
|
+
files: readonly string[],
|
|
287
|
+
derived: readonly string[] = derivedPlumbing(),
|
|
288
|
+
): GatedPaths {
|
|
289
|
+
const derivedSet = new Set(derived);
|
|
178
290
|
const kernelRules: string[] = [];
|
|
179
291
|
const governanceHooks: string[] = [];
|
|
180
292
|
const plumbing: string[] = [];
|
|
@@ -187,6 +299,7 @@ export function classifyPaths(files: readonly string[]): GatedPaths {
|
|
|
187
299
|
if (
|
|
188
300
|
p === SELF_PATH ||
|
|
189
301
|
p === READER_PATH ||
|
|
302
|
+
p === CLOSURE_READER_PATH ||
|
|
190
303
|
p === POLICY_PATH ||
|
|
191
304
|
p === WORKFLOW_PATH ||
|
|
192
305
|
ANCHOR_PATHS.includes(p)
|
|
@@ -203,7 +316,7 @@ export function classifyPaths(files: readonly string[]): GatedPaths {
|
|
|
203
316
|
governanceHooks.push(p);
|
|
204
317
|
continue;
|
|
205
318
|
}
|
|
206
|
-
if (PLUMBING_SOURCE_RE.test(p)) {
|
|
319
|
+
if (PLUMBING_SOURCE_RE.test(p) || derivedSet.has(p)) {
|
|
207
320
|
plumbing.push(p);
|
|
208
321
|
}
|
|
209
322
|
}
|
|
@@ -281,7 +394,12 @@ export function evaluate(
|
|
|
281
394
|
providerCount: number | null,
|
|
282
395
|
): GateResult {
|
|
283
396
|
const lines: string[] = [];
|
|
284
|
-
|
|
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
|
+
]);
|
|
285
403
|
const scanned = files.length;
|
|
286
404
|
|
|
287
405
|
// Every changed path is a planned target: the gate's denominator is the
|