@event4u/agent-config 16.3.0 → 16.5.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 +178 -78
- 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/design.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/design-antipatterns.md +1 -1
- 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 +14 -48
- 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/accessibility-auditor/data/aria-patterns.csv +2 -0
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- 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/canvas-design/SKILL.md +4 -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-intelligence/references/integration-mapping.md +13 -10
- 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/fe-design/references/design-patterns.md +49 -2
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
- 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 +4 -2
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
- 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/svg-authoring/SKILL.md +318 -0
- 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/skills/wireframe/SKILL.md +5 -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/commands/uiRender.js +201 -4
- package/dist/cli/commands/uiRenderAsset.js +1308 -0
- package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
- package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
- package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
- package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
- package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
- package/dist/cli/main.js +47 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/check_update_banner.js +195 -119
- package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-4DBG5K7M.js} +5 -5
- 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-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-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-NQ7WT43P.js → chunk-TMWYUOK4.js} +75 -12
- 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 +489 -238
- 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 +171 -107
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +7 -7
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +41 -22
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -11
- package/dist/hooks/dispatch.js +2520 -1924
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/svg_rules.chunk.cjs +1626 -0
- package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
- 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 +3063 -2160
- 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/scan_scope.js +105 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/svg_dom.js +284 -0
- package/dist/scripts/_lib/svg_geometry.js +477 -0
- package/dist/scripts/_lib/svg_motion.js +179 -0
- package/dist/scripts/_lib/svg_rules.js +1337 -0
- package/dist/scripts/_lib/svg_urls.js +67 -0
- package/dist/scripts/_lib/thin_rules.js +80 -3
- package/dist/scripts/lint_svg_asset.js +324 -0
- 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/design-antipatterns.md +1 -1
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +4 -3
- 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 +56 -16
- 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 +55 -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/svg_dom.ts +318 -0
- package/src/scripts/_lib/svg_geometry.ts +480 -0
- package/src/scripts/_lib/svg_motion.ts +208 -0
- package/src/scripts/_lib/svg_rules.ts +1453 -0
- package/src/scripts/_lib/svg_urls.ts +72 -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/verification_command.ts +59 -0
- package/src/scripts/_lib/verify_clause.ts +27 -17
- 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/block_speaking_inbox_dir.ts +16 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/design_pass_hook.ts +157 -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/svg_obligation.ts +497 -0
- package/src/scripts/hooks/svg_rules_loader.ts +133 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
- package/src/scripts/hooks/turn_end_gate_hook.ts +6 -3
- 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_consolidation_lineage.ts +6 -3
- package/src/scripts/lint_design_quality.ts +60 -12
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_motion_authority_drift.ts +27 -0
- 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_svg_asset.ts +365 -0
- 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
|
@@ -46,7 +46,9 @@
|
|
|
46
46
|
* branch AS THE SERVER REPORTS IT (`ls-remote --symref`), then the local
|
|
47
47
|
* `origin/HEAD`, then `main`. Every message names the base AND where it came
|
|
48
48
|
* from, because "I checked main because that is the PR's base" and "I checked
|
|
49
|
-
* main because I could not ask" must never print the same.
|
|
49
|
+
* main because I could not ask" must never print the same. An explicit
|
|
50
|
+
* `--base` given a blank value exits 2: an unset variable is not a request for
|
|
51
|
+
* the PR's base.
|
|
50
52
|
*
|
|
51
53
|
* The server symref matters for the same reason `remoteHead` exists: the local
|
|
52
54
|
* `refs/remotes/origin/HEAD` is written at clone time and never refreshed, so
|
|
@@ -67,6 +69,9 @@ import { fileURLToPath } from "node:url";
|
|
|
67
69
|
|
|
68
70
|
import { runGateCli, runSelfTest, type SelfTestCase } from "./_lib/gate_self_test.js";
|
|
69
71
|
import { workspaceIdentity } from "./_lib/git_common_dir.js";
|
|
72
|
+
import { describeRefusal, isRefusal, type GitConventionReading } from "./_lib/git_convention.js";
|
|
73
|
+
import { baseValueError, parseBaseRef } from "./_lib/git_base_ref.js";
|
|
74
|
+
import { makeTargetDeps, parseExactHeadSha, readCommittedConvention } from "./_lib/git_convention_carrier.js";
|
|
70
75
|
import { reportScanned } from "./_lib/scan_scope.js";
|
|
71
76
|
|
|
72
77
|
// ledger-exempt: single remote-ref probe — the entire scope is ONE ls-remote answer (0 or 1 refs) resolved to one aggregate ancestor verdict, and every empty path already publishes its reason via reportScanned allowEmpty; there is no per-target collection to account.
|
|
@@ -125,6 +130,8 @@ export type BaseSource =
|
|
|
125
130
|
export interface BaseResolution {
|
|
126
131
|
readonly base: string;
|
|
127
132
|
readonly source: BaseSource;
|
|
133
|
+
/** The remote `base` is a branch on; absent means origin. */
|
|
134
|
+
readonly remote?: string;
|
|
128
135
|
readonly pr?: number;
|
|
129
136
|
/** Present iff an open PR against a different base could not be ruled out. */
|
|
130
137
|
readonly unverified?: string;
|
|
@@ -270,6 +277,19 @@ export function explicitBase(argv: readonly string[]): string | null {
|
|
|
270
277
|
return explicit;
|
|
271
278
|
}
|
|
272
279
|
|
|
280
|
+
/**
|
|
281
|
+
* The usage error for a `--base` given a blank value, in either spelling, or
|
|
282
|
+
* null. A missing value or one that is another flag stays `explicitBase`'s
|
|
283
|
+
* typo case.
|
|
284
|
+
*/
|
|
285
|
+
export function blankExplicitBase(argv: readonly string[]): string | null {
|
|
286
|
+
const joined = argv.find((a) => a.startsWith("--base="));
|
|
287
|
+
if (joined !== undefined) return baseValueError(joined.slice("--base=".length));
|
|
288
|
+
const flag = argv.indexOf("--base");
|
|
289
|
+
const value = flag < 0 ? undefined : argv[flag + 1];
|
|
290
|
+
return value === undefined || value.startsWith("--") ? null : baseValueError(value);
|
|
291
|
+
}
|
|
292
|
+
|
|
273
293
|
/** `--base` wins, then the open PR's base, then the repo default. */
|
|
274
294
|
export function resolveBase(
|
|
275
295
|
argv: readonly string[],
|
|
@@ -277,8 +297,11 @@ export function resolveBase(
|
|
|
277
297
|
forge: (b: string) => ForgeAnswer = (b) => askForgeForBase(b),
|
|
278
298
|
): BaseResolution {
|
|
279
299
|
const explicit = explicitBase(argv);
|
|
280
|
-
|
|
281
|
-
|
|
300
|
+
const given = explicit === null ? null : parseBaseRef(explicit, configuredRemotes());
|
|
301
|
+
if (given !== null) {
|
|
302
|
+
return given.remote === "origin"
|
|
303
|
+
? { base: given.branch, source: "flag" }
|
|
304
|
+
: { base: given.branch, source: "flag", remote: given.remote };
|
|
282
305
|
}
|
|
283
306
|
// There is deliberately no cheap short-circuit for "the branch looks like the
|
|
284
307
|
// default". It was tried and reverted: it decided a VERDICT from
|
|
@@ -321,12 +344,25 @@ export function describeBase(r: BaseResolution): string {
|
|
|
321
344
|
|
|
322
345
|
/** The SHA the server reports for `<base>` right now — never a tracking ref. */
|
|
323
346
|
export function remoteHead(base: string, remote = "origin"): string | null {
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
347
|
+
const state = remoteHeadState(base, remote);
|
|
348
|
+
return state.kind === "sha" ? state.sha : null;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* `absent` is a remote that answered without the branch, which is a misspelled
|
|
353
|
+
* or deleted base; only a remote that did not answer is `unreachable`.
|
|
354
|
+
*/
|
|
355
|
+
export type RemoteHeadState = { kind: "sha"; sha: string } | { kind: "absent" } | { kind: "unreachable" };
|
|
356
|
+
|
|
357
|
+
export function remoteHeadState(base: string, remote = "origin"): RemoteHeadState {
|
|
358
|
+
const out = git(["ls-remote", remote, `refs/heads/${base}`]);
|
|
359
|
+
if (out === null) return { kind: "unreachable" };
|
|
360
|
+
const sha = parseExactHeadSha(out, base);
|
|
361
|
+
return sha === null ? { kind: "absent" } : { kind: "sha", sha };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function configuredRemotes(): string[] {
|
|
365
|
+
return (git(["remote"]) ?? "").split("\n").map((r) => r.trim()).filter((r) => r !== "");
|
|
330
366
|
}
|
|
331
367
|
|
|
332
368
|
/**
|
|
@@ -525,6 +561,52 @@ function scanReport(scanned: number, allowEmpty?: string): void {
|
|
|
525
561
|
});
|
|
526
562
|
}
|
|
527
563
|
|
|
564
|
+
/** Whether this checkout's task runner defines `push-ready`, the remedy only this package ships. */
|
|
565
|
+
function definesPushReady(): boolean {
|
|
566
|
+
const root = git(["rev-parse", "--show-toplevel"]);
|
|
567
|
+
if (root === null || root === "") return false;
|
|
568
|
+
try {
|
|
569
|
+
return /^ {2}push-ready:/m.test(fs.readFileSync(path.join(root, "taskfiles", "dev.yml"), "utf8"));
|
|
570
|
+
} catch {
|
|
571
|
+
return false;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* What to do about a behind branch. A merge command is printed only under
|
|
577
|
+
* `merge`: under any other strategy, or one that cannot be read, a merge of the
|
|
578
|
+
* base is the commit the declaration excludes.
|
|
579
|
+
*/
|
|
580
|
+
export function behindRemedy(base: string, strategy: GitConventionReading | null, remote = "origin"): string[] {
|
|
581
|
+
if (strategy === null) {
|
|
582
|
+
return [
|
|
583
|
+
" git.update_strategy was not read — the base's commit is not in this checkout, and",
|
|
584
|
+
" this gate does not fetch to choose advice. Update the branch per the git-workflow",
|
|
585
|
+
" skill's references/branch-update.md, then regenerate (./agent-config roadmap:progress)",
|
|
586
|
+
" and re-run the gates before pushing.",
|
|
587
|
+
];
|
|
588
|
+
}
|
|
589
|
+
if (!isRefusal(strategy.state) && strategy.value === "merge") {
|
|
590
|
+
return [
|
|
591
|
+
` git fetch ${remote} && git merge ${remote}/${base}`,
|
|
592
|
+
" ./agent-config roadmap:progress # regenerate AFTER every merge, not only on conflict",
|
|
593
|
+
" then re-run the gates before pushing.",
|
|
594
|
+
"",
|
|
595
|
+
" The dashboard line is not optional housekeeping: the roadmap dashboard is a",
|
|
596
|
+
" GENERATED file, so a merge that lands cleanly still leaves it describing",
|
|
597
|
+
" neither side's roadmap set. A clean auto-merge of a generated file is still wrong.",
|
|
598
|
+
];
|
|
599
|
+
}
|
|
600
|
+
const why = isRefusal(strategy.state)
|
|
601
|
+
? ` git.update_strategy cannot be read — ${describeRefusal(strategy)}`
|
|
602
|
+
: ` git.update_strategy is \`${strategy.value ?? "?"}\` — the base is not merged in.`;
|
|
603
|
+
return [
|
|
604
|
+
why,
|
|
605
|
+
" Update the branch per the git-workflow skill's references/branch-update.md,",
|
|
606
|
+
" then regenerate (./agent-config roadmap:progress) and re-run the gates before pushing.",
|
|
607
|
+
];
|
|
608
|
+
}
|
|
609
|
+
|
|
528
610
|
/**
|
|
529
611
|
* @param forge Seam for the forge lookup. Production passes nothing and gets the
|
|
530
612
|
* real `gh`. Tests MUST pass a stub: without this parameter the only way to
|
|
@@ -542,6 +624,12 @@ export function main(
|
|
|
542
624
|
return selfTest(path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", ".."));
|
|
543
625
|
}
|
|
544
626
|
const quiet = argv.includes("--quiet");
|
|
627
|
+
const blank = blankExplicitBase(argv);
|
|
628
|
+
if (blank !== null) {
|
|
629
|
+
scanReport(0, "argument error");
|
|
630
|
+
console.error(`❌ check_branch_freshness: ${blank}`);
|
|
631
|
+
return 2;
|
|
632
|
+
}
|
|
545
633
|
|
|
546
634
|
// CI merges or refuses on the server; a second opinion here would be noise.
|
|
547
635
|
if (process.env["CI"] === "true" || process.env["GITHUB_ACTIONS"] === "true") {
|
|
@@ -565,8 +653,11 @@ export function main(
|
|
|
565
653
|
// positionally already selects the real `gh` — no branch needed here.
|
|
566
654
|
const resolved = resolveBase(argv, branch, forge);
|
|
567
655
|
const base = resolved.base;
|
|
656
|
+
const remote = resolved.remote ?? "origin";
|
|
657
|
+
const shown = `${remote}/${base}`;
|
|
568
658
|
|
|
569
|
-
|
|
659
|
+
// Remote and branch together: local `main` is not `upstream/main`, which can be ahead of it.
|
|
660
|
+
if (branch === base && remote === "origin") {
|
|
570
661
|
scanReport(0, `standing on ${base} itself — a branch cannot be behind itself`);
|
|
571
662
|
return 0;
|
|
572
663
|
}
|
|
@@ -577,49 +668,81 @@ export function main(
|
|
|
577
668
|
if (resolved.unverified !== undefined) {
|
|
578
669
|
process.stdout.write(
|
|
579
670
|
`⚠️ check_branch_freshness: could not ask the forge for this branch's PR base ` +
|
|
580
|
-
`(${resolved.unverified}) — checking
|
|
671
|
+
`(${resolved.unverified}) — checking ${shown}, the ${describeBase(resolved)}. ` +
|
|
581
672
|
"If an open PR targets a different base, THAT base is NOT verified here.\n",
|
|
582
673
|
);
|
|
583
674
|
}
|
|
584
675
|
|
|
585
|
-
const
|
|
586
|
-
if (
|
|
676
|
+
const head = remoteHeadState(base, remote);
|
|
677
|
+
if (head.kind === "absent") {
|
|
678
|
+
// The remote answered, so this is not the offline case: passing it as NOT
|
|
679
|
+
// VERIFIED let a misspelled base (`origin/origin/main`) skip the check.
|
|
680
|
+
scanReport(1);
|
|
681
|
+
console.error(
|
|
682
|
+
`❌ check_branch_freshness: base \`${base}\` does not exist on ${remote} — ${describeBase(resolved)}. ` +
|
|
683
|
+
"Name a branch on the remote: `--base main` and `--base origin/main` are the same branch.",
|
|
684
|
+
);
|
|
685
|
+
return 1;
|
|
686
|
+
}
|
|
687
|
+
if (head.kind === "unreachable") {
|
|
587
688
|
// Loud, not silent. An offline run has not verified the invariant, and a
|
|
588
689
|
// gate that passes on an invariant it never evaluated is the false green
|
|
589
690
|
// this repo keeps finding.
|
|
590
|
-
scanReport(0,
|
|
691
|
+
scanReport(0, `${shown} unreachable — reported NOT VERIFIED rather than green`);
|
|
591
692
|
process.stdout.write(
|
|
592
|
-
`check_branch_freshness: could not reach
|
|
693
|
+
`check_branch_freshness: could not reach ${shown} — NOT VERIFIED (offline?). ` +
|
|
593
694
|
"Re-run before pushing.\n",
|
|
594
695
|
);
|
|
595
696
|
return 0;
|
|
596
697
|
}
|
|
597
698
|
|
|
699
|
+
const sha = head.sha;
|
|
598
700
|
scanReport(1);
|
|
599
701
|
if (containsCommit(sha) === true) {
|
|
600
702
|
if (!quiet) {
|
|
601
703
|
process.stdout.write(
|
|
602
|
-
`✅ branch is current with
|
|
704
|
+
`✅ branch is current with ${shown} (${sha.slice(0, 9)}) — ${describeBase(resolved)}\n`,
|
|
603
705
|
);
|
|
604
706
|
}
|
|
605
707
|
return 0;
|
|
606
708
|
}
|
|
607
709
|
|
|
608
710
|
console.error(
|
|
609
|
-
`❌ branch is BEHIND
|
|
711
|
+
`❌ branch is BEHIND ${shown} — the remote is at ${sha.slice(0, 9)}. ` +
|
|
610
712
|
`(${describeBase(resolved)})`,
|
|
611
713
|
);
|
|
612
714
|
console.error("");
|
|
613
715
|
console.error(" Pushing now opens a PR that may conflict, and worse: another branch");
|
|
614
716
|
console.error(" may already have shipped what this one is implementing.");
|
|
615
717
|
console.error("");
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
718
|
+
// Advice text never pays for a fetch: the measured commit is new on a behind
|
|
719
|
+
// branch, so reading the strategy there would fetch inside the push budget.
|
|
720
|
+
// Read it at that commit only if it is already local, else at the cached
|
|
721
|
+
// remote-tracking commit; with neither, the remedy is the generic pointer.
|
|
722
|
+
const localAt = [sha, `refs/remotes/${remote}/${base}`]
|
|
723
|
+
.map((ref) => git(["rev-parse", "--verify", "-q", `${ref}^{commit}`]))
|
|
724
|
+
.find((found): found is string => found !== null && found !== "");
|
|
725
|
+
const strategy = localAt === undefined
|
|
726
|
+
? null
|
|
727
|
+
: (readCommittedConvention(process.cwd(), {
|
|
728
|
+
override: shown,
|
|
729
|
+
keys: ["update_strategy"],
|
|
730
|
+
fetch: false,
|
|
731
|
+
deps: { ...makeTargetDeps(process.cwd()), remoteSha: () => localAt },
|
|
732
|
+
}).readings.update_strategy as GitConventionReading);
|
|
733
|
+
// Read by the pre-push hook to print the exact BASE it refused for. Only a
|
|
734
|
+
// checkout that defines the task gets it: a consumer runs this script from
|
|
735
|
+
// node_modules, where the task does not exist and the line would not run.
|
|
736
|
+
// A strategy read as something other than merge would make the task refuse, so it
|
|
737
|
+
// is not offered then. An unread strategy keeps it: the task reads the strategy itself.
|
|
738
|
+
const mergeAllowed = strategy === null || (!isRefusal(strategy.state) && strategy.value === "merge");
|
|
739
|
+
if (definesPushReady() && mergeAllowed) {
|
|
740
|
+
console.error(` task push-ready BASE=${remote === "origin" ? base : shown} # integrate the base set, regenerate, verify, re-check`);
|
|
741
|
+
console.error("");
|
|
742
|
+
}
|
|
743
|
+
for (const line of behindRemedy(base, strategy, remote)) {
|
|
744
|
+
console.error(line);
|
|
745
|
+
}
|
|
623
746
|
console.error("");
|
|
624
747
|
console.error(
|
|
625
748
|
" This gate asks the REMOTE, not your tracking ref — a fetch from earlier in",
|
|
@@ -40,6 +40,11 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
40
40
|
import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
|
|
41
41
|
import { count as count_artefacts } from './update_counts.js';
|
|
42
42
|
import { KERNEL_RULE_ID_SET } from './_lib/kernel_rules.js';
|
|
43
|
+
import {
|
|
44
|
+
loadRuleObligations,
|
|
45
|
+
RULE_OBLIGATIONS_PATH,
|
|
46
|
+
type RuleObligationEntry,
|
|
47
|
+
} from './_lib/rule_obligations.js';
|
|
43
48
|
import {
|
|
44
49
|
carrier_frequency_by_platform,
|
|
45
50
|
covers,
|
|
@@ -128,6 +133,68 @@ export interface RuleCoverage {
|
|
|
128
133
|
frequency_verdict: FrequencyVerdict;
|
|
129
134
|
/** Hook-capable platforms where no declared carrier covers the obligation. */
|
|
130
135
|
gap_platforms: string[];
|
|
136
|
+
/**
|
|
137
|
+
* Obligation-level credit, beside the rule-level `effective`.
|
|
138
|
+
*
|
|
139
|
+
* A rule-level `effective` credits every obligation of the rule for what one
|
|
140
|
+
* entry refuses. This credits only the ids an entry is bound to in
|
|
141
|
+
* `src/config/rule-obligations.json`. Nothing here changes `effective` or
|
|
142
|
+
* any summary field that existed before it.
|
|
143
|
+
*/
|
|
144
|
+
obligation: ObligationCredit;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface ObligationCredit {
|
|
148
|
+
/** Ids the rule declares; empty for the kernel and for a rule with no entry. */
|
|
149
|
+
ids: string[];
|
|
150
|
+
/** Ids bound to at least one entry that resolves to something that can fail a build. */
|
|
151
|
+
blocking: string[];
|
|
152
|
+
/** Ids bound only to entries that fire and never refuse. */
|
|
153
|
+
observer: string[];
|
|
154
|
+
/**
|
|
155
|
+
* Entries that resolve to blocking or observer and carry NO binding. They
|
|
156
|
+
* keep the rule-level credit they always had and are reported here so the
|
|
157
|
+
* credit is visibly unscoped. Never counted as obligation-level coverage.
|
|
158
|
+
*/
|
|
159
|
+
unbound_entries: string[];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Which of a rule's obligations its `enforced_by` entries actually carry.
|
|
164
|
+
*
|
|
165
|
+
* An entry absent from `bindings` is unbound. An entry bound to `[]` is a
|
|
166
|
+
* recorded finding that it refuses none of this rule's obligations; it is not
|
|
167
|
+
* unbound and credits nothing.
|
|
168
|
+
*/
|
|
169
|
+
export function obligation_credit(
|
|
170
|
+
declared: readonly string[],
|
|
171
|
+
resolutions: readonly Resolution[],
|
|
172
|
+
entry: RuleObligationEntry | undefined,
|
|
173
|
+
): ObligationCredit {
|
|
174
|
+
const ids = entry?.obligations ?? [];
|
|
175
|
+
const bindings = entry?.bindings ?? {};
|
|
176
|
+
const blocking = new Set<string>();
|
|
177
|
+
const observer = new Set<string>();
|
|
178
|
+
const unbound: string[] = [];
|
|
179
|
+
declared.forEach((d, i) => {
|
|
180
|
+
const res = resolutions[i];
|
|
181
|
+
if (res === undefined) return;
|
|
182
|
+
const carries = BLOCKING.has(res) || res === 'observer';
|
|
183
|
+
if (!carries) return;
|
|
184
|
+
const bound = bindings[d];
|
|
185
|
+
if (bound === undefined) {
|
|
186
|
+
unbound.push(d);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
for (const id of bound) {
|
|
190
|
+
if (!ids.includes(id)) continue;
|
|
191
|
+
if (BLOCKING.has(res)) blocking.add(id);
|
|
192
|
+
else observer.add(id);
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
for (const id of blocking) observer.delete(id);
|
|
196
|
+
const order = (xs: Set<string>): string[] => ids.filter((id) => xs.has(id));
|
|
197
|
+
return { ids: [...ids], blocking: order(blocking), observer: order(observer), unbound_entries: unbound };
|
|
131
198
|
}
|
|
132
199
|
|
|
133
200
|
/** Strength order — a rule is credited with its strongest resolving backstop. */
|
|
@@ -586,6 +653,10 @@ export function collect(): RuleCoverage[] {
|
|
|
586
653
|
const reachable_ci = reachable_scripts(ci_corpus);
|
|
587
654
|
const reachable_local = reachable_scripts(`${ci_corpus}\n${local_corpus}`);
|
|
588
655
|
|
|
656
|
+
const inventory = fs.existsSync(path.join(REPO_ROOT, RULE_OBLIGATIONS_PATH))
|
|
657
|
+
? loadRuleObligations(REPO_ROOT).rules
|
|
658
|
+
: {};
|
|
659
|
+
|
|
589
660
|
const out: RuleCoverage[] = [];
|
|
590
661
|
for (const name of fs.readdirSync(RULES_DIR).sort()) {
|
|
591
662
|
if (!name.endsWith('.md')) continue;
|
|
@@ -630,6 +701,7 @@ export function collect(): RuleCoverage[] {
|
|
|
630
701
|
carrier_frequency,
|
|
631
702
|
frequency_verdict: verdict,
|
|
632
703
|
gap_platforms,
|
|
704
|
+
obligation: obligation_credit(declared, resolutions, inventory[id]),
|
|
633
705
|
});
|
|
634
706
|
}
|
|
635
707
|
return out;
|
|
@@ -683,6 +755,53 @@ export interface Summary {
|
|
|
683
755
|
* populations agree.
|
|
684
756
|
*/
|
|
685
757
|
frames: DenominatorFrames;
|
|
758
|
+
/**
|
|
759
|
+
* The same coverage counted per obligation rather than per rule. Printed
|
|
760
|
+
* beside the rule counts, never instead of them, and no `--check` ratchet
|
|
761
|
+
* reads it: the roadmap that added it adds no failing condition.
|
|
762
|
+
*/
|
|
763
|
+
obligations: ObligationSummary;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
export interface ObligationSummary {
|
|
767
|
+
/** Ids declared across all rules. */
|
|
768
|
+
total: number;
|
|
769
|
+
/** Ids bound to an entry that can fail a build. */
|
|
770
|
+
blocking: number;
|
|
771
|
+
/** Ids bound only to an entry that fires and never refuses. */
|
|
772
|
+
observer: number;
|
|
773
|
+
/** Ids no entry is bound to: model-carried, or carried only by an unbound entry. */
|
|
774
|
+
not_bound: number;
|
|
775
|
+
/** Rules whose rule-level credit comes from at least one unbound entry. */
|
|
776
|
+
rules_with_unbound_entries: number;
|
|
777
|
+
/** Kernel rules: no ids by construction, counted at rule granularity. */
|
|
778
|
+
kernel_rules: number;
|
|
779
|
+
/** Non-kernel rules with no ids declared. */
|
|
780
|
+
rules_without_ids: number;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
export function summarise_obligations(rows: readonly RuleCoverage[]): ObligationSummary {
|
|
784
|
+
// Rows built before the field existed (test fixtures, older JSON) count as carrying no ids.
|
|
785
|
+
const none: ObligationCredit = { ids: [], blocking: [], observer: [], unbound_entries: [] };
|
|
786
|
+
const of = (r: RuleCoverage): ObligationCredit => r.obligation ?? none;
|
|
787
|
+
let total = 0;
|
|
788
|
+
let blocking = 0;
|
|
789
|
+
let observer = 0;
|
|
790
|
+
for (const r of rows) {
|
|
791
|
+
total += of(r).ids.length;
|
|
792
|
+
blocking += of(r).blocking.length;
|
|
793
|
+
observer += of(r).observer.length;
|
|
794
|
+
}
|
|
795
|
+
return {
|
|
796
|
+
total,
|
|
797
|
+
blocking,
|
|
798
|
+
observer,
|
|
799
|
+
not_bound: total - blocking - observer,
|
|
800
|
+
rules_with_unbound_entries: rows.filter((r) => of(r).unbound_entries.length > 0).length,
|
|
801
|
+
kernel_rules: rows.filter((r) => KERNEL_RULE_ID_SET.has(r.id)).length,
|
|
802
|
+
rules_without_ids: rows.filter((r) => !KERNEL_RULE_ID_SET.has(r.id) && of(r).ids.length === 0)
|
|
803
|
+
.length,
|
|
804
|
+
};
|
|
686
805
|
}
|
|
687
806
|
|
|
688
807
|
export function summarise(rows: RuleCoverage[]): Summary {
|
|
@@ -710,6 +829,7 @@ export function summarise(rows: RuleCoverage[]): Summary {
|
|
|
710
829
|
r.declared.every((d) => d.startsWith('observer:maintainer-review')),
|
|
711
830
|
),
|
|
712
831
|
frames: denominator_frames(rows.length),
|
|
832
|
+
obligations: summarise_obligations(rows),
|
|
713
833
|
};
|
|
714
834
|
}
|
|
715
835
|
|
|
@@ -883,6 +1003,16 @@ function main(argv: string[]): number {
|
|
|
883
1003
|
// source for that number: `check_enforcement_denominator` reds when a
|
|
884
1004
|
// tracked doc carries an enforcement count this resolver did not produce.
|
|
885
1005
|
lines.push(` ${denominator_line(summary.frames)}`);
|
|
1006
|
+
const ob = summary.obligations;
|
|
1007
|
+
lines.push(
|
|
1008
|
+
` obligations: ${ob.blocking}/${ob.total} bound to a backstop that fails a CI build · ` +
|
|
1009
|
+
`${ob.observer} observer · ${ob.not_bound} not bound — beside the rule counts, not instead of them`,
|
|
1010
|
+
);
|
|
1011
|
+
lines.push(
|
|
1012
|
+
` obligation frame: ${ob.rules_with_unbound_entries} rule(s) still credited by an unbound entry · ` +
|
|
1013
|
+
`${ob.kernel_rules} kernel rule(s) at rule granularity · ${ob.rules_without_ids} rule(s) without ids ` +
|
|
1014
|
+
`(${RULE_OBLIGATIONS_PATH})`,
|
|
1015
|
+
);
|
|
886
1016
|
|
|
887
1017
|
const gaps = rows.filter((r) => r.frequency_verdict === 'gap');
|
|
888
1018
|
if (gaps.length > 0) {
|
|
@@ -59,6 +59,10 @@ interface BudgetEntry {
|
|
|
59
59
|
/** `false` for wall-clock metrics: they cannot be compared run-to-run,
|
|
60
60
|
* so they are excluded from the drift check. Missing = deterministic. */
|
|
61
61
|
deterministic?: boolean;
|
|
62
|
+
/** Repository paths (globs as the workflow `paths` filter writes them) the
|
|
63
|
+
* metric is measured from. Every one must trigger the umbrella, so the PR
|
|
64
|
+
* that moves the input is the PR that sees the budget. */
|
|
65
|
+
inputs?: string[];
|
|
62
66
|
}
|
|
63
67
|
interface BudgetsDoc {
|
|
64
68
|
regression_pct: number;
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
*
|
|
9
9
|
* The record is a COMMITTED ledger — `agents/evidence/release-findings/
|
|
10
10
|
* <version>.json` — never the PR comment (council 2026-08-03: a comment is
|
|
11
|
-
* mutable and unaudited; it is transport, not a record). Every blocking
|
|
12
|
-
* finding in the ledger must carry a complete disposition
|
|
11
|
+
* mutable and unaudited; it is transport, not a record). Every blocking
|
|
12
|
+
* finding in the ledger (see `isBlocking`) must carry a complete disposition
|
|
13
13
|
* `{status: fixed|false_positive|accepted_risk, commit (when fixed),
|
|
14
14
|
* rationale, verified_by}`; the release workflow is red while one does not.
|
|
15
15
|
*
|
|
@@ -95,10 +95,9 @@ export type DispositionStatus = (typeof DISPOSITION_STATUSES)[number];
|
|
|
95
95
|
* to satisfy the requirement and the one that destroys it.
|
|
96
96
|
*
|
|
97
97
|
* It is deliberately NOT in {@link DISPOSITION_STATUSES}: a BLOCKING row may
|
|
98
|
-
* never pass on it. `missing_dispositions`
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* have.
|
|
98
|
+
* never pass on it. `missing_dispositions` reports a blocking finding marked
|
|
99
|
+
* `still_open` with its own message and the gate stays red — which is the
|
|
100
|
+
* behaviour a blocking finding that is still open should have.
|
|
102
101
|
*
|
|
103
102
|
* `open` from the schema is absent on purpose. The schema documents it as the
|
|
104
103
|
* INITIAL state, so counting it would make the tally reachable without anyone
|
|
@@ -272,21 +271,74 @@ function _clean_review_reason(artifact: Record<string, unknown>): string {
|
|
|
272
271
|
);
|
|
273
272
|
}
|
|
274
273
|
|
|
274
|
+
export const MEDIUM_SECURITY_BLOCKS_AFTER = '16.3.0';
|
|
275
|
+
|
|
276
|
+
function semverParts(v: string): [number, number, number] | null {
|
|
277
|
+
const m = /^v?(\d+)\.(\d+)\.(\d+)/.exec(v.trim());
|
|
278
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/** True unless `release` parses and is at or before the cutoff. */
|
|
282
|
+
export function mediumSecurityBinds(release: string | null): boolean {
|
|
283
|
+
if (release === null) return true;
|
|
284
|
+
const r = semverParts(release);
|
|
285
|
+
const cut = semverParts(MEDIUM_SECURITY_BLOCKS_AFTER) as [number, number, number];
|
|
286
|
+
if (r === null) return true;
|
|
287
|
+
for (let i = 0; i < 3; i++) {
|
|
288
|
+
const a = r[i] as number;
|
|
289
|
+
const b = cut[i] as number;
|
|
290
|
+
if (a !== b) return a > b;
|
|
291
|
+
}
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
|
|
275
295
|
/**
|
|
276
|
-
*
|
|
277
|
-
* MINUS anything the tree disproved.
|
|
278
|
-
*
|
|
279
|
-
*
|
|
280
|
-
*
|
|
281
|
-
*
|
|
282
|
-
*
|
|
283
|
-
*
|
|
284
|
-
*
|
|
296
|
+
* self_review_gate.classifyBlocking (security/claim × critical/high) PLUS
|
|
297
|
+
* security × medium, MINUS anything the tree disproved.
|
|
298
|
+
*
|
|
299
|
+
* Deliberately STRICTER than the merge gate, and the asymmetry is load-bearing
|
|
300
|
+
* rather than drift — do not re-mirror it. The merge gate asks whether a change
|
|
301
|
+
* may enter trunk while work is still integrating; this gate asks whether a
|
|
302
|
+
* known risk may ship to users without an accountable decision. 16.3.0 shipped
|
|
303
|
+
* a medium security finding that had already been open in 16.2.0 with nobody
|
|
304
|
+
* required to decide it, so a medium security finding needs a disposition here
|
|
305
|
+
* while it stays advisory at merge. `accepted_risk` with a rationale satisfies
|
|
306
|
+
* it; `still_open` does not. `claim × medium` stays advisory in both.
|
|
307
|
+
* Council 2026-10-07 (claude-sonnet-4-5 + codex, 2/2 concluded, option a,
|
|
308
|
+
* release gate only). Revisit if medium security findings routinely receive
|
|
309
|
+
* formulaic `accepted_risk` over three consecutive releases, or exceed about
|
|
310
|
+
* ten per release.
|
|
311
|
+
*
|
|
312
|
+
* The widening is prospective: it binds releases after
|
|
313
|
+
* {@link MEDIUM_SECURITY_BLOCKS_AFTER}, the last one published before the
|
|
314
|
+
* decision. A release already shipped cannot be made to wait, and re-gating it
|
|
315
|
+
* would leave exactly two outcomes for a medium security row that is genuinely
|
|
316
|
+
* still open — a red trunk, or an `accepted_risk` written only to turn it
|
|
317
|
+
* green, which is the relabelling this gate exists to refuse. Without a release
|
|
318
|
+
* (a reported finding, a test) the wider rule applies.
|
|
319
|
+
*
|
|
320
|
+
* The cutoff orders by version, not by publication date: a maintenance release
|
|
321
|
+
* on an older line (a 16.2.x cut after the decision) sits at or before the
|
|
322
|
+
* cutoff and is exempt. The council ratified the prospective cutoff and this
|
|
323
|
+
* documented exemption on 2026-10-07 (2/2); cutting a maintenance line is the
|
|
324
|
+
* condition that reopens it.
|
|
325
|
+
*
|
|
326
|
+
* The `contradicted` clause is shared with the merge gate, and has to be or the
|
|
327
|
+
* two disagree: the review would stop counting a disproved deletion claim as
|
|
328
|
+
* blocking while the release kept demanding a disposition for it. A
|
|
329
|
+
* `contradicted` value is written only by `annotateContradicted`, which checks
|
|
330
|
+
* the claim against `git diff --name-status base...HEAD` AND the working tree.
|
|
285
331
|
*/
|
|
286
|
-
export function isBlocking(
|
|
332
|
+
export function isBlocking(
|
|
333
|
+
f: Pick<LedgerFinding, 'kind' | 'severity' | 'contradicted'>,
|
|
334
|
+
release: string | null = null,
|
|
335
|
+
): boolean {
|
|
287
336
|
if ((f.contradicted ?? '').trim() !== '') {
|
|
288
337
|
return false;
|
|
289
338
|
}
|
|
339
|
+
if (f.kind === 'security' && f.severity === 'medium' && mediumSecurityBinds(release)) {
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
290
342
|
return (
|
|
291
343
|
(f.kind === 'security' || f.kind === 'claim') &&
|
|
292
344
|
(f.severity === 'critical' || f.severity === 'high')
|
|
@@ -299,10 +351,13 @@ export function isBlocking(f: Pick<LedgerFinding, 'kind' | 'severity' | 'contrad
|
|
|
299
351
|
* Empty rationale / verified_by is a hygiene violation (waiver precedent:
|
|
300
352
|
* "empty string = hygiene violation"); status `fixed` requires a commit.
|
|
301
353
|
*/
|
|
302
|
-
export function missing_dispositions(
|
|
354
|
+
export function missing_dispositions(
|
|
355
|
+
findings: readonly LedgerFinding[],
|
|
356
|
+
release: string | null = null,
|
|
357
|
+
): string[] {
|
|
303
358
|
const problems: string[] = [];
|
|
304
359
|
for (const f of findings) {
|
|
305
|
-
if (!isBlocking(f)) {
|
|
360
|
+
if (!isBlocking(f, release)) {
|
|
306
361
|
continue;
|
|
307
362
|
}
|
|
308
363
|
const label = `${f.finding_id} (${f.severity} ${f.kind}: ${f.title})`;
|
|
@@ -311,6 +366,10 @@ export function missing_dispositions(findings: readonly LedgerFinding[]): string
|
|
|
311
366
|
problems.push(`${label}: no disposition status`);
|
|
312
367
|
continue;
|
|
313
368
|
}
|
|
369
|
+
if (status === 'still_open') {
|
|
370
|
+
problems.push(`${label}: still_open does not satisfy a blocking finding — fix it, disprove it, or accept the risk with a reason`);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
314
373
|
if (!(DISPOSITION_STATUSES as readonly string[]).includes(status)) {
|
|
315
374
|
problems.push(`${label}: unknown status ${JSON.stringify(status)}`);
|
|
316
375
|
continue;
|
|
@@ -359,15 +418,18 @@ export interface DispositionTally {
|
|
|
359
418
|
* direction: a typo surfaces as a row the line says is unadjudicated, rather
|
|
360
419
|
* than disappearing into a count that reads as finished.
|
|
361
420
|
*/
|
|
362
|
-
export function disposition_tally(
|
|
421
|
+
export function disposition_tally(
|
|
422
|
+
findings: readonly LedgerFinding[],
|
|
423
|
+
release: string | null = null,
|
|
424
|
+
): DispositionTally {
|
|
363
425
|
let blockingTotal = 0;
|
|
364
426
|
let blockingDispositioned = 0;
|
|
365
427
|
let nonBlockingTotal = 0;
|
|
366
428
|
let nonBlockingTerminal = 0;
|
|
367
429
|
for (const f of findings) {
|
|
368
|
-
if (isBlocking(f)) {
|
|
430
|
+
if (isBlocking(f, release)) {
|
|
369
431
|
blockingTotal++;
|
|
370
|
-
if (missing_dispositions([f]).length === 0) {
|
|
432
|
+
if (missing_dispositions([f], release).length === 0) {
|
|
371
433
|
blockingDispositioned++;
|
|
372
434
|
}
|
|
373
435
|
continue;
|
|
@@ -540,10 +602,11 @@ export function resolve_release_status(release: string): ReleaseStatus {
|
|
|
540
602
|
export function unrecorded_findings(
|
|
541
603
|
reported: ReadonlyArray<Pick<LedgerFinding, 'finding_id' | 'severity' | 'kind' | 'title' | 'contradicted'>>,
|
|
542
604
|
ledger: readonly LedgerFinding[],
|
|
605
|
+
release: string | null = null,
|
|
543
606
|
): string[] {
|
|
544
607
|
const known = new Set(ledger.map((f) => f.finding_id));
|
|
545
608
|
return reported
|
|
546
|
-
.filter((f) => isBlocking(f) && !known.has(f.finding_id))
|
|
609
|
+
.filter((f) => isBlocking(f, release) && !known.has(f.finding_id))
|
|
547
610
|
.map((f) => `${f.finding_id} (${f.severity} ${f.kind}: ${f.title}) reported by the self-review but not in the ledger`);
|
|
548
611
|
}
|
|
549
612
|
|
|
@@ -773,10 +836,10 @@ function main(argv: readonly string[]): number {
|
|
|
773
836
|
throw exc;
|
|
774
837
|
}
|
|
775
838
|
|
|
776
|
-
const problems = missing_dispositions(ledger.findings);
|
|
839
|
+
const problems = missing_dispositions(ledger.findings, release);
|
|
777
840
|
if (pr) {
|
|
778
841
|
const reported = parse_comment_findings(_gh_comment_bodies(pr));
|
|
779
|
-
problems.push(...unrecorded_findings(reported, ledger.findings));
|
|
842
|
+
problems.push(...unrecorded_findings(reported, ledger.findings, release));
|
|
780
843
|
}
|
|
781
844
|
|
|
782
845
|
if (problems.length === 0) {
|
|
@@ -790,7 +853,7 @@ function main(argv: readonly string[]): number {
|
|
|
790
853
|
n === 0
|
|
791
854
|
? `✅ no recorded findings for ${release} (ledger ${fs.existsSync(ledgerPath) ? 'empty' : 'absent'})${suffix}\n`
|
|
792
855
|
: `✅ ${String(n)} recorded finding(s) for ${release} — ` +
|
|
793
|
-
`${disposition_summary(disposition_tally(ledger.findings))}${suffix}\n`,
|
|
856
|
+
`${disposition_summary(disposition_tally(ledger.findings, release))}${suffix}\n`,
|
|
794
857
|
);
|
|
795
858
|
return 0;
|
|
796
859
|
}
|