@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
|
@@ -15,45 +15,74 @@
|
|
|
15
15
|
*
|
|
16
16
|
* What is observed, and what is a shape guess — the line is drawn explicitly.
|
|
17
17
|
*
|
|
18
|
-
* `STRUCTURED_ASK_SHAPES`
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
18
|
+
* `STRUCTURED_ASK_SHAPES` carries **one** row, for `claude`, written from an
|
|
19
|
+
* observation under `host-capability-manifest.md` § Observation protocol —
|
|
20
|
+
* 48 `AskUserQuestion` `tool_use` blocks across host versions 2.1.252 to
|
|
21
|
+
* 2.1.284, 2026-09-01 to 2026-10-02. Artifact:
|
|
22
|
+
* `agents/evidence/analysis/structured-ask-host-observation-2026-10.md`. code-comment-allow provenance-comment -- the observation protocol declares a row inadmissible without its artifact citation, so this pointer is part of the contract the row satisfies, not evidence duplicated from a roadmap
|
|
23
|
+
* Every other host has no row, which is "never looked", and inventing one from
|
|
24
|
+
* a vendor's documentation is exactly what that protocol forbids.
|
|
22
25
|
*
|
|
23
|
-
* `STRUCTURED_ASK_TOOL_NAME_RE` is
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
26
|
+
* `STRUCTURED_ASK_TOOL_NAME_RE` is a **name-shape pattern, not a host fact**,
|
|
27
|
+
* and stays one even now that a host fact exists beside it: the pattern is what
|
|
28
|
+
* every caller passing no host id still matches on. It exists so that a picker
|
|
29
|
+
* appearing in a transcript is MEASURED rather than missed. A reader must not
|
|
30
|
+
* read a match as evidence that a named host ships that tool; the only thing
|
|
31
|
+
* that establishes that is an observation written into
|
|
29
32
|
* `STRUCTURED_ASK_SHAPES` with the protocol's four-part citation.
|
|
30
33
|
*
|
|
31
|
-
* Consequence, stated rather than discovered later: `native` is
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
+
* Consequence, stated rather than discovered later: `native` is **0** on every
|
|
35
|
+
* corpus this repo can scan today — and the 2026-10 observation does NOT change
|
|
36
|
+
* that, which is worth stating because it looks like it should.
|
|
37
|
+
* `probe_unblocked_ask` partitions hand-back ask turns, and a picker call is
|
|
38
|
+
* answered by a `tool_result` rather than by a free user turn, so it was never
|
|
39
|
+
* in that probe's denominator. The zero means "no prose hand-back used a picker
|
|
40
|
+
* instead"; it never meant "no picker exists".
|
|
41
|
+
*
|
|
42
|
+
* That makes the zero DEFINITIONAL for this probe, not a measurement of
|
|
43
|
+
* scarcity, and the earlier wording claimed the opposite on both halves: it
|
|
44
|
+
* called the zero a measurement, and attributed it to the capability being
|
|
45
|
+
* unobserved. The capability is observed and the zero is unchanged, which is
|
|
46
|
+
* the cleanest available proof that the two were never connected.
|
|
34
47
|
*/
|
|
35
48
|
|
|
36
49
|
/** The per-host shape of a structured-ask tool, once one has been OBSERVED. */
|
|
37
50
|
export interface StructuredAskShape {
|
|
38
51
|
/** The tool name as it appears in a transcript's `tool_use` block. */
|
|
39
52
|
readonly tool: string;
|
|
40
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* The largest question count OBSERVED in one call, never a host ceiling the
|
|
55
|
+
* host was asked for and refused. `1` is also the shape this repo wants.
|
|
56
|
+
*/
|
|
41
57
|
readonly max_questions: number;
|
|
42
|
-
/**
|
|
58
|
+
/** The largest option count OBSERVED on one question — same caveat. */
|
|
43
59
|
readonly max_options_per_question: number;
|
|
44
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* Whether the host's picker admits a free-text answer alongside the options.
|
|
62
|
+
* The WEAKEST of the four to establish, because it is read by negation — an
|
|
63
|
+
* answer carrying none of the offered labels. A cancelled ask looks the same
|
|
64
|
+
* from the outside, so a `true` here is "observed at least once", never a
|
|
65
|
+
* count. The citing artifact carries the per-row basis.
|
|
66
|
+
*/
|
|
45
67
|
readonly free_text: boolean;
|
|
46
68
|
}
|
|
47
69
|
|
|
48
70
|
/**
|
|
49
71
|
* Observed per-host structured-ask shapes.
|
|
50
72
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
73
|
+
* A row is written only from an observation in a real session under
|
|
74
|
+
* `contexts/execution/host-capability-manifest.md` § Observation protocol —
|
|
75
|
+
* host · host version · transcript reference · date. Never from a host's
|
|
76
|
+
* documentation, and never by analogy to another host.
|
|
77
|
+
*
|
|
78
|
+
* `claude`'s row is the first, and the two numeric fields are **observed
|
|
79
|
+
* maxima over 48 calls, not host ceilings**: no call carried two questions, so
|
|
80
|
+
* the host was never asked to accept two. The guard's threshold does not rest
|
|
81
|
+
* on them — one question per call is this repo's own rule either way.
|
|
55
82
|
*/
|
|
56
|
-
export const STRUCTURED_ASK_SHAPES: Readonly<Record<string, StructuredAskShape>> = {
|
|
83
|
+
export const STRUCTURED_ASK_SHAPES: Readonly<Record<string, StructuredAskShape>> = {
|
|
84
|
+
claude: { tool: 'AskUserQuestion', max_questions: 1, max_options_per_question: 4, free_text: true },
|
|
85
|
+
};
|
|
57
86
|
|
|
58
87
|
/** The observed shape for `hostId`, or `undefined` when none has been observed. */
|
|
59
88
|
export function structuredAskShape(
|
|
@@ -77,6 +106,20 @@ export const STRUCTURED_ASK_TOOL_NAME_RE = /^(ask[_-]?user[_-]?question|user[_-]
|
|
|
77
106
|
* An observed per-host shape wins outright — that is a fact about the host. The
|
|
78
107
|
* name-shape pattern is the fallback, and it is why the probe can report a
|
|
79
108
|
* measured zero instead of nothing at all.
|
|
109
|
+
*
|
|
110
|
+
* KNOWN ASYMMETRY, named rather than left for the next reader to hit. With a
|
|
111
|
+
* row present, this returns `name === shape.tool`: ONE observed name becomes an
|
|
112
|
+
* exclusive allowlist, so every other picker name is answered `false` on that
|
|
113
|
+
* host — a universal negative from a single positive observation, which is the
|
|
114
|
+
* shape of inference the observation protocol refuses elsewhere. It is left as
|
|
115
|
+
* it is because the alternative (union of the row and the pattern) would make
|
|
116
|
+
* an observed row buy nothing, and because no caller is affected today. There
|
|
117
|
+
* are TWO production callers, both host-blind, enumerated rather than recalled:
|
|
118
|
+
* `hooks/one_question_per_ask_hook.ts:103` and `probe_unblocked_ask.ts:184`,
|
|
119
|
+
* each calling `isStructuredAskTool(name)` with no second argument. A caller
|
|
120
|
+
* that does start passing one inherits the asymmetry, and
|
|
121
|
+
* `tests/scripts/ask_surface.test.ts` pins it so the inheritance is visible
|
|
122
|
+
* rather than silent.
|
|
80
123
|
*/
|
|
81
124
|
export function isStructuredAskTool(name: string, hostId?: string | null): boolean {
|
|
82
125
|
const shape = structuredAskShape(hostId);
|
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The thin-projection MECHANISM, with no CLI entry of its own.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `src/scripts/project_thin_rules.ts` for
|
|
5
|
+
* `road-to-an-installed-layer-that-is-thinned` step 1.1, which needs the
|
|
6
|
+
* INSTALLER to apply the projector's own predicate to `~/.claude/rules`.
|
|
7
|
+
*
|
|
8
|
+
* THE EXTRACTION IS THE POINT, NOT TIDYING. `src/scripts/install.ts` is the
|
|
9
|
+
* `build:install-bundle` entry, and `check_installer_import_purity` fails the
|
|
10
|
+
* build when any module reachable from it carries a module-level
|
|
11
|
+
* `process.exit()` — including the guarded `if (isMain) process.exit(main())`
|
|
12
|
+
* shape, which that gate's own test names as "the real incident shape".
|
|
13
|
+
* `project_thin_rules.ts` carries exactly that at its foot, so the installer
|
|
14
|
+
* could not import it. The two live alternatives were both worse: a second
|
|
15
|
+
* implementation of the stub form inside `src/install/` is the duplicate-reader
|
|
16
|
+
* defect this repository has already paid for twice (the `lean_projection.mode`
|
|
17
|
+
* header records the latest), and moving the CLI instead would break every
|
|
18
|
+
* caller of `project_thin_rules --measure`.
|
|
19
|
+
*
|
|
20
|
+
* So the mechanism moves and the CLI stays. `project_thin_rules.ts` re-exports
|
|
21
|
+
* every name below, so no existing importer changes.
|
|
22
|
+
*
|
|
23
|
+
* Nothing here runs on import, nothing here writes, and nothing here calls
|
|
24
|
+
* `process.exit`. `measure`, `write_thin`, the JSON dumper and the CLI remain in
|
|
25
|
+
* `project_thin_rules.ts`.
|
|
26
|
+
*/
|
|
27
|
+
import { createHash } from 'node:crypto';
|
|
28
|
+
import * as fs from 'node:fs';
|
|
29
|
+
import * as path from 'node:path';
|
|
30
|
+
import { fileURLToPath } from 'node:url';
|
|
31
|
+
|
|
32
|
+
import { lawText, ruleBody } from './rule_law_section.js';
|
|
33
|
+
import {
|
|
34
|
+
readConsequenceClass,
|
|
35
|
+
stubLawIds,
|
|
36
|
+
type ConsequenceClassConfig,
|
|
37
|
+
} from './rule_consequence_class.js';
|
|
38
|
+
import {
|
|
39
|
+
pathOnlyRuleIds,
|
|
40
|
+
triggerlessRuleIds,
|
|
41
|
+
type Router,
|
|
42
|
+
} from './rule_injection.js';
|
|
43
|
+
|
|
44
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
45
|
+
/**
|
|
46
|
+
* The package root.
|
|
47
|
+
*
|
|
48
|
+
* FOUR levels up, not three: this module sits at `src/scripts/_lib/`, one
|
|
49
|
+
* directory deeper than the `src/scripts/<file>` the arithmetic was written for.
|
|
50
|
+
* Keeping the old `'..','..'` here would have resolved to `src/` and every
|
|
51
|
+
* default root below would have been silently wrong — the extraction's one
|
|
52
|
+
* genuine hazard, so it is stated rather than left to be noticed.
|
|
53
|
+
*
|
|
54
|
+
* Correct for a module READ FROM SOURCE. A bundled caller must hand its own
|
|
55
|
+
* package root over via {@link ThinRoots}; see that interface for why.
|
|
56
|
+
*/
|
|
57
|
+
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..', '..');
|
|
58
|
+
|
|
59
|
+
export const RULES_SOURCE = path.join(REPO_ROOT, 'dist/agent-src', 'rules');
|
|
60
|
+
export const ROUTER = path.join(REPO_ROOT, 'dist', 'router.json');
|
|
61
|
+
|
|
62
|
+
/** `sorted(dir.glob("*.md"))` — non-recursive, lexically sorted abs paths. */
|
|
63
|
+
export function _globSortedMd(dir: string): string[] {
|
|
64
|
+
let names: string[];
|
|
65
|
+
try {
|
|
66
|
+
names = fs.readdirSync(dir);
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const out = names.filter((n) => n.endsWith('.md')).map((n) => path.join(dir, n));
|
|
71
|
+
out.sort();
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The always-full-bodied set — authoritative kernel list from the router. */
|
|
76
|
+
export function kernel_ids(routerPath: string = ROUTER): Set<string> {
|
|
77
|
+
const data = JSON.parse(fs.readFileSync(routerPath, 'utf-8')) as Record<string, unknown>;
|
|
78
|
+
const kernel = data.kernel;
|
|
79
|
+
if (Array.isArray(kernel)) {
|
|
80
|
+
return new Set(kernel.map((x) => String(x)));
|
|
81
|
+
}
|
|
82
|
+
return new Set();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Tier rules the router gives NO trigger at all.
|
|
87
|
+
*
|
|
88
|
+
* These cannot be delivered on a match, so thinning them would remove a body
|
|
89
|
+
* that nothing can ever put back — a silent hole rather than a saving. They
|
|
90
|
+
* project full-bodied, and `measure` reports them by name so the residue is
|
|
91
|
+
* visible instead of being absorbed into the pointer count
|
|
92
|
+
* (road-to-trigger-delivered-rule-bodies 1.3).
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* Rules whose ONLY triggers are path-shaped — reachable from `pre_tool_use`
|
|
96
|
+
* and from no other slot (road-to-delivery-for-every-host 3.2).
|
|
97
|
+
*
|
|
98
|
+
* They must never be thinned while the delivery concern is NOT bound on
|
|
99
|
+
* `pre_tool_use`, because a stub plus a slot nobody listens on is a rule that
|
|
100
|
+
* reaches the model at no scope at all. This is the same failure the
|
|
101
|
+
* `no_trigger_ids` exemption prevents, one step out: there the rule has nothing
|
|
102
|
+
* to match on, here it has nothing to match ON.
|
|
103
|
+
*
|
|
104
|
+
* Stated as a PROPERTY rather than a list of ids. If the concern is ever bound
|
|
105
|
+
* on `pre_tool_use` again, this exemption is what should be reconsidered — not
|
|
106
|
+
* three names someone has to remember.
|
|
107
|
+
*
|
|
108
|
+
* WHAT THIS EXEMPTION DOES **NOT** COVER — recorded 2026-09-08, R2 finding 1,
|
|
109
|
+
* because the silence read as coverage. The predicate is `every` trigger
|
|
110
|
+
* path-shaped. A rule with BOTH path and non-path triggers is therefore THINNED,
|
|
111
|
+
* and its path-shaped half then has no carrier at all: the stub carries no
|
|
112
|
+
* frontmatter (`condense.ts` writes it with `_writeText`, bypassing
|
|
113
|
+
* `_emit_claude_rule`'s host-native `paths:` key), `pre_tool_use` lost the
|
|
114
|
+
* binding under owner ruling E2, and `user_prompt_submit` never populates
|
|
115
|
+
* `openFiles`. Measured over the current `dist/router.json`: **18 such rules**,
|
|
116
|
+
* all non-kernel and all thinned — augment-edit-discipline, design-fidelity,
|
|
117
|
+
* doc-screenshot-hygiene, domain-adoption-policy,
|
|
118
|
+
* framework-neutrality-in-generic-skills, image-likeness-and-rights,
|
|
119
|
+
* laravel-translations, lethal-trifecta-guard, linked-projects-onboarding-gate,
|
|
120
|
+
* low-impact-corpus-privacy-floor, markdown-safe-codeblocks, onboarding-gate,
|
|
121
|
+
* persona-governance, php-coding, provider-lifecycle-discipline,
|
|
122
|
+
* roadmap-ci-steps-policy, roadmap-progress-sync, settings-ask-protocol.
|
|
123
|
+
*
|
|
124
|
+
* They do NOT reach the model at no scope: every non-path route
|
|
125
|
+
* (keyword / phrase / command) still delivers the body through the concern, and
|
|
126
|
+
* on `claude-code` they never had a separate host-native path route to lose —
|
|
127
|
+
* `_claude_paths_plan` emits no `paths:` for a mixed-trigger rule on purpose, so
|
|
128
|
+
* under `eager-all` they loaded UNCONDITIONALLY. What is lost is the difference
|
|
129
|
+
* between unconditional and prompt-triggered: a session that touches a matching
|
|
130
|
+
* file and says nothing that matches gets nothing.
|
|
131
|
+
*
|
|
132
|
+
* The labelled corpus cannot see this and the endpoint says so rather than
|
|
133
|
+
* implying otherwise — `model_rule_injection --endpoints` (b) now publishes the
|
|
134
|
+
* shipped `user_prompt_submit` reach beside the pre-registered reading, and
|
|
135
|
+
* every one of these 18 has at least one prompt-matching positive, so none
|
|
136
|
+
* appears in its "reachable only via a path trigger" column.
|
|
137
|
+
*
|
|
138
|
+
* WHY THIS IS NOT WIDENED TO `some` HERE. Measured: exempting all 18 moves the
|
|
139
|
+
* thin rule layer 23,592 -> 39,921 GPT tok (+16,329, +69 %), which is a budget
|
|
140
|
+
* move of a magnitude no agent may take, and it would erase most of the saving
|
|
141
|
+
* the flip is licensed on. Restoring a path route needs either that exemption
|
|
142
|
+
* under a re-anchored baseline or a `pre_tool_use` binding under a raised slot
|
|
143
|
+
* cap — both owner-reserved, so the closure is tracked rather than taken here.
|
|
144
|
+
* ADR-267's Consequences section names the receiver.
|
|
145
|
+
*/
|
|
146
|
+
/**
|
|
147
|
+
* ONE spelling of the router-derived sets, shared with the injector (R2
|
|
148
|
+
* finding 11).
|
|
149
|
+
*
|
|
150
|
+
* `path_only_ids` and `no_trigger_ids` were each a second, hand-rolled
|
|
151
|
+
* implementation of `_lib/rule_injection.ts::pathOnlyRuleIds` /
|
|
152
|
+
* `triggerlessRuleIds` — verified identical over the current router at the time
|
|
153
|
+
* (symmetric difference empty), so drift risk rather than a live defect. It is
|
|
154
|
+
* exactly the hazard `THIN_ENTRY_MARKER`'s own comment argues against one
|
|
155
|
+
* screen below ("a gate that re-spelled this string would drift from the writer
|
|
156
|
+
* silently"), and the two consumers here are the PROJECTOR's exemption set and
|
|
157
|
+
* the INJECTOR's reachability set: they must not be able to disagree about which
|
|
158
|
+
* rules the delivery path can reach.
|
|
159
|
+
*
|
|
160
|
+
* The finding named `path_only_ids`. The defect-pattern sweep found the SECOND
|
|
161
|
+
* instance one function down — `no_trigger_ids` against `triggerlessRuleIds` —
|
|
162
|
+
* and it is delegated in the same change. Count: 2 of 2 duplicated
|
|
163
|
+
* router-derived sets in this file, 0 remaining.
|
|
164
|
+
*/
|
|
165
|
+
function _router(routerPath: string = ROUTER): Router {
|
|
166
|
+
return JSON.parse(fs.readFileSync(routerPath, 'utf-8')) as Router;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export function path_only_ids(routerPath: string = ROUTER): Set<string> {
|
|
170
|
+
return pathOnlyRuleIds(_router(routerPath));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function no_trigger_ids(routerPath: string = ROUTER): Set<string> {
|
|
174
|
+
return new Set(triggerlessRuleIds(_router(routerPath)));
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* id → workspaces for every non-kernel router entry (router.json schema v2,
|
|
179
|
+
* road-to-request-scoped-rule-load Phase 1). Rules absent from the map (or
|
|
180
|
+
* with an empty list) fail safe: they stay in scope.
|
|
181
|
+
*/
|
|
182
|
+
export function rule_workspaces_map(routerPath: string = ROUTER): Map<string, string[]> {
|
|
183
|
+
const data = JSON.parse(fs.readFileSync(routerPath, 'utf-8')) as Record<string, unknown>;
|
|
184
|
+
const map = new Map<string, string[]>();
|
|
185
|
+
for (const tier of ['tier_1', 'tier_2']) {
|
|
186
|
+
const entries = data[tier];
|
|
187
|
+
if (!Array.isArray(entries)) continue;
|
|
188
|
+
for (const e of entries) {
|
|
189
|
+
const obj = e as Record<string, unknown>;
|
|
190
|
+
const ws = Array.isArray(obj.workspaces) ? obj.workspaces.map((w) => String(w)) : [];
|
|
191
|
+
map.set(String(obj.id), ws);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return map;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether a rule id survives a workspace scope. Kernel always survives;
|
|
199
|
+
* `scope === null` = legacy-all. An out-of-scope non-kernel rule is dropped
|
|
200
|
+
* entirely — neither body nor pointer line (the pointer floor shrinks with
|
|
201
|
+
* consumer scoping).
|
|
202
|
+
*/
|
|
203
|
+
export function id_in_scope(
|
|
204
|
+
rule_id: string,
|
|
205
|
+
scope: readonly string[] | null,
|
|
206
|
+
kernel: ReadonlySet<string>,
|
|
207
|
+
wsMap: ReadonlyMap<string, string[]>,
|
|
208
|
+
fallback_ws: readonly string[] = [],
|
|
209
|
+
): boolean {
|
|
210
|
+
if (scope === null || kernel.has(rule_id)) {
|
|
211
|
+
return true;
|
|
212
|
+
}
|
|
213
|
+
// Router map first (schema v2); `type: manual` rules are reference-only
|
|
214
|
+
// and never emitted to the router — their frontmatter is the fallback.
|
|
215
|
+
const ws = wsMap.get(rule_id) ?? fallback_ws;
|
|
216
|
+
if (ws.length === 0) {
|
|
217
|
+
return true; // untagged / unknown → fail safe
|
|
218
|
+
}
|
|
219
|
+
return ws.some((w) => scope.includes(w));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** `workspaces:` list parsed straight from a rule file's frontmatter. */
|
|
223
|
+
export function fm_workspaces(text: string): string[] {
|
|
224
|
+
const [fm] = split_frontmatter(text);
|
|
225
|
+
// Flow style: `workspaces: [a, b, c]`
|
|
226
|
+
const flow = /^workspaces:[ \t]*\[([^\]]*)\]/m.exec(fm);
|
|
227
|
+
if (flow) {
|
|
228
|
+
return (flow[1] as string)
|
|
229
|
+
.split(',')
|
|
230
|
+
.map((s) => s.trim().replace(/^["']|["']$/g, ''))
|
|
231
|
+
.filter((s) => s.length > 0);
|
|
232
|
+
}
|
|
233
|
+
// Block style; tolerate the list being the LAST frontmatter key.
|
|
234
|
+
const m = /^workspaces:[ \t]*\n((?:[ \t]+-[ \t]+.*(?:\n|$))+)/m.exec(fm);
|
|
235
|
+
if (!m) {
|
|
236
|
+
return [];
|
|
237
|
+
}
|
|
238
|
+
return [...(m[1] as string).matchAll(/-[ \t]+(\S+)/g)].map((x) => x[1] as string);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Return [frontmatter_including_fences, body]. Empty fm if none. */
|
|
242
|
+
export function split_frontmatter(text: string): [string, string] {
|
|
243
|
+
if (text.startsWith('---\n')) {
|
|
244
|
+
const end = text.indexOf('\n---\n', 4);
|
|
245
|
+
if (end !== -1) {
|
|
246
|
+
return [text.slice(0, end + 5), text.slice(end + 5)];
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return ['', text];
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function _description(fm: string): string {
|
|
253
|
+
// re.search(r'^description:\s*"?(.+?)"?\s*$', fm, re.MULTILINE)
|
|
254
|
+
const m = /^description:\s*"?(.+?)"?\s*$/m.exec(fm);
|
|
255
|
+
return m ? (m[1] as string).trim() : '';
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// How many trigger keywords/phrases to surface as the always-on match hint.
|
|
259
|
+
const _TRIGGER_HINT_LIMIT = 6;
|
|
260
|
+
|
|
261
|
+
/** A short, comma-joined sample of the rule's trigger keywords/phrases. */
|
|
262
|
+
function _trigger_hint(fm: string): string {
|
|
263
|
+
const hits: string[] = [];
|
|
264
|
+
// re.finditer(r'^\s*-\s*(?:keyword|phrase):\s*"?(.+?)"?\s*$', fm, re.MULTILINE)
|
|
265
|
+
const re = /^\s*-\s*(?:keyword|phrase):\s*"?(.+?)"?\s*$/gm;
|
|
266
|
+
let m: RegExpExecArray | null;
|
|
267
|
+
while ((m = re.exec(fm)) !== null) {
|
|
268
|
+
hits.push((m[1] as string).trim());
|
|
269
|
+
if (hits.length >= _TRIGGER_HINT_LIMIT) {
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return hits.join(', ');
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/** Python `str.title()` — uppercase first alpha of each run, rest lowercase. */
|
|
277
|
+
function _title(s: string): string {
|
|
278
|
+
return s.replace(/[A-Za-z]+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The marker that makes a projected entry recognisable as a stub.
|
|
283
|
+
*
|
|
284
|
+
* Exported so the WRITER and every DETECTOR share one definition. A gate that
|
|
285
|
+
* re-spelled this string would drift from the writer silently, and the failure
|
|
286
|
+
* would be invisible in exactly the direction that matters: a stub the gate
|
|
287
|
+
* fails to recognise reads as a complete rule body.
|
|
288
|
+
*/
|
|
289
|
+
export const THIN_ENTRY_MARKER = '> Routed rule — load the body on trigger-match.';
|
|
290
|
+
|
|
291
|
+
/** Is this projected entry a pointer stub rather than a rule body? */
|
|
292
|
+
export function is_thin_entry(text: string): boolean {
|
|
293
|
+
return text.includes(THIN_ENTRY_MARKER);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Where a stub tells a reader the body actually is.
|
|
298
|
+
*
|
|
299
|
+
* FIXED 2026-09-07: this pointed into the uncondensed source tree ADR-051
|
|
300
|
+
* retired — the literal path is deliberately NOT reproduced here, because
|
|
301
|
+
* `check_no_uncondensed_refs` matches the string and cannot tell a repair
|
|
302
|
+
* record from a live reference; `git log -S` on this file finds the old value.
|
|
303
|
+
* It exists in no checkout, so EVERY stub's fallback pointer
|
|
304
|
+
* resolved to nothing — invisible under `delivery`, where the hook loads the
|
|
305
|
+
* body from `dist/agent-src/rules` and never follows this link, and total under
|
|
306
|
+
* `thin`, where the pointer is the only path to the body there is.
|
|
307
|
+
*
|
|
308
|
+
* KNOWN LIMIT, stated rather than discovered later: one relative prefix cannot
|
|
309
|
+
* be right for every tree, because they sit at different depths — `.claude/rules`
|
|
310
|
+
* and `.cursor/rules` are two levels below the repo root and `.clinerules` is
|
|
311
|
+
* one. `../../` is correct for the first two, which are the only trees the
|
|
312
|
+
* shipped `lean_projection.hosts` default can thin. A stub in `.clinerules`
|
|
313
|
+
* needs an operator to have added `cline` to that list, and its pointer is off
|
|
314
|
+
* by one level. That is strictly better than the previous state, where the
|
|
315
|
+
* pointer was dead in all three, and it is recorded here so the remaining case
|
|
316
|
+
* is a known limit rather than a surprise.
|
|
317
|
+
*/
|
|
318
|
+
const BODY_LINK_PREFIX = '../../dist/agent-src/rules/';
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* The absolute body-link prefix for a tree whose rules were installed OUT of the
|
|
322
|
+
* repository — `~/.claude/rules`, where no relative prefix can be right.
|
|
323
|
+
*
|
|
324
|
+
* The limit documented on {@link BODY_LINK_PREFIX} is a statement about trees at
|
|
325
|
+
* a KNOWN depth below a repo root. An installed layer has no such relation to
|
|
326
|
+
* the package at all: `~/.claude/rules/x.md` and `<pkg>/dist/agent-src/rules/x.md`
|
|
327
|
+
* share no common ancestor the installer may assume, so the only prefix that can
|
|
328
|
+
* resolve is one rooted at the package the install was made from. The installer
|
|
329
|
+
* knows that path (`package_root`), which is why this takes it rather than
|
|
330
|
+
* deriving it from `import.meta.url` — inside a bundled CLI that arithmetic
|
|
331
|
+
* yields the parent of the package, the same miss `lean_projection_mode`'s header
|
|
332
|
+
* records for the template read.
|
|
333
|
+
*
|
|
334
|
+
* KNOWN LIMIT, stated rather than found later: the pointer is as durable as the
|
|
335
|
+
* package directory it names. A global `npm i -g` install is durable; an `npx`
|
|
336
|
+
* run is not, and a stub written from an ephemeral package root points at a path
|
|
337
|
+
* that will not exist afterwards. That is strictly better than the project-
|
|
338
|
+
* relative prefix, which resolves from an installed layer NEVER, and under
|
|
339
|
+
* `delivery` the hook loads the body and does not follow this link at all.
|
|
340
|
+
*/
|
|
341
|
+
export function absoluteBodyLinkPrefix(packageRoot: string): string {
|
|
342
|
+
return `${path.join(path.resolve(packageRoot), 'dist', 'agent-src', 'rules')}${path.sep}`;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** Build the minimal progressive-disclosure pointer for a non-kernel rule. */
|
|
346
|
+
export function thin_entry(
|
|
347
|
+
rule_id: string,
|
|
348
|
+
text: string,
|
|
349
|
+
bodyLinkPrefix: string = BODY_LINK_PREFIX,
|
|
350
|
+
): string {
|
|
351
|
+
const [fm] = split_frontmatter(text);
|
|
352
|
+
const desc = _description(fm);
|
|
353
|
+
const hint = _trigger_hint(fm);
|
|
354
|
+
const title = _title(rule_id.replace(/-/g, ' '));
|
|
355
|
+
const fires = hint ? ` Fires on: ${hint}.` : '';
|
|
356
|
+
return (
|
|
357
|
+
`## ${title}\n` +
|
|
358
|
+
`${THIN_ENTRY_MARKER}${fires} ${desc} ` +
|
|
359
|
+
`Body: [\`${rule_id}\`](${bodyLinkPrefix}${rule_id}.md)\n`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The marker that opens a stub's copied law block, and the one that closes it
|
|
365
|
+
* with the digest of what was copied.
|
|
366
|
+
*
|
|
367
|
+
* Exported for the same reason {@link THIN_ENTRY_MARKER} is: a detector that
|
|
368
|
+
* re-spelled either string would drift from the writer silently, and the
|
|
369
|
+
* failure would read as "this stub carries no law" — the one direction that
|
|
370
|
+
* matters.
|
|
371
|
+
*/
|
|
372
|
+
export const STUB_LAW_OPEN = '<!-- law: byte-copied from the rule, sha256 ';
|
|
373
|
+
export const STUB_LAW_CLOSE = '<!-- /law -->';
|
|
374
|
+
|
|
375
|
+
/** Short digest of a law text — the same function the stub writer and any reader use. */
|
|
376
|
+
export function lawDigest(law: string): string {
|
|
377
|
+
return createHash('sha256').update(law, 'utf-8').digest('hex').slice(0, 16);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Raised when a class member cannot carry its law and has not said so.
|
|
382
|
+
*
|
|
383
|
+
* The roadmap's own words: a class member whose law section is missing or over
|
|
384
|
+
* the ceiling FAILS the projection rather than shipping a shortened copy. The
|
|
385
|
+
* declared `no_stub` subset is the escape, and it costs a reason in the diff —
|
|
386
|
+
* which is the whole difference between an exception and a silent shrink.
|
|
387
|
+
*/
|
|
388
|
+
export class StubLawError extends Error {
|
|
389
|
+
readonly ruleId: string;
|
|
390
|
+
|
|
391
|
+
constructor(ruleId: string, message: string) {
|
|
392
|
+
super(message);
|
|
393
|
+
this.name = 'StubLawError';
|
|
394
|
+
this.ruleId = ruleId;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Characters of law a stub may carry — the hard ceiling `lint_rule_law_section` holds. */
|
|
399
|
+
export const STUB_LAW_MAX_CHARS = 2_000;
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* A stub that carries the rule's own law, byte for byte.
|
|
403
|
+
*
|
|
404
|
+
* The council rejected a compiled or summarised contract: a second
|
|
405
|
+
* authoritative representation of a rule is a new trust boundary whose
|
|
406
|
+
* omissions read as permission. So nothing here rewrites, truncates or
|
|
407
|
+
* reformats — the law text is `slice`d out of the source body and emitted
|
|
408
|
+
* verbatim, and the digest beside it is what makes "verbatim" checkable rather
|
|
409
|
+
* than asserted.
|
|
410
|
+
*/
|
|
411
|
+
export function thin_entry_with_law(
|
|
412
|
+
rule_id: string,
|
|
413
|
+
text: string,
|
|
414
|
+
bodyLinkPrefix: string = BODY_LINK_PREFIX,
|
|
415
|
+
): string {
|
|
416
|
+
const law = lawText(ruleBody(text));
|
|
417
|
+
if (law === null) {
|
|
418
|
+
throw new StubLawError(
|
|
419
|
+
rule_id,
|
|
420
|
+
`${rule_id} is a high-consequence class member with no law section — write one, or ` +
|
|
421
|
+
`record it under \`no_stub\` in src/config/rule-consequence-class.json with the reason`,
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
if (law.length > STUB_LAW_MAX_CHARS) {
|
|
425
|
+
throw new StubLawError(
|
|
426
|
+
rule_id,
|
|
427
|
+
`${rule_id} is a high-consequence class member whose law section is ${law.length} chars, ` +
|
|
428
|
+
`over the ${STUB_LAW_MAX_CHARS} ceiling — a stub never ships a shortened copy, so either ` +
|
|
429
|
+
'the law moves under the ceiling or the rule is recorded under `no_stub` in ' +
|
|
430
|
+
'src/config/rule-consequence-class.json with the reason',
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
return (
|
|
434
|
+
thin_entry(rule_id, text, bodyLinkPrefix) +
|
|
435
|
+
`\n${STUB_LAW_OPEN}${lawDigest(law)} -->\n` +
|
|
436
|
+
`${law}\n` +
|
|
437
|
+
`${STUB_LAW_CLOSE}\n`
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Overridable roots for a {@link build_thin} caller outside this repository.
|
|
443
|
+
*
|
|
444
|
+
* Added for `road-to-an-installed-layer-that-is-thinned` step 1.1: the INSTALLER
|
|
445
|
+
* applies the projector's predicate to `~/.claude/rules`, and it must read the
|
|
446
|
+
* router and the consequence class from the package it is installing FROM, not
|
|
447
|
+
* from wherever `import.meta.url` happens to resolve in a bundle.
|
|
448
|
+
*/
|
|
449
|
+
export interface ThinRoots {
|
|
450
|
+
/** Package root carrying `dist/router.json` and `src/config/`. */
|
|
451
|
+
readonly packageRoot?: string;
|
|
452
|
+
/** Explicit router path, overriding the one derived from `packageRoot`. */
|
|
453
|
+
readonly routerPath?: string;
|
|
454
|
+
/** Prefix a stub's `Body:` link is built on — see {@link absoluteBodyLinkPrefix}. */
|
|
455
|
+
readonly bodyLinkPrefix?: string;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** Map {filename: thin_or_full_text} for every rule. Kernel stays full. */
|
|
459
|
+
export function build_thin(
|
|
460
|
+
rules_dir: string = RULES_SOURCE,
|
|
461
|
+
scope: readonly string[] | null = null,
|
|
462
|
+
/**
|
|
463
|
+
* Optional sink for the D3 diagnostic
|
|
464
|
+
* (road-to-delivery-for-every-host 2.2).
|
|
465
|
+
*
|
|
466
|
+
* An `auto` rule the router gives NO trigger is already kept full-bodied by
|
|
467
|
+
* `noTrigger` below, which is the substantive protection and predates this
|
|
468
|
+
* roadmap. What did not exist is any way to SEE it happen: the rule is
|
|
469
|
+
* silently exempted, so an author who removes a rule's last trigger gets a
|
|
470
|
+
* silently eager rule and no signal. The line makes the exemption audible.
|
|
471
|
+
*/
|
|
472
|
+
announce: ((message: string) => void) | null = null,
|
|
473
|
+
/**
|
|
474
|
+
* The high-consequence class. Defaults to the committed config; a caller
|
|
475
|
+
* may inject one so a fixture can drive the stub-law form without a tree.
|
|
476
|
+
*/
|
|
477
|
+
consequenceClass: ConsequenceClassConfig | null = null,
|
|
478
|
+
/**
|
|
479
|
+
* Where the router, the consequence class and the stub body links come from
|
|
480
|
+
* when the caller is NOT this repository's own projector.
|
|
481
|
+
*
|
|
482
|
+
* Every default below is derived from {@link REPO_ROOT}, which is
|
|
483
|
+
* `import.meta.url` four directories up. That is correct for a module read
|
|
484
|
+
* from `<pkg>/src/scripts/`, and wrong inside a bundled CLI, where the same
|
|
485
|
+
* arithmetic lands on the PARENT of the package — so a caller that knows the
|
|
486
|
+
* real package root hands it over instead of letting the module guess.
|
|
487
|
+
*/
|
|
488
|
+
roots: ThinRoots | null = null,
|
|
489
|
+
): Map<string, string> {
|
|
490
|
+
const routerPath = roots?.routerPath ?? (roots?.packageRoot === undefined
|
|
491
|
+
? ROUTER
|
|
492
|
+
: path.join(roots.packageRoot, 'dist', 'router.json'));
|
|
493
|
+
const classRoot = roots?.packageRoot ?? REPO_ROOT;
|
|
494
|
+
const bodyLinkPrefix = roots?.bodyLinkPrefix ?? BODY_LINK_PREFIX;
|
|
495
|
+
const kernel = kernel_ids(routerPath);
|
|
496
|
+
const noTrigger = no_trigger_ids(routerPath);
|
|
497
|
+
const pathOnly = path_only_ids(routerPath);
|
|
498
|
+
const cls = consequenceClass ?? readConsequenceClass(classRoot);
|
|
499
|
+
const lawInStub = stubLawIds(cls);
|
|
500
|
+
const wsMap = scope !== null ? rule_workspaces_map(routerPath) : new Map<string, string[]>();
|
|
501
|
+
const out = new Map<string, string>();
|
|
502
|
+
for (const p of _globSortedMd(rules_dir)) {
|
|
503
|
+
const text = fs.readFileSync(p, 'utf-8');
|
|
504
|
+
const stem = path.basename(p).replace(/\.md$/, '');
|
|
505
|
+
if (!id_in_scope(stem, scope, kernel, wsMap, fm_workspaces(text))) {
|
|
506
|
+
continue; // out of workspace scope — no body, no pointer line
|
|
507
|
+
}
|
|
508
|
+
// A declared `no_stub` member keeps its whole body. Its law cannot be
|
|
509
|
+
// copied into a stub, and a stub with no law is exactly the shape the
|
|
510
|
+
// class exists to prevent for these rules — so the safe residue is the
|
|
511
|
+
// full text, not a pointer.
|
|
512
|
+
const noStub = cls.no_stub[stem] !== undefined;
|
|
513
|
+
const full = kernel.has(stem) || noTrigger.has(stem) || pathOnly.has(stem) || noStub;
|
|
514
|
+
if (announce !== null && noTrigger.has(stem) && !kernel.has(stem)) {
|
|
515
|
+
announce(`D3: trigger-less auto rule ${path.basename(p)} — kept full-bodied, never thinned`);
|
|
516
|
+
}
|
|
517
|
+
if (announce !== null && pathOnly.has(stem) && !kernel.has(stem) && !noTrigger.has(stem)) {
|
|
518
|
+
announce(
|
|
519
|
+
`E2: path-only auto rule ${path.basename(p)} — kept full-bodied; its only triggers ` +
|
|
520
|
+
`are path-shaped and the delivery concern is not bound on pre_tool_use`,
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
if (full) {
|
|
524
|
+
out.set(path.basename(p), text);
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
if (lawInStub.has(stem)) {
|
|
528
|
+
if (announce !== null) {
|
|
529
|
+
announce(
|
|
530
|
+
`LAW: high-consequence rule ${path.basename(p)} — stub carries its own law section, byte-copied`,
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
out.set(path.basename(p), thin_entry_with_law(stem, text, bodyLinkPrefix));
|
|
534
|
+
continue;
|
|
535
|
+
}
|
|
536
|
+
out.set(path.basename(p), thin_entry(stem, text, bodyLinkPrefix));
|
|
537
|
+
}
|
|
538
|
+
return out;
|
|
539
|
+
}
|
|
540
|
+
|