@event4u/agent-config 14.9.0 → 14.11.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 +384 -0
- 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/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/alerting-doctrine/evals/triggers.json +17 -0
- package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
- 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 +1 -1
- package/dist/agent-src/skills/code-review/SKILL.md +11 -0
- package/dist/agent-src/skills/command-routing/SKILL.md +6 -0
- package/dist/agent-src/skills/condense-memory/SKILL.md +4 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +4 -1
- 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 +80 -2
- 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/discovery-interview/SKILL.md +1 -1
- package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
- package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +20 -3
- package/dist/agent-src/skills/fe-design/SKILL.md +74 -41
- package/dist/agent-src/skills/fe-design/evals/triggers.json +65 -53
- 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/github-ci/SKILL.md +1 -1
- 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/evals/triggers.json +17 -0
- package/dist/agent-src/skills/js-library-packaging/scripts/check_package_surface.ts +270 -0
- package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
- package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
- package/dist/agent-src/skills/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
- package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
- 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/messaging-architecture/SKILL.md +1 -1
- package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
- package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
- package/dist/agent-src/skills/module-management/SKILL.md +1 -1
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -1
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
- 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/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +52 -21
- 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 +3 -3
- 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/SKILL.md +1 -1
- package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +5 -1
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
- package/dist/agent-src/skills/server-hardening/evals/triggers.json +17 -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/spreadsheet-authoring/SKILL.md +3 -0
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +194 -0
- package/dist/agent-src/skills/storybook-workshop/evals/triggers.json +17 -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 +9 -1
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +3 -0
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- 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/skills/workspace-link/evals/triggers.json +17 -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/uiAudit.js.map +1 -0
- package/dist/cli/commands/uiRender.js +206 -0
- package/dist/cli/commands/uiRender.js.map +1 -0
- package/dist/cli/main.js +22 -1
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/content.js +64 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/cli/mcp/dispatch.js +175 -10
- package/dist/cli/mcp/dispatch.js.map +1 -1
- package/dist/cli/registry.js +3 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-H4VIVD4C.js → chunk-236PNQZK.js} +413 -391
- 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-PLSG6UL5.js → chunk-K4FFODTS.js} +341 -204
- package/dist/cli-delegate/{chunk-YM4GZDJG.js → chunk-L43NCYPB.js} +5 -0
- 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 +380 -145
- 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 +2337 -1436
- package/dist/install/install.mjs +795 -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/lean_projection_mode.js.map +1 -0
- package/dist/scripts/_lib/scoped_projection.js +84 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -1
- package/dist/scripts/_lib/ui_surface.js +89 -0
- package/dist/scripts/_lib/ui_surface.js.map +1 -0
- package/dist/scripts/condense.js +6 -10
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +40 -2
- package/dist/scripts/project_thin_rules.js.map +1 -1
- package/dist/server/schemas/settings.js +3 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/skillRanking.js +159 -0
- package/dist/shared/skillRanking.js.map +1 -0
- package/docs/contracts/provider-lifecycle.md +47 -18
- package/docs/contracts/settings-classes.md +14 -4
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +49 -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/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/llms.txt +50 -39
- package/package.json +1 -1
- 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 +28 -0
- package/src/config/assurance-capability-registry.json +223 -0
- package/src/config/assurance-threshold-budget.json +65 -0
- package/src/config/ci-local-parity.yml +14 -0
- package/src/config/discovery/unassigned-artefacts.yml +2 -0
- package/src/config/dispatch-economy-metrics.json +4 -2
- package/src/config/evaluator-budgets.json +14 -14
- package/src/config/gate-coverage.yml +227 -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 +4 -2
- package/src/config/preamble-payload-budget.json +71 -67
- package/src/config/quorum-attendance-budget.json +12 -4
- 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/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/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/transcript_reads.ts +207 -0
- package/src/scripts/_lib/ui_authority.test.ts +251 -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/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/seating.ts +211 -0
- 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_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_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_pack_passport_reconciliation.ts +274 -0
- 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_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/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/design_slop_rules.test.ts +7 -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/generate_pack_manifests.ts +78 -0
- package/src/scripts/grade_target_readiness.ts +478 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +97 -16
- package/src/scripts/hooks/concern_registry.ts +4 -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/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 +2 -1
- 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_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/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/roadmap_context.ts +778 -0
- package/src/scripts/schemas/fidelity-measurement-sheet.schema.json +169 -0
- package/src/scripts/schemas/pack.schema.json +62 -4
- package/src/scripts/schemas/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +41 -2
- package/src/scripts/schemas/ui-authority.schema.json +114 -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/tighten_skill_descriptions.ts +264 -0
- 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 +5 -0
- package/src/shared/skillRanking.ts +183 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `lean_projection.mode` — ONE definition of what the three modes mean.
|
|
3
|
+
*
|
|
4
|
+
* The projector (`condense.ts`) resolves the value with a real YAML parse; the
|
|
5
|
+
* delivery concern (`hooks/rule_inject_hook.ts`) resolves it with an
|
|
6
|
+
* indentation-shaped read, because a hook must never fail a tool call because a
|
|
7
|
+
* parser could not load. Two readers is unavoidable. Two *normalisations* is
|
|
8
|
+
* not, and would be the defect worth preventing: a projector that writes thin
|
|
9
|
+
* files while the concern believes the mode is off delivers pointers and no
|
|
10
|
+
* bodies, which is exactly the 36.2 % arm the delivery mode exists to replace.
|
|
11
|
+
*
|
|
12
|
+
* So each side supplies the raw string from its own reader and this module
|
|
13
|
+
* decides what it means. Anything unrecognised — absent key, typo, `null`,
|
|
14
|
+
* a non-string — is `eager-all`, today's shipped behaviour: a mode nobody can
|
|
15
|
+
* spell must never silently thin the standing corpus.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
/** The three projection shapes. `eager-all` is the shipped default. */
|
|
19
|
+
export type LeanProjectionMode = 'eager-all' | 'thin' | 'delivery';
|
|
20
|
+
|
|
21
|
+
export const DEFAULT_LEAN_PROJECTION_MODE: LeanProjectionMode = 'eager-all';
|
|
22
|
+
|
|
23
|
+
/** Map a raw settings value onto a mode. Unrecognised → `eager-all`. */
|
|
24
|
+
export function normalizeLeanProjectionMode(raw: unknown): LeanProjectionMode {
|
|
25
|
+
const v = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
|
|
26
|
+
if (v === 'thin') return 'thin';
|
|
27
|
+
if (v === 'delivery') return 'delivery';
|
|
28
|
+
return DEFAULT_LEAN_PROJECTION_MODE;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* True when the projector writes pointer stubs instead of bodies.
|
|
33
|
+
*
|
|
34
|
+
* `delivery` is a superset of `thin`: it writes the same thin files AND binds
|
|
35
|
+
* the concern that delivers the bodies back on a trigger.
|
|
36
|
+
*/
|
|
37
|
+
export function writesThinFiles(mode: LeanProjectionMode): boolean {
|
|
38
|
+
return mode === 'thin' || mode === 'delivery';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** True only in the mode where a hook delivers rule bodies at runtime. */
|
|
42
|
+
export function deliversBodies(mode: LeanProjectionMode): boolean {
|
|
43
|
+
return mode === 'delivery';
|
|
44
|
+
}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* mcp_bridge — `.mcp.json` registration and the skill-tier projection lever.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `install.ts` by Phase 2.2 of `road-to-skill-delivery-over-mcp`,
|
|
5
|
+
* for a mechanical reason worth stating: `check_source_size_budget` is a ratchet
|
|
6
|
+
* over every line in a file above 1,500, `install.ts` is 5,466 of them, and the
|
|
7
|
+
* gate's own message says a new violation is fixed and never baselined away. So
|
|
8
|
+
* the feature lives here and `install.ts` keeps one-line call sites.
|
|
9
|
+
*
|
|
10
|
+
* Everything here is a pure function of its arguments or of the file system.
|
|
11
|
+
* `ensureMcpBridge` is the one exception and takes its writer by injection —
|
|
12
|
+
* `merge_json_file` lives in `install.ts` (eight other bridges use it) and
|
|
13
|
+
* importing it back would make the cycle.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from 'node:fs';
|
|
17
|
+
import * as path from 'node:path';
|
|
18
|
+
|
|
19
|
+
/** `install.ts`'s deploy tuple: [written, skipped, status, paths]. */
|
|
20
|
+
export type DeployTuple = [number, number, string, string[]];
|
|
21
|
+
|
|
22
|
+
/** The `merge_json_file` signature this module is handed. */
|
|
23
|
+
export type MergeJsonFile = (
|
|
24
|
+
p: string,
|
|
25
|
+
new_data: Record<string, unknown>,
|
|
26
|
+
force: boolean,
|
|
27
|
+
label: string,
|
|
28
|
+
) => Record<string, unknown>[];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The project-scope MCP server entry Claude Code reads from `.mcp.json`.
|
|
32
|
+
*
|
|
33
|
+
* Shape is fixed by `docs/getting-started-local-stdio.md` — the documented
|
|
34
|
+
* end-user path — so a consumer who followed the docs and a consumer who ran the
|
|
35
|
+
* installer end up with the same entry rather than two that drift.
|
|
36
|
+
*/
|
|
37
|
+
export const MCP_SERVER_KEY = 'agent-config';
|
|
38
|
+
export const MCP_BRIDGE_ENTRY = {
|
|
39
|
+
mcpServers: {
|
|
40
|
+
[MCP_SERVER_KEY]: {
|
|
41
|
+
command: 'npx',
|
|
42
|
+
args: ['-y', '@event4u/agent-config', 'mcp-server'],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* `.mcp.json` — the project-scope MCP config Claude Code reads.
|
|
49
|
+
*
|
|
50
|
+
* WHY THE INSTALLER OWNS THIS FILE AND `mcp_render` DOES NOT. `mcp_render`
|
|
51
|
+
* projects the consumer's OWN root `mcp.json` onto per-tool configs by
|
|
52
|
+
* overwriting them (`write_target`), which is correct for a file the renderer
|
|
53
|
+
* generates in full and wrong for one the consumer also edits: a render would
|
|
54
|
+
* delete both this entry and any server they added by hand. `merge_json_file`
|
|
55
|
+
* writes only our key, records it as an RFC-6901 pointer in
|
|
56
|
+
* `agents/installed-tools.lock`, and lets uninstall subtract exactly that key.
|
|
57
|
+
* `mcp:check` validates the file without ever writing it.
|
|
58
|
+
*
|
|
59
|
+
* Default ON for `claude-code`, because `rules/missing-skill-recovery.md` makes
|
|
60
|
+
* `suggest_skill_for_task` an Iron Law for every consumer and a rule that is
|
|
61
|
+
* unfulfillable by default is a defect, not a feature. Opting out is the same
|
|
62
|
+
* lever as every other bridge — deselect the tool (`--tools`), which is the gate
|
|
63
|
+
* the caller applies; `.agent-tools.yml` is deliberately NOT that lever, it is a
|
|
64
|
+
* maintainer-side projection-generator allowlist the installer cannot see and
|
|
65
|
+
* whose tool ids differ from `_VALID_TOOLS`.
|
|
66
|
+
*/
|
|
67
|
+
export function makeEnsureMcpBridge(
|
|
68
|
+
mergeJsonFile: MergeJsonFile,
|
|
69
|
+
): (projectRoot: string, force: boolean) => Record<string, unknown>[] {
|
|
70
|
+
return (projectRoot, force) =>
|
|
71
|
+
mergeJsonFile(path.join(projectRoot, '.mcp.json'), MCP_BRIDGE_ENTRY, force, '.mcp.json');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The three projection modes.
|
|
76
|
+
*
|
|
77
|
+
* `legacy-all` ships everything and stays the default. `scoped` narrows to the
|
|
78
|
+
* active packs. `tiered` narrows to the skills predicted to reach the model WITH
|
|
79
|
+
* their description, on the theory that a skill the host lists bare is better
|
|
80
|
+
* served by the MCP recovery tool than by a catalogue entry nobody can read.
|
|
81
|
+
*
|
|
82
|
+
* `tiered` is OPT-IN and stays opt-in until the roadmap's Phase 4.4 decides on
|
|
83
|
+
* evidence — which it did, at `measured-null`. The reason is stated rather than
|
|
84
|
+
* implied: the split usually comes from an ALPHABETICAL fallback order
|
|
85
|
+
* (`agents/runtime/metrics/skill-usage.jsonl` is absent on most machines), and
|
|
86
|
+
* the one real host observation this repo has disagrees with that fallback on
|
|
87
|
+
* four of eight sampled entries. Defaulting to `tiered` would hide skills on a
|
|
88
|
+
* prediction the tree itself can show to be wrong — strictly worse than the
|
|
89
|
+
* bare-but-listed defect it fixes (roadmap risk 4).
|
|
90
|
+
*/
|
|
91
|
+
export type ProjectionMode = 'scoped' | 'legacy-all' | 'tiered';
|
|
92
|
+
|
|
93
|
+
/** Resolve a raw `projection.mode` value. Anything unrecognised is `legacy-all`. */
|
|
94
|
+
export function projectionModeOf(raw: unknown): ProjectionMode {
|
|
95
|
+
return raw === 'scoped' ? 'scoped' : raw === 'tiered' ? 'tiered' : 'legacy-all';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Tier B skill names, or `null` when no split exists on this machine.
|
|
100
|
+
*
|
|
101
|
+
* `null` is load-bearing: it means "nobody computed a split here", and a
|
|
102
|
+
* `tiered` install that finds it must ship the FULL surface rather than prune on
|
|
103
|
+
* an empty set — pruning on absence would delete the entire catalogue. A
|
|
104
|
+
* malformed file is `null` for the same reason.
|
|
105
|
+
*/
|
|
106
|
+
export function resolveTierB(packageRoot: string): ReadonlySet<string> | null {
|
|
107
|
+
const p = path.join(packageRoot, 'agents', 'runtime', 'state', 'skill-tiers.json');
|
|
108
|
+
if (!fs.existsSync(p)) return null;
|
|
109
|
+
try {
|
|
110
|
+
const parsed = JSON.parse(fs.readFileSync(p, 'utf-8')) as { tier_b?: unknown };
|
|
111
|
+
if (!Array.isArray(parsed.tier_b)) return null;
|
|
112
|
+
return new Set(parsed.tier_b.filter((n): n is string => typeof n === 'string'));
|
|
113
|
+
} catch {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Predicate for `_prune_modules_by`: is this SKILL.md a Tier B skill? */
|
|
119
|
+
export function tierBPrunePredicate(
|
|
120
|
+
tierB: ReadonlySet<string>,
|
|
121
|
+
): (mdPath: string) => boolean {
|
|
122
|
+
return (mdPath) => tierB.has(path.basename(path.dirname(mdPath)));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The consumer-side alternative to tiering, surfaced as a recommendation only.
|
|
127
|
+
*
|
|
128
|
+
* Claude Code's listing budget is settable: `skillListingBudgetFraction` raises
|
|
129
|
+
* the fraction of the context window spent on skill DESCRIPTIONS, restoring them
|
|
130
|
+
* at a measured token price instead of withholding skills from the catalogue.
|
|
131
|
+
* That is the other lever, and for many consumers the better one — a description
|
|
132
|
+
* they can read beats a tool call they have to make.
|
|
133
|
+
*
|
|
134
|
+
* Returns `null` unless a tier split exists AND Tier B is non-empty: with no
|
|
135
|
+
* split there is nothing to recommend against, and an empty Tier B means the
|
|
136
|
+
* host already describes everything. This NEVER writes the consumer's
|
|
137
|
+
* `settings.json` — the setting is theirs, the token cost is theirs, and an
|
|
138
|
+
* installer that edited a host's own config to buy itself context would be
|
|
139
|
+
* taking that decision in their name.
|
|
140
|
+
*/
|
|
141
|
+
export function tierBAdvisory(packageRoot: string): string | null {
|
|
142
|
+
const tierB = resolveTierB(packageRoot);
|
|
143
|
+
if (tierB === null || tierB.size === 0) return null;
|
|
144
|
+
return (
|
|
145
|
+
`ℹ️ ${tierB.size} skill(s) are predicted to reach the model WITHOUT their ` +
|
|
146
|
+
'description on a default 200k window.\n' +
|
|
147
|
+
' Two levers, neither applied for you:\n' +
|
|
148
|
+
' • raise `skillListingBudgetFraction` in your Claude Code settings — ' +
|
|
149
|
+
'restores descriptions at a token cost (100% delivery of this catalogue ' +
|
|
150
|
+
'measures ~13,003 tok)\n' +
|
|
151
|
+
' • or set `projection.mode: tiered` to withhold them from the catalogue ' +
|
|
152
|
+
'and reach them via the MCP server instead (opt-in, still unproven — see ' +
|
|
153
|
+
'docs/mcp-server.md)'
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface TieredPruneOutcome {
|
|
158
|
+
deployResults: Record<string, DeployTuple>;
|
|
159
|
+
/** One line for the caller to print, or `null` when QUIET. */
|
|
160
|
+
message: string | null;
|
|
161
|
+
/** `warn` when no split exists, `info` when a prune happened. */
|
|
162
|
+
level: 'warn' | 'info';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Apply the Tier-B prune to a completed deploy.
|
|
167
|
+
*
|
|
168
|
+
* Takes `pruneBy` rather than importing it, so the prune mechanics stay in one
|
|
169
|
+
* place and this module does not become a second implementation of them.
|
|
170
|
+
*/
|
|
171
|
+
export function applyTieredPrune(
|
|
172
|
+
deployResults: Record<string, DeployTuple>,
|
|
173
|
+
packageRoot: string,
|
|
174
|
+
pruneBy: (
|
|
175
|
+
dr: Record<string, DeployTuple>,
|
|
176
|
+
isPruned: (mdPath: string) => boolean,
|
|
177
|
+
) => [number, Record<string, DeployTuple>],
|
|
178
|
+
): TieredPruneOutcome {
|
|
179
|
+
const tierB = resolveTierB(packageRoot);
|
|
180
|
+
if (tierB === null || tierB.size === 0) {
|
|
181
|
+
return {
|
|
182
|
+
deployResults,
|
|
183
|
+
level: 'warn',
|
|
184
|
+
message:
|
|
185
|
+
'projection.mode: tiered but no skill-tier split exists ' +
|
|
186
|
+
'(agents/runtime/state/skill-tiers.json) — shipping the full surface. ' +
|
|
187
|
+
'Compute one with `./scripts-run src/scripts/compute_skill_tiers` first.',
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
const [pruned, adjusted] = pruneBy(deployResults, tierBPrunePredicate(tierB));
|
|
191
|
+
return {
|
|
192
|
+
deployResults: adjusted,
|
|
193
|
+
level: 'info',
|
|
194
|
+
message:
|
|
195
|
+
`🧹 Tiered install: pruned ${pruned} Tier-B skill artefact(s) — still ` +
|
|
196
|
+
"reachable via the MCP server's suggest_skill_for_task / read_skill. " +
|
|
197
|
+
'Set projection.mode: legacy-all to restore the full surface.',
|
|
198
|
+
};
|
|
199
|
+
}
|
|
@@ -27,6 +27,26 @@ const PROVIDER_FAMILY: Readonly<Record<string, string>> = {
|
|
|
27
27
|
export type ReviewIndependence = 'cross-family' | 'same-family' | 'single-member' | 'unknown';
|
|
28
28
|
export type AcceptanceStatus = 'accepted' | 'provisional';
|
|
29
29
|
|
|
30
|
+
/**
|
|
31
|
+
* How the reviewer relates to the AUTHOR — a second axis, added 2026-08-23 by
|
|
32
|
+
* `road-to-review-independence` step 2.1.
|
|
33
|
+
*
|
|
34
|
+
* Model family and author relation are different questions, and the type carried only
|
|
35
|
+
* the first. **A cross-family pair that both read the implementer's envelope is not
|
|
36
|
+
* independent in the sense that matters here**: they disagree about the model, and they
|
|
37
|
+
* share the framing of what the change is for and what counts as done.
|
|
38
|
+
*
|
|
39
|
+
* - `fresh` — no implementation context. A separate dispatch that received the diff and
|
|
40
|
+
* the scope, and not the author's envelope.
|
|
41
|
+
* - `same-session` — ran inside the author's session, with the implementation context in
|
|
42
|
+
* scope. The common path today, and the one this axis exists to make visible.
|
|
43
|
+
* - `unknown` — not recorded. Treated as `same-session` for derivation, because an
|
|
44
|
+
* unrecorded relation is not evidence of freshness and the safe direction on an
|
|
45
|
+
* integrity field is the weaker claim (the same reasoning `unknown` already gets on
|
|
46
|
+
* the family axis).
|
|
47
|
+
*/
|
|
48
|
+
export type ContextRelation = 'fresh' | 'same-session' | 'unknown';
|
|
49
|
+
|
|
30
50
|
/**
|
|
31
51
|
* How much INDEPENDENT evidence backs the verdict — deliberately orthogonal to
|
|
32
52
|
* how much effort the reviewer spent.
|
|
@@ -54,30 +74,56 @@ export function reviewIndependence(memberNames: readonly string[]): ReviewIndepe
|
|
|
54
74
|
* is not evidence of independence, and the safe direction on an integrity field
|
|
55
75
|
* is the weaker claim.
|
|
56
76
|
*/
|
|
57
|
-
export function acceptanceStatus(
|
|
58
|
-
|
|
77
|
+
export function acceptanceStatus(
|
|
78
|
+
independence: ReviewIndependence,
|
|
79
|
+
relation: ContextRelation = 'unknown',
|
|
80
|
+
): AcceptanceStatus {
|
|
81
|
+
if (independence !== 'cross-family') return 'provisional';
|
|
82
|
+
// BOTH axes, and this is the whole point of step 2.1: a cross-family pair that both
|
|
83
|
+
// sat in the author's session shares the framing even though it disagrees about the
|
|
84
|
+
// model. `unknown` falls here too — an unrecorded relation is not evidence of
|
|
85
|
+
// freshness. The parameter defaults to `unknown` so every existing caller keeps
|
|
86
|
+
// compiling and gets the SAFER answer rather than the old one.
|
|
87
|
+
return relation === 'fresh' ? 'accepted' : 'provisional';
|
|
59
88
|
}
|
|
60
89
|
|
|
61
|
-
export function assuranceFor(
|
|
62
|
-
|
|
90
|
+
export function assuranceFor(
|
|
91
|
+
independence: ReviewIndependence,
|
|
92
|
+
relation: ContextRelation = 'unknown',
|
|
93
|
+
): Assurance {
|
|
63
94
|
if (independence === 'unknown') return 'unreviewed';
|
|
95
|
+
// `independent` requires both axes, for the same reason `accepted` does.
|
|
96
|
+
if (independence === 'cross-family' && relation === 'fresh') return 'independent';
|
|
64
97
|
return 'single-pass';
|
|
65
98
|
}
|
|
66
99
|
|
|
67
100
|
export interface IndependenceFields {
|
|
68
101
|
readonly review_independence: ReviewIndependence;
|
|
102
|
+
/** The recorded second axis. Derived fields below follow from BOTH. */
|
|
103
|
+
readonly context_relation: ContextRelation;
|
|
69
104
|
readonly acceptance_status: AcceptanceStatus;
|
|
70
105
|
readonly assurance: Assurance;
|
|
71
106
|
readonly reviewers: readonly string[];
|
|
72
107
|
}
|
|
73
108
|
|
|
74
|
-
/**
|
|
75
|
-
|
|
109
|
+
/**
|
|
110
|
+
* The recorded axes plus the fields derived from them.
|
|
111
|
+
*
|
|
112
|
+
* `context_relation` defaults to `unknown` so an existing caller that names only its
|
|
113
|
+
* members keeps compiling — and gets `provisional` rather than the `accepted` it used to
|
|
114
|
+
* get. That direction is deliberate: a producer that has not said whether its reviewer
|
|
115
|
+
* was fresh has not earned the stronger claim.
|
|
116
|
+
*/
|
|
117
|
+
export function independenceFields(
|
|
118
|
+
memberNames: readonly string[],
|
|
119
|
+
relation: ContextRelation = 'unknown',
|
|
120
|
+
): IndependenceFields {
|
|
76
121
|
const independence = reviewIndependence(memberNames);
|
|
77
122
|
return {
|
|
78
123
|
review_independence: independence,
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
context_relation: relation,
|
|
125
|
+
acceptance_status: acceptanceStatus(independence, relation),
|
|
126
|
+
assurance: assuranceFor(independence, relation),
|
|
81
127
|
reviewers: [...memberNames],
|
|
82
128
|
};
|
|
83
129
|
}
|
|
@@ -97,13 +143,19 @@ export function independenceViolations(doc: Record<string, unknown>): string[] {
|
|
|
97
143
|
out.push('review_independence is absent — an artifact that does not declare its independence reads as acceptance by default');
|
|
98
144
|
return out;
|
|
99
145
|
}
|
|
100
|
-
|
|
146
|
+
// The recorded relation, not a default: `independenceViolations` judges an artifact
|
|
147
|
+
// that already carries the fields, so reading `unknown` when the artifact says
|
|
148
|
+
// `fresh` would flag a correct producer.
|
|
149
|
+
const relRaw = doc['context_relation'];
|
|
150
|
+
const rel: ContextRelation =
|
|
151
|
+
relRaw === 'fresh' || relRaw === 'same-session' ? relRaw : 'unknown';
|
|
152
|
+
const expectedAccept = acceptanceStatus(indep as ReviewIndependence, rel);
|
|
101
153
|
if (accept !== expectedAccept) {
|
|
102
154
|
out.push(
|
|
103
155
|
`acceptance_status is ${String(accept)} but review_independence ${String(indep)} derives ${expectedAccept}`,
|
|
104
156
|
);
|
|
105
157
|
}
|
|
106
|
-
const expectedAssur = assuranceFor(indep as ReviewIndependence);
|
|
158
|
+
const expectedAssur = assuranceFor(indep as ReviewIndependence, rel);
|
|
107
159
|
if (assur !== undefined && assur !== expectedAssur) {
|
|
108
160
|
out.push(`assurance is ${String(assur)} but review_independence ${String(indep)} derives ${expectedAssur}`);
|
|
109
161
|
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rule-injection core — THE single module both the offline model and the
|
|
3
|
+
* runtime concern read.
|
|
4
|
+
*
|
|
5
|
+
* WHY ONE MODULE. `road-to-trigger-delivered-rule-bodies` step 0.5 states the
|
|
6
|
+
* failure this prevents in as many words: *an experiment whose offline pricing
|
|
7
|
+
* and runtime delivery use different matchers measures nothing*. So the price /
|
|
8
|
+
* recall model (`src/scripts/model_rule_injection.ts`) and the delivery concern
|
|
9
|
+
* (`src/scripts/hooks/rule_inject_hook.ts`) both import THIS file, and neither
|
|
10
|
+
* re-implements selection, ordering, capping, or body loading.
|
|
11
|
+
*
|
|
12
|
+
* IT OWNS NO MATCHER. Trigger semantics belong to
|
|
13
|
+
* [`_lib/router_match.ts`](router_match.ts), which is already the single
|
|
14
|
+
* implementation for every surface that answers "which rules fire on this
|
|
15
|
+
* prompt?" and is pinned by `tests/scripts/router_match_parity.test.ts`. A
|
|
16
|
+
* second matcher here would be the violation that test exists to catch, so
|
|
17
|
+
* `matchTierRules` is a thin, deterministic wrapper over `match_prompt` and
|
|
18
|
+
* nothing more. Step 0.3's pre-registered comparison found no reason to write
|
|
19
|
+
* one — see `model_rule_injection.ts`'s header for the measured verdict.
|
|
20
|
+
*
|
|
21
|
+
* KERNEL IS NEVER INJECTED. The nine kernel rules are in standing context by
|
|
22
|
+
* definition; injecting one would be paying twice for a body that is already
|
|
23
|
+
* there. `match_prompt` returns kernel ids in `activated_rules` because kernel
|
|
24
|
+
* rules are always active — this module filters them out by construction rather
|
|
25
|
+
* than relying on a caller to remember.
|
|
26
|
+
*
|
|
27
|
+
* IT IMPORTS NO TOKENIZER, AND THAT IS A HOT-PATH FACT RATHER THAN A STYLE
|
|
28
|
+
* CHOICE. `_lib/token_count.ts` resolves `js-tiktoken` AT MODULE LOAD, and the
|
|
29
|
+
* concern that imports this file is statically reachable from
|
|
30
|
+
* `concern_registry.ts` — so importing it here made every hook dispatch on
|
|
31
|
+
* every slot pay a tokenizer load for a concern that is default-OFF and emits
|
|
32
|
+
* nothing. Measured on this tree: unbinding the concern moved the
|
|
33
|
+
* `pre_tool_use` p95 from 202 ms to 196 ms, and the CI latency gate went red on
|
|
34
|
+
* the branch that introduced it while passing on main.
|
|
35
|
+
*
|
|
36
|
+
* So the runtime cap is in BYTES. That is not a weaker bound — it is the same
|
|
37
|
+
* bound in the unit the budget actually enforces: `hook-token-budget.json`
|
|
38
|
+
* measures "bytes of concern stdout payload fields", so the cap and its
|
|
39
|
+
* registered row are now the same number instead of two units that need a
|
|
40
|
+
* conversion factor to compare. The offline model keeps exact-BPE tokens for
|
|
41
|
+
* its price table, where the tokenizer is the point and nothing is on a hot
|
|
42
|
+
* path.
|
|
43
|
+
*
|
|
44
|
+
* DETERMINISM IS THE CONTRACT, and the collision cases are the reason it needs
|
|
45
|
+
* stating. Two triggers on the SAME rule collapse to one entry (a rule is
|
|
46
|
+
* delivered once, however many of its triggers fired) while the trigger count
|
|
47
|
+
* survives as `score`. Two DIFFERENT rules both firing are returned in router
|
|
48
|
+
* declaration order, tier_1 before tier_2 — never in match order, which would
|
|
49
|
+
* make the output depend on trigger authoring order inside a rule. Capping
|
|
50
|
+
* sorts by descending score with router order as the tie-break, so the same
|
|
51
|
+
* prompt against the same router always yields the same bytes.
|
|
52
|
+
*/
|
|
53
|
+
import * as fs from 'node:fs';
|
|
54
|
+
import * as path from 'node:path';
|
|
55
|
+
|
|
56
|
+
import { match_prompt, type Router, type Trigger } from './router_match.js';
|
|
57
|
+
|
|
58
|
+
export type { Router, Trigger };
|
|
59
|
+
|
|
60
|
+
/** Tier a matched rule came from. Kernel is never a value here. */
|
|
61
|
+
export type Tier = 'tier_1' | 'tier_2';
|
|
62
|
+
|
|
63
|
+
export interface TierRuleMatch {
|
|
64
|
+
id: string;
|
|
65
|
+
tier: Tier;
|
|
66
|
+
/** How many of this rule's triggers matched. Ties break on router order. */
|
|
67
|
+
score: number;
|
|
68
|
+
/** Router declaration index within its tier — the deterministic tie-break. */
|
|
69
|
+
order: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Kinds of trigger a prompt alone can never fire. */
|
|
73
|
+
export const PATH_TRIGGER_KINDS = ['path_prefix', 'file_pattern'] as const;
|
|
74
|
+
|
|
75
|
+
export function loadRouter(repoRoot: string): Router {
|
|
76
|
+
const p = path.join(repoRoot, 'dist', 'router.json');
|
|
77
|
+
return JSON.parse(fs.readFileSync(p, 'utf8')) as Router;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Kernel rule ids, as declared by the router. */
|
|
81
|
+
export function kernelIds(router: Router): Set<string> {
|
|
82
|
+
const k = Array.isArray(router['kernel']) ? router['kernel'] : [];
|
|
83
|
+
return new Set(k.map((x) => String(x)));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function tierEntries(router: Router, tier: Tier): Array<{ id: string; triggers: Trigger[] }> {
|
|
87
|
+
const raw = router[tier];
|
|
88
|
+
if (!Array.isArray(raw)) return [];
|
|
89
|
+
return (raw as unknown as Array<Record<string, unknown>>).map((r) => ({
|
|
90
|
+
id: String(r['id'] ?? ''),
|
|
91
|
+
triggers: Array.isArray(r['triggers']) ? (r['triggers'] as Trigger[]) : [],
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Every tier rule declared by the router, with its trigger list. */
|
|
96
|
+
export function allTierRules(router: Router): Array<{ id: string; tier: Tier; triggers: Trigger[] }> {
|
|
97
|
+
const out: Array<{ id: string; tier: Tier; triggers: Trigger[] }> = [];
|
|
98
|
+
for (const tier of ['tier_1', 'tier_2'] as Tier[]) {
|
|
99
|
+
for (const e of tierEntries(router, tier)) out.push({ ...e, tier });
|
|
100
|
+
}
|
|
101
|
+
return out;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Rules whose triggers are ALL path-shaped — unreachable from a prompt alone. */
|
|
105
|
+
export function pathOnlyRuleIds(router: Router): Set<string> {
|
|
106
|
+
const ids = new Set<string>();
|
|
107
|
+
for (const r of allTierRules(router)) {
|
|
108
|
+
if (r.triggers.length === 0) continue;
|
|
109
|
+
const allPath = r.triggers.every((t) =>
|
|
110
|
+
PATH_TRIGGER_KINDS.some((k) => k in t),
|
|
111
|
+
);
|
|
112
|
+
if (allPath) ids.add(r.id);
|
|
113
|
+
}
|
|
114
|
+
return ids;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Rules declaring at least one path-shaped trigger. */
|
|
118
|
+
export function pathCapableRuleIds(router: Router): Set<string> {
|
|
119
|
+
const ids = new Set<string>();
|
|
120
|
+
for (const r of allTierRules(router)) {
|
|
121
|
+
if (r.triggers.some((t) => PATH_TRIGGER_KINDS.some((k) => k in t))) ids.add(r.id);
|
|
122
|
+
}
|
|
123
|
+
return ids;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Rules declaring no trigger at all — cannot be delivered, must stay eager. */
|
|
127
|
+
export function triggerlessRuleIds(router: Router): string[] {
|
|
128
|
+
return allTierRules(router)
|
|
129
|
+
.filter((r) => r.triggers.length === 0)
|
|
130
|
+
.map((r) => r.id);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Which tier rules fire on this prompt (+ optional open files / command).
|
|
135
|
+
*
|
|
136
|
+
* Kernel is excluded. Order is router declaration order, tier_1 first.
|
|
137
|
+
*/
|
|
138
|
+
export function matchTierRules(
|
|
139
|
+
router: Router,
|
|
140
|
+
prompt: string,
|
|
141
|
+
openFiles?: Iterable<string> | null,
|
|
142
|
+
command?: string | null,
|
|
143
|
+
): TierRuleMatch[] {
|
|
144
|
+
const kernel = kernelIds(router);
|
|
145
|
+
const res = match_prompt(router, prompt, 'full', openFiles, command);
|
|
146
|
+
const scores = new Map<string, number>();
|
|
147
|
+
for (const mt of res.matched_triggers) {
|
|
148
|
+
const id = String(mt.rule ?? '');
|
|
149
|
+
if (id === '' || kernel.has(id)) continue;
|
|
150
|
+
scores.set(id, (scores.get(id) ?? 0) + 1);
|
|
151
|
+
}
|
|
152
|
+
const out: TierRuleMatch[] = [];
|
|
153
|
+
let order = 0;
|
|
154
|
+
for (const r of allTierRules(router)) {
|
|
155
|
+
const s = scores.get(r.id);
|
|
156
|
+
if (s === undefined) {
|
|
157
|
+
order += 1;
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
out.push({ id: r.id, tier: r.tier, score: s, order });
|
|
161
|
+
order += 1;
|
|
162
|
+
}
|
|
163
|
+
return out;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Where the projected body of a rule lives. */
|
|
167
|
+
export function ruleBodyPath(repoRoot: string, id: string): string {
|
|
168
|
+
return path.join(repoRoot, 'dist', 'agent-src', 'rules', `${id}.md`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** The projected body, or `null` when the rule has no projected file. */
|
|
172
|
+
export function loadRuleBody(repoRoot: string, id: string): string | null {
|
|
173
|
+
const p = ruleBodyPath(repoRoot, id);
|
|
174
|
+
if (!fs.existsSync(p)) return null;
|
|
175
|
+
return fs.readFileSync(p, 'utf8');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* UTF-8 byte length — the runtime measure, dependency-free by design.
|
|
180
|
+
*
|
|
181
|
+
* Deliberately NOT a token count: see the header. Bytes are exact, need no
|
|
182
|
+
* tokenizer, and are the unit `hook-token-budget.json` enforces.
|
|
183
|
+
*/
|
|
184
|
+
export function bytesOf(text: string): number {
|
|
185
|
+
return Buffer.byteLength(text, 'utf8');
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface SelectionResult {
|
|
189
|
+
selected: TierRuleMatch[];
|
|
190
|
+
/** Matched but dropped because the byte cap was reached. */
|
|
191
|
+
dropped: TierRuleMatch[];
|
|
192
|
+
/** Byte sum of the selected bodies. */
|
|
193
|
+
bytes: number;
|
|
194
|
+
/** Per-rule body byte counts, keyed by id, for every MATCHED rule. */
|
|
195
|
+
bodyBytes: Map<string, number>;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Apply the per-prompt BYTE cap.
|
|
200
|
+
*
|
|
201
|
+
* Highest score first, router order as tie-break; a rule whose body would push
|
|
202
|
+
* the running total past `capBytes` is dropped and the walk continues, so one
|
|
203
|
+
* oversized body cannot starve the rest. The returned `selected` list is
|
|
204
|
+
* re-sorted back into router order — cap order is a budgeting concern, delivery
|
|
205
|
+
* order is not.
|
|
206
|
+
*
|
|
207
|
+
* ONE selection for both callers. The offline model and the runtime concern
|
|
208
|
+
* call THIS function with THE SAME cap, so the set the price table prices is
|
|
209
|
+
* byte-for-byte the set the concern delivers. That is step 0.5's requirement,
|
|
210
|
+
* and expressing the cap in bytes makes it hold without a token/byte
|
|
211
|
+
* conversion sitting between the two arms.
|
|
212
|
+
*/
|
|
213
|
+
export function selectForInjection(
|
|
214
|
+
repoRoot: string,
|
|
215
|
+
matches: TierRuleMatch[],
|
|
216
|
+
capBytes: number,
|
|
217
|
+
): SelectionResult {
|
|
218
|
+
const bodyBytes = new Map<string, number>();
|
|
219
|
+
for (const m of matches) {
|
|
220
|
+
const body = loadRuleBody(repoRoot, m.id);
|
|
221
|
+
bodyBytes.set(m.id, body === null ? 0 : bytesOf(body));
|
|
222
|
+
}
|
|
223
|
+
const ranked = [...matches].sort((a, b) => b.score - a.score || a.order - b.order);
|
|
224
|
+
const selected: TierRuleMatch[] = [];
|
|
225
|
+
const dropped: TierRuleMatch[] = [];
|
|
226
|
+
let total = 0;
|
|
227
|
+
for (const m of ranked) {
|
|
228
|
+
const t = bodyBytes.get(m.id) ?? 0;
|
|
229
|
+
if (total + t > capBytes && selected.length > 0) {
|
|
230
|
+
dropped.push(m);
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
selected.push(m);
|
|
234
|
+
total += t;
|
|
235
|
+
}
|
|
236
|
+
selected.sort((a, b) => a.order - b.order);
|
|
237
|
+
dropped.sort((a, b) => a.order - b.order);
|
|
238
|
+
return { selected, dropped, bytes: total, bodyBytes };
|
|
239
|
+
}
|