@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
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a Claude JSONL transcript tail, for the turn-end gate's detectors.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `hooks/turn_end_gate_hook.ts` for the reason its own detector
|
|
5
|
+
* E carries one line above its import: `_lib` is where this prose costs no
|
|
6
|
+
* ratchet debt. The gate reached 1,593 lines against a 1,500-line source
|
|
7
|
+
* budget when the shadow read landed, and `check_source_size_budget` only
|
|
8
|
+
* turns one way — so the choice was to extract or to delete comments that
|
|
9
|
+
* record why the reader is shaped as it is. This module is the extraction.
|
|
10
|
+
*
|
|
11
|
+
* Nothing about the behaviour moves: every function below is byte-identical to
|
|
12
|
+
* the version that lived in the gate, and the gate re-exports all three public
|
|
13
|
+
* names so no importer changes. The two guards this reader depends on stay
|
|
14
|
+
* where they are — `isSafeTranscriptPath` is the security-relevant
|
|
15
|
+
* home-confinement predicate and `isSyntheticPrompt` is the ordinal filter, and
|
|
16
|
+
* relocating either as a side effect of a size budget is exactly the drive-by
|
|
17
|
+
* this tree's diff discipline forbids.
|
|
18
|
+
*/
|
|
19
|
+
import * as fs from 'node:fs';
|
|
20
|
+
|
|
21
|
+
import { isSyntheticPrompt } from '../language_mirror_hook.js';
|
|
22
|
+
import { isSafeTranscriptPath } from '../hooks/end_review_nudge_hook.js';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* One tool call the assistant made, reduced to what a detector can reason
|
|
26
|
+
* about: the tool's name, the shell command when it is a shell call, and the
|
|
27
|
+
* target path when it is a file write.
|
|
28
|
+
*
|
|
29
|
+
* Nothing else is kept. A tool input can hold a whole file body, and this
|
|
30
|
+
* struct is what a refusal quotes back — so it is shaped to be INCAPABLE of
|
|
31
|
+
* carrying one, the same PII-exclusion-by-construction discipline
|
|
32
|
+
* `domain-safety-pii` § Surface 2 asks for in a log line.
|
|
33
|
+
*/
|
|
34
|
+
export interface ToolCall {
|
|
35
|
+
name: string;
|
|
36
|
+
/** `Bash` only — the command line, so "did anything verify" is answerable. */
|
|
37
|
+
command?: string;
|
|
38
|
+
/** Edit/Write only — the file the turn changed, used as the evidence span. */
|
|
39
|
+
path?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface TranscriptTail {
|
|
43
|
+
lastAssistant: string;
|
|
44
|
+
/**
|
|
45
|
+
* Every assistant text of the CURRENT turn, in order — reset at each genuine
|
|
46
|
+
* user prompt exactly like `toolCalls`, and for the same reason: a question
|
|
47
|
+
* asked three turns ago was answered, a question asked earlier in THIS turn
|
|
48
|
+
* was not. Detector E reads it; the other four read `lastAssistant`, which is
|
|
49
|
+
* always the last element when this is non-empty.
|
|
50
|
+
*
|
|
51
|
+
* A tool-only assistant entry contributes NOTHING here. `_messageText`
|
|
52
|
+
* returns null for it and the collector skips it, so the ordinary shape of a
|
|
53
|
+
* working turn — prose, tool call, prose — produces two elements and not
|
|
54
|
+
* three. That is the whole false-positive surface of detector E, and it is
|
|
55
|
+
* closed here rather than in the detector.
|
|
56
|
+
*/
|
|
57
|
+
assistantTurnTexts: string[];
|
|
58
|
+
/**
|
|
59
|
+
* How many GENUINE user prompts the transcript carries — harness-injected
|
|
60
|
+
* user-role entries excluded via `isSyntheticPrompt`. This is the turn's
|
|
61
|
+
* identity; see `alreadyRefusedTurn` for why the prompt's text is not.
|
|
62
|
+
*/
|
|
63
|
+
turnOrdinal: number;
|
|
64
|
+
/**
|
|
65
|
+
* The tool calls of the CURRENT turn, in order — reset at every genuine user
|
|
66
|
+
* prompt, so a verification from three turns ago cannot vouch for an edit
|
|
67
|
+
* made now. `_messageText` keeps only `type === 'text'` blocks, which is why
|
|
68
|
+
* this needed its own extraction rather than a reading of `lastAssistant`:
|
|
69
|
+
* tool activity is not in the prose at all.
|
|
70
|
+
*/
|
|
71
|
+
toolCalls: ToolCall[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The last assistant text, plus the turn ordinal, from a Claude JSONL
|
|
76
|
+
* transcript. Mirrors `chat_history._extract_claude_transcript_response` for
|
|
77
|
+
* the assistant half.
|
|
78
|
+
*
|
|
79
|
+
* `isSyntheticPrompt` is applied to every user-role entry — the same filter
|
|
80
|
+
* `language_mirror_hook` uses, and for the same reason it was added there
|
|
81
|
+
* (round-5 § 6.5): a background-task notification and a `<system-reminder>`
|
|
82
|
+
* both occupy the user role without being chat messages. Counting them would
|
|
83
|
+
* move the turn ordinal mid-turn, which is R2 finding 3.
|
|
84
|
+
*/
|
|
85
|
+
export function readTranscriptTail(
|
|
86
|
+
transcriptPath: string,
|
|
87
|
+
opts: { homeDir?: string; maxBytes?: number } = {},
|
|
88
|
+
): TranscriptTail {
|
|
89
|
+
const empty: TranscriptTail = {
|
|
90
|
+
lastAssistant: '',
|
|
91
|
+
turnOrdinal: 0,
|
|
92
|
+
toolCalls: [],
|
|
93
|
+
assistantTurnTexts: [],
|
|
94
|
+
};
|
|
95
|
+
if (!transcriptPath || !isSafeTranscriptPath(transcriptPath, opts)) return empty;
|
|
96
|
+
let lines: string[];
|
|
97
|
+
try {
|
|
98
|
+
// R2 finding 12: `maxBytes` was accepted in the options type and never
|
|
99
|
+
// used, so the declared cap was decoration. It is enforced here as well
|
|
100
|
+
// as inside `isSafeTranscriptPath`, because this is the read it bounds.
|
|
101
|
+
// The whole file still has to be walked — the turn ordinal is a count
|
|
102
|
+
// over all entries, not something a tail can answer — so the cap is the
|
|
103
|
+
// guard, not an optimisation.
|
|
104
|
+
if (opts.maxBytes !== undefined && fs.statSync(transcriptPath).size > opts.maxBytes) {
|
|
105
|
+
return empty;
|
|
106
|
+
}
|
|
107
|
+
lines = fs.readFileSync(transcriptPath, 'utf-8').split('\n');
|
|
108
|
+
} catch {
|
|
109
|
+
return empty;
|
|
110
|
+
}
|
|
111
|
+
let lastAssistant = '';
|
|
112
|
+
let turnOrdinal = 0;
|
|
113
|
+
let toolCalls: ToolCall[] = [];
|
|
114
|
+
let assistantTurnTexts: string[] = [];
|
|
115
|
+
for (const rawLine of lines) {
|
|
116
|
+
const line = rawLine.trim();
|
|
117
|
+
if (!line) continue;
|
|
118
|
+
let obj: unknown;
|
|
119
|
+
try {
|
|
120
|
+
obj = JSON.parse(line);
|
|
121
|
+
} catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) continue;
|
|
125
|
+
const entry = obj as Record<string, unknown>;
|
|
126
|
+
// Sidechain entries are a SUBAGENT's conversation recorded in the same
|
|
127
|
+
// JSONL. A subagent prompt is a genuine-looking user-role text entry
|
|
128
|
+
// appended mid-turn, so counting it moves the ordinal within the turn —
|
|
129
|
+
// finding 3's failure class in a new shape (R2 round 2, finding 3).
|
|
130
|
+
if (entry['isSidechain'] === true) continue;
|
|
131
|
+
const role = entry['type'];
|
|
132
|
+
if (role !== 'assistant' && role !== 'user') continue;
|
|
133
|
+
const msg = entry['message'];
|
|
134
|
+
if (typeof msg !== 'object' || msg === null || Array.isArray(msg)) continue;
|
|
135
|
+
const content = (msg as Record<string, unknown>)['content'];
|
|
136
|
+
// Tool calls are read BEFORE the text guard: an assistant entry that is
|
|
137
|
+
// only a tool_use block has no text at all, so `continue`-ing on a null
|
|
138
|
+
// text would drop exactly the entries this detector exists to see.
|
|
139
|
+
if (role === 'assistant') {
|
|
140
|
+
toolCalls.push(...extractToolCalls(content));
|
|
141
|
+
}
|
|
142
|
+
const text = _messageText(content);
|
|
143
|
+
if (text === null) continue;
|
|
144
|
+
if (role === 'assistant') {
|
|
145
|
+
lastAssistant = text;
|
|
146
|
+
assistantTurnTexts.push(text);
|
|
147
|
+
} else if (!isSyntheticPrompt(text)) {
|
|
148
|
+
turnOrdinal += 1;
|
|
149
|
+
// A genuine user prompt starts a new turn, so the previous turn's
|
|
150
|
+
// tool activity stops counting. Without this reset, a verification
|
|
151
|
+
// run three turns ago would vouch for an edit made now — the
|
|
152
|
+
// "fresh" in edit-without-FRESH-verification is this line.
|
|
153
|
+
toolCalls = [];
|
|
154
|
+
// Same boundary, same reason, for detector E: an options block the
|
|
155
|
+
// user has since replied to is answered, not dropped. Resetting on
|
|
156
|
+
// the identical line is what makes "in the SAME user turn" true of
|
|
157
|
+
// the array rather than merely intended.
|
|
158
|
+
assistantTurnTexts = [];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
lastAssistant: lastAssistant.trim(),
|
|
163
|
+
turnOrdinal,
|
|
164
|
+
toolCalls,
|
|
165
|
+
assistantTurnTexts,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Extract this entry's tool calls, keeping only name, shell command and target
|
|
171
|
+
* path. A tool input can hold a whole file body; nothing but those three fields
|
|
172
|
+
* is carried forward.
|
|
173
|
+
*
|
|
174
|
+
* Exported because `measure_turn_end_gate` scores detectors C and E over a real
|
|
175
|
+
* transcript corpus and therefore has to rebuild the same `ToolCall[]` the gate
|
|
176
|
+
* sees. A second extractor there would be a second dialect of "what the turn
|
|
177
|
+
* did", and the measurement would then be of that dialect rather than of the
|
|
178
|
+
* shipped gate — the exact population-parity defect that script's own header
|
|
179
|
+
* documents twice.
|
|
180
|
+
*/
|
|
181
|
+
export function extractToolCalls(content: unknown): ToolCall[] {
|
|
182
|
+
if (!Array.isArray(content)) return [];
|
|
183
|
+
const out: ToolCall[] = [];
|
|
184
|
+
for (const blk of content) {
|
|
185
|
+
if (typeof blk !== 'object' || blk === null || Array.isArray(blk)) continue;
|
|
186
|
+
const b = blk as Record<string, unknown>;
|
|
187
|
+
if (b['type'] !== 'tool_use') continue;
|
|
188
|
+
const name = b['name'];
|
|
189
|
+
if (typeof name !== 'string') continue;
|
|
190
|
+
const input = b['input'];
|
|
191
|
+
const call: ToolCall = { name };
|
|
192
|
+
if (typeof input === 'object' && input !== null && !Array.isArray(input)) {
|
|
193
|
+
const inp = input as Record<string, unknown>;
|
|
194
|
+
const cmd = inp['command'];
|
|
195
|
+
if (typeof cmd === 'string') call.command = cmd;
|
|
196
|
+
const p = inp['file_path'] ?? inp['path'] ?? inp['notebook_path'];
|
|
197
|
+
if (typeof p === 'string') call.path = p;
|
|
198
|
+
}
|
|
199
|
+
out.push(call);
|
|
200
|
+
}
|
|
201
|
+
return out;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function _messageText(content: unknown): string | null {
|
|
205
|
+
if (typeof content === 'string') return content;
|
|
206
|
+
if (!Array.isArray(content)) return null;
|
|
207
|
+
const parts: string[] = [];
|
|
208
|
+
for (const blk of content) {
|
|
209
|
+
if (typeof blk !== 'object' || blk === null || Array.isArray(blk)) continue;
|
|
210
|
+
const b = blk as Record<string, unknown>;
|
|
211
|
+
if (b['type'] !== 'text') continue;
|
|
212
|
+
const t = b['text'];
|
|
213
|
+
if (typeof t === 'string') parts.push(t);
|
|
214
|
+
}
|
|
215
|
+
return parts.length > 0 ? parts.join('\n') : null;
|
|
216
|
+
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The verification-command selector — one predicate, two importers.
|
|
3
|
+
*
|
|
4
|
+
* It first moved here byte-identical out of `hooks/turn_end_gate_hook.ts` so
|
|
5
|
+
* that `_lib/verification_evidence.ts` could read it without importing a hook
|
|
6
|
+
* that imports the evidence module straight back. That cycle resolves at
|
|
7
|
+
* runtime today and breaks the day either side grows a module-level constant
|
|
8
|
+
* reading the other, which is a failure nobody would trace to an import line.
|
|
9
|
+
*
|
|
10
|
+
* The hook re-exports the name, so every existing consumer — including both
|
|
11
|
+
* pinning test files — addresses it at the path it always did.
|
|
12
|
+
*
|
|
13
|
+
* WHY IT STOPPED BEING A REGEX (2026-09-30).
|
|
14
|
+
* The tree carried TWO expressions answering "did this command verify
|
|
15
|
+
* anything", and they disagreed in both directions:
|
|
16
|
+
*
|
|
17
|
+
* - `_VERIFY_RE` here matched a verification token **anywhere on the line**, so
|
|
18
|
+
* `ls tests`, `cat build.log`, `git checkout main`, `mkdir build` and
|
|
19
|
+
* `git commit -m "fix ci"` all cleared detector C — the exact case this
|
|
20
|
+
* header claimed to reject. Measured at the pin: all six returned `true`.
|
|
21
|
+
* - `_VERIFICATION_RE` in `before_complete_hook.ts` was head-anchored to a
|
|
22
|
+
* fixed vendor list and refused `npx vitest run`, `tsc --noEmit` and every
|
|
23
|
+
* `./scripts-run src/scripts/lint_*` — commands this project runs constantly.
|
|
24
|
+
* Measured at the pin: all three returned `false`.
|
|
25
|
+
*
|
|
26
|
+
* Both accepted `npm test || true`, where the shell discards the failure.
|
|
27
|
+
*
|
|
28
|
+
* The audited allowlist passed the whole time the first defect was live,
|
|
29
|
+
* because not one of its seven negatives put a verify token in an ARGUMENT
|
|
30
|
+
* position. A word-boundary regex cannot express "the head of the segment",
|
|
31
|
+
* which is the property that actually distinguishes `npm test` from `ls tests`,
|
|
32
|
+
* so the predicate is now structural: split on shell separators, drop a
|
|
33
|
+
* segment whose failure the shell discards, and classify each remaining
|
|
34
|
+
* segment on its HEAD.
|
|
35
|
+
*
|
|
36
|
+
* THE LISTS ARE STILL DELIBERATELY NARROW.
|
|
37
|
+
* A positive list. The alternative — treating any `Bash` call as verification —
|
|
38
|
+
* was rejected because `ls` would then clear an unverified edit, which is the
|
|
39
|
+
* failure mode `verify-before-complete` names ("relying on partial
|
|
40
|
+
* verification"). The cost of narrowness is a MISSED detection when a project
|
|
41
|
+
* verifies by some command not listed here, and that is the safe direction for
|
|
42
|
+
* a gate that can refuse a turn: a false negative costs one unguarded turn, a
|
|
43
|
+
* false positive teaches the user to switch the gate off.
|
|
44
|
+
*
|
|
45
|
+
* AUDITED 2026-08-17, re-seeded 2026-09-30 from the union of both lists minus
|
|
46
|
+
* every row that only ever matched on an argument. The table of every command
|
|
47
|
+
* with its verdict is `turn_end_verify_allowlist.test.ts`; the two directions
|
|
48
|
+
* of the old disagreement are `verification_command_anchoring.test.ts`.
|
|
49
|
+
*
|
|
50
|
+
* Deliberate NON-additions, kept because Risk 2 of the originating roadmap is
|
|
51
|
+
* that every addition is a way to satisfy the gate without verifying anything:
|
|
52
|
+
*
|
|
53
|
+
* · `npm run prepack` — a lifecycle hook whose content is per-project. Here
|
|
54
|
+
* it validates; elsewhere it copies files.
|
|
55
|
+
* · `task sync` / `task generate-tools` / `agent-config roadmap:progress` —
|
|
56
|
+
* GENERATORS. They rewrite the tree; they check nothing.
|
|
57
|
+
* · `agent-config gates --all` — enumerates gates, runs none.
|
|
58
|
+
* · `rector` — a refactoring tool. Its dry run prints a diff; it asserts
|
|
59
|
+
* nothing. It appears in `_VERIFICATION_RE`'s list and is dropped from the
|
|
60
|
+
* union on the strength of the audited `false` row, which carries a reason
|
|
61
|
+
* where the other list carried only a name.
|
|
62
|
+
* · `./scripts-run src/scripts/rule_activation_census` — a census writes a
|
|
63
|
+
* report; it asserts nothing. This is why the `scripts-run` rule keys on
|
|
64
|
+
* the `check_` / `lint_` prefix rather than on the runner.
|
|
65
|
+
*
|
|
66
|
+
* `psalm` IS carried, unlike the pre-2026-09-30 note here which argued it out
|
|
67
|
+
* for want of a local user: it was in `_VERIFICATION_RE` all along, so the
|
|
68
|
+
* union rule admits it and dropping it would be a narrowing this change did not
|
|
69
|
+
* set out to make.
|
|
70
|
+
*
|
|
71
|
+
* WHAT THIS SELECTOR STILL CANNOT DO: it reads the command TEXT, so
|
|
72
|
+
* `vitest run` matches whether or not it passed. Deciding whether a run proved
|
|
73
|
+
* anything is `verification_evidence.ts`, which reads the exit code and the
|
|
74
|
+
* summary the command printed. This selector decides only what is WORTH
|
|
75
|
+
* recording and classifying.
|
|
76
|
+
*/
|
|
77
|
+
|
|
78
|
+
/** Binaries that verify when they are the head of a segment. */
|
|
79
|
+
const VERIFY_BINARIES: ReadonlySet<string> = new Set([
|
|
80
|
+
'vitest',
|
|
81
|
+
'jest',
|
|
82
|
+
'pytest',
|
|
83
|
+
'phpunit',
|
|
84
|
+
'pest',
|
|
85
|
+
'tsc',
|
|
86
|
+
'eslint',
|
|
87
|
+
'ruff',
|
|
88
|
+
'mypy',
|
|
89
|
+
'pyright',
|
|
90
|
+
'clippy',
|
|
91
|
+
'phpstan',
|
|
92
|
+
'psalm',
|
|
93
|
+
'ecs',
|
|
94
|
+
]);
|
|
95
|
+
|
|
96
|
+
/** Prefixes that delegate to the real command; dropped before classifying. */
|
|
97
|
+
const DELEGATING_HEADS: ReadonlySet<string> = new Set([
|
|
98
|
+
'npx',
|
|
99
|
+
'bunx',
|
|
100
|
+
'sudo',
|
|
101
|
+
'time',
|
|
102
|
+
'command',
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
/** Path prefixes a binary is commonly reached through. */
|
|
106
|
+
const PATH_PREFIXES: readonly string[] = [
|
|
107
|
+
'./',
|
|
108
|
+
'vendor/bin/',
|
|
109
|
+
'.venv/bin/',
|
|
110
|
+
'node_modules/.bin/',
|
|
111
|
+
'bin/',
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
/** A script or subcommand name that names a check rather than a build step. */
|
|
115
|
+
const VERIFY_WORD = /(^|[-_:])(test|tests|check|checks|lint|typecheck|tsc|qa|preflight|smoke|validate)([-_:]|$)/i;
|
|
116
|
+
|
|
117
|
+
/** `npm run build` verifies here; `task build` is not in the audited surface. */
|
|
118
|
+
const NPM_SCRIPT_WORD = /(^|[-_:])(test|tests|check|checks|lint|typecheck|tsc|build)([-_:]|$)/i;
|
|
119
|
+
|
|
120
|
+
/** Segment separators, and whether a failure before them survives. */
|
|
121
|
+
interface Segment {
|
|
122
|
+
readonly words: readonly string[];
|
|
123
|
+
/** The separator that FOLLOWS this segment, or null at the end. */
|
|
124
|
+
readonly nextSep: '&&' | '||' | ';' | '|' | null;
|
|
125
|
+
/** The head word of the segment that follows, lowercased. */
|
|
126
|
+
readonly nextHead: string | null;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Strip an unquoted trailing `#` comment. */
|
|
130
|
+
function stripComment(command: string): string {
|
|
131
|
+
let inSingle = false;
|
|
132
|
+
let inDouble = false;
|
|
133
|
+
for (let i = 0; i < command.length; i += 1) {
|
|
134
|
+
const ch = command[i];
|
|
135
|
+
if (ch === "'" && !inDouble) inSingle = !inSingle;
|
|
136
|
+
else if (ch === '"' && !inSingle) inDouble = !inDouble;
|
|
137
|
+
else if (ch === '#' && !inSingle && !inDouble && (i === 0 || /\s/.test(command[i - 1] ?? ''))) {
|
|
138
|
+
return command.slice(0, i);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return command;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Split into segments, remembering the separator that follows each. */
|
|
145
|
+
function splitSegments(command: string): Segment[] {
|
|
146
|
+
const raw: Array<{ text: string; sep: Segment['nextSep'] }> = [];
|
|
147
|
+
let buf = '';
|
|
148
|
+
let inSingle = false;
|
|
149
|
+
let inDouble = false;
|
|
150
|
+
for (let i = 0; i < command.length; i += 1) {
|
|
151
|
+
const ch = command[i] as string;
|
|
152
|
+
const two = command.slice(i, i + 2);
|
|
153
|
+
if (ch === "'" && !inDouble) {
|
|
154
|
+
inSingle = !inSingle;
|
|
155
|
+
buf += ch;
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (ch === '"' && !inSingle) {
|
|
159
|
+
inDouble = !inDouble;
|
|
160
|
+
buf += ch;
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (!inSingle && !inDouble) {
|
|
164
|
+
if (two === '&&' || two === '||') {
|
|
165
|
+
raw.push({ text: buf, sep: two as '&&' | '||' });
|
|
166
|
+
buf = '';
|
|
167
|
+
i += 1;
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (ch === ';' || ch === '|') {
|
|
171
|
+
raw.push({ text: buf, sep: ch as ';' | '|' });
|
|
172
|
+
buf = '';
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
buf += ch;
|
|
177
|
+
}
|
|
178
|
+
raw.push({ text: buf, sep: null });
|
|
179
|
+
|
|
180
|
+
return raw.map((entry, idx) => {
|
|
181
|
+
const next = raw[idx + 1];
|
|
182
|
+
const nextWords = next ? words(next.text) : [];
|
|
183
|
+
return {
|
|
184
|
+
words: words(entry.text),
|
|
185
|
+
nextSep: entry.sep,
|
|
186
|
+
nextHead: (nextWords[0] ?? null)?.toLowerCase() ?? null,
|
|
187
|
+
};
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Tokenise a segment, dropping leading `VAR=value` assignments. */
|
|
192
|
+
function words(text: string): string[] {
|
|
193
|
+
const tokens = text.trim().split(/\s+/).filter((w) => w.length > 0);
|
|
194
|
+
let i = 0;
|
|
195
|
+
while (i < tokens.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(tokens[i] as string)) i += 1;
|
|
196
|
+
return tokens.slice(i);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Strip a path prefix from a binary name. */
|
|
200
|
+
function bareName(word: string): string {
|
|
201
|
+
let w = word;
|
|
202
|
+
for (const prefix of PATH_PREFIXES) {
|
|
203
|
+
if (w.startsWith(prefix)) {
|
|
204
|
+
w = w.slice(prefix.length);
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
const slash = w.lastIndexOf('/');
|
|
209
|
+
return (slash >= 0 ? w.slice(slash + 1) : w).toLowerCase();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Does the shell discard this segment's failure?
|
|
214
|
+
*
|
|
215
|
+
* `cmd || true`, `cmd || :` and `cmd ; true` all leave the caller with exit 0
|
|
216
|
+
* whatever `cmd` did, so a verification there proves nothing about the tree.
|
|
217
|
+
* Deliberately narrow: `cmd || exit 1` propagates the failure and still counts.
|
|
218
|
+
*/
|
|
219
|
+
function failureIsDiscarded(seg: Segment): boolean {
|
|
220
|
+
if (seg.nextSep === '||') return seg.nextHead === 'true' || seg.nextHead === ':';
|
|
221
|
+
if (seg.nextSep === ';') return seg.nextHead === 'true' || seg.nextHead === ':';
|
|
222
|
+
return false;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** Classify one segment on its head. */
|
|
226
|
+
function segmentVerifies(rawWords: readonly string[]): boolean {
|
|
227
|
+
const w = rawWords.filter((x) => x.length > 0);
|
|
228
|
+
let idx = 0;
|
|
229
|
+
// Peel delegating prefixes: `npx vitest run`, `sudo make test`.
|
|
230
|
+
while (idx < w.length && DELEGATING_HEADS.has(bareName(w[idx] as string))) idx += 1;
|
|
231
|
+
if (idx >= w.length) return false;
|
|
232
|
+
|
|
233
|
+
const head = bareName(w[idx] as string);
|
|
234
|
+
const rest = w.slice(idx + 1).map((x) => x.toLowerCase());
|
|
235
|
+
const arg1 = rest[0] ?? '';
|
|
236
|
+
|
|
237
|
+
if (VERIFY_BINARIES.has(head)) return true;
|
|
238
|
+
|
|
239
|
+
switch (head) {
|
|
240
|
+
case 'npm':
|
|
241
|
+
case 'pnpm':
|
|
242
|
+
case 'yarn':
|
|
243
|
+
case 'bun': {
|
|
244
|
+
// `pnpm dlx vitest` delegates; `npm run test:ts` names a script.
|
|
245
|
+
if (arg1 === 'dlx' || arg1 === 'exec') return segmentVerifies(rest.slice(1));
|
|
246
|
+
const script = arg1 === 'run' ? (rest[1] ?? '') : arg1;
|
|
247
|
+
return NPM_SCRIPT_WORD.test(script);
|
|
248
|
+
}
|
|
249
|
+
case 'task':
|
|
250
|
+
return VERIFY_WORD.test(arg1) || /^ci([-_:]|$)/i.test(arg1);
|
|
251
|
+
case 'cargo':
|
|
252
|
+
return arg1 === 'test' || arg1 === 'check' || arg1 === 'clippy';
|
|
253
|
+
case 'go':
|
|
254
|
+
return arg1 === 'test' || arg1 === 'vet';
|
|
255
|
+
case 'make':
|
|
256
|
+
return VERIFY_WORD.test(arg1) || /^ci([-_:]|$)/i.test(arg1);
|
|
257
|
+
case 'composer':
|
|
258
|
+
return VERIFY_WORD.test(arg1) || arg1 === 'phpstan';
|
|
259
|
+
case 'php':
|
|
260
|
+
return arg1 === 'artisan' && (rest[1] ?? '') === 'test';
|
|
261
|
+
case 'artisan':
|
|
262
|
+
return arg1 === 'test';
|
|
263
|
+
case 'python':
|
|
264
|
+
case 'python3':
|
|
265
|
+
return arg1 === '-m' && VERIFY_BINARIES.has(rest[1] ?? '');
|
|
266
|
+
case 'node':
|
|
267
|
+
case 'deno':
|
|
268
|
+
// `node --test` is a runner selected by a FLAG, not by a
|
|
269
|
+
// subcommand, so head-anchoring alone would refuse it. Caught by
|
|
270
|
+
// `verification_evidence.test.ts` when this predicate narrowed:
|
|
271
|
+
// the old token-anywhere regex accepted it by accident and neither
|
|
272
|
+
// explicit list named it, so the union seeding missed it too.
|
|
273
|
+
return rest.some((a) => a === 'test' || a.startsWith('--test'));
|
|
274
|
+
case 'scripts-run': {
|
|
275
|
+
// `./scripts-run src/scripts/lint_x` verifies; `…/rule_activation_census`
|
|
276
|
+
// writes a report and asserts nothing, which is why this keys on the
|
|
277
|
+
// script's own prefix rather than on the runner.
|
|
278
|
+
const script = bareName(arg1);
|
|
279
|
+
return /^(check|lint|verify|validate)[-_]/.test(script);
|
|
280
|
+
}
|
|
281
|
+
default:
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* True when the command runs something that asserts the tree still holds.
|
|
288
|
+
*
|
|
289
|
+
* Classified per shell segment on the segment's HEAD, so a verification token
|
|
290
|
+
* appearing as an argument — `ls tests`, `git commit -m "fix ci"` — never
|
|
291
|
+
* counts, and a chained `task sync && task ci` counts on its second segment.
|
|
292
|
+
*/
|
|
293
|
+
export function isVerificationCommand(command: string): boolean {
|
|
294
|
+
if (!command) return false;
|
|
295
|
+
const segments = splitSegments(stripComment(command));
|
|
296
|
+
return segments.some((seg) => !failureIsDiscarded(seg) && segmentVerifies(seg.words));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* The RECORDER's net — deliberately wider than the predicate above, and not a
|
|
301
|
+
* leftover of the two-regex era it replaces.
|
|
302
|
+
*
|
|
303
|
+
* The two answer different questions. `isVerificationCommand` answers *did
|
|
304
|
+
* this verify*, and a false positive there clears an unverified edit, so it is
|
|
305
|
+
* head-anchored and narrow. This one answers *is this worth writing down for
|
|
306
|
+
* `classifyRun` to judge*, and there the costly error is the opposite: a
|
|
307
|
+
* recorder that filtered `echo test` out would leave the turn-end gate unable
|
|
308
|
+
* to tell "the turn ran a non-verification command" from "the turn ran
|
|
309
|
+
* nothing at all", and `not_a_verification_command` is a verdict the gate
|
|
310
|
+
* needs to SEE rather than infer from silence.
|
|
311
|
+
*
|
|
312
|
+
* That is why unifying the two call sites into one predicate was tried and
|
|
313
|
+
* rejected on 2026-09-30: it is not two spellings of one idea, it is two
|
|
314
|
+
* questions with opposite error costs. What was wrong before was that each
|
|
315
|
+
* question had its own private regex in its own file and the two also
|
|
316
|
+
* disagreed about the FIRST question. Both now live here, and only this one
|
|
317
|
+
* matches on a token anywhere in the line.
|
|
318
|
+
*/
|
|
319
|
+
const _WIDE_CANDIDATE_RE =
|
|
320
|
+
/\b(test|tests|vitest|jest|pytest|phpunit|pest|tsc|eslint|ruff|mypy|pyright|clippy|phpstan|typecheck|lint[-_:a-z]*|build|ci|preflight|smoke[-_:a-z]*|check[-_:a-z]*|validate[-_:a-z]*)\b|\b(task|npm|pnpm|yarn|composer|cargo|go|make|php artisan|bun)\s+(run\s+)?\S*(test|check|lint|build|ci|typecheck)/i;
|
|
321
|
+
|
|
322
|
+
export function mightBeVerification(command: string): boolean {
|
|
323
|
+
return _WIDE_CANDIDATE_RE.test(command);
|
|
324
|
+
}
|