@event4u/agent-config 16.0.0 → 16.2.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 +289 -140
- package/CONTRIBUTING.md +9 -0
- package/README.md +2 -2
- package/dist/agent-src/commands/analyze/inbox.md +26 -0
- package/dist/agent-src/commands/analyze/roadmap-repos.md +27 -0
- package/dist/agent-src/commands/challenge-me/closure.md +178 -0
- package/dist/agent-src/commands/challenge-me.md +9 -2
- package/dist/agent-src/commands/council/default.md +25 -5
- package/dist/agent-src/commands/feature/plan.md +28 -1
- package/dist/agent-src/commands/feature/roadmap.md +28 -1
- package/dist/agent-src/commands/implement-ticket.md +27 -0
- package/dist/agent-src/commands/jira-ticket.md +27 -0
- package/dist/agent-src/commands/pr/create.md +29 -0
- package/dist/agent-src/commands/pr/merge.md +60 -9
- package/dist/agent-src/commands/roadmap/ai-council.md +7 -1
- package/dist/agent-src/commands/roadmap/create.md +74 -27
- package/dist/agent-src/commands/roadmap/materialize.md +27 -0
- package/dist/agent-src/commands/roadmap/process-full.md +123 -18
- package/dist/agent-src/commands/team.md +22 -0
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/scope-growth-ownership.md +80 -0
- package/dist/agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +47 -0
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +44 -3
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +19 -23
- package/dist/agent-src/contexts/execution/decision-closure-execution.md +93 -0
- package/dist/agent-src/contexts/execution/fix-loop-ladder.md +108 -0
- package/dist/agent-src/contexts/execution/plan-confidence-gate.md +4 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +75 -6
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/dist/agent-src/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/dist/agent-src/guidelines/design-fidelity-routing.md +29 -0
- package/dist/agent-src/rules/autonomous-execution.md +15 -8
- package/dist/agent-src/rules/delegation-policy.md +23 -45
- package/dist/agent-src/rules/design-fidelity.md +32 -37
- package/dist/agent-src/rules/evaluator-independence.md +43 -49
- package/dist/agent-src/rules/test-first.md +41 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- package/dist/agent-src/skills/ai-council/SKILL.md +11 -3
- package/dist/agent-src/skills/api-design/data/manifest.json +3 -3
- package/dist/agent-src/skills/database/data/manifest.json +4 -4
- package/dist/agent-src/skills/database/data/query-tuning.csv +3 -3
- package/dist/agent-src/skills/design-review/SKILL.md +80 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +30 -5
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/experiment-loop/SKILL.md +31 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +42 -3
- package/dist/agent-src/skills/threat-modeling/SKILL.md +9 -4
- package/dist/agent-src/skills/threat-modeling/data/manifest.json +4 -4
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +23 -0
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +37 -3
- package/dist/agent-src/templates/agent-settings.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +218 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -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 +260 -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/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-delegate/check_update_banner.js +40 -2
- package/dist/cli-delegate/chunk-4E2W63DA.js +335 -0
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-4XFP2Y3E.js} +1 -1
- package/dist/cli-delegate/chunk-5R7HOLAF.js +99 -0
- package/dist/cli-delegate/{chunk-IVBPRCQO.js → chunk-6KFEC3G7.js} +1 -1
- package/dist/cli-delegate/{chunk-2TAQ2K4V.js → chunk-6KM6ORRY.js} +55 -386
- package/dist/cli-delegate/{chunk-AK6HODNQ.js → chunk-7XGBX6ZH.js} +973 -555
- package/dist/cli-delegate/{chunk-2XY7ILBT.js → chunk-FC75PGJE.js} +42 -2
- package/dist/cli-delegate/{chunk-55ACLDUE.js → chunk-FP36SWJD.js} +587 -141
- package/dist/cli-delegate/{chunk-3FKR64R5.js → chunk-IGD6ERX6.js} +62 -7
- package/dist/cli-delegate/{chunk-GYC3VRYN.js → chunk-JNMVLWLR.js} +99 -14
- package/dist/cli-delegate/{chunk-H32DGUJH.js → chunk-P6ZHP5W2.js} +1 -1
- package/dist/cli-delegate/{chunk-57PN4GYJ.js → chunk-RGNIWTEA.js} +1 -1
- package/dist/cli-delegate/chunk-ROYYTEZU.js +39 -0
- package/dist/cli-delegate/{chunk-2DK2VJWG.js → chunk-WGXTVKCQ.js} +20 -6
- package/dist/cli-delegate/chunk-YDPILCOL.js +152 -0
- package/dist/cli-delegate/{chunk-QJWAUMJW.js → chunk-YJ72UIAK.js} +730 -15
- 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 +985 -831
- package/dist/cli-delegate/cmd_doctor.js +7 -5
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +9 -7
- package/dist/cli-delegate/cmd_fleet.js +4 -2
- package/dist/cli-delegate/cmd_handoff.js +7 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_preflight.js +4 -2
- package/dist/cli-delegate/cmd_prune.js +10 -8
- package/dist/cli-delegate/cmd_route_audit.js +3 -3
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +28 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +9 -7
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -8
- package/dist/cli-delegate/cmd_uninstall.js +16 -13
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +12 -10
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +90 -43
- 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 +10 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +8 -6
- package/dist/hooks/dispatch.js +7682 -2772
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudeRuleRewrite.js +14 -3
- package/dist/install/conflict.js +60 -10
- package/dist/install/conflictTracker.js +218 -0
- package/dist/install/fsPrimitives.js +54 -0
- package/dist/install/install.mjs +2200 -1559
- package/dist/install/manifestFiles.js +259 -0
- package/dist/install/preflight.js +14 -2
- package/dist/install/preserve.js +157 -0
- package/dist/install/recordedOwnership.js +132 -0
- package/dist/install/txlog.js +11 -2
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +3066 -1470
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +3 -2
- package/dist/scripts/_lib/fs_atomic.js +167 -0
- package/dist/scripts/_lib/installed_tools.js +460 -0
- package/dist/scripts/_lib/lean_projection_mode.js +3 -2
- package/dist/scripts/_lib/settings_renamed_keys.js +78 -0
- package/dist/server/routes/install.js +8 -0
- package/dist/server/routes/wizard.js +9 -3
- package/dist/server/schemas/settings.js +40 -5
- package/dist/shared/missionExecution.js +97 -0
- 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/MIGRATION.md +103 -0
- package/docs/contracts/settings-classes.md +28 -7
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +57 -0
- package/docs/guidelines/agent-infra/evaluator-independence-mechanics.md +102 -0
- package/docs/guidelines/design-fidelity-routing.md +29 -0
- package/package.json +4 -4
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +62 -2
- package/src/agent-src/templates/scripts/work_engine/directives/backend/test.ts +1 -0
- package/src/agent-src/templates/scripts/work_engine/directives/backend/verify.ts +1 -0
- 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 +260 -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/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 +168 -27
- package/src/config/auto-rule-budget.json +16 -0
- package/src/config/ci-local-parity.yml +36 -5
- package/src/config/continuity-surface.json +23 -2
- package/src/config/gate-coverage.yml +377 -13
- package/src/config/gate-violation-baselines.json +10 -6
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +4 -2
- package/src/config/loop-surfaces.yaml +215 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/platform-anchor.json +3 -2
- package/src/config/release-gate-locality.yml +11 -0
- package/src/config/routing-coverage-seed.json +33 -5
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/install/claudeRuleRewrite.ts +16 -2
- package/src/install/conflict.ts +65 -9
- package/src/install/conflictTracker.ts +299 -0
- package/src/install/fsPrimitives.ts +54 -0
- package/src/install/manifestFiles.ts +312 -0
- package/src/install/preflight.ts +16 -2
- package/src/install/preserve.ts +205 -0
- package/src/install/recordedOwnership.ts +149 -0
- package/src/install/txlog.ts +11 -2
- package/src/install/types.ts +19 -4
- package/src/scripts/_cli/cmd_conformance.ts +88 -15
- package/src/scripts/_cli/cmd_doctor.ts +7 -20
- package/src/scripts/_cli/cmd_route_explain.ts +9 -2
- package/src/scripts/_cli/cmd_settings_get.ts +44 -0
- package/src/scripts/_cli/doctor_execution.ts +173 -0
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_lib/activation_payload.ts +247 -0
- package/src/scripts/_lib/agent_settings.ts +4 -2
- package/src/scripts/_lib/authority_path.ts +102 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cascade_base.ts +94 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/changelog_eras.ts +7 -2
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/continuation_ladder.ts +149 -2
- package/src/scripts/_lib/council_transport.ts +129 -0
- package/src/scripts/_lib/delivery_ready.ts +112 -0
- package/src/scripts/_lib/forge_protection.ts +381 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/hook_platform_keys.ts +27 -0
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/json_python_parity.ts +127 -0
- package/src/scripts/_lib/lean_projection_mode.ts +14 -3
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/loop_surfaces.ts +382 -0
- package/src/scripts/_lib/mission_record.ts +190 -0
- package/src/scripts/_lib/obligation_frequency.ts +217 -0
- package/src/scripts/_lib/obligations.ts +513 -0
- package/src/scripts/_lib/orchestration_record.ts +11 -20
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +201 -11
- package/src/scripts/_lib/release_holds.ts +493 -0
- package/src/scripts/_lib/release_migration_gate.ts +184 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/rides_along.ts +102 -0
- package/src/scripts/_lib/settings_renamed_keys.ts +82 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/test_delta.ts +166 -0
- package/src/scripts/_lib/test_provenance.ts +171 -0
- package/src/scripts/_lib/turn_end_refusals.ts +525 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/typed_op_grant.ts +246 -0
- package/src/scripts/_lib/typed_op_watch.ts +185 -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/ai_council/config.ts +8 -11
- package/src/scripts/ai_council/fallback_config.ts +5 -3
- package/src/scripts/ai_council/ownership_classes.ts +118 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/ask_block_census.ts +285 -15
- package/src/scripts/before_complete_hook.ts +340 -23
- package/src/scripts/bench_hook_latency.ts +412 -5
- 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 +866 -0
- package/src/scripts/check_finding_dispositions.ts +22 -3
- package/src/scripts/check_gate_reachability.ts +173 -10
- package/src/scripts/check_generator_sync.ts +1025 -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 +47 -5
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_preamble_payload_budget.ts +75 -2
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +33 -1
- package/src/scripts/check_release_holds.ts +568 -0
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_rule_projection_integrity.ts +22 -12
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_test_delta.ts +294 -0
- package/src/scripts/check_test_weakening.ts +299 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +447 -0
- package/src/scripts/compile_hook_manifest.ts +102 -18
- package/src/scripts/council_record_shape.ts +209 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +127 -11
- package/src/scripts/hooks/block_config_weakening.ts +229 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +314 -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 +1 -2
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_registry.ts +6 -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 +221 -5
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +25 -19
- 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 +97 -2
- package/src/scripts/hooks/exit_codes.ts +128 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +128 -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 +539 -0
- package/src/scripts/hooks/obligation_settle_hook.ts +328 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +25 -4
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- 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 +55 -4
- package/src/scripts/hooks/run_continuation_hook.ts +30 -35
- 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 +49 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +1 -2
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +337 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_doctor.ts +85 -0
- package/src/scripts/hooks_status.ts +19 -0
- package/src/scripts/install.ts +109 -210
- package/src/scripts/janitor.ts +7 -0
- package/src/scripts/lint_decision_classes.ts +464 -0
- 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 +66 -0
- package/src/scripts/lint_major_migration_sections.ts +450 -0
- package/src/scripts/lint_plan_risk_register.ts +47 -3
- package/src/scripts/lint_roadmap_blockers.ts +139 -12
- package/src/scripts/lint_roadmap_producers.ts +202 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_rule_budget.ts +101 -5
- package/src/scripts/measure_skill_ranker_baseline.ts +305 -51
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/new_roadmap.ts +37 -0
- package/src/scripts/orchestration_record.ts +0 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/release.ts +14 -98
- package/src/scripts/release_drill.ts +267 -1
- package/src/scripts/release_publication.ts +64 -1
- package/src/scripts/report_enforcement_drift.ts +566 -0
- package/src/scripts/report_host_injection_effect.ts +3 -11
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_standing_payload_by_host.ts +26 -152
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/schemas/agent-settings.schema.json +23 -0
- package/src/scripts/schemas/command.schema.json +48 -0
- package/src/scripts/schemas/review-findings.schema.json +4 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/scope_growth.ts +219 -0
- package/src/scripts/self_review_gate.ts +359 -10
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/trigger_eval_grandfather.json +0 -3
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +774 -0
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +62 -7
- package/src/shared/missionExecution.ts +126 -0
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-523HCFYO.js +0 -90
- package/dist/ui/assets/index-D0jaCRPM.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
- /package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-7RQP4UDH.js} +0 -0
|
@@ -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(
|
|
@@ -6,22 +6,32 @@
|
|
|
6
6
|
* WHY THIS GATE EXISTS (road-to-conformance-round5 Phase 1, measured 2026-08-07)
|
|
7
7
|
* ---------------------------------------------------------------------------
|
|
8
8
|
* `docs/contracts/rule-router.md` (§ "The router earns its place as a
|
|
9
|
-
* **compile-time** artifact")
|
|
10
|
-
* loads `dist/router.json` at runtime, so **projection is the only
|
|
11
|
-
* mechanism there is
|
|
9
|
+
* **compile-time** artifact") used to state that under every shipped default
|
|
10
|
+
* nothing loads `dist/router.json` at runtime, so **projection is the only
|
|
11
|
+
* reach mechanism there is**. Both halves of that have since been corrected,
|
|
12
|
+
* and the gate outlived both:
|
|
12
13
|
*
|
|
13
14
|
* CORRECTED 2026-08-26. This docblock used to cite that section's "measured:
|
|
14
15
|
* zero consumers under `src/scripts/hooks/`", and that measurement is stale: the
|
|
15
16
|
* `rule-inject` concern reads the router via `_lib/rule_injection.ts:76-79` and
|
|
16
|
-
* is bound on three slots.
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
17
|
+
* is bound on three slots. What no longer holds is the stronger claim that no
|
|
18
|
+
* such mechanism exists.
|
|
19
|
+
*
|
|
20
|
+
* CORRECTED AGAIN 2026-09-12. The 2026-08-26 note rescued the rationale with
|
|
21
|
+
* "the concern is DEFAULT-OFF … so on a shipped default, projection IS still
|
|
22
|
+
* the only reach mechanism". That rescue is now false and the clause is gone:
|
|
23
|
+
* since ADR-267 the shipped template carries `lean_projection.mode: delivery`
|
|
24
|
+
* with `hosts: [claude-code]`, so on the shipped default that one host does
|
|
25
|
+
* reach rule bodies at runtime. The gate never depended on the rescue — its two
|
|
26
|
+
* assertions below are about the emit plan and the entry mtimes, and a thinned
|
|
27
|
+
* tree of stubs is exactly as load-bearing as a tree of bodies: a missing entry
|
|
28
|
+
* is a rule the model neither reads nor has a pointer to, in either mode.
|
|
29
|
+
*
|
|
30
|
+
* Both are stated rather than quietly re-worded, because a gate whose stated
|
|
31
|
+
* reason rests on a refuted premise is exactly the decoration ADR-127 rejects:
|
|
32
|
+
* a non-kernel rule activates by the model's judgment over text already in
|
|
33
|
+
* context, and the per-tool rule tree is what puts it there. That makes the
|
|
34
|
+
* state of the projection load-bearing, and it was broken:
|
|
25
35
|
*
|
|
26
36
|
* - `.claude/rules/` in the working checkout held **92** entries. A
|
|
27
37
|
* regeneration in a clean worktree produces **108**. The tree was last
|
|
@@ -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) {
|