@event4u/agent-config 14.10.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 +335 -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/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-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 +3 -0
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +3 -0
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +10 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/SKILL.md +3 -0
- package/dist/agent-src/skills/design-intelligence/data/landing.csv +36 -35
- package/dist/agent-src/skills/design-intelligence/data/stacks/threejs.csv +1 -0
- package/dist/agent-src/skills/design-review/SKILL.md +79 -1
- package/dist/agent-src/skills/design-review/references/verification-automation.md +41 -0
- package/dist/agent-src/skills/design-system-capture/SKILL.md +35 -40
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +50 -0
- package/dist/agent-src/skills/design-tokens/SKILL.md +3 -0
- package/dist/agent-src/skills/design-variations/SKILL.md +3 -0
- package/dist/agent-src/skills/developer-like-execution/SKILL.md +3 -0
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +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/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/laravel-migration/SKILL.md +3 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +3 -0
- package/dist/agent-src/skills/lint-skills/SKILL.md +3 -0
- package/dist/agent-src/skills/llm-provider-knowledge/SKILL.md +3 -0
- package/dist/agent-src/skills/logging-monitoring/SKILL.md +234 -51
- package/dist/agent-src/skills/logo-generation/SKILL.md +3 -0
- package/dist/agent-src/skills/md-language-check/SKILL.md +3 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +149 -0
- package/dist/agent-src/skills/monorepo-workspace/evals/triggers.json +17 -0
- package/dist/agent-src/skills/motion-choreographer/SKILL.md +6 -0
- package/dist/agent-src/skills/multi-tenancy/SKILL.md +4 -0
- package/dist/agent-src/skills/operational-readiness/SKILL.md +184 -0
- package/dist/agent-src/skills/operational-readiness/evals/triggers.json +17 -0
- package/dist/agent-src/skills/override-management/SKILL.md +3 -0
- package/dist/agent-src/skills/persona-improvement/SKILL.md +3 -0
- package/dist/agent-src/skills/pest-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/playbook-authoring/SKILL.md +162 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +3 -0
- package/dist/agent-src/skills/privacy-review/SKILL.md +3 -0
- package/dist/agent-src/skills/prompt-engineering-image/SKILL.md +3 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +5 -0
- package/dist/agent-src/skills/react-shadcn-ui/SKILL.md +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 +2 -2
- package/dist/agent-src/skills/refine-prompt/SKILL.md +3 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +3 -0
- package/dist/agent-src/skills/requesting-code-review/SKILL.md +2 -2
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +23 -41
- package/dist/agent-src/skills/roadmap-writing/references/follow-up-roadmap-shape.md +47 -0
- package/dist/agent-src/skills/rtk-output-filtering/SKILL.md +22 -6
- package/dist/agent-src/skills/runway-cognition/SKILL.md +3 -0
- package/dist/agent-src/skills/scene-expander/scene-blueprint.schema.yaml +26 -0
- package/dist/agent-src/skills/secrets-management/SKILL.md +4 -0
- package/dist/agent-src/skills/server-hardening/SKILL.md +159 -0
- package/dist/agent-src/skills/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/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 +359 -124
- 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 +207 -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/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/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_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/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/rule.schema.json +24 -2
- package/src/scripts/schemas/skill.schema.json +39 -0
- package/src/scripts/schemas/ui-authority.schema.json +114 -0
- package/src/scripts/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/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
|
@@ -12,13 +12,20 @@
|
|
|
12
12
|
* detection. No behaviour changes.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
+
import { execFileSync } from 'node:child_process';
|
|
15
16
|
import * as fs from 'node:fs';
|
|
16
17
|
import * as path from 'node:path';
|
|
17
18
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
18
19
|
|
|
19
20
|
import { SRC_SKILLS } from './_lib/agent_src.js';
|
|
21
|
+
import { asOf } from './_lib/as_of.js';
|
|
20
22
|
import { checkRatchet } from './_lib/gate_baseline.js';
|
|
21
23
|
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
24
|
+
import {
|
|
25
|
+
compute_active_pack_ids,
|
|
26
|
+
is_pruned_under_scoped,
|
|
27
|
+
load_packs_registry,
|
|
28
|
+
} from './_lib/scoped_projection.js';
|
|
22
29
|
|
|
23
30
|
const _HERE = fileURLToPath(import.meta.url);
|
|
24
31
|
const REPO = path.resolve(path.dirname(_HERE), '..', '..');
|
|
@@ -237,6 +244,8 @@ function _resolve(p: string): string {
|
|
|
237
244
|
|
|
238
245
|
export function lint(skills_dir: string): Violation[] {
|
|
239
246
|
const senior_skills: Map<string, string> = new Map();
|
|
247
|
+
/** `file|line|href` of every dangling link the senior pass already reported. */
|
|
248
|
+
const reported_dangling = new Set<string>();
|
|
240
249
|
const all_skills: Map<string, string> = new Map();
|
|
241
250
|
for (const skill_md of _rglobSkillMd(skills_dir)) {
|
|
242
251
|
const text = fs.readFileSync(skill_md, 'utf-8');
|
|
@@ -267,6 +276,7 @@ export function lint(skills_dir: string): Violation[] {
|
|
|
267
276
|
}
|
|
268
277
|
graph.get(skill_path)!.add(target);
|
|
269
278
|
if (!all_skills.has(target)) {
|
|
279
|
+
reported_dangling.add(`${skill_path}|${String(lineno)}|${link}`);
|
|
270
280
|
violations.push({
|
|
271
281
|
file: skill_path,
|
|
272
282
|
line: lineno,
|
|
@@ -291,6 +301,7 @@ export function lint(skills_dir: string): Violation[] {
|
|
|
291
301
|
for (const [lineno, slug, link] of extract_links(when_not)) {
|
|
292
302
|
const target = resolve_target(skill_path, link);
|
|
293
303
|
if (!all_skills.has(target)) {
|
|
304
|
+
reported_dangling.add(`${skill_path}|${String(lineno)}|${link}`);
|
|
294
305
|
violations.push({
|
|
295
306
|
file: skill_path,
|
|
296
307
|
line: lineno,
|
|
@@ -321,9 +332,487 @@ export function lint(skills_dir: string): Violation[] {
|
|
|
321
332
|
message: `composition cycle: ${names}`,
|
|
322
333
|
});
|
|
323
334
|
}
|
|
335
|
+
|
|
336
|
+
// Whole-body dangling pass — see the BodyLink block below for why the
|
|
337
|
+
// scope widens here and NOT for tier mismatch or cycle detection.
|
|
338
|
+
for (const bl of collect_body_links(skills_dir)) {
|
|
339
|
+
if (all_skills.has(bl.target)) {
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
if (reported_dangling.has(`${bl.file}|${String(bl.line)}|${bl.link}`)) {
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
violations.push({
|
|
346
|
+
file: bl.file,
|
|
347
|
+
line: bl.line,
|
|
348
|
+
code: 'handoff_dangling',
|
|
349
|
+
message: `link to \`${bl.slug}\` resolves to missing file ${bl.link}`,
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
violations.sort((a, b) => (a.file === b.file ? a.line - b.line : a.file < b.file ? -1 : 1));
|
|
324
353
|
return violations;
|
|
325
354
|
}
|
|
326
355
|
|
|
356
|
+
// ── Whole-body cross-skill links + the link census (skill-link-integrity) ──
|
|
357
|
+
// The senior / `## Related Skills` pass above is a COMPOSITION contract: it
|
|
358
|
+
// judges tier and builds the DAG, and both of those only mean anything inside
|
|
359
|
+
// a declared handoff block. A dead link is a different fact — a body sentence
|
|
360
|
+
// telling the agent to read a file that is not on disk is wrong wherever it
|
|
361
|
+
// sits — and every file carrying today's dead links declares neither a `tier:`
|
|
362
|
+
// nor that heading, which is exactly why a gate that has owned
|
|
363
|
+
// `handoff_dangling` since 2026-08-02 had never seen one of them.
|
|
364
|
+
//
|
|
365
|
+
// So the scope widens for the dangling check ONLY. Tier mismatch and cycle
|
|
366
|
+
// detection keep the narrow scope on purpose: widening them would read every
|
|
367
|
+
// prose cross-reference as a composition edge, which it is not.
|
|
368
|
+
|
|
369
|
+
/** One `[slug](…SKILL.md)` link found anywhere in a SKILL.md body. */
|
|
370
|
+
export interface BodyLink {
|
|
371
|
+
/** Absolute, symlink-resolved path of the SKILL.md carrying the link. */
|
|
372
|
+
file: string;
|
|
373
|
+
line: number;
|
|
374
|
+
/** Link text. `LINK_RE` only matches slug-shaped text, so this is a slug. */
|
|
375
|
+
slug: string;
|
|
376
|
+
/** The href exactly as written. */
|
|
377
|
+
link: string;
|
|
378
|
+
/** `resolve_target(file, link)` — absolute, symlink-resolved. */
|
|
379
|
+
target: string;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/** Every `[slug](…SKILL.md)` link in every SKILL.md body under `skills_dir`. */
|
|
383
|
+
export function collect_body_links(skills_dir: string): BodyLink[] {
|
|
384
|
+
const out: BodyLink[] = [];
|
|
385
|
+
for (const skill_md of _rglobSkillMd(skills_dir)) {
|
|
386
|
+
const file = _resolve(skill_md);
|
|
387
|
+
const lines = fs.readFileSync(skill_md, 'utf-8').split('\n');
|
|
388
|
+
for (let idx = 0; idx < lines.length; idx++) {
|
|
389
|
+
const raw = lines[idx] as string;
|
|
390
|
+
LINK_RE.lastIndex = 0;
|
|
391
|
+
let m: RegExpExecArray | null;
|
|
392
|
+
while ((m = LINK_RE.exec(raw)) !== null) {
|
|
393
|
+
const link = m[2] as string;
|
|
394
|
+
out.push({
|
|
395
|
+
file,
|
|
396
|
+
line: idx + 1,
|
|
397
|
+
slug: m[1] as string,
|
|
398
|
+
link,
|
|
399
|
+
target: resolve_target(file, link),
|
|
400
|
+
});
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return out;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/** Values of a block-list frontmatter key (`key:` then ` - item` lines). */
|
|
408
|
+
export function parse_frontmatter_list(text: string, key: string): string[] {
|
|
409
|
+
if (!text.startsWith('---\n')) {
|
|
410
|
+
return [];
|
|
411
|
+
}
|
|
412
|
+
const end = text.indexOf('\n---\n', 4);
|
|
413
|
+
if (end === -1) {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
const out: string[] = [];
|
|
417
|
+
let inKey = false;
|
|
418
|
+
for (const raw of text.slice(4, end).split('\n')) {
|
|
419
|
+
if (/^\S/.test(raw)) {
|
|
420
|
+
inKey = raw.replace(/:.*$/, '').trim() === key;
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
if (inKey) {
|
|
424
|
+
const m = /^\s+-\s*(.+?)\s*$/.exec(raw);
|
|
425
|
+
if (m) out.push(_strip(_strip(m[1] as string, '"'), "'"));
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
return out;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/** A dead link, as the census records it. */
|
|
432
|
+
export interface DeadLinkRecord {
|
|
433
|
+
/** Repo-relative path of the referring SKILL.md. */
|
|
434
|
+
file: string;
|
|
435
|
+
line: number;
|
|
436
|
+
/** The href exactly as written. */
|
|
437
|
+
target: string;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/** A surviving skill linking a slug that `scoped` prunes. */
|
|
441
|
+
export interface ScopedDangleRecord {
|
|
442
|
+
survivor: string;
|
|
443
|
+
target: string;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* The link census Phase 0 of `road-to-skill-link-integrity-and-manifest-sync`
|
|
448
|
+
* publishes. Produced by the SAME collector the gate above scans with, so the
|
|
449
|
+
* published figure and the gate verdict cannot disagree — which is the whole
|
|
450
|
+
* reason it lives here rather than in a script of its own.
|
|
451
|
+
*/
|
|
452
|
+
export interface SkillLinkCensus {
|
|
453
|
+
schema_version: 1;
|
|
454
|
+
_comment: string;
|
|
455
|
+
commit: string | null;
|
|
456
|
+
/** `](../<slug>/SKILL.md` occurrences — the Reproduction B.1 grep. */
|
|
457
|
+
total_links_skill_md: number;
|
|
458
|
+
/** `](../<slug>/` occurrences — B.1 widened to bare directory targets. */
|
|
459
|
+
total_links_any_dir: number;
|
|
460
|
+
/**
|
|
461
|
+
* Links the GATE sees (`LINK_RE`). Lower than `total_links_skill_md`
|
|
462
|
+
* because LINK_RE additionally requires slug-shaped link TEXT — and, in
|
|
463
|
+
* the other direction, its href is unconstrained, so it catches targets
|
|
464
|
+
* the B.1 character class cannot. Both are recorded because the gap is
|
|
465
|
+
* where a dead link hid: `../create-pr:description-only/SKILL.md` carries
|
|
466
|
+
* a colon, so `[a-z0-9-]*` never matched it and the drafted census
|
|
467
|
+
* reported 14 dead links where the gate finds 16.
|
|
468
|
+
*/
|
|
469
|
+
links_matched_by_gate: number;
|
|
470
|
+
files_with_skill_md_link: number;
|
|
471
|
+
files_with_any_relative_link: number;
|
|
472
|
+
skills_total: number;
|
|
473
|
+
dead_links: DeadLinkRecord[];
|
|
474
|
+
/** Links whose target slug is absent from the linker's `requires_skills:`. */
|
|
475
|
+
undeclared_in_requires: number;
|
|
476
|
+
skills_declaring_requires: number;
|
|
477
|
+
scoped_survivors: number;
|
|
478
|
+
scoped_pruned: number;
|
|
479
|
+
scoped_dangles: ScopedDangleRecord[];
|
|
480
|
+
survivors_with_dangle: number;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// ── Scoped-dangle follow rate, on a guarded instrument ──────────────────────
|
|
484
|
+
// Phase 4 of road-to-skill-link-integrity-and-manifest-sync. 24 links from 17
|
|
485
|
+
// surviving skills point at a skill that `projection.mode: scoped` prunes. Is
|
|
486
|
+
// that a defect, or behaviour the consumer opted into? The roadmap does not
|
|
487
|
+
// pick; it records the metric that decides, and it guards the instrument FIRST
|
|
488
|
+
// because an unguarded "zero read attempts" is not a null — it is a stopped
|
|
489
|
+
// clock reported as evidence.
|
|
490
|
+
|
|
491
|
+
/** Days back from `now` a record must fall in to count the instrument live. */
|
|
492
|
+
export const DANGLE_WINDOW_DAYS = 30;
|
|
493
|
+
|
|
494
|
+
/** Verdict on whether the usage store can answer anything at all. */
|
|
495
|
+
export interface InstrumentVerdict {
|
|
496
|
+
instrument_live: boolean;
|
|
497
|
+
/** Why it is dead. Empty when live. */
|
|
498
|
+
reason: string;
|
|
499
|
+
/** Records parsed. 0 for a missing file. */
|
|
500
|
+
records: number;
|
|
501
|
+
/** Newest record timestamp, ISO, or null. */
|
|
502
|
+
newest: string | null;
|
|
503
|
+
/** Distinct `kind` values observed, sorted — see the follow-rate note. */
|
|
504
|
+
kinds: string[];
|
|
505
|
+
window_start: string;
|
|
506
|
+
window_end: string;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Guard the instrument before any count is read.
|
|
511
|
+
*
|
|
512
|
+
* A MISSING FILE AND A STALE FILE ARE BOTH DEAD, and both had to be handled
|
|
513
|
+
* because both occur: the store is gitignored and machine-local, so it is
|
|
514
|
+
* ABSENT in a fresh checkout or worktree, and in the parent checkout it holds
|
|
515
|
+
* 181 records that all carry the SAME timestamp from a single session on
|
|
516
|
+
* 2026-05-15. Either way a naive count returns zero and a phase closes as
|
|
517
|
+
* "harmless" on a measurement that never happened.
|
|
518
|
+
*/
|
|
519
|
+
export function instrument_verdict(
|
|
520
|
+
storePath: string,
|
|
521
|
+
now: Date = asOf(),
|
|
522
|
+
windowDays: number = DANGLE_WINDOW_DAYS,
|
|
523
|
+
): InstrumentVerdict {
|
|
524
|
+
const end = now;
|
|
525
|
+
const start = new Date(end.getTime() - windowDays * 86_400_000);
|
|
526
|
+
const base = {
|
|
527
|
+
window_start: start.toISOString(),
|
|
528
|
+
window_end: end.toISOString(),
|
|
529
|
+
};
|
|
530
|
+
let text: string;
|
|
531
|
+
try {
|
|
532
|
+
text = fs.readFileSync(storePath, 'utf-8');
|
|
533
|
+
} catch {
|
|
534
|
+
return {
|
|
535
|
+
instrument_live: false,
|
|
536
|
+
// Repo-relative: this string lands in a COMMITTED metric row, and
|
|
537
|
+
// an absolute path would bake one machine's home directory into it.
|
|
538
|
+
reason:
|
|
539
|
+
`instrument absent — ${_isUnder(storePath, REPO) ? _relTo(storePath, REPO) : path.basename(storePath)}` +
|
|
540
|
+
' does not exist (the store is gitignored and machine-local)',
|
|
541
|
+
records: 0,
|
|
542
|
+
newest: null,
|
|
543
|
+
kinds: [],
|
|
544
|
+
...base,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
let records = 0;
|
|
548
|
+
let newest: number | null = null;
|
|
549
|
+
let inWindow = 0;
|
|
550
|
+
const kinds = new Set<string>();
|
|
551
|
+
for (const line of text.split('\n')) {
|
|
552
|
+
if (line.trim() === '') continue;
|
|
553
|
+
let row: { ts?: unknown; kind?: unknown; slug?: unknown };
|
|
554
|
+
try {
|
|
555
|
+
row = JSON.parse(line) as typeof row;
|
|
556
|
+
} catch {
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
records += 1;
|
|
560
|
+
if (typeof row.kind === 'string') kinds.add(row.kind);
|
|
561
|
+
const t = typeof row.ts === 'string' ? Date.parse(row.ts) : NaN;
|
|
562
|
+
if (!Number.isNaN(t)) {
|
|
563
|
+
if (newest === null || t > newest) newest = t;
|
|
564
|
+
if (t >= start.getTime() && t <= end.getTime()) inWindow += 1;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const newestIso = newest === null ? null : new Date(newest).toISOString();
|
|
568
|
+
if (inWindow === 0) {
|
|
569
|
+
const age =
|
|
570
|
+
newest === null
|
|
571
|
+
? 'no parseable timestamp in any record'
|
|
572
|
+
: `newest record is ${String(Math.floor((end.getTime() - newest) / 86_400_000))} days old (${String(newestIso)})`;
|
|
573
|
+
return {
|
|
574
|
+
instrument_live: false,
|
|
575
|
+
reason: `instrument dead — ${age}; no record inside the ${String(windowDays)}-day window`,
|
|
576
|
+
records,
|
|
577
|
+
newest: newestIso,
|
|
578
|
+
kinds: [...kinds].sort(),
|
|
579
|
+
...base,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
return {
|
|
583
|
+
instrument_live: true,
|
|
584
|
+
reason: '',
|
|
585
|
+
records,
|
|
586
|
+
newest: newestIso,
|
|
587
|
+
kinds: [...kinds].sort(),
|
|
588
|
+
...base,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
/** Event kinds that would represent an agent actually following a link. */
|
|
593
|
+
export const FOLLOW_KINDS: ReadonlySet<string> = new Set(['read', 'read_attempt', 'follow']);
|
|
594
|
+
|
|
595
|
+
/** The Phase-4 metric row. `attempts` is null whenever it was not measured. */
|
|
596
|
+
export interface ScopedDangleFollowRate {
|
|
597
|
+
schema_version: 1;
|
|
598
|
+
_comment: string;
|
|
599
|
+
commit: string | null;
|
|
600
|
+
window_start: string;
|
|
601
|
+
window_end: string;
|
|
602
|
+
instrument: string;
|
|
603
|
+
instrument_live: boolean;
|
|
604
|
+
instrument_reason: string;
|
|
605
|
+
instrument_records: number;
|
|
606
|
+
instrument_newest: string | null;
|
|
607
|
+
instrument_kinds: string[];
|
|
608
|
+
/** Read attempts against a pruned slug. null = not measured. */
|
|
609
|
+
attempts: number | null;
|
|
610
|
+
/** Distinct pruned slugs with at least one attempt. null = not measured. */
|
|
611
|
+
pruned_targets_hit: string[] | null;
|
|
612
|
+
scoped_dangles: number;
|
|
613
|
+
survivors_with_dangle: number;
|
|
614
|
+
null_branch: string;
|
|
615
|
+
measured_branch: string;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/** Build the Phase-4 row. Guard first; count only if the guard passes. */
|
|
619
|
+
export function scoped_dangle_follow_rate(
|
|
620
|
+
skills_dir: string,
|
|
621
|
+
package_root: string,
|
|
622
|
+
storePath: string,
|
|
623
|
+
commit: string | null = null,
|
|
624
|
+
now: Date = asOf(),
|
|
625
|
+
): ScopedDangleFollowRate {
|
|
626
|
+
const census = skill_link_census(skills_dir, package_root, commit);
|
|
627
|
+
const v = instrument_verdict(storePath, now);
|
|
628
|
+
let attempts: number | null = null;
|
|
629
|
+
let hit: string[] | null = null;
|
|
630
|
+
if (v.instrument_live) {
|
|
631
|
+
// Same predicate as the census above, by construction: the pruned set
|
|
632
|
+
// comes from the census, which uses `is_pruned_under_scoped` — the
|
|
633
|
+
// function `install.ts` itself applies. Two definitions of "pruned"
|
|
634
|
+
// free to disagree is the defect, not the fix.
|
|
635
|
+
const prunedSlugs = new Set(census.scoped_dangles.map((d) => d.target));
|
|
636
|
+
const seen = new Set<string>();
|
|
637
|
+
let n = 0;
|
|
638
|
+
for (const line of fs.readFileSync(storePath, 'utf-8').split('\n')) {
|
|
639
|
+
if (line.trim() === '') continue;
|
|
640
|
+
let row: { ts?: unknown; kind?: unknown; slug?: unknown };
|
|
641
|
+
try {
|
|
642
|
+
row = JSON.parse(line) as typeof row;
|
|
643
|
+
} catch {
|
|
644
|
+
continue;
|
|
645
|
+
}
|
|
646
|
+
const t = typeof row.ts === 'string' ? Date.parse(row.ts) : NaN;
|
|
647
|
+
if (Number.isNaN(t) || t < Date.parse(v.window_start) || t > Date.parse(v.window_end)) {
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
if (typeof row.kind !== 'string' || !FOLLOW_KINDS.has(row.kind)) continue;
|
|
651
|
+
if (typeof row.slug !== 'string' || !prunedSlugs.has(row.slug)) continue;
|
|
652
|
+
n += 1;
|
|
653
|
+
seen.add(row.slug);
|
|
654
|
+
}
|
|
655
|
+
attempts = n;
|
|
656
|
+
hit = [...seen].sort();
|
|
657
|
+
}
|
|
658
|
+
return {
|
|
659
|
+
schema_version: 1,
|
|
660
|
+
_comment: DANGLE_COMMENT,
|
|
661
|
+
commit,
|
|
662
|
+
window_start: v.window_start,
|
|
663
|
+
window_end: v.window_end,
|
|
664
|
+
instrument: 'agents/runtime/metrics/skill-usage.jsonl',
|
|
665
|
+
instrument_live: v.instrument_live,
|
|
666
|
+
instrument_reason: v.reason,
|
|
667
|
+
instrument_records: v.records,
|
|
668
|
+
instrument_newest: v.newest,
|
|
669
|
+
instrument_kinds: v.kinds,
|
|
670
|
+
attempts,
|
|
671
|
+
pruned_targets_hit: hit,
|
|
672
|
+
scoped_dangles: census.scoped_dangles.length,
|
|
673
|
+
survivors_with_dangle: census.survivors_with_dangle,
|
|
674
|
+
null_branch:
|
|
675
|
+
'Zero attempts over a LIVE window closes the question as a published ' +
|
|
676
|
+
'null and the dangling links stay: a link to a pruned sibling is then ' +
|
|
677
|
+
'behaviour the consumer opted into by choosing `projection.mode: ' +
|
|
678
|
+
'scoped`, not a defect. A null over a DEAD window closes nothing and ' +
|
|
679
|
+
'is not permitted to be reported as one — that is what the guard is ' +
|
|
680
|
+
'for, and why `attempts` is null rather than 0 when the guard fails.',
|
|
681
|
+
measured_branch:
|
|
682
|
+
'A nonzero count promotes the fix: rewrite each dangling link in the ' +
|
|
683
|
+
'PROJECTED SKILL.md to name the slug and its pack instead of linking ' +
|
|
684
|
+
'it — source tree untouched, projection honest about itself, using ' +
|
|
685
|
+
'the same `is_pruned_under_scoped` predicate this counter uses so the ' +
|
|
686
|
+
'two cannot disagree.',
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/** Provenance carried inside the Phase-4 row. */
|
|
691
|
+
const DANGLE_COMMENT =
|
|
692
|
+
'Written by `./scripts-run src/scripts/lint_handoffs --scoped-dangle-json`. Do ' +
|
|
693
|
+
'not hand-edit — regenerate it. The instrument is guarded BEFORE any count is ' +
|
|
694
|
+
'read: `attempts` and `pruned_targets_hit` are null, never 0, whenever ' +
|
|
695
|
+
'`instrument_live` is false, because a stopped clock reporting zero is not a ' +
|
|
696
|
+
'null. Read `instrument_reason` first. NOTE ON KINDS: the store has only ever ' +
|
|
697
|
+
'recorded `kind: "exposure"`, and no event in FOLLOW_KINDS (`read`, ' +
|
|
698
|
+
'`read_attempt`, `follow`) is emitted anywhere in the tree — so even a LIVE ' +
|
|
699
|
+
'window could not answer this question today. Emitting a follow event is the ' +
|
|
700
|
+
'prerequisite this measurement is blocked on, and it is not in this roadmap.';
|
|
701
|
+
|
|
702
|
+
/** Provenance carried inside the row, so a reader never has to find this file. */
|
|
703
|
+
const CENSUS_COMMENT =
|
|
704
|
+
'Written by `./scripts-run src/scripts/lint_handoffs --census-json`. Do not ' +
|
|
705
|
+
'hand-edit — regenerate it. The collector is `collect_body_links`, the same ' +
|
|
706
|
+
'one the gate scans with, so the published figure and the gate verdict ' +
|
|
707
|
+
'cannot drift apart. `dead_links` uses the gate predicate (a target absent ' +
|
|
708
|
+
'from the live SKILL.md set), NOT the Reproduction B.1 grep — see ' +
|
|
709
|
+
'`links_matched_by_gate` for why the two differ and which dead link the ' +
|
|
710
|
+
'grep was blind to.';
|
|
711
|
+
|
|
712
|
+
/** `](../<slug>/SKILL.md` — the Reproduction B.1 pattern, verbatim. */
|
|
713
|
+
const B1_SKILL_MD_RE = /\]\(\.\.\/[a-z0-9-]*\/SKILL\.md/g;
|
|
714
|
+
/** `](../<slug>/` — B.1 widened to bare directory targets. */
|
|
715
|
+
const B1_ANY_DIR_RE = /\]\(\.\.\/[a-z0-9-]*\//g;
|
|
716
|
+
/** `](../` — the "carries any relative link" file counter from B.1. */
|
|
717
|
+
const B1_ANY_REL_RE = /\]\(\.\.\//;
|
|
718
|
+
|
|
719
|
+
/** Build the census over a skills root. `commit` is passed in, never guessed. */
|
|
720
|
+
export function skill_link_census(
|
|
721
|
+
skills_dir: string,
|
|
722
|
+
package_root: string,
|
|
723
|
+
commit: string | null = null,
|
|
724
|
+
): SkillLinkCensus {
|
|
725
|
+
const files = _rglobSkillMd(skills_dir);
|
|
726
|
+
const live = new Set(files.map(_resolve));
|
|
727
|
+
|
|
728
|
+
let total_links_skill_md = 0;
|
|
729
|
+
let total_links_any_dir = 0;
|
|
730
|
+
let files_with_skill_md_link = 0;
|
|
731
|
+
let files_with_any_relative_link = 0;
|
|
732
|
+
let skills_declaring_requires = 0;
|
|
733
|
+
const requires_by_file = new Map<string, Set<string>>();
|
|
734
|
+
|
|
735
|
+
for (const f of files) {
|
|
736
|
+
const text = fs.readFileSync(f, 'utf-8');
|
|
737
|
+
const a = text.match(B1_SKILL_MD_RE) ?? [];
|
|
738
|
+
const b = text.match(B1_ANY_DIR_RE) ?? [];
|
|
739
|
+
total_links_skill_md += a.length;
|
|
740
|
+
total_links_any_dir += b.length;
|
|
741
|
+
if (a.length > 0) files_with_skill_md_link += 1;
|
|
742
|
+
if (B1_ANY_REL_RE.test(text)) files_with_any_relative_link += 1;
|
|
743
|
+
const req = parse_frontmatter_list(text, 'requires_skills');
|
|
744
|
+
if (text.includes('requires_skills')) skills_declaring_requires += 1;
|
|
745
|
+
requires_by_file.set(_resolve(f), new Set(req));
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
const links = collect_body_links(skills_dir);
|
|
749
|
+
const dead_links: DeadLinkRecord[] = [];
|
|
750
|
+
let undeclared_in_requires = 0;
|
|
751
|
+
for (const bl of links) {
|
|
752
|
+
if (!live.has(bl.target)) {
|
|
753
|
+
dead_links.push({
|
|
754
|
+
file: _relTo(bl.file, package_root),
|
|
755
|
+
line: bl.line,
|
|
756
|
+
target: bl.link,
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
if (!(requires_by_file.get(bl.file) ?? new Set()).has(bl.slug)) {
|
|
760
|
+
undeclared_in_requires += 1;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
// Scoped projection, with the installer's own predicate — never a second
|
|
765
|
+
// definition of "pruned" free to disagree with the one install.ts applies.
|
|
766
|
+
const active = compute_active_pack_ids(load_packs_registry(package_root), []);
|
|
767
|
+
let scoped_survivors = 0;
|
|
768
|
+
let scoped_pruned = 0;
|
|
769
|
+
const pruned_paths = new Set<string>();
|
|
770
|
+
for (const f of files) {
|
|
771
|
+
if (is_pruned_under_scoped(f, active)) {
|
|
772
|
+
scoped_pruned += 1;
|
|
773
|
+
pruned_paths.add(_resolve(f));
|
|
774
|
+
} else {
|
|
775
|
+
scoped_survivors += 1;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const scoped_dangles: ScopedDangleRecord[] = [];
|
|
779
|
+
const survivors = new Set<string>();
|
|
780
|
+
for (const bl of links) {
|
|
781
|
+
if (pruned_paths.has(bl.file) || !pruned_paths.has(bl.target)) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
scoped_dangles.push({
|
|
785
|
+
survivor: path.basename(path.dirname(bl.file)),
|
|
786
|
+
target: path.basename(path.dirname(bl.target)),
|
|
787
|
+
});
|
|
788
|
+
survivors.add(bl.file);
|
|
789
|
+
}
|
|
790
|
+
scoped_dangles.sort((x, y) =>
|
|
791
|
+
x.survivor === y.survivor
|
|
792
|
+
? x.target.localeCompare(y.target)
|
|
793
|
+
: x.survivor.localeCompare(y.survivor),
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
return {
|
|
797
|
+
schema_version: 1,
|
|
798
|
+
_comment: CENSUS_COMMENT,
|
|
799
|
+
commit,
|
|
800
|
+
total_links_skill_md,
|
|
801
|
+
total_links_any_dir,
|
|
802
|
+
links_matched_by_gate: links.length,
|
|
803
|
+
files_with_skill_md_link,
|
|
804
|
+
files_with_any_relative_link,
|
|
805
|
+
skills_total: files.length,
|
|
806
|
+
dead_links,
|
|
807
|
+
undeclared_in_requires,
|
|
808
|
+
skills_declaring_requires,
|
|
809
|
+
scoped_survivors,
|
|
810
|
+
scoped_pruned,
|
|
811
|
+
scoped_dangles,
|
|
812
|
+
survivors_with_dangle: survivors.size,
|
|
813
|
+
};
|
|
814
|
+
}
|
|
815
|
+
|
|
327
816
|
/** Python repr() of a string (single-quote preference). */
|
|
328
817
|
function _pyRepr(s: string): string {
|
|
329
818
|
if (s.includes("'") && !s.includes('"')) {
|
|
@@ -445,6 +934,15 @@ export function validate_handoff_open_questions(text: string): string | null {
|
|
|
445
934
|
return null;
|
|
446
935
|
}
|
|
447
936
|
|
|
937
|
+
/** `git rev-parse HEAD`, or null outside a repository. */
|
|
938
|
+
function _headCommit(): string | null {
|
|
939
|
+
try {
|
|
940
|
+
return execFileSync('git', ['rev-parse', 'HEAD'], { cwd: REPO, encoding: 'utf-8' }).trim();
|
|
941
|
+
} catch {
|
|
942
|
+
return null;
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
|
|
448
946
|
export function main(argv?: readonly string[]): number {
|
|
449
947
|
let skills_dir = SKILLS_DIR;
|
|
450
948
|
// Positional-only: flags are NOT paths. Before this filter the CI invocation
|
|
@@ -453,7 +951,33 @@ export function main(argv?: readonly string[]): number {
|
|
|
453
951
|
// red under the exact argv CI runs while green when probed bare. That is the
|
|
454
952
|
// inverse of this suite's dead-scope defect and the reason gate-coverage
|
|
455
953
|
// rule 2 pins CI-identical argv.
|
|
456
|
-
const
|
|
954
|
+
const rawArgs = argv ?? process.argv.slice(2);
|
|
955
|
+
const args = rawArgs.filter((a) => !String(a).startsWith('-'));
|
|
956
|
+
// Census mode. An added mode on the gate's own entry point rather than a
|
|
957
|
+
// new script: the census MUST be produced by the collector the gate scans
|
|
958
|
+
// with, or the published figure and the verdict are free to drift, which
|
|
959
|
+
// is the class of defect this whole roadmap is about.
|
|
960
|
+
if (rawArgs.includes('--scoped-dangle-json')) {
|
|
961
|
+
if (args.length > 0) {
|
|
962
|
+
skills_dir = _resolve(args[0] as string);
|
|
963
|
+
}
|
|
964
|
+
const row = scoped_dangle_follow_rate(
|
|
965
|
+
skills_dir,
|
|
966
|
+
REPO,
|
|
967
|
+
path.join(REPO, 'agents', 'runtime', 'metrics', 'skill-usage.jsonl'),
|
|
968
|
+
_headCommit(),
|
|
969
|
+
);
|
|
970
|
+
process.stdout.write(JSON.stringify(row, null, 2) + '\n');
|
|
971
|
+
return 0;
|
|
972
|
+
}
|
|
973
|
+
if (rawArgs.includes('--census-json')) {
|
|
974
|
+
if (args.length > 0) {
|
|
975
|
+
skills_dir = _resolve(args[0] as string);
|
|
976
|
+
}
|
|
977
|
+
const census = skill_link_census(skills_dir, REPO, _headCommit());
|
|
978
|
+
process.stdout.write(JSON.stringify(census, null, 2) + '\n');
|
|
979
|
+
return 0;
|
|
980
|
+
}
|
|
457
981
|
if (args.length > 0 && String(args[0]).endsWith('HANDOFF.md')) {
|
|
458
982
|
// artifact mode — validate the workflow-resume file's required fields
|
|
459
983
|
const text = fs.readFileSync(_resolve(args[0] as string), 'utf-8');
|