@event4u/agent-config 16.2.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 +89 -237
- 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 +4 -4
- 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/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/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/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/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-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- 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/monorepo-workspace/SKILL.md +1 -1
- 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 +2 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- 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-IGD6ERX6.js → chunk-JQVYKIZA.js} +10 -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-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-NQ7WT43P.js} +1186 -244
- 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-7XGBX6ZH.js → chunk-RQNNW7IF.js} +1314 -560
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-TRI6DQZ6.js} +2781 -340
- 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 +1167 -234
- 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 +127 -75
- 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 +5357 -6656
- package/dist/hooks/dispatch.sha256 +1 -1
- 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 +2080 -1024
- 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 +31965 -27154
- 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 +6 -0
- package/dist/shared/skillRanking.js +74 -13
- package/docs/contracts/settings-classes.md +7 -5
- package/package.json +1 -1
- 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/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +62 -22
- package/src/config/continuity-surface.json +7 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +96 -3
- package/src/config/gate-violation-baselines.json +17 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/routing-coverage-seed.json +24 -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 +23 -71
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- 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/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_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/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/prompt_shape.ts +46 -0
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- 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/skill_origin.ts +254 -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 +77 -2
- 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/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +116 -4
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_kernel_edit_ratified.ts +49 -11
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- 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 +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +150 -9
- package/src/scripts/hooks/block_config_weakening.ts +67 -2
- package/src/scripts/hooks/block_plumbing_writes.ts +29 -1
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/rule_inject_hook.ts +757 -63
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +64 -8
- package/src/scripts/hooks/turn_end_gate_hook.ts +84 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +72 -185
- 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_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_rule_law_section.ts +517 -0
- 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/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +216 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_single_token_triggers.ts +277 -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/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/self_review_gate.ts +62 -7
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- 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 -5
- package/src/server/schemas/settings.ts +10 -0
- package/src/shared/skillRanking.ts +117 -11
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
|
@@ -52,6 +52,38 @@ export function isAcceptedEdge(e: CodeEdge): boolean {
|
|
|
52
52
|
return isStatedResolution(e.resolved_via);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
+
/** Why a gate verb refused. A closed set so a caller can branch on it. */
|
|
56
|
+
export type RefusalReason = 'foreign-edges-not-accepted' | 'entry-points-unavailable';
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* A graph whose every edge came from a file this package did not build.
|
|
60
|
+
*
|
|
61
|
+
* Those edges are tagged `foreign` and sit in the guess set, so a gate verb
|
|
62
|
+
* walking one would accept nothing and report an empty answer — and an empty
|
|
63
|
+
* answer from a gate reads as "nothing to worry about", which is the wrong
|
|
64
|
+
* reading and the dangerous one. The verbs refuse instead, by name, so the
|
|
65
|
+
* consumer learns that their graph is readable but not gate-grade rather than
|
|
66
|
+
* being told their diff impacts nothing.
|
|
67
|
+
*
|
|
68
|
+
* A MIXED graph is not refused: it carries edges this package did resolve, and
|
|
69
|
+
* the guess filter already excludes the foreign ones from the walk.
|
|
70
|
+
*/
|
|
71
|
+
export function foreignRefusal(g: LoadedGraph): string | null {
|
|
72
|
+
// `graph` is null on the index-backed read path, which answers per-node
|
|
73
|
+
// from the twin and never materialises the edge list. Nothing to classify
|
|
74
|
+
// there, and an index-backed graph is one this package built.
|
|
75
|
+
const edges: readonly CodeEdge[] = g.graph?.edges ?? [];
|
|
76
|
+
if (edges.length === 0) return null;
|
|
77
|
+
if (!edges.every((e) => e.resolved_via === 'foreign')) return null;
|
|
78
|
+
return (
|
|
79
|
+
'every edge in this graph was read from a file this package did not build, so all of them ' +
|
|
80
|
+
'are guesses and none is gate-grade. A gate verb would accept no edge and report an empty ' +
|
|
81
|
+
'answer, which reads as "nothing found" rather than "nothing trusted". Build a native graph ' +
|
|
82
|
+
'(`agent-config graph build`) to use this verb, or use `query` and `explain`, which answer ' +
|
|
83
|
+
'from foreign edges and say so.'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
55
87
|
/**
|
|
56
88
|
* Relations that constitute a REFERENCE to the target, as opposed to
|
|
57
89
|
* containment of it.
|
|
@@ -132,6 +164,10 @@ export interface ReachedNode {
|
|
|
132
164
|
}
|
|
133
165
|
|
|
134
166
|
export interface ImpactResult extends VerbReport {
|
|
167
|
+
/** Non-null when the verb REFUSED to answer. Every list is then empty. */
|
|
168
|
+
refusal: string | null;
|
|
169
|
+
/** Machine-readable reason, when `refusal` is set. */
|
|
170
|
+
refusal_reason: RefusalReason | null;
|
|
135
171
|
/** Node ids the diff touched that the graph knows. */
|
|
136
172
|
seeds: string[];
|
|
137
173
|
/**
|
|
@@ -219,6 +255,26 @@ export function impact(
|
|
|
219
255
|
depth = 2,
|
|
220
256
|
isTest: (relPath: string) => boolean = () => false,
|
|
221
257
|
): ImpactResult {
|
|
258
|
+
const foreign = foreignRefusal(g);
|
|
259
|
+
if (foreign !== null) {
|
|
260
|
+
return {
|
|
261
|
+
source: g.source,
|
|
262
|
+
state,
|
|
263
|
+
accepted_via: '(none)',
|
|
264
|
+
rejected_via: '(none)',
|
|
265
|
+
refusal: foreign,
|
|
266
|
+
refusal_reason: 'foreign-edges-not-accepted',
|
|
267
|
+
seeds: [],
|
|
268
|
+
unresolved_files: [],
|
|
269
|
+
not_indexed_files: [],
|
|
270
|
+
dependents: [],
|
|
271
|
+
reached: [],
|
|
272
|
+
test_files: [],
|
|
273
|
+
producing_edges: [],
|
|
274
|
+
lines: [],
|
|
275
|
+
recommended_reads: [],
|
|
276
|
+
};
|
|
277
|
+
}
|
|
222
278
|
const { seeds, unresolved, not_indexed } = seedsForFiles(g, files);
|
|
223
279
|
const accepted: CodeEdge[] = [];
|
|
224
280
|
const rejected: CodeEdge[] = [];
|
|
@@ -258,6 +314,8 @@ export function impact(
|
|
|
258
314
|
state,
|
|
259
315
|
accepted_via: viaHistogram(accepted),
|
|
260
316
|
rejected_via: viaHistogram(rejected),
|
|
317
|
+
refusal: null,
|
|
318
|
+
refusal_reason: null,
|
|
261
319
|
seeds,
|
|
262
320
|
unresolved_files: unresolved,
|
|
263
321
|
not_indexed_files: not_indexed,
|
|
@@ -334,6 +392,10 @@ export function testsFor(g: LoadedGraph, symbol: string, state: GraphState): Tes
|
|
|
334
392
|
}
|
|
335
393
|
|
|
336
394
|
export interface UntestedResult extends VerbReport {
|
|
395
|
+
/** Non-null when the verb REFUSED to answer. Every list is then empty. */
|
|
396
|
+
refusal: string | null;
|
|
397
|
+
/** Machine-readable reason, when `refusal` is set. */
|
|
398
|
+
refusal_reason: RefusalReason | null;
|
|
337
399
|
seeds: string[];
|
|
338
400
|
unresolved_files: string[];
|
|
339
401
|
not_indexed_files: string[];
|
|
@@ -351,6 +413,24 @@ export interface UntestedResult extends VerbReport {
|
|
|
351
413
|
* count and make the ratio meaningless.
|
|
352
414
|
*/
|
|
353
415
|
export function untested(g: LoadedGraph, files: readonly string[], state: GraphState): UntestedResult {
|
|
416
|
+
const foreign = foreignRefusal(g);
|
|
417
|
+
if (foreign !== null) {
|
|
418
|
+
return {
|
|
419
|
+
source: g.source,
|
|
420
|
+
state,
|
|
421
|
+
accepted_via: '(none)',
|
|
422
|
+
rejected_via: '(none)',
|
|
423
|
+
refusal: foreign,
|
|
424
|
+
refusal_reason: 'foreign-edges-not-accepted',
|
|
425
|
+
seeds: [],
|
|
426
|
+
unresolved_files: [],
|
|
427
|
+
not_indexed_files: [],
|
|
428
|
+
untested: [],
|
|
429
|
+
tested: [],
|
|
430
|
+
lines: [],
|
|
431
|
+
recommended_reads: [],
|
|
432
|
+
};
|
|
433
|
+
}
|
|
354
434
|
const { seeds, unresolved, not_indexed } = seedsForFiles(g, files);
|
|
355
435
|
const accepted: CodeEdge[] = [];
|
|
356
436
|
const rejected: CodeEdge[] = [];
|
|
@@ -375,6 +455,8 @@ export function untested(g: LoadedGraph, files: readonly string[], state: GraphS
|
|
|
375
455
|
state,
|
|
376
456
|
accepted_via: viaHistogram(accepted),
|
|
377
457
|
rejected_via: viaHistogram(rejected),
|
|
458
|
+
refusal: null,
|
|
459
|
+
refusal_reason: null,
|
|
378
460
|
seeds,
|
|
379
461
|
unresolved_files: unresolved,
|
|
380
462
|
not_indexed_files: not_indexed,
|
|
@@ -403,6 +485,8 @@ export interface DeadResult extends VerbReport {
|
|
|
403
485
|
sources: EntryPointSource[];
|
|
404
486
|
/** Non-null when the verb REFUSED to answer. The list is then empty. */
|
|
405
487
|
refusal: string | null;
|
|
488
|
+
/** Machine-readable reason, when `refusal` is set. */
|
|
489
|
+
refusal_reason: RefusalReason | null;
|
|
406
490
|
dead: string[];
|
|
407
491
|
/** Entry points excluded from `dead` — the audit trail for the exclusion. */
|
|
408
492
|
excluded: string[];
|
|
@@ -448,6 +532,23 @@ export function dead(
|
|
|
448
532
|
state,
|
|
449
533
|
sources: [...sources],
|
|
450
534
|
};
|
|
535
|
+
// Checked BEFORE the entry-point sources: on an all-foreign graph every
|
|
536
|
+
// in-edge is a guess, so every symbol would report dead — the confident
|
|
537
|
+
// false "dead" this verb's own header calls worse than no verb at all.
|
|
538
|
+
const foreign = foreignRefusal(g);
|
|
539
|
+
if (foreign !== null) {
|
|
540
|
+
return {
|
|
541
|
+
...base,
|
|
542
|
+
accepted_via: '(none)',
|
|
543
|
+
rejected_via: '(none)',
|
|
544
|
+
refusal: foreign,
|
|
545
|
+
refusal_reason: 'foreign-edges-not-accepted',
|
|
546
|
+
dead: [],
|
|
547
|
+
excluded: [],
|
|
548
|
+
lines: [],
|
|
549
|
+
recommended_reads: [],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
451
552
|
if (unavailable.length > 0 && opts.acceptMissingExports !== true) {
|
|
452
553
|
return {
|
|
453
554
|
...base,
|
|
@@ -460,6 +561,7 @@ export function dead(
|
|
|
460
561
|
'A symbol they would have declared an entry point is indistinguishable from a dead one, ' +
|
|
461
562
|
'so no list is produced. Supply the missing source with --entry-points <file>, or state ' +
|
|
462
563
|
'that you accept the gap with --accept-missing-exports.',
|
|
564
|
+
refusal_reason: 'entry-points-unavailable',
|
|
463
565
|
dead: [],
|
|
464
566
|
excluded: [],
|
|
465
567
|
lines: [],
|
|
@@ -496,6 +598,7 @@ export function dead(
|
|
|
496
598
|
accepted_via: viaHistogram(accepted),
|
|
497
599
|
rejected_via: viaHistogram(rejected),
|
|
498
600
|
refusal: null,
|
|
601
|
+
refusal_reason: null,
|
|
499
602
|
dead: list,
|
|
500
603
|
excluded: excluded.sort(),
|
|
501
604
|
lines: list,
|
|
@@ -79,12 +79,44 @@ const TABLES = {
|
|
|
79
79
|
|
|
80
80
|
export type TableName = keyof typeof TABLES;
|
|
81
81
|
|
|
82
|
+
/**
|
|
83
|
+
* The TWO labels a compiled table carries, and why one was not enough.
|
|
84
|
+
*
|
|
85
|
+
* `fingerprint` binds the file to its YAML SOURCE — it answers "is this
|
|
86
|
+
* compiled from the YAML as it stands". `body_fingerprint` binds the file to
|
|
87
|
+
* ITSELF: it is the fingerprint of the exact body bytes this writer emitted,
|
|
88
|
+
* and it answers "is the body a reader is about to serve the body that was
|
|
89
|
+
* compiled".
|
|
90
|
+
*
|
|
91
|
+
* road-to-blocking-severities 2.3. The source label alone left a real hole,
|
|
92
|
+
* and it is the one the roadmap measured: edit a cell of the compiled `table`
|
|
93
|
+
* and leave `fingerprint` alone, and it still equals the YAML's fingerprint,
|
|
94
|
+
* so the reader serves the edited body — and since both readers prefer the
|
|
95
|
+
* compiled form, the edited cell is what every host is told while the reviewed
|
|
96
|
+
* YAML beside it says something else.
|
|
97
|
+
*
|
|
98
|
+
* WHAT THIS DOES NOT CLAIM. Someone who edits the body can recompute this
|
|
99
|
+
* label as easily as the writer does; it is an integrity check against a
|
|
100
|
+
* PARTIAL edit — a hand edit, a bad merge, a truncated write — not a signature.
|
|
101
|
+
* That is the same honesty the source label already owed: neither resists an
|
|
102
|
+
* author, and resisting an author is what the tool-call deny (2.2) and the
|
|
103
|
+
* ratification record (2.1) are for. Three cheap mechanisms over one expensive
|
|
104
|
+
* one, each named for what it actually covers.
|
|
105
|
+
*
|
|
106
|
+
* The body text is `JSON.stringify` of the parsed document, which is exactly
|
|
107
|
+
* what lands in the file, so a reader recovers it by re-stringifying what it
|
|
108
|
+
* parsed. A test pins that round trip rather than assuming it.
|
|
109
|
+
*/
|
|
82
110
|
export function compile(yamlText: string, table: TableName = 'manifest'): string {
|
|
83
111
|
const spec = TABLES[table];
|
|
84
112
|
const doc = (spec.yamlVersion === undefined
|
|
85
113
|
? parseYaml(yamlText)
|
|
86
114
|
: parseYaml(yamlText, { version: spec.yamlVersion })) as unknown;
|
|
87
|
-
return JSON.stringify({
|
|
115
|
+
return JSON.stringify({
|
|
116
|
+
fingerprint: tableFingerprint(yamlText),
|
|
117
|
+
body_fingerprint: tableFingerprint(JSON.stringify(doc)),
|
|
118
|
+
[spec.key]: doc,
|
|
119
|
+
});
|
|
88
120
|
}
|
|
89
121
|
|
|
90
122
|
export function main(argv: readonly string[] = process.argv.slice(2)): number {
|
package/src/scripts/condense.ts
CHANGED
|
@@ -42,6 +42,7 @@ import {
|
|
|
42
42
|
strip_source_prefix,
|
|
43
43
|
} from './_lib/agent_src.js';
|
|
44
44
|
import { rewriteProjectedBodyLinks } from './_lib/guidelines_lane.js';
|
|
45
|
+
import { render_windsurfrules, strip_frontmatter } from './_lib/windsurf_render.js';
|
|
45
46
|
// Import-safety note: `project_thin_rules` guards its CLI entry
|
|
46
47
|
// (`_isCliEntry()` before `process.exit(main())`), so importing it here is
|
|
47
48
|
// side-effect-free. condense.ts is bundled into the installer, where a bare
|
|
@@ -49,7 +50,7 @@ import { rewriteProjectedBodyLinks } from './_lib/guidelines_lane.js';
|
|
|
49
50
|
// bundled-CLI-entry-guard landmine. Verified before wiring, not assumed.
|
|
50
51
|
import { build_thin } from './project_thin_rules.js';
|
|
51
52
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
52
|
-
import { describeDroppedHosts,
|
|
53
|
+
import { describeDroppedHosts, resolveLeanProjection, thinsHost,
|
|
53
54
|
writesThinFiles, type LeanProjectionHosts, type LeanProjectionMode } from './_lib/lean_projection_mode.js';
|
|
54
55
|
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
55
56
|
import { project_settings_path, load_agent_settings } from './_lib/agent_settings.js';
|
|
@@ -461,16 +462,14 @@ function _read_projection_scope_dedup(): boolean {
|
|
|
461
462
|
|
|
462
463
|
|
|
463
464
|
function _lean_projection_settings(): { mode: LeanProjectionMode; hosts: LeanProjectionHosts } {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
hosts: resolveLeanProjectionHosts(obj['hosts']),
|
|
473
|
-
};
|
|
465
|
+
// Step 1.2 of `road-to-a-rule-carrier-that-works-outside-the-repo`: this is
|
|
466
|
+
// now the shared resolver rather than one of three private readers. The
|
|
467
|
+
// INPUT is unchanged — `MODULE_STATE.SETTINGS_FILE` is still the file, so
|
|
468
|
+
// the projector's answer is the answer it always gave — and only the code
|
|
469
|
+
// that interprets it is shared. That is risk 2 of the roadmap's register
|
|
470
|
+
// ("making the hook agree with the projector could also move the
|
|
471
|
+
// projector") discharged by construction instead of by a test alone.
|
|
472
|
+
return resolveLeanProjection({ settingsPath: MODULE_STATE.SETTINGS_FILE });
|
|
474
473
|
}
|
|
475
474
|
|
|
476
475
|
// --- hashing -----------------------------------------------------------------
|
|
@@ -778,15 +777,9 @@ function _filter_tool_dirs(mapping: Record<string, string>): Record<string, stri
|
|
|
778
777
|
return out;
|
|
779
778
|
}
|
|
780
779
|
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if (end !== -1) {
|
|
785
|
-
content = _lstripNewlines(content.slice(end + 3));
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
return content;
|
|
789
|
-
}
|
|
780
|
+
// Moved to `_lib/windsurf_render.ts` beside its one remaining caller here and
|
|
781
|
+
// the census that renders through it; re-exported so every importer's path holds.
|
|
782
|
+
export { strip_frontmatter };
|
|
790
783
|
|
|
791
784
|
// ── Path rewriter (P1 of road-to-path-fixes.md) ───────────────────────────
|
|
792
785
|
// Kept byte-identical to check_condensation.ts::_rewritePaths (the two MUST
|
|
@@ -1229,14 +1222,8 @@ export function generate_windsurfrules(): number {
|
|
|
1229
1222
|
// on `.windsurf/rules`. Without this line removing the per-run filter regressed
|
|
1230
1223
|
// it 13 → 113: the per-run filter had been covering it by accident.
|
|
1231
1224
|
const rules = partition_rules_for_dir('.windsurf/rules', _scoped_rule_basenames());
|
|
1232
|
-
const parts = ['# Auto-generated from dist/agent-src/rules/ — do not edit directly\n'];
|
|
1233
|
-
for (const rule of rules) {
|
|
1234
|
-
const p = path.join(MODULE_STATE.RULES_SOURCE, rule);
|
|
1235
|
-
const content = strip_frontmatter(_readText(p));
|
|
1236
|
-
parts.push(`---\n\n${_strip(content)}\n`);
|
|
1237
|
-
}
|
|
1238
1225
|
const output = path.join(MODULE_STATE.PROJECT_ROOT, '.windsurfrules');
|
|
1239
|
-
_writeText(output,
|
|
1226
|
+
_writeText(output, render_windsurfrules(MODULE_STATE.RULES_SOURCE, rules));
|
|
1240
1227
|
info(` ✅ Generated .windsurfrules (${rules.length} rules)`);
|
|
1241
1228
|
return rules.length;
|
|
1242
1229
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* What the `estate_offset_exempt` population actually says — the producing command.
|
|
4
|
+
*
|
|
5
|
+
* THIS IS A READER, NOT A GATE. It exits 0 on every corpus, including one where
|
|
6
|
+
* no reason names anything. The refusal lives in `check_estate_count`, applies
|
|
7
|
+
* to ADDED files only, and is grandfathered precisely because of what this
|
|
8
|
+
* script measures: 43 of the 221 reasons in the tree on 2026-10-01 name no
|
|
9
|
+
* alternative disposition, so a check that re-read them would land red on a
|
|
10
|
+
* fifth of its corpus.
|
|
11
|
+
*
|
|
12
|
+
* WHAT IT BUYS. Two numbers nobody could produce before: how many exemptions
|
|
13
|
+
* name a rejected alternative, and how many repeat another file's reason
|
|
14
|
+
* verbatim. Both are the inputs to whether the refusal vocabulary is the right
|
|
15
|
+
* one, and both move as the estate moves — so the NEXT reading is a delta
|
|
16
|
+
* rather than a re-derivation, which is the same bargain `roadmap_verify_share`
|
|
17
|
+
* makes for the verify grammar.
|
|
18
|
+
*
|
|
19
|
+
* THE VOCABULARY IS NOT THIS SCRIPT'S. It imports the lemma list the gate
|
|
20
|
+
* refuses on, so the report and the refusal cannot drift. A drifted pair fails
|
|
21
|
+
* silently and in the worst direction: the report says the tree conforms while
|
|
22
|
+
* the gate rejects it.
|
|
23
|
+
*
|
|
24
|
+
* CLI:
|
|
25
|
+
* ./scripts-run src/scripts/estate_exemption_shape
|
|
26
|
+
* ./scripts-run src/scripts/estate_exemption_shape --json
|
|
27
|
+
* ./scripts-run src/scripts/estate_exemption_shape --root <dir>
|
|
28
|
+
*
|
|
29
|
+
* Exit codes: 0 always, except 2 for bad argv or a scan root that holds nothing.
|
|
30
|
+
*/
|
|
31
|
+
import * as fs from 'node:fs';
|
|
32
|
+
import * as path from 'node:path';
|
|
33
|
+
import process from 'node:process';
|
|
34
|
+
import { fileURLToPath } from 'node:url';
|
|
35
|
+
|
|
36
|
+
import { exemptionReason } from './_lib/estate_offsets.js';
|
|
37
|
+
import { DISPOSITION_LEMMAS, lemmasNamed, namesDisposition, reasonKey } from './_lib/exemption_shape.js';
|
|
38
|
+
import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
|
|
39
|
+
|
|
40
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
41
|
+
const _PROG = 'estate_exemption_shape';
|
|
42
|
+
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
43
|
+
|
|
44
|
+
/** The three trees an exemption can sit in. `stubs/` and `skipped/` carry none. */
|
|
45
|
+
const TREES: readonly { readonly label: string; readonly dir: string }[] = [
|
|
46
|
+
{ label: 'active', dir: 'agents/roadmaps' },
|
|
47
|
+
{ label: 'later', dir: 'agents/roadmaps/later' },
|
|
48
|
+
{ label: 'archive', dir: 'agents/roadmaps/archive' },
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
export interface ExemptionRow {
|
|
52
|
+
readonly tree: string;
|
|
53
|
+
readonly file: string;
|
|
54
|
+
readonly reason: string;
|
|
55
|
+
/** The disposition lemmas this reason names; empty means it names none. */
|
|
56
|
+
readonly lemmas: readonly string[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface ShapeReport {
|
|
60
|
+
readonly rows: readonly ExemptionRow[];
|
|
61
|
+
readonly shaped: number;
|
|
62
|
+
readonly shapeless: number;
|
|
63
|
+
/** Per-lemma hit counts, in the gate's own lemma order. */
|
|
64
|
+
readonly byLemma: Readonly<Record<string, number>>;
|
|
65
|
+
/** Groups of two or more files sharing one reason, largest first. */
|
|
66
|
+
readonly duplicates: readonly (readonly string[])[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Read every exemption under `root`, in a stable order. */
|
|
70
|
+
export function collectExemptions(root: string): ExemptionRow[] {
|
|
71
|
+
const rows: ExemptionRow[] = [];
|
|
72
|
+
for (const tree of TREES) {
|
|
73
|
+
const abs = path.join(root, tree.dir);
|
|
74
|
+
let names: string[];
|
|
75
|
+
try {
|
|
76
|
+
names = fs.readdirSync(abs);
|
|
77
|
+
} catch {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
for (const name of [...names].sort()) {
|
|
81
|
+
if (!name.endsWith('.md')) continue;
|
|
82
|
+
const file = path.join(abs, name);
|
|
83
|
+
let text: string;
|
|
84
|
+
try {
|
|
85
|
+
if (!fs.statSync(file).isFile()) continue;
|
|
86
|
+
text = fs.readFileSync(file, 'utf-8');
|
|
87
|
+
} catch {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const reason = exemptionReason(text);
|
|
91
|
+
if (reason === null) continue;
|
|
92
|
+
rows.push({ tree: tree.label, file: `${tree.dir}/${name}`, reason, lemmas: lemmasNamed(reason) });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return rows;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Summarise the population against the gate's vocabulary. */
|
|
99
|
+
export function report(rows: readonly ExemptionRow[]): ShapeReport {
|
|
100
|
+
const byLemma: Record<string, number> = {};
|
|
101
|
+
for (const l of DISPOSITION_LEMMAS) {
|
|
102
|
+
byLemma[l.name] = rows.filter((r) => r.lemmas.includes(l.name)).length;
|
|
103
|
+
}
|
|
104
|
+
const groups = new Map<string, string[]>();
|
|
105
|
+
for (const r of rows) {
|
|
106
|
+
const key = reasonKey(r.reason);
|
|
107
|
+
const list = groups.get(key) ?? [];
|
|
108
|
+
list.push(r.file);
|
|
109
|
+
groups.set(key, list);
|
|
110
|
+
}
|
|
111
|
+
const duplicates = [...groups.values()].filter((g) => g.length > 1).sort((a, b) => b.length - a.length);
|
|
112
|
+
return {
|
|
113
|
+
rows,
|
|
114
|
+
shaped: rows.filter((r) => namesDisposition(r.reason)).length,
|
|
115
|
+
shapeless: rows.filter((r) => !namesDisposition(r.reason)).length,
|
|
116
|
+
byLemma,
|
|
117
|
+
duplicates,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function render(rep: ShapeReport): string {
|
|
122
|
+
const out: string[] = [];
|
|
123
|
+
const total = rep.rows.length;
|
|
124
|
+
out.push(`${_PROG}: ${String(total)} exemption(s)`);
|
|
125
|
+
for (const tree of TREES) {
|
|
126
|
+
out.push(` ${tree.label.padEnd(10)} ${String(rep.rows.filter((r) => r.tree === tree.label).length).padStart(4)}`);
|
|
127
|
+
}
|
|
128
|
+
out.push(` names a rejected alternative ${String(rep.shaped).padStart(4)}`);
|
|
129
|
+
out.push(` names none ${String(rep.shapeless).padStart(4)}`);
|
|
130
|
+
out.push(' per lemma:');
|
|
131
|
+
for (const l of DISPOSITION_LEMMAS) {
|
|
132
|
+
out.push(` ${l.name.padEnd(12)} ${String(rep.byLemma[l.name] ?? 0).padStart(4)}`);
|
|
133
|
+
}
|
|
134
|
+
const dupFiles = rep.duplicates.reduce((n, g) => n + g.length, 0);
|
|
135
|
+
out.push(` verbatim duplicate groups ${String(rep.duplicates.length).padStart(4)} covering ${String(dupFiles)} file(s)`);
|
|
136
|
+
for (const g of rep.duplicates) {
|
|
137
|
+
out.push(` (${String(g.length)}x) ${g.join(', ')}`);
|
|
138
|
+
}
|
|
139
|
+
if (rep.shapeless > 0) {
|
|
140
|
+
out.push(' naming no rejected alternative:');
|
|
141
|
+
for (const r of rep.rows.filter((x) => !namesDisposition(x.reason))) {
|
|
142
|
+
out.push(` ${r.file}: ${r.reason.slice(0, 120)}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return out.join('\n') + '\n';
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function main(argv: string[] = process.argv.slice(2)): number {
|
|
149
|
+
const json = argv.includes('--json');
|
|
150
|
+
const rootIdx = argv.indexOf('--root');
|
|
151
|
+
let root = REPO_ROOT;
|
|
152
|
+
if (rootIdx !== -1) {
|
|
153
|
+
const next = argv[rootIdx + 1];
|
|
154
|
+
if (next === undefined || next.startsWith('-')) {
|
|
155
|
+
process.stderr.write(`usage: ${_PROG} [--root <dir>] [--json]\n`);
|
|
156
|
+
return 2;
|
|
157
|
+
}
|
|
158
|
+
root = path.resolve(next);
|
|
159
|
+
}
|
|
160
|
+
const unknown = argv.filter((a) => a.startsWith('-') && a !== '--json' && a !== '--root');
|
|
161
|
+
if (unknown.length > 0) {
|
|
162
|
+
process.stderr.write(`${_PROG}: unknown argument(s) ${unknown.join(', ')}\n`);
|
|
163
|
+
return 2;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const rows = collectExemptions(root);
|
|
167
|
+
try {
|
|
168
|
+
// A root holding no roadmap at all is a dead scope, never an estate with
|
|
169
|
+
// nothing to report: this reader would otherwise print a confident zero
|
|
170
|
+
// over a path that does not exist.
|
|
171
|
+
assertScanned({
|
|
172
|
+
gate: _PROG,
|
|
173
|
+
scanned: TREES.reduce((n, t) => {
|
|
174
|
+
try {
|
|
175
|
+
return n + fs.readdirSync(path.join(root, t.dir)).filter((f) => f.endsWith('.md')).length;
|
|
176
|
+
} catch {
|
|
177
|
+
return n;
|
|
178
|
+
}
|
|
179
|
+
}, 0),
|
|
180
|
+
units: 'roadmap file(s)',
|
|
181
|
+
roots: TREES.map((t) => t.dir),
|
|
182
|
+
});
|
|
183
|
+
} catch (err) {
|
|
184
|
+
if (err instanceof DeadScopeError) {
|
|
185
|
+
process.stderr.write(`❌ ${_PROG}: ${err.message}\n`);
|
|
186
|
+
return 2;
|
|
187
|
+
}
|
|
188
|
+
throw err;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const rep = report(rows);
|
|
192
|
+
if (json) {
|
|
193
|
+
process.stdout.write(JSON.stringify(rep, null, 2) + '\n');
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
process.stdout.write(render(rep));
|
|
197
|
+
return 0;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (path.resolve(process.argv[1] ?? '') === _HERE) {
|
|
201
|
+
process.exit(main());
|
|
202
|
+
}
|
|
@@ -63,7 +63,7 @@ import {
|
|
|
63
63
|
const _HERE = fileURLToPath(import.meta.url);
|
|
64
64
|
export const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
65
65
|
|
|
66
|
-
/** The contract the README points at for projection modes
|
|
66
|
+
/** The contract the README points at for projection modes. */
|
|
67
67
|
export const CONTRACT_REL = path.join('docs', 'contracts', 'rule-router.md');
|
|
68
68
|
|
|
69
69
|
export const BEGIN = '<!-- BEGIN generated: host-standing-cost -->';
|