@event4u/agent-config 14.22.0 → 15.0.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 +333 -356
- package/README.md +1 -1
- package/dist/agent-src/commands/agent-handoff.md +8 -7
- package/dist/agent-src/commands/agents/init.md +1 -1
- package/dist/agent-src/commands/agents/user/accept.md +4 -4
- package/dist/agent-src/commands/agents/user/delete.md +2 -2
- package/dist/agent-src/commands/agents/user/init.md +3 -3
- package/dist/agent-src/commands/agents/user/review.md +3 -3
- package/dist/agent-src/commands/agents/user/show.md +3 -3
- package/dist/agent-src/commands/agents/user/update.md +2 -2
- package/dist/agent-src/commands/agents/user.md +6 -6
- package/dist/agent-src/commands/analytics/prune.md +1 -1
- package/dist/agent-src/commands/analytics/show.md +2 -2
- package/dist/agent-src/commands/analyze/decision.md +2 -2
- package/dist/agent-src/commands/analyze/inbox.md +5 -5
- package/dist/agent-src/commands/analyze/incident.md +3 -3
- package/dist/agent-src/commands/analyze/near-miss.md +3 -3
- package/dist/agent-src/commands/analyze/postmortem.md +4 -4
- package/dist/agent-src/commands/analyze/premortem.md +2 -2
- package/dist/agent-src/commands/analyze.md +2 -2
- package/dist/agent-src/commands/bug/fix.md +1 -1
- package/dist/agent-src/commands/bug/investigate.md +2 -2
- package/dist/agent-src/commands/cost/profile.md +3 -3
- package/dist/agent-src/commands/design.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +1 -1
- package/dist/agent-src/commands/feature/plan.md +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +2 -2
- package/dist/agent-src/commands/ghostwriter/fetch.md +4 -4
- package/dist/agent-src/commands/ghostwriter/list.md +1 -1
- package/dist/agent-src/commands/ghostwriter/show.md +1 -1
- package/dist/agent-src/commands/ghostwriter/write.md +9 -9
- package/dist/agent-src/commands/humanize.md +6 -6
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/judge/solo.md +1 -1
- package/dist/agent-src/commands/judge.md +1 -1
- package/dist/agent-src/commands/knowledge/cross-repo.md +1 -1
- package/dist/agent-src/commands/knowledge/forget.md +1 -1
- package/dist/agent-src/commands/knowledge/ingest.md +2 -2
- package/dist/agent-src/commands/knowledge/list.md +1 -1
- package/dist/agent-src/commands/memory/add.md +3 -3
- package/dist/agent-src/commands/memory/load.md +4 -4
- package/dist/agent-src/commands/memory/mine-session.md +1 -1
- package/dist/agent-src/commands/memory/promote.md +4 -4
- package/dist/agent-src/commands/memory/propose.md +2 -2
- package/dist/agent-src/commands/mode.md +5 -5
- package/dist/agent-src/commands/post-as/ghostwriter.md +3 -3
- package/dist/agent-src/commands/post-as/me.md +7 -7
- package/dist/agent-src/commands/pr/create.md +1 -1
- package/dist/agent-src/commands/pr/merge.md +48 -39
- package/dist/agent-src/commands/profile/activate.md +1 -1
- package/dist/agent-src/commands/profile/show.md +1 -1
- package/dist/agent-src/commands/research/deep.md +2 -2
- package/dist/agent-src/commands/research/report.md +2 -2
- package/dist/agent-src/commands/review/changes.md +1 -1
- package/dist/agent-src/commands/review/routing.md +1 -1
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +2 -2
- package/dist/agent-src/commands/roadmap/next.md +9 -1
- package/dist/agent-src/commands/roadmap/process-full.md +2 -2
- package/dist/agent-src/commands/security-audit-config.md +3 -3
- package/dist/agent-src/commands/skill/preview.md +1 -1
- package/dist/agent-src/commands/skills/discover.md +1 -1
- package/dist/agent-src/commands/sync/agent-settings.md +2 -2
- package/dist/agent-src/commands/sync/gitignore/fix.md +1 -1
- package/dist/agent-src/commands/tests/create.md +1 -1
- package/dist/agent-src/commands/tests/e2e-heal.md +1 -1
- package/dist/agent-src/commands/tests/e2e-plan.md +1 -1
- package/dist/agent-src/contexts/authority/kernel-rule-edits.md +6 -6
- package/dist/agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +1 -2
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +2 -2
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +88 -1
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +1 -1
- package/dist/agent-src/contexts/contracts/frugality-charter.md +1 -1
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +1 -1
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +1 -1
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +13 -1
- package/dist/agent-src/contexts/execution/cheap-question-mechanics.md +1 -1
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +3 -3
- package/dist/agent-src/contexts/execution/mandated-lines.md +1 -1
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +1 -1
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +1 -1
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +5 -5
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +1 -1
- package/dist/agent-src/contexts/execution/subagent-modes-detail.md +1 -1
- package/dist/agent-src/contexts/execution/subagent-routing.md +2 -2
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +1 -1
- package/dist/agent-src/contexts/execution/user-memory-channels.md +3 -3
- package/dist/agent-src/contexts/execution/verification-mechanics.md +1 -1
- package/dist/agent-src/contexts/judges/persona-voice-rubric.md +2 -2
- package/dist/agent-src/guidelines/abstraction-thresholds.md +88 -0
- package/dist/agent-src/guidelines/agent-infra/5w2h-analysis.md +260 -0
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +99 -0
- package/dist/agent-src/guidelines/agent-infra/agent-interaction-and-decision-quality.md +325 -0
- package/dist/agent-src/guidelines/agent-infra/api-cost-levers.md +177 -0
- package/dist/agent-src/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +222 -0
- package/dist/agent-src/guidelines/agent-infra/ask-when-uncertain-demos.md +134 -0
- package/dist/agent-src/guidelines/agent-infra/asking-and-brevity-examples.md +198 -0
- package/dist/agent-src/guidelines/agent-infra/break-glass-usage.md +113 -0
- package/dist/agent-src/guidelines/agent-infra/carve-out-predicates.md +17 -0
- package/dist/agent-src/guidelines/agent-infra/comparison-matrix.md +179 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +165 -0
- package/dist/agent-src/guidelines/agent-infra/corpus-grounding-authoring.md +91 -0
- package/dist/agent-src/guidelines/agent-infra/critical-thinking.md +156 -0
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +109 -0
- package/dist/agent-src/guidelines/agent-infra/developer-judgment.md +82 -0
- package/dist/agent-src/guidelines/agent-infra/direct-answers-demos.md +142 -0
- package/dist/agent-src/guidelines/agent-infra/domain-adoption-gates.md +112 -0
- package/dist/agent-src/guidelines/agent-infra/domain-eval-anti-pattern.md +53 -0
- package/dist/agent-src/guidelines/agent-infra/domain-pack-architecture.md +56 -0
- package/dist/agent-src/guidelines/agent-infra/downstream-changes-mechanics.md +91 -0
- package/dist/agent-src/guidelines/agent-infra/emphasis-budget.md +45 -0
- package/dist/agent-src/guidelines/agent-infra/engineering-memory-data-format.md +210 -0
- package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +92 -0
- package/dist/agent-src/guidelines/agent-infra/existence-question-verdicts.md +80 -0
- package/dist/agent-src/guidelines/agent-infra/failure-signatures.md +70 -0
- package/dist/agent-src/guidelines/agent-infra/false-green.md +233 -0
- package/dist/agent-src/guidelines/agent-infra/first-principles.md +192 -0
- package/dist/agent-src/guidelines/agent-infra/framework-neutrality-patterns.md +66 -0
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +169 -0
- package/dist/agent-src/guidelines/agent-infra/gate-authoring.md +177 -0
- package/dist/agent-src/guidelines/agent-infra/guarded-baseline.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/installed-tools-manifest.md +142 -0
- package/dist/agent-src/guidelines/agent-infra/inversion-thinking.md +388 -0
- package/dist/agent-src/guidelines/agent-infra/ios-simulator-guide.md +378 -0
- package/dist/agent-src/guidelines/agent-infra/language-and-tone-examples.md +141 -0
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +317 -0
- package/dist/agent-src/guidelines/agent-infra/linked-projects-onboarding-gate.md +80 -0
- package/dist/agent-src/guidelines/agent-infra/mcp-request-signing.md +197 -0
- package/dist/agent-src/guidelines/agent-infra/memory-access.md +149 -0
- package/dist/agent-src/guidelines/agent-infra/mental-models.md +314 -0
- package/dist/agent-src/guidelines/agent-infra/minimal-safe-diff-mechanics.md +241 -0
- package/dist/agent-src/guidelines/agent-infra/missing-tool-handling.md +66 -0
- package/dist/agent-src/guidelines/agent-infra/model-recommendation.md +101 -0
- package/dist/agent-src/guidelines/agent-infra/naming.md +69 -0
- package/dist/agent-src/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/dist/agent-src/guidelines/agent-infra/output-patterns.md +117 -0
- package/dist/agent-src/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
- package/dist/agent-src/guidelines/agent-infra/reuse-verdict-mechanics.md +71 -0
- package/dist/agent-src/guidelines/agent-infra/review-routing-data-format.md +143 -0
- package/dist/agent-src/guidelines/agent-infra/roadmap-deferred-resolution-provenance.md +54 -0
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +274 -0
- package/dist/agent-src/guidelines/agent-infra/role-contracts.md +209 -0
- package/dist/agent-src/guidelines/agent-infra/role-mode-router.md +89 -0
- package/dist/agent-src/guidelines/agent-infra/rule-body-migration-inventory.md +241 -0
- package/dist/agent-src/guidelines/agent-infra/rule-type-governance.md +73 -0
- package/dist/agent-src/guidelines/agent-infra/runtime-layer.md +89 -0
- package/dist/agent-src/guidelines/agent-infra/scqa-framework.md +526 -0
- package/dist/agent-src/guidelines/agent-infra/security-lint-containment.md +121 -0
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +134 -0
- package/dist/agent-src/guidelines/agent-infra/simplicity-and-goal-demos.md +211 -0
- package/dist/agent-src/guidelines/agent-infra/six-hats.md +353 -0
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +229 -0
- package/dist/agent-src/guidelines/agent-infra/skill-quality-checklist.md +157 -0
- package/dist/agent-src/guidelines/agent-infra/source-confidentiality-mechanics.md +109 -0
- package/dist/agent-src/guidelines/agent-infra/symptom-driven-harvest-loop.md +104 -0
- package/dist/agent-src/guidelines/agent-infra/systems-thinking.md +220 -0
- package/dist/agent-src/guidelines/agent-infra/tool-description-as-policy.md +35 -0
- package/dist/agent-src/guidelines/agent-infra/tool-integration.md +73 -0
- package/dist/agent-src/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
- package/dist/agent-src/guidelines/agent-infra/untrusted-input-spotlighting.md +166 -0
- package/dist/agent-src/guidelines/agent-infra/verify-before-complete-demos.md +128 -0
- package/dist/agent-src/guidelines/augment-portability-patterns.md +70 -0
- package/dist/agent-src/guidelines/code-clarity.md +350 -0
- package/dist/agent-src/guidelines/component-oriented-and-oop-development.md +189 -0
- package/dist/agent-src/guidelines/cross-role-handoff.md +127 -0
- package/dist/agent-src/guidelines/design-antipatterns-triggers.json +18 -0
- package/dist/agent-src/guidelines/design-antipatterns.md +327 -0
- package/dist/agent-src/guidelines/design-asset-discipline.md +38 -0
- package/dist/agent-src/guidelines/design-canon.md +63 -0
- package/dist/agent-src/guidelines/design-fidelity-mechanics.md +293 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +78 -0
- package/dist/agent-src/guidelines/design-handover-extraction.md +117 -0
- package/dist/agent-src/guidelines/design-modes.md +143 -0
- package/dist/agent-src/guidelines/design-reconciliation.md +172 -0
- package/dist/agent-src/guidelines/docs/readme-size-and-splitting.md +209 -0
- package/dist/agent-src/guidelines/e2e/playwright.md +363 -0
- package/dist/agent-src/guidelines/gtm-handoff.md +114 -0
- package/dist/agent-src/guidelines/monorepo-antipatterns.md +141 -0
- package/dist/agent-src/guidelines/php/api-design.md +115 -0
- package/dist/agent-src/guidelines/php/artisan-commands.md +81 -0
- package/dist/agent-src/guidelines/php/blade-ui.md +78 -0
- package/dist/agent-src/guidelines/php/controllers.md +90 -0
- package/dist/agent-src/guidelines/php/database.md +194 -0
- package/dist/agent-src/guidelines/php/eloquent.md +208 -0
- package/dist/agent-src/guidelines/php/flux.md +80 -0
- package/dist/agent-src/guidelines/php/general.md +207 -0
- package/dist/agent-src/guidelines/php/git.md +164 -0
- package/dist/agent-src/guidelines/php/jobs.md +111 -0
- package/dist/agent-src/guidelines/php/livewire.md +71 -0
- package/dist/agent-src/guidelines/php/logging.md +79 -0
- package/dist/agent-src/guidelines/php/naming.md +89 -0
- package/dist/agent-src/guidelines/php/patterns/dependency-injection.md +57 -0
- package/dist/agent-src/guidelines/php/patterns/dtos.md +199 -0
- package/dist/agent-src/guidelines/php/patterns/events.md +67 -0
- package/dist/agent-src/guidelines/php/patterns/factory.md +53 -0
- package/dist/agent-src/guidelines/php/patterns/pipelines.md +66 -0
- package/dist/agent-src/guidelines/php/patterns/policies.md +66 -0
- package/dist/agent-src/guidelines/php/patterns/repositories.md +122 -0
- package/dist/agent-src/guidelines/php/patterns/service-layer.md +64 -0
- package/dist/agent-src/guidelines/php/patterns/strategy.md +247 -0
- package/dist/agent-src/guidelines/php/patterns.md +28 -0
- package/dist/agent-src/guidelines/php/performance.md +92 -0
- package/dist/agent-src/guidelines/php/php-coding-patterns.md +80 -0
- package/dist/agent-src/guidelines/php/resources.md +100 -0
- package/dist/agent-src/guidelines/php/security.md +110 -0
- package/dist/agent-src/guidelines/php/sql.md +97 -0
- package/dist/agent-src/guidelines/php/validations.md +119 -0
- package/dist/agent-src/guidelines/php/websocket.md +100 -0
- package/dist/agent-src/guidelines/prompt-templates.md +189 -0
- package/dist/agent-src/guidelines/redundancy-taxonomy.md +208 -0
- package/dist/agent-src/guidelines/wing4-handoff.md +127 -0
- package/dist/agent-src/personas/README.md +1 -1
- package/dist/agent-src/personas/_template-specialist/persona.md +1 -1
- package/dist/agent-src/personas/product-owner.md +1 -1
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/architecture.md +2 -2
- package/dist/agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/dist/agent-src/rules/ask-when-uncertain.md +2 -2
- package/dist/agent-src/rules/brand-source-of-truth.md +9 -7
- package/dist/agent-src/rules/code-comment-discipline.md +2 -2
- package/dist/agent-src/rules/code-provenance.md +1 -1
- package/dist/agent-src/rules/content-quoting-floor.md +1 -1
- package/dist/agent-src/rules/context-hygiene.md +5 -5
- package/dist/agent-src/rules/cross-source-consistency.md +1 -1
- package/dist/agent-src/rules/design-fidelity.md +36 -46
- package/dist/agent-src/rules/direct-answers.md +2 -2
- package/dist/agent-src/rules/domain-adoption-policy.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +1 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +1 -1
- package/dist/agent-src/rules/icon-consistency.md +5 -1
- package/dist/agent-src/rules/improve-before-implement.md +1 -1
- package/dist/agent-src/rules/language-and-tone.md +3 -3
- package/dist/agent-src/rules/minimal-safe-diff.md +1 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
- package/dist/agent-src/rules/recurring-criticism.md +1 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/role-mode-adherence.md +2 -2
- package/dist/agent-src/rules/security-sensitive-stop.md +1 -1
- package/dist/agent-src/rules/size-enforcement.md +1 -1
- package/dist/agent-src/rules/source-confidentiality.md +2 -2
- package/dist/agent-src/rules/think-before-action.md +1 -1
- package/dist/agent-src/rules/token-efficiency.md +7 -5
- package/dist/agent-src/rules/ui-audit-gate.md +3 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +3 -3
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
- package/dist/agent-src/skills/activation-design/SKILL.md +2 -2
- package/dist/agent-src/skills/adr-create/SKILL.md +5 -5
- package/dist/agent-src/skills/ai-council/SKILL.md +1 -1
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +1 -1
- package/dist/agent-src/skills/blade-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/brand-to-tokens/SKILL.md +24 -3
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/build-buy-partner/SKILL.md +1 -1
- package/dist/agent-src/skills/churn-prevention/SKILL.md +2 -2
- package/dist/agent-src/skills/code-intelligence/SKILL.md +57 -1
- package/dist/agent-src/skills/code-refactoring/SKILL.md +3 -3
- package/dist/agent-src/skills/code-review/SKILL.md +3 -3
- package/dist/agent-src/skills/command-writing/SKILL.md +3 -3
- package/dist/agent-src/skills/comp-banding/SKILL.md +1 -1
- package/dist/agent-src/skills/competitive-moat-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/complexity-first-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/content-funnel-design/SKILL.md +2 -2
- package/dist/agent-src/skills/contracts-cognition/SKILL.md +1 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +2 -2
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +2 -2
- package/dist/agent-src/skills/customer-research/SKILL.md +2 -2
- package/dist/agent-src/skills/dashboard-design/SKILL.md +1 -1
- package/dist/agent-src/skills/data-handling-judgment/SKILL.md +1 -1
- package/dist/agent-src/skills/deal-qualification-meddic/SKILL.md +2 -2
- package/dist/agent-src/skills/decision-record/SKILL.md +3 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +8 -8
- package/dist/agent-src/skills/deep-reading-analyst/SKILL.md +22 -22
- package/dist/agent-src/skills/design-intelligence/SKILL.md +4 -4
- package/dist/agent-src/skills/design-intelligence/references/context-and-registers.md +2 -2
- package/dist/agent-src/skills/design-review/SKILL.md +4 -4
- package/dist/agent-src/skills/design-system-capture/references/design-system-json.md +20 -1
- package/dist/agent-src/skills/design-tokens/SKILL.md +17 -2
- package/dist/agent-src/skills/design-variations/SKILL.md +2 -2
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +2 -2
- package/dist/agent-src/skills/discovery-interview/SKILL.md +2 -2
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +3 -3
- package/dist/agent-src/skills/editorial-calendar/SKILL.md +2 -2
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +44 -1
- package/dist/agent-src/skills/existing-ui-audit/references/anti-slop-cross-reference.md +2 -2
- package/dist/agent-src/skills/expansion-playbook/SKILL.md +2 -2
- package/dist/agent-src/skills/experiment-loop/SKILL.md +1 -1
- package/dist/agent-src/skills/experiment-loop/references/protocol.md +1 -1
- package/dist/agent-src/skills/fe-design/SKILL.md +3 -3
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +1 -1
- package/dist/agent-src/skills/fe-design/references/design-read-and-memory.md +3 -3
- package/dist/agent-src/skills/fe-design/references/source-led-port.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/flux/SKILL.md +1 -1
- package/dist/agent-src/skills/forecast-accuracy/SKILL.md +2 -2
- package/dist/agent-src/skills/forecasting/SKILL.md +2 -2
- package/dist/agent-src/skills/fundraising-narrative/SKILL.md +2 -2
- package/dist/agent-src/skills/funnel-analysis/SKILL.md +2 -2
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
- package/dist/agent-src/skills/gtm-launch/SKILL.md +2 -2
- package/dist/agent-src/skills/hiring-loop-design/SKILL.md +1 -1
- package/dist/agent-src/skills/html-deck/SKILL.md +5 -5
- package/dist/agent-src/skills/humanizer/SKILL.md +1 -1
- package/dist/agent-src/skills/humanizer/data/patterns.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +3 -3
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +1 -1
- package/dist/agent-src/skills/laravel-api-endpoint/SKILL.md +2 -2
- package/dist/agent-src/skills/launch-readiness/SKILL.md +3 -3
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +6 -6
- package/dist/agent-src/skills/livewire/SKILL.md +1 -1
- package/dist/agent-src/skills/market-entry-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/mcp-builder/SKILL.md +1 -1
- package/dist/agent-src/skills/memory-consolidation/SKILL.md +3 -3
- package/dist/agent-src/skills/messaging-architecture/SKILL.md +2 -2
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/onboarding-design/SKILL.md +2 -2
- package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
- package/dist/agent-src/skills/one-on-one-cadence/SKILL.md +1 -1
- package/dist/agent-src/skills/org-design/SKILL.md +1 -1
- package/dist/agent-src/skills/overbuild-review-lens/SKILL.md +3 -3
- package/dist/agent-src/skills/override-management/SKILL.md +1 -1
- package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/php-coder/SKILL.md +1 -1
- package/dist/agent-src/skills/pipeline-strategy/SKILL.md +2 -2
- package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -3
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/positioning-strategy/SKILL.md +2 -2
- package/dist/agent-src/skills/privacy-review/SKILL.md +1 -1
- package/dist/agent-src/skills/prompt-optimizer/SKILL.md +3 -3
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +1 -1
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -2
- package/dist/agent-src/skills/receiving-code-review/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +2 -2
- package/dist/agent-src/skills/release-comms/SKILL.md +2 -2
- package/dist/agent-src/skills/retention-loops/SKILL.md +2 -2
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-management/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-management/references/archival.md +1 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +5 -5
- package/dist/agent-src/skills/rule-writing/SKILL.md +6 -6
- package/dist/agent-src/skills/runway-cognition/SKILL.md +2 -2
- package/dist/agent-src/skills/scenario-modeling/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-writing/references/procedure.md +1 -1
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +3 -3
- package/dist/agent-src/skills/spreadsheet-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/stakeholder-tradeoff/SKILL.md +3 -3
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +1 -1
- package/dist/agent-src/skills/supply-chain-intake/SKILL.md +1 -1
- package/dist/agent-src/skills/systematic-debugging/SKILL.md +2 -2
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +60 -4
- package/dist/agent-src/skills/throughput-vs-morale-tradeoff/SKILL.md +1 -1
- package/dist/agent-src/skills/token-optimizer/SKILL.md +1 -1
- package/dist/agent-src/skills/typography-system/SKILL.md +2 -2
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +28 -8
- package/dist/agent-src/skills/unit-economics-modeling/SKILL.md +2 -2
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +1 -1
- package/dist/agent-src/skills/validate-feature-fit/SKILL.md +2 -2
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/skills/vision-articulation/SKILL.md +1 -1
- package/dist/agent-src/skills/voc-extract/SKILL.md +2 -2
- package/dist/agent-src/skills/voice-and-tone-design/SKILL.md +2 -2
- package/dist/agent-src/skills/wireframe/SKILL.md +1 -1
- package/dist/agent-src/skills/workspace-link/SKILL.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +6 -6
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/contexts/knowledge-pages.md +2 -3
- package/dist/agent-src/templates/copilot-instructions.md +1 -1
- package/dist/agent-src/templates/roadmaps.md +1 -1
- package/dist/agent-src/templates/scripts/README.md +2 -2
- package/dist/agent-src/user-types/_template/user-type.md +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +52 -4
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/{chunk-TGIPZBCW.js → chunk-2KKXJW25.js} +22 -8
- package/dist/cli-delegate/{chunk-NLP54U3J.js → chunk-2XY7ILBT.js} +13 -1
- package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-3FKR64R5.js} +34 -6
- package/dist/cli-delegate/{chunk-J3RFNKG4.js → chunk-55ACLDUE.js} +2 -2
- package/dist/cli-delegate/{chunk-PKOBQADP.js → chunk-57PN4GYJ.js} +1 -1
- package/dist/cli-delegate/{chunk-BIXEDQPZ.js → chunk-AK6HODNQ.js} +4 -4
- package/dist/cli-delegate/{chunk-2FTMENJL.js → chunk-GYC3VRYN.js} +1 -1
- package/dist/cli-delegate/{chunk-LWTYTX6F.js → chunk-HZ52BRGG.js} +24 -18
- package/dist/cli-delegate/{chunk-6RJA2F5A.js → chunk-IVBPRCQO.js} +1 -1
- package/dist/cli-delegate/{chunk-I6RHFQEG.js → chunk-YUM5KURG.js} +17 -1
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +24 -16
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +3 -3
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +4 -14
- package/dist/cli-delegate/cmd_settings_get.js +15 -9
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +41 -11
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +27 -80
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +5 -7
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +4 -6
- package/dist/hooks/dispatch.js +9249 -8981
- package/dist/install/install.mjs +47 -8
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +5496 -2400
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +73 -0
- package/dist/scripts/_lib/agent_src.js +20 -0
- package/dist/scripts/_lib/guidelines_lane.js +158 -0
- package/dist/scripts/_lib/lean_projection_mode.js +108 -0
- package/dist/scripts/_lib/router_match.js +243 -0
- package/dist/scripts/_lib/rule_injection.js +216 -0
- package/dist/scripts/condense.js +22 -25
- package/dist/scripts/project_thin_rules.js +139 -19
- package/dist/server/io/yamlIO.js +125 -17
- package/dist/server/schemas/settings.js +32 -5
- package/dist/shared/settingsSurface.js +6 -1
- package/docs/MIGRATION.md +56 -0
- package/docs/contracts/settings-classes.md +48 -7
- package/docs/guidelines/abstraction-thresholds.md +35 -0
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +3 -3
- package/docs/guidelines/agent-infra/false-green.md +2 -2
- package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/skill-quality-checklist.md +1 -1
- package/docs/guidelines/design-fidelity-mechanics.md +4 -0
- package/docs/guidelines/design-fidelity-routing.md +78 -0
- package/docs/guidelines/design-reconciliation.md +172 -0
- package/docs/guidelines/gtm-handoff.md +2 -2
- package/docs/guidelines/wing4-handoff.md +2 -2
- package/llms.txt +1 -1
- package/package.json +7 -3
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +106 -4
- package/src/config/agent-settings.template.yml +94 -1
- package/src/config/ci-local-parity.yml +34 -0
- package/src/config/continuity-surface.json +8 -29
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +31 -8
- package/src/config/gate-reachability-exemptions.json +1 -0
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/hook-token-budget.json +8 -7
- package/src/config/host-injection-effect.json +2 -2
- package/src/config/pack-size-budget.json +55 -4
- package/src/config/packed-binary-manifest.json +1 -1
- package/src/config/platform-anchor.json +47 -0
- package/src/config/preamble-payload-budget.json +2 -3
- package/src/config/publish-surface.json +4 -2
- package/src/config/ratification-policy.json +7 -0
- package/src/config/routing-coverage-seed.json +9 -2
- package/src/config/rule-stub-ceilings.json +16 -9
- package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
- package/src/scripts/_cli/cmd_settings_get.ts +22 -7
- package/src/scripts/_cli/handoff_sessions.ts +14 -44
- package/src/scripts/_lib/agent_settings.ts +85 -0
- package/src/scripts/_lib/agent_src.ts +21 -0
- package/src/scripts/_lib/artifact_maturity.ts +242 -0
- package/src/scripts/_lib/conformance_report.ts +403 -0
- package/src/scripts/_lib/continuity_slot.ts +295 -0
- package/src/scripts/_lib/continuity_writer.ts +312 -0
- package/src/scripts/_lib/design_system_import.ts +20 -0
- package/src/scripts/_lib/design_tolerance.ts +281 -0
- package/src/scripts/_lib/guidelines_lane.ts +164 -0
- package/src/scripts/_lib/hook_settings.ts +85 -0
- package/src/scripts/_lib/lean_projection_mode.ts +127 -0
- package/src/scripts/_lib/obligation_frequency.ts +2 -3
- package/src/scripts/_lib/packed_binary_predicate.ts +37 -0
- package/src/scripts/_lib/platform_anchor.ts +1053 -0
- package/src/scripts/_lib/ratification_artifact.ts +278 -0
- package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
- package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
- package/src/scripts/_lib/security_lint.ts +23 -2
- package/src/scripts/_lib/self_repair.ts +21 -0
- package/src/scripts/_lib/session_eol.ts +44 -0
- package/src/scripts/_lib/session_index_trust.ts +387 -0
- package/src/scripts/_lib/standing_bound_ratchet.ts +220 -0
- package/src/scripts/_lib/tolerance_shadow.ts +177 -0
- package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
- package/src/scripts/ai_council/redact_low_impact_entry.ts +8 -0
- package/src/scripts/audit_mcp_tools.ts +69 -25
- package/src/scripts/autonomy_friction_traffic.ts +379 -0
- package/src/scripts/build_mcp_catalog.ts +24 -1
- package/src/scripts/check_host_tree_parity.ts +291 -0
- package/src/scripts/check_kernel_edit_ratified.ts +462 -0
- package/src/scripts/check_loss_class_declared.ts +793 -46
- package/src/scripts/check_platform_anchor.ts +390 -0
- package/src/scripts/check_preamble_payload_budget.ts +424 -7
- package/src/scripts/check_projected_rule_routes.ts +395 -0
- package/src/scripts/check_rule_projection_integrity.ts +102 -2
- package/src/scripts/ci_settle.ts +205 -5
- package/src/scripts/code_graph/build.ts +79 -0
- package/src/scripts/code_graph/cli.ts +264 -7
- package/src/scripts/code_graph/detect.ts +38 -0
- package/src/scripts/code_graph/query.ts +25 -2
- package/src/scripts/code_graph/sqlite_store.ts +16 -1
- package/src/scripts/code_graph/types.ts +52 -3
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +590 -0
- package/src/scripts/condense.ts +22 -26
- package/src/scripts/generate_host_cost_table.ts +354 -0
- package/src/scripts/handoff_context_hook.ts +39 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +59 -28
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
- package/src/scripts/hooks/category_a.ts +1 -1
- package/src/scripts/hooks/chain_nudge_hook.ts +311 -25
- package/src/scripts/hooks/code_graph_context_hook.ts +7 -18
- package/src/scripts/hooks/push_settle_hook.ts +3 -2
- package/src/scripts/hooks/rule_inject_hook.ts +107 -20
- package/src/scripts/hooks/session_eol_hook.ts +177 -145
- package/src/scripts/hooks/source_first_gate_hook.ts +12 -0
- package/src/scripts/hooks/ui_route_nudge_hook.ts +52 -15
- package/src/scripts/hot_context_hook.ts +103 -408
- package/src/scripts/lint_evidence_artifacts.ts +1 -0
- package/src/scripts/lint_knowledge_scale.ts +0 -26
- package/src/scripts/lint_roadmap_blockers.ts +164 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
- package/src/scripts/mcp_server/graph_tools.ts +294 -0
- package/src/scripts/mcp_server/path_util.ts +31 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
- package/src/scripts/mcp_server/tools.ts +3 -13
- package/src/scripts/memory_lookup.ts +9 -9
- package/src/scripts/model_rule_injection.ts +61 -4
- package/src/scripts/project_thin_rules.ts +149 -16
- package/src/scripts/report_standing_payload_by_host.ts +650 -0
- package/src/scripts/schemas/agent-settings.schema.json +7 -1
- package/src/scripts/session_memory_index.ts +86 -8
- package/src/scripts/sync_agent_settings.ts +270 -10
- package/src/server/io/yamlIO.ts +127 -17
- package/src/server/schemas/settings.ts +45 -6
- package/src/shared/settingsSurface.ts +13 -2
- package/src/templates/compact-instructions.md +8 -3
- package/src/vendor/grammars/README.md +27 -2
- package/dist/agent-src/commands/chat-history/import.md +0 -170
- package/dist/agent-src/commands/chat-history.md +0 -63
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The standing-payload bounds are shrink-only, checked against the base ref.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS
|
|
5
|
+
* ---------------
|
|
6
|
+
* `src/config/preamble-payload-budget.json` says of its grace ceiling: *"It may
|
|
7
|
+
* never move UP."* `grace_ceiling_history` in the same file records it moving up
|
|
8
|
+
* twice — 138,212 → 138,273 on 2026-09-02 and 138,273 → 138,490 on 2026-09-08 —
|
|
9
|
+
* each with a careful prose justification. ADR-264 resolved that contradiction
|
|
10
|
+
* against the practice: the sentence stands, the two raises are violations caused
|
|
11
|
+
* by contradictory wording, and standing-rule growth needs a compensating
|
|
12
|
+
* reduction elsewhere.
|
|
13
|
+
*
|
|
14
|
+
* That decision then had no enforcement. A sentence in a JSON string is not a
|
|
15
|
+
* gate, and the file's own history is the evidence: nothing objected either time.
|
|
16
|
+
* This module is the enforcement — and it is the ONLY thing
|
|
17
|
+
* `road-to-a-standing-budget-with-headroom` step 1.3 ships, because the 128-token
|
|
18
|
+
* Iron Law reserve that step designed was refused by an AI council 2/2 on
|
|
19
|
+
* 2026-09-08. See ADR-265.
|
|
20
|
+
*
|
|
21
|
+
* WHAT IT CANNOT DO, STATED PLAINLY
|
|
22
|
+
* ---------------------------------
|
|
23
|
+
* The change under review can edit this file. Both council seats named that as
|
|
24
|
+
* the defect fatal to the reserve — "the checker, its imports, token census,
|
|
25
|
+
* workflow, and status context are all part of the trusted computing base", so a
|
|
26
|
+
* protected record read by an unprotected verifier grants nothing. It is fatal to
|
|
27
|
+
* a mechanism that GRANTS budget and it is not fatal to one that only REFUSES,
|
|
28
|
+
* which is the whole reason the reserve stayed unshipped and this ratchet did
|
|
29
|
+
* not: disabling this check requires visibly editing it, and every gate in this
|
|
30
|
+
* repository already rests on that same review. A ratchet whose only failure mode
|
|
31
|
+
* is "someone deleted the ratchet" is strictly better than the sentence it
|
|
32
|
+
* replaces, which had no failure mode because it did nothing.
|
|
33
|
+
*/
|
|
34
|
+
import { spawnSync } from 'node:child_process';
|
|
35
|
+
|
|
36
|
+
/** Repo-relative path of the budget config the bound lives in. */
|
|
37
|
+
export const BUDGET_CONFIG_PATH = 'src/config/preamble-payload-budget.json';
|
|
38
|
+
|
|
39
|
+
/** Injection seam: run git, capture output, never throw on a non-zero exit. */
|
|
40
|
+
export type GitRunner = (args: readonly string[], cwd: string) => {
|
|
41
|
+
ok: boolean;
|
|
42
|
+
stdout: string;
|
|
43
|
+
stderr: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const realGit: GitRunner = (args, cwd) => {
|
|
47
|
+
const r = spawnSync('git', [...args], { cwd, encoding: 'utf-8' });
|
|
48
|
+
return { ok: r.status === 0, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export interface BoundsRatchetVerdict {
|
|
52
|
+
ok: boolean;
|
|
53
|
+
violations: string[];
|
|
54
|
+
/** The bound read at the base ref, or `null` when it could not be read. */
|
|
55
|
+
baseGraceCeiling: number | null;
|
|
56
|
+
/** The ref compared against, or `null` when none resolved. */
|
|
57
|
+
baseRef: string | null;
|
|
58
|
+
/** Why the comparison was skipped, when it was. Printed, never silent. */
|
|
59
|
+
note: string | null;
|
|
60
|
+
/**
|
|
61
|
+
* `false` means the bound could not be READ, not that it rose.
|
|
62
|
+
*
|
|
63
|
+
* Two refusals reach a caller with `ok: false` and they need opposite
|
|
64
|
+
* actions: a risen ceiling is fixed by lowering the addition, an
|
|
65
|
+
* unverifiable one by repairing the checkout. A completion review caught
|
|
66
|
+
* both being rendered as "ROSE", which would send an operator to shrink a
|
|
67
|
+
* rule over what was a fetch problem. Absent means verified — the only
|
|
68
|
+
* `ok: false` that predates this field is a genuine rise.
|
|
69
|
+
*/
|
|
70
|
+
verified: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function showAtRef(git: GitRunner, repoRoot: string, ref: string, relPath: string): string | null {
|
|
74
|
+
const r = git(['show', `${ref}:${relPath}`], repoRoot);
|
|
75
|
+
return r.ok ? r.stdout : null;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* A condition that stops the comparison: skip when advisory, refuse when not.
|
|
80
|
+
*
|
|
81
|
+
* One helper for all four causes rather than four inline branches, so the
|
|
82
|
+
* posture cannot drift between them — the failure this closes was four
|
|
83
|
+
* independent `ok: true` returns that all had to be found before any of them
|
|
84
|
+
* could be trusted.
|
|
85
|
+
*/
|
|
86
|
+
function unverified(
|
|
87
|
+
reason: string,
|
|
88
|
+
requireBase: boolean,
|
|
89
|
+
baseGraceCeiling: number | null,
|
|
90
|
+
baseRef: string | null,
|
|
91
|
+
): BoundsRatchetVerdict {
|
|
92
|
+
if (requireBase) {
|
|
93
|
+
return {
|
|
94
|
+
ok: false,
|
|
95
|
+
verified: false,
|
|
96
|
+
baseGraceCeiling,
|
|
97
|
+
baseRef,
|
|
98
|
+
note: `${reason}, so the shrink-only bound was NOT verified in this run`,
|
|
99
|
+
violations: [
|
|
100
|
+
`the shrink-only bound could not be verified: ${reason}. This run is `
|
|
101
|
+
+ 'ENFORCING, so an unverifiable bound refuses rather than skips — an '
|
|
102
|
+
+ 'unreadable base would otherwise grant budget on an infrastructure '
|
|
103
|
+
+ 'failure. This is NOT a ceiling rise: fix the checkout (a full fetch '
|
|
104
|
+
+ 'of the base ref) rather than lowering the addition.',
|
|
105
|
+
],
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
ok: true,
|
|
110
|
+
verified: false,
|
|
111
|
+
violations: [],
|
|
112
|
+
baseGraceCeiling,
|
|
113
|
+
baseRef,
|
|
114
|
+
note: `${reason}, so the shrink-only bound was NOT verified in this run`,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Compare the grace ceiling in this tree against the one at the base ref.
|
|
120
|
+
*
|
|
121
|
+
* TWO POSTURES, AND WHICH ONE APPLIES IS THE CALLER'S TO DECLARE.
|
|
122
|
+
*
|
|
123
|
+
* Advisory (the default, and what every caller got before `requireBase`): a
|
|
124
|
+
* base ref or base config that cannot be read SKIPS, reported in both the human
|
|
125
|
+
* and JSON output so it cannot pass for a check. That default is not laziness
|
|
126
|
+
* and the original reasoning still holds — a shallow clone, a first commit and
|
|
127
|
+
* a detached build all legitimately have no base, and a gate that reds on them
|
|
128
|
+
* gets switched off, which is how a repository loses the enforcement it just
|
|
129
|
+
* built.
|
|
130
|
+
*
|
|
131
|
+
* Enforcing (`requireBase: true`): the same conditions REFUSE. An AI council
|
|
132
|
+
* (2/2, 2026-09-10) made this blocking for the move to a base-measured ceiling,
|
|
133
|
+
* and the reason is that the asymmetry above inverts once the base ref stops
|
|
134
|
+
* being a cross-check and becomes the ceiling itself. Today an unreadable base
|
|
135
|
+
* costs a comparison; there it would grant an unbounded budget on an
|
|
136
|
+
* infrastructure failure — the exact fail-open the reserve decision refused.
|
|
137
|
+
* anthropic's wording was "replaced or mode-gated"; this is the mode gate, so
|
|
138
|
+
* that no existing caller silently changes behaviour.
|
|
139
|
+
*
|
|
140
|
+
* The flag is the caller's rather than derived from `GITHUB_ACTIONS` here: this
|
|
141
|
+
* module cannot tell an enforcing CI run from a diagnostic one on the same
|
|
142
|
+
* runner, and a gate that guesses its own strictness from an environment
|
|
143
|
+
* variable is one `env` change away from being advisory everywhere.
|
|
144
|
+
*/
|
|
145
|
+
export function assertBoundsDidNotRise(opts: {
|
|
146
|
+
repoRoot: string;
|
|
147
|
+
baseRef: string | null;
|
|
148
|
+
git?: GitRunner;
|
|
149
|
+
/**
|
|
150
|
+
* Refuse instead of skipping when the base cannot be read.
|
|
151
|
+
*
|
|
152
|
+
* Absent is advisory, which keeps every pre-existing caller on the
|
|
153
|
+
* behaviour it was written against. The CI path sets it.
|
|
154
|
+
*/
|
|
155
|
+
requireBase?: boolean;
|
|
156
|
+
/**
|
|
157
|
+
* The ceiling this run actually compares the payload against.
|
|
158
|
+
*
|
|
159
|
+
* The CI step passes `--ceiling <grace_ceiling>`, so the effective bound can
|
|
160
|
+
* be raised either by editing the config or by passing a bigger number. Both
|
|
161
|
+
* arrive here as one value, which closes the second path without a second
|
|
162
|
+
* check.
|
|
163
|
+
*/
|
|
164
|
+
headGraceCeiling: number;
|
|
165
|
+
}): BoundsRatchetVerdict {
|
|
166
|
+
const git = opts.git ?? realGit;
|
|
167
|
+
const requireBase = opts.requireBase === true;
|
|
168
|
+
if (opts.baseRef === null || opts.baseRef.trim() === '') {
|
|
169
|
+
return unverified('no base ref resolved', requireBase, null, null);
|
|
170
|
+
}
|
|
171
|
+
const cfg = showAtRef(git, opts.repoRoot, opts.baseRef, BUDGET_CONFIG_PATH);
|
|
172
|
+
if (cfg === null) {
|
|
173
|
+
return unverified(
|
|
174
|
+
`${BUDGET_CONFIG_PATH} could not be read at ${opts.baseRef}`,
|
|
175
|
+
requireBase,
|
|
176
|
+
null,
|
|
177
|
+
opts.baseRef,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
let baseGrace: number | null = null;
|
|
181
|
+
try {
|
|
182
|
+
const parsed = JSON.parse(cfg) as Record<string, unknown>;
|
|
183
|
+
const delivery = (parsed['ci_delivery'] ?? {}) as Record<string, unknown>;
|
|
184
|
+
const n = Number(delivery['grace_ceiling']);
|
|
185
|
+
baseGrace = Number.isFinite(n) ? n : null;
|
|
186
|
+
} catch {
|
|
187
|
+
return unverified(
|
|
188
|
+
`${BUDGET_CONFIG_PATH} at ${opts.baseRef} is not parseable`,
|
|
189
|
+
requireBase,
|
|
190
|
+
null,
|
|
191
|
+
opts.baseRef,
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
if (baseGrace === null) {
|
|
195
|
+
return unverified(
|
|
196
|
+
`there is no ci_delivery.grace_ceiling at ${opts.baseRef} to ratchet against`,
|
|
197
|
+
requireBase,
|
|
198
|
+
null,
|
|
199
|
+
opts.baseRef,
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
if (opts.headGraceCeiling > baseGrace) {
|
|
203
|
+
return {
|
|
204
|
+
ok: false,
|
|
205
|
+
verified: true,
|
|
206
|
+
baseGraceCeiling: baseGrace,
|
|
207
|
+
baseRef: opts.baseRef,
|
|
208
|
+
note: null,
|
|
209
|
+
violations: [
|
|
210
|
+
`the standing-payload grace ceiling rose from ${String(baseGrace)} to ` +
|
|
211
|
+
`${String(opts.headGraceCeiling)} against ${opts.baseRef}. ` +
|
|
212
|
+
`${BUDGET_CONFIG_PATH} says it may not move upward and ADR-264 decided that ` +
|
|
213
|
+
'the sentence stands: standing-rule growth needs a compensating reduction ' +
|
|
214
|
+
'elsewhere, not a bigger ceiling. Lower the addition, migrate the prose out of ' +
|
|
215
|
+
'the standing rule, or reduce another standing asset by the same amount.',
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return { ok: true, verified: true, violations: [], baseGraceCeiling: baseGrace, baseRef: opts.baseRef, note: null };
|
|
220
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow records for the value-reconciliation tolerance — the window whose
|
|
3
|
+
* distribution a threshold may later be read off.
|
|
4
|
+
*
|
|
5
|
+
* NO NEW CONCERN. `check_estate_count` reports zero growth allowance on hook
|
|
6
|
+
* concerns, and the roadmap step says so in as many words: extend the existing
|
|
7
|
+
* shadow gate. These records therefore ride `source_first_gate_hook.ts`'s
|
|
8
|
+
* `SHADOW_LOG` — the same JSONL file, the same gitignored local-only path — and
|
|
9
|
+
* are told apart by their own `record` discriminator. Nothing new is registered,
|
|
10
|
+
* nothing new fires, and the concern count is unmoved.
|
|
11
|
+
*
|
|
12
|
+
* NO SELF-REPORTED VERDICT, which is the property the step's verify clause
|
|
13
|
+
* names and the one most easily lost. A record says what the distance WAS and
|
|
14
|
+
* what each candidate threshold WOULD have done with it. It never says whether
|
|
15
|
+
* that was right. A field like `was_correct` would be the mechanism grading its
|
|
16
|
+
* own homework, and a distribution of self-assessments measures the assessor —
|
|
17
|
+
* the same reason `source_first_gate_hook` records `would_warn` per candidate
|
|
18
|
+
* instead of one `warn`.
|
|
19
|
+
*
|
|
20
|
+
* COUNTERFACTUAL, NOT CHOSEN. `CANDIDATE_COLOR_THRESHOLDS` and
|
|
21
|
+
* `CANDIDATE_LENGTH_THRESHOLDS` are evaluation points, not defaults: every
|
|
22
|
+
* record carries the outcome at each, so the window yields a curve rather than
|
|
23
|
+
* a yes/no. That is the council's own instruction — evaluate several candidate
|
|
24
|
+
* thresholds counterfactually, never select one as behaviour — and it is why a
|
|
25
|
+
* spread exists at all. `SHIPPED_TOLERANCE` stays `null` on both axes
|
|
26
|
+
* throughout the window.
|
|
27
|
+
*
|
|
28
|
+
* PRIVACY BY CONSTRUCTION. The record type carries a kind, a metric, numbers,
|
|
29
|
+
* a token NAME, and booleans. It has no field able to hold a file path, a
|
|
30
|
+
* prompt, a selector, or any document content — the same stance
|
|
31
|
+
* `source_first_gate_hook.ts` states for its own record. A token name is a
|
|
32
|
+
* project's own vocabulary and is the one string here; it is what makes a
|
|
33
|
+
* distribution readable per token rather than only in aggregate.
|
|
34
|
+
*/
|
|
35
|
+
import { appendShadowLine } from '../hooks/source_first_gate_hook.js';
|
|
36
|
+
|
|
37
|
+
import type {
|
|
38
|
+
COLOR_DISTANCE_METRIC,
|
|
39
|
+
LENGTH_DISTANCE_METRIC,
|
|
40
|
+
ValueKind,
|
|
41
|
+
ValueRow,
|
|
42
|
+
} from './design_tolerance.js';
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Colour ΔEOK evaluation points. Spread deliberately across two orders of
|
|
46
|
+
* magnitude: the window has to show where the curve bends, and a spread
|
|
47
|
+
* clustered around a guess would only confirm the guess.
|
|
48
|
+
*/
|
|
49
|
+
export const CANDIDATE_COLOR_THRESHOLDS: readonly number[] = [0.001, 0.005, 0.01, 0.02, 0.05];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Length evaluation points, in CSS pixels. `1` is here because it is the one
|
|
53
|
+
* externally observed candidate in the evidence set (Primer's spacing plugin),
|
|
54
|
+
* and it is one point on a curve rather than a preferred value.
|
|
55
|
+
*/
|
|
56
|
+
export const CANDIDATE_LENGTH_THRESHOLDS: readonly number[] = [0.5, 1, 2, 4];
|
|
57
|
+
|
|
58
|
+
/** Discriminator on the shared shadow log. */
|
|
59
|
+
export const TOLERANCE_SHADOW_RECORD = 'tolerance_shadow' as const;
|
|
60
|
+
|
|
61
|
+
export interface CounterfactualOutcome {
|
|
62
|
+
threshold: number;
|
|
63
|
+
/** Would this value have been reconciled onto the token at that threshold? */
|
|
64
|
+
would_reconcile: boolean;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface ToleranceShadowRecord {
|
|
68
|
+
record: typeof TOLERANCE_SHADOW_RECORD;
|
|
69
|
+
kind: ValueKind;
|
|
70
|
+
metric: typeof COLOR_DISTANCE_METRIC | typeof LENGTH_DISTANCE_METRIC;
|
|
71
|
+
/** Distance to the nearest project token, in `metric`. */
|
|
72
|
+
distance: number;
|
|
73
|
+
/** The token's own name. No value, no path, no content. */
|
|
74
|
+
nearest_token: string;
|
|
75
|
+
/** What the mechanism actually did — `false` throughout the shadow window. */
|
|
76
|
+
acted: boolean;
|
|
77
|
+
counterfactual: CounterfactualOutcome[];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function candidatesFor(kind: ValueKind): readonly number[] {
|
|
81
|
+
return kind === 'color' ? CANDIDATE_COLOR_THRESHOLDS : CANDIDATE_LENGTH_THRESHOLDS;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Build a record from a reconciled value row, or `null` when the row carries
|
|
86
|
+
* nothing to measure.
|
|
87
|
+
*
|
|
88
|
+
* A row with no candidate token is deliberately NOT recorded as a distance of
|
|
89
|
+
* zero or of infinity: it is the absence of a measurement, and a window that
|
|
90
|
+
* silently folded those in would report a distribution over a population that
|
|
91
|
+
* includes non-observations.
|
|
92
|
+
*/
|
|
93
|
+
export function toleranceShadowRecord(
|
|
94
|
+
kind: ValueKind,
|
|
95
|
+
row: ValueRow,
|
|
96
|
+
): ToleranceShadowRecord | null {
|
|
97
|
+
if (row.nearest === null || row.distance === null || row.metric === null) return null;
|
|
98
|
+
const distance = row.distance;
|
|
99
|
+
return {
|
|
100
|
+
record: TOLERANCE_SHADOW_RECORD,
|
|
101
|
+
kind,
|
|
102
|
+
metric: row.metric,
|
|
103
|
+
distance,
|
|
104
|
+
nearest_token: row.nearest.name,
|
|
105
|
+
acted: row.outcome === 'reconciled',
|
|
106
|
+
counterfactual: candidatesFor(kind).map((threshold) => ({
|
|
107
|
+
threshold,
|
|
108
|
+
would_reconcile: distance <= threshold,
|
|
109
|
+
})),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Append the records for one reconciliation pass to the shared shadow log.
|
|
115
|
+
*
|
|
116
|
+
* THE WINDOW IS ONLY OPEN IF SOMETHING WRITES TO IT. A blind review of the
|
|
117
|
+
* branch that shipped this module found the header above claiming records ride
|
|
118
|
+
* `SHADOW_LOG` while the module had no writer and no caller — so
|
|
119
|
+
* `FLIP_CRITERION`'s reverse trigger ("fewer than 20 records in 8 weeks")
|
|
120
|
+
* was satisfied by construction, which is a criterion that measures nothing.
|
|
121
|
+
* The writer is `appendShadowLine`, reused rather than reimplemented: it is
|
|
122
|
+
* already replay-aware and already fail-open, and a second writer to one log is
|
|
123
|
+
* two chances to disagree about the format.
|
|
124
|
+
*
|
|
125
|
+
* Returns the number of records written — rows with nothing to measure are
|
|
126
|
+
* skipped, so this is not `rows.length`.
|
|
127
|
+
*/
|
|
128
|
+
export function recordToleranceShadow(
|
|
129
|
+
root: string,
|
|
130
|
+
rows: ReadonlyArray<{ kind: ValueKind; row: ValueRow }>,
|
|
131
|
+
): number {
|
|
132
|
+
let written = 0;
|
|
133
|
+
for (const { kind, row } of rows) {
|
|
134
|
+
const record = toleranceShadowRecord(kind, row);
|
|
135
|
+
if (record === null) continue;
|
|
136
|
+
appendShadowLine(root, record as unknown as Record<string, unknown>);
|
|
137
|
+
written += 1;
|
|
138
|
+
}
|
|
139
|
+
return written;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Pre-registered flip criterion, written BEFORE the window opens — which is the
|
|
144
|
+
* third clause of the step's verify and the one a later reader cannot
|
|
145
|
+
* reconstruct. Prose rather than code on purpose: nothing evaluates it
|
|
146
|
+
* automatically, and a function that returned `true` here would be the
|
|
147
|
+
* mechanism authorising its own promotion.
|
|
148
|
+
*/
|
|
149
|
+
export const FLIP_CRITERION = `
|
|
150
|
+
Shadow -> a shipped tolerance, when ALL of:
|
|
151
|
+
|
|
152
|
+
a. >= 200 tolerance_shadow records, or >= 4 weeks of window, whichever comes
|
|
153
|
+
first, with at least 50 records on EACH axis. Colour and length are
|
|
154
|
+
separate distributions and a threshold read off a pooled one is read off
|
|
155
|
+
the wrong population.
|
|
156
|
+
b. The chosen candidate's counterfactual FALSE-RECONCILIATION share -- rows a
|
|
157
|
+
human reviewer marks as "should have been preserved" that the candidate
|
|
158
|
+
would have reconciled -- is at or below 1 %, estimated per axis. The
|
|
159
|
+
reviewer labels are collected OUTSIDE this record type on purpose: a
|
|
160
|
+
record that carried its own correctness label would be the self-report
|
|
161
|
+
this module refuses.
|
|
162
|
+
c. The MISSED-reconciliation share is reported alongside it and is not
|
|
163
|
+
optimised away. A threshold of zero trivially satisfies (b) and is useless;
|
|
164
|
+
both error directions are stated or the criterion is not met.
|
|
165
|
+
d. The distribution is not obviously bimodal. A bimodal curve means the
|
|
166
|
+
tolerance model itself is wrong -- two populations are being measured as
|
|
167
|
+
one -- and the response is to re-cut the model, never to pick a threshold
|
|
168
|
+
between the modes.
|
|
169
|
+
|
|
170
|
+
Reverse trigger: fewer than 20 records in 8 weeks -> evaluate removing the
|
|
171
|
+
mechanism. A reconciliation nobody exercises is prose with a config key.
|
|
172
|
+
|
|
173
|
+
Never lifted by this criterion: enabling the mechanism BY DEFAULT is a
|
|
174
|
+
consumer-facing default flip and stays owner-reserved, recorded in
|
|
175
|
+
agents/roadmaps/stubs/road-to-frontend-power-default-flip.md. Meeting a to d
|
|
176
|
+
authorises a shipped tolerance VALUE, not a shipped default.
|
|
177
|
+
`.trim();
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The vendored grammars' one anchor outside the manifest.
|
|
3
|
+
*
|
|
4
|
+
* WHAT THIS ANSWERS.
|
|
5
|
+
*
|
|
6
|
+
* `packed_binary_predicate.ts` verifies eleven conditions over
|
|
7
|
+
* `src/config/packed-binary-manifest.json`, and every one of them is
|
|
8
|
+
* manifest-internal: the manifest states a sha256 and the bytes are hashed
|
|
9
|
+
* against it, so a single commit that edits both passes all eleven. The
|
|
10
|
+
* `release/14.22.0` self-review finding `d1696732ac28` names exactly that gap.
|
|
11
|
+
* This module closes it by comparing the vendored bytes against a source the
|
|
12
|
+
* repository does not author: the `tree-sitter-wasms` release pinned in
|
|
13
|
+
* `package-lock.json` and installed by `npm ci`.
|
|
14
|
+
*
|
|
15
|
+
* WHAT IT MAY CLAIM, AND WHAT IT MAY NOT.
|
|
16
|
+
*
|
|
17
|
+
* It MAY claim: the vendored bytes equal the bytes of the locked upstream
|
|
18
|
+
* package, independent of the binary manifest's own self-description.
|
|
19
|
+
*
|
|
20
|
+
* It MUST NOT claim to be independent of all repository state, and it MUST NOT
|
|
21
|
+
* claim to verify the grammars cryptographically against the registry
|
|
22
|
+
* independent of local build state. It does neither. The chain is
|
|
23
|
+
* registry tarball -> `npm ci` checks its sha512 against the lock's
|
|
24
|
+
* `integrity` -> extracted bytes -> vendored bytes, and only the last link is
|
|
25
|
+
* checked here. An attacker who edits `node_modules` after install defeats
|
|
26
|
+
* this, and a lock entry that points at a hostile package defeats it earlier
|
|
27
|
+
* still — that link is review's job, not a check's, because the lock is
|
|
28
|
+
* human-readable text and the manifest is machine-only JSON.
|
|
29
|
+
*
|
|
30
|
+
* Both scoping sentences are the AI council's wording (2026-09-09, 2 seats,
|
|
31
|
+
* unanimous on this question), kept verbatim so a later reader can tell the
|
|
32
|
+
* boundary from an oversight.
|
|
33
|
+
*
|
|
34
|
+
* WHY IT REFUSES RATHER THAN SKIPS.
|
|
35
|
+
*
|
|
36
|
+
* An upstream comparison is trivially written so that it passes when the
|
|
37
|
+
* upstream is absent, which is a green that verifies nothing — the exact shape
|
|
38
|
+
* of the eleven conditions it is here to supplement. So an absent package, a
|
|
39
|
+
* version that is not the locked one, an absent counterpart file, an empty
|
|
40
|
+
* claim set, and two admitted entries sharing a filename are all refusals,
|
|
41
|
+
* never passes.
|
|
42
|
+
*
|
|
43
|
+
* The last of those is the subtlest, and it was added on an R2 review finding
|
|
44
|
+
* rather than foreseen: the upstream keeps its grammars in one flat directory,
|
|
45
|
+
* so the counterpart is addressed by basename. Two manifest rows with the same
|
|
46
|
+
* filename in different directories would anchor to the same upstream file —
|
|
47
|
+
* one admitted binary silently unanchored while the verdict reported full
|
|
48
|
+
* coverage, which is the false green this module exists to remove. The vendored
|
|
49
|
+
* side resolves the entry's OWN repo-relative path, never a basename under a
|
|
50
|
+
* fixed directory, for the same reason.
|
|
51
|
+
*/
|
|
52
|
+
import { createHash } from 'node:crypto';
|
|
53
|
+
import * as fs from 'node:fs';
|
|
54
|
+
import * as path from 'node:path';
|
|
55
|
+
|
|
56
|
+
import { loadManifest, VERIFIABLE_KIND, type PackedBinaryManifest } from './packed_binary_predicate.js';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The upstream this repository copies from. Hardcoded rather than derived: the
|
|
60
|
+
* vendored set exists precisely because the package is NOT a runtime
|
|
61
|
+
* dependency, so there is no dependency edge to read it off, and
|
|
62
|
+
* `src/vendor/grammars/README.md` names the same string as the refresh source.
|
|
63
|
+
*/
|
|
64
|
+
export const UPSTREAM_PACKAGE = 'tree-sitter-wasms';
|
|
65
|
+
|
|
66
|
+
/** Where the upstream keeps its compiled grammars inside its own package. */
|
|
67
|
+
const UPSTREAM_GRAMMAR_SUBDIR = 'out';
|
|
68
|
+
|
|
69
|
+
/** A refusal — the anchor could not be established, so no verdict is honest. */
|
|
70
|
+
export class UpstreamAnchorRefusal extends Error {
|
|
71
|
+
constructor(message: string) {
|
|
72
|
+
super(message);
|
|
73
|
+
this.name = 'UpstreamAnchorRefusal';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** One vendored file compared against its upstream counterpart. */
|
|
78
|
+
export interface GrammarComparison {
|
|
79
|
+
readonly vendoredPath: string;
|
|
80
|
+
readonly upstreamPath: string;
|
|
81
|
+
readonly vendoredSha256: string;
|
|
82
|
+
readonly upstreamSha256: string;
|
|
83
|
+
readonly equal: boolean;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface UpstreamAnchorVerdict {
|
|
87
|
+
readonly lockedVersion: string;
|
|
88
|
+
readonly lockedIntegrity: string;
|
|
89
|
+
readonly installedVersion: string;
|
|
90
|
+
readonly comparisons: readonly GrammarComparison[];
|
|
91
|
+
/** Human-readable divergence lines. Empty iff every comparison is equal. */
|
|
92
|
+
readonly divergences: readonly string[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface UpstreamAnchorOptions {
|
|
96
|
+
readonly repoRoot: string;
|
|
97
|
+
/** Defaults to `<repoRoot>/node_modules/<UPSTREAM_PACKAGE>`. Fixture seam. */
|
|
98
|
+
readonly upstreamPackageDir?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Root the manifest's repo-relative `path` values resolve against.
|
|
101
|
+
* Defaults to `repoRoot`. Fixture seam — NOT a grammar directory: resolving
|
|
102
|
+
* by directory would discard the entry's own path, and two rows sharing a
|
|
103
|
+
* basename in different directories would then anchor to the same file.
|
|
104
|
+
*/
|
|
105
|
+
readonly vendoredRoot?: string;
|
|
106
|
+
/** Defaults to the manifest at `<repoRoot>/src/config/…`. Fixture seam. */
|
|
107
|
+
readonly manifest?: PackedBinaryManifest;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export interface LockPin {
|
|
111
|
+
readonly version: string;
|
|
112
|
+
readonly integrity: string;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The locked pin for the upstream, read from `package-lock.json`.
|
|
117
|
+
*
|
|
118
|
+
* `integrity` is not used to verify anything here — it is carried into the
|
|
119
|
+
* verdict so the caller can print the anchor it is trusting, and its absence
|
|
120
|
+
* is a refusal because a lock row without one pins a version and not bytes.
|
|
121
|
+
*/
|
|
122
|
+
export function readLockPin(repoRoot: string): LockPin {
|
|
123
|
+
const lockPath = path.join(repoRoot, 'package-lock.json');
|
|
124
|
+
if (!fs.existsSync(lockPath)) {
|
|
125
|
+
throw new UpstreamAnchorRefusal(`no package-lock.json at ${lockPath} — there is no locked upstream to anchor to`);
|
|
126
|
+
}
|
|
127
|
+
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf-8')) as {
|
|
128
|
+
packages?: Record<string, { version?: string; integrity?: string }>;
|
|
129
|
+
};
|
|
130
|
+
const key = `node_modules/${UPSTREAM_PACKAGE}`;
|
|
131
|
+
const row = lock.packages?.[key];
|
|
132
|
+
if (row === undefined) {
|
|
133
|
+
throw new UpstreamAnchorRefusal(`package-lock.json carries no \`${key}\` row — the vendored grammars have no locked source`);
|
|
134
|
+
}
|
|
135
|
+
if (typeof row.version !== 'string' || row.version.length === 0) {
|
|
136
|
+
throw new UpstreamAnchorRefusal(`\`${key}\` in package-lock.json carries no version`);
|
|
137
|
+
}
|
|
138
|
+
if (typeof row.integrity !== 'string' || row.integrity.length === 0) {
|
|
139
|
+
throw new UpstreamAnchorRefusal(
|
|
140
|
+
`\`${key}\` in package-lock.json carries no \`integrity\` — the row pins a version but not bytes, so \`npm ci\` verifies nothing for it`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
return { version: row.version, integrity: row.integrity };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The manifest, read through the predicate's own reader.
|
|
148
|
+
*
|
|
149
|
+
* A second reader would drift: a shape condition added there — a
|
|
150
|
+
* `schema_version` check, a kind allowlist — would leave this module accepting
|
|
151
|
+
* a manifest the predicate rejects, and this module's whole value is that it
|
|
152
|
+
* covers what the manifest admits. So `loadManifest` is the reader and this
|
|
153
|
+
* wrapper only re-types its failures as refusals, because an unreadable
|
|
154
|
+
* manifest means the anchor was not established rather than that it failed.
|
|
155
|
+
* R2 finding 2, 2026-09-09 — the duplicate reader this replaces was one
|
|
156
|
+
* identifier away from the import already at the top of this file.
|
|
157
|
+
*/
|
|
158
|
+
function readManifest(repoRoot: string): PackedBinaryManifest {
|
|
159
|
+
try {
|
|
160
|
+
return loadManifest(repoRoot);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
throw new UpstreamAnchorRefusal(
|
|
163
|
+
`the packed-binary manifest could not be read, so nothing was compared: ${err instanceof Error ? err.message : String(err)}`,
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function sha256(file: string): string {
|
|
169
|
+
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Compare every vendored grammar the manifest admits against the locked
|
|
174
|
+
* upstream's copy of the same filename.
|
|
175
|
+
*
|
|
176
|
+
* Throws `UpstreamAnchorRefusal` when the anchor cannot be established at all;
|
|
177
|
+
* returns a verdict whose `divergences` is non-empty when it can be established
|
|
178
|
+
* and the bytes differ. The two are deliberately different outcomes: a refusal
|
|
179
|
+
* says "this was not checked", a divergence says "this was checked and is
|
|
180
|
+
* wrong", and collapsing them is how a check becomes a permanent green.
|
|
181
|
+
*/
|
|
182
|
+
export function verifyVendoredGrammarsAgainstUpstream(options: UpstreamAnchorOptions): UpstreamAnchorVerdict {
|
|
183
|
+
const { repoRoot } = options;
|
|
184
|
+
const upstreamDir = options.upstreamPackageDir ?? path.join(repoRoot, 'node_modules', UPSTREAM_PACKAGE);
|
|
185
|
+
const vendoredRoot = options.vendoredRoot ?? repoRoot;
|
|
186
|
+
const manifest = options.manifest ?? readManifest(repoRoot);
|
|
187
|
+
|
|
188
|
+
const pin = readLockPin(repoRoot);
|
|
189
|
+
|
|
190
|
+
const claims = manifest.entries.filter((e) => e.kind === VERIFIABLE_KIND);
|
|
191
|
+
if (claims.length === 0) {
|
|
192
|
+
throw new UpstreamAnchorRefusal(
|
|
193
|
+
`the manifest admits no \`${VERIFIABLE_KIND}\` entry — nothing was compared, and reporting that as a pass would be a green over an empty corpus`,
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// The upstream keeps its grammars in one flat directory, so the counterpart
|
|
198
|
+
// is addressed by basename. Two admitted entries sharing a basename would
|
|
199
|
+
// therefore point at the same upstream file, which is an ambiguous anchor
|
|
200
|
+
// rather than a comparison — refuse instead of picking one.
|
|
201
|
+
const byBase = new Map<string, string[]>();
|
|
202
|
+
for (const e of claims) {
|
|
203
|
+
const base = path.basename(e.path);
|
|
204
|
+
byBase.set(base, [...(byBase.get(base) ?? []), e.path]);
|
|
205
|
+
}
|
|
206
|
+
const ambiguous = [...byBase.entries()].filter(([, paths]) => paths.length > 1);
|
|
207
|
+
if (ambiguous.length > 0) {
|
|
208
|
+
throw new UpstreamAnchorRefusal(
|
|
209
|
+
`two or more admitted entries share a filename, so they would anchor to the same upstream file: ${ambiguous
|
|
210
|
+
.map(([base, paths]) => `${base} <- ${paths.join(', ')}`)
|
|
211
|
+
.join('; ')}`,
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const upstreamPkgJson = path.join(upstreamDir, 'package.json');
|
|
216
|
+
if (!fs.existsSync(upstreamPkgJson)) {
|
|
217
|
+
throw new UpstreamAnchorRefusal(
|
|
218
|
+
`${UPSTREAM_PACKAGE} is not installed at ${upstreamDir} — run \`npm ci\`. The vendored bytes were NOT verified; an absent upstream is a refusal, never a pass`,
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
const installed = JSON.parse(fs.readFileSync(upstreamPkgJson, 'utf-8')) as { version?: string };
|
|
222
|
+
if (installed.version !== pin.version) {
|
|
223
|
+
throw new UpstreamAnchorRefusal(
|
|
224
|
+
`installed ${UPSTREAM_PACKAGE} is ${String(installed.version)} but package-lock.json pins ${pin.version} — comparing against an unlocked tree would anchor to nothing`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const comparisons: GrammarComparison[] = [];
|
|
229
|
+
const divergences: string[] = [];
|
|
230
|
+
|
|
231
|
+
for (const entry of claims) {
|
|
232
|
+
const vendoredPath = path.join(vendoredRoot, entry.path);
|
|
233
|
+
const upstreamPath = path.join(upstreamDir, UPSTREAM_GRAMMAR_SUBDIR, path.basename(entry.path));
|
|
234
|
+
if (!fs.existsSync(vendoredPath)) {
|
|
235
|
+
throw new UpstreamAnchorRefusal(`the manifest admits ${entry.path} but ${vendoredPath} does not exist`);
|
|
236
|
+
}
|
|
237
|
+
if (!fs.existsSync(upstreamPath)) {
|
|
238
|
+
throw new UpstreamAnchorRefusal(
|
|
239
|
+
`${entry.path} has no counterpart at ${upstreamPath} — the locked upstream does not carry this grammar, so this file has no anchor`,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
const vendoredSha256 = sha256(vendoredPath);
|
|
243
|
+
const upstreamSha256 = sha256(upstreamPath);
|
|
244
|
+
const equal = vendoredSha256 === upstreamSha256;
|
|
245
|
+
comparisons.push({ vendoredPath, upstreamPath, vendoredSha256, upstreamSha256, equal });
|
|
246
|
+
if (!equal) {
|
|
247
|
+
divergences.push(
|
|
248
|
+
`${entry.path}: vendored sha256 ${vendoredSha256} != upstream ${upstreamSha256} (${UPSTREAM_PACKAGE}@${pin.version})`,
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return buildVerdict({ pin, installedVersion: installed.version, comparisons, divergences });
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Assemble the verdict from the two version sources, kept separate.
|
|
258
|
+
*
|
|
259
|
+
* Extracted so the sourcing is testable at all. On any tree the check accepts,
|
|
260
|
+
* `pin.version` and the installed version are equal — the strict guard above
|
|
261
|
+
* refuses otherwise — so a test driving the whole function can never tell which
|
|
262
|
+
* of the two the `installedVersion` field came from, and the round-1 regression
|
|
263
|
+
* test for exactly that defect passed with the defect restored. R2 finding 1,
|
|
264
|
+
* 2026-09-09. Here the two can be given different values, which is the only
|
|
265
|
+
* place the distinction is observable.
|
|
266
|
+
*/
|
|
267
|
+
export function buildVerdict(args: {
|
|
268
|
+
readonly pin: LockPin;
|
|
269
|
+
readonly installedVersion: string;
|
|
270
|
+
readonly comparisons: readonly GrammarComparison[];
|
|
271
|
+
readonly divergences: readonly string[];
|
|
272
|
+
}): UpstreamAnchorVerdict {
|
|
273
|
+
return {
|
|
274
|
+
lockedVersion: args.pin.version,
|
|
275
|
+
lockedIntegrity: args.pin.integrity,
|
|
276
|
+
installedVersion: args.installedVersion,
|
|
277
|
+
comparisons: args.comparisons,
|
|
278
|
+
divergences: args.divergences,
|
|
279
|
+
};
|
|
280
|
+
}
|