@event4u/agent-config 16.1.0 → 16.3.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 +98 -257
- package/README.md +14 -12
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/feature/plan.md +1 -1
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +1 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/process-full.md +5 -5
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -0
- package/dist/agent-src/rules/broken-access-control.md +1 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
- package/dist/agent-src/rules/context-hygiene.md +1 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
- package/dist/agent-src/rules/design-fidelity.md +13 -16
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/domain-safety-retention.md +1 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +1 -0
- package/dist/agent-src/rules/git-history-discipline.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -2
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +14 -17
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
- package/dist/agent-src/rules/session-canary.md +8 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
- package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
- package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1862 -809
- package/dist/cli-delegate/cmd_doctor.js +13 -6
- package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
- package/dist/cli-delegate/cmd_explain.js +5 -5
- package/dist/cli-delegate/cmd_export.js +11 -8
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +10 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +11 -8
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +8 -6
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +4 -3
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +11 -8
- package/dist/cli-delegate/cmd_uninstall.js +11 -8
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +11 -8
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +5 -5
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +132 -80
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +7972 -5186
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2226 -1055
- package/dist/install/installThinLayer.js +261 -0
- package/dist/install/installedRuleLinks.js +176 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +40913 -34865
- package/dist/scripts/_lib/agent_settings.js +11 -0
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/installed_layer.js +378 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/thin_rules.js +458 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/schemas/settings.js +29 -0
- package/dist/shared/skillRanking.js +74 -13
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +10 -6
- package/package.json +4 -4
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +91 -22
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +327 -3
- package/src/config/gate-violation-baselines.json +21 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +8 -3
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +38 -3
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +99 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +312 -0
- package/src/install/installedRuleLinks.ts +220 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_doctor.ts +28 -90
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
- package/src/scripts/_lib/graph_feeder_record.ts +214 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +538 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/thin_rules.ts +540 -0
- package/src/scripts/_lib/touched_file_quality.ts +434 -0
- package/src/scripts/_lib/turn_end_refusals.ts +600 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +1 -1
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +573 -25
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +621 -22
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +138 -7
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +89 -9
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/compile_hook_manifest.ts +90 -14
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +192 -14
- package/src/scripts/hooks/block_config_weakening.ts +294 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +77 -71
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +129 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +146 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +765 -68
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +70 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +75 -189
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +108 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_rule_law_section.ts +517 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/lint_workflow_security.ts +161 -5
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_skill_ranker_confusion.ts +442 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +7 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +421 -17
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/trigger_eval_grandfather.json +0 -7
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +41 -0
- package/src/shared/skillRanking.ts +117 -11
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
- package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
|
@@ -22,8 +22,22 @@
|
|
|
22
22
|
* adding corpus cases lowers coverage while every count rises, and a count
|
|
23
23
|
* ratchet would call that progress.
|
|
24
24
|
*
|
|
25
|
-
*
|
|
25
|
+
* THE THIRD SCOPE, ADDED BY `road-to-a-menu-whose-precision-is-measured` 2.1.
|
|
26
|
+
* The two ratios above are estate-wide and therefore patient: a contributor can
|
|
27
|
+
* edit a corpus-less skill for a year without either ratio moving, because the
|
|
28
|
+
* numerator and the denominator both stay put. `touched` closes that on the one
|
|
29
|
+
* occasion where the cost of writing a corpus is lowest — the author already has
|
|
30
|
+
* the skill open. A skill CHANGED by the diff must carry `evals/triggers.json`.
|
|
31
|
+
*
|
|
32
|
+
* It is deliberately not a fourth ratio. A ratio over a diff is meaningless when
|
|
33
|
+
* the diff touches one skill, and the obligation here is per skill rather than
|
|
34
|
+
* per estate: one uncovered touched skill is one failure, whatever the estate
|
|
35
|
+
* average says.
|
|
36
|
+
*
|
|
37
|
+
* Exit codes: 0 = at or above seed and no uncovered touched skill · 1 = a scope
|
|
38
|
+
* fell or a touched skill has no corpus · 2 = dead scope.
|
|
26
39
|
*/
|
|
40
|
+
import { spawnSync } from 'node:child_process';
|
|
27
41
|
import * as fs from 'node:fs';
|
|
28
42
|
import * as os from 'node:os';
|
|
29
43
|
import * as path from 'node:path';
|
|
@@ -32,6 +46,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
32
46
|
|
|
33
47
|
import { GateLedger } from './_lib/gate_ledger.js';
|
|
34
48
|
import { type SelfTestCase, runGateCli, runSelfTest } from './_lib/gate_self_test.js';
|
|
49
|
+
import { gitEnv } from './_lib/git_env.js';
|
|
35
50
|
import { DeadScopeError, reportScanned } from './_lib/scan_scope.js';
|
|
36
51
|
|
|
37
52
|
const HERE = fileURLToPath(import.meta.url);
|
|
@@ -114,6 +129,73 @@ export function measureSkills(root: string): ScopeReading {
|
|
|
114
129
|
return { scope: 'skills', cases, units, ratio: units === 0 ? 0 : cases / units };
|
|
115
130
|
}
|
|
116
131
|
|
|
132
|
+
/**
|
|
133
|
+
* The skills carrying no `evals/triggers.json`, grouped by the packs they declare.
|
|
134
|
+
*
|
|
135
|
+
* `road-to-a-menu-whose-precision-is-measured` 2.1's second half. The ratio above
|
|
136
|
+
* says HOW MANY are uncovered; this says WHICH, in the grouping a contributor
|
|
137
|
+
* can act on — a pack owner can take their own column without reading the other
|
|
138
|
+
* twenty-six. A skill declaring several packs appears under each, so the column
|
|
139
|
+
* sums CAN exceed the total. Measured 2026-09-29 they do not: no skill in this
|
|
140
|
+
* tree declares more than one pack, so the columns sum to exactly the total.
|
|
141
|
+
* Stated as a property of the renderer rather than of today's tree, because a
|
|
142
|
+
* reader checking the arithmetic against the table needs to know which it is.
|
|
143
|
+
*
|
|
144
|
+
* A skill declaring no pack lands under `(no pack declared)` rather than being
|
|
145
|
+
* dropped, because a silently omitted skill is exactly the coverage hole this
|
|
146
|
+
* census exists to make visible.
|
|
147
|
+
*/
|
|
148
|
+
export function uncoveredByPack(root: string): Record<string, string[]> {
|
|
149
|
+
const base = path.join(root, 'src', 'skills');
|
|
150
|
+
const out: Record<string, string[]> = {};
|
|
151
|
+
for (const e of fs.readdirSync(base, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
152
|
+
if (!e.isDirectory()) continue;
|
|
153
|
+
const skillMd = path.join(base, e.name, 'SKILL.md');
|
|
154
|
+
if (!fs.existsSync(skillMd)) continue;
|
|
155
|
+
if (fs.existsSync(path.join(base, e.name, 'evals', 'triggers.json'))) continue;
|
|
156
|
+
const packs = readPacks(skillMd);
|
|
157
|
+
for (const p of packs.length > 0 ? packs : ['(no pack declared)']) {
|
|
158
|
+
(out[p] ??= []).push(e.name);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The `packs:` list from a SKILL.md frontmatter block. Line-oriented on purpose. */
|
|
165
|
+
function readPacks(skillMd: string): string[] {
|
|
166
|
+
const text = fs.readFileSync(skillMd, 'utf-8');
|
|
167
|
+
if (!text.startsWith('---')) return [];
|
|
168
|
+
const end = text.indexOf('\n---', 3);
|
|
169
|
+
if (end === -1) return [];
|
|
170
|
+
const lines = text.slice(3, end).split('\n');
|
|
171
|
+
const at = lines.findIndex((l) => /^packs:/u.test(l));
|
|
172
|
+
if (at === -1) return [];
|
|
173
|
+
const out: string[] = [];
|
|
174
|
+
for (let i = at + 1; i < lines.length; i += 1) {
|
|
175
|
+
const m = /^\s+-\s+(\S+)\s*$/u.exec(lines[i] as string);
|
|
176
|
+
if (!m) break;
|
|
177
|
+
out.push(m[1] as string);
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** Render the census as the markdown section `docs/SKILL_CENSUS.md` carries. */
|
|
183
|
+
export function renderUncoveredCensus(root: string): string[] {
|
|
184
|
+
const byPack = uncoveredByPack(root);
|
|
185
|
+
const reading = measureSkills(root);
|
|
186
|
+
const total = reading.units - reading.cases;
|
|
187
|
+
const L: string[] = [];
|
|
188
|
+
L.push(`Skills with no \`evals/triggers.json\`: **${String(total)} of ${String(reading.units)}**.`);
|
|
189
|
+
L.push('');
|
|
190
|
+
L.push('| Pack | Uncovered | Skills |');
|
|
191
|
+
L.push('|---|---:|---|');
|
|
192
|
+
for (const pack of Object.keys(byPack).sort()) {
|
|
193
|
+
const names = byPack[pack] as string[];
|
|
194
|
+
L.push(`| \`${pack}\` | ${String(names.length)} | ${names.map((n) => `\`${n}\``).join(', ')} |`);
|
|
195
|
+
}
|
|
196
|
+
return L;
|
|
197
|
+
}
|
|
198
|
+
|
|
117
199
|
export function readSeed(root: string): Seed {
|
|
118
200
|
const p = path.join(root, SEED_REL);
|
|
119
201
|
let raw: string;
|
|
@@ -136,10 +218,125 @@ export function readSeed(root: string): Seed {
|
|
|
136
218
|
return { rules: s.rules, skills: s.skills };
|
|
137
219
|
}
|
|
138
220
|
|
|
221
|
+
/** The ref the touched-skill scope diffs against when `--base` is not given. */
|
|
222
|
+
export const TOUCHED_BASE_DEFAULT = 'origin/main';
|
|
223
|
+
|
|
224
|
+
/** One git invocation: the lines it printed, and whether it actually ran. */
|
|
225
|
+
interface GitRead {
|
|
226
|
+
lines: string[];
|
|
227
|
+
ok: boolean;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function gitLines(root: string, args: string[]): GitRead {
|
|
231
|
+
const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env: gitEnv() });
|
|
232
|
+
return { lines: r.status === 0 ? r.stdout.split('\n') : [], ok: r.status === 0 };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Paths the BRANCH added or changed against `base`.
|
|
237
|
+
*
|
|
238
|
+
* `ok` is separate from the path list and that separation is the whole point: a
|
|
239
|
+
* failed `git diff` and a diff that changed nothing both print zero lines, and
|
|
240
|
+
* reading the first as the second is how a gate reports green over a corpus it
|
|
241
|
+
* never saw. `ok: false` means the branch arm was not measured, never that it
|
|
242
|
+
* was measured and found empty.
|
|
243
|
+
*
|
|
244
|
+
* `ACMR` and not `D`: a DELETED skill cannot be asked for a corpus, and reading
|
|
245
|
+
* a deletion as an uncovered touch would make removing a skill impossible.
|
|
246
|
+
*/
|
|
247
|
+
export function branchPaths(root: string, base: string): GitRead {
|
|
248
|
+
return gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR', `${base}...HEAD`]);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Paths changed in this checkout with no base ref involved — dirty, staged and
|
|
253
|
+
* untracked.
|
|
254
|
+
*
|
|
255
|
+
* Measured even when the branch arm cannot be: these three need no merge base,
|
|
256
|
+
* so discarding them because a base ref is unresolvable throws away the
|
|
257
|
+
* coverage that WAS available. Untracked is included so a brand-new skill
|
|
258
|
+
* counts as touched before its first commit.
|
|
259
|
+
*/
|
|
260
|
+
export function localPaths(root: string): string[] {
|
|
261
|
+
return [
|
|
262
|
+
...new Set([
|
|
263
|
+
...gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR']).lines,
|
|
264
|
+
...gitLines(root, ['diff', '--name-only', '--diff-filter=ACMR', '--cached']).lines,
|
|
265
|
+
...gitLines(root, ['ls-files', '--others', '--exclude-standard']).lines,
|
|
266
|
+
]),
|
|
267
|
+
].filter((f) => f.trim().length > 0);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** Every path the diff touched, branch arm included when it could be read. */
|
|
271
|
+
export function changedPaths(root: string, base: string): string[] {
|
|
272
|
+
const branch = baseUsable(root, base) ? branchPaths(root, base).lines : [];
|
|
273
|
+
return [...new Set([...branch, ...localPaths(root)])].filter((f) => f.trim().length > 0);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* True when `base` can actually be diffed against, not merely named.
|
|
278
|
+
*
|
|
279
|
+
* Ref existence is NOT the question, and assuming it was is the defect this
|
|
280
|
+
* function is named after. In a shallow clone `git rev-parse origin/main` exits
|
|
281
|
+
* 0 while `git diff origin/main...HEAD` exits 128 with `no merge base`, because
|
|
282
|
+
* the common ancestor was never fetched. The only honest probe is the diff
|
|
283
|
+
* itself, so that is what this runs.
|
|
284
|
+
*/
|
|
285
|
+
export function baseUsable(root: string, base: string): boolean {
|
|
286
|
+
if (!gitLines(root, ['rev-parse', '--verify', '--quiet', `${base}^{commit}`]).ok) return false;
|
|
287
|
+
return branchPaths(root, base).ok;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Retained name for the ref-existence half alone. Prefer {@link baseUsable}. */
|
|
291
|
+
export function baseResolvable(root: string, base: string): boolean {
|
|
292
|
+
return gitLines(root, ['rev-parse', '--verify', '--quiet', `${base}^{commit}`]).ok;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export interface TouchedReading {
|
|
296
|
+
/** Skills the diff changed, sorted. */
|
|
297
|
+
touched: string[];
|
|
298
|
+
/** Of those, the ones with no `evals/triggers.json`, sorted. */
|
|
299
|
+
uncovered: string[];
|
|
300
|
+
/**
|
|
301
|
+
* False when the BRANCH arm could not be read — an unresolvable base, or a
|
|
302
|
+
* shallow clone with no merge base. The local arms below are still measured
|
|
303
|
+
* and can still fail the run; `measured: false` narrows what the run may
|
|
304
|
+
* CLAIM, it does not switch the scope off.
|
|
305
|
+
*/
|
|
306
|
+
measured: boolean;
|
|
307
|
+
base: string;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Which skills the diff touched, and which of those still have no corpus.
|
|
312
|
+
*
|
|
313
|
+
* An unresolvable base is NOT an empty touch set. A shallow clone, a detached
|
|
314
|
+
* build or a fresh worktree with no remote would otherwise report "nothing
|
|
315
|
+
* touched" and pass, which is the silent-green shape this repository's gates are
|
|
316
|
+
* written against. It reports `measured: false` and the caller says so out loud.
|
|
317
|
+
*/
|
|
318
|
+
export function measureTouchedSkills(root: string, base: string): TouchedReading {
|
|
319
|
+
const measured = baseUsable(root, base);
|
|
320
|
+
const rels = measured ? changedPaths(root, base) : localPaths(root);
|
|
321
|
+
const names = new Set<string>();
|
|
322
|
+
for (const rel of rels) {
|
|
323
|
+
const m = /^src\/skills\/([^/]+)\//.exec(rel.replace(/\\/g, '/'));
|
|
324
|
+
if (m?.[1] !== undefined) names.add(m[1]);
|
|
325
|
+
}
|
|
326
|
+
const touched = [...names].sort();
|
|
327
|
+
const uncovered = touched.filter(
|
|
328
|
+
(n) =>
|
|
329
|
+
fs.existsSync(path.join(root, 'src', 'skills', n, 'SKILL.md')) &&
|
|
330
|
+
!fs.existsSync(path.join(root, 'src', 'skills', n, 'evals', 'triggers.json')),
|
|
331
|
+
);
|
|
332
|
+
return { touched, uncovered, measured, base };
|
|
333
|
+
}
|
|
334
|
+
|
|
139
335
|
export interface Verdict {
|
|
140
336
|
readings: ScopeReading[];
|
|
141
337
|
seed: Seed;
|
|
142
338
|
fallen: Scope[];
|
|
339
|
+
touched: TouchedReading;
|
|
143
340
|
ledger: GateLedger;
|
|
144
341
|
}
|
|
145
342
|
|
|
@@ -163,14 +360,14 @@ export function r4(n: number): number {
|
|
|
163
360
|
return Math.round(n * 10_000) / 10_000;
|
|
164
361
|
}
|
|
165
362
|
|
|
166
|
-
export function evaluate(root = REPO_ROOT): Verdict {
|
|
363
|
+
export function evaluate(root = REPO_ROOT, base = TOUCHED_BASE_DEFAULT): Verdict {
|
|
167
364
|
const seed = readSeed(root);
|
|
168
365
|
const readings = [measureRules(root), measureSkills(root)];
|
|
169
366
|
// The ledger's target is the SCOPE, not the individual unit: this gate's
|
|
170
367
|
// verdict is per scope, so a ledger over 404 units would report 404 rows
|
|
171
368
|
// no one can act on while hiding which of the two actually fell.
|
|
172
369
|
const ledger = new GateLedger('check_routing_coverage');
|
|
173
|
-
ledger.plan(readings.map((r) => r.scope));
|
|
370
|
+
ledger.plan([...readings.map((r) => r.scope), 'touched']);
|
|
174
371
|
const fallen: Scope[] = [];
|
|
175
372
|
for (const r of readings) {
|
|
176
373
|
if (r4(r.ratio) < r4(seed[r.scope])) {
|
|
@@ -184,7 +381,18 @@ export function evaluate(root = REPO_ROOT): Verdict {
|
|
|
184
381
|
ledger.complete(r.scope);
|
|
185
382
|
}
|
|
186
383
|
}
|
|
187
|
-
|
|
384
|
+
const touched = measureTouchedSkills(root, base);
|
|
385
|
+
if (touched.uncovered.length > 0) {
|
|
386
|
+
ledger.fail('touched', `${String(touched.uncovered.length)} touched skill(s) with no corpus`);
|
|
387
|
+
} else if (!touched.measured) {
|
|
388
|
+
// A finding outranks the precondition: the local arms need no base ref,
|
|
389
|
+
// so an uncovered skill they found is a real failure even though the
|
|
390
|
+
// branch arm did not run. Only a CLEAN unmeasured run is a skip.
|
|
391
|
+
ledger.skip('touched', 'precondition_unmet');
|
|
392
|
+
} else {
|
|
393
|
+
ledger.complete('touched');
|
|
394
|
+
}
|
|
395
|
+
return { readings, seed, fallen, touched, ledger };
|
|
188
396
|
}
|
|
189
397
|
|
|
190
398
|
/**
|
|
@@ -231,12 +439,64 @@ function selfTestRoot(
|
|
|
231
439
|
return root;
|
|
232
440
|
}
|
|
233
441
|
|
|
442
|
+
/**
|
|
443
|
+
* Turn a self-test root into a git repository with a `base` commit, then apply
|
|
444
|
+
* `edit` as the working-tree change the gate will read as "this diff".
|
|
445
|
+
*
|
|
446
|
+
* A real repository rather than a stubbed diff: the touched scope's whole risk
|
|
447
|
+
* is that its git invocation resolves against the wrong tree (an inherited
|
|
448
|
+
* `GIT_DIR` does exactly that), and a stub would never see it.
|
|
449
|
+
*/
|
|
450
|
+
function gitBaseline(root: string, edit: (root: string) => void): string {
|
|
451
|
+
const env = gitEnv();
|
|
452
|
+
const git = (...args: string[]): void => {
|
|
453
|
+
const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env });
|
|
454
|
+
if (r.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`);
|
|
455
|
+
};
|
|
456
|
+
git('init', '--quiet', '--initial-branch=base');
|
|
457
|
+
git('config', 'user.email', 'selftest@example.invalid');
|
|
458
|
+
git('config', 'user.name', 'selftest');
|
|
459
|
+
git('config', 'commit.gpgsign', 'false');
|
|
460
|
+
git('add', '--all');
|
|
461
|
+
git('commit', '--quiet', '-m', 'baseline');
|
|
462
|
+
edit(root);
|
|
463
|
+
return root;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Like {@link gitBaseline}, but the edit is COMMITTED on a feature branch.
|
|
468
|
+
*
|
|
469
|
+
* The arm CI actually uses is `git diff <base>...HEAD`, and every case that
|
|
470
|
+
* leaves its edit in the working tree exercises the other three arms instead.
|
|
471
|
+
* A committed fixture is the only one that can fail when the branch arm breaks.
|
|
472
|
+
*/
|
|
473
|
+
function gitBranchCommit(root: string, edit: (root: string) => void): string {
|
|
474
|
+
gitBaseline(root, () => undefined);
|
|
475
|
+
const env = gitEnv();
|
|
476
|
+
const git = (...args: string[]): void => {
|
|
477
|
+
const r = spawnSync('git', args, { cwd: root, encoding: 'utf-8', env });
|
|
478
|
+
if (r.status !== 0) throw new Error(`git ${args.join(' ')} failed: ${r.stderr}`);
|
|
479
|
+
};
|
|
480
|
+
git('checkout', '--quiet', '-b', 'feature');
|
|
481
|
+
edit(root);
|
|
482
|
+
git('add', '--all');
|
|
483
|
+
git('commit', '--quiet', '-m', 'the change');
|
|
484
|
+
return root;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
/** Write `evals/triggers.json` for a self-test skill. */
|
|
488
|
+
function plantCorpus(root: string, skill: string): void {
|
|
489
|
+
const d = path.join(root, 'src', 'skills', skill, 'evals');
|
|
490
|
+
fs.mkdirSync(d, { recursive: true });
|
|
491
|
+
fs.writeFileSync(path.join(d, 'triggers.json'), '[]\n');
|
|
492
|
+
}
|
|
493
|
+
|
|
234
494
|
export function selfTest(): number {
|
|
235
495
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'crc-selftest-'));
|
|
236
|
-
const run = (root: string): number => {
|
|
496
|
+
const run = (root: string, args: readonly string[] = []): number => {
|
|
237
497
|
process.env['CHECK_ROUTING_COVERAGE_ROOT'] = root;
|
|
238
498
|
try {
|
|
239
|
-
return runGateCli(REAL_REPO_ROOT, 'src/scripts/check_routing_coverage.ts',
|
|
499
|
+
return runGateCli(REAL_REPO_ROOT, 'src/scripts/check_routing_coverage.ts', args, root);
|
|
240
500
|
} finally {
|
|
241
501
|
delete process.env['CHECK_ROUTING_COVERAGE_ROOT'];
|
|
242
502
|
}
|
|
@@ -285,13 +545,82 @@ export function selfTest(): number {
|
|
|
285
545
|
return run(root);
|
|
286
546
|
},
|
|
287
547
|
},
|
|
548
|
+
{
|
|
549
|
+
name: 'TOUCHING a skill that has no evals/triggers.json is rejected',
|
|
550
|
+
expect: 'reject',
|
|
551
|
+
run: () => {
|
|
552
|
+
// st-skill-9 is outside `sc`, so the baseline leaves it corpus-less.
|
|
553
|
+
const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
554
|
+
fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
|
|
555
|
+
});
|
|
556
|
+
return run(root, ['--base', 'base']);
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
{
|
|
560
|
+
name: 'touching a skill that DOES carry a corpus is accepted',
|
|
561
|
+
expect: 'accept',
|
|
562
|
+
run: () => {
|
|
563
|
+
const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
564
|
+
fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-0', 'SKILL.md'), '\nedited\n');
|
|
565
|
+
});
|
|
566
|
+
return run(root, ['--base', 'base']);
|
|
567
|
+
},
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: 'a corpus-less skill the diff did NOT touch is accepted — the scope is the diff',
|
|
571
|
+
expect: 'accept',
|
|
572
|
+
run: () => {
|
|
573
|
+
const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
574
|
+
fs.writeFileSync(path.join(r, 'unrelated.txt'), 'x\n');
|
|
575
|
+
});
|
|
576
|
+
return run(root, ['--base', 'base']);
|
|
577
|
+
},
|
|
578
|
+
},
|
|
579
|
+
{
|
|
580
|
+
name: 'a NEW corpus-less skill added by the diff is rejected before its first commit',
|
|
581
|
+
expect: 'reject',
|
|
582
|
+
run: () => {
|
|
583
|
+
const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
584
|
+
const d = path.join(r, 'src', 'skills', 'st-skill-new');
|
|
585
|
+
fs.mkdirSync(d, { recursive: true });
|
|
586
|
+
fs.writeFileSync(path.join(d, 'SKILL.md'), '---\nname: st\n---\n');
|
|
587
|
+
// A corpus-less sibling gains one, so the SKILLS RATIO stays above
|
|
588
|
+
// seed (6/11 > 0.5) and the only thing left to reject on is the new
|
|
589
|
+
// skill's own missing corpus. Without this the case rejected for the
|
|
590
|
+
// ratio instead — green for the wrong reason, which the sabotage
|
|
591
|
+
// probe caught.
|
|
592
|
+
plantCorpus(r, 'st-skill-6');
|
|
593
|
+
});
|
|
594
|
+
return run(root, ['--base', 'base']);
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
{
|
|
598
|
+
name: 'a skill edited in a COMMIT, not the working tree, is rejected — the arm CI uses',
|
|
599
|
+
expect: 'reject',
|
|
600
|
+
run: () => {
|
|
601
|
+
const root = gitBranchCommit(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
602
|
+
fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
|
|
603
|
+
});
|
|
604
|
+
return run(root, ['--base', 'base']);
|
|
605
|
+
},
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
name: 'an UNDIFFABLE base still rejects a dirty uncovered skill — the local arms need no base',
|
|
609
|
+
expect: 'reject',
|
|
610
|
+
run: () => {
|
|
611
|
+
const root = gitBaseline(selfTestRoot(tmp, { r: 10, rc: 8, s: 10, sc: 5 }), (r) => {
|
|
612
|
+
fs.appendFileSync(path.join(r, 'src', 'skills', 'st-skill-9', 'SKILL.md'), '\nedited\n');
|
|
613
|
+
});
|
|
614
|
+
return run(root, ['--base', 'no-such-ref-at-all']);
|
|
615
|
+
},
|
|
616
|
+
},
|
|
288
617
|
];
|
|
289
618
|
try {
|
|
290
619
|
return runSelfTest({
|
|
291
620
|
gate: 'check_routing_coverage',
|
|
292
621
|
cases,
|
|
293
|
-
minCases:
|
|
294
|
-
minRejectCases:
|
|
622
|
+
minCases: 13,
|
|
623
|
+
minRejectCases: 9,
|
|
295
624
|
});
|
|
296
625
|
} finally {
|
|
297
626
|
fs.rmSync(tmp, { recursive: true, force: true });
|
|
@@ -300,9 +629,15 @@ export function selfTest(): number {
|
|
|
300
629
|
|
|
301
630
|
export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT): number {
|
|
302
631
|
if (argv.includes('--self-test')) return selfTest();
|
|
632
|
+
if (argv.includes('--census')) {
|
|
633
|
+
process.stdout.write(`${renderUncoveredCensus(root).join('\n')}\n`);
|
|
634
|
+
return 0;
|
|
635
|
+
}
|
|
636
|
+
const baseIdx = argv.indexOf('--base');
|
|
637
|
+
const base = baseIdx !== -1 ? (argv[baseIdx + 1] ?? TOUCHED_BASE_DEFAULT) : TOUCHED_BASE_DEFAULT;
|
|
303
638
|
let v: Verdict;
|
|
304
639
|
try {
|
|
305
|
-
v = evaluate(root);
|
|
640
|
+
v = evaluate(root, base);
|
|
306
641
|
reportScanned({
|
|
307
642
|
gate: 'check_routing_coverage',
|
|
308
643
|
scanned: v.readings.reduce((n, r) => n + r.units, 0),
|
|
@@ -330,10 +665,42 @@ export function main(argv: string[] = process.argv.slice(2), root = REPO_ROOT):
|
|
|
330
665
|
`${r.ratio.toFixed(4)} (seed ${seed.toFixed(4)})\n`,
|
|
331
666
|
);
|
|
332
667
|
}
|
|
333
|
-
if (v.
|
|
334
|
-
process.stdout.write(
|
|
668
|
+
if (!v.touched.measured) {
|
|
669
|
+
process.stdout.write(
|
|
670
|
+
` · touched ${String(v.touched.touched.length).padStart(4)} skill(s) from the working ` +
|
|
671
|
+
`tree only — base ${v.touched.base} is not diffable here (missing ref, or a shallow ` +
|
|
672
|
+
`clone with no merge base), so the branch arm did NOT run. ` +
|
|
673
|
+
`${String(v.touched.uncovered.length)} without evals/triggers.json.\n`,
|
|
674
|
+
);
|
|
675
|
+
} else {
|
|
676
|
+
process.stdout.write(
|
|
677
|
+
` ${v.touched.uncovered.length === 0 ? '=' : '❌'} touched ${String(v.touched.touched.length).padStart(4)} skill(s) ` +
|
|
678
|
+
`vs ${v.touched.base}, ${String(v.touched.uncovered.length)} without evals/triggers.json\n`,
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
if (v.fallen.length === 0 && v.touched.uncovered.length === 0) {
|
|
682
|
+
// The claim is narrowed to what actually ran. A single affirmative line
|
|
683
|
+
// covering an unmeasured scope is the silent green this scope exists to
|
|
684
|
+
// refuse, and it would be this gate committing it.
|
|
685
|
+
process.stdout.write(
|
|
686
|
+
v.touched.measured
|
|
687
|
+
? '✅ routing coverage at or above seed, and every touched skill carries a corpus.\n'
|
|
688
|
+
: '✅ routing coverage at or above seed. ⚠️ The branch arm of the touched-skill ' +
|
|
689
|
+
`scope did NOT run (base ${v.touched.base} is not diffable here), so nothing ` +
|
|
690
|
+
'is claimed about skills this branch changed in a commit.\n',
|
|
691
|
+
);
|
|
335
692
|
return 0;
|
|
336
693
|
}
|
|
694
|
+
if (v.touched.uncovered.length > 0) {
|
|
695
|
+
process.stderr.write(
|
|
696
|
+
`❌ ${String(v.touched.uncovered.length)} skill(s) changed by this diff carry no ` +
|
|
697
|
+
'evals/triggers.json:\n' +
|
|
698
|
+
v.touched.uncovered.map((n) => ` · src/skills/${n}/evals/triggers.json\n`).join('') +
|
|
699
|
+
' The corpus is cheapest to write while the skill is open. Write the trigger ' +
|
|
700
|
+
'cases for each; the discipline each file must meet is enforced by ' +
|
|
701
|
+
'lint_skill_trigger_corpus.\n',
|
|
702
|
+
);
|
|
703
|
+
}
|
|
337
704
|
for (const s of v.fallen) {
|
|
338
705
|
const r = v.readings.find((x) => x.scope === s)!;
|
|
339
706
|
process.stderr.write(
|
|
@@ -31,24 +31,44 @@
|
|
|
31
31
|
* Exit codes: `0` under cap · `1` over cap or an unstated cap raise · `2` usage
|
|
32
32
|
* error · `3` dead scan scope.
|
|
33
33
|
*
|
|
34
|
-
*
|
|
34
|
+
* THIS IS DELIBERATELY NOT A CI GATE, AND THE REASON IS NARROWER THAN IT WAS.
|
|
35
35
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
* **neither** layer exists
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
36
|
+
* **Corrected 2026-10-05 (step 0.1 of
|
|
37
|
+
* `road-to-a-rule-carrier-that-works-outside-the-repo`).** This section used to
|
|
38
|
+
* say "no CI workflow performs a user-scope install, so on a fresh runner
|
|
39
|
+
* **neither** layer exists", and then concluded that making the measurement
|
|
40
|
+
* CI-observable "needs the `InstructionsLoaded` record committed from a real
|
|
41
|
+
* session, which is a different piece of work". The first clause describes a
|
|
42
|
+
* workflow inventory; the second turns it into a capability claim, and that
|
|
43
|
+
* step does not hold. A runner CAN stage a user-scope install: the installer
|
|
44
|
+
* reads `HOME` from the environment, and
|
|
45
|
+
* `tests/install/global_install_hooks_smoke.test.ts` already drives
|
|
46
|
+
* `src/scripts/install --global --tools=claude-code` against a `mkdtemp` HOME
|
|
47
|
+
* and asserts the tree it produced. Nothing about the `InstructionsLoaded`
|
|
48
|
+
* event is required to observe the FILESYSTEM half, which is what this script's
|
|
49
|
+
* `filesystem` input reads.
|
|
45
50
|
*
|
|
46
|
-
* What
|
|
51
|
+
* What stays true, and is the real reason this is not in `gate-coverage.yml`:
|
|
52
|
+
* **this gate measures THIS machine**, by design. `main()` resolves
|
|
53
|
+
* `os.homedir()` and `REPO_ROOT`, and the question it answers — "how much is
|
|
54
|
+
* the developer in front of me actually loading" — is about a machine, not
|
|
55
|
+
* about a commit. A CI run of it would measure the runner, which is a tree
|
|
56
|
+
* nobody works in, so the number would be real and uninteresting. The
|
|
57
|
+
* `measureStandingDelivery` function underneath takes both directories as
|
|
58
|
+
* PARAMETERS and is therefore already temp-HOME-capable; what is missing is a
|
|
59
|
+
* reason to point it at a runner, not a way to.
|
|
60
|
+
*
|
|
61
|
+
* The honest cost is unchanged: the duplicate-delivery defect is not
|
|
47
62
|
* mechanically caught for anyone who never runs this locally. The enforced layer
|
|
48
63
|
* is the installer gate (`install.ts::_gate_rule_layer_overlap`), which refuses
|
|
49
64
|
* to CREATE the doubled state; this script measures a state that already exists.
|
|
50
|
-
*
|
|
51
|
-
*
|
|
65
|
+
*
|
|
66
|
+
* `_lib/installed_layer.ts` is the step-0.1 library that takes the same reading
|
|
67
|
+
* per host rule directory with every input as a parameter, and
|
|
68
|
+
* `installed_layer_report.ts` is its report. The two measure different things —
|
|
69
|
+
* that one counts characters after the frontmatter and block-comment strip,
|
|
70
|
+
* this one counts tokens over whole files against a cap — and neither number
|
|
71
|
+
* converts into the other.
|
|
52
72
|
*/
|
|
53
73
|
|
|
54
74
|
import * as fs from 'node:fs';
|
|
@@ -266,6 +266,23 @@ export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
|
|
266
266
|
}
|
|
267
267
|
if (present.exitCode !== 0 || absent.exitCode !== 0) {
|
|
268
268
|
process.stderr.write('❌ one of the two runs is red; parity over a red suite proves nothing\n');
|
|
269
|
+
// Name them. This branch used to print the sentence above and nothing
|
|
270
|
+
// else, while holding every per-test verdict in memory — so a red that
|
|
271
|
+
// reproduced only on the CI runner was undiagnosable from its log, and
|
|
272
|
+
// the only move left was to re-run and hope. Found 2026-09-30 by being
|
|
273
|
+
// on the receiving end of exactly that.
|
|
274
|
+
for (const [label, result] of [['present-but-off', present], ['absent', absent]] as const) {
|
|
275
|
+
const quiescent = new Set(['passed', 'pending', 'skipped', 'todo', 'disabled']);
|
|
276
|
+
const failed = result.verdicts.filter((v) => !quiescent.has(v.status));
|
|
277
|
+
if (failed.length === 0) continue;
|
|
278
|
+
process.stderr.write(` ${label} — ${failed.length} not passing:\n`);
|
|
279
|
+
for (const v of failed.slice(0, 20)) {
|
|
280
|
+
process.stderr.write(` · ${v.status} ${v.name}\n`);
|
|
281
|
+
}
|
|
282
|
+
if (failed.length > 20) {
|
|
283
|
+
process.stderr.write(` … and ${failed.length - 20} more\n`);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
269
286
|
return 1;
|
|
270
287
|
}
|
|
271
288
|
if (differences.length > 0) {
|