@event4u/agent-config 16.1.0 → 16.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +98 -257
- package/README.md +14 -12
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/feature/plan.md +1 -1
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +1 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/process-full.md +5 -5
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -0
- package/dist/agent-src/rules/broken-access-control.md +1 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
- package/dist/agent-src/rules/context-hygiene.md +1 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
- package/dist/agent-src/rules/design-fidelity.md +13 -16
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/domain-safety-retention.md +1 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +1 -0
- package/dist/agent-src/rules/git-history-discipline.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -2
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +14 -17
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
- package/dist/agent-src/rules/session-canary.md +8 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
- package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
- package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1862 -809
- package/dist/cli-delegate/cmd_doctor.js +13 -6
- package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
- package/dist/cli-delegate/cmd_explain.js +5 -5
- package/dist/cli-delegate/cmd_export.js +11 -8
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +10 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +11 -8
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +8 -6
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +4 -3
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +11 -8
- package/dist/cli-delegate/cmd_uninstall.js +11 -8
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +11 -8
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +5 -5
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +132 -80
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +7972 -5186
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2226 -1055
- package/dist/install/installThinLayer.js +261 -0
- package/dist/install/installedRuleLinks.js +176 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +40913 -34865
- package/dist/scripts/_lib/agent_settings.js +11 -0
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/installed_layer.js +378 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/thin_rules.js +458 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/schemas/settings.js +29 -0
- package/dist/shared/skillRanking.js +74 -13
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +10 -6
- package/package.json +4 -4
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +91 -22
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +327 -3
- package/src/config/gate-violation-baselines.json +21 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +8 -3
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +38 -3
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +99 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +312 -0
- package/src/install/installedRuleLinks.ts +220 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_doctor.ts +28 -90
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
- package/src/scripts/_lib/graph_feeder_record.ts +214 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +538 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/thin_rules.ts +540 -0
- package/src/scripts/_lib/touched_file_quality.ts +434 -0
- package/src/scripts/_lib/turn_end_refusals.ts +600 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +1 -1
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +573 -25
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +621 -22
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +138 -7
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +89 -9
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/compile_hook_manifest.ts +90 -14
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +192 -14
- package/src/scripts/hooks/block_config_weakening.ts +294 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +77 -71
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +129 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +146 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +765 -68
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +70 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +75 -189
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +108 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_rule_law_section.ts +517 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/lint_workflow_security.ts +161 -5
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_skill_ranker_confusion.ts +442 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +7 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +421 -17
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/trigger_eval_grandfather.json +0 -7
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +41 -0
- package/src/shared/skillRanking.ts +117 -11
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
- package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `probe_inputs` — what the UI-conformance probe read, in a form its reader can
|
|
3
|
+
* re-check without starting a browser.
|
|
4
|
+
*
|
|
5
|
+
* `ui-conformance.json` recorded `generated_at` and nothing about its inputs, so
|
|
6
|
+
* a probe run before the last UI edit was indistinguishable from one run after
|
|
7
|
+
* it. This module is the shared vocabulary that closes that: the probe writes an
|
|
8
|
+
* `inputs` block with a content digest per input, and the design-pass reader
|
|
9
|
+
* recomputes those digests and compares. ONE definition, imported by both sides,
|
|
10
|
+
* because a producer and a consumer with separate copies of a hash rule agree
|
|
11
|
+
* only until one of them is edited.
|
|
12
|
+
*
|
|
13
|
+
* DIGESTS, NOT MTIMES, AND THAT IS THE WHOLE DECISION (D1). The reader already
|
|
14
|
+
* applies an mtime rule to the ui-audit artefact, and reusing it here was the
|
|
15
|
+
* obvious move. It answers the wrong question: an mtime moves on a checkout, a
|
|
16
|
+
* `cp -p`-less copy, or a bare `touch`, so it would report movement on turns
|
|
17
|
+
* where no byte changed — and a reader who learns to ignore a false stale has
|
|
18
|
+
* learned to ignore a real one. A content digest reports exactly what is being
|
|
19
|
+
* asked: are the bytes the probe saw still the bytes on disk?
|
|
20
|
+
*
|
|
21
|
+
* THE TARGET DIGEST IS A TREE DIGEST, and this is the one place the roadmap's
|
|
22
|
+
* own text needed correcting. A UI surface is an entry document plus the styles
|
|
23
|
+
* and scripts beside it; a digest of `index.html` alone would be blind to a CSS
|
|
24
|
+
* edit, which is the single most common UI change there is — and the roadmap's
|
|
25
|
+
* own verify line for step 1.1 names exactly that case. So the record covers the
|
|
26
|
+
* directory the entry document sits in. It costs a small recursive read the
|
|
27
|
+
* probe does not otherwise perform (the browser, not Node, opens `styles.css`),
|
|
28
|
+
* which is a real cost the roadmap's Risk 3 mitigation did not anticipate. It is
|
|
29
|
+
* bounded: a few small text files against a run that launches Chromium and
|
|
30
|
+
* reloads the page dozens of times per node.
|
|
31
|
+
*
|
|
32
|
+
* NO BROWSER IS REACHABLE FROM HERE. This module imports `node:crypto`,
|
|
33
|
+
* `node:fs` and `node:path` and nothing else, which is what lets the hook decide
|
|
34
|
+
* staleness on a host with no browser binaries installed.
|
|
35
|
+
*/
|
|
36
|
+
import { createHash } from 'node:crypto';
|
|
37
|
+
import fs from 'node:fs';
|
|
38
|
+
import path from 'node:path';
|
|
39
|
+
|
|
40
|
+
/** Schema of the `inputs` block. A reader that does not know it says unknown. */
|
|
41
|
+
export const INPUTS_SCHEMA = 'ui-conformance-inputs/v1';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The probe build that wrote the block. Bumped when a change to the probe makes
|
|
45
|
+
* an older artefact's findings incomparable with a newer run's; a reader seeing
|
|
46
|
+
* a value it does not know reports unknown rather than guessing.
|
|
47
|
+
*/
|
|
48
|
+
export const PROBE_VERSION = 'ui-conformance-probe/v1';
|
|
49
|
+
|
|
50
|
+
/** The sibling file the probe reads for declared, approved deviations. */
|
|
51
|
+
export const DECLARATIONS_BASENAME = 'conformance.declared.json';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Bound on a tree digest. A surface directory is a handful of files; anything
|
|
55
|
+
* past this is a checkout root someone pointed the probe at by mistake, and
|
|
56
|
+
* hashing it would cost more than the probe run. Exceeding it is recorded as
|
|
57
|
+
* absent with a reason — never silently truncated, which would produce a digest
|
|
58
|
+
* that looks authoritative and covers an arbitrary subset.
|
|
59
|
+
*/
|
|
60
|
+
export const MAX_TREE_FILES = 2000;
|
|
61
|
+
|
|
62
|
+
/** Directories never worth digesting, and ruinous to walk. */
|
|
63
|
+
const SKIP_DIRS = new Set(['node_modules', '.git']);
|
|
64
|
+
|
|
65
|
+
export const INPUT_KEYS = ['target', 'reference', 'declarations'] as const;
|
|
66
|
+
export type InputKey = (typeof INPUT_KEYS)[number];
|
|
67
|
+
|
|
68
|
+
export interface InputRecord {
|
|
69
|
+
/**
|
|
70
|
+
* What was digested — a directory for `tree`, a file for `file`. `null` only
|
|
71
|
+
* when no path was supplied at all, which is itself recorded rather than
|
|
72
|
+
* omitted.
|
|
73
|
+
*/
|
|
74
|
+
path: string | null;
|
|
75
|
+
kind: 'tree' | 'file';
|
|
76
|
+
/** `absent` carries a reason and a null digest. Never an omitted key. */
|
|
77
|
+
state: 'read' | 'absent';
|
|
78
|
+
/** `sha256:<hex>` when read, `null` when absent. */
|
|
79
|
+
digest: string | null;
|
|
80
|
+
/** Files folded into a tree digest. Absent on a `file` record. */
|
|
81
|
+
files?: number;
|
|
82
|
+
reason?: string;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ProbeInputs {
|
|
86
|
+
schema: string;
|
|
87
|
+
probe: string;
|
|
88
|
+
target: InputRecord;
|
|
89
|
+
reference: InputRecord;
|
|
90
|
+
declarations: InputRecord;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const sha256 = (data: Buffer | string): string => `sha256:${createHash('sha256').update(data).digest('hex')}`;
|
|
94
|
+
|
|
95
|
+
const why = (err: unknown): string => {
|
|
96
|
+
const code = (err as NodeJS.ErrnoException | undefined)?.code;
|
|
97
|
+
return code ? String(code) : String(err);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The ONE definition of where a target's declarations file lives. The probe's
|
|
102
|
+
* CLI computed this inline; a second copy here would be a rule that agrees with
|
|
103
|
+
* the probe only until one of the two is edited.
|
|
104
|
+
*/
|
|
105
|
+
export function declarationsPathFor(targetEntryFile: string): string {
|
|
106
|
+
return path.join(path.dirname(targetEntryFile), DECLARATIONS_BASENAME);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Digest one file. Never throws — an unreadable input is a recorded fact. */
|
|
110
|
+
export function digestFile(file: string | null, whenNull = 'no path was supplied'): InputRecord {
|
|
111
|
+
if (!file) return { path: null, kind: 'file', state: 'absent', digest: null, reason: whenNull };
|
|
112
|
+
try {
|
|
113
|
+
return { path: file, kind: 'file', state: 'read', digest: sha256(fs.readFileSync(file)) };
|
|
114
|
+
} catch (err) {
|
|
115
|
+
return { path: file, kind: 'file', state: 'absent', digest: null, reason: `unreadable: ${why(err)}` };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Digest a directory as one value: every file under it, sorted by relative path,
|
|
121
|
+
* folded path-and-content into a single hash. Sorted because a directory listing
|
|
122
|
+
* order is a filesystem detail, and a digest that moved with it would report
|
|
123
|
+
* movement nobody made.
|
|
124
|
+
*
|
|
125
|
+
* `exclude` drops names recorded as inputs in their own right — the declarations
|
|
126
|
+
* file — so an edit to it moves exactly one digest rather than two.
|
|
127
|
+
*/
|
|
128
|
+
export function digestTree(
|
|
129
|
+
dir: string | null,
|
|
130
|
+
exclude: readonly string[] = [],
|
|
131
|
+
whenNull = 'no path was supplied',
|
|
132
|
+
): InputRecord {
|
|
133
|
+
if (!dir) return { path: null, kind: 'tree', state: 'absent', digest: null, reason: whenNull };
|
|
134
|
+
const skip = new Set(exclude);
|
|
135
|
+
const found: string[] = [];
|
|
136
|
+
|
|
137
|
+
const walk = (rel: string): string | null => {
|
|
138
|
+
let entries: fs.Dirent[];
|
|
139
|
+
try {
|
|
140
|
+
entries = fs.readdirSync(path.join(dir, rel), { withFileTypes: true });
|
|
141
|
+
} catch (err) {
|
|
142
|
+
return `unreadable: ${why(err)}`;
|
|
143
|
+
}
|
|
144
|
+
for (const e of [...entries].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
|
|
145
|
+
const child = rel ? `${rel}/${e.name}` : e.name;
|
|
146
|
+
if (rel === '' && skip.has(e.name)) continue;
|
|
147
|
+
if (e.isDirectory()) {
|
|
148
|
+
if (SKIP_DIRS.has(e.name)) continue;
|
|
149
|
+
const failed = walk(child);
|
|
150
|
+
if (failed) return failed;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
if (!e.isFile()) continue;
|
|
154
|
+
found.push(child);
|
|
155
|
+
if (found.length > MAX_TREE_FILES) {
|
|
156
|
+
return `directory holds more than ${MAX_TREE_FILES} files — too large to digest`;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return null;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
const failure = walk('');
|
|
163
|
+
if (failure) return { path: dir, kind: 'tree', state: 'absent', digest: null, reason: failure };
|
|
164
|
+
|
|
165
|
+
const h = createHash('sha256');
|
|
166
|
+
for (const rel of found.sort()) {
|
|
167
|
+
let body: Buffer;
|
|
168
|
+
try {
|
|
169
|
+
body = fs.readFileSync(path.join(dir, rel));
|
|
170
|
+
} catch (err) {
|
|
171
|
+
return { path: dir, kind: 'tree', state: 'absent', digest: null, reason: `unreadable: ${why(err)}` };
|
|
172
|
+
}
|
|
173
|
+
// The path is folded in alongside the bytes, so a rename moves the digest
|
|
174
|
+
// even when the content is byte-identical.
|
|
175
|
+
h.update(rel).update('\0').update(body).update('\0');
|
|
176
|
+
}
|
|
177
|
+
return {
|
|
178
|
+
path: dir,
|
|
179
|
+
kind: 'tree',
|
|
180
|
+
state: 'read',
|
|
181
|
+
digest: `sha256:${h.digest('hex')}`,
|
|
182
|
+
files: found.length,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* The block the probe writes beside `generated_at`. A target or reference is
|
|
188
|
+
* digested as the surface directory it belongs to; the declarations file is
|
|
189
|
+
* digested on its own.
|
|
190
|
+
*/
|
|
191
|
+
export function collectInputs(targetEntryFile: string | null, referenceEntryFile: string | null): ProbeInputs {
|
|
192
|
+
const dirOf = (f: string | null): string | null => (f ? path.dirname(f) : null);
|
|
193
|
+
return {
|
|
194
|
+
schema: INPUTS_SCHEMA,
|
|
195
|
+
probe: PROBE_VERSION,
|
|
196
|
+
target: digestTree(dirOf(targetEntryFile), [DECLARATIONS_BASENAME], 'no --target was supplied'),
|
|
197
|
+
reference: digestTree(
|
|
198
|
+
dirOf(referenceEntryFile),
|
|
199
|
+
[DECLARATIONS_BASENAME],
|
|
200
|
+
'no --reference was given; nothing was compared against',
|
|
201
|
+
),
|
|
202
|
+
declarations: targetEntryFile
|
|
203
|
+
? digestFile(declarationsPathFor(targetEntryFile))
|
|
204
|
+
: digestFile(null, 'no --target was supplied, so no declarations file was looked for'),
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export type InputComparison =
|
|
209
|
+
| { verdict: 'unknown'; reason: string; moved: readonly InputKey[] }
|
|
210
|
+
| { verdict: 'unchanged'; moved: readonly InputKey[] }
|
|
211
|
+
| { verdict: 'moved'; moved: readonly InputKey[] };
|
|
212
|
+
|
|
213
|
+
const isRecord = (v: unknown): v is InputRecord =>
|
|
214
|
+
typeof v === 'object' && v !== null && 'state' in (v as object) && 'kind' in (v as object);
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Re-read each recorded input and say whether it moved.
|
|
218
|
+
*
|
|
219
|
+
* THREE verdicts, and `unknown` is not a hedge. An artefact written before the
|
|
220
|
+
* `inputs` block existed has nothing to compare, and calling that `moved` would
|
|
221
|
+
* report stale for every pre-existing run on the first turn after this change —
|
|
222
|
+
* Risk 1 of the roadmap. It is a version skew, not evidence of movement, and it
|
|
223
|
+
* is equally not evidence of freshness.
|
|
224
|
+
*
|
|
225
|
+
* `resolve` maps a recorded path to this host's filesystem; the caller owns that
|
|
226
|
+
* because the probe records the path it was given and only the reader knows the
|
|
227
|
+
* root it is reading from.
|
|
228
|
+
*/
|
|
229
|
+
export function compareInputs(recorded: unknown, resolve: (p: string) => string): InputComparison {
|
|
230
|
+
if (typeof recorded !== 'object' || recorded === null) {
|
|
231
|
+
return {
|
|
232
|
+
verdict: 'unknown',
|
|
233
|
+
moved: [],
|
|
234
|
+
reason: 'the artefact carries no inputs block — it predates the input digests',
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
const r = recorded as Partial<ProbeInputs>;
|
|
238
|
+
if (r.schema !== INPUTS_SCHEMA) {
|
|
239
|
+
return {
|
|
240
|
+
verdict: 'unknown',
|
|
241
|
+
moved: [],
|
|
242
|
+
reason: `inputs schema ${String(r.schema)} is not ${INPUTS_SCHEMA} — this reader cannot compare it`,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
if (r.probe !== PROBE_VERSION) {
|
|
246
|
+
return {
|
|
247
|
+
verdict: 'unknown',
|
|
248
|
+
moved: [],
|
|
249
|
+
reason: `written by ${String(r.probe)}, read by ${PROBE_VERSION} — findings across builds are not comparable`,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const moved: InputKey[] = [];
|
|
254
|
+
for (const key of INPUT_KEYS) {
|
|
255
|
+
const rec = r[key];
|
|
256
|
+
if (!isRecord(rec)) {
|
|
257
|
+
return {
|
|
258
|
+
verdict: 'unknown',
|
|
259
|
+
moved: [],
|
|
260
|
+
reason: `the inputs block carries no usable record for ${key}`,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// An input that was never supplied has nothing to move to or from.
|
|
264
|
+
if (rec.path === null) continue;
|
|
265
|
+
const here = rec.kind === 'tree'
|
|
266
|
+
? digestTree(resolve(rec.path), [DECLARATIONS_BASENAME])
|
|
267
|
+
: digestFile(resolve(rec.path));
|
|
268
|
+
if (here.state !== rec.state || here.digest !== rec.digest) moved.push(key);
|
|
269
|
+
}
|
|
270
|
+
return moved.length ? { verdict: 'moved', moved } : { verdict: 'unchanged', moved: [] };
|
|
271
|
+
}
|
|
@@ -41,6 +41,50 @@
|
|
|
41
41
|
* over-filtering would silently stop pinning real prompts, which is worse.
|
|
42
42
|
* Measured across 1 540 user-role entries: 509 open at character zero with a
|
|
43
43
|
* marker, and in 0 of them does human text survive wrapper removal.
|
|
44
|
+
*
|
|
45
|
+
* `<agent-message` added 2026-10-03, by the same under-filtering it warns
|
|
46
|
+
* about: a subagent hand-back reaches this slot as `<agent-message from="…">`,
|
|
47
|
+
* and 12 of the 153 hook-sourced user records in this project's capture open
|
|
48
|
+
* with it. It pinned a German session to `en`, after which the turn-end gate
|
|
49
|
+
* refused each German reply — a reply language derived from a frame nobody
|
|
50
|
+
* typed. Its literal stops before the `>` because the element carries an
|
|
51
|
+
* attribute; the anchor at character zero is unchanged.
|
|
52
|
+
*
|
|
53
|
+
* **The fourth shape has NO envelope tag, and it is the exception this file's
|
|
54
|
+
* own "structural, not linguistic" rule has to name rather than quietly break.**
|
|
55
|
+
* The host resumes a rate-limited session with a plain sentence —
|
|
56
|
+
* `Your claude.ai usage limit has reset. Continue the task you were working
|
|
57
|
+
* on…` — carrying no element, no bracket and no stamp. 5 of the 153
|
|
58
|
+
* hook-sourced user records in this project's capture open with it. Measured
|
|
59
|
+
* 2026-10-03: it pinned a German session to `en` with `prompt_chars: 148`,
|
|
60
|
+
* which is that sentence's exact length, and every German reply for the rest
|
|
61
|
+
* of the run was then refused by the turn-end gate as wrong-language.
|
|
62
|
+
*
|
|
63
|
+
* Matching prose is what the paragraph above warns against, so what makes this
|
|
64
|
+
* one admissible is stated rather than assumed: it is a FIXED host string, not
|
|
65
|
+
* a linguistic pattern, and the character-zero anchor does the same work a tag
|
|
66
|
+
* would — a human asking *why did I get "Your claude.ai usage limit has
|
|
67
|
+
* reset"* does not open with it. A test pins that direction. If the host ever
|
|
68
|
+
* reworded the sentence this guard would silently stop matching, which is the
|
|
69
|
+
* honest failure mode: it under-filters back to today's behaviour rather than
|
|
70
|
+
* misfiring on a real turn.
|
|
71
|
+
*
|
|
72
|
+
* It is deliberately NOT added to `machine_wake.ts`. There the same
|
|
73
|
+
* misclassification retains an authorization instead of mis-pinning a reply,
|
|
74
|
+
* and a prose match is too weak a warrant for that consequence — see the
|
|
75
|
+
* asymmetry note below, which this is the first real test of.
|
|
76
|
+
*
|
|
77
|
+
* **A SECOND VOCABULARY ANSWERS THIS SAME QUESTION AND THE TWO HAVE DRIFTED.**
|
|
78
|
+
* `_lib/machine_wake.ts`'s `humanTypedThisTurn` guards per-turn STATE on this
|
|
79
|
+
* slot (the git-authorization ledger, the suggestion latch) while this one
|
|
80
|
+
* guards the language pin. As of this change both know `<task-notification>`,
|
|
81
|
+
* `<system-reminder>` and `<agent-message`; only this one knows
|
|
82
|
+
* `[SYSTEM NOTIFICATION` and `<local-command-caveat>`. That asymmetry is NOT
|
|
83
|
+
* obviously a defect and was deliberately left: mistaking a real turn for a
|
|
84
|
+
* machine one costs a mis-pinned reply here and a RETAINED AUTHORIZATION
|
|
85
|
+
* there, so the two sides do not want the same error budget — and
|
|
86
|
+
* `<local-command-caveat>` in particular precedes a real typed prompt. Unify
|
|
87
|
+
* them only with that asymmetry answered, never as tidying.
|
|
44
88
|
*/
|
|
45
89
|
export function isSyntheticPrompt(prompt: string): boolean {
|
|
46
90
|
const head = prompt.trimStart().slice(0, 400);
|
|
@@ -49,6 +93,8 @@ export function isSyntheticPrompt(prompt: string): boolean {
|
|
|
49
93
|
head.startsWith("<task-notification>") ||
|
|
50
94
|
head.startsWith("<system-reminder>") ||
|
|
51
95
|
head.startsWith("<local-command-caveat>") ||
|
|
96
|
+
head.startsWith("<agent-message") ||
|
|
97
|
+
head.startsWith("Your claude.ai usage limit has reset") ||
|
|
52
98
|
/^\[SYSTEM NOTIFICATION\b/.test(head)
|
|
53
99
|
);
|
|
54
100
|
}
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-surface scanner — derive the sanitize floor's coverage list from the
|
|
3
|
+
* imports, not from intent
|
|
4
|
+
* (road-to-a-sanitize-list-that-is-generated, Phase 1).
|
|
5
|
+
*
|
|
6
|
+
* WHY A SCANNER AND NOT A LIST
|
|
7
|
+
* ----------------------------
|
|
8
|
+
* `retrieval_sanitize.ts` used to carry a hand-maintained prose list of the
|
|
9
|
+
* read surfaces it covers, with its own header stating the rule for anything
|
|
10
|
+
* not on it: uncovered. That list went wrong in both directions — a path that
|
|
11
|
+
* reaches the outside world and IS sanitized was absent from it, and two paths
|
|
12
|
+
* carrying fetched bytes toward a model-facing surface were neither listed nor
|
|
13
|
+
* covered. The header already recorded the same failure happening once before,
|
|
14
|
+
* for the same reason: a list maintained by hand records what its author meant,
|
|
15
|
+
* and the tree records what the code does.
|
|
16
|
+
*
|
|
17
|
+
* This module derives the list. A row cannot disagree with the tree, because
|
|
18
|
+
* the tree is the only thing it reads.
|
|
19
|
+
*
|
|
20
|
+
* WHAT A ROW MEANS, AND WHAT IT DOES NOT
|
|
21
|
+
* --------------------------------------
|
|
22
|
+
* A row says a module matched an INBOUND shape — a recognised way outside bytes
|
|
23
|
+
* enter this process — and whether that module imports the sanitize floor. It
|
|
24
|
+
* does not say the floor is applied to the right string, at the right point, or
|
|
25
|
+
* at all: an import is evidence of intent, and only a read of the module
|
|
26
|
+
* establishes the rest. `covered` here means "the floor is in scope in this
|
|
27
|
+
* module", never "this module is safe".
|
|
28
|
+
*
|
|
29
|
+
* THE MISSING-ROW FAILURE, AND WHY `unclassified` EXISTS
|
|
30
|
+
* -----------------------------------------------------
|
|
31
|
+
* A generated table carries an authority a hand-written one never claimed, so
|
|
32
|
+
* its worst failure is not a wrong row — it is an ABSENT one. A fetch shape the
|
|
33
|
+
* scanner does not recognise produces no row, and a missing row reads as "no
|
|
34
|
+
* surface here".
|
|
35
|
+
*
|
|
36
|
+
* Two things push back. Inbound shapes are plural and named (a `fetch`, a
|
|
37
|
+
* `node:http(s)` request, a subprocess of a remote-fetching tool, a parse of
|
|
38
|
+
* markup arriving on stdin) rather than a single pattern. And a WEAK shape —
|
|
39
|
+
* a network call reached through an injected or computed callee, which the
|
|
40
|
+
* strong detectors cannot see — still emits a row, marked `unclassified`, so an
|
|
41
|
+
* unrecognised shape appears in the table as a visible gap instead of as
|
|
42
|
+
* silence. The residual is stated in the table's own header rather than left
|
|
43
|
+
* for a reader to discover: a module whose network access is reached through a
|
|
44
|
+
* shape matching neither set is still absent.
|
|
45
|
+
*/
|
|
46
|
+
import * as fs from 'node:fs';
|
|
47
|
+
import * as path from 'node:path';
|
|
48
|
+
|
|
49
|
+
/** Coverage verdict for one read surface. */
|
|
50
|
+
export type SurfaceCoverage = 'covered' | 'uncovered' | 'unclassified';
|
|
51
|
+
|
|
52
|
+
/** One module that brings outside bytes in and hands a string onward. */
|
|
53
|
+
export interface ReadSurfaceRow {
|
|
54
|
+
/** Path relative to `src/scripts/`, POSIX separators. */
|
|
55
|
+
readonly module: string;
|
|
56
|
+
/** Inbound shape id that matched. */
|
|
57
|
+
readonly inbound: string;
|
|
58
|
+
/** Emit shape id that matched, or `undecided`. */
|
|
59
|
+
readonly emit: string;
|
|
60
|
+
readonly coverage: SurfaceCoverage;
|
|
61
|
+
/** Why the row reads the way it does — one clause, no paraphrase. */
|
|
62
|
+
readonly evidence: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
interface ShapeDetector {
|
|
66
|
+
readonly id: string;
|
|
67
|
+
/** True when this module's source carries the shape. */
|
|
68
|
+
readonly match: (src: string) => boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** True when the module imports node's HTTP client at all. */
|
|
72
|
+
function _importsNodeHttp(src: string): boolean {
|
|
73
|
+
return /from\s+['"]node:https?['"]/.test(src);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Strong inbound shapes — a recognised way outside bytes enter this process.
|
|
78
|
+
*
|
|
79
|
+
* Each is deliberately narrow. `await fetch(` rather than `fetch(` keeps a
|
|
80
|
+
* fetch call named inside a string literal (a spawned one-liner, a doc example)
|
|
81
|
+
* out of the table; the node-HTTP shape requires the import as well as the call
|
|
82
|
+
* so a `.get(` on an unrelated object cannot produce a row.
|
|
83
|
+
*/
|
|
84
|
+
export const STRONG_INBOUND: readonly ShapeDetector[] = [
|
|
85
|
+
{
|
|
86
|
+
id: 'network-fetch',
|
|
87
|
+
match: (s) => /\bawait\s+fetch\s*\(/.test(s),
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'node-http-request',
|
|
91
|
+
match: (s) => _importsNodeHttp(s) && /\bhttps?\s*\.\s*(?:get|request)\s*\(/.test(s),
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
id: 'remote-subprocess',
|
|
95
|
+
match: (s) =>
|
|
96
|
+
/\b(?:spawnSync|spawn|execSync|execFileSync|execFile)\s*\(\s*\[?\s*['"`](?:curl|wget|gh)['"`]/.test(s),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'stdin-markup-parse',
|
|
100
|
+
match: (s) =>
|
|
101
|
+
/\bprocess\s*\.\s*stdin\b/.test(s) && /&(?:amp|lt|gt|quot|nbsp|#x?[0-9a-f]+);/i.test(s),
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
id: 'declared',
|
|
105
|
+
match: (s) => _DECLARATION_RE.test(s),
|
|
106
|
+
},
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* A module may declare its own inbound shape when no syntactic one captures it.
|
|
111
|
+
*
|
|
112
|
+
* The shapes above are all things the scanner can SEE. A host primitive cannot
|
|
113
|
+
* be seen: a subagent reply, an MCP tool result and a host file-read arrive
|
|
114
|
+
* through a channel this package does not own, so no call site in this tree
|
|
115
|
+
* spells them. Risk 1 of the roadmap that produced this scanner names exactly
|
|
116
|
+
* that class, and the answer it demands is a visible row rather than silence.
|
|
117
|
+
*
|
|
118
|
+
* The marker is a comment at the site — `// read-surface: <shape-id>` — not a
|
|
119
|
+
* line in a central list, which is the arrangement this whole module exists to
|
|
120
|
+
* retire. A declaration says only "outside bytes arrive here"; the coverage
|
|
121
|
+
* verdict is still read from the imports, so declaring a surface cannot make it
|
|
122
|
+
* read `covered`.
|
|
123
|
+
*/
|
|
124
|
+
const _DECLARATION_RE = /\/\/\s*read-surface:\s*([a-z0-9-]+)/;
|
|
125
|
+
|
|
126
|
+
/** `git` is deliberately NOT a remote command above. */
|
|
127
|
+
export const LOCAL_VCS_EXCLUSION =
|
|
128
|
+
'a `git` subprocess reads local repository state, not outside bytes, so it is not an ' +
|
|
129
|
+
'inbound shape. `git fetch` and `git ls-remote` move refs rather than content and are ' +
|
|
130
|
+
'excluded with the rest — a branch name is attacker-influenceable in principle, and if ' +
|
|
131
|
+
'that ever reaches a model-facing surface it needs its own row, not a widened matcher ' +
|
|
132
|
+
'that would re-add every local `git status` wrapper in the tree.';
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Weak inbound shapes — a network call the strong detectors cannot resolve.
|
|
136
|
+
*
|
|
137
|
+
* `await this._fetch(url, …)` is a real HTTP request behind an injected
|
|
138
|
+
* dependency, which is exactly the shape a strong matcher on `await fetch(`
|
|
139
|
+
* misses. It produces a row rather than silence; the verdict is
|
|
140
|
+
* `unclassified`, because the scanner cannot tell an injected fetch from an
|
|
141
|
+
* injected stub without executing it.
|
|
142
|
+
*/
|
|
143
|
+
export const WEAK_INBOUND: readonly ShapeDetector[] = [
|
|
144
|
+
{
|
|
145
|
+
id: 'indirect-fetch',
|
|
146
|
+
match: (s) => /\bawait\s+(?:this|self|[a-z_$][\w$]*)\s*\.\s*_?fetch\s*\(/i.test(s),
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
|
+
|
|
150
|
+
/** Recognised ways a module hands a string onward. */
|
|
151
|
+
export const EMIT_SHAPES: readonly ShapeDetector[] = [
|
|
152
|
+
{ id: 'file-write', match: (s) => /\b(?:writeFileSync|appendFileSync|writeFile)\s*\(/.test(s) },
|
|
153
|
+
{ id: 'stdout-write', match: (s) => /\bprocess\s*\.\s*stdout\s*\.\s*write\s*\(/.test(s) },
|
|
154
|
+
{ id: 'exported-api', match: (s) => /^export\s+(?:async\s+)?function\s/m.test(s) },
|
|
155
|
+
];
|
|
156
|
+
|
|
157
|
+
/** The sanitize floor, as an import. One hop; a transitive import is not counted. */
|
|
158
|
+
const _SANITIZE_IMPORT_RE =
|
|
159
|
+
/import\s*\{[^}]*\b(?:sanitize_text|sanitize_entry|sanitize_markup)\b[^}]*\}\s*from\s*['"][^'"]*retrieval_sanitize(?:\.js)?['"]/;
|
|
160
|
+
|
|
161
|
+
/** Test files under `src/scripts/` — fixtures, not surfaces that ship bytes. */
|
|
162
|
+
const _EXCLUDE_RE = /(?:^|\/)(?:__tests__|node_modules)\/|\.test\.ts$/;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Modules that define or read the coverage rather than being a surface.
|
|
166
|
+
*
|
|
167
|
+
* The scanner and its gate quote every detector pattern above as a literal, so
|
|
168
|
+
* they match their own matchers. Excluding them by name is the honest fix: a
|
|
169
|
+
* row for the scanner would be an artefact of the scanner reading itself, and
|
|
170
|
+
* carrying it would train a reader to skip rows.
|
|
171
|
+
*/
|
|
172
|
+
const _SELF_MODULES: ReadonlySet<string> = new Set([
|
|
173
|
+
'_lib/retrieval_sanitize.ts',
|
|
174
|
+
'_lib/read_surface_scan.ts',
|
|
175
|
+
'check_read_surface_coverage.ts',
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Raised when a directory under the scan root cannot be read.
|
|
180
|
+
*
|
|
181
|
+
* The walk used to swallow the error and return, which made an INCOMPLETE scan
|
|
182
|
+
* indistinguishable from a complete one: a permission error on one subtree
|
|
183
|
+
* silently removed every module under it from the table, and the gate then
|
|
184
|
+
* reported the shortened list as matching the tree. An independent review named
|
|
185
|
+
* it; the direction is the same one `assertScanned` enforces at the corpus
|
|
186
|
+
* level, applied to the traversal that builds the corpus.
|
|
187
|
+
*/
|
|
188
|
+
export class ScanTraversalError extends Error {
|
|
189
|
+
constructor(readonly dir: string, cause: unknown) {
|
|
190
|
+
super(`read_surface_scan: cannot read ${dir} — the scan is incomplete, not clean (${String(cause)})`);
|
|
191
|
+
this.name = 'ScanTraversalError';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Directory reader, injectable for one reason: to test the failure.
|
|
197
|
+
*
|
|
198
|
+
* The fail-closed branch below can only be exercised by a `readdirSync` that
|
|
199
|
+
* throws, and on ESM `vi.spyOn(fs, 'readdirSync')` cannot redefine the
|
|
200
|
+
* namespace property. The alternative — chmod-000 on a real directory — is not
|
|
201
|
+
* portable, because under root the mode is no barrier and the test then fails
|
|
202
|
+
* in exactly the environment it was written to survive. A narrow seam is the
|
|
203
|
+
* honest shape: production passes nothing and gets `fs.readdirSync`.
|
|
204
|
+
*/
|
|
205
|
+
export type ReadDir = (dir: string) => fs.Dirent[];
|
|
206
|
+
|
|
207
|
+
const _defaultReadDir: ReadDir = (dir) => fs.readdirSync(dir, { withFileTypes: true });
|
|
208
|
+
|
|
209
|
+
function _walk(dir: string, base: string, out: string[], readDir: ReadDir): void {
|
|
210
|
+
let entries: fs.Dirent[];
|
|
211
|
+
try {
|
|
212
|
+
entries = readDir(dir);
|
|
213
|
+
} catch (exc) {
|
|
214
|
+
throw new ScanTraversalError(dir, exc);
|
|
215
|
+
}
|
|
216
|
+
for (const e of entries) {
|
|
217
|
+
const abs = path.join(dir, e.name);
|
|
218
|
+
const rel = path.relative(base, abs).split(path.sep).join('/');
|
|
219
|
+
if (e.isDirectory()) {
|
|
220
|
+
_walk(abs, base, out, readDir);
|
|
221
|
+
} else if (e.isFile() && rel.endsWith('.ts') && !rel.endsWith('.d.ts') && !_EXCLUDE_RE.test(rel)) {
|
|
222
|
+
out.push(rel);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/** The scan root, repo-relative. Published so the gate can name it in an error. */
|
|
228
|
+
export const SCAN_ROOT = 'src/scripts';
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Scan every TypeScript module under `src/scripts/` and return one row per
|
|
232
|
+
* module that both brings outside bytes in and hands a string onward.
|
|
233
|
+
*
|
|
234
|
+
* Rows are sorted by module path so the rendered table is stable across
|
|
235
|
+
* filesystems.
|
|
236
|
+
*/
|
|
237
|
+
export function scanReadSurfaces(repoRoot: string, readDir: ReadDir = _defaultReadDir): ReadSurfaceRow[] {
|
|
238
|
+
const base = path.join(repoRoot, SCAN_ROOT);
|
|
239
|
+
const files: string[] = [];
|
|
240
|
+
_walk(base, base, files, readDir);
|
|
241
|
+
files.sort();
|
|
242
|
+
|
|
243
|
+
const rows: ReadSurfaceRow[] = [];
|
|
244
|
+
for (const rel of files) {
|
|
245
|
+
if (_SELF_MODULES.has(rel)) continue;
|
|
246
|
+
let src: string;
|
|
247
|
+
try {
|
|
248
|
+
src = fs.readFileSync(path.join(base, rel), 'utf-8');
|
|
249
|
+
} catch {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const strong = STRONG_INBOUND.find((d) => d.match(src));
|
|
253
|
+
const weak = strong === undefined ? WEAK_INBOUND.find((d) => d.match(src)) : undefined;
|
|
254
|
+
if (strong === undefined && weak === undefined) continue;
|
|
255
|
+
// A declared surface reports the shape its own marker names, so the
|
|
256
|
+
// table says `ingested-corpus` rather than the detector's own id.
|
|
257
|
+
const strongId =
|
|
258
|
+
strong?.id === 'declared' ? (_DECLARATION_RE.exec(src)?.[1] ?? 'declared') : strong?.id;
|
|
259
|
+
|
|
260
|
+
const emit = EMIT_SHAPES.find((d) => d.match(src));
|
|
261
|
+
const sanitized = _SANITIZE_IMPORT_RE.test(src);
|
|
262
|
+
|
|
263
|
+
if (weak !== undefined) {
|
|
264
|
+
rows.push({
|
|
265
|
+
module: rel,
|
|
266
|
+
inbound: weak.id,
|
|
267
|
+
emit: emit?.id ?? 'undecided',
|
|
268
|
+
coverage: 'unclassified',
|
|
269
|
+
evidence: 'network access through a callee the scanner cannot resolve — read the module',
|
|
270
|
+
});
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (emit === undefined) {
|
|
274
|
+
rows.push({
|
|
275
|
+
module: rel,
|
|
276
|
+
inbound: strongId ?? 'unknown',
|
|
277
|
+
emit: 'undecided',
|
|
278
|
+
coverage: 'unclassified',
|
|
279
|
+
evidence: 'inbound bytes with no recognised emit — the onward path is undecided',
|
|
280
|
+
});
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
rows.push({
|
|
284
|
+
module: rel,
|
|
285
|
+
inbound: strongId ?? 'unknown',
|
|
286
|
+
emit: emit.id,
|
|
287
|
+
coverage: sanitized ? 'covered' : 'uncovered',
|
|
288
|
+
evidence: sanitized
|
|
289
|
+
? 'imports the sanitize floor'
|
|
290
|
+
: 'no sanitize import — fetched bytes reach the emit unfiltered',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
return rows;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/** Counts by verdict, for the gate's scanned line and for the table header. */
|
|
297
|
+
export function tally(rows: readonly ReadSurfaceRow[]): Record<SurfaceCoverage, number> {
|
|
298
|
+
const out: Record<SurfaceCoverage, number> = { covered: 0, uncovered: 0, unclassified: 0 };
|
|
299
|
+
for (const r of rows) out[r.coverage] += 1;
|
|
300
|
+
return out;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/** Render the rows as the markdown table body committed to the contract doc. */
|
|
304
|
+
export function renderTable(rows: readonly ReadSurfaceRow[]): string {
|
|
305
|
+
const lines = [
|
|
306
|
+
'| module (under `src/scripts/`) | inbound shape | emit shape | coverage | evidence |',
|
|
307
|
+
'|---|---|---|---|---|',
|
|
308
|
+
];
|
|
309
|
+
for (const r of rows) {
|
|
310
|
+
lines.push(
|
|
311
|
+
`| \`${r.module}\` | \`${r.inbound}\` | \`${r.emit}\` | **${r.coverage}** | ${r.evidence} |`,
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
const t = tally(rows);
|
|
315
|
+
lines.push('');
|
|
316
|
+
lines.push(
|
|
317
|
+
`${String(rows.length)} read surface(s): ` +
|
|
318
|
+
`${String(t.covered)} covered, ${String(t.uncovered)} uncovered, ` +
|
|
319
|
+
`${String(t.unclassified)} unclassified.`,
|
|
320
|
+
);
|
|
321
|
+
return lines.join('\n');
|
|
322
|
+
}
|
|
@@ -574,7 +574,8 @@ export function section_publication_blockers(
|
|
|
574
574
|
'fails, so a missing line means the collection failed during `task release`. ' +
|
|
575
575
|
'Fix the collection, then render the number and put it on the section — ' +
|
|
576
576
|
`the release commit already exists on ${where}, so amend it there. Do not ` +
|
|
577
|
-
'invent the count: `npx vitest list |
|
|
577
|
+
'invent the count: `npx vitest list --staticParse=false | grep -c .` is the same ' +
|
|
578
|
+
'probe the writer runs.',
|
|
578
579
|
);
|
|
579
580
|
}
|
|
580
581
|
return out;
|