@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
|
@@ -34,7 +34,13 @@ import { appendFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs
|
|
|
34
34
|
import path from 'node:path';
|
|
35
35
|
import { fileURLToPath } from 'node:url';
|
|
36
36
|
|
|
37
|
-
import {
|
|
37
|
+
import { CLI_CONSUMER_SELF_REVIEW } from './ai_council/cli_call_budget.js';
|
|
38
|
+
import {
|
|
39
|
+
AnthropicClient,
|
|
40
|
+
AnthropicCliClient,
|
|
41
|
+
type ExternalAIClient,
|
|
42
|
+
load_anthropic_key,
|
|
43
|
+
} from './ai_council/clients.js';
|
|
38
44
|
import { independenceFields } from './_lib/review_independence.js';
|
|
39
45
|
|
|
40
46
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -50,6 +56,15 @@ export interface Finding {
|
|
|
50
56
|
title: string;
|
|
51
57
|
detail: string;
|
|
52
58
|
file?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Set when the tree mechanically DISPROVES the finding's premise — today
|
|
61
|
+
* only for the deletion class: the finding says an artifact was removed,
|
|
62
|
+
* the range deletes nothing by that name, and the artifact is still in the
|
|
63
|
+
* tree. Carries the disproof, so the finding stays readable rather than
|
|
64
|
+
* disappearing. A finding carrying this is not merge-blocking; see
|
|
65
|
+
* `classifyBlocking`.
|
|
66
|
+
*/
|
|
67
|
+
contradicted?: string;
|
|
53
68
|
}
|
|
54
69
|
|
|
55
70
|
/**
|
|
@@ -74,6 +89,13 @@ export function findingId(f: Pick<Finding, 'kind' | 'title' | 'file'>): string {
|
|
|
74
89
|
* block ONLY on the narrow security/claim × {critical,high} intersection.
|
|
75
90
|
*/
|
|
76
91
|
export function classifyBlocking(f: Finding): boolean {
|
|
92
|
+
// A premise the tree DISPROVES cannot block a merge. This is not leniency
|
|
93
|
+
// and not a heuristic: `contradicted` is set only where a deletion claim
|
|
94
|
+
// was checked against `git diff --name-status base...HEAD` AND against the
|
|
95
|
+
// working tree, and both said the named artifact is present and undeleted.
|
|
96
|
+
// See `contradictedByTree` for why this class needed a mechanical disproof
|
|
97
|
+
// rather than one more sentence in the prompt.
|
|
98
|
+
if ((f.contradicted ?? '').trim() !== '') return false;
|
|
77
99
|
return (f.kind === 'security' || f.kind === 'claim') && (f.severity === 'critical' || f.severity === 'high');
|
|
78
100
|
}
|
|
79
101
|
|
|
@@ -120,6 +142,271 @@ function changedFiles(baseRef: string, cwd: string = REPO_ROOT): string[] {
|
|
|
120
142
|
.filter(isReviewablePath);
|
|
121
143
|
}
|
|
122
144
|
|
|
145
|
+
/**
|
|
146
|
+
* The paths the range actually DELETES, from `git diff --name-status`.
|
|
147
|
+
*
|
|
148
|
+
* Deliberately NOT filtered by `isReviewablePath`: the question this answers is
|
|
149
|
+
* "did the range remove this artifact", and a removal under `dist/agent-src/`
|
|
150
|
+
* is a removal even though the projection is never sent to the reviewer. A
|
|
151
|
+
* filter here would turn a real deletion into a disproof of itself.
|
|
152
|
+
*
|
|
153
|
+
* Renames are reported by `--name-status` as `R<score>\told\tnew`, and the old
|
|
154
|
+
* path IS gone, so it is counted. A roadmap moved into `archive/` shows up as a
|
|
155
|
+
* genuine deletion of its old path, which is exactly the reading a finding
|
|
156
|
+
* about that path deserves.
|
|
157
|
+
*/
|
|
158
|
+
export function rangeDeletions(baseRef: string, cwd: string = REPO_ROOT): Set<string> {
|
|
159
|
+
return parseDeletions(nameStatusText(baseRef, cwd));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* The paths the range MODIFIES, adds, or renames TO — everything it touched
|
|
164
|
+
* that still has a path afterwards.
|
|
165
|
+
*
|
|
166
|
+
* The complement of `rangeDeletions` over the same `--name-status` output, and
|
|
167
|
+
* the input `contradictedByTree` needs to tell "the range did not touch this"
|
|
168
|
+
* from "the range changed the inside of this". Unfiltered for the same reason
|
|
169
|
+
* `rangeDeletions` is: the question is what the RANGE did, not what the
|
|
170
|
+
* reviewer was sent.
|
|
171
|
+
*/
|
|
172
|
+
export function rangeModifications(baseRef: string, cwd: string = REPO_ROOT): Set<string> {
|
|
173
|
+
return parseModifications(nameStatusText(baseRef, cwd));
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** One `--name-status` read, shared by both parses so the range is read once. */
|
|
177
|
+
function nameStatusText(baseRef: string, cwd: string = REPO_ROOT): string {
|
|
178
|
+
const r = spawnSync('git', ['-c', 'core.quotePath=false', 'diff', '--name-status', `${baseRef}...HEAD`], {
|
|
179
|
+
cwd,
|
|
180
|
+
encoding: 'utf8',
|
|
181
|
+
});
|
|
182
|
+
if (r.status !== 0) {
|
|
183
|
+
throw new Error(`git diff --name-status failed: ${(r.stderr ?? '').toString().slice(0, 300)}`);
|
|
184
|
+
}
|
|
185
|
+
return r.stdout ?? '';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Pure core of `rangeDeletions`, so the parse is testable without a repo. */
|
|
189
|
+
export function parseDeletions(nameStatus: string): Set<string> {
|
|
190
|
+
const out = new Set<string>();
|
|
191
|
+
for (const line of nameStatus.split('\n')) {
|
|
192
|
+
const cols = line.split('\t');
|
|
193
|
+
const status = (cols[0] ?? '').trim();
|
|
194
|
+
if (status === '') continue;
|
|
195
|
+
if (status === 'D' && cols[1]) out.add(cols[1].trim());
|
|
196
|
+
// A rename removes its OLD path; `R100\told\tnew` puts it in column 1.
|
|
197
|
+
if (status.startsWith('R') && cols[1]) out.add(cols[1].trim());
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Pure core of `rangeModifications` — every path the range left standing.
|
|
204
|
+
*
|
|
205
|
+
* A `D` row contributes nothing. A rename contributes its NEW path (column 2),
|
|
206
|
+
* never its old one: the old path is a deletion and is parsed as such. Every
|
|
207
|
+
* other status (`M`, `A`, `C`, `T`, and the `U` of an unmerged path) has its
|
|
208
|
+
* path in column 1 and that path exists after the range.
|
|
209
|
+
*/
|
|
210
|
+
export function parseModifications(nameStatus: string): Set<string> {
|
|
211
|
+
const out = new Set<string>();
|
|
212
|
+
for (const line of nameStatus.split('\n')) {
|
|
213
|
+
const cols = line.split('\t');
|
|
214
|
+
const status = (cols[0] ?? '').trim();
|
|
215
|
+
if (status === '' || status === 'D') continue;
|
|
216
|
+
// `R<score>\told\tnew` / `C<score>\tsrc\tdest` — the surviving path is column 2.
|
|
217
|
+
const col = status.startsWith('R') || status.startsWith('C') ? cols[2] : cols[1];
|
|
218
|
+
if (col) out.add(col.trim());
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Names a finding points at: its `file`, plus every backticked token in the
|
|
225
|
+
* title and detail that could be a path or an artifact directory name.
|
|
226
|
+
*
|
|
227
|
+
* Backticks are the convention the review prompt asks for and the one every
|
|
228
|
+
* measured finding used, so this reads what the model actually writes rather
|
|
229
|
+
* than guessing at prose.
|
|
230
|
+
*/
|
|
231
|
+
export function namedArtifacts(f: Pick<Finding, 'title' | 'detail' | 'file'>): string[] {
|
|
232
|
+
const out = new Set<string>();
|
|
233
|
+
if (f.file) out.add(f.file);
|
|
234
|
+
for (const m of `${f.title}\n${f.detail}`.matchAll(/`([^`\n]{2,120})`/g)) {
|
|
235
|
+
const tok = (m[1] ?? '').trim();
|
|
236
|
+
// A token with whitespace is prose in backticks, not a name.
|
|
237
|
+
if (tok !== '' && !/\s/.test(tok)) out.add(tok);
|
|
238
|
+
}
|
|
239
|
+
return [...out];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/** Does the finding's text assert that something was removed from the tree? */
|
|
243
|
+
export function assertsDeletion(f: Pick<Finding, 'title' | 'detail'>): boolean {
|
|
244
|
+
return /\b(deleted|removed|deletion|removal)\b/i.test(`${f.title}\n${f.detail}`);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* The disproof, or null when there is none.
|
|
249
|
+
*
|
|
250
|
+
* WHY THIS IS MECHANICAL AND NOT A PROMPT LINE. The review runs over a
|
|
251
|
+
* per-file PARTITION of the span and, on a large release, over only part of it
|
|
252
|
+
* — 202 of 401 files at the 16.1.0 cut. A reviewer holding half the files
|
|
253
|
+
* cannot distinguish "this artifact is absent from my chunk" from "this
|
|
254
|
+
* artifact was deleted", and on that cut it reported two `critical security`
|
|
255
|
+
* deletions of skills the range never touched. Both blocked the release and
|
|
256
|
+
* cost a full adjudication cycle each.
|
|
257
|
+
*
|
|
258
|
+
* The prompt already carries a sentence for the neighbouring class ("Do NOT
|
|
259
|
+
* report a feature as not in the diff when it is present in the release
|
|
260
|
+
* range") and the same run violated it, which is the evidence that a sentence
|
|
261
|
+
* is not the control here.
|
|
262
|
+
*
|
|
263
|
+
* The test is conservative in the direction that matters: a disproof needs a
|
|
264
|
+
* named artifact that is BOTH absent from the range's deletion set AND present
|
|
265
|
+
* in the tree AND untouched by the range.
|
|
266
|
+
*
|
|
267
|
+
* THE THIRD HALF, AND WHY IT WAS MISSING. The first two were written against
|
|
268
|
+
* WHOLE-FILE deletion, where "a genuine deletion satisfies neither half" is
|
|
269
|
+
* true. It is false for a removal asserted INSIDE a file: "digest verification
|
|
270
|
+
* was removed in `install.ts`" names a path the range MODIFIED, so the path is
|
|
271
|
+
* absent from the deletion set and present in the tree, and the finding was
|
|
272
|
+
* de-blocked as though it had been checked. A path the range modified is
|
|
273
|
+
* evidence the range touched that artifact — the opposite of this disproof's
|
|
274
|
+
* premise — so it can no longer carry one.
|
|
275
|
+
*
|
|
276
|
+
* WHAT THIS GATE STILL CANNOT DECIDE, and says nothing about. The disproof is
|
|
277
|
+
* built entirely out of PATHS: the deletion set, the modification set, and the
|
|
278
|
+
* working tree. A claim about something that has no path of its own — a
|
|
279
|
+
* symbol, an export, a config key, a behaviour, a sentence inside a surviving
|
|
280
|
+
* file — cannot be resolved to a candidate, so no half of the test applies and
|
|
281
|
+
* the finding is returned unannotated. That is the intended reading: the gate
|
|
282
|
+
* cannot decide such a claim, it is not asserting the claim is true, and an
|
|
283
|
+
* operator adjudicating the block is reading an UNCHECKED finding rather than
|
|
284
|
+
* an unrefuted one. Widening the test to cover that class would mean deciding
|
|
285
|
+
* a claim from the same partition the reviewer had, which is the fabrication
|
|
286
|
+
* this mechanism exists to absorb.
|
|
287
|
+
*/
|
|
288
|
+
export function contradictedByTree(
|
|
289
|
+
f: Pick<Finding, 'title' | 'detail' | 'file'>,
|
|
290
|
+
deleted: ReadonlySet<string>,
|
|
291
|
+
exists: (p: string) => boolean,
|
|
292
|
+
modified: ReadonlySet<string> = new Set<string>(),
|
|
293
|
+
): string | null {
|
|
294
|
+
if (!assertsDeletion(f)) return null;
|
|
295
|
+
for (const name of namedArtifacts(f)) {
|
|
296
|
+
// Any deletion whose path mentions the token leaves the claim standing.
|
|
297
|
+
if ([...deleted].some((d) => d === name || d.includes(name))) continue;
|
|
298
|
+
const candidates = [name, `src/skills/${name}/SKILL.md`, `src/rules/${name}.md`, `src/scripts/${name}`];
|
|
299
|
+
// The range touched something this token resolves to — a whole-artifact
|
|
300
|
+
// disproof cannot be read off a path the range changed. Checked over
|
|
301
|
+
// EVERY candidate, not only the one `exists` happens to hit first: the
|
|
302
|
+
// conservative direction is fewer disproofs, never more.
|
|
303
|
+
if (candidates.some((c) => modified.has(c))) continue;
|
|
304
|
+
const hit = candidates.find((c) => exists(c));
|
|
305
|
+
if (hit !== undefined) {
|
|
306
|
+
return (
|
|
307
|
+
`the range deletes nothing matching \`${name}\`, and \`${hit}\` is present in the tree — ` +
|
|
308
|
+
'checked against `git diff --name-status base...HEAD` and the working tree, not against the review chunk.'
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* The two counts road-to-a-fact-plane Step 2.3 pre-registers as its falsifier,
|
|
317
|
+
* written into the findings artifact so the next cut can be read rather than
|
|
318
|
+
* recomputed.
|
|
319
|
+
*
|
|
320
|
+
* `asserting_removal` is the population — findings that make a checkable
|
|
321
|
+
* factual claim of the deletion class. `disproved_by_tree` is how many of them
|
|
322
|
+
* the range and the tree refuted. The premise of the fact block is that
|
|
323
|
+
* supplying the range's facts lowers the first and, with it, the second; a
|
|
324
|
+
* `disproved_by_tree` that does not fall after the block ships is the honest
|
|
325
|
+
* null, and the roadmap records that result rather than widening the mechanism
|
|
326
|
+
* to chase it.
|
|
327
|
+
*
|
|
328
|
+
* Every recorded cut so far reports `disproved_by_tree` as 0 for a reason that
|
|
329
|
+
* is not a measurement: the annotation pass landed after the last of them, so
|
|
330
|
+
* the number was never produced. The baseline is "never observed", not
|
|
331
|
+
* "observed as zero", and the first cut after this change is the first reading.
|
|
332
|
+
*/
|
|
333
|
+
export function factClaimCounts(findings: readonly Finding[]): {
|
|
334
|
+
asserting_removal: number;
|
|
335
|
+
disproved_by_tree: number;
|
|
336
|
+
} {
|
|
337
|
+
return {
|
|
338
|
+
asserting_removal: findings.filter((f) => assertsDeletion(f)).length,
|
|
339
|
+
disproved_by_tree: findings.filter((f) => (f.contradicted ?? '').trim() !== '').length,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/** Render one list of the fact block, or an explicit `(none)`. */
|
|
344
|
+
function factList(label: string, paths: readonly string[]): string {
|
|
345
|
+
const head = `${label} — ${String(paths.length)}:`;
|
|
346
|
+
return paths.length === 0 ? `${head}\n (none)` : `${head}\n${paths.map((p) => ` ${p}`).join('\n')}`;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* The range's facts, serialised for the reviewer.
|
|
351
|
+
*
|
|
352
|
+
* WHY THE REVIEWER IS HANDED THESE RATHER THAN LEFT TO INFER THEM. The review
|
|
353
|
+
* runs over a per-file PARTITION of the span and, on a large release, over only
|
|
354
|
+
* part of it — 202 of 401 files at the 16.1.0 cut. From inside one chunk, "this
|
|
355
|
+
* path is not in front of me" and "this path was deleted" are the same
|
|
356
|
+
* observation, and on that cut the model resolved the ambiguity the wrong way
|
|
357
|
+
* twice, in `critical security` findings that each cost an adjudication cycle.
|
|
358
|
+
* `contradictedByTree` catches that class AFTER the spend; this block removes
|
|
359
|
+
* the inference that produces it.
|
|
360
|
+
*
|
|
361
|
+
* Exactly three lists, all of them already computed by this gate: the paths the
|
|
362
|
+
* range deleted, the paths it changed and left standing, and the working tree's
|
|
363
|
+
* presence check over both — reported by its EXCEPTIONS, because the normal
|
|
364
|
+
* case is empty and a third full copy of the same paths would spend budget on
|
|
365
|
+
* a line the reviewer can derive. Nothing else is added: the block is paths, so
|
|
366
|
+
* it scales with file count and not with change size, and the six-request
|
|
367
|
+
* ceiling stays a budget question this does not reopen.
|
|
368
|
+
*/
|
|
369
|
+
export function factBlock(
|
|
370
|
+
deleted: ReadonlySet<string>,
|
|
371
|
+
modified: ReadonlySet<string>,
|
|
372
|
+
exists: (p: string) => boolean,
|
|
373
|
+
): string {
|
|
374
|
+
const del = [...deleted].sort();
|
|
375
|
+
const mod = [...modified].sort();
|
|
376
|
+
return [
|
|
377
|
+
'=== RANGE FACTS (AUTHORITATIVE) ===',
|
|
378
|
+
'Read from `git diff --name-status <base>...HEAD` and from the working tree,',
|
|
379
|
+
'NOT from the diff below. The diff below is ONE PARTITION of this range: a',
|
|
380
|
+
'path missing from it is a path outside your chunk, never a deleted path.',
|
|
381
|
+
'A claim that contradicts these lists is a defect in the finding, not a',
|
|
382
|
+
'finding about the tree — do not report it.',
|
|
383
|
+
'',
|
|
384
|
+
factList('DELETED BY THIS RANGE', del),
|
|
385
|
+
'',
|
|
386
|
+
factList('CHANGED BY THIS RANGE, AND THE TREE HOLDS THEM', mod),
|
|
387
|
+
'',
|
|
388
|
+
'PRESENCE CHECK (exceptions to the two lists above):',
|
|
389
|
+
factList(' deleted, yet still present in the working tree', del.filter((p) => exists(p))),
|
|
390
|
+
factList(' changed, yet absent from the working tree', mod.filter((p) => !exists(p))),
|
|
391
|
+
'=== END RANGE FACTS ===',
|
|
392
|
+
'',
|
|
393
|
+
'',
|
|
394
|
+
].join('\n');
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Annotate every finding the tree disproves. Pure given `deleted` + `exists`. */
|
|
398
|
+
export function annotateContradicted(
|
|
399
|
+
findings: readonly Finding[],
|
|
400
|
+
deleted: ReadonlySet<string>,
|
|
401
|
+
exists: (p: string) => boolean,
|
|
402
|
+
modified: ReadonlySet<string> = new Set<string>(),
|
|
403
|
+
): Finding[] {
|
|
404
|
+
return findings.map((f) => {
|
|
405
|
+
const why = contradictedByTree(f, deleted, exists, modified);
|
|
406
|
+
return why === null ? f : { ...f, contradicted: why };
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
123
410
|
function diffText(baseRef: string, files: string[], cwd: string = REPO_ROOT): string {
|
|
124
411
|
if (files.length === 0) return '';
|
|
125
412
|
const r = spawnSync('git', ['diff', `${baseRef}...HEAD`, '--', ...files], {
|
|
@@ -459,6 +746,14 @@ export interface ReviewPlan {
|
|
|
459
746
|
* it thrown away.
|
|
460
747
|
*/
|
|
461
748
|
partition: DiffPartition;
|
|
749
|
+
/**
|
|
750
|
+
* The range facts prepended to EVERY request — see `factBlock`.
|
|
751
|
+
*
|
|
752
|
+
* Computed in the plan rather than at send time so `--dry-run` can print
|
|
753
|
+
* the request the live run would send, and so the partition budget can
|
|
754
|
+
* reserve the block's length instead of discovering it after the split.
|
|
755
|
+
*/
|
|
756
|
+
facts: string;
|
|
462
757
|
/** Model calls the live run will make — one per budgeted chunk. */
|
|
463
758
|
requests: number;
|
|
464
759
|
/** Files no request will carry, with the reason. */
|
|
@@ -499,22 +794,33 @@ export function buildPlan(baseRef: string, cwd: string = REPO_ROOT): ReviewPlan
|
|
|
499
794
|
// REQUEST COUNT rather than only a token estimate. This gate spends money
|
|
500
795
|
// per request, and a plan that reports "~N tokens" without saying how many
|
|
501
796
|
// calls that becomes hides the number the maintainer is deciding about.
|
|
502
|
-
|
|
797
|
+
// The facts ride on every request, so the per-request diff budget has to
|
|
798
|
+
// pay for them. Reserving here rather than at send time is what keeps the
|
|
799
|
+
// block from silently pushing a chunk over the provider's cap — the exact
|
|
800
|
+
// failure four consecutive releases already spent a review on.
|
|
801
|
+
const facts = factBlock(rangeDeletions(analysisBase, cwd), rangeModifications(analysisBase, cwd), (p) =>
|
|
802
|
+
existsSync(path.join(cwd, p)),
|
|
803
|
+
);
|
|
804
|
+
const partition = partitionDiff(
|
|
805
|
+
perFileDiffs(analysisBase, files, cwd),
|
|
806
|
+
Math.max(1, PROMPT_BUDGET_CHARS - facts.length),
|
|
807
|
+
);
|
|
503
808
|
return {
|
|
504
809
|
partition,
|
|
810
|
+
facts,
|
|
505
811
|
requests: partition.chunks.length,
|
|
506
812
|
unreviewed: partition.unreviewed,
|
|
507
813
|
skills: [...REVIEW_SKILLS],
|
|
508
814
|
files,
|
|
509
815
|
analysisBase,
|
|
510
816
|
...(release ? { release } : {}),
|
|
511
|
-
promptChars: systemPrompt.length + diff.length,
|
|
817
|
+
promptChars: systemPrompt.length + facts.length + diff.length,
|
|
512
818
|
escalation,
|
|
513
819
|
note:
|
|
514
820
|
files.length === 0
|
|
515
821
|
? 'No reviewable (non-generated) files changed — the live review would no-op.'
|
|
516
822
|
: `${files.length} reviewable file(s); live review would send ~${Math.ceil(
|
|
517
|
-
(systemPrompt.length * Math.max(1, partition.chunks.length) + diff.length) /
|
|
823
|
+
((systemPrompt.length + facts.length) * Math.max(1, partition.chunks.length) + diff.length) /
|
|
518
824
|
BUDGET_CHARS_PER_TOKEN,
|
|
519
825
|
)} input tokens across ${partition.chunks.length} request(s)` +
|
|
520
826
|
(partition.unreviewed.length > 0
|
|
@@ -639,7 +945,11 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
|
|
|
639
945
|
// read as inconsistent with the narrow `blocking.length` verdict count.
|
|
640
946
|
const rows = findings
|
|
641
947
|
.map((f) => {
|
|
642
|
-
const marker =
|
|
948
|
+
const marker = (f.contradicted ?? '') !== ''
|
|
949
|
+
? 'Contradicted'
|
|
950
|
+
: classifyBlocking(f)
|
|
951
|
+
? 'Blocking'
|
|
952
|
+
: 'Advisory';
|
|
643
953
|
return `| ${findingId(f)} | ${f.severity} (${marker}) | ${f.kind} | ${f.file ?? '—'} | ${f.title} |`;
|
|
644
954
|
})
|
|
645
955
|
.join('\n');
|
|
@@ -653,6 +963,12 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
|
|
|
653
963
|
// 12-hex id looks exactly like a short SHA, so the rendered comment now says
|
|
654
964
|
// what it is and where its disposition lives. This is the only place a reader
|
|
655
965
|
// of the comment can learn it without opening the source.
|
|
966
|
+
const contradicted = findings.filter((f) => (f.contradicted ?? '') !== '');
|
|
967
|
+
const contradictedNote = contradicted.length === 0
|
|
968
|
+
? ''
|
|
969
|
+
: '\n\n**Contradicted by the tree** — these assert a removal the range does not contain, '
|
|
970
|
+
+ 'so they are reported and NOT counted as blocking:\n'
|
|
971
|
+
+ contradicted.map((f) => `- \`${findingId(f)}\` ${f.title} — ${f.contradicted ?? ''}`).join('\n');
|
|
656
972
|
const idNote =
|
|
657
973
|
'\n\n`id` above is a FINDING id — the first 12 hex of sha256(kind|title|file), '
|
|
658
974
|
+ '**not a commit SHA**. Searching the git log for it finds nothing, by design. '
|
|
@@ -662,9 +978,20 @@ export function renderReview(findings: Finding[], enforce: boolean, escalation:
|
|
|
662
978
|
// demands committed dispositions. The comment is transport, never the
|
|
663
979
|
// record — the record is `agents/evidence/release-findings/<version>.json`.
|
|
664
980
|
const machine = `\n\n<!-- release-findings-json: ${JSON.stringify(
|
|
665
|
-
findings.map((f) => ({
|
|
981
|
+
findings.map((f) => ({
|
|
982
|
+
finding_id: findingId(f),
|
|
983
|
+
severity: f.severity,
|
|
984
|
+
kind: f.kind,
|
|
985
|
+
title: f.title,
|
|
986
|
+
file: f.file ?? null,
|
|
987
|
+
// Carried into the trigger block, not only the rendered table: the
|
|
988
|
+
// PR-comment path of `check_finding_dispositions` reads THIS, and
|
|
989
|
+
// would otherwise demand a disposition for a claim the tree already
|
|
990
|
+
// disproved.
|
|
991
|
+
...((f.contradicted ?? '') !== '' ? { contradicted: f.contradicted } : {}),
|
|
992
|
+
})),
|
|
666
993
|
)} -->`;
|
|
667
|
-
return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${idNote}${cov}${esc}${machine}`;
|
|
994
|
+
return `${banner}\n\n${verdictLine}\n\n| id | severity | kind | file | finding |\n|---|---|---|---|---|\n${rows}${contradictedNote}${idNote}${cov}${esc}${machine}`;
|
|
668
995
|
}
|
|
669
996
|
|
|
670
997
|
function postReview(body: string): void {
|
|
@@ -691,6 +1018,49 @@ function resolveKey(): string | null {
|
|
|
691
1018
|
}
|
|
692
1019
|
}
|
|
693
1020
|
|
|
1021
|
+
/** The transport a reviewer was reached on, for the run's own report. */
|
|
1022
|
+
interface Reviewer {
|
|
1023
|
+
client: ExternalAIClient;
|
|
1024
|
+
transport: 'cli' | 'api';
|
|
1025
|
+
how: string;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
/**
|
|
1029
|
+
* Resolve the review client CLI-FIRST — the subscription transport before the
|
|
1030
|
+
* metered one, which is the ordering the rest of this suite already uses for
|
|
1031
|
+
* coding work and which the council has used since it grew a `mode: cli`.
|
|
1032
|
+
*
|
|
1033
|
+
* The ordering is not a preference. An API key is a METERED resource with a
|
|
1034
|
+
* balance that runs out, and when it does the gate reports NEUTRAL — a release
|
|
1035
|
+
* then carries no review at all. That is not hypothetical: it is how 16.3.0
|
|
1036
|
+
* stalled, with six chunks returning `credit balance is too low` and the
|
|
1037
|
+
* release step refusing for want of a findings artefact. A vendor CLI backed by
|
|
1038
|
+
* a subscription has no balance to exhaust, so trying it first removes the one
|
|
1039
|
+
* failure mode that has actually produced an unreviewed release here.
|
|
1040
|
+
*
|
|
1041
|
+
* `AnthropicCliClient`'s constructor THROWS when `claude` is not on PATH, which
|
|
1042
|
+
* is the probe: a hosted GitHub runner has no vendor CLI and falls straight
|
|
1043
|
+
* through to the API transport, so this change costs CI nothing and buys every
|
|
1044
|
+
* local and self-hosted run a free review. The catch is deliberately broad —
|
|
1045
|
+
* anything that goes wrong reaching for the CLI resolves to the API path rather
|
|
1046
|
+
* than to a NEUTRAL, because a reviewed release beats a diagnosed one.
|
|
1047
|
+
*
|
|
1048
|
+
* Calls book against `CLI_CONSUMER_SELF_REVIEW` so the daily CLI counter can
|
|
1049
|
+
* tell a review apart from a council round; without the attribution the gate's
|
|
1050
|
+
* six chunks would read as unexplained council spend.
|
|
1051
|
+
*/
|
|
1052
|
+
function resolveReviewer(): Reviewer | null {
|
|
1053
|
+
try {
|
|
1054
|
+
const client = new AnthropicCliClient({ consumer: CLI_CONSUMER_SELF_REVIEW });
|
|
1055
|
+
return { client, transport: 'cli', how: `vendor CLI (${client.binary}), subscription` };
|
|
1056
|
+
} catch {
|
|
1057
|
+
// No `claude` on PATH, or the client refused to construct. Fall through.
|
|
1058
|
+
}
|
|
1059
|
+
const key = resolveKey();
|
|
1060
|
+
if (!key) return null;
|
|
1061
|
+
return { client: new AnthropicClient({ api_key: key }), transport: 'api', how: 'ANTHROPIC_API_KEY' };
|
|
1062
|
+
}
|
|
1063
|
+
|
|
694
1064
|
// ── CLI ───────────────────────────────────────────────────────────────
|
|
695
1065
|
export function main(argv: string[]): 0 | 2 {
|
|
696
1066
|
const dryRun = argv.includes('--dry-run');
|
|
@@ -700,6 +1070,16 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
700
1070
|
|
|
701
1071
|
const plan = buildPlan(baseRef);
|
|
702
1072
|
|
|
1073
|
+
// The request the live run would send, verbatim and unspent. A summary
|
|
1074
|
+
// would be this script's description of the request rather than the
|
|
1075
|
+
// request, and the whole point of the fact block is that what the reviewer
|
|
1076
|
+
// is handed can be read rather than inferred — including by a maintainer.
|
|
1077
|
+
if (argv.includes('--print-request')) {
|
|
1078
|
+
const first = plan.partition.chunks[0];
|
|
1079
|
+
process.stdout.write(plan.facts + (first?.text ?? '(no chunk — nothing to review in this range)\n'));
|
|
1080
|
+
return 0;
|
|
1081
|
+
}
|
|
1082
|
+
|
|
703
1083
|
if (dryRun) {
|
|
704
1084
|
process.stdout.write(
|
|
705
1085
|
`self-review-gate (dry-run — no spend, advisory):\n` +
|
|
@@ -756,22 +1136,28 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
756
1136
|
return 0;
|
|
757
1137
|
}
|
|
758
1138
|
|
|
759
|
-
const
|
|
760
|
-
if (!
|
|
1139
|
+
const reviewer = resolveReviewer();
|
|
1140
|
+
if (!reviewer) {
|
|
761
1141
|
// Explicit NEUTRAL state — never a bare green that reads as "reviewed".
|
|
1142
|
+
// BOTH transports are named, because naming only the API one is what
|
|
1143
|
+
// made the CLI path invisible: an operator reading this warning set the
|
|
1144
|
+
// secret and never learned a logged-in `claude` would also have served.
|
|
762
1145
|
process.stdout.write(
|
|
763
|
-
'::warning::self-review-gate NEUTRAL — no
|
|
764
|
-
'
|
|
1146
|
+
'::warning::self-review-gate NEUTRAL — no reviewer reachable (no `claude` CLI on PATH, ' +
|
|
1147
|
+
'no ANTHROPIC_API_KEY), NOTHING was reviewed. Log in to the vendor CLI or set the ' +
|
|
1148
|
+
'repo secret to enable the live dogfooded review.\n',
|
|
765
1149
|
);
|
|
766
1150
|
const summary = process.env.GITHUB_STEP_SUMMARY;
|
|
767
1151
|
if (summary) {
|
|
768
1152
|
appendFileSync(
|
|
769
1153
|
summary,
|
|
770
|
-
'### Self-review gate: NEUTRAL\n\nNo
|
|
1154
|
+
'### Self-review gate: NEUTRAL\n\nNo reviewer reachable — neither the `claude` CLI ' +
|
|
1155
|
+
'(subscription) nor `ANTHROPIC_API_KEY` (metered). Nothing was reviewed. This is not a pass.\n',
|
|
771
1156
|
);
|
|
772
1157
|
}
|
|
773
1158
|
return 0;
|
|
774
1159
|
}
|
|
1160
|
+
process.stdout.write(`::notice::self-review-gate — transport: ${reviewer.transport} (${reviewer.how}).\n`);
|
|
775
1161
|
|
|
776
1162
|
// Belt-and-suspenders: the WHOLE live path is wrapped so that ANYTHING going
|
|
777
1163
|
// wrong — no API credit / balance (HTTP 402), rate-limit (429), network error,
|
|
@@ -780,7 +1166,7 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
780
1166
|
// gate is advisory by design. (Only an --enforce run with real blocking
|
|
781
1167
|
// findings returns 2 — an error is not a finding.)
|
|
782
1168
|
try {
|
|
783
|
-
const client =
|
|
1169
|
+
const client = reviewer.client;
|
|
784
1170
|
const systemPrompt = buildSystemPrompt(plan.release, baseRef);
|
|
785
1171
|
const partition = plan.partition;
|
|
786
1172
|
|
|
@@ -812,7 +1198,10 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
812
1198
|
let reviewedChunks = 0;
|
|
813
1199
|
let filesRead = 0;
|
|
814
1200
|
for (const [i, chunk] of partition.chunks.entries()) {
|
|
815
|
-
|
|
1201
|
+
// Facts FIRST, then the partition. The reviewer reads what the
|
|
1202
|
+
// range did before it reads the fragment it was given, so "absent
|
|
1203
|
+
// from my chunk" can no longer be mistaken for "deleted".
|
|
1204
|
+
const resp = client.ask(systemPrompt, plan.facts + chunk.text, 4096);
|
|
816
1205
|
if (resp.error) {
|
|
817
1206
|
process.stdout.write(
|
|
818
1207
|
`::warning::self-review-gate — chunk ${String(i + 1)}/` +
|
|
@@ -854,6 +1243,16 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
854
1243
|
filesTotal: plan.files.length,
|
|
855
1244
|
unreviewed,
|
|
856
1245
|
};
|
|
1246
|
+
// Ground-truth pass, ONCE, before anything reads the finding set. A
|
|
1247
|
+
// deletion claim the range and the tree both disprove is annotated
|
|
1248
|
+
// here and stops being merge-blocking; it is still written to the
|
|
1249
|
+
// artifact and still rendered, carrying its disproof.
|
|
1250
|
+
const reviewed = annotateContradicted(
|
|
1251
|
+
dedupeFindings(findings),
|
|
1252
|
+
rangeDeletions(baseRef),
|
|
1253
|
+
(p) => existsSync(path.join(REPO_ROOT, p)),
|
|
1254
|
+
rangeModifications(baseRef),
|
|
1255
|
+
);
|
|
857
1256
|
const outIdx = argv.indexOf('--findings-out');
|
|
858
1257
|
if (outIdx >= 0 && argv[outIdx + 1]) {
|
|
859
1258
|
// Durable ingestion input for the disposition ledger (release-truth
|
|
@@ -869,7 +1268,12 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
869
1268
|
schema_version: 1,
|
|
870
1269
|
...independenceFields(['anthropic']),
|
|
871
1270
|
coverage,
|
|
872
|
-
|
|
1271
|
+
// Readable, not recomputable: the falsifier for the
|
|
1272
|
+
// supplied-facts change is a count over the NEXT cut,
|
|
1273
|
+
// and a number a reader has to re-derive from 50
|
|
1274
|
+
// findings is a number nobody checks.
|
|
1275
|
+
fact_claims: factClaimCounts(reviewed),
|
|
1276
|
+
findings: reviewed.map((f) => ({
|
|
873
1277
|
finding_id: findingId(f),
|
|
874
1278
|
...f,
|
|
875
1279
|
})),
|
|
@@ -879,10 +1283,10 @@ export function main(argv: string[]): 0 | 2 {
|
|
|
879
1283
|
) + '\n',
|
|
880
1284
|
);
|
|
881
1285
|
}
|
|
882
|
-
const body = renderReview(
|
|
1286
|
+
const body = renderReview(reviewed, enforce, plan.escalation, coverage);
|
|
883
1287
|
postReview(body);
|
|
884
1288
|
|
|
885
|
-
const code = gateVerdict(
|
|
1289
|
+
const code = gateVerdict(reviewed, { enforce });
|
|
886
1290
|
if (code === 2) {
|
|
887
1291
|
process.stdout.write('::error::self-review-gate — merge-blocking findings under enforce mode.\n');
|
|
888
1292
|
}
|