@event4u/agent-config 14.10.0 → 14.12.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 +161 -208
- package/README.md +1 -1
- package/dist/agent-src/commands/design.md +88 -0
- package/dist/agent-src/commands/project/analyze.md +47 -0
- package/dist/agent-src/commands/review/changes.md +67 -6
- package/dist/agent-src/commands/roadmap/create.md +49 -0
- package/dist/agent-src/commands/roadmap/next.md +27 -11
- package/dist/agent-src/commands/roadmap/process-full.md +14 -0
- package/dist/agent-src/commands/video/from-script.md +60 -3
- package/dist/agent-src/commands/video/stitch.md +14 -0
- package/dist/agent-src/contexts/communication/rules-auto/guidelines-mechanics.md +1 -0
- package/dist/agent-src/contexts/execution/evidence-discipline.md +26 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +154 -2
- package/dist/agent-src/rules/active-remediation.md +3 -3
- package/dist/agent-src/rules/code-provenance.md +2 -2
- package/dist/agent-src/rules/context-hygiene.md +13 -33
- package/dist/agent-src/rules/council-availability.md +2 -2
- package/dist/agent-src/rules/decision-revisit-gate.md +3 -1
- package/dist/agent-src/rules/design-fidelity.md +18 -0
- package/dist/agent-src/rules/design-review-after-ui-write.md +2 -2
- package/dist/agent-src/rules/evaluator-independence.md +56 -44
- package/dist/agent-src/rules/fix-what-you-see.md +9 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +14 -20
- package/dist/agent-src/rules/missing-skill-recovery.md +13 -1
- package/dist/agent-src/rules/playbook-precedence.md +105 -0
- package/dist/agent-src/rules/recurring-criticism.md +3 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +14 -13
- package/dist/agent-src/rules/scale-discipline.md +24 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -2
- package/dist/agent-src/rules/settings-ask-protocol.md +2 -2
- package/dist/agent-src/rules/ui-audit-gate.md +27 -15
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -2
- package/dist/agent-src/skills/accessibility-auditor/SKILL.md +19 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +7 -0
- package/dist/agent-src/skills/agents-md-thin-root/SKILL.md +33 -0
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +17 -3
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +31 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +19 -0
- package/dist/agent-src/skills/alerting-doctrine/SKILL.md +225 -0
- package/dist/agent-src/skills/authz-review/SKILL.md +0 -1
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +32 -0
- package/dist/agent-src/skills/brand/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-asset-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-audit/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-identity/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-strategy/SKILL.md +3 -0
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/check-refs/SKILL.md +3 -0
- package/dist/agent-src/skills/code-refactoring/SKILL.md +45 -1
- package/dist/agent-src/skills/code-review/SKILL.md +75 -5
- package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
- package/dist/agent-src/skills/command-writing/SKILL.md +35 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +3 -0
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
- package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
- package/dist/agent-src/skills/design-review/SKILL.md +79 -1
- package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
- package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
- package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +28 -4
- package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
- package/dist/agent-src/skills/fe-design/references/craft-floor.md +52 -0
- package/dist/agent-src/skills/fe-design/references/source-led-port.md +36 -0
- package/dist/agent-src/skills/file-editor/SKILL.md +5 -0
- package/dist/agent-src/skills/finishing-a-development-branch/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/SKILL.md +3 -0
- package/dist/agent-src/skills/html-deck/SKILL.md +3 -0
- package/dist/agent-src/skills/iconography/SKILL.md +3 -0
- package/dist/agent-src/skills/image-editing/SKILL.md +3 -0
- package/dist/agent-src/skills/image-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +3 -0
- package/dist/agent-src/skills/incident-commander/SKILL.md +10 -0
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +173 -0
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
- package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
- package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
- package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
- package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
- package/dist/agent-src/skills/project-analysis-core/SKILL.md +23 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +81 -33
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/readme-writing-package/SKILL.md +3 -0
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
- package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
- package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-management/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-reviewer/SKILL.md +3 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +6 -0
- package/dist/agent-src/skills/skill-writing/references/procedure.md +24 -2
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +3 -0
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +203 -0
- package/dist/agent-src/skills/storybook-workshop/scripts/story_contrast_floor.ts +134 -0
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/dist/agent-src/skills/subagent-orchestration/prompts/README.md +25 -0
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +12 -0
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +1 -1
- package/dist/agent-src/skills/terraform/SKILL.md +3 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +1 -1
- package/dist/agent-src/skills/threat-modeling/SKILL.md +0 -3
- package/dist/agent-src/skills/token-optimizer/SKILL.md +3 -0
- package/dist/agent-src/skills/typography-system/SKILL.md +3 -0
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +17 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +3 -0
- package/dist/agent-src/skills/verify-completion-evidence/SKILL.md +2 -2
- package/dist/agent-src/skills/wireframe/SKILL.md +3 -0
- package/dist/agent-src/skills/wireframe/references/story-beat-ledger.schema.json +115 -0
- package/dist/agent-src/skills/workspace-link/SKILL.md +131 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/playbook.md +75 -0
- package/dist/agent-src/templates/roadmaps.md +37 -0
- package/dist/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/dist/cli/commands/uiAudit.js +221 -0
- package/dist/cli/commands/uiRender.js +206 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/mcp/content.js +64 -1
- package/dist/cli/mcp/dispatch.js +175 -10
- package/dist/cli/registry.js +3 -0
- package/dist/cli-delegate/{chunk-G23AT6GD.js → chunk-DMSZHXZS.js} +57 -1
- package/dist/cli-delegate/{chunk-77GR3VD4.js → chunk-FF7WAFMR.js} +58 -3
- package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-JCW7ZLHK.js} +10 -0
- package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-JJA2QEWA.js} +413 -391
- package/dist/cli-delegate/{chunk-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
- package/dist/cli-delegate/{chunk-B2ERU2VA.js → chunk-WOKWX5PL.js} +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +8 -6
- package/dist/cli-delegate/cmd_conformance.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +70 -9
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +362 -127
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +18 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -6
- package/dist/hooks/dispatch.js +2575 -1472
- package/dist/install/install.mjs +800 -708
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1685 -1250
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/lean_projection_mode.js +40 -0
- package/dist/scripts/_lib/scoped_projection.js +84 -0
- package/dist/scripts/_lib/ui_surface.js +89 -0
- package/dist/scripts/condense.js +6 -10
- package/dist/scripts/project_thin_rules.js +40 -2
- package/dist/server/schemas/settings.js +6 -0
- package/dist/shared/skillRanking.js +159 -0
- package/docs/contracts/provider-lifecycle.md +47 -18
- package/docs/contracts/settings-classes.md +35 -5
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -0
- package/docs/guidelines/agent-infra/downstream-changes-mechanics.md +91 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +45 -1
- package/docs/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
- package/docs/guidelines/code-clarity.md +21 -0
- package/docs/guidelines/design-antipatterns.md +4 -2
- package/docs/guidelines/design-asset-discipline.md +38 -0
- package/docs/guidelines/design-fidelity-mechanics.md +45 -29
- package/docs/guidelines/design-handover-extraction.md +35 -5
- package/docs/guidelines/design-modes.md +41 -1
- package/docs/guidelines/monorepo-antipatterns.md +141 -0
- package/docs/guidelines/redundancy-taxonomy.md +180 -0
- package/llms.txt +50 -39
- package/package.json +6 -2
- package/src/agent-src/templates/scripts/check_playbook_invokes.ts +231 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +24 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +2 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +216 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +159 -0
- package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +73 -4
- package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +325 -40
- package/src/config/agent-settings.template.yml +44 -0
- package/src/config/assurance-capability-registry.json +223 -0
- package/src/config/assurance-threshold-budget.json +65 -0
- package/src/config/budgets.yml +19 -0
- package/src/config/ci-local-parity.yml +29 -0
- package/src/config/discovery/prompt-patterns.yml +56 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/dispatch-economy-metrics.json +4 -2
- package/src/config/estate-count-budget.json +8 -3
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +392 -8
- package/src/config/gate-violation-baselines.json +76 -72
- package/src/config/hook-latency-budget.json +1 -0
- package/src/config/hook-token-budget.json +137 -125
- package/src/config/pack-size-budget.json +39 -10
- package/src/config/preamble-payload-budget.json +97 -71
- package/src/config/quorum-attendance-budget.json +12 -4
- package/src/config/routing-coverage-seed.json +29 -0
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/config/rule-stub-ceilings.json +112 -91
- package/src/config/suggestion-capture.json +28 -0
- package/src/config/surface-matrix.yml +21 -2
- package/src/config/web-launch-readiness.json +188 -0
- package/src/scripts/_cli/cmd_doctor.ts +15 -16
- package/src/scripts/_cli/doctor_install_mode.ts +49 -0
- package/src/scripts/_cli/doctor_overrides.ts +154 -0
- package/src/scripts/_dispatch.bash +17 -0
- package/src/scripts/_lib/as_of.ts +412 -0
- package/src/scripts/_lib/exec_evidence.ts +7 -0
- package/src/scripts/_lib/gate_baseline.ts +150 -1
- package/src/scripts/_lib/hook_settings.ts +33 -0
- package/src/scripts/_lib/host_listing_model.ts +192 -0
- package/src/scripts/_lib/lean_projection_mode.ts +44 -0
- package/src/scripts/_lib/mcp_bridge.ts +199 -0
- package/src/scripts/_lib/review_independence.ts +62 -10
- package/src/scripts/_lib/rule_injection.ts +239 -0
- package/src/scripts/_lib/run_checkpoint.ts +54 -1
- package/src/scripts/_lib/scoped_projection.ts +96 -0
- package/src/scripts/_lib/session_register.ts +44 -2
- package/src/scripts/_lib/skill_catalogue.ts +38 -2
- package/src/scripts/_lib/skill_estate.ts +115 -0
- package/src/scripts/_lib/transcript_reads.ts +207 -0
- package/src/scripts/_lib/ui_authority.ts +343 -0
- package/src/scripts/ai-video/adapters/fal.sh +8 -3
- package/src/scripts/ai-video/adapters/gemini-veo.sh +10 -0
- package/src/scripts/ai-video/adapters/higgsfield.sh +10 -2
- package/src/scripts/ai-video/adapters/kling.sh +10 -0
- package/src/scripts/ai-video/adapters/sora.sh +10 -0
- package/src/scripts/ai-video/lib/calibrate-cost.sh +145 -0
- package/src/scripts/ai-video/lib/model-capabilities/README.md +53 -7
- package/src/scripts/ai-video/lib/model-capabilities/comfyui.json +15 -3
- package/src/scripts/ai-video/lib/model-capabilities/fal.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/gemini-veo.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/higgsfield.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/kling.json +23 -0
- package/src/scripts/ai-video/lib/model-capabilities/musetalk.json +8 -2
- package/src/scripts/ai-video/lib/model-capabilities/replicate.json +36 -6
- package/src/scripts/ai-video/lib/model-capabilities/sora.json +45 -0
- package/src/scripts/ai-video/lib/model-capabilities/syncso.json +8 -2
- package/src/scripts/ai-video/lib/parse-blueprint.sh +52 -5
- package/src/scripts/ai-video/lib/resume-scan.sh +92 -3
- package/src/scripts/ai-video/lib/seam-plan.sh +212 -0
- package/src/scripts/ai-video/smoke-trace.sh +450 -2
- package/src/scripts/ai-video/stitch.sh +191 -10
- package/src/scripts/ai-video/test-pipeline.sh +84 -6
- package/src/scripts/ai_council/argument_exhaustion.ts +130 -0
- package/src/scripts/ai_council/cli_agency_bounds.ts +139 -0
- package/src/scripts/ai_council/cli_least_agency_canary.ts +347 -0
- package/src/scripts/ai_council/clients.ts +3 -2
- package/src/scripts/ai_council/consensus.ts +63 -6
- package/src/scripts/ai_council/events_log.ts +46 -1
- package/src/scripts/ai_council/orchestrator.ts +103 -108
- package/src/scripts/ai_council/orchestrator_results.ts +166 -0
- package/src/scripts/ai_council/prompts.ts +12 -5
- package/src/scripts/ai_council/quorum.ts +82 -1
- package/src/scripts/ai_council/quorum_wiring.ts +82 -5
- package/src/scripts/ai_council/stance_tally.ts +22 -0
- package/src/scripts/bench_hook_latency.ts +62 -0
- package/src/scripts/build_proof.ts +12 -1
- package/src/scripts/capture_skill_catalogue.ts +21 -0
- package/src/scripts/check_always_budget.ts +2 -1
- package/src/scripts/check_augmentignore.ts +2 -1
- package/src/scripts/check_beta_review_markers.ts +2 -1
- package/src/scripts/check_bundle_path_leakage.ts +162 -7
- package/src/scripts/check_command_examples.ts +557 -0
- package/src/scripts/check_composite_arming.ts +364 -0
- package/src/scripts/check_corpus_staleness.ts +2 -1
- package/src/scripts/check_council_pin_staleness.ts +3 -1
- package/src/scripts/check_enforcement_coverage.ts +116 -3
- package/src/scripts/check_enforcement_denominator.ts +365 -0
- package/src/scripts/check_estate_count.ts +122 -3
- package/src/scripts/check_gate_coverage.ts +2 -1
- package/src/scripts/check_hook_bundle_freshness.ts +13 -1
- package/src/scripts/check_knowledge_cards.ts +2 -1
- package/src/scripts/check_knowledge_pages.ts +3 -2
- package/src/scripts/check_memory.ts +2 -1
- package/src/scripts/check_no_automerge_key.ts +287 -0
- package/src/scripts/check_pack_conformance_fixture.ts +565 -0
- package/src/scripts/check_preamble_payload_budget.ts +30 -3
- package/src/scripts/check_proposal.ts +2 -1
- package/src/scripts/check_reach_staleness.ts +2 -1
- package/src/scripts/check_release_adjacent_health.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +5 -0
- package/src/scripts/check_review_schema.ts +34 -1
- package/src/scripts/check_roadmap_trackable.ts +48 -3
- package/src/scripts/check_routing_coverage.ts +352 -0
- package/src/scripts/check_score_contract.ts +505 -0
- package/src/scripts/check_skill_admissions.ts +514 -0
- package/src/scripts/check_source_size_budget.ts +2 -1
- package/src/scripts/check_standing_payload_delta.ts +505 -0
- package/src/scripts/check_trigger_evals.ts +2 -1
- package/src/scripts/check_web_launch_readiness.ts +705 -0
- package/src/scripts/classify_change_risk.ts +281 -0
- package/src/scripts/compute_skill_tiers.ts +186 -0
- package/src/scripts/condense.ts +8 -13
- package/src/scripts/council_attendance_metrics.ts +183 -0
- package/src/scripts/council_cli.ts +10 -8
- package/src/scripts/council_parse_rate.ts +116 -0
- package/src/scripts/derive_playbooks.ts +406 -0
- package/src/scripts/description_route_check.ts +494 -0
- package/src/scripts/design_slop_rules.ts +56 -2
- package/src/scripts/dispatch_r2_reviewer.ts +16 -0
- package/src/scripts/frontend_corpus_hash.ts +116 -0
- package/src/scripts/grade_target_readiness.ts +478 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +124 -16
- package/src/scripts/hooks/concern_registry.ts +6 -0
- package/src/scripts/hooks/design_pass_hook.ts +356 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +30 -7
- package/src/scripts/hooks/rule_inject_hook.ts +318 -0
- package/src/scripts/hooks/suggestion_capture_hook.ts +368 -0
- package/src/scripts/hot_context_hook.ts +74 -7
- package/src/scripts/install.ts +40 -89
- package/src/scripts/lint_adapter_tier.ts +663 -0
- package/src/scripts/lint_archived_skills.ts +93 -0
- package/src/scripts/lint_budget_ownership.ts +56 -5
- package/src/scripts/lint_deterministic_time.ts +470 -0
- package/src/scripts/lint_framework_leakage.ts +5 -0
- package/src/scripts/lint_handoffs.ts +525 -1
- package/src/scripts/lint_judge_prompt_expectation.ts +155 -0
- package/src/scripts/lint_marketplace.ts +88 -0
- package/src/scripts/lint_media_policy_linkage.ts +140 -1
- package/src/scripts/lint_one_off_age.ts +6 -1
- package/src/scripts/lint_override_kernel_guard.ts +87 -5
- package/src/scripts/lint_roadmap_complexity.ts +56 -0
- package/src/scripts/lint_rule_enforcement_declaration.ts +24 -7
- package/src/scripts/lint_rule_norm_pin.ts +321 -0
- package/src/scripts/lint_skill_trigger_corpus.ts +349 -0
- package/src/scripts/lint_spawn_payload.ts +42 -5
- package/src/scripts/lint_symptom_intake.ts +2 -1
- package/src/scripts/mcp_parity_smoke.ts +30 -4
- package/src/scripts/mcp_render.ts +66 -1
- package/src/scripts/mcp_tool_fingerprint.ts +241 -0
- package/src/scripts/measure_risk_class_drift.ts +140 -0
- package/src/scripts/measure_skill_ranker_baseline.ts +212 -0
- package/src/scripts/media/lib/adapter-common.sh +211 -1
- package/src/scripts/media/lib/adapter-contract.md +74 -6
- package/src/scripts/model_rule_injection.ts +907 -0
- package/src/scripts/pack_install_smoke.ts +363 -0
- package/src/scripts/pr_conflict_census.ts +187 -0
- package/src/scripts/project_thin_rules.ts +45 -2
- package/src/scripts/release.ts +66 -860
- package/src/scripts/release_drill.ts +59 -3
- package/src/scripts/release_env.ts +298 -0
- package/src/scripts/release_publication.ts +720 -0
- package/src/scripts/report_pack_delta.ts +204 -0
- package/src/scripts/roadmap_context.ts +778 -0
- package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
- package/src/scripts/schemas/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +39 -0
- package/src/scripts/schemas/ui-authority.schema.json +114 -0
- package/src/scripts/select_analysis_mode.ts +112 -0
- package/src/scripts/session_register_hook.ts +29 -3
- package/src/scripts/sessions_cli.ts +67 -3
- package/src/scripts/skill_tools/score_skill_relevance.ts +36 -53
- package/src/scripts/sync_pr_branch.ts +40 -1
- package/src/scripts/token_sink_report.ts +239 -0
- package/src/scripts/trigger_coverage.ts +122 -3
- package/src/scripts/update_counts.ts +7 -3
- package/src/scripts/validate_frontmatter.ts +66 -0
- package/src/server/schemas/settings.ts +10 -0
- package/src/shared/skillRanking.ts +183 -0
- package/dist/agent-src/skills/accessibility-auditor/evals/evals.json +0 -15
- package/dist/agent-src/skills/agent-security-review/evals/triggers.json +0 -52
- package/dist/agent-src/skills/ai-code-blindspots/evals/triggers.json +0 -52
- package/dist/agent-src/skills/analysis-skill-router/evals/evals.json +0 -13
- package/dist/agent-src/skills/brand/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-asset-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-audit/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-identity/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-strategy/evals/triggers.json +0 -17
- package/dist/agent-src/skills/brand-to-tokens/evals/triggers.json +0 -17
- package/dist/agent-src/skills/bug-analyzer/evals/evals.json +0 -13
- package/dist/agent-src/skills/canvas-design/evals/triggers.json +0 -17
- package/dist/agent-src/skills/character-consistency/evals/evals.json +0 -16
- package/dist/agent-src/skills/code-intelligence/evals/triggers.json +0 -52
- package/dist/agent-src/skills/code-review/evals/evals.json +0 -100
- package/dist/agent-src/skills/command-routing/evals/evals.json +0 -14
- package/dist/agent-src/skills/complexity-first-planning/evals/triggers.json +0 -17
- package/dist/agent-src/skills/contract-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/conventional-commits-writing/evals/evals.json +0 -15
- package/dist/agent-src/skills/corpus-grounding/evals/triggers.json +0 -17
- package/dist/agent-src/skills/dashboard-design/evals/evals.json +0 -16
- package/dist/agent-src/skills/dcf-modeling/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/dcf-modeling/evals/evals.json +0 -20
- package/dist/agent-src/skills/decision-record/evals/evals.json +0 -20
- package/dist/agent-src/skills/decision-record/evals/triggers.json +0 -87
- package/dist/agent-src/skills/design-intelligence/evals/evals.json +0 -14
- package/dist/agent-src/skills/design-intelligence/evals/triggers.json +0 -17
- package/dist/agent-src/skills/design-system-capture/evals/evals.json +0 -15
- package/dist/agent-src/skills/design-tokens/evals/triggers.json +0 -17
- package/dist/agent-src/skills/design-variations/evals/triggers.json +0 -17
- package/dist/agent-src/skills/developer-like-execution/evals/evals.json +0 -16
- package/dist/agent-src/skills/doc-coauthoring/evals/evals.json +0 -16
- package/dist/agent-src/skills/doc-coauthoring/evals/triggers.json +0 -17
- package/dist/agent-src/skills/docx-authoring/evals/triggers.json +0 -52
- package/dist/agent-src/skills/dpa-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/editorial-calendar/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/editorial-calendar/evals/evals.json +0 -16
- package/dist/agent-src/skills/eloquent/evals/triggers.json +0 -17
- package/dist/agent-src/skills/estimate-ticket/evals/evals.json +0 -16
- package/dist/agent-src/skills/estimate-ticket/evals/output-schema.yml +0 -20
- package/dist/agent-src/skills/estimate-ticket/evals/triggers.json +0 -19
- package/dist/agent-src/skills/evaluate-llm-feature/evals/triggers.json +0 -52
- package/dist/agent-src/skills/existing-ui-audit/evals/triggers.json +0 -47
- package/dist/agent-src/skills/experiment-loop/evals/output-schema.yml +0 -27
- package/dist/agent-src/skills/experiment-loop/evals/triggers.json +0 -17
- package/dist/agent-src/skills/fe-design/evals/triggers.json +0 -55
- package/dist/agent-src/skills/forecasting/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/forecasting/evals/evals.json +0 -20
- package/dist/agent-src/skills/forensics-report/evals/triggers.json +0 -17
- package/dist/agent-src/skills/frontend-render-security/evals/triggers.json +0 -52
- package/dist/agent-src/skills/fundraising-narrative/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/fundraising-narrative/evals/evals.json +0 -16
- package/dist/agent-src/skills/gated-reach/evals/triggers.json +0 -72
- package/dist/agent-src/skills/github-ci/evals/evals.json +0 -25
- package/dist/agent-src/skills/history-design/evals/triggers.json +0 -17
- package/dist/agent-src/skills/html-deck/evals/triggers.json +0 -17
- package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +0 -57
- package/dist/agent-src/skills/humanizer/evals/triggers.json +0 -17
- package/dist/agent-src/skills/iconography/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-analyser/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-creator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-editing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/image-provider-routing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/incident-commander/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/incident-commander/evals/evals.json +0 -20
- package/dist/agent-src/skills/judge-artifact-completeness/evals/triggers.json +0 -17
- package/dist/agent-src/skills/judge-injection-defense/evals/triggers.json +0 -17
- package/dist/agent-src/skills/judge-synthesis/evals/triggers.json +0 -17
- package/dist/agent-src/skills/launch-readiness/evals/evals.json +0 -21
- package/dist/agent-src/skills/learning-tutor/evals/triggers.json +0 -52
- package/dist/agent-src/skills/legal-intake-triage/evals/triggers.json +0 -17
- package/dist/agent-src/skills/legal-practice-profile/evals/triggers.json +0 -17
- package/dist/agent-src/skills/license-compliance-audit/evals/triggers.json +0 -52
- package/dist/agent-src/skills/license-compliance-borrow-check/evals/triggers.json +0 -52
- package/dist/agent-src/skills/license-compliance-credits/evals/triggers.json +0 -52
- package/dist/agent-src/skills/llm-provider-knowledge/evals/triggers.json +0 -20
- package/dist/agent-src/skills/logging-monitoring/evals/evals.json +0 -16
- package/dist/agent-src/skills/logo-generation/evals/triggers.json +0 -17
- package/dist/agent-src/skills/messaging-architecture/evals/domain-truth.json +0 -16
- package/dist/agent-src/skills/messaging-architecture/evals/evals.json +0 -16
- package/dist/agent-src/skills/nda-triage/evals/triggers.json +0 -17
- package/dist/agent-src/skills/pdf-tools/evals/triggers.json +0 -52
- package/dist/agent-src/skills/perf-feedback-craft/evals/evals.json +0 -16
- package/dist/agent-src/skills/persona-improvement/evals/triggers.json +0 -52
- package/dist/agent-src/skills/php-coder/evals/triggers.json +0 -17
- package/dist/agent-src/skills/prediction-pool-optimizer/evals/triggers.json +0 -21
- package/dist/agent-src/skills/prompt-engineering-image/evals/triggers.json +0 -17
- package/dist/agent-src/skills/prompt-engineering-patterns/evals/evals.json +0 -16
- package/dist/agent-src/skills/prompt-validator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/quality-tools/evals/evals.json +0 -15
- package/dist/agent-src/skills/reasoning-orchestrator/evals/triggers.json +0 -17
- package/dist/agent-src/skills/refine-prompt/evals/evals.json +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/evals.json +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/output-schema.yml +0 -16
- package/dist/agent-src/skills/refine-ticket/evals/triggers.json +0 -17
- package/dist/agent-src/skills/release-comms/evals/evals.json +0 -16
- package/dist/agent-src/skills/rice-prioritization/evals/evals.json +0 -20
- package/dist/agent-src/skills/runway-cognition/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/runway-cognition/evals/evals.json +0 -20
- package/dist/agent-src/skills/scenario-modeling/evals/domain-truth.json +0 -29
- package/dist/agent-src/skills/scenario-modeling/evals/evals.json +0 -20
- package/dist/agent-src/skills/schema-review/evals/triggers.json +0 -17
- package/dist/agent-src/skills/screenshot-hygiene/evals/triggers.json +0 -52
- package/dist/agent-src/skills/security-maturity-assessment/evals/triggers.json +0 -52
- package/dist/agent-src/skills/skill-writing/evals/triggers.json +0 -17
- package/dist/agent-src/skills/spreadsheet-authoring/evals/triggers.json +0 -47
- package/dist/agent-src/skills/subagent-orchestration/evals/evals.json +0 -60
- package/dist/agent-src/skills/supply-chain-intake/evals/triggers.json +0 -52
- package/dist/agent-src/skills/systematic-debugging/evals/evals.json +0 -20
- package/dist/agent-src/skills/test-case-discovery/evals/triggers.json +0 -52
- package/dist/agent-src/skills/test-driven-development/evals/evals.json +0 -16
- package/dist/agent-src/skills/threat-modeling/evals/evals.json +0 -21
- package/dist/agent-src/skills/typography-system/evals/evals.json +0 -14
- package/dist/agent-src/skills/typography-system/evals/triggers.json +0 -17
- package/dist/agent-src/skills/unit-economics-modeling/evals/domain-truth.json +0 -40
- package/dist/agent-src/skills/unit-economics-modeling/evals/evals.json +0 -24
- package/dist/agent-src/skills/verify-completion-evidence/evals/evals.json +0 -16
- package/dist/agent-src/skills/verify-repair-loop/evals/output-schema.yml +0 -20
- package/dist/agent-src/skills/verify-repair-loop/evals/triggers.json +0 -17
- package/dist/agent-src/skills/vision-articulation/evals/evals.json +0 -16
- package/dist/agent-src/skills/voice-and-tone-design/evals/evals.json +0 -16
- package/dist/agent-src/skills/wireframe/evals/triggers.json +0 -17
- package/dist/agent-src/skills/worktree-lifecycle/evals/evals.json +0 -38
- package/dist/agent-src/skills/worktree-lifecycle/evals/triggers.json +0 -52
- package/dist/cli/agent-config.js.map +0 -1
- package/dist/cli/bash/runBash.js.map +0 -1
- package/dist/cli/commands/commands.js.map +0 -1
- package/dist/cli/commands/config.js.map +0 -1
- package/dist/cli/commands/doctorShell.js.map +0 -1
- package/dist/cli/commands/mcpServer.js.map +0 -1
- package/dist/cli/commands/packs.js.map +0 -1
- package/dist/cli/commands/profiles.js.map +0 -1
- package/dist/cli/commands/recordTriggerEval.js.map +0 -1
- package/dist/cli/commands/rtkDetect.js.map +0 -1
- package/dist/cli/commands/settings.js.map +0 -1
- package/dist/cli/commands/uiServe.js.map +0 -1
- package/dist/cli/commands/versions.js.map +0 -1
- package/dist/cli/commands/workspaces.js.map +0 -1
- package/dist/cli/configRoot.js.map +0 -1
- package/dist/cli/discovery/loadManifest.js.map +0 -1
- package/dist/cli/firstRunNotice.js.map +0 -1
- package/dist/cli/help.js.map +0 -1
- package/dist/cli/initRouting.js.map +0 -1
- package/dist/cli/log/logger.js.map +0 -1
- package/dist/cli/main.js.map +0 -1
- package/dist/cli/mcp/content.js.map +0 -1
- package/dist/cli/mcp/dispatch.js.map +0 -1
- package/dist/cli/mcp/stdio.js.map +0 -1
- package/dist/cli/paths.js.map +0 -1
- package/dist/cli/python/knowledge_ingest.js.map +0 -1
- package/dist/cli/python/workspace_analytics.js.map +0 -1
- package/dist/cli/python/workspace_crypto.js.map +0 -1
- package/dist/cli/python/workspace_documents.js.map +0 -1
- package/dist/cli/python/workspace_drive.js.map +0 -1
- package/dist/cli/python/workspace_drive_health.js.map +0 -1
- package/dist/cli/python/workspace_explain.js.map +0 -1
- package/dist/cli/python/workspace_hosts.js.map +0 -1
- package/dist/cli/python/workspace_inbox.js.map +0 -1
- package/dist/cli/python/workspace_render.js.map +0 -1
- package/dist/cli/python/workspace_roles.js.map +0 -1
- package/dist/cli/python/workspace_secrets.js.map +0 -1
- package/dist/cli/python/workspace_sessions.js.map +0 -1
- package/dist/cli/python/workspace_skills.js.map +0 -1
- package/dist/cli/registry.js.map +0 -1
- package/dist/install/agentSwitchDetection.js.map +0 -1
- package/dist/install/agentSwitchProfile.js.map +0 -1
- package/dist/install/atomic.js.map +0 -1
- package/dist/install/claudePathsPlan.js.map +0 -1
- package/dist/install/conflict.js.map +0 -1
- package/dist/install/detect.js.map +0 -1
- package/dist/install/emit_host_rules_cli.js.map +0 -1
- package/dist/install/globalRuleLayers.js.map +0 -1
- package/dist/install/hostLayerFingerprint.js.map +0 -1
- package/dist/install/partitionEligibility.js.map +0 -1
- package/dist/install/paths.js.map +0 -1
- package/dist/install/plan.js.map +0 -1
- package/dist/install/preflight.js.map +0 -1
- package/dist/install/rtkDetection.js.map +0 -1
- package/dist/install/ruleInScope.js.map +0 -1
- package/dist/install/ruleLayerPartition.js.map +0 -1
- package/dist/install/rule_scope.js.map +0 -1
- package/dist/install/rule_scope_cli.js.map +0 -1
- package/dist/install/selectedTools.js.map +0 -1
- package/dist/install/toolDetection.js.map +0 -1
- package/dist/install/txlog.js.map +0 -1
- package/dist/install/types.js.map +0 -1
- package/dist/install/wizard-plan.js.map +0 -1
- package/dist/install/wizardDismissals.js.map +0 -1
- package/dist/scripts/_lib/agent_settings.js.map +0 -1
- package/dist/scripts/_lib/agent_src.js.map +0 -1
- package/dist/scripts/_lib/claude_builtin_names.js.map +0 -1
- package/dist/scripts/_lib/claude_plugin.js.map +0 -1
- package/dist/scripts/_lib/claude_settings_hooks.js.map +0 -1
- package/dist/scripts/_lib/compile_time_toggles.js.map +0 -1
- package/dist/scripts/_lib/environment_detector.js.map +0 -1
- package/dist/scripts/_lib/host_launch.js.map +0 -1
- package/dist/scripts/_lib/install_layout.js.map +0 -1
- package/dist/scripts/_lib/installed_lock.js.map +0 -1
- package/dist/scripts/_lib/model_tier.js.map +0 -1
- package/dist/scripts/_lib/prune_empty_dirs.js.map +0 -1
- package/dist/scripts/_lib/scoped_projection.js.map +0 -1
- package/dist/scripts/_lib/script_output.js.map +0 -1
- package/dist/scripts/_lib/spawn_env.js.map +0 -1
- package/dist/scripts/_lib/surface_tiers.js.map +0 -1
- package/dist/scripts/_lib/token_count.js.map +0 -1
- package/dist/scripts/_lib/trigger_eval_floors.js.map +0 -1
- package/dist/scripts/_lib/user_global_paths.js.map +0 -1
- package/dist/scripts/ai_council/modes.js.map +0 -1
- package/dist/scripts/ai_council/transport_resolver.js.map +0 -1
- package/dist/scripts/condense.js.map +0 -1
- package/dist/scripts/project_thin_rules.js.map +0 -1
- package/dist/server/app.js.map +0 -1
- package/dist/server/io/atomicMultiWrite.js.map +0 -1
- package/dist/server/io/atomicWrite.js.map +0 -1
- package/dist/server/io/sharedWriteCheck.js.map +0 -1
- package/dist/server/io/substituteTemplate.js.map +0 -1
- package/dist/server/io/yamlIO.js.map +0 -1
- package/dist/server/nicknameResolver.js.map +0 -1
- package/dist/server/port.js.map +0 -1
- package/dist/server/routes/discovery.js.map +0 -1
- package/dist/server/routes/install.js.map +0 -1
- package/dist/server/routes/ping.js.map +0 -1
- package/dist/server/routes/schema.js.map +0 -1
- package/dist/server/routes/settings.js.map +0 -1
- package/dist/server/routes/settingsChanges.js.map +0 -1
- package/dist/server/routes/userMd.js.map +0 -1
- package/dist/server/routes/wizard.js.map +0 -1
- package/dist/server/routes/workspace.js.map +0 -1
- package/dist/server/schemas/settings.js.map +0 -1
- package/dist/server/serverInfo.js.map +0 -1
- package/dist/server/token.js.map +0 -1
- package/dist/server/writeRoot.js.map +0 -1
- package/dist/shared/capabilities.js.map +0 -1
- package/dist/shared/interactiveContext.js.map +0 -1
- package/dist/shared/nicknamePrefill.js.map +0 -1
- package/dist/shared/settingsAsks.js.map +0 -1
- package/dist/shared/settingsCarveOut.js.map +0 -1
- package/dist/shared/settingsClasses.js.map +0 -1
- package/dist/shared/settingsConsent.js.map +0 -1
- package/dist/shared/settingsSurface.js.map +0 -1
- package/dist/shared/userMd/formAdapter.js.map +0 -1
- package/dist/shared/userMd/schema.js.map +0 -1
- package/dist/shared/userMd/utils.js.map +0 -1
- package/dist/ui/assets/index-Cxgy63kz.js.map +0 -1
- package/src/scripts/_lib/adr_frontmatter.test.ts +0 -365
- package/src/scripts/_lib/adversarial_council_gate.test.ts +0 -112
- package/src/scripts/_lib/adversarial_reconcile.test.ts +0 -197
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +0 -150
- package/src/scripts/_lib/secret_detector.test.ts +0 -108
- package/src/scripts/_lib/secret_detector_adversarial.test.ts +0 -116
- package/src/scripts/_lib/untrusted_content.test.ts +0 -175
- package/src/scripts/check_secret_leak.test.ts +0 -90
- package/src/scripts/design_slop_rules.test.ts +0 -239
- package/src/scripts/self_review_gate.test.ts +0 -296
- package/src/shared/capabilities.test.ts +0 -43
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `asOf()` — the one place a gate is allowed to ask what time it is.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* ---------------
|
|
6
|
+
* A gate's verdict should be a function of the tree, not of the hour it ran.
|
|
7
|
+
* Measured on 2026-08-23 (`grep -lE 'Date\.now\(\)|new Date\(\)'
|
|
8
|
+
* src/scripts/check_*.ts src/scripts/lint_*.ts`): **17** gate scripts read the
|
|
9
|
+
* wall clock directly, and no `--as-of` / `AC_AS_OF` surface existed anywhere in
|
|
10
|
+
* `src/scripts/`. The consequence is not theoretical — every one of those
|
|
11
|
+
* verdicts is a staleness or age judgement, so a green on a reviewer's machine
|
|
12
|
+
* on Monday is a different verdict from the same tree on Friday, and neither is
|
|
13
|
+
* reproducible from the commit alone.
|
|
14
|
+
*
|
|
15
|
+
* THE RESOLUTION ORDER
|
|
16
|
+
* --------------------
|
|
17
|
+
* First rung that answers wins:
|
|
18
|
+
*
|
|
19
|
+
* 1. `--as-of <iso>` on argv (also `--as-of=<iso>`) — an explicit pin. This is
|
|
20
|
+
* what CI passes, and what a reviewer passes to reproduce a CI verdict.
|
|
21
|
+
* 2. `AC_AS_OF` in the environment — the same pin where argv is not reachable
|
|
22
|
+
* (a gate invoked through a wrapper, a hook, or a task runner).
|
|
23
|
+
* 3. The commit date of the tree under test, when `CI` is set. Committed,
|
|
24
|
+
* therefore reproducible; and the change under review cannot rewrite it
|
|
25
|
+
* without changing the commit.
|
|
26
|
+
* 4. The wall clock, with a one-line WARN on stderr naming the run as
|
|
27
|
+
* non-reproducible. The fallback STAYS so no gate loses its ability to run
|
|
28
|
+
* outside CI; it just stops being silent about what it did.
|
|
29
|
+
*
|
|
30
|
+
* WHY RUNG 3 IS THE **COMMIT** DATE AND NOT THE MERGE-BASE DATE
|
|
31
|
+
* ------------------------------------------------------------
|
|
32
|
+
* The roadmap that commissioned this seam
|
|
33
|
+
* (`agents/roadmaps/road-to-deterministic-time-in-gates.md`, step 1.1) specifies
|
|
34
|
+
* "the merge-base commit date when running in CI". Implementing that literally
|
|
35
|
+
* would **weaken every gate it touches**, which is why it is not implemented
|
|
36
|
+
* literally and the deviation is recorded here rather than in a commit message
|
|
37
|
+
* nobody reads.
|
|
38
|
+
*
|
|
39
|
+
* The merge-base date is, by construction, `<=` the HEAD commit date: it is
|
|
40
|
+
* where the branch forked from its base. All 17 callers are *age* gates — "this
|
|
41
|
+
* pin is stale", "this one-off has expired", "this review marker is overdue" —
|
|
42
|
+
* so an EARLIER "now" makes every one of them strictly more permissive. Pinning
|
|
43
|
+
* to the merge-base therefore hands a long-lived branch a free extension on
|
|
44
|
+
* every staleness budget in the tree, silently, proportional to the branch's
|
|
45
|
+
* age. That is a gate weakening bought with a determinism improvement, and the
|
|
46
|
+
* determinism improvement does not require it.
|
|
47
|
+
*
|
|
48
|
+
* The HEAD commit date has the same reproducibility property — it is committed,
|
|
49
|
+
* a reviewer can recover it from the commit, and two runs of the same commit
|
|
50
|
+
* agree — while being the *tightest* committed clock available. It is never
|
|
51
|
+
* later than real time and never earlier than the merge-base. So it is what
|
|
52
|
+
* rung 3 reads.
|
|
53
|
+
*
|
|
54
|
+
* WHAT THIS MODULE DELIBERATELY DOES NOT DO
|
|
55
|
+
* -----------------------------------------
|
|
56
|
+
* It does not change a single threshold, message, or exit code. Substituting
|
|
57
|
+
* `asOf()` for `new Date()` is mechanical by design (roadmap step 1.2): a gate
|
|
58
|
+
* that flips its verdict on the substitution has found a real dependency on the
|
|
59
|
+
* hour, which is the defect being fixed, not a regression introduced by it.
|
|
60
|
+
*/
|
|
61
|
+
import { spawnSync } from 'node:child_process';
|
|
62
|
+
import * as fs from 'node:fs';
|
|
63
|
+
import * as path from 'node:path';
|
|
64
|
+
import process from 'node:process';
|
|
65
|
+
import { fileURLToPath } from 'node:url';
|
|
66
|
+
|
|
67
|
+
/** Which rung of the resolution order answered. */
|
|
68
|
+
export type AsOfRung = 'argv' | 'env' | 'commit' | 'wall-clock';
|
|
69
|
+
|
|
70
|
+
export interface AsOfResolution {
|
|
71
|
+
/** The resolved "now". */
|
|
72
|
+
at: Date;
|
|
73
|
+
/** Which rung produced it. */
|
|
74
|
+
rung: AsOfRung;
|
|
75
|
+
/**
|
|
76
|
+
* `true` when the same tree would resolve the same instant again. Only the
|
|
77
|
+
* wall-clock rung is `false`.
|
|
78
|
+
*/
|
|
79
|
+
reproducible: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Injection seam — every source the resolver reads, so the self-test can drive it. */
|
|
83
|
+
export interface AsOfSources {
|
|
84
|
+
argv?: readonly string[];
|
|
85
|
+
env?: Record<string, string | undefined>;
|
|
86
|
+
/** Committed clock for rung 3. `null` when it cannot be established. */
|
|
87
|
+
commitDate?: () => Date | null;
|
|
88
|
+
/** Rung 4. */
|
|
89
|
+
wallClock?: () => Date;
|
|
90
|
+
/** Where the non-reproducibility WARN goes. */
|
|
91
|
+
warn?: (line: string) => void;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Thrown when an explicit pin is present but unparseable.
|
|
96
|
+
*
|
|
97
|
+
* A malformed pin is never silently downgraded to the next rung: the caller
|
|
98
|
+
* asked for a specific instant, and quietly substituting a different one is the
|
|
99
|
+
* failure mode this whole module exists to remove.
|
|
100
|
+
*/
|
|
101
|
+
export class AsOfPinError extends Error {}
|
|
102
|
+
|
|
103
|
+
const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
|
104
|
+
|
|
105
|
+
/** `--as-of <iso>` / `--as-of=<iso>`; `undefined` when the flag is absent. */
|
|
106
|
+
function readArgvPin(argv: readonly string[]): string | undefined {
|
|
107
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
108
|
+
const arg = argv[i] ?? '';
|
|
109
|
+
if (arg === '--as-of') return argv[i + 1] ?? '';
|
|
110
|
+
if (arg.startsWith('--as-of=')) return arg.slice('--as-of='.length);
|
|
111
|
+
}
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parsePin(raw: string, source: string): Date {
|
|
116
|
+
const trimmed = raw.trim();
|
|
117
|
+
const ms = Date.parse(trimmed);
|
|
118
|
+
if (trimmed === '' || Number.isNaN(ms)) {
|
|
119
|
+
throw new AsOfPinError(
|
|
120
|
+
`${source} is not a parseable date: ${JSON.stringify(raw)}. ` +
|
|
121
|
+
'Pass an ISO-8601 instant, e.g. 2026-08-23T00:00:00Z.',
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
return new Date(ms);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** `true` for a set, non-falsey `CI` value. GitHub Actions sets `CI=true`. */
|
|
128
|
+
function inCi(env: Record<string, string | undefined>): boolean {
|
|
129
|
+
const raw = (env.CI ?? '').trim().toLowerCase();
|
|
130
|
+
return raw !== '' && raw !== '0' && raw !== 'false';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Commit date of `HEAD` as an ISO instant, or `null` when git cannot answer. */
|
|
134
|
+
export function headCommitDate(cwd: string = REPO_ROOT): Date | null {
|
|
135
|
+
const res = spawnSync('git', ['log', '-1', '--format=%cI', 'HEAD'], {
|
|
136
|
+
cwd,
|
|
137
|
+
encoding: 'utf-8',
|
|
138
|
+
});
|
|
139
|
+
if (res.status !== 0) return null;
|
|
140
|
+
const iso = (res.stdout ?? '').trim();
|
|
141
|
+
if (iso === '') return null;
|
|
142
|
+
const ms = Date.parse(iso);
|
|
143
|
+
return Number.isNaN(ms) ? null : new Date(ms);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Resolve "now" from the four rungs. Pure with respect to {@link AsOfSources},
|
|
148
|
+
* so the self-test drives every rung without touching the real environment.
|
|
149
|
+
*
|
|
150
|
+
* @throws {AsOfPinError} when an explicit pin is present but unparseable.
|
|
151
|
+
*/
|
|
152
|
+
export function resolveAsOf(sources: AsOfSources = {}): AsOfResolution {
|
|
153
|
+
const argv = sources.argv ?? process.argv.slice(2);
|
|
154
|
+
const env = sources.env ?? process.env;
|
|
155
|
+
|
|
156
|
+
const argvPin = readArgvPin(argv);
|
|
157
|
+
if (argvPin !== undefined) {
|
|
158
|
+
return { at: parsePin(argvPin, '--as-of'), rung: 'argv', reproducible: true };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const envPin = env.AC_AS_OF;
|
|
162
|
+
if (envPin !== undefined && envPin.trim() !== '') {
|
|
163
|
+
return { at: parsePin(envPin, 'AC_AS_OF'), rung: 'env', reproducible: true };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (inCi(env)) {
|
|
167
|
+
const commit = (sources.commitDate ?? headCommitDate)();
|
|
168
|
+
if (commit !== null) {
|
|
169
|
+
return { at: commit, rung: 'commit', reproducible: true };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const warn = sources.warn ?? ((line: string) => process.stderr.write(line));
|
|
174
|
+
const at = (sources.wallClock ?? (() => new Date()))();
|
|
175
|
+
warn(
|
|
176
|
+
`⚠️ as-of: unpinned run — using the wall clock (${at.toISOString()}); ` +
|
|
177
|
+
'this verdict is not reproducible. Pass --as-of <iso> or set AC_AS_OF to pin it.\n',
|
|
178
|
+
);
|
|
179
|
+
return { at, rung: 'wall-clock', reproducible: false };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
let cached: AsOfResolution | null = null;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* The resolution for this process, computed once. Memoised so 17 gate scripts
|
|
186
|
+
* sharing one process cannot disagree about "now", and so the wall-clock WARN
|
|
187
|
+
* is emitted at most once per run.
|
|
188
|
+
*/
|
|
189
|
+
export function asOfResolution(): AsOfResolution {
|
|
190
|
+
cached ??= resolveAsOf();
|
|
191
|
+
return cached;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* "Now", for a gate. The one sanctioned reader of the wall clock in
|
|
196
|
+
* `src/scripts/`.
|
|
197
|
+
*
|
|
198
|
+
* Returns a fresh `Date` each call so a caller mutating it cannot poison the
|
|
199
|
+
* memoised value.
|
|
200
|
+
*/
|
|
201
|
+
export function asOf(): Date {
|
|
202
|
+
return new Date(asOfResolution().at.getTime());
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** `Date.now()` equivalent, for the arithmetic call sites. */
|
|
206
|
+
export function asOfMs(): number {
|
|
207
|
+
return asOfResolution().at.getTime();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* One line a gate prints so a reviewer can see WHICH "now" produced the verdict.
|
|
212
|
+
*
|
|
213
|
+
* Without it, pinning is invisible: a reproducible run and a wall-clock run look
|
|
214
|
+
* identical in CI output, and "reproduce it with --as-of" is advice nobody can
|
|
215
|
+
* act on because the value to pass was never published.
|
|
216
|
+
*
|
|
217
|
+
* @param override an explicit date the gate's own flag supplied (e.g. `--today`),
|
|
218
|
+
* which outranks the seam and must be reported as such.
|
|
219
|
+
*/
|
|
220
|
+
export function asOfBanner(override?: string): string {
|
|
221
|
+
if (override !== undefined) return `as-of: ${override} (source=flag)`;
|
|
222
|
+
const r = asOfResolution();
|
|
223
|
+
return `as-of: ${r.at.toISOString()} (rung=${r.rung}, reproducible=${String(r.reproducible)})`;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Test seam — drops the memo so a self-test can exercise more than one rung. */
|
|
227
|
+
export function _resetAsOfCacheForTests(): void {
|
|
228
|
+
cached = null;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/* ------------------------------------------------------------------ *
|
|
232
|
+
* CLI
|
|
233
|
+
* ------------------------------------------------------------------ */
|
|
234
|
+
|
|
235
|
+
function tsxBin(): string {
|
|
236
|
+
return path.join(
|
|
237
|
+
REPO_ROOT,
|
|
238
|
+
'node_modules',
|
|
239
|
+
'.bin',
|
|
240
|
+
process.platform === 'win32' ? 'tsx.cmd' : 'tsx',
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
interface ChildRun {
|
|
245
|
+
status: number;
|
|
246
|
+
stdout: string;
|
|
247
|
+
stderr: string;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** Invoke THIS module's own CLI, so the self-test exercises the shipped path. */
|
|
251
|
+
function runSelf(args: readonly string[], env: Record<string, string | undefined>): ChildRun {
|
|
252
|
+
const res = spawnSync(tsxBin(), [fileURLToPath(import.meta.url), ...args], {
|
|
253
|
+
cwd: REPO_ROOT,
|
|
254
|
+
encoding: 'utf-8',
|
|
255
|
+
env: env as NodeJS.ProcessEnv,
|
|
256
|
+
});
|
|
257
|
+
return {
|
|
258
|
+
status: res.status ?? 1,
|
|
259
|
+
stdout: res.stdout ?? '',
|
|
260
|
+
stderr: res.stderr ?? '',
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/** Environment with every pin and CI marker cleared, so a case sets only its own. */
|
|
265
|
+
function bareEnv(): Record<string, string | undefined> {
|
|
266
|
+
const env: Record<string, string | undefined> = { ...process.env };
|
|
267
|
+
delete env.AC_AS_OF;
|
|
268
|
+
delete env.CI;
|
|
269
|
+
delete env.GITHUB_ACTIONS;
|
|
270
|
+
return env;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function selfTest(write: (chunk: string) => unknown): number {
|
|
274
|
+
const commit = headCommitDate();
|
|
275
|
+
let failed = 0;
|
|
276
|
+
let checks = 0;
|
|
277
|
+
|
|
278
|
+
const expect = (name: string, ok: boolean, detail: string): void => {
|
|
279
|
+
checks += 1;
|
|
280
|
+
if (ok) {
|
|
281
|
+
write(`✅ ${name}\n`);
|
|
282
|
+
} else {
|
|
283
|
+
write(`❌ ${name}: ${detail}\n`);
|
|
284
|
+
failed += 1;
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
// Rung 1 — argv.
|
|
289
|
+
const argvRun = runSelf(['--print', '--as-of', '2026-01-02T03:04:05Z'], bareEnv());
|
|
290
|
+
expect(
|
|
291
|
+
'rung 1 (argv) — an explicit --as-of wins',
|
|
292
|
+
argvRun.status === 0 &&
|
|
293
|
+
argvRun.stdout.includes('rung=argv') &&
|
|
294
|
+
argvRun.stdout.includes('at=2026-01-02T03:04:05.000Z'),
|
|
295
|
+
`exit ${String(argvRun.status)}, stdout ${JSON.stringify(argvRun.stdout)}`,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
// Rung 1 — argv beats env, so a pin the caller typed is never shadowed.
|
|
299
|
+
const precedence = runSelf(['--print', '--as-of=2026-01-02T03:04:05Z'], {
|
|
300
|
+
...bareEnv(),
|
|
301
|
+
AC_AS_OF: '2020-01-01T00:00:00Z',
|
|
302
|
+
});
|
|
303
|
+
expect(
|
|
304
|
+
'rung 1 precedence — --as-of= overrides AC_AS_OF',
|
|
305
|
+
precedence.status === 0 &&
|
|
306
|
+
precedence.stdout.includes('rung=argv') &&
|
|
307
|
+
precedence.stdout.includes('at=2026-01-02T03:04:05.000Z'),
|
|
308
|
+
`exit ${String(precedence.status)}, stdout ${JSON.stringify(precedence.stdout)}`,
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
// Rung 2 — env.
|
|
312
|
+
const envRun = runSelf(['--print'], { ...bareEnv(), AC_AS_OF: '2025-06-07T08:09:10Z' });
|
|
313
|
+
expect(
|
|
314
|
+
'rung 2 (env) — AC_AS_OF answers when argv is silent',
|
|
315
|
+
envRun.status === 0 &&
|
|
316
|
+
envRun.stdout.includes('rung=env') &&
|
|
317
|
+
envRun.stdout.includes('at=2025-06-07T08:09:10.000Z'),
|
|
318
|
+
`exit ${String(envRun.status)}, stdout ${JSON.stringify(envRun.stdout)}`,
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
// Rung 3 — the committed clock, under CI.
|
|
322
|
+
const ciRun = runSelf(['--print'], { ...bareEnv(), CI: 'true' });
|
|
323
|
+
expect(
|
|
324
|
+
'rung 3 (commit) — CI pins to the tree under test',
|
|
325
|
+
ciRun.status === 0 &&
|
|
326
|
+
ciRun.stdout.includes('rung=commit') &&
|
|
327
|
+
commit !== null &&
|
|
328
|
+
ciRun.stdout.includes(`at=${commit.toISOString()}`),
|
|
329
|
+
`exit ${String(ciRun.status)}, stdout ${JSON.stringify(ciRun.stdout)}, ` +
|
|
330
|
+
`HEAD date ${commit === null ? 'UNAVAILABLE' : commit.toISOString()}`,
|
|
331
|
+
);
|
|
332
|
+
|
|
333
|
+
// Rung 4 — the wall clock, and it must say so.
|
|
334
|
+
const wallRun = runSelf(['--print'], bareEnv());
|
|
335
|
+
expect(
|
|
336
|
+
'rung 4 (wall-clock) — unpinned runs resolve and WARN once',
|
|
337
|
+
wallRun.status === 0 &&
|
|
338
|
+
wallRun.stdout.includes('rung=wall-clock') &&
|
|
339
|
+
wallRun.stdout.includes('reproducible=false') &&
|
|
340
|
+
wallRun.stderr.includes('not reproducible'),
|
|
341
|
+
`exit ${String(wallRun.status)}, stdout ${JSON.stringify(wallRun.stdout)}, ` +
|
|
342
|
+
`stderr ${JSON.stringify(wallRun.stderr)}`,
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
// A malformed pin is a rejection, never a silent downgrade.
|
|
346
|
+
const badArgv = runSelf(['--print', '--as-of', 'not-a-date'], bareEnv());
|
|
347
|
+
expect(
|
|
348
|
+
'malformed --as-of is rejected, not downgraded',
|
|
349
|
+
badArgv.status !== 0 && badArgv.stderr.includes('not a parseable date'),
|
|
350
|
+
`exit ${String(badArgv.status)}, stderr ${JSON.stringify(badArgv.stderr)}`,
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
const badEnv = runSelf(['--print'], { ...bareEnv(), AC_AS_OF: '2026-13-45' });
|
|
354
|
+
expect(
|
|
355
|
+
'malformed AC_AS_OF is rejected, not downgraded',
|
|
356
|
+
badEnv.status !== 0 && badEnv.stderr.includes('not a parseable date'),
|
|
357
|
+
`exit ${String(badEnv.status)}, stderr ${JSON.stringify(badEnv.stderr)}`,
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
// A truncated suite must fail rather than print success — deleting cases is
|
|
361
|
+
// otherwise the cheapest route to a green self-test.
|
|
362
|
+
const FLOOR = 7;
|
|
363
|
+
if (checks < FLOOR) {
|
|
364
|
+
write(
|
|
365
|
+
`❌ as_of --self-test: ${String(checks)} case(s) is below the declared ` +
|
|
366
|
+
`floor of ${String(FLOOR)}.\n`,
|
|
367
|
+
);
|
|
368
|
+
return 1;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
write(
|
|
372
|
+
`\nas_of --self-test: ${String(checks - failed)}/${String(checks)} case(s) behaved ` +
|
|
373
|
+
`(floor ${String(FLOOR)})\n`,
|
|
374
|
+
);
|
|
375
|
+
return failed > 0 ? 1 : 0;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function main(argv: readonly string[]): number {
|
|
379
|
+
if (argv.includes('--self-test')) {
|
|
380
|
+
return selfTest((chunk) => process.stdout.write(chunk));
|
|
381
|
+
}
|
|
382
|
+
let res: AsOfResolution;
|
|
383
|
+
try {
|
|
384
|
+
res = resolveAsOf({ argv });
|
|
385
|
+
} catch (e) {
|
|
386
|
+
if (e instanceof AsOfPinError) {
|
|
387
|
+
process.stderr.write(`❌ as-of: ${e.message}\n`);
|
|
388
|
+
return 2;
|
|
389
|
+
}
|
|
390
|
+
throw e;
|
|
391
|
+
}
|
|
392
|
+
process.stdout.write(
|
|
393
|
+
`rung=${res.rung} at=${res.at.toISOString()} reproducible=${String(res.reproducible)}\n`,
|
|
394
|
+
);
|
|
395
|
+
return 0;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function isCliEntry(): boolean {
|
|
399
|
+
if (process.argv[1] === undefined) return false;
|
|
400
|
+
try {
|
|
401
|
+
return (
|
|
402
|
+
fs.realpathSync(fileURLToPath(import.meta.url)) ===
|
|
403
|
+
fs.realpathSync(path.resolve(process.argv[1]))
|
|
404
|
+
);
|
|
405
|
+
} catch {
|
|
406
|
+
return false;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
if (isCliEntry()) {
|
|
411
|
+
process.exit(main(process.argv.slice(2)));
|
|
412
|
+
}
|
|
@@ -53,6 +53,13 @@ export const ALLOWLIST_PREFIXES: readonly (readonly string[])[] = [
|
|
|
53
53
|
// under `--check`; same deterministic, model-free class as its neighbours.
|
|
54
54
|
['update_counts'],
|
|
55
55
|
['check_enforcement_coverage'],
|
|
56
|
+
// The denominator half of the same property. `check_enforcement_coverage`
|
|
57
|
+
// PUBLISHES the number; this one refuses a second, hand-written copy of it
|
|
58
|
+
// in a published doc — so its exit code IS the verdict for
|
|
59
|
+
// `claim:enforcement-undeclared-denominator`, not merely evidence that some
|
|
60
|
+
// report exists. Read-only, deterministic, model-free, and already run in
|
|
61
|
+
// CI (`rule-backstops.yml`) for its own reasons.
|
|
62
|
+
['check_enforcement_denominator'],
|
|
56
63
|
['check_token_regression'],
|
|
57
64
|
['lint_agent_security'],
|
|
58
65
|
['measure_lexical_ranking'],
|
|
@@ -32,8 +32,10 @@
|
|
|
32
32
|
* Dates are plain `YYYY-MM-DD`; ages are whole days in UTC, so the verdict does
|
|
33
33
|
* not depend on the machine's timezone.
|
|
34
34
|
*/
|
|
35
|
+
import { execFileSync } from 'node:child_process';
|
|
35
36
|
import * as fs from 'node:fs';
|
|
36
37
|
import * as path from 'node:path';
|
|
38
|
+
import process from 'node:process';
|
|
37
39
|
|
|
38
40
|
/** Days a baseline may sit unchanged before it must be fixed or reaffirmed. */
|
|
39
41
|
export const STALE_AFTER_DAYS = 56;
|
|
@@ -105,12 +107,132 @@ export function loadBaselines(repoRoot: string, relPath: string = BASELINE_REL):
|
|
|
105
107
|
} catch {
|
|
106
108
|
return { gates: {} };
|
|
107
109
|
}
|
|
110
|
+
return parseBaselines(raw);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function parseBaselines(raw: string): BaselineFile {
|
|
108
114
|
const parsed = JSON.parse(raw) as Partial<BaselineFile>;
|
|
109
115
|
const out: BaselineFile = { gates: parsed.gates ?? {} };
|
|
110
116
|
if (parsed.$comment !== undefined) out.$comment = parsed.$comment;
|
|
111
117
|
return out;
|
|
112
118
|
}
|
|
113
119
|
|
|
120
|
+
/**
|
|
121
|
+
* A baseline-resolution failure, which is a HARD error and never a fallback.
|
|
122
|
+
*
|
|
123
|
+
* `loadBaselines` returns an empty ratchet when the file is missing, and that
|
|
124
|
+
* is correct for a working-tree read: a repository with no baseline file has no
|
|
125
|
+
* ratchet. It is NOT correct for a target-commit read. There, a failure to
|
|
126
|
+
* resolve the ref or the blob means the governing policy could not be
|
|
127
|
+
* determined — and silently falling back to the working tree would let an
|
|
128
|
+
* infrastructure error change which policy applies, invisibly. The AI council
|
|
129
|
+
* was unanimous on this point (2026-08-25) and stated it applies to whichever
|
|
130
|
+
* invariant won.
|
|
131
|
+
*/
|
|
132
|
+
export class BaselineResolutionError extends Error {
|
|
133
|
+
constructor(message: string) {
|
|
134
|
+
super(message);
|
|
135
|
+
this.name = 'BaselineResolutionError';
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Read the baseline as it stands at a specific commit.
|
|
141
|
+
*
|
|
142
|
+
* `road-to-merge-surface-zero` Phase 3.1, under the **ABSOLUTE invariant** the
|
|
143
|
+
* AI council picked 2/2 on 2026-08-25 to resolve blocker B5.
|
|
144
|
+
*
|
|
145
|
+
* ## Why the target commit and not the merge base
|
|
146
|
+
*
|
|
147
|
+
* The merge-base read was the roadmap's original proposal and it selects the
|
|
148
|
+
* *contribution* invariant: a PR passes if it did not worsen the state it
|
|
149
|
+
* branched from. The worked example that killed it uses numbers this repository
|
|
150
|
+
* actually carries — `main` tightens `ci-parity:local-only` 165 → 160, a PR that
|
|
151
|
+
* branched earlier measures 163, the merge-base read returns 165, the PR
|
|
152
|
+
* **passes**, and after it merges `main` measures 163 against a baseline of 160.
|
|
153
|
+
* **A tightening is undone by a PR that never touched the baseline file and
|
|
154
|
+
* never saw a red.**
|
|
155
|
+
*
|
|
156
|
+
* The remedy of checking both was refused on a correctness ground rather than a
|
|
157
|
+
* preference: **violation counts are not necessarily compositional.** Conflict
|
|
158
|
+
* resolution, file movement, generated outputs and interactions with changes on
|
|
159
|
+
* `main` can make a prospective merge regress even when the isolated PR delta is
|
|
160
|
+
* non-positive. If trunk health is the invariant, the merge RESULT is what has
|
|
161
|
+
* to be measured.
|
|
162
|
+
*
|
|
163
|
+
* ## Why the TARGET commit specifically, and not the merge tree
|
|
164
|
+
*
|
|
165
|
+
* The count is measured on the prospective merge tree; the **policy** is read
|
|
166
|
+
* from the target. Reading the baseline out of the merge tree would let a PR
|
|
167
|
+
* loosen the very number it is being judged against, in the same diff.
|
|
168
|
+
*
|
|
169
|
+
* @throws BaselineResolutionError when the ref or the blob cannot be resolved.
|
|
170
|
+
*/
|
|
171
|
+
export function loadBaselinesAt(
|
|
172
|
+
repoRoot: string,
|
|
173
|
+
targetRef: string,
|
|
174
|
+
relPath: string = BASELINE_REL,
|
|
175
|
+
run: (args: string[]) => string = (args) =>
|
|
176
|
+
execFileSync('git', args, { cwd: repoRoot, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }),
|
|
177
|
+
): BaselineFile {
|
|
178
|
+
let sha: string;
|
|
179
|
+
try {
|
|
180
|
+
sha = run(['rev-parse', '--verify', `${targetRef}^{commit}`]).trim();
|
|
181
|
+
} catch {
|
|
182
|
+
throw new BaselineResolutionError(
|
|
183
|
+
`cannot resolve target ref "${targetRef}" — the governing baseline is unknown, ` +
|
|
184
|
+
'and a working-tree fallback would let an infrastructure error decide the policy.',
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
let raw: string;
|
|
188
|
+
try {
|
|
189
|
+
raw = run(['show', `${sha}:${relPath}`]);
|
|
190
|
+
} catch {
|
|
191
|
+
throw new BaselineResolutionError(
|
|
192
|
+
`${relPath} does not exist at ${sha.slice(0, 9)} ("${targetRef}") — this is a ` +
|
|
193
|
+
'resolution failure, NOT an empty ratchet. A missing blob at a named commit ' +
|
|
194
|
+
'means the read was wrong, not that the ratchet is empty.',
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
return parseBaselines(raw);
|
|
199
|
+
} catch {
|
|
200
|
+
throw new BaselineResolutionError(
|
|
201
|
+
`${relPath} at ${sha.slice(0, 9)} is not parseable JSON — refusing to continue ` +
|
|
202
|
+
'with an unknown policy.',
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* The merge-base reading, kept as a DIAGNOSTIC and never as the verdict.
|
|
209
|
+
*
|
|
210
|
+
* Both council seats asked for this explicitly. It does not decide pass/fail; it
|
|
211
|
+
* distinguishes two failures a reader cannot otherwise tell apart:
|
|
212
|
+
*
|
|
213
|
+
* - `branch-regression` — the branch itself made the count worse. Fix the code.
|
|
214
|
+
* - `main-tightened` — the branch is unchanged and `main` improved underneath
|
|
215
|
+
* it. Rebase, then re-run.
|
|
216
|
+
*
|
|
217
|
+
* The distinction matters because the remediations differ, and one seat was
|
|
218
|
+
* pointed about the wording: "rebase and re-run" is incomplete advice for a PR
|
|
219
|
+
* that genuinely reintroduced violations, so a diagnostic must say which case it
|
|
220
|
+
* is rather than always suggesting a rebase.
|
|
221
|
+
*/
|
|
222
|
+
export type RegressionCause = 'branch-regression' | 'main-tightened' | 'none';
|
|
223
|
+
|
|
224
|
+
export function diagnoseRegression(opts: {
|
|
225
|
+
actual: number;
|
|
226
|
+
targetBaseline: number;
|
|
227
|
+
mergeBaseBaseline: number | null;
|
|
228
|
+
}): RegressionCause {
|
|
229
|
+
if (opts.actual <= opts.targetBaseline) return 'none';
|
|
230
|
+
if (opts.mergeBaseBaseline === null) return 'branch-regression';
|
|
231
|
+
// The branch is at or under the policy it branched from, and it still
|
|
232
|
+
// exceeds the target's policy: main tightened while this work was open.
|
|
233
|
+
return opts.actual <= opts.mergeBaseBaseline ? 'main-tightened' : 'branch-regression';
|
|
234
|
+
}
|
|
235
|
+
|
|
114
236
|
export interface RatchetOptions {
|
|
115
237
|
/** Gate name — the key in the baseline file. */
|
|
116
238
|
gate: string;
|
|
@@ -122,6 +244,12 @@ export interface RatchetOptions {
|
|
|
122
244
|
baselineRel?: string;
|
|
123
245
|
/** Override "now" as `YYYY-MM-DD` (tests); defaults to the current UTC date. */
|
|
124
246
|
today?: string;
|
|
247
|
+
/**
|
|
248
|
+
* Read the governing baseline from this commit instead of the working tree.
|
|
249
|
+
* Defaults to `process.env[TARGET_REF_ENV]`; absent means a local run with
|
|
250
|
+
* no merge to judge, which is not the same as a failed resolution.
|
|
251
|
+
*/
|
|
252
|
+
targetRef?: string;
|
|
125
253
|
}
|
|
126
254
|
|
|
127
255
|
/**
|
|
@@ -131,9 +259,30 @@ export interface RatchetOptions {
|
|
|
131
259
|
* does not print or exit. A gate with no entry behaves exactly as it did before
|
|
132
260
|
* the ratchet existed: any violation fails.
|
|
133
261
|
*/
|
|
262
|
+
/**
|
|
263
|
+
* The env var that switches every ratchet onto the ABSOLUTE invariant at once.
|
|
264
|
+
*
|
|
265
|
+
* Set it to the target ref (`origin/main`, or the base SHA a PR job already
|
|
266
|
+
* knows) and all 18 `checkRatchet` call sites read the governing baseline from
|
|
267
|
+
* that commit instead of the working tree — no per-gate migration, no chance of
|
|
268
|
+
* migrating seventeen and forgetting one.
|
|
269
|
+
*
|
|
270
|
+
* **Unset is not a fallback.** A local run with no target configured has no
|
|
271
|
+
* merge to judge, so the working-tree read is the right and only answer there.
|
|
272
|
+
* What the council forbade is different and is honoured: a target that IS
|
|
273
|
+
* configured and fails to resolve throws, rather than quietly reverting to the
|
|
274
|
+
* working tree and changing the governing policy on an infrastructure error.
|
|
275
|
+
*/
|
|
276
|
+
export const TARGET_REF_ENV = 'GATE_BASELINE_TARGET_REF';
|
|
277
|
+
|
|
134
278
|
export function checkRatchet(opts: RatchetOptions): BaselineVerdict {
|
|
135
279
|
const { gate, actual } = opts;
|
|
136
|
-
const
|
|
280
|
+
const rel = opts.baselineRel ?? BASELINE_REL;
|
|
281
|
+
const target = opts.targetRef ?? process.env[TARGET_REF_ENV];
|
|
282
|
+
const file =
|
|
283
|
+
target !== undefined && target !== ''
|
|
284
|
+
? loadBaselinesAt(opts.repoRoot, target, rel)
|
|
285
|
+
: loadBaselines(opts.repoRoot, rel);
|
|
137
286
|
const entry = file.gates[gate];
|
|
138
287
|
|
|
139
288
|
if (entry === undefined) {
|
|
@@ -77,3 +77,36 @@ export function hookSectionEnabled(root: string, section: string): boolean {
|
|
|
77
77
|
|
|
78
78
|
return false;
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Raw `lean_projection.mode` string out of `.agent-settings.yml`, or `''`.
|
|
83
|
+
*
|
|
84
|
+
* Same indentation-shaped discipline as `hookSectionEnabled` above and for the
|
|
85
|
+
* same reason — no YAML parser in a hook — but a top-level two-level key rather
|
|
86
|
+
* than a `hooks.<section>.enabled` flag. Interpretation is NOT done here:
|
|
87
|
+
* `_lib/lean_projection_mode.ts` owns it, so the projector and the concern
|
|
88
|
+
* cannot disagree about what the string means.
|
|
89
|
+
*/
|
|
90
|
+
export function leanProjectionModeRaw(root: string): string {
|
|
91
|
+
const file = path.join(root, SETTINGS_FILE);
|
|
92
|
+
let text: string;
|
|
93
|
+
try {
|
|
94
|
+
if (!fs.statSync(file).isFile()) return '';
|
|
95
|
+
text = fs.readFileSync(file, 'utf-8');
|
|
96
|
+
} catch {
|
|
97
|
+
return '';
|
|
98
|
+
}
|
|
99
|
+
let inSection = false;
|
|
100
|
+
for (const raw of text.split(/\r\n|\r|\n/)) {
|
|
101
|
+
const line = raw.replace(/\s+$/, '');
|
|
102
|
+
if (!line || line.replace(/^\s+/, '').startsWith('#')) continue;
|
|
103
|
+
if (!(line.startsWith(' ') || line.startsWith('\t'))) {
|
|
104
|
+
inSection = /^lean_projection\s*:\s*$/.test(line);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (!inSection) continue;
|
|
108
|
+
const m = /^\s+mode\s*:\s*(.+)$/.exec(line);
|
|
109
|
+
if (m) return (m[1] ?? '').trim().replace(/^["']|["']$/g, '');
|
|
110
|
+
}
|
|
111
|
+
return '';
|
|
112
|
+
}
|