@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
|
@@ -31,19 +31,23 @@
|
|
|
31
31
|
* "per-host `enforced_by` resolved from the table, not from a host name" means
|
|
32
32
|
* in practice.
|
|
33
33
|
*
|
|
34
|
-
* Once per
|
|
34
|
+
* Once per distinct SEARCH TARGET, capped at five lines per session (step 2.2),
|
|
35
|
+
* so it cannot nag. It was once-per-session until step 2.1 added `Bash`: with
|
|
36
|
+
* shell searches in scope a single line covered the first `grep` of a session
|
|
37
|
+
* and nothing after it, which is the opposite of the problem — the state that
|
|
38
|
+
* matters is the state at the question being asked.
|
|
35
39
|
* fail_closed: false — every error path returns allow; a context line must
|
|
36
40
|
* never break a tool call.
|
|
37
41
|
*/
|
|
42
|
+
import { createHash } from 'node:crypto';
|
|
38
43
|
import fs from 'node:fs';
|
|
39
44
|
import path from 'node:path';
|
|
40
45
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
41
46
|
|
|
42
47
|
import { type GraphState, graphState } from '../code_graph/detect.js';
|
|
43
48
|
import { readHookStdin } from './hook_stdin.js';
|
|
49
|
+
import { EXIT_ALLOW, EXIT_WARN } from './exit_codes.js';
|
|
44
50
|
|
|
45
|
-
const EXIT_ALLOW = 0;
|
|
46
|
-
const EXIT_WARN = 2;
|
|
47
51
|
const CODE_EXT = /\.(php|ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i;
|
|
48
52
|
|
|
49
53
|
type JsonValue = string | number | boolean | null | JsonValue[] | { [k: string]: JsonValue };
|
|
@@ -56,13 +60,127 @@ function isObject(v: unknown): v is JsonObject {
|
|
|
56
60
|
interface ToolIntent {
|
|
57
61
|
isSearch: boolean;
|
|
58
62
|
isCodeRead: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* What this call is ABOUT — a Grep/Glob pattern, a read path, or the search
|
|
65
|
+
* term of a shell search. The per-target latch key (step 2.2): the same
|
|
66
|
+
* question asked twice is one question, and a hook that answered it once
|
|
67
|
+
* has nothing to add the second time.
|
|
68
|
+
*
|
|
69
|
+
* Empty when the call is neither a search nor a code read, and never the
|
|
70
|
+
* raw command line of an arbitrary Bash call — see {@link bashSearchToken}.
|
|
71
|
+
*/
|
|
72
|
+
target: string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The shell search heads this hook recognises.
|
|
77
|
+
*
|
|
78
|
+
* A closed list on purpose. "Any Bash command might be a search" is the reading
|
|
79
|
+
* that makes the hook chatty — Risk-Register rank 2 — so a command whose head
|
|
80
|
+
* is not here is silent AND does not latch, exactly as a non-code `Read` is.
|
|
81
|
+
*/
|
|
82
|
+
const SEARCH_HEADS = new Set(['grep', 'rg', 'ag', 'find', 'ack', 'fd']);
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Short options that consume the NEXT word as their value.
|
|
86
|
+
*
|
|
87
|
+
* Without this the value is read as the search term: a completion review of
|
|
88
|
+
* step 2.1 measured `grep -A 5 foo` keying the latch on `"5"` and
|
|
89
|
+
* `rg -g *.ts alpha` keying on `"*.ts"` — which also silenced a second,
|
|
90
|
+
* genuinely different search behind the same glob. A latch keyed on an option's
|
|
91
|
+
* argument is worse than no latch: it collapses distinct questions onto one slot.
|
|
92
|
+
*/
|
|
93
|
+
const VALUE_TAKING = new Set([
|
|
94
|
+
'-A', '-B', '-C', '-m', '-e', '-f', '-g', '-t', '-T', '-d', '--include',
|
|
95
|
+
'--exclude', '--glob', '--max-count', '--after-context', '--before-context',
|
|
96
|
+
'--context', '--regexp', '--file', '--type', '--maxdepth', '--depth',
|
|
97
|
+
]);
|
|
98
|
+
|
|
99
|
+
/** Asking a tool for its own usage is not a search, and must not spend a slot. */
|
|
100
|
+
const NOT_A_SEARCH = new Set(['--help', '-h', '--version', '-V', '--usage']);
|
|
101
|
+
|
|
102
|
+
/** `find` carries its subject in `-name`, not in the first positional. */
|
|
103
|
+
const FIND_SUBJECT = new Set(['-name', '-iname', '-path', '-ipath', '-regex']);
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* The search term inside ONE pipeline segment, or `null`.
|
|
107
|
+
*
|
|
108
|
+
* Split out from {@link bashSearchToken} so a pipeline can be scanned segment
|
|
109
|
+
* by segment: `cat x | grep needle` is a search, and reading only the head of
|
|
110
|
+
* the whole command missed every one of them. The same review measured that.
|
|
111
|
+
*/
|
|
112
|
+
function segmentSearchToken(words: readonly string[]): string | null {
|
|
113
|
+
if (words.length === 0) return null;
|
|
114
|
+
let i = 0;
|
|
115
|
+
let head = (words[0] as string).split('/').pop() as string;
|
|
116
|
+
if (head === 'git') {
|
|
117
|
+
// `git -C <path> grep <term>` is the form this tree's own token-efficiency
|
|
118
|
+
// rule mandates, and a `words[1] === 'grep'` test missed all of it. Scan
|
|
119
|
+
// past git's own options to the subcommand.
|
|
120
|
+
let j = 1;
|
|
121
|
+
while (j < words.length && (words[j] as string).startsWith('-')) {
|
|
122
|
+
j += VALUE_TAKING.has(words[j] as string) || words[j] === '-C' ? 2 : 1;
|
|
123
|
+
}
|
|
124
|
+
if (words[j] !== 'grep') return null;
|
|
125
|
+
head = 'git grep';
|
|
126
|
+
i = j + 1;
|
|
127
|
+
} else if (SEARCH_HEADS.has(head)) {
|
|
128
|
+
i = 1;
|
|
129
|
+
} else {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
const rest = words.slice(i);
|
|
133
|
+
if (rest.some((w) => NOT_A_SEARCH.has(w))) return null;
|
|
134
|
+
if (head === 'find') {
|
|
135
|
+
const at = rest.findIndex((w) => FIND_SUBJECT.has(w));
|
|
136
|
+
if (at !== -1 && at + 1 < rest.length) return strip(rest[at + 1] as string);
|
|
137
|
+
}
|
|
138
|
+
for (let k = 0; k < rest.length; k += 1) {
|
|
139
|
+
const w = rest[k] as string;
|
|
140
|
+
if (w.startsWith('-')) {
|
|
141
|
+
if (VALUE_TAKING.has(w)) k += 1; // its value is not the subject
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
return strip(w);
|
|
145
|
+
}
|
|
146
|
+
return head;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function strip(w: string): string {
|
|
150
|
+
return w.replace(/^["']|["']$/g, '');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The thing a shell search is searching FOR, or `null` when the command is not
|
|
155
|
+
* a structure search at all.
|
|
156
|
+
*
|
|
157
|
+
* Reads EVERY pipeline segment, not just the first: `cat x | grep needle` and
|
|
158
|
+
* `… | rg symbol` are searches, and they are the common shape. The first
|
|
159
|
+
* segment that is a search wins, so one line yields one latch key.
|
|
160
|
+
*
|
|
161
|
+
* `git grep` is the one two-word head and is resolved past git's own options.
|
|
162
|
+
* Failing a positional subject the key is the command's head, which collapses a
|
|
163
|
+
* flag-only search onto one slot rather than leaking a command line into a
|
|
164
|
+
* state file — and the key is stored as a digest in any case
|
|
165
|
+
* ({@link latchKey}).
|
|
166
|
+
*/
|
|
167
|
+
export function bashSearchToken(command: string): string | null {
|
|
168
|
+
for (const segment of command.split(/\||&&|;/)) {
|
|
169
|
+
const words = segment.trim().split(/\s+/).filter((w) => w !== '');
|
|
170
|
+
const hit = segmentSearchToken(words);
|
|
171
|
+
if (hit !== null) return hit;
|
|
172
|
+
}
|
|
173
|
+
return null;
|
|
59
174
|
}
|
|
60
175
|
|
|
61
176
|
/**
|
|
62
177
|
* Best-effort read of the intercepted tool + its target from the envelope.
|
|
63
178
|
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
179
|
+
* The manifest's `tools:` filter is pinned against exactly this branch surface
|
|
180
|
+
* — Grep, Glob, Read, Bash. `Bash` joined it in step 2.1 for the reason the
|
|
181
|
+
* step gives: the searches an agent actually runs in this tree are shell
|
|
182
|
+
* searches, so a matcher that saw only the structured search tools was watching
|
|
183
|
+
* the door nobody uses.
|
|
66
184
|
*/
|
|
67
185
|
export function classifyTool(envelope: JsonObject): ToolIntent {
|
|
68
186
|
const payload = isObject(envelope['payload']) ? envelope['payload'] : envelope;
|
|
@@ -72,13 +190,29 @@ export function classifyTool(envelope: JsonObject): ToolIntent {
|
|
|
72
190
|
const ti = (isObject(payload['tool_input']) ? payload['tool_input'] : envelope['tool_input']) as
|
|
73
191
|
| JsonObject
|
|
74
192
|
| undefined;
|
|
75
|
-
const
|
|
76
|
-
|
|
193
|
+
const silent: ToolIntent = { isSearch: false, isCodeRead: false, target: '' };
|
|
194
|
+
|
|
195
|
+
if (name === 'Grep' || name === 'Glob') {
|
|
196
|
+
const pat = isObject(ti) ? (ti['pattern'] ?? ti['query']) : undefined;
|
|
197
|
+
return { isSearch: true, isCodeRead: false, target: typeof pat === 'string' ? pat : name };
|
|
198
|
+
}
|
|
77
199
|
if (name === 'Read' && isObject(ti)) {
|
|
78
200
|
const fp = ti['file_path'] ?? ti['path'];
|
|
79
|
-
if (typeof fp === 'string'
|
|
201
|
+
if (typeof fp === 'string' && CODE_EXT.test(fp)) {
|
|
202
|
+
return { isSearch: false, isCodeRead: true, target: fp };
|
|
203
|
+
}
|
|
204
|
+
return silent;
|
|
205
|
+
}
|
|
206
|
+
if (name === 'Bash' && isObject(ti)) {
|
|
207
|
+
const cmd = ti['command'];
|
|
208
|
+
if (typeof cmd !== 'string') return silent;
|
|
209
|
+
const token = bashSearchToken(cmd);
|
|
210
|
+
// Any other Bash command is silent AND does not latch: it consumed no
|
|
211
|
+
// slot, so the next real search still gets its line.
|
|
212
|
+
if (token === null) return silent;
|
|
213
|
+
return { isSearch: true, isCodeRead: false, target: token };
|
|
80
214
|
}
|
|
81
|
-
return
|
|
215
|
+
return silent;
|
|
82
216
|
}
|
|
83
217
|
|
|
84
218
|
function sessionId(envelope: JsonObject): string {
|
|
@@ -89,33 +223,117 @@ function sessionId(envelope: JsonObject): string {
|
|
|
89
223
|
function latchFile(root: string): string {
|
|
90
224
|
return path.join(root, 'agents', 'runtime', 'state', 'code-graph-context.json');
|
|
91
225
|
}
|
|
92
|
-
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* At most this many context lines in one session (step 2.2).
|
|
229
|
+
*
|
|
230
|
+
* The cap is the chattiness bound, and it is the half that does not depend on
|
|
231
|
+
* the agent's behaviour: per-target alone is unbounded, because a session that
|
|
232
|
+
* greps thirty distinct symbols would hear thirty lines and the consumer would
|
|
233
|
+
* turn the hook off. Five is a stated default, not a measurement — a session
|
|
234
|
+
* that has been told the graph's state five times has been told.
|
|
235
|
+
*/
|
|
236
|
+
export const MAX_CONTEXT_LINES_PER_SESSION = 5;
|
|
237
|
+
|
|
238
|
+
/** How many sessions the latch file retains before evicting the oldest. */
|
|
239
|
+
export const MAX_SESSIONS_LATCHED = 200;
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* What the latch file records for a target: a truncated digest, never the
|
|
243
|
+
* target itself.
|
|
244
|
+
*
|
|
245
|
+
* A council review of step 2.1 (2026-10-01, anthropic + openai) asked for one
|
|
246
|
+
* concrete thing before the shell tool was routed here — that command text is
|
|
247
|
+
* neither persisted nor carried into diagnostics. Routing on the shell tool
|
|
248
|
+
* makes the target a search TERM an operator typed, and a term can be a
|
|
249
|
+
* customer name, a token fragment or an internal hostname. Nothing downstream
|
|
250
|
+
* needs to read it back: the latch answers "have I spoken about this one", and
|
|
251
|
+
* equality over a digest answers that exactly as well.
|
|
252
|
+
*
|
|
253
|
+
* Not a security boundary and not claimed as one — a short digest of a short
|
|
254
|
+
* term is guessable by anyone who can enumerate terms. It is the same
|
|
255
|
+
* PII-exclusion-by-construction move `domain-safety-pii` § Surface 2 asks for:
|
|
256
|
+
* the file is shaped so the plaintext is not in it to leak.
|
|
257
|
+
*/
|
|
258
|
+
export function latchKey(target: string): string {
|
|
259
|
+
return createHash('sha256').update(target).digest('hex').slice(0, 16);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Should this target get a line, given what this session already heard?
|
|
264
|
+
*
|
|
265
|
+
* Pure, so the two rules it encodes can be tested without a filesystem: a
|
|
266
|
+
* repeat is silent at any count, and a NEW target is silent once the cap is
|
|
267
|
+
* reached. Takes the target in the clear and hashes here, so no caller can
|
|
268
|
+
* forget to.
|
|
269
|
+
*/
|
|
270
|
+
export function speaksFor(spoken: readonly string[], target: string): boolean {
|
|
271
|
+
if (spoken.includes(latchKey(target))) return false;
|
|
272
|
+
return spoken.length < MAX_CONTEXT_LINES_PER_SESSION;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function readLatch(root: string): Record<string, string[]> {
|
|
93
276
|
try {
|
|
94
|
-
const
|
|
95
|
-
|
|
277
|
+
const raw = JSON.parse(fs.readFileSync(latchFile(root), 'utf-8')) as Record<string, unknown>;
|
|
278
|
+
const out: Record<string, string[]> = {};
|
|
279
|
+
for (const [k, v] of Object.entries(raw)) {
|
|
280
|
+
// A pre-2.2 file stored `true` per session. Read it as "this session
|
|
281
|
+
// is done", which is what it meant — never as an empty list, which
|
|
282
|
+
// would hand an upgraded session a fresh budget it already spent.
|
|
283
|
+
if (Array.isArray(v)) out[k] = v.filter((x): x is string => typeof x === 'string');
|
|
284
|
+
else if (v === true) out[k] = Array.from({ length: MAX_CONTEXT_LINES_PER_SESSION }, (_, i) => `legacy:${String(i)}`);
|
|
285
|
+
}
|
|
286
|
+
return out;
|
|
96
287
|
} catch {
|
|
97
|
-
return
|
|
288
|
+
return {};
|
|
98
289
|
}
|
|
99
290
|
}
|
|
100
|
-
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* The cheap read-only half, so the `git status` probe behind `graphState` runs
|
|
294
|
+
* only for a call that could still produce a line (Risk-Register rank 3).
|
|
295
|
+
*
|
|
296
|
+
* Advisory only — {@link latchTarget} re-reads and remains the authority. A
|
|
297
|
+
* disagreement between the two can only come from a concurrent write, and it
|
|
298
|
+
* resolves toward silence, which is the safe direction for a context line.
|
|
299
|
+
*/
|
|
300
|
+
export function wouldSpeak(root: string, session: string, target: string): boolean {
|
|
301
|
+
return speaksFor(readLatch(root)[session] ?? [], target);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* Decide and record in one step, because the two must not drift apart: a reader
|
|
306
|
+
* that says yes and a writer that records something else is how a cap leaks.
|
|
307
|
+
* Returns whether the caller may speak.
|
|
308
|
+
*/
|
|
309
|
+
export function latchTarget(root: string, session: string, target: string): boolean {
|
|
310
|
+
const state = readLatch(root);
|
|
311
|
+
const spoken = state[session] ?? [];
|
|
312
|
+
if (!speaksFor(spoken, target)) return false;
|
|
101
313
|
try {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
314
|
+
state[session] = [...spoken, latchKey(target)];
|
|
315
|
+
// Nothing prunes a finished session, so without this the file
|
|
316
|
+
// accumulated up to five digests per session id for the life of the
|
|
317
|
+
// repository — a review's finding, and the sibling instrument added in
|
|
318
|
+
// the same change caps itself and says why local-and-gitignored is not
|
|
319
|
+
// the same as free. Insertion order is the eviction order: the oldest
|
|
320
|
+
// session is the one least likely to speak again, and re-latching a
|
|
321
|
+
// dropped session costs one extra line, never a wrong answer.
|
|
322
|
+
const ids = Object.keys(state);
|
|
323
|
+
for (const old of ids.slice(0, Math.max(0, ids.length - MAX_SESSIONS_LATCHED))) {
|
|
324
|
+
if (old !== session) delete state[old];
|
|
108
325
|
}
|
|
109
|
-
|
|
326
|
+
const p = latchFile(root);
|
|
110
327
|
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
111
328
|
fs.writeFileSync(p, JSON.stringify(state));
|
|
112
329
|
} catch {
|
|
113
330
|
/* fail-open: a persistence failure must not break the tool call */
|
|
114
331
|
}
|
|
332
|
+
return true;
|
|
115
333
|
}
|
|
116
334
|
|
|
117
335
|
/**
|
|
118
|
-
* The
|
|
336
|
+
* The four-state staleness token this hook reports.
|
|
119
337
|
*
|
|
120
338
|
* Re-exported, not defined here: Phase 3's verbs must print the same token, and
|
|
121
339
|
* an engine module importing this hook to learn it would invert the dependency
|
|
@@ -140,6 +358,13 @@ export function contextLine(state: GraphState): string {
|
|
|
140
358
|
'grep is the other path — say which one answered.'
|
|
141
359
|
);
|
|
142
360
|
}
|
|
361
|
+
if (state === 'edited') {
|
|
362
|
+
return (
|
|
363
|
+
'code-graph: level with HEAD, but indexed files are edited in the working tree. ' +
|
|
364
|
+
'Relationship answers miss anything written since the last build — ' +
|
|
365
|
+
'`agent-config code-graph refresh` picks the edits up, or use grep and say so.'
|
|
366
|
+
);
|
|
367
|
+
}
|
|
143
368
|
const n = state.slice('behind:'.length);
|
|
144
369
|
return (
|
|
145
370
|
`code-graph: ${n} commit(s) behind. Relationship answers may miss anything newer — ` +
|
|
@@ -161,11 +386,14 @@ export function main(): number {
|
|
|
161
386
|
const pr = envelope['workspace_root'] ?? envelope['project_root'];
|
|
162
387
|
const root = typeof cwd === 'string' && cwd ? cwd : typeof pr === 'string' && pr ? pr : '.';
|
|
163
388
|
|
|
164
|
-
const { isSearch, isCodeRead } = classifyTool(envelope);
|
|
389
|
+
const { isSearch, isCodeRead, target } = classifyTool(envelope);
|
|
165
390
|
if (!isSearch && !isCodeRead) return EXIT_ALLOW;
|
|
166
391
|
|
|
167
392
|
const session = sessionId(envelope);
|
|
168
|
-
|
|
393
|
+
// Once per distinct target, at most five per session (2.2). Checked BEFORE
|
|
394
|
+
// `graphState`, because that call now runs a `git status` probe and a line
|
|
395
|
+
// this session will not emit must not pay for one.
|
|
396
|
+
if (!wouldSpeak(root, session, target)) return EXIT_ALLOW;
|
|
169
397
|
|
|
170
398
|
let state: GraphState;
|
|
171
399
|
try {
|
|
@@ -177,7 +405,7 @@ export function main(): number {
|
|
|
177
405
|
// so a session that builds a graph mid-flight still gets the line once.
|
|
178
406
|
if (state === 'absent') return EXIT_ALLOW;
|
|
179
407
|
|
|
180
|
-
|
|
408
|
+
if (!latchTarget(root, session, target)) return EXIT_ALLOW;
|
|
181
409
|
process.stdout.write(`${JSON.stringify({ decision: 'warn', reason: contextLine(state) })}\n`);
|
|
182
410
|
return EXIT_WARN;
|
|
183
411
|
}
|
|
@@ -47,9 +47,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
47
47
|
import { isScannable, scanText, type CommentFinding } from '../lint_code_comments.js';
|
|
48
48
|
import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
|
|
49
49
|
import { readHookStdin } from './hook_stdin.js';
|
|
50
|
-
|
|
51
|
-
const EXIT_ALLOW = 0;
|
|
52
|
-
const EXIT_WARN = 2;
|
|
50
|
+
import { EXIT_ALLOW, EXIT_WARN } from './exit_codes.js';
|
|
53
51
|
|
|
54
52
|
/** Findings named in the advisory line. The rest are counted, never listed. */
|
|
55
53
|
export const MAX_REPORTED = 3;
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a concern's DECLARED SEVERITY means when the concern produced no verdict.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `dispatch_hook.ts` on 2026-10-03, and not for tidiness.
|
|
5
|
+
* `check_source_size_budget` measured that file at exactly 1,500 lines on
|
|
6
|
+
* `main` — sitting precisely on the cap — and step 3.3's additions put it 203
|
|
7
|
+
* lines over. The ratchet turns one way, and the doctrine every entry in
|
|
8
|
+
* `gate-violation-baselines.json` records is that a MOVE gives the budget back
|
|
9
|
+
* where raising the baseline spends it. The same move
|
|
10
|
+
* `_lib/concern_sla_window.ts` made out of `bench_hook_latency.ts`.
|
|
11
|
+
*
|
|
12
|
+
* It is also the better boundary on its own terms. Everything here answers one
|
|
13
|
+
* question — what a concern's declaration permits and requires when the
|
|
14
|
+
* concern itself said nothing — and none of it is about RUNNING a concern,
|
|
15
|
+
* which is what the dispatcher does. `stdin_failure_policy.ts` is its sibling:
|
|
16
|
+
* the same question for a payload the dispatcher could not read.
|
|
17
|
+
*
|
|
18
|
+
* The dispatcher re-exports every symbol here, so callers and tests that
|
|
19
|
+
* imported them from `dispatch_hook.js` are unaffected.
|
|
20
|
+
*/
|
|
21
|
+
import { EXIT_ALLOW, EXIT_BLOCK, EXIT_ERROR } from './exit_codes.js';
|
|
22
|
+
|
|
23
|
+
type JsonObject = Record<string, unknown>;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* P0.2 (road-to-rule-coherence) — is this concern declared advisory?
|
|
27
|
+
*
|
|
28
|
+
* An advisory concern MUST never produce a BLOCK verdict on any host. Four
|
|
29
|
+
* PreToolUse concerns document themselves as advisory in prose
|
|
30
|
+
* (`design_slop_hook`: "FLAGS, NEVER A BLOCK") while the transport happily
|
|
31
|
+
* turned their WARN into a host-level deny. Prose is not enforcement: the
|
|
32
|
+
* manifest now declares severity and the dispatcher enforces the ceiling.
|
|
33
|
+
*/
|
|
34
|
+
export function _is_advisory(concern: JsonObject): boolean {
|
|
35
|
+
return String(concern["severity"] ?? "").trim().toLowerCase() === "advisory";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Is this concern declared `severity: blocking`?
|
|
40
|
+
*
|
|
41
|
+
* The positive twin of `_is_advisory`, and NOT its negation. A concern whose
|
|
42
|
+
* `severity` is absent, misspelt or any third value is neither — and step 3.3
|
|
43
|
+
* below promotes a crash to a refusal only on an EXPLICIT `blocking`, so an
|
|
44
|
+
* undeclared concern keeps the historical fail-open rather than inheriting a
|
|
45
|
+
* refusal from a typo. `lint_hook_manifest` requires the key, so the middle
|
|
46
|
+
* case should not exist; this reads it as the safe direction anyway, because
|
|
47
|
+
* the cost of the two mistakes is not symmetric.
|
|
48
|
+
*/
|
|
49
|
+
export function _is_blocking(concern: JsonObject): boolean {
|
|
50
|
+
return String(concern["severity"] ?? "").trim().toLowerCase() === "blocking";
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* What an `rc >= 3` (crash / could-not-decide) becomes — step 3.3 of
|
|
55
|
+
* `road-to-a-kernel-that-guards-its-plumbing`.
|
|
56
|
+
*
|
|
57
|
+
* WHAT CHANGED, AND WHAT AUTHORISES IT.
|
|
58
|
+
* Until this step the resolution read the `fail_closed:` FLAG: a crash blocked
|
|
59
|
+
* when the concern opted in and allowed otherwise. Three of nine
|
|
60
|
+
* `severity: blocking` concerns carried the flag, so six guards declared
|
|
61
|
+
* blocking in the manifest — the declaration that AUTHORISES a refusal, and the
|
|
62
|
+
* one `_is_advisory` already enforces as a ceiling — and then allowed the call
|
|
63
|
+
* through whenever they crashed. A guard that refuses when it works and permits
|
|
64
|
+
* when it breaks is not a guard: a crash is exactly the moment the check did
|
|
65
|
+
* not happen, so it is the moment the answer matters most.
|
|
66
|
+
*
|
|
67
|
+
* So `severity` now decides both directions. It is the ceiling (an advisory
|
|
68
|
+
* concern may never block) AND the floor (a blocking concern that could not
|
|
69
|
+
* decide refuses). `fail_closed:` is no longer consulted on this branch — all
|
|
70
|
+
* three concerns carrying it are `severity: blocking`, so no verdict moves
|
|
71
|
+
* because it stopped being read here.
|
|
72
|
+
*
|
|
73
|
+
* WHAT A SLOW CONCERN IS NOT, ON EITHER PATH.
|
|
74
|
+
* This branch resolves a concern that produced NO VERDICT. It is not a latency
|
|
75
|
+
* gate and no reading of `concern_sla_ms` reaches it. On the default in-process
|
|
76
|
+
* route a kill-timeout cannot preempt synchronous code, so an overrun there is
|
|
77
|
+
* a POST-HOC observation about a concern that did in fact answer — refusing it
|
|
78
|
+
* afterwards would deny a call whose guard passed, buying no enforcement while
|
|
79
|
+
* spending exactly the availability risk Risk 1 of the owning roadmap holds
|
|
80
|
+
* down. On the spawn route the timeout stays the historical 30 s, because the
|
|
81
|
+
* registered SLA measures in-process concern work and is three to fifteen times
|
|
82
|
+
* smaller than interpreter startup alone; `SPAWN_TIMEOUT_MS` carries that
|
|
83
|
+
* measurement and the probe that refused the substitution.
|
|
84
|
+
*
|
|
85
|
+
* So `sla_ms x 3` remains what the warn-only window made it: a number the bench
|
|
86
|
+
* prints beside each measured p95, gating nothing. Its readings are recorded
|
|
87
|
+
* with the owning plan and no count is repeated here — a review of an earlier
|
|
88
|
+
* revision found two different tallies in one diff, because a number had been
|
|
89
|
+
* pasted into an implementation comment while the record kept accumulating.
|
|
90
|
+
* The readings validate the bound as an OBSERVATION, which is the only thing
|
|
91
|
+
* this step asks of them.
|
|
92
|
+
*
|
|
93
|
+
* The discriminator for this branch is whether a VERDICT EXISTS. A crash and a
|
|
94
|
+
* killed spawn leave none, so they fail closed. A slow success leaves one, so
|
|
95
|
+
* it is honoured, however slow it was.
|
|
96
|
+
*
|
|
97
|
+
* THE ONE-RETRY ESCAPE, AND WHY THE STOP SLOT NEEDS IT.
|
|
98
|
+
* `turn-end-gate` and `run-continuation` are `severity: blocking` on `stop`,
|
|
99
|
+
* and `tests/hooks/concern_severity.test.ts` records the hazard in as many
|
|
100
|
+
* words: a turn-end gate that fails closed "does not degrade, it wedges the
|
|
101
|
+
* session". Neither carries `skip_on_refusal_retry`, so without this clause a
|
|
102
|
+
* DETERMINISTIC crash in either would refuse every Stop, indefinitely, with no
|
|
103
|
+
* escape the user can reach.
|
|
104
|
+
*
|
|
105
|
+
* So the promotion is spent ONCE. On the retry the host itself marks
|
|
106
|
+
* (`stop_hook_active`, read by `_is_refusal_retry`) a crash falls back to
|
|
107
|
+
* fail-open and the turn ends. The legitimate refusal path is untouched: a
|
|
108
|
+
* concern that DECIDED to refuse returns `EXIT_BLOCK` and never reaches this
|
|
109
|
+
* branch. What a broken concern loses is only the power to refuse forever on
|
|
110
|
+
* the strength of being broken.
|
|
111
|
+
*
|
|
112
|
+
* `pre_tool_use` is untouched by the clause — `_is_refusal_retry` is false on
|
|
113
|
+
* every event but `stop` — and that asymmetry is deliberate. A crashing
|
|
114
|
+
* tool-call guard refuses every attempt, which is the right answer there: the
|
|
115
|
+
* user can run a different command and nothing is wedged, whereas a turn that
|
|
116
|
+
* cannot end leaves no move at all.
|
|
117
|
+
*/
|
|
118
|
+
export function _resolve_execution_failure(
|
|
119
|
+
concern: JsonObject,
|
|
120
|
+
refusal_retry: boolean,
|
|
121
|
+
): number {
|
|
122
|
+
if (!_is_blocking(concern)) return EXIT_ALLOW;
|
|
123
|
+
if (refusal_retry) return EXIT_ALLOW;
|
|
124
|
+
return EXIT_BLOCK;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The spawn kill-timeout, and why `concern_sla_ms` is NOT what sets it.
|
|
129
|
+
*
|
|
130
|
+
* Step 3.3 of `road-to-a-kernel-that-guards-its-plumbing` reads "timeout for a
|
|
131
|
+
* blocking concern becomes `sla_ms x 3`". That clause is NOT landed, and the
|
|
132
|
+
* reason is a unit mismatch that is measurable rather than arguable.
|
|
133
|
+
*
|
|
134
|
+
* `concern_sla_ms` is derived from the per-concern `duration_ms` this file
|
|
135
|
+
* reports into the timings sink, which on the in-process route brackets
|
|
136
|
+
* `main_fn(argsList)` and nothing else — the concern's OWN work. The registered
|
|
137
|
+
* rows are 0.564 to 1.587 ms, so `sla_ms x 3` is 1.7 to 4.8 ms.
|
|
138
|
+
*
|
|
139
|
+
* A `spawnSync` timeout bounds something else entirely: fork, interpreter
|
|
140
|
+
* start, module graph load, and only then the same work. The bench's own
|
|
141
|
+
* control row measures the interpreter term alone at p95 17 ms on the 1 vCPU
|
|
142
|
+
* reference class and 26 ms on the GitHub runner — three to fifteen times the
|
|
143
|
+
* whole proposed bound, before the concern has run a line.
|
|
144
|
+
*
|
|
145
|
+
* Wiring it was tried on this branch and probed against the real dispatcher.
|
|
146
|
+
* With `AGENT_CONFIG_HOOKS_ISOLATED=1` on `claude/pre_tool_use`, all six
|
|
147
|
+
* blocking concerns returned `ETIMEDOUT`, left no verdict, and — resolved by
|
|
148
|
+
* `_resolve_execution_failure` — turned the dispatch into a deny: exit 2 on an
|
|
149
|
+
* ordinary `Read`. That is Risk 1 of the owning roadmap ("fail-closed wedges
|
|
150
|
+
* slow hosts") firing on every host rather than a slow one, reached through a
|
|
151
|
+
* documented escape hatch. The two changes are individually defensible and
|
|
152
|
+
* lethal together, which is exactly the shape a measured bound is supposed to
|
|
153
|
+
* prevent.
|
|
154
|
+
*
|
|
155
|
+
* So the bound stays where the window put it: an observation
|
|
156
|
+
* `src/scripts/_lib/concern_sla_window.ts` prints on every bench run, gating
|
|
157
|
+
* nothing. The spawn path keeps the historical 30 s. Re-wiring it needs a
|
|
158
|
+
* SPAWN-path measurement — which this tree does not have, because the sink it
|
|
159
|
+
* would come from records the in-process number — and not a second reading of
|
|
160
|
+
* the one registered here.
|
|
161
|
+
*
|
|
162
|
+
* `spawn_path_keeps_the_historical_timeout` in `dispatch_hook.test.ts` pins
|
|
163
|
+
* this: it dispatches a blocking concern whose NAME carries a registered SLA
|
|
164
|
+
* row down the spawn path and asserts the call is allowed. Under the bound it
|
|
165
|
+
* reds.
|
|
166
|
+
*/
|
|
167
|
+
export const SPAWN_TIMEOUT_MS = 30000;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* The signal name a killed child reports, or the honest placeholder.
|
|
171
|
+
*
|
|
172
|
+
* A SIGNALLED child has `status === null` and `signal` set, and reaches the
|
|
173
|
+
* dispatcher's spawn branch with no `proc.error` whenever the signal came from
|
|
174
|
+
* outside this process — an OOM kill, a SIGTERM from a supervisor, a crash in
|
|
175
|
+
* the child's own runtime. `proc.status ?? 0` read that as exit 0, i.e. ALLOW,
|
|
176
|
+
* which is a fail-OPEN at the one moment the error band exists to fail closed:
|
|
177
|
+
* the concern was killed, so it decided nothing. The coalescing default was
|
|
178
|
+
* harmless while the band meant fail-open for six of nine blocking concerns;
|
|
179
|
+
* with the severity resolution it is the difference between a guard that
|
|
180
|
+
* refuses when it is killed and one that waves the call through. Found by an
|
|
181
|
+
* independent review, 2026-10-03, latent since the spawn path was written.
|
|
182
|
+
*
|
|
183
|
+
* `null` for both fields is possible and is reported as an unknown signal
|
|
184
|
+
* rather than guessed at — a refusal whose cause reads "unknown" is still
|
|
185
|
+
* actionable, and one whose cause is invented is not.
|
|
186
|
+
*/
|
|
187
|
+
export function signalLabel(signal: NodeJS.Signals | null): string {
|
|
188
|
+
return signal === null ? 'unknown signal' : signal;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* What an operator is told when a blocking concern refuses without deciding.
|
|
193
|
+
*
|
|
194
|
+
* Printed where the refusal is produced rather than left in a source comment.
|
|
195
|
+
* Both review seats of 2026-10-03 named the same gap: on `pre_tool_use` a
|
|
196
|
+
* deterministically crashing blocking concern refuses every tool call, and the
|
|
197
|
+
* operator cannot discover the escape hatch from inside a wedged session.
|
|
198
|
+
* `stop` is bounded by the one-retry clause and needs no such line, so the
|
|
199
|
+
* caller prints this only where the denial can repeat.
|
|
200
|
+
*/
|
|
201
|
+
export function noVerdictRefusalNotice(name: string): string {
|
|
202
|
+
return (
|
|
203
|
+
`dispatch_hook: '${name}' is severity: blocking and produced no ` +
|
|
204
|
+
`verdict, so this call is refused. If it keeps failing, ` +
|
|
205
|
+
`AGENT_CONFIG_HOOKS_ISOLATED=1 runs concerns out of process and ` +
|
|
206
|
+
`agents/runtime/state/dispatch-issues.jsonl names the cause.\n`
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* What a finished `spawnSync` actually said, in the vocabulary of verdicts.
|
|
212
|
+
*
|
|
213
|
+
* Three ways a spawned concern produces NO VERDICT, and until 2026-10-03 only
|
|
214
|
+
* two of them were recognised as such:
|
|
215
|
+
*
|
|
216
|
+
* - **`proc.error`** — an OSError, or the kill-timeout expiring. Always a
|
|
217
|
+
* no-verdict and always was.
|
|
218
|
+
* - **`status === null` with a signal** — the child was killed from outside
|
|
219
|
+
* this process: an OOM kill, a supervisor SIGTERM, an abort in the child's
|
|
220
|
+
* own runtime. `proc.status ?? 0` read this as exit 0, i.e. ALLOW, which is
|
|
221
|
+
* a fail-OPEN at the one moment the error band exists to fail closed. The
|
|
222
|
+
* coalescing default was harmless while the band meant fail-open for six of
|
|
223
|
+
* nine blocking concerns; with the severity resolution it is the difference
|
|
224
|
+
* between a guard that refuses when it is killed and one that waves the call
|
|
225
|
+
* through. Found by an independent review, latent since the spawn path was
|
|
226
|
+
* written.
|
|
227
|
+
* - **an exit code in the error band** — a child that simply exited 3. This
|
|
228
|
+
* logged nothing until step 3.3: `proc.error` covers the first case, the
|
|
229
|
+
* in-process route logs its own throw, and this fell between the two. That
|
|
230
|
+
* was survivable while the band meant fail-open; now it can produce a
|
|
231
|
+
* refusal, and a refusal whose cause is in no record is one nobody can act
|
|
232
|
+
* on.
|
|
233
|
+
*
|
|
234
|
+
* Returning a classification rather than writing the log here keeps this module
|
|
235
|
+
* free of I/O — the caller owns `log_dispatch_issue` and the workspace it needs,
|
|
236
|
+
* and this owns what the three cases MEAN.
|
|
237
|
+
*/
|
|
238
|
+
export interface SpawnClassification {
|
|
239
|
+
/** The exit code the dispatcher should carry forward. */
|
|
240
|
+
readonly rc: number;
|
|
241
|
+
/** `execution_failed` detail to record, or `null` when there is nothing to record. */
|
|
242
|
+
readonly issueDetail: string | null;
|
|
243
|
+
/** True when the concern produced no verdict and its own stdout must be discarded. */
|
|
244
|
+
readonly noVerdict: boolean;
|
|
245
|
+
/** Prefix for the stderr line, or `null` to pass the child's stderr through unchanged. */
|
|
246
|
+
readonly stderrNote: string | null;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export function classifySpawnResult(
|
|
250
|
+
error: Error | undefined,
|
|
251
|
+
status: number | null,
|
|
252
|
+
signal: NodeJS.Signals | null,
|
|
253
|
+
): SpawnClassification {
|
|
254
|
+
if (error !== undefined) {
|
|
255
|
+
const err = error as NodeJS.ErrnoException;
|
|
256
|
+
const typeName = err.code === 'ETIMEDOUT' ? 'TimeoutExpired' : err.name || 'OSError';
|
|
257
|
+
return {
|
|
258
|
+
rc: EXIT_ERROR,
|
|
259
|
+
issueDetail: `${typeName}: ${err.message}`,
|
|
260
|
+
noVerdict: true,
|
|
261
|
+
stderrNote: err.message,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
if (status === null) {
|
|
265
|
+
const sig = signalLabel(signal);
|
|
266
|
+
return {
|
|
267
|
+
rc: EXIT_ERROR,
|
|
268
|
+
issueDetail: `concern was terminated by ${sig} without a verdict`,
|
|
269
|
+
noVerdict: true,
|
|
270
|
+
stderrNote: `terminated by ${sig}`,
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
rc: status,
|
|
275
|
+
issueDetail:
|
|
276
|
+
status >= EXIT_ERROR ? `concern exited ${String(status)} without a verdict` : null,
|
|
277
|
+
noVerdict: false,
|
|
278
|
+
stderrNote: null,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
@@ -52,6 +52,7 @@ import { main as gitAuthorizationMain } from '../git_authorization_hook.js';
|
|
|
52
52
|
import { main as evidenceIndependenceMain } from './evidence_independence.js';
|
|
53
53
|
import { main as blockNoVerifyMain } from './block_no_verify.js';
|
|
54
54
|
import { main as blockKernelRuleWritesMain } from './block_kernel_rule_writes.js';
|
|
55
|
+
import { main as blockPlumbingWritesMain } from './block_plumbing_writes.js';
|
|
55
56
|
import { main as blockConfigWeakeningMain } from './block_config_weakening.js';
|
|
56
57
|
import { main as blockSpeakingInboxDirMain } from './block_speaking_inbox_dir.js';
|
|
57
58
|
import { main as oneQuestionPerAskMain } from './one_question_per_ask_hook.js';
|
|
@@ -118,6 +119,7 @@ export const CONCERN_REGISTRY: Readonly<Record<string, ConcernMain>> = {
|
|
|
118
119
|
'src/scripts/hooks/evidence_independence.ts': evidenceIndependenceMain as ConcernMain,
|
|
119
120
|
'src/scripts/hooks/block_no_verify.ts': blockNoVerifyMain as ConcernMain,
|
|
120
121
|
'src/scripts/hooks/block_kernel_rule_writes.ts': blockKernelRuleWritesMain as ConcernMain,
|
|
122
|
+
'src/scripts/hooks/block_plumbing_writes.ts': blockPlumbingWritesMain as ConcernMain,
|
|
121
123
|
'src/scripts/hooks/block_config_weakening.ts': blockConfigWeakeningMain as ConcernMain,
|
|
122
124
|
'src/scripts/hooks/block_speaking_inbox_dir.ts': blockSpeakingInboxDirMain as ConcernMain,
|
|
123
125
|
'src/scripts/hooks/one_question_per_ask_hook.ts': oneQuestionPerAskMain as ConcernMain,
|