@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
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
* converts into the other.
|
|
72
72
|
*/
|
|
73
73
|
|
|
74
|
+
import { createHash } from 'node:crypto';
|
|
74
75
|
import * as fs from 'node:fs';
|
|
75
76
|
import * as os from 'node:os';
|
|
76
77
|
import * as path from 'node:path';
|
|
@@ -80,6 +81,8 @@ import { fileURLToPath } from 'node:url';
|
|
|
80
81
|
import { assertScanned, DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
81
82
|
import { measure, method_note } from './_lib/token_count.js';
|
|
82
83
|
import * as rule_layer_overlap from './_lib/rule_layer_overlap.js';
|
|
84
|
+
import { lawText, ruleBody } from './_lib/rule_law_section.js';
|
|
85
|
+
import { is_thin_entry, lawDigest, STUB_LAW_OPEN } from './_lib/thin_rules.js';
|
|
83
86
|
|
|
84
87
|
const PROG = 'check_standing_rule_delivery';
|
|
85
88
|
const _HERE = fileURLToPath(import.meta.url);
|
|
@@ -205,6 +208,80 @@ export function measureStandingDelivery(
|
|
|
205
208
|
};
|
|
206
209
|
}
|
|
207
210
|
|
|
211
|
+
/** One installed rule, compared against the source it was installed from. */
|
|
212
|
+
export interface ReinstallRow {
|
|
213
|
+
readonly layer: string;
|
|
214
|
+
readonly id: string;
|
|
215
|
+
/** What the two digests are of — named, so a reader knows what "differs" means. */
|
|
216
|
+
readonly compared: 'body' | 'law' | 'none';
|
|
217
|
+
readonly installed: string;
|
|
218
|
+
readonly source: string;
|
|
219
|
+
readonly status: 'current' | 'pending reinstall' | 'no source';
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function bodyDigest(text: string): string {
|
|
223
|
+
return createHash('sha256').update(ruleBody(text), 'utf-8').digest('hex').slice(0, 16);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Per installed rule: its digest against the CURRENT source digest
|
|
228
|
+
* (`road-to-a-default-install-served-once` step 1.2).
|
|
229
|
+
*
|
|
230
|
+
* The cap above measures installed directories only, by design, so a source
|
|
231
|
+
* rule that got smaller is invisible until a reinstall. This names every rule
|
|
232
|
+
* whose installed copy no longer matches its source as `pending reinstall`.
|
|
233
|
+
* It REPORTS and fails nothing — every source edit makes an older install
|
|
234
|
+
* differ, and that is a state to act on, not an error.
|
|
235
|
+
*
|
|
236
|
+
* What is compared depends on the installed form, and the row says which:
|
|
237
|
+
*
|
|
238
|
+
* - **body** — a full-bodied copy: sha256 of the body after the frontmatter
|
|
239
|
+
* and comment strip, because the installer rewrites the frontmatter into
|
|
240
|
+
* host form and the body is what the host reads.
|
|
241
|
+
* - **law** — a stub carrying a copied law: the digest the stub recorded
|
|
242
|
+
* against the digest of the source's law section today.
|
|
243
|
+
* - **none** — a stub with no copied text: its body is read from the package
|
|
244
|
+
* at delivery, so nothing installed can go stale.
|
|
245
|
+
*/
|
|
246
|
+
export function reinstallRows(
|
|
247
|
+
layer: string,
|
|
248
|
+
installed_dir: string,
|
|
249
|
+
source_dir: string,
|
|
250
|
+
): ReinstallRow[] {
|
|
251
|
+
const l = rule_layer_overlap.readRuleLayer(installed_dir);
|
|
252
|
+
if (l === null) return [];
|
|
253
|
+
const rows: ReinstallRow[] = [];
|
|
254
|
+
for (const [name, text] of l.files) {
|
|
255
|
+
const id = name.replace(/\.md$/, '');
|
|
256
|
+
let src: string | null;
|
|
257
|
+
try {
|
|
258
|
+
src = fs.readFileSync(path.join(source_dir, name), 'utf-8');
|
|
259
|
+
} catch {
|
|
260
|
+
src = null;
|
|
261
|
+
}
|
|
262
|
+
let compared: ReinstallRow['compared'] = 'body';
|
|
263
|
+
let installed = bodyDigest(text);
|
|
264
|
+
let source = src === null ? '-' : bodyDigest(src);
|
|
265
|
+
if (is_thin_entry(text)) {
|
|
266
|
+
const at = text.indexOf(STUB_LAW_OPEN);
|
|
267
|
+
if (at === -1) {
|
|
268
|
+
compared = 'none';
|
|
269
|
+
installed = '-';
|
|
270
|
+
source = '-';
|
|
271
|
+
} else {
|
|
272
|
+
compared = 'law';
|
|
273
|
+
installed = text.slice(at + STUB_LAW_OPEN.length).split(' ')[0] ?? '';
|
|
274
|
+
const law = src === null ? null : lawText(ruleBody(src));
|
|
275
|
+
source = law === null ? '-' : lawDigest(law);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const status: ReinstallRow['status'] =
|
|
279
|
+
src === null ? 'no source' : installed === source ? 'current' : 'pending reinstall';
|
|
280
|
+
rows.push({ layer, id, compared, installed, source, status });
|
|
281
|
+
}
|
|
282
|
+
return rows;
|
|
283
|
+
}
|
|
284
|
+
|
|
208
285
|
/**
|
|
209
286
|
* Where the loaded-instruction record would live if the host wrote one.
|
|
210
287
|
*
|
|
@@ -331,6 +408,25 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
331
408
|
);
|
|
332
409
|
}
|
|
333
410
|
|
|
411
|
+
// Step 1.2 — the installed copy against the current source, per rule.
|
|
412
|
+
// Reported, never failed: see `reinstallRows`.
|
|
413
|
+
const source_dir = path.join(REPO_ROOT, 'dist', 'agent-src', 'rules');
|
|
414
|
+
const rows = [
|
|
415
|
+
...reinstallRows('global', path.join(os.homedir(), '.claude', 'rules'), source_dir),
|
|
416
|
+
...reinstallRows('project', path.join(REPO_ROOT, '.claude', 'rules'), source_dir),
|
|
417
|
+
];
|
|
418
|
+
const pending = rows.filter((r) => r.status === 'pending reinstall');
|
|
419
|
+
out(
|
|
420
|
+
` reinstall ${pending.length} of ${rows.length} installed rule(s) differ from `
|
|
421
|
+
+ `${path.relative(REPO_ROOT, source_dir)} (installed digest vs current source digest)\n`,
|
|
422
|
+
);
|
|
423
|
+
for (const r of rows) {
|
|
424
|
+
out(
|
|
425
|
+
` ${r.layer.padEnd(8)} ${r.id.padEnd(40)} ${r.compared.padEnd(5)} `
|
|
426
|
+
+ `${r.installed.padEnd(16)} ${r.source.padEnd(16)} ${r.status}\n`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
334
430
|
const pct = total / budget.total_cap_tokens;
|
|
335
431
|
out(
|
|
336
432
|
` TOTAL ${String(total).padStart(13)} tok / ${budget.total_cap_tokens} cap `
|
|
@@ -264,11 +264,8 @@ export const SUPPRESSION_INVENTORY: readonly SuppressionSpec[] = [
|
|
|
264
264
|
file: 'src/config/conformance-claim-baseline.json',
|
|
265
265
|
listKey: 'undeclared',
|
|
266
266
|
tier: 'string_list',
|
|
267
|
-
//
|
|
268
|
-
//
|
|
269
|
-
// main — the scan below fails while it is set and the file resolves,
|
|
270
|
-
// so the flag closes itself rather than needing to be remembered.
|
|
271
|
-
newInThisChange: true,
|
|
267
|
+
// `newInThisChange` removed: the baseline resolves at the base ref since
|
|
268
|
+
// 9a013fac5, and the scan reported the stale flag as designed.
|
|
272
269
|
what: 'skill and rule artefacts carrying a standard-conformance claim with no enforced_by declaration',
|
|
273
270
|
},
|
|
274
271
|
];
|
package/src/scripts/ci_settle.ts
CHANGED
|
@@ -64,7 +64,7 @@ export const FOREGROUND_CEILING_MIN = 9;
|
|
|
64
64
|
/** What one poll told us. `unreadable` is deliberately not a verdict. */
|
|
65
65
|
export type PollState =
|
|
66
66
|
| { kind: 'unreadable'; reason: string }
|
|
67
|
-
| { kind: 'pending'; total: number; done: number }
|
|
67
|
+
| { kind: 'pending'; total: number; done: number; pendingNames?: string[] }
|
|
68
68
|
| { kind: 'settled'; failing: string[]; total: number };
|
|
69
69
|
|
|
70
70
|
/**
|
|
@@ -234,7 +234,8 @@ export function classifyPoll(stdout: string, stderr: string, status: number | nu
|
|
|
234
234
|
typeof r.conclusion === 'string' && r.conclusion.length > 0;
|
|
235
235
|
const done = rows.filter(terminal).length;
|
|
236
236
|
if (done < rows.length) {
|
|
237
|
-
|
|
237
|
+
const pendingNames = rows.filter((r) => !terminal(r)).map((r) => String(r.name ?? '?'));
|
|
238
|
+
return { kind: 'pending', total: rows.length, done, pendingNames };
|
|
238
239
|
}
|
|
239
240
|
const bad = new Set(['FAILURE', 'TIMED_OUT', 'CANCELLED', 'ACTION_REQUIRED', 'STARTUP_FAILURE']);
|
|
240
241
|
// A concurrency group that supersedes its own earlier run leaves TWO rows
|
|
@@ -264,6 +265,44 @@ export function classifyPoll(stdout: string, stderr: string, status: number | nu
|
|
|
264
265
|
return { kind: 'settled', failing, total: rows.length };
|
|
265
266
|
}
|
|
266
267
|
|
|
268
|
+
const MAX_NAMED = 8;
|
|
269
|
+
const MAX_NAME_CHARS = 80;
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* The lines an expired wait prints, the `DID NOT SETTLE` line LAST because the
|
|
273
|
+
* last line is the one a caller reads as the outcome.
|
|
274
|
+
*
|
|
275
|
+
* WHY IT SAYS MORE THAN IT USED TO (`road-to-blocking-time-by-cause` 3.3,
|
|
276
|
+
* claim `turnaround-blocking-by-cause-targets`): in the 2026-10 reading 20 of
|
|
277
|
+
* 44 CI waits ended here and were re-run in the FOREGROUND, 184 of 261 ci-wait
|
|
278
|
+
* minutes. Naming what is still pending and that the next wait belongs in the
|
|
279
|
+
* background shortens the caller's blocking time without skipping a check: the
|
|
280
|
+
* forge still runs every one, and the verdict still comes from this waiter.
|
|
281
|
+
*
|
|
282
|
+
* Check names come from the forge and are DATA: control characters are
|
|
283
|
+
* stripped, each name is capped, and the list is bounded, so a name cannot
|
|
284
|
+
* forge a second `next wait:` line or a terminal escape.
|
|
285
|
+
*/
|
|
286
|
+
export function renderNoSettle(timeoutMin: number, last: PollState | null): string[] {
|
|
287
|
+
const lines: string[] = [];
|
|
288
|
+
let next = '';
|
|
289
|
+
if (last !== null && last.kind === 'pending' && last.total > 0) {
|
|
290
|
+
const names = (last.pendingNames ?? [])
|
|
291
|
+
.map((n) => n.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]/g, ' ').trim().slice(0, MAX_NAME_CHARS))
|
|
292
|
+
.filter((n) => n !== '');
|
|
293
|
+
const shown = names.slice(0, MAX_NAMED).join(', ');
|
|
294
|
+
const more = names.length > MAX_NAMED ? ` (+${String(names.length - MAX_NAMED)} more)` : '';
|
|
295
|
+
lines.push(
|
|
296
|
+
`ci_settle: ${String(last.done)}/${String(last.total)} settled; still pending: ${shown}${more}`,
|
|
297
|
+
);
|
|
298
|
+
next =
|
|
299
|
+
' Checks are still running, so next wait: background — re-run this waiter with the host\'s ' +
|
|
300
|
+
'background primitive; a second foreground wait repeats this one.';
|
|
301
|
+
}
|
|
302
|
+
lines.push(`ci_settle: DID NOT SETTLE within ${String(timeoutMin)} min — no verdict is claimed.${next}`);
|
|
303
|
+
return lines;
|
|
304
|
+
}
|
|
305
|
+
|
|
267
306
|
function poll(pr: string): PollState {
|
|
268
307
|
const r = spawnSync('gh', ['pr', 'view', pr, '--json', 'statusCheckRollup'], {
|
|
269
308
|
encoding: 'utf-8',
|
|
@@ -519,9 +558,7 @@ export function main(argv: readonly string[]): number {
|
|
|
519
558
|
);
|
|
520
559
|
}
|
|
521
560
|
if (Date.now() >= deadline) {
|
|
522
|
-
process.stdout.write(
|
|
523
|
-
`ci_settle: DID NOT SETTLE within ${String(timeoutMin)} min — no verdict is claimed.\n`,
|
|
524
|
-
);
|
|
561
|
+
for (const line of renderNoSettle(timeoutMin, state)) process.stdout.write(`${line}\n`);
|
|
525
562
|
return 2;
|
|
526
563
|
}
|
|
527
564
|
sleepSync(intervalSec);
|
|
@@ -0,0 +1,320 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* classify_merge_risk — ADR-282's danger gate in front of an authorized
|
|
4
|
+
* auto-merge.
|
|
5
|
+
*
|
|
6
|
+
* Says `routine` or `needs-council` for a diff, plus the triggers that fired.
|
|
7
|
+
* `routine` merges on the owner's auto-merge instruction with no review;
|
|
8
|
+
* `needs-council` sends the diff to the AI council first. The classifier is
|
|
9
|
+
* advisory to that flow and grants nothing on its own.
|
|
10
|
+
*
|
|
11
|
+
* Every trigger is lifted from a rule that already exists, never invented here:
|
|
12
|
+
*
|
|
13
|
+
* - `non-destructive-by-default` Hard-Floor rows: bulk deletion (a removed
|
|
14
|
+
* directory, or DELETION_THRESHOLD deleted files), infrastructure config,
|
|
15
|
+
* production data and migrations, deploy and release paths.
|
|
16
|
+
* - `security-sensitive-stop` surfaces: auth, authz, tenancy, billing,
|
|
17
|
+
* secrets, uploads, webhooks, route files (public endpoints).
|
|
18
|
+
* - The ratification surfaces, read through `check_kernel_edit_ratified`'s own
|
|
19
|
+
* `classifyPaths` — a second list would drift from the first.
|
|
20
|
+
* - Any CI workflow: `process-full` makes a disabled required check the thing
|
|
21
|
+
* delivery-ready must refuse, and a green rollup on a weakened workflow is
|
|
22
|
+
* exactly what a no-review merge would trust.
|
|
23
|
+
* - Its own surface — this file and the two commands that call it — for the
|
|
24
|
+
* reason the ratification gate watches itself: a routine-class PR must not
|
|
25
|
+
* be able to loosen the gate that classed it.
|
|
26
|
+
*
|
|
27
|
+
* Prose files (`.md`, `.mdx`, `.txt`, `.rst`) are exempt from the security,
|
|
28
|
+
* infra and migration token checks: a skill ABOUT secrets is not a secret
|
|
29
|
+
* surface. They stay subject to deletion, governance and self-surface checks.
|
|
30
|
+
*
|
|
31
|
+
* Fails closed: a diff that cannot be read is `needs-council`, never `routine`,
|
|
32
|
+
* and never an internal-error exit a caller might read as "nothing found".
|
|
33
|
+
*
|
|
34
|
+
* Inputs (one of):
|
|
35
|
+
* <pr-number> PR mode: base and head read via `gh`, head fetched
|
|
36
|
+
* --range BASE..HEAD range mode (`...` also accepted)
|
|
37
|
+
* Options:
|
|
38
|
+
* --root DIR repository to read (default: cwd)
|
|
39
|
+
* --json one JSON object instead of text
|
|
40
|
+
*
|
|
41
|
+
* Exit codes: 0 = routine · 1 = needs-council (including fail-closed).
|
|
42
|
+
* The LAST output line is the verdict.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { spawnSync } from 'node:child_process';
|
|
46
|
+
import * as path from 'node:path';
|
|
47
|
+
import { pathToFileURL } from 'node:url';
|
|
48
|
+
|
|
49
|
+
import { classifyPaths, requiresRatification } from './check_kernel_edit_ratified.js';
|
|
50
|
+
|
|
51
|
+
export const DELETION_THRESHOLD = 5;
|
|
52
|
+
|
|
53
|
+
export interface ChangeEntry {
|
|
54
|
+
status: string;
|
|
55
|
+
path: string;
|
|
56
|
+
oldPath?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface Trigger {
|
|
60
|
+
id: string;
|
|
61
|
+
rule: string;
|
|
62
|
+
paths: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface Classification {
|
|
66
|
+
verdict: 'routine' | 'needs-council';
|
|
67
|
+
triggers: Trigger[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ClassifyOptions {
|
|
71
|
+
/** Does `dir` still exist in the head tree? Decides a removed directory. */
|
|
72
|
+
dirExistsInHead: (dir: string) => boolean;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const PROSE_EXT = new Set(['.md', '.mdx', '.txt', '.rst']);
|
|
76
|
+
|
|
77
|
+
const SECURITY_TOKENS = new Set([
|
|
78
|
+
'auth', 'authn', 'authz', 'authenticate', 'authentication', 'authorization', 'authorize',
|
|
79
|
+
'login', 'logout', 'oauth', 'sso', 'saml', 'mfa', '2fa', 'password', 'passwords', 'passwd', 'jwt',
|
|
80
|
+
'permission', 'permissions', 'rbac', 'acl', 'policy', 'policies',
|
|
81
|
+
'tenant', 'tenants', 'tenancy', 'multitenancy',
|
|
82
|
+
'billing', 'payment', 'payments', 'invoice', 'invoices', 'subscription', 'subscriptions',
|
|
83
|
+
'refund', 'refunds', 'charge', 'charges',
|
|
84
|
+
'secret', 'secrets', 'credential', 'credentials', 'vault', 'kms', 'env',
|
|
85
|
+
'upload', 'uploads', 'webhook', 'webhooks', 'routes',
|
|
86
|
+
]);
|
|
87
|
+
|
|
88
|
+
const INFRA_SEGMENTS = new Set([
|
|
89
|
+
'terraform', 'terragrunt', 'pulumi', 'k8s', 'kubernetes', 'helm', 'charts', 'ansible',
|
|
90
|
+
'cloudformation', 'cdk', 'infra', 'infrastructure',
|
|
91
|
+
]);
|
|
92
|
+
const INFRA_EXT = new Set(['.tf', '.tfvars', '.hcl']);
|
|
93
|
+
const INFRA_BASENAME_RE = /^(?:Pulumi(?:\.[\w-]+)?\.ya?ml|Chart\.ya?ml|kustomization\.ya?ml|serverless\.ya?ml)$/;
|
|
94
|
+
|
|
95
|
+
const PROD_TOKENS = new Set(['prod', 'production']);
|
|
96
|
+
const MIGRATION_SEGMENTS = new Set(['migrations', 'migration', 'migrate']);
|
|
97
|
+
const DEPLOY_TOKENS = new Set(['deploy', 'deployment', 'deployments', 'release', 'releases', 'publish']);
|
|
98
|
+
|
|
99
|
+
const SELF_SURFACE_RE =
|
|
100
|
+
/(?:^|\/)(?:scripts\/classify_merge_risk\.ts|domains\/git\/pr\/merge\/command\.md|domains\/product-basic\/roadmap\/process-full\/command\.md)$|^docs\/decisions\/ADR-282-[^/]+\.md$/;
|
|
101
|
+
|
|
102
|
+
/** Lower-cased word tokens of a path: split on separators and camelCase. */
|
|
103
|
+
function tokens(p: string): string[] {
|
|
104
|
+
return p
|
|
105
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
106
|
+
.toLowerCase()
|
|
107
|
+
.split(/[\/._\-\s]+/)
|
|
108
|
+
.filter((t) => t !== '');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function segments(p: string): string[] {
|
|
112
|
+
return p.toLowerCase().split('/');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function isProse(p: string): boolean {
|
|
116
|
+
return PROSE_EXT.has(path.posix.extname(p).toLowerCase());
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function isSecurity(p: string): boolean {
|
|
120
|
+
if (isProse(p)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const base = path.posix.basename(p);
|
|
124
|
+
if (base === '.env' || base.startsWith('.env.')) {
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return tokens(p).some((t) => SECURITY_TOKENS.has(t));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function isInfra(p: string): boolean {
|
|
131
|
+
if (INFRA_EXT.has(path.posix.extname(p).toLowerCase()) || INFRA_BASENAME_RE.test(path.posix.basename(p))) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
return !isProse(p) && segments(p).slice(0, -1).some((s) => INFRA_SEGMENTS.has(s));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function isProdOrMigration(p: string): boolean {
|
|
138
|
+
if (path.posix.extname(p).toLowerCase() === '.sql') {
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
if (isProse(p)) {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
return segments(p).some((s) => MIGRATION_SEGMENTS.has(s)) || tokens(p).some((t) => PROD_TOKENS.has(t));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function isDeployOrRelease(p: string): boolean {
|
|
148
|
+
return !isProse(p) && tokens(p).some((t) => DEPLOY_TOKENS.has(t));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function isWorkflow(p: string): boolean {
|
|
152
|
+
return p.startsWith('.github/workflows/');
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Every path a change touches — both sides of a rename or copy. */
|
|
156
|
+
function touched(entries: readonly ChangeEntry[]): string[] {
|
|
157
|
+
const out = new Set<string>();
|
|
158
|
+
for (const e of entries) {
|
|
159
|
+
out.add(e.path);
|
|
160
|
+
if (e.oldPath !== undefined) {
|
|
161
|
+
out.add(e.oldPath);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return [...out];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function classifyChanges(entries: readonly ChangeEntry[], opts: ClassifyOptions): Classification {
|
|
168
|
+
const triggers: Trigger[] = [];
|
|
169
|
+
const add = (id: string, rule: string, paths: string[]): void => {
|
|
170
|
+
if (paths.length > 0) {
|
|
171
|
+
triggers.push({ id, rule, paths: [...paths].sort() });
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const all = touched(entries);
|
|
175
|
+
const deleted = entries.filter((e) => e.status === 'D').map((e) => e.path);
|
|
176
|
+
|
|
177
|
+
if (deleted.length >= DELETION_THRESHOLD) {
|
|
178
|
+
add('bulk-deletion', `non-destructive-by-default — ${deleted.length} deleted files (≥ ${DELETION_THRESHOLD})`, deleted);
|
|
179
|
+
}
|
|
180
|
+
const goneDirs = [...new Set(deleted.map((p) => path.posix.dirname(p)).filter((d) => d !== '.'))].filter(
|
|
181
|
+
(d) => !opts.dirExistsInHead(d),
|
|
182
|
+
);
|
|
183
|
+
add(
|
|
184
|
+
'removed-directory',
|
|
185
|
+
'non-destructive-by-default — a directory no longer exists in the head',
|
|
186
|
+
deleted.filter((p) => goneDirs.includes(path.posix.dirname(p))),
|
|
187
|
+
);
|
|
188
|
+
add('infra-config', 'non-destructive-by-default — Terraform / Pulumi / k8s / Ansible / cloud config', all.filter(isInfra));
|
|
189
|
+
add('prod-data-or-migration', 'non-destructive-by-default — production data or a migration', all.filter(isProdOrMigration));
|
|
190
|
+
add('deploy-or-release', 'non-destructive-by-default — deploy or release path', all.filter(isDeployOrRelease));
|
|
191
|
+
add('ci-workflow', 'process-full — a disabled required check cannot reach delivery-ready', all.filter(isWorkflow));
|
|
192
|
+
add('security-surface', 'security-sensitive-stop — auth, authz, tenancy, billing, secrets, uploads, webhooks, routes', all.filter(isSecurity));
|
|
193
|
+
|
|
194
|
+
const gated = classifyPaths(all);
|
|
195
|
+
if (requiresRatification(gated)) {
|
|
196
|
+
const gatedPaths = [...gated.kernelRules, ...gated.governanceHooks, ...gated.plumbing];
|
|
197
|
+
add(
|
|
198
|
+
'governance-surface',
|
|
199
|
+
'check_kernel_edit_ratified — kernel rule, governance hook, hook plumbing or the ratification mechanism',
|
|
200
|
+
gatedPaths.length > 0 ? gatedPaths : all.filter((p) => classifyPaths([p]).self),
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
add('merge-authority-surface', 'ADR-282 — the danger gate and the commands that call it', all.filter((p) => SELF_SURFACE_RE.test(p)));
|
|
204
|
+
|
|
205
|
+
return { verdict: triggers.length > 0 ? 'needs-council' : 'routine', triggers };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Parse `git diff --name-status` output. Throws on a line it cannot read. */
|
|
209
|
+
export function parseNameStatus(text: string): ChangeEntry[] {
|
|
210
|
+
const out: ChangeEntry[] = [];
|
|
211
|
+
for (const line of text.split('\n')) {
|
|
212
|
+
if (line.trim() === '') {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const parts = line.split('\t');
|
|
216
|
+
const status = (parts[0] ?? '').charAt(0);
|
|
217
|
+
if (parts.length < 2 || !/^[AMDRCTU]$/.test(status)) {
|
|
218
|
+
throw new Error(`unreadable name-status line: ${JSON.stringify(line)}`);
|
|
219
|
+
}
|
|
220
|
+
if ((status === 'R' || status === 'C') && parts.length >= 3) {
|
|
221
|
+
out.push({ status, oldPath: parts[1] ?? '', path: parts[2] ?? '' });
|
|
222
|
+
} else {
|
|
223
|
+
out.push({ status, path: parts[1] ?? '' });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return out;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function git(root: string, args: string[]): { ok: boolean; out: string } {
|
|
230
|
+
const r = spawnSync('git', args, { cwd: root, encoding: 'utf8' });
|
|
231
|
+
return { ok: r.status === 0, out: r.stdout ?? '' };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
interface Resolved {
|
|
235
|
+
base: string;
|
|
236
|
+
head: string;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function resolvePr(root: string, pr: string): Resolved | null {
|
|
240
|
+
const v = spawnSync('gh', ['pr', 'view', pr, '--json', 'baseRefName,headRefOid'], { cwd: root, encoding: 'utf8' });
|
|
241
|
+
if (v.status !== 0) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const meta = JSON.parse(v.stdout) as { baseRefName?: string; headRefOid?: string };
|
|
245
|
+
if (meta.baseRefName === undefined || meta.headRefOid === undefined) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
git(root, ['fetch', '-q', 'origin', meta.baseRefName, `pull/${pr}/head`]);
|
|
249
|
+
return { base: `origin/${meta.baseRefName}`, head: meta.headRefOid };
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function resolveRange(range: string): Resolved | null {
|
|
253
|
+
const m = /^(.+?)\.\.\.?(.+)$/.exec(range);
|
|
254
|
+
return m === null ? null : { base: m[1] ?? '', head: m[2] ?? '' };
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function failClosed(reason: string): Classification {
|
|
258
|
+
return { verdict: 'needs-council', triggers: [{ id: 'diff-unreadable', rule: 'fail closed — ADR-282', paths: [reason] }] };
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export function classifyTarget(root: string, target: { pr?: string | undefined; range?: string | undefined }): Classification {
|
|
262
|
+
const resolved = target.pr !== undefined ? resolvePr(root, target.pr) : resolveRange(target.range ?? '');
|
|
263
|
+
if (resolved === null) {
|
|
264
|
+
return failClosed('could not resolve base and head');
|
|
265
|
+
}
|
|
266
|
+
const diff = git(root, ['diff', '--name-status', '-M', `${resolved.base}...${resolved.head}`]);
|
|
267
|
+
if (!diff.ok) {
|
|
268
|
+
return failClosed(`git diff ${resolved.base}...${resolved.head} failed`);
|
|
269
|
+
}
|
|
270
|
+
let entries: ChangeEntry[];
|
|
271
|
+
try {
|
|
272
|
+
entries = parseNameStatus(diff.out);
|
|
273
|
+
} catch (e) {
|
|
274
|
+
return failClosed((e as Error).message);
|
|
275
|
+
}
|
|
276
|
+
return classifyChanges(entries, {
|
|
277
|
+
dirExistsInHead: (dir) => git(root, ['cat-file', '-e', `${resolved.head}:${dir}`]).ok,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function main(argv: readonly string[] = process.argv.slice(2), write: (l: string) => void = (l) => process.stdout.write(`${l}\n`)): number {
|
|
282
|
+
let root = process.cwd();
|
|
283
|
+
let range: string | undefined;
|
|
284
|
+
let pr: string | undefined;
|
|
285
|
+
let json = false;
|
|
286
|
+
for (let i = 0; i < argv.length; i++) {
|
|
287
|
+
const a = argv[i] ?? '';
|
|
288
|
+
if (a === '--root') {
|
|
289
|
+
root = argv[++i] ?? root;
|
|
290
|
+
} else if (a === '--range') {
|
|
291
|
+
range = argv[++i];
|
|
292
|
+
} else if (a === '--json') {
|
|
293
|
+
json = true;
|
|
294
|
+
} else if (/^\d+$/.test(a)) {
|
|
295
|
+
pr = a;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
const result =
|
|
299
|
+
pr === undefined && range === undefined ? failClosed('no PR number and no --range given') : classifyTarget(root, { pr, range });
|
|
300
|
+
if (json) {
|
|
301
|
+
write(JSON.stringify(result));
|
|
302
|
+
} else {
|
|
303
|
+
for (const t of result.triggers) {
|
|
304
|
+
write(`trigger ${t.id} — ${t.rule}`);
|
|
305
|
+
for (const p of t.paths) {
|
|
306
|
+
write(` ${p}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
write(
|
|
310
|
+
result.verdict === 'routine'
|
|
311
|
+
? 'verdict: routine'
|
|
312
|
+
: `verdict: needs-council (${result.triggers.length} trigger(s): ${result.triggers.map((t) => t.id).join(', ')})`,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
return result.verdict === 'routine' ? 0 : 1;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (import.meta.url === pathToFileURL(process.argv[1] ?? '').href) {
|
|
319
|
+
process.exit(main());
|
|
320
|
+
}
|
|
@@ -130,7 +130,12 @@ export const NON_BLOCKING_KINDS: ReadonlySet<Kind> = new Set<Kind>(['unfalsifiab
|
|
|
130
130
|
* on the CONTENT a step asserts. Anything whose exit code already tracks the
|
|
131
131
|
* claim — `grep` (1 on no match), a linter, a test runner — is absent on
|
|
132
132
|
* purpose: including it would turn this listing into a census of every verify
|
|
133
|
-
* line in the tree, which is step 2.2's instrument, not this one.
|
|
133
|
+
* line in the tree, which is step 2.2's instrument, not this one. One case
|
|
134
|
+
* reads titles and runs nothing: a name-filtered test run
|
|
135
|
+
* (`vitest run <target> -t <name>`) whose target exists carries its own
|
|
136
|
+
* failure mode a plain exit-code read cannot see — `-t` with no matching
|
|
137
|
+
* title skips every test and still exits 0 — so that one shape is read for
|
|
138
|
+
* content, never executed, in `_nameFilterHasNoMatch` below.
|
|
134
139
|
*/
|
|
135
140
|
const FIXED_OUTPUT_HEADS: ReadonlySet<string> = new Set(['echo', 'true', 'printf', 'cat', 'ls', 'pwd', 'date']);
|
|
136
141
|
|
|
@@ -149,6 +154,87 @@ const FIXED_OUTPUT_HEADS: ReadonlySet<string> = new Set(['echo', 'true', 'printf
|
|
|
149
154
|
* remainder as clean, which is the same mistake as reporting a share off a
|
|
150
155
|
* corpus nobody scanned.
|
|
151
156
|
*/
|
|
157
|
+
// `vitest run <target> -t <name>` (optionally `npx`-prefixed); `<name>` may
|
|
158
|
+
// be bare, single-quoted or double-quoted. The target is captured
|
|
159
|
+
// non-greedily up to the first whitespace or quote.
|
|
160
|
+
const VITEST_NAME_FILTER_RE = /(?:^|\s)vitest\s+run\s+(\S+)\s+-t\s+(?:'([^']+)'|"([^"]+)"|(\S+))/;
|
|
161
|
+
|
|
162
|
+
// `describe(`, `it(`, `test(`, and their `.only`/`.skip`/`.each`/`.todo`
|
|
163
|
+
// modifiers, each with a quoted first argument — the title vitest matches
|
|
164
|
+
// `-t` against (concatenated with its ancestor `describe` titles at
|
|
165
|
+
// run time; this reads each title string standalone, which is sufficient to
|
|
166
|
+
// prove a filter CAN match something — the question this function asks).
|
|
167
|
+
const TEST_TITLE_RE = /\b(?:describe|it|test)(?:\.\w+)?\s*\(\s*(['"`])((?:(?!\1).)*)\1/g;
|
|
168
|
+
|
|
169
|
+
function _collectTestFiles(target: string): string[] {
|
|
170
|
+
let stat: fs.Stats;
|
|
171
|
+
try {
|
|
172
|
+
stat = fs.statSync(target);
|
|
173
|
+
} catch {
|
|
174
|
+
return [];
|
|
175
|
+
}
|
|
176
|
+
if (stat.isFile()) return [target];
|
|
177
|
+
if (!stat.isDirectory()) return [];
|
|
178
|
+
const out: string[] = [];
|
|
179
|
+
let entries: string[];
|
|
180
|
+
try {
|
|
181
|
+
entries = fs.readdirSync(target, { recursive: true }) as string[];
|
|
182
|
+
} catch {
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
for (const rel of entries) {
|
|
186
|
+
if (!/\.(?:test|spec)\.tsx?$/.test(rel)) continue;
|
|
187
|
+
out.push(path.join(target, rel));
|
|
188
|
+
}
|
|
189
|
+
return out;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function _extractTitles(file: string): string[] {
|
|
193
|
+
let text: string;
|
|
194
|
+
try {
|
|
195
|
+
text = fs.readFileSync(file, 'utf-8');
|
|
196
|
+
} catch {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
const titles: string[] = [];
|
|
200
|
+
const re = new RegExp(TEST_TITLE_RE.source, TEST_TITLE_RE.flags);
|
|
201
|
+
let m: RegExpExecArray | null;
|
|
202
|
+
while ((m = re.exec(text)) !== null) {
|
|
203
|
+
titles.push(m[2] ?? '');
|
|
204
|
+
}
|
|
205
|
+
return titles;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* A name-filtered vitest run whose target exists but carries no test title
|
|
210
|
+
* containing the filter — the oracle exits 0 on every test skipped, which a
|
|
211
|
+
* plain exit-code read cannot distinguish from every test passing.
|
|
212
|
+
*
|
|
213
|
+
* `null` whenever the shape does not match, the target cannot be resolved
|
|
214
|
+
* (nothing to read), or no title could be extracted at all (a parse miss,
|
|
215
|
+
* not evidence of absence) — in every one of those the finding would be a
|
|
216
|
+
* guess, and this function only reports what it read.
|
|
217
|
+
*/
|
|
218
|
+
function _nameFilterHasNoMatch(command: string): string | null {
|
|
219
|
+
const m = VITEST_NAME_FILTER_RE.exec(command);
|
|
220
|
+
if (m === null) return null;
|
|
221
|
+
const targetRaw = m[1] as string;
|
|
222
|
+
const name = (m[2] ?? m[3] ?? m[4] ?? '').trim();
|
|
223
|
+
if (name === '') return null;
|
|
224
|
+
const target = path.resolve(process.cwd(), targetRaw);
|
|
225
|
+
const files = _collectTestFiles(target);
|
|
226
|
+
if (files.length === 0) return null;
|
|
227
|
+
let sawAnyTitle = false;
|
|
228
|
+
for (const file of files) {
|
|
229
|
+
for (const title of _extractTitles(file)) {
|
|
230
|
+
sawAnyTitle = true;
|
|
231
|
+
if (title.includes(name)) return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
if (!sawAnyTitle) return null;
|
|
235
|
+
return `\`-t ${name}\` matches no test title under ${targetRaw}: the run exits 0 with every test skipped`;
|
|
236
|
+
}
|
|
237
|
+
|
|
152
238
|
export function unfalsifiableReason(clause: VerifyClause): string | null {
|
|
153
239
|
if (clause.command === null) {
|
|
154
240
|
// A MANUAL clause asserting a QUANTITY nothing produced. Narrowed to a
|
|
@@ -176,6 +262,10 @@ export function unfalsifiableReason(clause: VerifyClause): string | null {
|
|
|
176
262
|
// An expectation that cannot compile never became one upstream.
|
|
177
263
|
}
|
|
178
264
|
}
|
|
265
|
+
|
|
266
|
+
const nameFilterReason = _nameFilterHasNoMatch(clause.command);
|
|
267
|
+
if (nameFilterReason !== null) return nameFilterReason;
|
|
268
|
+
|
|
179
269
|
return null;
|
|
180
270
|
}
|
|
181
271
|
|