@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
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
import * as fs from 'node:fs';
|
|
56
56
|
import * as os from 'node:os';
|
|
57
57
|
import * as path from 'node:path';
|
|
58
|
+
|
|
59
|
+
import * as yaml from 'js-yaml';
|
|
58
60
|
import process from 'node:process';
|
|
59
61
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
60
62
|
|
|
@@ -75,6 +77,19 @@ export const LOWERING_REL = path.join('src', 'scripts', 'hooks', 'host_lowering.
|
|
|
75
77
|
export const BEGIN_MARKER = '<!-- BEGIN GENERATED: enforcement-configured-by-slot -->';
|
|
76
78
|
export const END_MARKER = '<!-- END GENERATED: enforcement-configured-by-slot -->';
|
|
77
79
|
|
|
80
|
+
/**
|
|
81
|
+
* The second generated region: every `blocking` concern, with the enforcement
|
|
82
|
+
* the host it is bound on can actually carry.
|
|
83
|
+
*
|
|
84
|
+
* road-to-blocking-severities 1.2. Its own marker pair rather than an extra
|
|
85
|
+
* column in the first region, because the two tables have different units — the
|
|
86
|
+
* first is one row per host-SLOT, this is one row per host-slot-CONCERN — and
|
|
87
|
+
* merging them would make every host-slot row repeat once per concern bound to
|
|
88
|
+
* it.
|
|
89
|
+
*/
|
|
90
|
+
export const SEVERITY_BEGIN_MARKER = '<!-- BEGIN GENERATED: blocking-severity-by-binding -->';
|
|
91
|
+
export const SEVERITY_END_MARKER = '<!-- END GENERATED: blocking-severity-by-binding -->';
|
|
92
|
+
|
|
78
93
|
/** The command a drift failure tells the reader to run. Printed, never guessed at. */
|
|
79
94
|
export const REGEN_COMMAND = `./scripts-run src/scripts/${GATE} --write`;
|
|
80
95
|
|
|
@@ -88,12 +103,28 @@ export const CONFIGURED_OUTCOMES = ['refusal', 'halt-by-state', 'warning', 'unen
|
|
|
88
103
|
|
|
89
104
|
export type ConfiguredOutcome = (typeof CONFIGURED_OUTCOMES)[number];
|
|
90
105
|
|
|
106
|
+
/**
|
|
107
|
+
* What the `Answered` column prints for a pair the configuration never dated.
|
|
108
|
+
*
|
|
109
|
+
* A word rather than an empty cell, because an empty cell in a rendered table
|
|
110
|
+
* is indistinguishable from a rendering bug, and this one is the finding.
|
|
111
|
+
* `lint_hook_manifest._check_slot_answers` is what actually refuses it; this
|
|
112
|
+
* gate's job is to make sure the published document cannot hide it.
|
|
113
|
+
*/
|
|
114
|
+
export const UNDATED = '`undated`';
|
|
115
|
+
|
|
91
116
|
export interface MatrixRow {
|
|
92
117
|
host: string;
|
|
93
118
|
slot: string;
|
|
94
119
|
outcome: ConfiguredOutcome;
|
|
95
120
|
/** The configuration values the outcome was read off, quoted for the table. */
|
|
96
121
|
backing: string;
|
|
122
|
+
/**
|
|
123
|
+
* `answered_at` for this pair, or `undated` when the configuration carries
|
|
124
|
+
* none. Printed LAST so the outcome stays in column three and
|
|
125
|
+
* {@link committedCells} keeps reading the same index it always did.
|
|
126
|
+
*/
|
|
127
|
+
answered: string;
|
|
97
128
|
}
|
|
98
129
|
|
|
99
130
|
export interface BuildResult {
|
|
@@ -192,6 +223,7 @@ export function buildRows(lowering: HostLowering): BuildResult {
|
|
|
192
223
|
slot: reading.slot,
|
|
193
224
|
outcome,
|
|
194
225
|
backing: backingFor(reading, surface.fail_policy),
|
|
226
|
+
answered: surface.slots.get(reading.slot)?.answered_at ?? UNDATED,
|
|
195
227
|
});
|
|
196
228
|
}
|
|
197
229
|
}
|
|
@@ -295,10 +327,12 @@ export function renderRegion(lowering: HostLowering): string {
|
|
|
295
327
|
out.push('');
|
|
296
328
|
out.push(summaryLine(lowering, rows));
|
|
297
329
|
out.push('');
|
|
298
|
-
out.push('| Host | Slot | Configured outcome | Backing |');
|
|
299
|
-
out.push('
|
|
330
|
+
out.push('| Host | Slot | Configured outcome | Backing | Answered |');
|
|
331
|
+
out.push('|---|---|---|---|---|');
|
|
300
332
|
for (const r of rows) {
|
|
301
|
-
out.push(
|
|
333
|
+
out.push(
|
|
334
|
+
`| \`${r.host}\` | \`${r.slot}\` | \`${r.outcome}\` | ${r.backing} | ${r.answered} |`,
|
|
335
|
+
);
|
|
302
336
|
}
|
|
303
337
|
out.push('');
|
|
304
338
|
if (slotlessHosts.length === 0) {
|
|
@@ -314,31 +348,200 @@ export function renderRegion(lowering: HostLowering): string {
|
|
|
314
348
|
return out.join('\n');
|
|
315
349
|
}
|
|
316
350
|
|
|
317
|
-
/**
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
351
|
+
/**
|
|
352
|
+
* How each {@link VerifiedEnforcement} is worded in the published table.
|
|
353
|
+
*
|
|
354
|
+
* `unverified` and `warning-only` are worded so a reader cannot slide between
|
|
355
|
+
* them. Only `warning-only` says "cannot"; `unverified` says what is missing.
|
|
356
|
+
*/
|
|
357
|
+
export const ENFORCEMENT_TEXT: Readonly<Record<VerifiedEnforcement, string>> = {
|
|
358
|
+
refusal: 'the slot denies — the concern does what it declares',
|
|
359
|
+
'proof-expired': 'the slot can deny; this package’s `verified` citation has lapsed',
|
|
360
|
+
'warning-only': 'the slot is bound and `block_exit` is null — it runs and warns, it cannot refuse',
|
|
361
|
+
unverified: 'no lowering row for this slot — nothing is bound natively, and nothing is established',
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The 1.2 region body: one row per `blocking` binding, two independent columns.
|
|
366
|
+
*
|
|
367
|
+
* Grouped by host and ordered as `concernSlotAudit` sorted it, so a
|
|
368
|
+
* regeneration is a no-op whenever nothing moved.
|
|
369
|
+
*/
|
|
370
|
+
export function renderSeverityRegion(audit: readonly HostConcernAudit[]): string {
|
|
371
|
+
const all = audit.flatMap((h) => h.blocking.map((b) => b));
|
|
372
|
+
const out: string[] = [];
|
|
373
|
+
out.push(
|
|
374
|
+
`Projected from \`${MANIFEST_REL}\` (declared severity) and \`${LOWERING_REL}\` ` +
|
|
375
|
+
'(verified enforcement). **Two independent facts, never folded into one.**',
|
|
376
|
+
);
|
|
377
|
+
out.push('');
|
|
378
|
+
out.push(
|
|
379
|
+
'The manifest declares what a concern is *meant* to do; the lowering table records ' +
|
|
380
|
+
'what the host slot it is bound on *can* do. A `blocking` concern on a slot that ' +
|
|
381
|
+
'cannot refuse still runs and still warns — it is not silent, and it is not a ' +
|
|
382
|
+
'refusal either. These columns say which.',
|
|
383
|
+
);
|
|
384
|
+
out.push('');
|
|
385
|
+
const byEnforcement = (e: VerifiedEnforcement): number =>
|
|
386
|
+
all.filter((b) => b.enforcement === e).length;
|
|
387
|
+
out.push(
|
|
388
|
+
`**${String(all.length)} binding(s) declare \`blocking\`: ` +
|
|
389
|
+
`${String(byEnforcement('refusal'))} refuse, ` +
|
|
390
|
+
`${String(byEnforcement('warning-only'))} run and warn on a slot verified unable to ` +
|
|
391
|
+
`refuse, ${String(byEnforcement('unverified'))} sit on a slot with no lowering row, ` +
|
|
392
|
+
`${String(byEnforcement('proof-expired'))} sit on a slot whose proof has lapsed.**`,
|
|
393
|
+
);
|
|
394
|
+
out.push('');
|
|
395
|
+
out.push('| Host | Slot | Concern | Declared severity | Verified enforcement | What that means |');
|
|
396
|
+
out.push('|---|---|---|---|---|---|');
|
|
397
|
+
for (const b of all) {
|
|
398
|
+
out.push(
|
|
399
|
+
`| \`${b.host}\` | \`${b.slot}\` | \`${b.concern}\` | \`${b.declared}\` | ` +
|
|
400
|
+
`\`${b.enforcement}\` | ${ENFORCEMENT_TEXT[b.enforcement]} |`,
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
if (all.length === 0) {
|
|
404
|
+
out.push('');
|
|
405
|
+
out.push('No concern in the manifest declares `blocking`.');
|
|
406
|
+
}
|
|
407
|
+
return out.join('\n');
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** Replace the block between one marker pair. Idempotent by construction. */
|
|
411
|
+
function _splice(text: string, region: string, begin: string, end: string): string {
|
|
412
|
+
const b = text.indexOf(begin);
|
|
413
|
+
const e = text.indexOf(end);
|
|
321
414
|
if (b === -1 || e === -1 || e < b) {
|
|
322
415
|
throw new Error(
|
|
323
416
|
`${GATE}: ${DOC_REL} is missing the generated-region markers ` +
|
|
324
|
-
`(${
|
|
417
|
+
`(${begin} … ${end}). Restore them rather than hand-writing the ` +
|
|
325
418
|
'table — the markers are what makes the region regenerable.',
|
|
326
419
|
);
|
|
327
420
|
}
|
|
328
|
-
return `${text.slice(0, b +
|
|
421
|
+
return `${text.slice(0, b + begin.length)}\n${region}\n${text.slice(e)}`;
|
|
329
422
|
}
|
|
330
423
|
|
|
331
|
-
/**
|
|
332
|
-
export function
|
|
333
|
-
|
|
334
|
-
|
|
424
|
+
/** Replace the block between the markers. Idempotent by construction. */
|
|
425
|
+
export function spliceDoc(text: string, region: string): string {
|
|
426
|
+
return _splice(text, region, BEGIN_MARKER, END_MARKER);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/** Replace the blocking-severity region. Same contract as {@link spliceDoc}. */
|
|
430
|
+
export function spliceSeverityDoc(text: string, region: string): string {
|
|
431
|
+
return _splice(text, region, SEVERITY_BEGIN_MARKER, SEVERITY_END_MARKER);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function _extract(text: string, begin: string, end: string): string | null {
|
|
435
|
+
const b = text.indexOf(begin);
|
|
436
|
+
const e = text.indexOf(end);
|
|
335
437
|
if (b === -1 || e === -1 || e < b) return null;
|
|
336
438
|
return text
|
|
337
|
-
.slice(b +
|
|
439
|
+
.slice(b + begin.length, e)
|
|
338
440
|
.replace(/^\n/, '')
|
|
339
441
|
.replace(/\n$/, '');
|
|
340
442
|
}
|
|
341
443
|
|
|
444
|
+
/** The region body a committed document carries, or null when the markers are gone. */
|
|
445
|
+
export function extractRegion(text: string): string | null {
|
|
446
|
+
return _extract(text, BEGIN_MARKER, END_MARKER);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** The blocking-severity region a committed document carries, or null. */
|
|
450
|
+
export function extractSeverityRegion(text: string): string | null {
|
|
451
|
+
return _extract(text, SEVERITY_BEGIN_MARKER, SEVERITY_END_MARKER);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/** One published blocking-severity row, read off the committed region. */
|
|
455
|
+
export interface CommittedSeverityCell {
|
|
456
|
+
readonly host: string;
|
|
457
|
+
readonly slot: string;
|
|
458
|
+
readonly concern: string;
|
|
459
|
+
readonly declared: string;
|
|
460
|
+
readonly enforcement: string;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Blocking-severity rows a committed region carries, in document order. */
|
|
464
|
+
export function committedSeverityCells(region: string): CommittedSeverityCell[] {
|
|
465
|
+
const out: CommittedSeverityCell[] = [];
|
|
466
|
+
for (const line of region.split('\n')) {
|
|
467
|
+
if (!line.trim().startsWith('|')) continue;
|
|
468
|
+
const cells = splitMarkdownRow(line);
|
|
469
|
+
if (cells.length < 5) continue;
|
|
470
|
+
if (cells[0] === 'Host' || /^-+$/.test((cells[0] ?? '').trim())) continue;
|
|
471
|
+
const strip = (s: string): string => s.trim().replace(/^`|`$/g, '');
|
|
472
|
+
out.push({
|
|
473
|
+
host: strip(cells[0] ?? ''),
|
|
474
|
+
slot: strip(cells[1] ?? ''),
|
|
475
|
+
concern: strip(cells[2] ?? ''),
|
|
476
|
+
declared: strip(cells[3] ?? ''),
|
|
477
|
+
enforcement: strip(cells[4] ?? ''),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* The 1.2 HARD gate: published enforcement claims the configuration refutes.
|
|
485
|
+
*
|
|
486
|
+
* This is the half the council asked to fail rather than report, and the
|
|
487
|
+
* distinction from the audit above is the reason it may. `concernSlotAudit`
|
|
488
|
+
* counts BINDINGS, a population with a legacy backlog this package did not
|
|
489
|
+
* create in one diff — failing on it would red the tree on history. These
|
|
490
|
+
* findings compare a GENERATED DOCUMENT against the configuration it is
|
|
491
|
+
* generated from, which has no backlog by construction: every row was written
|
|
492
|
+
* by the generator one command ago, so a disagreement is a hand edit or a
|
|
493
|
+
* generator bug and neither should ship.
|
|
494
|
+
*
|
|
495
|
+
* Both seats named the same two dishonesty classes, and both directions are
|
|
496
|
+
* checked because they fail in opposite ways:
|
|
497
|
+
*
|
|
498
|
+
* - A row claiming `refusal` where the slot's `block_exit` is null, or where
|
|
499
|
+
* there is no row at all, OVERSTATES the guarantee. A reader takes it as a
|
|
500
|
+
* deterministic block and gets a warning.
|
|
501
|
+
* - A row claiming `warning-only` where the lowering table has NO row for the
|
|
502
|
+
* slot UNDERSTATES what is known: it asserts the host cannot refuse, which
|
|
503
|
+
* is a host fact this package has not established and which
|
|
504
|
+
* `host_lowering.yaml` explicitly disclaims. `unverified` is the honest
|
|
505
|
+
* value there, and collapsing the two is how an absence of measurement
|
|
506
|
+
* becomes a measurement.
|
|
507
|
+
*
|
|
508
|
+
* A value outside {@link VERIFIED_ENFORCEMENTS} is its own finding, for the
|
|
509
|
+
* same reason `unbackedOutcomes` rejects one: an invented category in a
|
|
510
|
+
* generated table looks exactly as authoritative as a real one.
|
|
511
|
+
*/
|
|
512
|
+
export function severityFindings(region: string, lowering: HostLowering): string[] {
|
|
513
|
+
const findings: string[] = [];
|
|
514
|
+
for (const cell of committedSeverityCells(region)) {
|
|
515
|
+
const label = `${cell.host}/${cell.slot}/${cell.concern}`;
|
|
516
|
+
if (!(VERIFIED_ENFORCEMENTS as readonly string[]).includes(cell.enforcement)) {
|
|
517
|
+
findings.push(
|
|
518
|
+
`${label}: \`${cell.enforcement}\` is not in the closed enforcement vocabulary ` +
|
|
519
|
+
`(${VERIFIED_ENFORCEMENTS.map((e) => `\`${e}\``).join(' · ')}).`,
|
|
520
|
+
);
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const row = readSlots(lowering, cell.host).slots.find(
|
|
524
|
+
(s: SlotReading) => s.slot === cell.slot,
|
|
525
|
+
);
|
|
526
|
+
if (cell.enforcement === 'refusal' && (row === undefined || row.literal === null)) {
|
|
527
|
+
findings.push(
|
|
528
|
+
`${label}: published \`refusal\`, but ${LOWERING_REL} carries ` +
|
|
529
|
+
`${row === undefined ? 'no row for this slot' : '`block_exit: null`'} — a ` +
|
|
530
|
+
'refusal has nowhere to go, so the published row overstates the guarantee.',
|
|
531
|
+
);
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
if (cell.enforcement === 'warning-only' && row === undefined) {
|
|
535
|
+
findings.push(
|
|
536
|
+
`${label}: published \`warning-only\`, which asserts this slot CANNOT refuse, ` +
|
|
537
|
+
`but ${LOWERING_REL} carries no row for it at all. An absent row is ` +
|
|
538
|
+
'`unverified`, never a host fact — the table says so in its own header.',
|
|
539
|
+
);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
return findings;
|
|
543
|
+
}
|
|
544
|
+
|
|
342
545
|
/** Outcome cells a committed region carries, with their row label. */
|
|
343
546
|
export function committedCells(region: string): { label: string; value: string }[] {
|
|
344
547
|
const out: { label: string; value: string }[] = [];
|
|
@@ -394,6 +597,317 @@ function _read(root: string, rel: string): string | null {
|
|
|
394
597
|
/** Flags this gate understands. `--root` is the only one taking a value. */
|
|
395
598
|
const KNOWN_FLAGS: ReadonlySet<string> = new Set(['--self-test', '--root', '--write', '--quiet']);
|
|
396
599
|
|
|
600
|
+
/** Where the concern→slot bindings and per-concern severities live. */
|
|
601
|
+
export const MANIFEST_REL = path.join('src', 'scripts', 'hook_manifest.yaml');
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Why a bound concern's verdict cannot leave its slot.
|
|
605
|
+
*
|
|
606
|
+
* Three states, not two, and the third is the one an earlier version of this
|
|
607
|
+
* audit got wrong by skipping it. They are ordered worst-first:
|
|
608
|
+
*
|
|
609
|
+
* - `unlowerable` — the lowering table carries NO row for this slot on this
|
|
610
|
+
* host, so the installer writes no native binding at all.
|
|
611
|
+
* `cowork` is the worked case: `slots: {}` against sixty-odd
|
|
612
|
+
* declared bindings. Skipping it printed `0/0 · 0 blocking`,
|
|
613
|
+
* byte-identical to a host that binds nothing — a false
|
|
614
|
+
* green on the host with the largest gap.
|
|
615
|
+
* - `null-block` — a row exists and its literal `block_exit` is null. The
|
|
616
|
+
* slot is wired and a refusal has nowhere to go.
|
|
617
|
+
* - `stale-proof` — the literal `block_exit` CAN deny, but the row's
|
|
618
|
+
* `verified` block has expired, so `effective` reads null.
|
|
619
|
+
* This is a statement about this package's provenance, NOT
|
|
620
|
+
* about the host: `host_lowering.yaml` says in its own
|
|
621
|
+
* header that an absent `verified` "does NOT mean the host
|
|
622
|
+
* cannot enforce". Printing "cannot deny" here would
|
|
623
|
+
* assert exactly the host fact that file disclaims, so it
|
|
624
|
+
* is reported as its own class and worded differently.
|
|
625
|
+
*/
|
|
626
|
+
export type NoDenyReason = 'unlowerable' | 'null-block' | 'stale-proof';
|
|
627
|
+
|
|
628
|
+
/** One concern bound where its verdict cannot produce a refusal. */
|
|
629
|
+
export interface NullBlockBinding {
|
|
630
|
+
readonly host: string;
|
|
631
|
+
readonly slot: string;
|
|
632
|
+
readonly concern: string;
|
|
633
|
+
/** The concern's declared `severity`, or `(undeclared)`. */
|
|
634
|
+
readonly severity: string;
|
|
635
|
+
readonly reason: NoDenyReason;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/** What {@link concernSlotAudit} establishes for one host. */
|
|
639
|
+
export interface HostConcernAudit {
|
|
640
|
+
readonly host: string;
|
|
641
|
+
/** Lowerable slots whose literal `block_exit` can deny. */
|
|
642
|
+
readonly denySlots: number;
|
|
643
|
+
/** Lowerable slots in total. */
|
|
644
|
+
readonly lowerableSlots: number;
|
|
645
|
+
/** Distinct slots this host binds a concern to, whether lowerable or not. */
|
|
646
|
+
readonly boundSlots: number;
|
|
647
|
+
/**
|
|
648
|
+
* Bindings whose verdict cannot become a refusal, worst reason first and
|
|
649
|
+
* `blocking` severity ahead of the rest within a reason.
|
|
650
|
+
*
|
|
651
|
+
* An `advisory` concern here is CONSISTENT, not a defect — most concerns are
|
|
652
|
+
* advisory and belong on a reporting slot. The row that matters is a
|
|
653
|
+
* `blocking` severity: a concern declared to refuse, bound where a refusal
|
|
654
|
+
* has nowhere to go.
|
|
655
|
+
*/
|
|
656
|
+
readonly noDeny: readonly NullBlockBinding[];
|
|
657
|
+
/**
|
|
658
|
+
* EVERY binding on this host whose declared severity is `blocking`, with
|
|
659
|
+
* the enforcement its slot carries — including the ones that CAN refuse,
|
|
660
|
+
* which `noDeny` by construction never holds.
|
|
661
|
+
*
|
|
662
|
+
* Separate from `noDeny` rather than a filter over it, because the
|
|
663
|
+
* published table needs the complete picture: a reader who sees only the
|
|
664
|
+
* gaps cannot tell a host that refuses nowhere from a host with no blocking
|
|
665
|
+
* concerns at all, and those are the two readings the 1.2 table exists to
|
|
666
|
+
* keep apart.
|
|
667
|
+
*/
|
|
668
|
+
readonly blocking: readonly BlockingBinding[];
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/** Slots that carry a dispatcher verdict. `ask` is a UI affordance, not one. */
|
|
672
|
+
const NON_VERDICT_SLOTS: ReadonlySet<string> = new Set(['ask']);
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Cross-reference the manifest's concern bindings against the lowering table's
|
|
676
|
+
* per-slot `block_exit`.
|
|
677
|
+
*
|
|
678
|
+
* road-to-a-content-scanner-on-a-slot-that-can-refuse 2.1. The gate above
|
|
679
|
+
* compares published host-SLOT rows against the configuration and is green
|
|
680
|
+
* whenever those agree — it never asks whether a CONCERN is bound to a slot
|
|
681
|
+
* that can carry its severity. That question was answered once, by an external
|
|
682
|
+
* reader comparing two files by hand, which is why it drifted back: a hand
|
|
683
|
+
* comparison leaves no number behind. This makes it a printed count.
|
|
684
|
+
*
|
|
685
|
+
* DELIBERATELY NOT A FAILURE. It prints; the exit code is unchanged. Failing
|
|
686
|
+
* would red the tree on bindings that predate this check — the same reason the
|
|
687
|
+
* continuity and estate ratchets report distance-to-target instead of gating on
|
|
688
|
+
* it. What a reader gets is a number that moves when a binding or a slot moves.
|
|
689
|
+
*
|
|
690
|
+
* NOTHING BOUND IS SILENTLY DROPPED. Every bound slot lands in exactly one of
|
|
691
|
+
* {can deny, one of the three {@link NoDenyReason} classes, non-verdict}, and
|
|
692
|
+
* `boundSlots` publishes the denominator so a reader can check the arithmetic
|
|
693
|
+
* rather than trust it. That is the correction the first version needed: it
|
|
694
|
+
* skipped any slot missing from the lowering table, which is precisely the
|
|
695
|
+
* largest gap in the tree.
|
|
696
|
+
*/
|
|
697
|
+
export function concernSlotAudit(root: string, lowering: HostLowering): HostConcernAudit[] {
|
|
698
|
+
const manifestText = _read(root, MANIFEST_REL);
|
|
699
|
+
if (manifestText === null) return [];
|
|
700
|
+
let manifest: unknown;
|
|
701
|
+
try {
|
|
702
|
+
manifest = yaml.load(manifestText);
|
|
703
|
+
} catch {
|
|
704
|
+
return [];
|
|
705
|
+
}
|
|
706
|
+
if (manifest === null || typeof manifest !== 'object') return [];
|
|
707
|
+
const m = manifest as Record<string, unknown>;
|
|
708
|
+
|
|
709
|
+
const severities = new Map<string, string>();
|
|
710
|
+
const concerns = m['concerns'];
|
|
711
|
+
if (concerns !== null && typeof concerns === 'object') {
|
|
712
|
+
for (const [name, spec] of Object.entries(concerns as Record<string, unknown>)) {
|
|
713
|
+
const sev =
|
|
714
|
+
spec !== null && typeof spec === 'object'
|
|
715
|
+
? (spec as Record<string, unknown>)['severity']
|
|
716
|
+
: undefined;
|
|
717
|
+
severities.set(name, typeof sev === 'string' ? sev : '(undeclared)');
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
const platforms = m['platforms'];
|
|
722
|
+
if (platforms === null || typeof platforms !== 'object') return [];
|
|
723
|
+
|
|
724
|
+
const out: HostConcernAudit[] = [];
|
|
725
|
+
for (const [host, blockRaw] of Object.entries(platforms as Record<string, unknown>)) {
|
|
726
|
+
const reading = readSlots(lowering, host);
|
|
727
|
+
const rows = new Map<string, SlotReading>(reading.slots.map((s: SlotReading) => [s.slot, s]));
|
|
728
|
+
const denySlots = reading.slots.filter((s: SlotReading) => s.literal !== null).length;
|
|
729
|
+
|
|
730
|
+
const noDeny: NullBlockBinding[] = [];
|
|
731
|
+
const blocking: BlockingBinding[] = [];
|
|
732
|
+
let boundSlots = 0;
|
|
733
|
+
if (blockRaw !== null && typeof blockRaw === 'object') {
|
|
734
|
+
for (const [slot, value] of Object.entries(blockRaw as Record<string, unknown>)) {
|
|
735
|
+
if (!Array.isArray(value)) continue;
|
|
736
|
+
if (NON_VERDICT_SLOTS.has(slot)) continue;
|
|
737
|
+
boundSlots += 1;
|
|
738
|
+
const row = rows.get(slot);
|
|
739
|
+
// The literal decides whether a refusal can leave the slot; the
|
|
740
|
+
// `verified` currency decides only whether this package has
|
|
741
|
+
// cited its proof. Reading `effective` here would turn an
|
|
742
|
+
// expired citation into a claim that the host cannot enforce.
|
|
743
|
+
const reason: NoDenyReason | null =
|
|
744
|
+
row === undefined
|
|
745
|
+
? 'unlowerable'
|
|
746
|
+
: row.literal === null
|
|
747
|
+
? 'null-block'
|
|
748
|
+
: row.effective === null
|
|
749
|
+
? 'stale-proof'
|
|
750
|
+
: null;
|
|
751
|
+
// The 1.2 roster runs BEFORE the `reason === null` return, so a
|
|
752
|
+
// binding on a slot that CAN refuse is recorded here and
|
|
753
|
+
// nowhere else.
|
|
754
|
+
for (const concern of value) {
|
|
755
|
+
if (typeof concern !== 'string') continue;
|
|
756
|
+
const declared = severities.get(concern) ?? '(unknown concern)';
|
|
757
|
+
if (declared !== 'blocking') continue;
|
|
758
|
+
blocking.push({
|
|
759
|
+
host,
|
|
760
|
+
slot,
|
|
761
|
+
concern,
|
|
762
|
+
declared,
|
|
763
|
+
enforcement: enforcementFor(reason),
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
if (reason === null) continue;
|
|
767
|
+
for (const concern of value) {
|
|
768
|
+
if (typeof concern !== 'string') continue;
|
|
769
|
+
noDeny.push({
|
|
770
|
+
host,
|
|
771
|
+
slot,
|
|
772
|
+
concern,
|
|
773
|
+
severity: severities.get(concern) ?? '(unknown concern)',
|
|
774
|
+
reason,
|
|
775
|
+
});
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
blocking.sort(
|
|
780
|
+
(a, b) => a.slot.localeCompare(b.slot) || a.concern.localeCompare(b.concern),
|
|
781
|
+
);
|
|
782
|
+
const reasonRank = (r: NoDenyReason): number =>
|
|
783
|
+
r === 'unlowerable' ? 0 : r === 'null-block' ? 1 : 2;
|
|
784
|
+
const sevRank = (s: string): number => (s === 'blocking' ? 0 : s === 'advisory' ? 2 : 1);
|
|
785
|
+
noDeny.sort(
|
|
786
|
+
(a, b) =>
|
|
787
|
+
reasonRank(a.reason) - reasonRank(b.reason) ||
|
|
788
|
+
sevRank(a.severity) - sevRank(b.severity) ||
|
|
789
|
+
a.slot.localeCompare(b.slot) ||
|
|
790
|
+
a.concern.localeCompare(b.concern),
|
|
791
|
+
);
|
|
792
|
+
out.push({
|
|
793
|
+
host,
|
|
794
|
+
denySlots,
|
|
795
|
+
lowerableSlots: reading.slots.length,
|
|
796
|
+
boundSlots,
|
|
797
|
+
noDeny,
|
|
798
|
+
blocking,
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
return out;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* What a binding's host slot can actually carry, as a closed vocabulary.
|
|
806
|
+
*
|
|
807
|
+
* road-to-blocking-severities 1.2, option (a) as the council revised it on
|
|
808
|
+
* 2026-10-01 (2/2, both seats). The revision is in the NAMES and it is the
|
|
809
|
+
* whole point: the first draft called this the "effective severity", and the
|
|
810
|
+
* second seat refused that term — severity has not changed, enforcement
|
|
811
|
+
* strength has, and a generated document that silently redefines the
|
|
812
|
+
* manifest's own word is worse than one that says nothing. So a binding
|
|
813
|
+
* carries two independent facts, published side by side and never folded into
|
|
814
|
+
* one: the DECLARED severity, which is the concern's policy intent and belongs
|
|
815
|
+
* to `hook_manifest.yaml`, and the VERIFIED ENFORCEMENT below, which is the
|
|
816
|
+
* host slot's measured capability and belongs to `host_lowering.yaml`.
|
|
817
|
+
*
|
|
818
|
+
* - `refusal` — the slot's `block_exit` can deny and its `verified`
|
|
819
|
+
* proof is current. A `blocking` concern here does what
|
|
820
|
+
* it says.
|
|
821
|
+
* - `warning-only` — a lowering row exists and its `block_exit` is null. The
|
|
822
|
+
* slot is wired, the concern runs, and its verdict cannot
|
|
823
|
+
* become a refusal. This is the only value that asserts
|
|
824
|
+
* the host CANNOT refuse, and it is backed by a row.
|
|
825
|
+
* - `unverified` — no lowering row for this slot on this host. Nothing is
|
|
826
|
+
* bound natively and nothing has been established.
|
|
827
|
+
* Deliberately NOT `warning-only`: `host_lowering.yaml`
|
|
828
|
+
* says in its own header that an absence "does NOT mean
|
|
829
|
+
* the host cannot enforce", and both council seats named
|
|
830
|
+
* collapsing the two the defect to avoid. The gate below
|
|
831
|
+
* fails a published row that collapses them.
|
|
832
|
+
* - `proof-expired` — the literal `block_exit` can deny but this package's
|
|
833
|
+
* `verified` citation has lapsed. A statement about our
|
|
834
|
+
* provenance, never about the host.
|
|
835
|
+
*
|
|
836
|
+
* Order is strongest-first, matching {@link CONFIGURED_OUTCOMES}'s convention.
|
|
837
|
+
*/
|
|
838
|
+
export const VERIFIED_ENFORCEMENTS = [
|
|
839
|
+
'refusal',
|
|
840
|
+
'proof-expired',
|
|
841
|
+
'warning-only',
|
|
842
|
+
'unverified',
|
|
843
|
+
] as const;
|
|
844
|
+
|
|
845
|
+
export type VerifiedEnforcement = (typeof VERIFIED_ENFORCEMENTS)[number];
|
|
846
|
+
|
|
847
|
+
/**
|
|
848
|
+
* The enforcement a {@link NoDenyReason} corresponds to; `null` means the slot
|
|
849
|
+
* can deny, which is the one case `concernSlotAudit` does not record a reason
|
|
850
|
+
* for.
|
|
851
|
+
*/
|
|
852
|
+
export function enforcementFor(reason: NoDenyReason | null): VerifiedEnforcement {
|
|
853
|
+
if (reason === null) return 'refusal';
|
|
854
|
+
if (reason === 'unlowerable') return 'unverified';
|
|
855
|
+
if (reason === 'null-block') return 'warning-only';
|
|
856
|
+
return 'proof-expired';
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/** One concern declared `blocking`, with the enforcement its host slot carries. */
|
|
860
|
+
export interface BlockingBinding {
|
|
861
|
+
readonly host: string;
|
|
862
|
+
readonly slot: string;
|
|
863
|
+
readonly concern: string;
|
|
864
|
+
/** Always `blocking` here — kept explicit so the published row carries both facts. */
|
|
865
|
+
readonly declared: string;
|
|
866
|
+
readonly enforcement: VerifiedEnforcement;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/** How each {@link NoDenyReason} is worded. `stale-proof` never says "cannot". */
|
|
870
|
+
const REASON_TEXT: Readonly<Record<NoDenyReason, string>> = {
|
|
871
|
+
unlowerable: 'no lowering row for this slot — nothing is bound natively at all',
|
|
872
|
+
'null-block': 'block_exit is null — a refusal has nowhere to go',
|
|
873
|
+
'stale-proof': 'block_exit can deny, but this row’s `verified` proof has expired',
|
|
874
|
+
};
|
|
875
|
+
|
|
876
|
+
/** Render the audit as the lines the reporter prints. */
|
|
877
|
+
export function renderConcernAudit(audit: readonly HostConcernAudit[]): string[] {
|
|
878
|
+
const lines: string[] = [];
|
|
879
|
+
lines.push('');
|
|
880
|
+
lines.push(`${GATE} · concerns bound where a verdict cannot become a refusal`);
|
|
881
|
+
lines.push(
|
|
882
|
+
' A blocking severity here is a concern declared to refuse, bound where a refusal',
|
|
883
|
+
);
|
|
884
|
+
lines.push(' cannot happen. An advisory one is consistent and is counted, not flagged.');
|
|
885
|
+
lines.push(' `stale-proof` is a statement about this package’s citation, never about the host.');
|
|
886
|
+
for (const h of audit) {
|
|
887
|
+
const blocking = h.noDeny.filter((b) => b.severity === 'blocking');
|
|
888
|
+
const others = h.noDeny.length - blocking.length;
|
|
889
|
+
lines.push(
|
|
890
|
+
` ${h.host.padEnd(10)} ${String(h.denySlots)}/${String(h.lowerableSlots)} lowerable slot(s) can deny · ` +
|
|
891
|
+
`${String(h.boundSlots)} verdict-bearing slot(s) bound · ` +
|
|
892
|
+
`${String(blocking.length)} blocking + ${String(others)} non-blocking binding(s) cannot refuse`,
|
|
893
|
+
);
|
|
894
|
+
// One line per (reason, slot) rather than per concern: a host with an
|
|
895
|
+
// empty `slots:` map has sixty-odd bindings and the reason is the fact,
|
|
896
|
+
// not the roster.
|
|
897
|
+
const seen = new Set<string>();
|
|
898
|
+
for (const b of h.noDeny) {
|
|
899
|
+
if (b.severity !== 'blocking') continue;
|
|
900
|
+
const key = `${b.reason}/${b.slot}`;
|
|
901
|
+
if (!seen.has(key)) {
|
|
902
|
+
seen.add(key);
|
|
903
|
+
lines.push(` ⚠️ ${b.slot} — ${REASON_TEXT[b.reason]}`);
|
|
904
|
+
}
|
|
905
|
+
lines.push(` ${b.concern} (${b.severity})`);
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
return lines;
|
|
909
|
+
}
|
|
910
|
+
|
|
397
911
|
export function main(argv?: readonly string[]): number {
|
|
398
912
|
const args = argv ?? process.argv.slice(2);
|
|
399
913
|
// An unrecognised flag is refused rather than ignored. Both ratification
|
|
@@ -437,19 +951,27 @@ export function main(argv?: readonly string[]): number {
|
|
|
437
951
|
const ledger = new GateLedger(GATE);
|
|
438
952
|
ledger.plan(built.rows.map((r) => `${r.host}/${r.slot}`));
|
|
439
953
|
const region = renderRegion(lowering);
|
|
954
|
+
const audit = concernSlotAudit(root, lowering);
|
|
955
|
+
const severityRegion = renderSeverityRegion(audit);
|
|
440
956
|
|
|
441
957
|
if (write) {
|
|
442
958
|
let next: string;
|
|
443
959
|
try {
|
|
444
|
-
next = spliceDoc(docText, region);
|
|
960
|
+
next = spliceSeverityDoc(spliceDoc(docText, region), severityRegion);
|
|
445
961
|
} catch (exc) {
|
|
446
962
|
process.stderr.write(`❌ ${String(exc)}\n`);
|
|
447
963
|
return 2;
|
|
448
964
|
}
|
|
449
965
|
// The backing check runs BEFORE the write, so a generator bug cannot
|
|
450
966
|
// put a phantom value into the document and then be caught by the
|
|
451
|
-
// check that same generator's output feeds.
|
|
452
|
-
|
|
967
|
+
// check that same generator's output feeds. The 1.2 severity check
|
|
968
|
+
// rides in the same position and for the same reason: a generator that
|
|
969
|
+
// could write `refusal` onto a null `block_exit` must be stopped at the
|
|
970
|
+
// write, not discovered by the next read-only run.
|
|
971
|
+
const phantom = [
|
|
972
|
+
...unbackedOutcomes(region, backedOutcomes(lowering)),
|
|
973
|
+
...severityFindings(severityRegion, lowering),
|
|
974
|
+
];
|
|
453
975
|
if (phantom.length > 0) {
|
|
454
976
|
for (const f of phantom) process.stderr.write(`❌ ${GATE}: ${f}\n`);
|
|
455
977
|
process.stderr.write(
|
|
@@ -483,14 +1005,31 @@ export function main(argv?: readonly string[]): number {
|
|
|
483
1005
|
return 2;
|
|
484
1006
|
}
|
|
485
1007
|
|
|
1008
|
+
const committedSeverity = extractSeverityRegion(docText);
|
|
1009
|
+
if (committedSeverity === null) {
|
|
1010
|
+
process.stderr.write(
|
|
1011
|
+
`❌ ${GATE}: ${DOC_REL} has no blocking-severity region. The markers ` +
|
|
1012
|
+
`${SEVERITY_BEGIN_MARKER} … ${SEVERITY_END_MARKER} are what make that table ` +
|
|
1013
|
+
`regenerable; restore them and run \`${REGEN_COMMAND}\`.\n`,
|
|
1014
|
+
);
|
|
1015
|
+
return 2;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
486
1018
|
const findings: string[] = [];
|
|
487
1019
|
findings.push(...unbackedOutcomes(committed, backedOutcomes(lowering)));
|
|
488
1020
|
findings.push(...built.contradictions);
|
|
1021
|
+
findings.push(...severityFindings(committedSeverity, lowering));
|
|
489
1022
|
if (committed !== region) {
|
|
490
1023
|
findings.push(
|
|
491
1024
|
`${DOC_REL} § the generated region differs from what \`${LOWERING_REL}\` produces.`,
|
|
492
1025
|
);
|
|
493
1026
|
}
|
|
1027
|
+
if (committedSeverity !== severityRegion) {
|
|
1028
|
+
findings.push(
|
|
1029
|
+
`${DOC_REL} § the blocking-severity region differs from what \`${MANIFEST_REL}\` ` +
|
|
1030
|
+
`and \`${LOWERING_REL}\` produce.`,
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
494
1033
|
|
|
495
1034
|
// A pair is `fail`ed when the committed cell for it is absent or carries a
|
|
496
1035
|
// different outcome, and `complete`d otherwise. The denominator is the
|
|
@@ -517,6 +1056,13 @@ export function main(argv?: readonly string[]): number {
|
|
|
517
1056
|
});
|
|
518
1057
|
ledger.report();
|
|
519
1058
|
|
|
1059
|
+
// The concern-vs-slot audit prints on every read-only run, before the
|
|
1060
|
+
// verdict, so it is visible whether the row comparison passed or failed.
|
|
1061
|
+
// It never changes the exit code — see `concernSlotAudit`.
|
|
1062
|
+
for (const line of renderConcernAudit(audit)) {
|
|
1063
|
+
process.stdout.write(`${line}\n`);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
520
1066
|
if (findings.length > 0) {
|
|
521
1067
|
for (const f of findings) process.stdout.write(`❌ ${GATE}: ${f}\n`);
|
|
522
1068
|
process.stdout.write(
|
|
@@ -546,15 +1092,33 @@ export function main(argv?: readonly string[]): number {
|
|
|
546
1092
|
function selfTest(): number {
|
|
547
1093
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'enforcement-matrix-'));
|
|
548
1094
|
const lowering = _read(ROOT, LOWERING_REL) ?? '';
|
|
549
|
-
const
|
|
1095
|
+
const manifest = _read(ROOT, MANIFEST_REL) ?? '';
|
|
1096
|
+
/**
|
|
1097
|
+
* Plant a fixture tree carrying BOTH generated regions.
|
|
1098
|
+
*
|
|
1099
|
+
* The manifest is copied in alongside the lowering table because the
|
|
1100
|
+
* severity region is derived from the two together — without it
|
|
1101
|
+
* `concernSlotAudit` reads nothing and every severity case would exercise
|
|
1102
|
+
* an empty table, which is a fixture that cannot fail.
|
|
1103
|
+
*/
|
|
1104
|
+
const plant = (
|
|
1105
|
+
name: string,
|
|
1106
|
+
mutate: (region: string) => string,
|
|
1107
|
+
mutateSeverity: (region: string) => string = (r) => r,
|
|
1108
|
+
): string => {
|
|
550
1109
|
const dir = path.join(tmp, name);
|
|
551
1110
|
fs.mkdirSync(path.join(dir, path.dirname(LOWERING_REL)), { recursive: true });
|
|
1111
|
+
fs.mkdirSync(path.join(dir, path.dirname(MANIFEST_REL)), { recursive: true });
|
|
552
1112
|
fs.mkdirSync(path.join(dir, path.dirname(DOC_REL)), { recursive: true });
|
|
553
1113
|
fs.writeFileSync(path.join(dir, LOWERING_REL), lowering, 'utf-8');
|
|
554
|
-
|
|
1114
|
+
fs.writeFileSync(path.join(dir, MANIFEST_REL), manifest, 'utf-8');
|
|
1115
|
+
const table = parseHostLowering(lowering);
|
|
1116
|
+
const region = renderRegion(table);
|
|
1117
|
+
const severity = renderSeverityRegion(concernSlotAudit(dir, table));
|
|
555
1118
|
fs.writeFileSync(
|
|
556
1119
|
path.join(dir, DOC_REL),
|
|
557
|
-
`# fixture\n\n${BEGIN_MARKER}\n${mutate(region)}\n${END_MARKER}\n
|
|
1120
|
+
`# fixture\n\n${BEGIN_MARKER}\n${mutate(region)}\n${END_MARKER}\n\n` +
|
|
1121
|
+
`${SEVERITY_BEGIN_MARKER}\n${mutateSeverity(severity)}\n${SEVERITY_END_MARKER}\n`,
|
|
558
1122
|
'utf-8',
|
|
559
1123
|
);
|
|
560
1124
|
return dir;
|
|
@@ -565,9 +1129,44 @@ function selfTest(): number {
|
|
|
565
1129
|
try {
|
|
566
1130
|
return runSelfTest({
|
|
567
1131
|
gate: GATE,
|
|
568
|
-
minCases:
|
|
569
|
-
minRejectCases:
|
|
1132
|
+
minCases: 8,
|
|
1133
|
+
minRejectCases: 7,
|
|
570
1134
|
cases: [
|
|
1135
|
+
// road-to-blocking-severities 1.2 — the two dishonesty classes
|
|
1136
|
+
// the council asked to FAIL rather than report. Both are planted
|
|
1137
|
+
// by hand, because a generator that could emit either is the bug
|
|
1138
|
+
// these cases exist to catch, so a generated fixture could not
|
|
1139
|
+
// demonstrate one.
|
|
1140
|
+
{
|
|
1141
|
+
name: 'a published `refusal` on a slot whose block_exit is null is rejected',
|
|
1142
|
+
expect: 'reject',
|
|
1143
|
+
run: () =>
|
|
1144
|
+
run(
|
|
1145
|
+
plant('overstate', (r) => r, (s) =>
|
|
1146
|
+
s.replace('| `warning-only` |', '| `refusal` |'),
|
|
1147
|
+
),
|
|
1148
|
+
),
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
name: 'a published `warning-only` on a slot with no lowering row is rejected',
|
|
1152
|
+
expect: 'reject',
|
|
1153
|
+
run: () =>
|
|
1154
|
+
run(
|
|
1155
|
+
plant('understate', (r) => r, (s) =>
|
|
1156
|
+
s.replace('| `unverified` |', '| `warning-only` |'),
|
|
1157
|
+
),
|
|
1158
|
+
),
|
|
1159
|
+
},
|
|
1160
|
+
{
|
|
1161
|
+
name: 'an enforcement value outside the closed vocabulary is rejected',
|
|
1162
|
+
expect: 'reject',
|
|
1163
|
+
run: () =>
|
|
1164
|
+
run(
|
|
1165
|
+
plant('enf-vocab', (r) => r, (s) =>
|
|
1166
|
+
s.replace('| `unverified` |', '| `advisory` |'),
|
|
1167
|
+
),
|
|
1168
|
+
),
|
|
1169
|
+
},
|
|
571
1170
|
{
|
|
572
1171
|
name: 'a cell claiming `halt-by-state`, which nothing emits, is rejected',
|
|
573
1172
|
expect: 'reject',
|