@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
|
@@ -53,7 +53,33 @@ import * as fs from 'node:fs';
|
|
|
53
53
|
import * as os from 'node:os';
|
|
54
54
|
import * as path from 'node:path';
|
|
55
55
|
import { fileURLToPath } from 'node:url';
|
|
56
|
+
import { parse as parseYaml } from 'yaml';
|
|
57
|
+
|
|
56
58
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
59
|
+
// The per-concern SLA surface moved to its own module when this file crossed
|
|
60
|
+
// the 1,500-line source ceiling (`check_source_size_budget`). Re-exported here
|
|
61
|
+
// so every caller's import path is unchanged and the move is invisible outside
|
|
62
|
+
// these two files.
|
|
63
|
+
import {
|
|
64
|
+
ASK_PROBE,
|
|
65
|
+
mergeProbeSamples,
|
|
66
|
+
PROBE_TARGET,
|
|
67
|
+
slaOverruns,
|
|
68
|
+
type ToolShape,
|
|
69
|
+
windowState,
|
|
70
|
+
} from './_lib/concern_sla_window.js';
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
ASK_PROBE,
|
|
74
|
+
mergeProbeSamples,
|
|
75
|
+
PROBE_TARGET,
|
|
76
|
+
slaOverruns,
|
|
77
|
+
windowState,
|
|
78
|
+
type BoundedConcern,
|
|
79
|
+
type SlaOverrun,
|
|
80
|
+
type ToolShape,
|
|
81
|
+
type WindowState,
|
|
82
|
+
} from './_lib/concern_sla_window.js';
|
|
57
83
|
|
|
58
84
|
const REPO_ROOT = path.resolve(fileURLToPath(import.meta.url), '..', '..', '..');
|
|
59
85
|
const BUNDLE = path.join(REPO_ROOT, 'dist', 'hooks', 'dispatch.js');
|
|
@@ -136,7 +162,7 @@ let PAYLOAD_BYTES = 0;
|
|
|
136
162
|
*/
|
|
137
163
|
const TOOL_EVENTS: ReadonlySet<string> = new Set(['pre_tool_use', 'post_tool_use']);
|
|
138
164
|
|
|
139
|
-
function syntheticPayload(event: string, workspace: string): string {
|
|
165
|
+
function syntheticPayload(event: string, workspace: string, tool?: ToolShape): string {
|
|
140
166
|
// Claude-shaped payload; concerns read tool_name/tool_input for
|
|
141
167
|
// pre/post_tool_use. A plain Read is the common non-matching case —
|
|
142
168
|
// the fast path consumers pay on every tool call.
|
|
@@ -144,8 +170,8 @@ function syntheticPayload(event: string, workspace: string): string {
|
|
|
144
170
|
session_id: 'bench-hook-latency',
|
|
145
171
|
cwd: workspace,
|
|
146
172
|
hook_event_name: event,
|
|
147
|
-
tool_name: 'Read',
|
|
148
|
-
tool_input: { file_path: path.join(workspace, 'README.md') },
|
|
173
|
+
tool_name: tool?.tool_name ?? 'Read',
|
|
174
|
+
tool_input: tool?.tool_input ?? { file_path: path.join(workspace, 'README.md') },
|
|
149
175
|
};
|
|
150
176
|
if (PAYLOAD_BYTES > 0 && TOOL_EVENTS.has(event)) {
|
|
151
177
|
// Filler with no JSON metacharacters, so the cost measured is
|
|
@@ -188,9 +214,12 @@ export function benchEvent(
|
|
|
188
214
|
runs: number,
|
|
189
215
|
workspace: string,
|
|
190
216
|
via: InvocationPath = 'bundle',
|
|
217
|
+
timingsSink?: string,
|
|
218
|
+
replay = true,
|
|
219
|
+
tool?: ToolShape,
|
|
191
220
|
): EventResult {
|
|
192
221
|
const durations: number[] = [];
|
|
193
|
-
const payload = syntheticPayload(event, workspace);
|
|
222
|
+
const payload = syntheticPayload(event, workspace, tool);
|
|
194
223
|
const command: [string, string[]] =
|
|
195
224
|
via === 'cli'
|
|
196
225
|
? ['bash', ['-c', hooksJsonCommand(event)]]
|
|
@@ -211,7 +240,21 @@ export function benchEvent(
|
|
|
211
240
|
const proc = spawnSync(command[0], command[1], {
|
|
212
241
|
input: payload,
|
|
213
242
|
encoding: 'utf-8',
|
|
214
|
-
env: {
|
|
243
|
+
env: {
|
|
244
|
+
...process.env,
|
|
245
|
+
// Replay is the DEFAULT and the gated slot numbers depend on it
|
|
246
|
+
// -- the pre-registered budget was measured with it on. The
|
|
247
|
+
// per-concern SLA pass below turns it OFF deliberately; see
|
|
248
|
+
// `concernSlaPass`.
|
|
249
|
+
...(replay ? { AGENT_CONFIG_REPLAY: '1' } : {}),
|
|
250
|
+
CLAUDE_PROJECT_DIR: workspace,
|
|
251
|
+
// Step 3.2: arm the dispatcher's per-concern timings sink for the
|
|
252
|
+
// duration of the bench. Unset on a real dispatch, so this costs one
|
|
253
|
+
// `process.env` lookup outside the harness.
|
|
254
|
+
...(timingsSink === undefined
|
|
255
|
+
? {}
|
|
256
|
+
: { AGENT_CONFIG_HOOK_TIMINGS: timingsSink }),
|
|
257
|
+
},
|
|
215
258
|
timeout: 60000,
|
|
216
259
|
});
|
|
217
260
|
const elapsed = performance.now() - started;
|
|
@@ -517,6 +560,277 @@ export function capFor(budget: Budget, event: string): number {
|
|
|
517
560
|
return capsFor(budget, event)[0]?.cap_ms ?? Number.POSITIVE_INFINITY;
|
|
518
561
|
}
|
|
519
562
|
|
|
563
|
+
/**
|
|
564
|
+
* Per-concern latency (step 3.2 of `road-to-a-kernel-that-guards-its-plumbing`).
|
|
565
|
+
*
|
|
566
|
+
* This is deliberately not the slot number divided by the concern count.
|
|
567
|
+
* The slot rows above measure a whole dispatch: spawn + interpreter start +
|
|
568
|
+
* bundle load + the concern chain. Step 3.3 wants to give each BLOCKING
|
|
569
|
+
* concern its own timeout, and a timeout derived from a slot p95 would be a
|
|
570
|
+
* number about the runner, not about the concern — on a slow host every
|
|
571
|
+
* concern would inherit the same inflated bound, and on a fast one the
|
|
572
|
+
* slowest concern would be bounded by the average of its neighbours.
|
|
573
|
+
*
|
|
574
|
+
* So the dispatcher reports its own per-concern `duration_ms` into a JSONL
|
|
575
|
+
* sink (`_write_concern_timings`, armed by `AGENT_CONFIG_HOOK_TIMINGS`) and
|
|
576
|
+
* this reads the distribution back. The samples are the concern's own work,
|
|
577
|
+
* measured inside the process that ran it, with the spawn term already
|
|
578
|
+
* excluded by construction.
|
|
579
|
+
*
|
|
580
|
+
* The report says `not_measured`, never `0`.
|
|
581
|
+
* A blocking concern with no samples in the sink is reported `not_measured`.
|
|
582
|
+
* It is the whole point of the step's verify line and it is load-bearing in a
|
|
583
|
+
* way that is easy to lose: `0` is a legal p95 for a fast concern, so a
|
|
584
|
+
* zero-filled row for a concern that never ran is indistinguishable from a
|
|
585
|
+
* real measurement of the fastest possible one — and step 3.3 would then
|
|
586
|
+
* compute a timeout of `0 x 3` and refuse every call. The absence has to stay
|
|
587
|
+
* visible as an absence all the way to the report.
|
|
588
|
+
*
|
|
589
|
+
* A concern can be absent for ordinary reasons: it is bound to a slot this
|
|
590
|
+
* bench does not exercise, or its `tools:` filter skipped it on the synthetic
|
|
591
|
+
* payload. That is information, not a failure, which is why this reports and
|
|
592
|
+
* does not gate.
|
|
593
|
+
*
|
|
594
|
+
* ONE NUMBER PER CONCERN, AND IT IS THE MAX OVER EVENTS, NOT THE P95 OF THE
|
|
595
|
+
* UNION. A concern bound to several slots has a distribution per slot, and the
|
|
596
|
+
* first version of this took a p95 across all of them pooled — which is a
|
|
597
|
+
* number describing no slot in particular and is biased by whichever slot the
|
|
598
|
+
* bench happens to run most. Step 3.3 derives ONE timeout per concern
|
|
599
|
+
* (`sla_ms x 3`) and the dispatcher applies it wherever the concern runs, so
|
|
600
|
+
* the bound has to hold on the concern's WORST slot or it refuses there. The
|
|
601
|
+
* row therefore carries the max of the per-event p95s and names the event that
|
|
602
|
+
* produced it, so a reader can see which slot sets the bound.
|
|
603
|
+
*/
|
|
604
|
+
export interface ConcernLatencyRow {
|
|
605
|
+
concern: string;
|
|
606
|
+
fail_closed: boolean;
|
|
607
|
+
/** Sample count found in the sink. */
|
|
608
|
+
n: number;
|
|
609
|
+
/**
|
|
610
|
+
* p95 of the concern's own durations in MICROseconds, or null when it was
|
|
611
|
+
* never timed.
|
|
612
|
+
*
|
|
613
|
+
* Microseconds, not milliseconds, because the unit was a defect. Every
|
|
614
|
+
* concern in the static registry runs in-process and finishes in well
|
|
615
|
+
* under a millisecond, so the dispatcher's floored `duration_ms` reported
|
|
616
|
+
* exactly `0` for all nine blocking concerns on the first run of this
|
|
617
|
+
* report. A `0 ms` p95 is not a missing measurement and would not be
|
|
618
|
+
* caught by the `not_measured` branch below — it is a real-looking
|
|
619
|
+
* number that step 3.3 would multiply by three to get a timeout of zero.
|
|
620
|
+
*/
|
|
621
|
+
p95_us: number | null;
|
|
622
|
+
/** Which event produced `p95_us` — the slowest slot this concern runs on. */
|
|
623
|
+
p95_event: string | null;
|
|
624
|
+
/** The registered SLA in milliseconds, or null when none is registered yet. */
|
|
625
|
+
sla_ms: number | null;
|
|
626
|
+
/** The budget carried a value for this concern that is not a number. */
|
|
627
|
+
sla_malformed: boolean;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/** Placeholder printed for a number the run could not establish. */
|
|
631
|
+
export const NOT_MEASURED = 'not_measured';
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Every `severity: blocking` concern in the manifest, in declaration order.
|
|
635
|
+
*
|
|
636
|
+
* Read from the manifest rather than hardcoded: the step was written against
|
|
637
|
+
* eight blocking concerns and `block-plumbing-writes` (step 1.2) made it nine
|
|
638
|
+
* in the same roadmap. A list in this file would have been stale before the
|
|
639
|
+
* phase closed.
|
|
640
|
+
*/
|
|
641
|
+
export function blockingConcerns(manifestPath: string = MANIFEST_PATH): {
|
|
642
|
+
name: string;
|
|
643
|
+
fail_closed: boolean;
|
|
644
|
+
}[] {
|
|
645
|
+
const raw = parseYaml(fs.readFileSync(manifestPath, 'utf-8'), { version: '1.1' }) as
|
|
646
|
+
| { concerns?: Record<string, { severity?: string; fail_closed?: boolean }> }
|
|
647
|
+
| null;
|
|
648
|
+
const concerns = raw?.concerns ?? {};
|
|
649
|
+
return Object.entries(concerns)
|
|
650
|
+
.filter(([, def]) => String(def?.severity ?? '').trim().toLowerCase() === 'blocking')
|
|
651
|
+
.map(([name, def]) => ({ name, fail_closed: Boolean(def?.fail_closed) }));
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Read the JSONL sink into per-concern sample arrays.
|
|
656
|
+
*
|
|
657
|
+
* A malformed or unreadable line is skipped rather than fatal — the sink is a
|
|
658
|
+
* measurement aid, and a bench that dies on one bad row would convert an
|
|
659
|
+
* observation into an outage. A skipped line lowers `n`, which the report
|
|
660
|
+
* prints, so the reduction is visible rather than silent.
|
|
661
|
+
*/
|
|
662
|
+
export interface ConcernTimings {
|
|
663
|
+
/** concern -> event -> samples. Nested, never flattened; see below. */
|
|
664
|
+
byConcernEvent: Map<string, Map<string, number[]>>;
|
|
665
|
+
/** Lines present in the sink that could not be read as a sample. */
|
|
666
|
+
skipped: number;
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
export function readConcernTimings(sinkPath: string): ConcernTimings {
|
|
670
|
+
const byConcernEvent = new Map<string, Map<string, number[]>>();
|
|
671
|
+
let skipped = 0;
|
|
672
|
+
let text: string;
|
|
673
|
+
try {
|
|
674
|
+
text = fs.readFileSync(sinkPath, 'utf-8');
|
|
675
|
+
} catch {
|
|
676
|
+
return { byConcernEvent, skipped };
|
|
677
|
+
}
|
|
678
|
+
for (const line of text.split('\n')) {
|
|
679
|
+
const trimmed = line.trim();
|
|
680
|
+
if (trimmed === '') continue;
|
|
681
|
+
let row: { concern?: unknown; duration_us?: unknown; event?: unknown };
|
|
682
|
+
try {
|
|
683
|
+
row = JSON.parse(trimmed) as typeof row;
|
|
684
|
+
} catch {
|
|
685
|
+
skipped += 1;
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
688
|
+
const name = typeof row.concern === 'string' ? row.concern : '';
|
|
689
|
+
const us = row.duration_us;
|
|
690
|
+
if (name === '' || typeof us !== 'number' || !Number.isFinite(us)) {
|
|
691
|
+
skipped += 1;
|
|
692
|
+
continue;
|
|
693
|
+
}
|
|
694
|
+
const event = typeof row.event === 'string' && row.event !== '' ? row.event : 'unknown';
|
|
695
|
+
let perEvent = byConcernEvent.get(name);
|
|
696
|
+
if (perEvent === undefined) {
|
|
697
|
+
perEvent = new Map<string, number[]>();
|
|
698
|
+
byConcernEvent.set(name, perEvent);
|
|
699
|
+
}
|
|
700
|
+
const bucket = perEvent.get(event);
|
|
701
|
+
if (bucket === undefined) perEvent.set(event, [us]);
|
|
702
|
+
else bucket.push(us);
|
|
703
|
+
}
|
|
704
|
+
return { byConcernEvent, skipped };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/**
|
|
708
|
+
* One row per blocking concern — measured p95 beside the registered SLA.
|
|
709
|
+
*
|
|
710
|
+
* Both columns are independently nullable, and the two nulls mean different
|
|
711
|
+
* things: a null `p95_ms` is "this run did not time it", a null `sla_ms` is
|
|
712
|
+
* "no SLA is registered yet". Collapsing them would hide the state step 3.3
|
|
713
|
+
* needs to distinguish — a concern it may bound, versus one it may not.
|
|
714
|
+
*/
|
|
715
|
+
export function perConcernRows(
|
|
716
|
+
blocking: readonly { name: string; fail_closed: boolean }[],
|
|
717
|
+
samples: ReadonlyMap<string, ReadonlyMap<string, number[]>>,
|
|
718
|
+
sla: Readonly<Record<string, unknown>> = {},
|
|
719
|
+
): ConcernLatencyRow[] {
|
|
720
|
+
return blocking.map(({ name, fail_closed }) => {
|
|
721
|
+
const perEvent = samples.get(name);
|
|
722
|
+
let n = 0;
|
|
723
|
+
let worst: number | null = null;
|
|
724
|
+
let worstEvent: string | null = null;
|
|
725
|
+
for (const [event, xs] of perEvent ?? []) {
|
|
726
|
+
n += xs.length;
|
|
727
|
+
if (xs.length === 0) continue;
|
|
728
|
+
const sorted = [...xs].sort((a, b) => a - b);
|
|
729
|
+
const p = Math.round(percentile(sorted, 95));
|
|
730
|
+
if (worst === null || p > worst) {
|
|
731
|
+
worst = p;
|
|
732
|
+
worstEvent = event;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
const registered = sla[name];
|
|
736
|
+
// A value that is present but not a number is NOT silently `null`. The
|
|
737
|
+
// two states print differently and mean different things: `null` is a
|
|
738
|
+
// registered absence, a malformed entry is a typo in the budget file
|
|
739
|
+
// that would otherwise read as a missing measurement.
|
|
740
|
+
const slaMalformed =
|
|
741
|
+
registered !== undefined &&
|
|
742
|
+
registered !== null &&
|
|
743
|
+
(typeof registered !== 'number' || !Number.isFinite(registered));
|
|
744
|
+
return {
|
|
745
|
+
concern: name,
|
|
746
|
+
fail_closed,
|
|
747
|
+
n,
|
|
748
|
+
p95_us: worst,
|
|
749
|
+
p95_event: worstEvent,
|
|
750
|
+
sla_ms: typeof registered === 'number' && Number.isFinite(registered) ? registered : null,
|
|
751
|
+
sla_malformed: slaMalformed,
|
|
752
|
+
};
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/**
|
|
757
|
+
* Collect per-concern samples with replay mode OFF.
|
|
758
|
+
*
|
|
759
|
+
* MEASURED, and the reason this is a separate pass rather than a read of the
|
|
760
|
+
* gated run. `AGENT_CONFIG_REPLAY=1` suppresses state writes (`state_io.ts`),
|
|
761
|
+
* and several blocking concerns do most of their work through state — so the
|
|
762
|
+
* gated bench, which needs replay on because the pre-registered slot budget
|
|
763
|
+
* was measured with it, times those concerns at a fraction of their real cost.
|
|
764
|
+
*
|
|
765
|
+
* A/B over this tree, n=25 per cell, 2026-09-30:
|
|
766
|
+
*
|
|
767
|
+
* verify-before-complete live 1249 us replay 310 us x4.03
|
|
768
|
+
* run-continuation live 431 us replay 220 us x1.96
|
|
769
|
+
* journal-record live 175 us replay 141 us x1.24
|
|
770
|
+
* block-no-verify live 779 us replay 642 us x1.21
|
|
771
|
+
* (everything else within +/-10%, i.e. noise)
|
|
772
|
+
*
|
|
773
|
+
* Step 3.3 derives a timeout of `sla_ms x 3`. An SLA of 310 us for a concern
|
|
774
|
+
* whose real p95 is 1249 us yields a 930 us bound that the concern exceeds on
|
|
775
|
+
* an ORDINARY run — which is Risk 1 of the owning roadmap, reached through a
|
|
776
|
+
* measurement instead of through a guess. So the numbers step 3.3 will one day
|
|
777
|
+
* consume have to come from a pass that runs the concerns for real.
|
|
778
|
+
*
|
|
779
|
+
* It runs against a throwaway workspace, so the state writes it permits land in
|
|
780
|
+
* a temp dir and nothing in the repository is touched. It gates NOTHING and
|
|
781
|
+
* contributes to no budget row: it is an observation, and the slot rows above
|
|
782
|
+
* remain the only gated numbers.
|
|
783
|
+
*/
|
|
784
|
+
export function concernSlaPass(
|
|
785
|
+
runs: number,
|
|
786
|
+
workspace: string,
|
|
787
|
+
sinkPath: string,
|
|
788
|
+
events: readonly string[] = EVENTS,
|
|
789
|
+
): void {
|
|
790
|
+
for (const event of events) {
|
|
791
|
+
benchEvent(event, runs, workspace, 'bundle', sinkPath, false);
|
|
792
|
+
}
|
|
793
|
+
// One extra `pre_tool_use` pass under the ask-shaped probe, so the ninth
|
|
794
|
+
// blocking concern is measured rather than waived. See `ASK_PROBE`: every
|
|
795
|
+
// other blocking concern is reached by the default `Read` payload, and
|
|
796
|
+
// `one-question-per-ask`'s `tools:` filter means no number of `Read` runs
|
|
797
|
+
// will ever produce a sample for it.
|
|
798
|
+
//
|
|
799
|
+
// IT WRITES TO ITS OWN SINK, AND THAT IS THE WHOLE POINT OF THE SPLIT.
|
|
800
|
+
// The probe dispatches the whole `pre_tool_use` event, not one concern —
|
|
801
|
+
// only the manifest's `tools:` filter isolates the target, so every
|
|
802
|
+
// UNFILTERED concern on that slot runs under the ask payload too and emits
|
|
803
|
+
// a timing. Writing both passes to one sink pools them under the same
|
|
804
|
+
// `pre_tool_use` key, and the neighbours' p95 then describes a mixture of
|
|
805
|
+
// two payload shapes while the comment above claims their number comes
|
|
806
|
+
// from the default shape alone. Measured, not hypothesised: the sample
|
|
807
|
+
// count for every `pre_tool_use` concern went from n=20 to n=40 the moment
|
|
808
|
+
// the probe landed, which is the contamination showing up in the report's
|
|
809
|
+
// own output. Caught by an independent review, 2026-10-01.
|
|
810
|
+
//
|
|
811
|
+
// `mergeProbeSamples` then takes ONLY the target concern across. The
|
|
812
|
+
// neighbours' ask-shaped samples are measured and discarded, which is
|
|
813
|
+
// correct: they are a real cost of a payload shape the gated numbers do
|
|
814
|
+
// not model.
|
|
815
|
+
const probeSink = `${sinkPath}.probe`;
|
|
816
|
+
benchEvent('pre_tool_use', runs, workspace, 'bundle', probeSink, false, ASK_PROBE);
|
|
817
|
+
mergeProbeSamples(probeSink, sinkPath, PROBE_TARGET);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/** Render one row. `null` prints `not_measured` — never `0`. */
|
|
821
|
+
export function renderConcernRow(row: ConcernLatencyRow): string {
|
|
822
|
+
// Three decimals, because the numbers are microseconds and the whole point
|
|
823
|
+
// of the unit change is that rounding them to whole milliseconds produced
|
|
824
|
+
// nine zeroes.
|
|
825
|
+
const p95 = row.p95_us === null ? NOT_MEASURED : `${(row.p95_us / 1000).toFixed(3)} ms`;
|
|
826
|
+
const sla = row.sla_malformed ? 'MALFORMED' : row.sla_ms === null ? NOT_MEASURED : `${row.sla_ms} ms`;
|
|
827
|
+
const via = row.p95_event === null ? '' : `, worst on ${row.p95_event}`;
|
|
828
|
+
return (
|
|
829
|
+
` ${row.concern.padEnd(26)} p95 ${p95.padStart(13)} · sla ${sla.padStart(13)} ` +
|
|
830
|
+
`(n=${row.n}${via}${row.fail_closed ? ', fail_closed' : ''})`
|
|
831
|
+
);
|
|
832
|
+
}
|
|
833
|
+
|
|
520
834
|
function loadJson<T>(p: string): T | null {
|
|
521
835
|
try {
|
|
522
836
|
return JSON.parse(fs.readFileSync(p, 'utf-8')) as T;
|
|
@@ -531,6 +845,14 @@ export interface Budget {
|
|
|
531
845
|
any_hook_event: { p95_ci: number; blocking?: boolean };
|
|
532
846
|
};
|
|
533
847
|
regression_gate: { max_regression_pct: number };
|
|
848
|
+
/**
|
|
849
|
+
* Per-concern SLA in milliseconds, keyed by concern name (step 3.2).
|
|
850
|
+
*
|
|
851
|
+
* A `null` value is a registered ABSENCE — the concern is known and its
|
|
852
|
+
* bound has deliberately not been set yet. Step 3.3 may only bound a
|
|
853
|
+
* concern whose value here is a number.
|
|
854
|
+
*/
|
|
855
|
+
concern_sla_ms?: Record<string, number | null>;
|
|
534
856
|
gate_remeasure?: { attempts?: number };
|
|
535
857
|
normalized?: {
|
|
536
858
|
observe_only?: boolean;
|
|
@@ -770,6 +1092,7 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
770
1092
|
}
|
|
771
1093
|
}
|
|
772
1094
|
const budget = loadJson<Budget>(BUDGET_PATH);
|
|
1095
|
+
let concernRows: ConcernLatencyRow[] = [];
|
|
773
1096
|
if (budget === null) {
|
|
774
1097
|
process.stderr.write(`bench_hook_latency: budget file missing/invalid at ${BUDGET_PATH}\n`);
|
|
775
1098
|
return 2;
|
|
@@ -796,6 +1119,10 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
796
1119
|
}
|
|
797
1120
|
|
|
798
1121
|
const workspace = fs.mkdtempSync(path.join(os.tmpdir(), 'ac-hook-bench-'));
|
|
1122
|
+
// Step 3.2's per-concern sink. Lives beside the workspace and is torn down
|
|
1123
|
+
// with it, so a bench run leaves nothing behind and a stale file from an
|
|
1124
|
+
// earlier run can never be read as this run's distribution.
|
|
1125
|
+
const timingsSink = path.join(workspace, 'concern-timings.jsonl');
|
|
799
1126
|
const results: EventResult[] = [];
|
|
800
1127
|
let control: EventResult | null = null;
|
|
801
1128
|
try {
|
|
@@ -826,6 +1153,86 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
826
1153
|
`${'control (node -e 0)'.padEnd(20)} p50 ${String(control.p50_ms).padStart(5)} ms · p95 ${String(control.p95_ms).padStart(5)} ms · max ${String(control.max_ms).padStart(5)} ms (n=${control.runs})\n`,
|
|
827
1154
|
);
|
|
828
1155
|
|
|
1156
|
+
// Step 3.2 — per-concern p95 beside the registered SLA, for every
|
|
1157
|
+
// BLOCKING concern. Its own pass, with replay OFF: the gated slot runs
|
|
1158
|
+
// above need replay on, and replay times several concerns at a
|
|
1159
|
+
// fraction of their real cost. `concernSlaPass` carries the A/B.
|
|
1160
|
+
//
|
|
1161
|
+
// Printed on every run, gate or not: a row that appeared only under
|
|
1162
|
+
// `--gate` would make the number step 3.3 depends on unavailable
|
|
1163
|
+
// exactly where it is cheapest to read, which is the same mistake the
|
|
1164
|
+
// composite row above documents having made once. It reports and never
|
|
1165
|
+
// fails — the SLAs it feeds are not armed, and a measurement that can
|
|
1166
|
+
// red a build before its bar exists is the guessed-threshold failure
|
|
1167
|
+
// Risk 1 of the owning roadmap names.
|
|
1168
|
+
concernSlaPass(Math.min(runs, 20), workspace, timingsSink);
|
|
1169
|
+
const timings = readConcernTimings(timingsSink);
|
|
1170
|
+
concernRows = perConcernRows(
|
|
1171
|
+
blockingConcerns(),
|
|
1172
|
+
timings.byConcernEvent,
|
|
1173
|
+
budget?.concern_sla_ms ?? {},
|
|
1174
|
+
);
|
|
1175
|
+
if (timings.skipped > 0) {
|
|
1176
|
+
// The CAUSE of a lowered `n`, not only its effect. An earlier
|
|
1177
|
+
// version claimed the reduction was visible because `n` was
|
|
1178
|
+
// printed; `n` shows that fewer samples arrived, never that some
|
|
1179
|
+
// were unreadable.
|
|
1180
|
+
process.stdout.write(
|
|
1181
|
+
` ⚠️ ${timings.skipped} unreadable line(s) in the timings sink — samples lost, n is lower than the run count\n`,
|
|
1182
|
+
);
|
|
1183
|
+
}
|
|
1184
|
+
process.stdout.write(
|
|
1185
|
+
`blocking concerns (${concernRows.length}) — per-concern p95 vs registered SLA:\n`,
|
|
1186
|
+
);
|
|
1187
|
+
for (const row of concernRows) {
|
|
1188
|
+
process.stdout.write(`${renderConcernRow(row)}\n`);
|
|
1189
|
+
}
|
|
1190
|
+
const unmeasured = concernRows.filter((r) => r.p95_us === null).length;
|
|
1191
|
+
if (unmeasured > 0) {
|
|
1192
|
+
process.stdout.write(
|
|
1193
|
+
` ℹ️ ${unmeasured} of ${concernRows.length} not measured by this run — ` +
|
|
1194
|
+
'a concern bound to a slot this bench does not exercise, or skipped by its ' +
|
|
1195
|
+
"`tools:` filter, has no sample. Reported as an absence, never as 0.\n",
|
|
1196
|
+
);
|
|
1197
|
+
}
|
|
1198
|
+
// The warn-only window step 3.3 names, read out loud. Observe-only: it
|
|
1199
|
+
// reports and never changes the exit code, because the bound it checks
|
|
1200
|
+
// is not armed anywhere and a measurement that can red a build before
|
|
1201
|
+
// its bar is validated is Risk 1 of the owning roadmap.
|
|
1202
|
+
const overruns = slaOverruns(concernRows);
|
|
1203
|
+
const w = windowState(concernRows);
|
|
1204
|
+
if (w.registered === 0) {
|
|
1205
|
+
process.stdout.write(
|
|
1206
|
+
' ℹ️ no `concern_sla_ms` registered — the warn-only window has not started.\n',
|
|
1207
|
+
);
|
|
1208
|
+
} else if (overruns.length === 0 && w.unusable > 0) {
|
|
1209
|
+
// NOT a clean run. A registered concern this pass could not time is
|
|
1210
|
+
// an unknown, and `slaOverruns` skips it — so counting the SUCCESS
|
|
1211
|
+
// marker off `registered` alone prints "none over" for a window
|
|
1212
|
+
// that observed nothing about `unusable` of its bounded concerns.
|
|
1213
|
+
// The same unknown-is-not-fine invariant `slaOverruns` keeps per
|
|
1214
|
+
// row, kept here for the summary line. Caught by an independent
|
|
1215
|
+
// review, 2026-10-01.
|
|
1216
|
+
process.stdout.write(
|
|
1217
|
+
` ⚠️ warn-only window INCOMPLETE: ${w.usable} of ${w.registered} bounded ` +
|
|
1218
|
+
`concerns measured this run, ${w.unusable} unmeasured or malformed — ` +
|
|
1219
|
+
'no overrun seen, and this run does not count as clean.\n',
|
|
1220
|
+
);
|
|
1221
|
+
} else if (overruns.length === 0) {
|
|
1222
|
+
process.stdout.write(
|
|
1223
|
+
` ✅ warn-only window: ${w.usable} of ${concernRows.length} bounded and ` +
|
|
1224
|
+
'measured, none over `sla_ms × 3` on this run.\n',
|
|
1225
|
+
);
|
|
1226
|
+
} else {
|
|
1227
|
+
for (const o of overruns) {
|
|
1228
|
+
process.stdout.write(
|
|
1229
|
+
` ⚠️ warn-only window: ${o.concern} p95 ${(o.p95_us / 1000).toFixed(3)} ms ` +
|
|
1230
|
+
`is over its \`sla_ms × 3\` bound of ${(o.bound_us / 1000).toFixed(3)} ms — ` +
|
|
1231
|
+
'observe-only; step 3.3 must not flip to deny while this stands.\n',
|
|
1232
|
+
);
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
|
|
829
1236
|
// Breach re-measure (--gate only). A single p95 over the cap is not yet
|
|
830
1237
|
// evidence of a regression: a load spike inside one 50-run sample moves
|
|
831
1238
|
// p95 upward and never downward, so re-measuring and keeping the MINIMUM
|