@event4u/agent-config 16.1.0 → 16.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +98 -257
- package/README.md +14 -12
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/feature/plan.md +1 -1
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +1 -1
- package/dist/agent-src/commands/implement-ticket.md +1 -1
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/process-full.md +5 -5
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +361 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/work.md +4 -0
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +6 -6
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +4 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +32 -2
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +101 -7
- package/dist/agent-src/rules/active-remediation.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -0
- package/dist/agent-src/rules/broken-access-control.md +1 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -0
- package/dist/agent-src/rules/context-hygiene.md +1 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +16 -33
- package/dist/agent-src/rules/design-fidelity.md +13 -16
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -0
- package/dist/agent-src/rules/domain-safety-pii.md +1 -0
- package/dist/agent-src/rules/domain-safety-retention.md +1 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -0
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +1 -0
- package/dist/agent-src/rules/git-history-discipline.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -2
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +14 -17
- package/dist/agent-src/rules/persona-governance.md +2 -2
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +1 -1
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -2
- package/dist/agent-src/rules/session-canary.md +8 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +1 -0
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -0
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +1 -1
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/design-review/SKILL.md +30 -4
- package/dist/agent-src/skills/design-review/references/verification-automation.md +32 -0
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +18 -3
- package/dist/agent-src/skills/existing-ui-audit/references/component-taxonomy-detection.md +56 -0
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/markitdown/SKILL.md +4 -0
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/playwright-testing/SKILL.md +30 -6
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +1 -1
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +25 -2
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +30 -0
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/dist/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/dist/cli/python/knowledge_ingest.js +1 -1
- package/dist/cli/python/workspace_drive.js +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +12 -1
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-CQWX336L.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/chunk-EEHDXSYD.js +2040 -0
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-GKE7ZVFA.js} +4 -4
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/{chunk-UNCRJBT3.js → chunk-JQVYKIZA.js} +41 -0
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-JY2I2DXV.js} +12 -1
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-YUM5KURG.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-LIYTLH6O.js} +1 -1
- package/dist/cli-delegate/chunk-NQ7WT43P.js +2729 -0
- package/dist/cli-delegate/chunk-NXEILF2K.js +2160 -0
- package/dist/cli-delegate/{chunk-5R7HOLAF.js → chunk-NYX3L2PZ.js} +1 -1
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-QLDHIPWR.js} +1 -1
- package/dist/cli-delegate/{chunk-HDVVGPQW.js → chunk-RQNNW7IF.js} +1405 -567
- package/dist/cli-delegate/{chunk-S3VFXMTM.js → chunk-S4UMZNES.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-S4YWNRKO.js} +24 -8
- package/dist/cli-delegate/{chunk-GJCBB2UG.js → chunk-TRI6DQZ6.js} +3054 -325
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +1862 -809
- package/dist/cli-delegate/cmd_doctor.js +13 -6
- package/dist/cli-delegate/cmd_doctor_neighbours.js +23 -0
- package/dist/cli-delegate/cmd_explain.js +5 -5
- package/dist/cli-delegate/cmd_export.js +11 -8
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +10 -7
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +3 -3
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +11 -8
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +8 -6
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +4 -3
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +11 -8
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +11 -8
- package/dist/cli-delegate/cmd_uninstall.js +11 -8
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +3 -2
- package/dist/cli-delegate/cmd_validate.js +11 -8
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +5 -5
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +132 -80
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +7972 -5186
- package/dist/hooks/dispatch.sha256 +1 -0
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2226 -1055
- package/dist/install/installThinLayer.js +261 -0
- package/dist/install/installedRuleLinks.js +176 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +40913 -34865
- package/dist/scripts/_lib/agent_settings.js +11 -0
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/installed_layer.js +378 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/thin_rules.js +458 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/schemas/settings.js +29 -0
- package/dist/shared/skillRanking.js +74 -13
- package/dist/ui/assets/index-Cwq4d0hX.js +23 -0
- package/dist/ui/assets/index-Cyjy8bWO.css +1 -0
- package/dist/ui/index.html +2 -2
- package/docs/contracts/settings-classes.md +10 -6
- package/package.json +4 -4
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/scripts/work_engine/directives/ui/app_spec.ts +10 -4
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +302 -21
- package/src/agent-src/templates/scripts/work_engine/directives/ui/audit.ts +22 -3
- package/src/agent-src/templates/scripts/work_engine/directives/ui/scaffold.ts +6 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/agent-src/templates/scripts/work_engine/state.ts +57 -7
- package/src/agent-src/templates/scripts/work_engine/taxonomy/detect.ts +500 -0
- package/src/agent-src/templates/scripts/work_engine/taxonomy/place.ts +183 -0
- package/src/config/agent-settings.template.yml +91 -22
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/continuity-surface.json +9 -2
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +327 -3
- package/src/config/gate-violation-baselines.json +21 -9
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-latency-budget.json +21 -0
- package/src/config/hook-token-budget.json +8 -3
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/metric-registry.yml +114 -0
- package/src/config/placeholder-drift-budget.json +10 -0
- package/src/config/routing-coverage-seed.json +38 -3
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +99 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +312 -0
- package/src/install/installedRuleLinks.ts +220 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_doctor.ts +28 -90
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +293 -0
- package/src/scripts/_cli/doctor_execution.ts +100 -17
- package/src/scripts/_cli/doctor_network_posture.ts +226 -0
- package/src/scripts/_dispatch.bash +16 -2
- package/src/scripts/_lib/agent_settings.ts +11 -0
- package/src/scripts/_lib/body_portable.ts +102 -0
- package/src/scripts/_lib/cc_transcript.ts +132 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/concern_sla_window.ts +200 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/forge_protection.ts +114 -0
- package/src/scripts/_lib/forge_reader.ts +558 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +32 -0
- package/src/scripts/_lib/graph_feeder_record.ts +214 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_env_write.ts +111 -0
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +538 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/llm_proposer_transport.ts +14 -4
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +189 -0
- package/src/scripts/_lib/obligations.ts +119 -8
- package/src/scripts/_lib/probe_inputs.ts +271 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/read_surface_scan.ts +322 -0
- package/src/scripts/_lib/release_highlights.ts +2 -1
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +77 -16
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/shell_write_shapes.ts +108 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structural_hiding.ts +622 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/thin_rules.ts +540 -0
- package/src/scripts/_lib/touched_file_quality.ts +434 -0
- package/src/scripts/_lib/turn_end_refusals.ts +600 -4
- package/src/scripts/_lib/turn_end_transcript.ts +216 -0
- package/src/scripts/_lib/verification_command.ts +324 -0
- package/src/scripts/_lib/verification_evidence.ts +647 -0
- package/src/scripts/_lib/verify_clause.ts +280 -0
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +1 -1
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/team_dispatch.ts +4 -0
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/before_complete_hook.ts +573 -25
- package/src/scripts/bench_hook_latency.ts +412 -5
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/cache_realization_report.ts +61 -1
- package/src/scripts/check_argument_hint.ts +287 -0
- package/src/scripts/check_claims.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +38 -9
- package/src/scripts/check_enforcement_matrix.ts +621 -22
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_finding_dispositions.ts +138 -7
- package/src/scripts/check_generator_sync.ts +71 -0
- package/src/scripts/check_hook_bundle_composition.ts +370 -0
- package/src/scripts/check_host_docs_digest.ts +786 -0
- package/src/scripts/check_host_format_column.ts +559 -0
- package/src/scripts/check_input_references.ts +228 -0
- package/src/scripts/check_inputs_parity.ts +235 -0
- package/src/scripts/check_kernel_edit_ratified.ts +89 -9
- package/src/scripts/check_kill_switch_table.ts +294 -0
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_placeholder_drift.ts +221 -0
- package/src/scripts/check_read_surface_coverage.ts +515 -0
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_holds.ts +2 -1
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_routing_coverage.ts +378 -11
- package/src/scripts/check_standing_rule_delivery.ts +33 -13
- package/src/scripts/check_static_parity.ts +17 -0
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +145 -19
- package/src/scripts/closure_scan.ts +115 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/compile_hook_manifest.ts +90 -14
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/external_sources_denylist.json +29 -4
- package/src/scripts/generate_host_cost_table.ts +1 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +192 -14
- package/src/scripts/hooks/block_config_weakening.ts +294 -4
- package/src/scripts/hooks/block_kernel_rule_writes.ts +9 -78
- package/src/scripts/hooks/block_plumbing_writes.ts +342 -0
- package/src/scripts/hooks/bundle_integrity.ts +291 -0
- package/src/scripts/hooks/chain_nudge_hook.ts +1 -2
- package/src/scripts/hooks/code_graph_context_hook.ts +253 -25
- package/src/scripts/hooks/comment_discipline_hook.ts +1 -3
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/concern_timings.ts +94 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +1 -2
- package/src/scripts/hooks/design_pass_hook.ts +100 -9
- package/src/scripts/hooks/design_slop_hook.ts +1 -2
- package/src/scripts/hooks/dispatch_hook.ts +77 -71
- package/src/scripts/hooks/edit_shape_hook.ts +1 -2
- package/src/scripts/hooks/end_review_nudge_hook.ts +1 -1
- package/src/scripts/hooks/evidence_independence.ts +1 -2
- package/src/scripts/hooks/exit_codes.ts +129 -0
- package/src/scripts/hooks/host_lowering.json +1 -0
- package/src/scripts/hooks/host_lowering.ts +146 -3
- package/src/scripts/hooks/host_lowering.yaml +262 -46
- package/src/scripts/hooks/host_semantics.ts +30 -2
- package/src/scripts/hooks/injection_budget.ts +9 -2
- package/src/scripts/hooks/interruption_ledger_hook.ts +1 -2
- package/src/scripts/hooks/journal_record_hook.ts +1 -2
- package/src/scripts/hooks/memory_recall_hook.ts +1 -2
- package/src/scripts/hooks/obligation_settle_hook.ts +151 -5
- package/src/scripts/hooks/one_question_per_ask_hook.ts +43 -6
- package/src/scripts/hooks/orchestration_record_hook.ts +1 -2
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/permission_gate.ts +1 -2
- package/src/scripts/hooks/push_settle_hook.ts +1 -3
- package/src/scripts/hooks/replay_hook.ts +2 -1
- package/src/scripts/hooks/reread_guard_hook.ts +1 -2
- package/src/scripts/hooks/review_baseline_hook.ts +1 -2
- package/src/scripts/hooks/rtk_wrap_hook.ts +1 -2
- package/src/scripts/hooks/rule_inject_hook.ts +765 -68
- package/src/scripts/hooks/run_continuation_hook.ts +13 -15
- package/src/scripts/hooks/skill_route_hook.ts +1 -2
- package/src/scripts/hooks/source_first_gate_hook.ts +1 -2
- package/src/scripts/hooks/spawn_guard_shadow_hook.ts +1 -2
- package/src/scripts/hooks/subagent_ledger_hook.ts +1 -2
- package/src/scripts/hooks/suggestion_capture_hook.ts +1 -1
- package/src/scripts/hooks/table_fingerprint.ts +70 -0
- package/src/scripts/hooks/telemetry_flush_hook.ts +1 -2
- package/src/scripts/hooks/telemetry_usage_hook.ts +65 -10
- package/src/scripts/hooks/tool_result_bytes_hook.ts +93 -9
- package/src/scripts/hooks/turn_end_gate_hook.ts +421 -324
- package/src/scripts/hooks/ui_route_nudge_hook.ts +1 -2
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install.ts +75 -189
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_deny_text.ts +419 -0
- package/src/scripts/lint_exit_codes.ts +262 -0
- package/src/scripts/lint_hook_manifest.ts +108 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +63 -5
- package/src/scripts/lint_roadmap_blockers.ts +78 -12
- package/src/scripts/lint_rule_law_section.ts +517 -0
- package/src/scripts/lint_skill_descriptions.ts +1 -1
- package/src/scripts/lint_skill_scripts_readonly.ts +1 -1
- package/src/scripts/lint_workflow_security.ts +161 -5
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/mcp_server/prompts.ts +142 -1
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +515 -53
- package/src/scripts/measure_turn_end_gate.ts +193 -0
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/memory_lookup.ts +1 -1
- package/src/scripts/model_rule_injection.ts +52 -5
- package/src/scripts/prepack-check.mjs +53 -0
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/release_publication.ts +13 -1
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +157 -0
- package/src/scripts/report_invocation_surface.ts +383 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_obligation_carriers.ts +699 -0
- package/src/scripts/report_obligation_mechanism.ts +230 -0
- package/src/scripts/report_obligation_writer_split.ts +236 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_menu_census.ts +208 -0
- package/src/scripts/report_skill_ranker_confusion.ts +442 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/roadmap_verify_share.ts +213 -0
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/command.schema.json +44 -0
- package/src/scripts/schemas/review-findings.schema.json +7 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +44 -0
- package/src/scripts/self_review_gate.ts +421 -17
- package/src/scripts/semantic_parity_census.ts +314 -0
- package/src/scripts/session_eol_report.ts +54 -1
- package/src/scripts/skill_tools/score_skill_relevance.ts +290 -25
- package/src/scripts/sweep_dead_scan_roots.ts +1 -1
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/trigger_eval_grandfather.json +0 -7
- package/src/scripts/trigger_eval_rotation.ts +122 -27
- package/src/scripts/ui_conformance_probe.ts +156 -4
- package/src/scripts/update_prices.ts +85 -3
- package/src/scripts/write_bundle_digest.mjs +74 -0
- package/src/server/schemas/settings.ts +41 -0
- package/src/shared/skillRanking.ts +117 -11
- package/src/templates/consumer-settings/ONBOARDING.md +1 -0
- package/dist/cli-delegate/chunk-FXYUYU4N.js +0 -4416
- package/dist/cli-delegate/chunk-QJWAUMJW.js +0 -1072
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/dist/ui/assets/index-DH1xc62v.js +0 -29
- package/dist/ui/assets/index-jSfMW8_t.css +0 -1
|
@@ -0,0 +1,657 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Classify every tracked file in the evidence tree as hot, warm or cold —
|
|
4
|
+
* by reference, never by age.
|
|
5
|
+
*
|
|
6
|
+
* `road-to-an-evidence-tree-with-a-cold-half` Phase 1.1 and 3.2. The tree is
|
|
7
|
+
* 32 MB over 1,300-odd files and its retention is `permanent`, so the question
|
|
8
|
+
* "which of these does anything still read" has to be answerable by a rule a
|
|
9
|
+
* reader can check, not by a judgement someone made once.
|
|
10
|
+
*
|
|
11
|
+
* THE RULE, stated before the code so it can be argued with:
|
|
12
|
+
*
|
|
13
|
+
* cold — no tracked file anywhere in the repository references its path or,
|
|
14
|
+
* when the filename is unique repo-wide, its bare filename.
|
|
15
|
+
* warm — referenced only by archived or skipped roadmaps, or only by other
|
|
16
|
+
* files that are themselves inside the evidence tree.
|
|
17
|
+
* hot — referenced by at least one live tracked file outside the evidence
|
|
18
|
+
* tree, OR sitting under a directory a gate enumerates as its corpus.
|
|
19
|
+
*
|
|
20
|
+
* Two properties of that rule carry the whole instrument.
|
|
21
|
+
*
|
|
22
|
+
* FIRST, cold means zero inbound references from ANYWHERE, including from
|
|
23
|
+
* inside the evidence tree. The weaker reading — "nothing outside evidence
|
|
24
|
+
* cites it" — would class a file that a hot analysis document links to as cold,
|
|
25
|
+
* and moving it would break `check_references`, which scans all of `agents/`.
|
|
26
|
+
* Zero-inbound is the only definition under which the cold set is safe to move
|
|
27
|
+
* without rewriting a single link.
|
|
28
|
+
*
|
|
29
|
+
* SECOND, matching is deliberately PERMISSIVE and the error direction is
|
|
30
|
+
* deliberately one-way. A citation form this misses classes a live file cold,
|
|
31
|
+
* which is the expensive mistake; a loose match classes a dead file hot, which
|
|
32
|
+
* only means it stays where it is. So tokens are harvested from prose, from
|
|
33
|
+
* JSON values and from code alike — not only from backticks or markdown links —
|
|
34
|
+
* and a bare filename counts whenever no other tracked file in the repository
|
|
35
|
+
* shares it.
|
|
36
|
+
*
|
|
37
|
+
* WHAT IT DELIBERATELY DOES NOT DO. It never deletes, never moves, never
|
|
38
|
+
* refuses. Exit is 0 even when the cold set grows, because a census that can
|
|
39
|
+
* fail is a gate, and a gate over a corpus nobody has pruned yet would be a
|
|
40
|
+
* refusal with no remedy behind it. `--since` names what became cold since a
|
|
41
|
+
* previous report; it still exits 0.
|
|
42
|
+
*
|
|
43
|
+
* Usage:
|
|
44
|
+
* ./scripts-run src/scripts/report_evidence_temperature
|
|
45
|
+
* ./scripts-run src/scripts/report_evidence_temperature --write
|
|
46
|
+
* ./scripts-run src/scripts/report_evidence_temperature --json
|
|
47
|
+
* ./scripts-run src/scripts/report_evidence_temperature --since <report.md>
|
|
48
|
+
* ./scripts-run src/scripts/report_evidence_temperature --write --since latest
|
|
49
|
+
*
|
|
50
|
+
* Exit codes: 0 always, except 2 for a usage error.
|
|
51
|
+
*/
|
|
52
|
+
|
|
53
|
+
import { execFileSync } from 'node:child_process';
|
|
54
|
+
import * as fs from 'node:fs';
|
|
55
|
+
import * as path from 'node:path';
|
|
56
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
57
|
+
|
|
58
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
59
|
+
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
60
|
+
|
|
61
|
+
/** The tree this census is about, repo-relative and POSIX-separated. */
|
|
62
|
+
export const EVIDENCE_ROOT = 'agents/evidence';
|
|
63
|
+
|
|
64
|
+
/** Where `--write` puts the report, relative to the repository root. */
|
|
65
|
+
export const REPORT_DIR = 'agents/evidence/analysis';
|
|
66
|
+
|
|
67
|
+
/** Reports of this family are excluded from the reference index — see `isSelfReport`. */
|
|
68
|
+
const REPORT_PREFIX = 'evidence-temperature-';
|
|
69
|
+
|
|
70
|
+
/** Extension of a census report. Shared by the writer and by `--since latest`. */
|
|
71
|
+
const REPORT_EXT = '.md';
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* The day a census is stamped with. UTC, so a run either side of local midnight
|
|
75
|
+
* names the same report.
|
|
76
|
+
*/
|
|
77
|
+
export function censusDateStamp(now: Date = new Date()): string {
|
|
78
|
+
return now.toISOString().slice(0, 10);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* The path `--write` composes for a census taken on `generatedAt`. Every part of
|
|
83
|
+
* the name comes from a constant this module shares — `REPORT_DIR`,
|
|
84
|
+
* `REPORT_PREFIX`, `REPORT_EXT` — or from `censusDateStamp`, and
|
|
85
|
+
* `latestReportBefore` filters on the same two, so discovery and writing cannot
|
|
86
|
+
* drift apart. The release-PR shape gate's test goes through this builder too.
|
|
87
|
+
* Writer and gate holding independent spellings of this name is what broke
|
|
88
|
+
* `task release` for 16.3.0.
|
|
89
|
+
*/
|
|
90
|
+
export function defaultReportPath(generatedAt: string): string {
|
|
91
|
+
return path.posix.join(REPORT_DIR, `${REPORT_PREFIX}${generatedAt}${REPORT_EXT}`);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** File extensions a path token may end in. Anything else is not a path to us. */
|
|
95
|
+
const PATH_EXTENSIONS = [
|
|
96
|
+
'md',
|
|
97
|
+
'json',
|
|
98
|
+
'jsonl',
|
|
99
|
+
'yaml',
|
|
100
|
+
'yml',
|
|
101
|
+
'csv',
|
|
102
|
+
'txt',
|
|
103
|
+
'patch',
|
|
104
|
+
'sh',
|
|
105
|
+
'mjs',
|
|
106
|
+
'ts',
|
|
107
|
+
'tsx',
|
|
108
|
+
'py',
|
|
109
|
+
'png',
|
|
110
|
+
'svg',
|
|
111
|
+
'html',
|
|
112
|
+
] as const;
|
|
113
|
+
|
|
114
|
+
const TOKEN_RE = new RegExp(
|
|
115
|
+
String.raw`(?:[A-Za-z0-9_.@+-]+\/)*[A-Za-z0-9_.@+-]+\.(?:${PATH_EXTENSIONS.join('|')})(?![A-Za-z0-9])`,
|
|
116
|
+
'g',
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
/** Largest file this will read looking for references. Bigger ones are skipped. */
|
|
120
|
+
const MAX_READ_BYTES = 8 * 1024 * 1024;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Directories inside the evidence tree that a gate enumerates as its corpus.
|
|
124
|
+
*
|
|
125
|
+
* Membership is the point: these gates do not merely happen to read a file they
|
|
126
|
+
* were handed, they walk the directory and derive a verdict from what is in it.
|
|
127
|
+
* A file that left one of these roots would leave the gate scanning fewer files
|
|
128
|
+
* and still exiting green, which is the gate-that-scans-nothing shape this
|
|
129
|
+
* repository refuses elsewhere. So every file beneath them is hot by that fact
|
|
130
|
+
* alone, whatever cites it — this is Phase 1.2's clause that a gate relying on a
|
|
131
|
+
* file reclassifies it hot, applied mechanically instead of by hand.
|
|
132
|
+
*
|
|
133
|
+
* `agents/` as a whole is NOT in this list even though `check_references` scans
|
|
134
|
+
* it, because that gate validates the links a file contains rather than
|
|
135
|
+
* requiring the file to exist: moving a file to another directory under
|
|
136
|
+
* `agents/` keeps it inside that scan root and changes nothing for it.
|
|
137
|
+
*/
|
|
138
|
+
export const GATE_SCAN_ROOTS: ReadonlyArray<{
|
|
139
|
+
readonly dir: string;
|
|
140
|
+
readonly gates: readonly string[];
|
|
141
|
+
readonly loses: string;
|
|
142
|
+
}> = [
|
|
143
|
+
{
|
|
144
|
+
dir: 'reviews',
|
|
145
|
+
gates: [
|
|
146
|
+
'check_review_dispositions',
|
|
147
|
+
'check_review_schema',
|
|
148
|
+
'check_completion_review',
|
|
149
|
+
'probe_review_binding_drift',
|
|
150
|
+
'lint_judge_prompt_expectation',
|
|
151
|
+
],
|
|
152
|
+
loses:
|
|
153
|
+
'Archived round records are validated in place — a finding left open after it was fixed ' +
|
|
154
|
+
'is caught only by walking this directory. Five gates read it, one of them specifically ' +
|
|
155
|
+
'for the archived rounds, so a file leaving the root is coverage leaving with it.',
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
dir: 'release-findings',
|
|
159
|
+
gates: ['check_review_schema', 'check_release_pr_shape', 'release_publication'],
|
|
160
|
+
loses:
|
|
161
|
+
'Per-release ledgers are read by version, and the release PR shape check asserts one is ' +
|
|
162
|
+
'present. A missing ledger is indistinguishable from a release that produced no findings.',
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
dir: 'archived-skills',
|
|
166
|
+
gates: ['lint_archived_skills'],
|
|
167
|
+
loses: 'The archive note for a retired skill is the record that it was retired rather than lost.',
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
dir: 'ratifications',
|
|
171
|
+
gates: ['_lib/ratification_artifact'],
|
|
172
|
+
loses: 'A governance diff is pushed only against a ratification artefact found in this directory.',
|
|
173
|
+
},
|
|
174
|
+
];
|
|
175
|
+
|
|
176
|
+
/** Paths that are structural rather than evidential — an index or a directory keeper. */
|
|
177
|
+
function isStructural(rel: string): boolean {
|
|
178
|
+
const base = path.posix.basename(rel);
|
|
179
|
+
return base === '.gitkeep' || base === 'README.md';
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/** True for the census reports themselves, which name every cold file and would otherwise warm them all. */
|
|
183
|
+
export function isSelfReport(rel: string): boolean {
|
|
184
|
+
return path.posix.basename(rel).startsWith(REPORT_PREFIX);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export type Temperature = 'hot' | 'warm' | 'cold';
|
|
188
|
+
export type ReferrerClass = 'live' | 'archived' | 'evidence';
|
|
189
|
+
export type Reason = 'scan-root' | 'structural' | 'live-citation' | 'historical-citation' | 'uncited';
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Which kind of thing a referring file is.
|
|
193
|
+
*
|
|
194
|
+
* `archived` is the class that makes warm a useful word: an archived or skipped
|
|
195
|
+
* roadmap is a record of work that finished, so a citation from one says the
|
|
196
|
+
* file mattered once, not that anything reads it now.
|
|
197
|
+
*/
|
|
198
|
+
export function referrerClass(rel: string): ReferrerClass {
|
|
199
|
+
if (rel.startsWith(`${EVIDENCE_ROOT}/`)) return 'evidence';
|
|
200
|
+
if (rel.startsWith('agents/roadmaps/archive/')) return 'archived';
|
|
201
|
+
if (rel.startsWith('agents/roadmaps/skipped/')) return 'archived';
|
|
202
|
+
if (rel.startsWith('agents/tmp.old/')) return 'archived';
|
|
203
|
+
return 'live';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** Strip leading `./` and `../` segments so a relative link and an absolute path share one key. */
|
|
207
|
+
export function normaliseToken(token: string): string {
|
|
208
|
+
const parts = token.split('/').filter((p) => p !== '' && p !== '.' && p !== '..');
|
|
209
|
+
return parts.join('/');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Every path-shaped token in a blob of text, normalised, deduplicated. */
|
|
213
|
+
export function extractPathTokens(text: string): string[] {
|
|
214
|
+
const out = new Set<string>();
|
|
215
|
+
for (const m of text.matchAll(TOKEN_RE)) {
|
|
216
|
+
const norm = normaliseToken(m[0]);
|
|
217
|
+
if (norm !== '') out.add(norm);
|
|
218
|
+
}
|
|
219
|
+
return [...out];
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** The keys under which a tracked path may be cited: the full path and each of its suffixes. */
|
|
223
|
+
export function citationKeys(rel: string): string[] {
|
|
224
|
+
const parts = rel.split('/');
|
|
225
|
+
const keys: string[] = [];
|
|
226
|
+
for (let i = 0; i < parts.length; i += 1) keys.push(parts.slice(i).join('/'));
|
|
227
|
+
return keys;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export interface Reference {
|
|
231
|
+
readonly from: string;
|
|
232
|
+
readonly cls: ReferrerClass;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function classify(
|
|
236
|
+
refs: readonly Reference[],
|
|
237
|
+
scanRoot: string | null,
|
|
238
|
+
structural: boolean,
|
|
239
|
+
): { temperature: Temperature; reason: Reason } {
|
|
240
|
+
if (scanRoot !== null) return { temperature: 'hot', reason: 'scan-root' };
|
|
241
|
+
if (structural) return { temperature: 'hot', reason: 'structural' };
|
|
242
|
+
if (refs.some((r) => r.cls === 'live')) return { temperature: 'hot', reason: 'live-citation' };
|
|
243
|
+
if (refs.length > 0) return { temperature: 'warm', reason: 'historical-citation' };
|
|
244
|
+
return { temperature: 'cold', reason: 'uncited' };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** The gate scan root a path sits under, or null. */
|
|
248
|
+
export function scanRootFor(rel: string): string | null {
|
|
249
|
+
for (const root of GATE_SCAN_ROOTS) {
|
|
250
|
+
if (rel.startsWith(`${EVIDENCE_ROOT}/${root.dir}/`)) return root.dir;
|
|
251
|
+
}
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
export interface EvidenceFile {
|
|
256
|
+
readonly path: string;
|
|
257
|
+
readonly bytes: number;
|
|
258
|
+
readonly addedOn: string | null;
|
|
259
|
+
readonly temperature: Temperature;
|
|
260
|
+
readonly reason: Reason;
|
|
261
|
+
readonly referrers: readonly string[];
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export interface Census {
|
|
265
|
+
readonly generatedAt: string;
|
|
266
|
+
readonly commit: string;
|
|
267
|
+
readonly files: readonly EvidenceFile[];
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function git(args: readonly string[], root: string): string {
|
|
271
|
+
return execFileSync('git', [...args], { cwd: root, encoding: 'utf8', maxBuffer: 256 * 1024 * 1024 });
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/** Earliest add date per path, from one walk of the history. */
|
|
275
|
+
export function parseAddDates(log: string): Map<string, string> {
|
|
276
|
+
const dates = new Map<string, string>();
|
|
277
|
+
let current: string | null = null;
|
|
278
|
+
for (const line of log.split('\n')) {
|
|
279
|
+
if (line.startsWith('@')) {
|
|
280
|
+
current = line.slice(1).trim();
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const file = line.trim();
|
|
284
|
+
if (file === '' || current === null) continue;
|
|
285
|
+
// git log walks newest-first, so the LAST sighting is the earliest add.
|
|
286
|
+
dates.set(file, current);
|
|
287
|
+
}
|
|
288
|
+
return dates;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function readTextOrNull(abs: string): string | null {
|
|
292
|
+
let stat: fs.Stats;
|
|
293
|
+
try {
|
|
294
|
+
stat = fs.statSync(abs);
|
|
295
|
+
} catch {
|
|
296
|
+
return null;
|
|
297
|
+
}
|
|
298
|
+
if (!stat.isFile() || stat.size > MAX_READ_BYTES) return null;
|
|
299
|
+
try {
|
|
300
|
+
return fs.readFileSync(abs, 'utf8');
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
export function buildCensus(root: string): Census {
|
|
307
|
+
const tracked = git(['ls-files', '-z'], root)
|
|
308
|
+
.split('\0')
|
|
309
|
+
.filter((p) => p !== '');
|
|
310
|
+
const evidence = tracked.filter((p) => p.startsWith(`${EVIDENCE_ROOT}/`));
|
|
311
|
+
|
|
312
|
+
const basenameCount = new Map<string, number>();
|
|
313
|
+
for (const p of tracked) {
|
|
314
|
+
const b = path.posix.basename(p);
|
|
315
|
+
basenameCount.set(b, (basenameCount.get(b) ?? 0) + 1);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// token -> referring files. Self-reports are excluded at index time, not at
|
|
319
|
+
// lookup time, so a report can never warm the set it was written about.
|
|
320
|
+
const index = new Map<string, Set<string>>();
|
|
321
|
+
for (const rel of tracked) {
|
|
322
|
+
if (isSelfReport(rel)) continue;
|
|
323
|
+
const text = readTextOrNull(path.join(root, rel));
|
|
324
|
+
if (text === null) continue;
|
|
325
|
+
for (const token of extractPathTokens(text)) {
|
|
326
|
+
let bucket = index.get(token);
|
|
327
|
+
if (bucket === undefined) {
|
|
328
|
+
bucket = new Set<string>();
|
|
329
|
+
index.set(token, bucket);
|
|
330
|
+
}
|
|
331
|
+
bucket.add(rel);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
const addDates = parseAddDates(
|
|
336
|
+
git(['log', '--diff-filter=A', '--name-only', '--date=short', '--format=@%ad', '--', EVIDENCE_ROOT], root),
|
|
337
|
+
);
|
|
338
|
+
|
|
339
|
+
const files: EvidenceFile[] = [];
|
|
340
|
+
for (const rel of evidence) {
|
|
341
|
+
const keys = citationKeys(rel);
|
|
342
|
+
const bare = keys[keys.length - 1];
|
|
343
|
+
const seen = new Map<string, ReferrerClass>();
|
|
344
|
+
for (const key of keys) {
|
|
345
|
+
// A bare filename counts only when nothing else in the tree shares it.
|
|
346
|
+
if (key === bare && (basenameCount.get(bare) ?? 0) > 1) continue;
|
|
347
|
+
for (const from of index.get(key) ?? []) {
|
|
348
|
+
if (from === rel) continue;
|
|
349
|
+
seen.set(from, referrerClass(from));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
const refs: Reference[] = [...seen].map(([from, cls]) => ({ from, cls }));
|
|
353
|
+
const { temperature, reason } = classify(refs, scanRootFor(rel), isStructural(rel));
|
|
354
|
+
let bytes = 0;
|
|
355
|
+
try {
|
|
356
|
+
bytes = fs.statSync(path.join(root, rel)).size;
|
|
357
|
+
} catch {
|
|
358
|
+
bytes = 0;
|
|
359
|
+
}
|
|
360
|
+
files.push({
|
|
361
|
+
path: rel,
|
|
362
|
+
bytes,
|
|
363
|
+
addedOn: addDates.get(rel) ?? null,
|
|
364
|
+
temperature,
|
|
365
|
+
reason,
|
|
366
|
+
referrers: refs.map((r) => r.from).sort(),
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
files.sort((a, b) => a.path.localeCompare(b.path));
|
|
370
|
+
|
|
371
|
+
let commit = 'unknown';
|
|
372
|
+
try {
|
|
373
|
+
commit = git(['rev-parse', '--short', 'HEAD'], root).trim();
|
|
374
|
+
} catch {
|
|
375
|
+
commit = 'unknown';
|
|
376
|
+
}
|
|
377
|
+
return { generatedAt: censusDateStamp(), commit, files };
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export interface Totals {
|
|
381
|
+
readonly files: number;
|
|
382
|
+
readonly bytes: number;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export function totalsBy(files: readonly EvidenceFile[], key: (f: EvidenceFile) => string): Map<string, Totals> {
|
|
386
|
+
const out = new Map<string, Totals>();
|
|
387
|
+
for (const f of files) {
|
|
388
|
+
const k = key(f);
|
|
389
|
+
const prev = out.get(k) ?? { files: 0, bytes: 0 };
|
|
390
|
+
out.set(k, { files: prev.files + 1, bytes: prev.bytes + f.bytes });
|
|
391
|
+
}
|
|
392
|
+
return out;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** Top-level directory under the evidence root, or `(root)` for a file directly in it. */
|
|
396
|
+
export function subtreeOf(rel: string): string {
|
|
397
|
+
const rest = rel.slice(EVIDENCE_ROOT.length + 1);
|
|
398
|
+
const slash = rest.indexOf('/');
|
|
399
|
+
return slash === -1 ? '(root)' : rest.slice(0, slash);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
function mib(bytes: number): string {
|
|
403
|
+
return `${(bytes / (1024 * 1024)).toFixed(2)} MiB`;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* The newest existing report other than `exclude`, or null when there is none.
|
|
408
|
+
*
|
|
409
|
+
* `--since latest` resolves through this so the release pipeline needs no date
|
|
410
|
+
* arithmetic: the names sort lexicographically because they carry an ISO date,
|
|
411
|
+
* and the file about to be written is excluded so a same-day re-run compares
|
|
412
|
+
* against an earlier report rather than against itself.
|
|
413
|
+
*
|
|
414
|
+
* It picks the newest OTHER report, not the newest report of an earlier
|
|
415
|
+
* release. A release resumed across UTC midnight writes a second census, and
|
|
416
|
+
* that one's baseline is the first census of the same release — so its delta
|
|
417
|
+
* covers minutes, not a release. The heading it renders under says "since the
|
|
418
|
+
* previous report", which is exactly that, so nothing misreports; a reader
|
|
419
|
+
* wanting a release-wide delta compares against the earlier release by hand.
|
|
420
|
+
*/
|
|
421
|
+
export function latestReportBefore(names: readonly string[], exclude: string): string | null {
|
|
422
|
+
const candidates = names
|
|
423
|
+
.filter((n) => n.startsWith(REPORT_PREFIX) && n.endsWith(REPORT_EXT) && n !== exclude)
|
|
424
|
+
.sort();
|
|
425
|
+
return candidates.at(-1) ?? null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/** The cold paths a previous report listed, read back out of its own fenced block. */
|
|
429
|
+
export function coldPathsIn(reportText: string): Set<string> {
|
|
430
|
+
const out = new Set<string>();
|
|
431
|
+
for (const line of reportText.split('\n')) {
|
|
432
|
+
const t = line.trim();
|
|
433
|
+
if (t.startsWith(`${EVIDENCE_ROOT}/`)) out.add(t);
|
|
434
|
+
}
|
|
435
|
+
return out;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function renderReport(census: Census, previousCold: Set<string> | null): string {
|
|
439
|
+
const byTemp = totalsBy(census.files, (f) => f.temperature);
|
|
440
|
+
const cold = census.files.filter((f) => f.temperature === 'cold');
|
|
441
|
+
const warm = census.files.filter((f) => f.temperature === 'warm');
|
|
442
|
+
const all: Totals = { files: census.files.length, bytes: census.files.reduce((s, f) => s + f.bytes, 0) };
|
|
443
|
+
|
|
444
|
+
const L: string[] = [];
|
|
445
|
+
L.push('<!-- evidence-type: analysis -->');
|
|
446
|
+
L.push('');
|
|
447
|
+
L.push(`# Evidence temperature census — ${census.generatedAt}`);
|
|
448
|
+
L.push('');
|
|
449
|
+
L.push(
|
|
450
|
+
`Generated by \`./scripts-run src/scripts/report_evidence_temperature --write\` at \`${census.commit}\`. ` +
|
|
451
|
+
'Report-only: it classifies, and refuses nothing.',
|
|
452
|
+
);
|
|
453
|
+
L.push('');
|
|
454
|
+
L.push('## The rule');
|
|
455
|
+
L.push('');
|
|
456
|
+
L.push(
|
|
457
|
+
'Classification is by reference, never by age. An old file a contract cites is load-bearing; ' +
|
|
458
|
+
'a file added last week that nothing reads is not.',
|
|
459
|
+
);
|
|
460
|
+
L.push('');
|
|
461
|
+
L.push('| Class | Definition |');
|
|
462
|
+
L.push('|---|---|');
|
|
463
|
+
L.push(
|
|
464
|
+
'| cold | No tracked file anywhere in the repository references its path, or — when the filename is unique repo-wide — its bare filename. |',
|
|
465
|
+
);
|
|
466
|
+
L.push(
|
|
467
|
+
'| warm | Referenced only by archived or skipped roadmaps, or only by other files that are themselves under the evidence tree. |',
|
|
468
|
+
);
|
|
469
|
+
L.push(
|
|
470
|
+
'| hot | Referenced by at least one live tracked file outside the evidence tree, or sitting under a directory a gate enumerates as its corpus. |',
|
|
471
|
+
);
|
|
472
|
+
L.push('');
|
|
473
|
+
L.push(
|
|
474
|
+
'Cold means zero inbound references from anywhere, the evidence tree included. The weaker reading — ' +
|
|
475
|
+
'nothing outside evidence cites it — would class a file that a live analysis document links to as cold, ' +
|
|
476
|
+
'and moving it would break the reference checker, which scans the whole of `agents/`. Zero-inbound is the ' +
|
|
477
|
+
'only definition under which the cold set moves without rewriting one link.',
|
|
478
|
+
);
|
|
479
|
+
L.push('');
|
|
480
|
+
L.push(
|
|
481
|
+
'Matching is permissive on purpose and the error direction is one-way: a citation form the resolver misses ' +
|
|
482
|
+
'would class a live file cold, which is the expensive mistake, while a loose match only leaves a dead file ' +
|
|
483
|
+
'where it already is. Tokens are therefore read from prose, from JSON values and from code alike, not only ' +
|
|
484
|
+
'from backticks or links.',
|
|
485
|
+
);
|
|
486
|
+
L.push('');
|
|
487
|
+
L.push('## Totals');
|
|
488
|
+
L.push('');
|
|
489
|
+
L.push('| Class | Files | Bytes |');
|
|
490
|
+
L.push('|---|---:|---:|');
|
|
491
|
+
for (const t of ['hot', 'warm', 'cold'] as const) {
|
|
492
|
+
const v = byTemp.get(t) ?? { files: 0, bytes: 0 };
|
|
493
|
+
L.push(`| ${t} | ${v.files} | ${mib(v.bytes)} |`);
|
|
494
|
+
}
|
|
495
|
+
L.push(`| **all** | **${all.files}** | **${mib(all.bytes)}** |`);
|
|
496
|
+
L.push('');
|
|
497
|
+
L.push('## Why each file is where it is');
|
|
498
|
+
L.push('');
|
|
499
|
+
L.push('| Reason | Files | Bytes |');
|
|
500
|
+
L.push('|---|---:|---:|');
|
|
501
|
+
const byReason = totalsBy(census.files, (f) => f.reason);
|
|
502
|
+
for (const [reason, v] of [...byReason].sort((a, b) => b[1].files - a[1].files)) {
|
|
503
|
+
L.push(`| ${reason} | ${v.files} | ${mib(v.bytes)} |`);
|
|
504
|
+
}
|
|
505
|
+
L.push('');
|
|
506
|
+
L.push('## By subtree');
|
|
507
|
+
L.push('');
|
|
508
|
+
L.push('| Subtree | Files | Bytes | hot | warm | cold |');
|
|
509
|
+
L.push('|---|---:|---:|---:|---:|---:|');
|
|
510
|
+
const subtrees = [...new Set(census.files.map((f) => subtreeOf(f.path)))].sort();
|
|
511
|
+
for (const sub of subtrees) {
|
|
512
|
+
const inSub = census.files.filter((f) => subtreeOf(f.path) === sub);
|
|
513
|
+
const bytes = inSub.reduce((s, f) => s + f.bytes, 0);
|
|
514
|
+
const h = inSub.filter((f) => f.temperature === 'hot').length;
|
|
515
|
+
const w = inSub.filter((f) => f.temperature === 'warm').length;
|
|
516
|
+
const c = inSub.filter((f) => f.temperature === 'cold').length;
|
|
517
|
+
L.push(`| ${sub} | ${inSub.length} | ${mib(bytes)} | ${h} | ${w} | ${c} |`);
|
|
518
|
+
}
|
|
519
|
+
L.push('');
|
|
520
|
+
L.push('## Gates that scan the tree, and what each would lose');
|
|
521
|
+
L.push('');
|
|
522
|
+
L.push(
|
|
523
|
+
'A gate below enumerates its directory and derives a verdict from what is in it. Membership is the point, ' +
|
|
524
|
+
'so every file beneath one of these roots is hot by that fact alone — a gate that relies on a file ' +
|
|
525
|
+
'reclassifies it hot, applied mechanically rather than by hand. A file leaving one of these roots would ' +
|
|
526
|
+
'leave its gate scanning fewer files and still exiting green.',
|
|
527
|
+
);
|
|
528
|
+
L.push('');
|
|
529
|
+
L.push('| Scan root | Files | Gates reading it | What leaving the scan root would cost |');
|
|
530
|
+
L.push('|---|---:|---|---|');
|
|
531
|
+
for (const root of GATE_SCAN_ROOTS) {
|
|
532
|
+
const n = census.files.filter((f) => f.path.startsWith(`${EVIDENCE_ROOT}/${root.dir}/`)).length;
|
|
533
|
+
L.push(`| \`${root.dir}/\` | ${n} | ${root.gates.map((g) => `\`${g}\``).join(', ')} | ${root.loses} |`);
|
|
534
|
+
}
|
|
535
|
+
L.push('');
|
|
536
|
+
L.push(
|
|
537
|
+
'Four readers of the tree are deliberately absent from that table. `check_references` scans the whole of ' +
|
|
538
|
+
'`agents/` but validates the links a file contains rather than requiring the file to exist, so a file ' +
|
|
539
|
+
'moved to another directory under `agents/` stays inside its scan root and nothing changes for it. ' +
|
|
540
|
+
'`lint_evidence_artifacts` is diff-scoped against a base ref and fires on files added, not on files ' +
|
|
541
|
+
'present, so it gains and loses nothing from a move either. `check_council_references` and ' +
|
|
542
|
+
'`lint_hidden_unicode` name the analysis directory as an allowed prefix rather than as a corpus; a cold ' +
|
|
543
|
+
'location added beside it would need the same one-line entry in each, and that is the only edit either ' +
|
|
544
|
+
'of them needs.',
|
|
545
|
+
);
|
|
546
|
+
L.push('');
|
|
547
|
+
L.push('## The cold set');
|
|
548
|
+
L.push('');
|
|
549
|
+
L.push(
|
|
550
|
+
`${cold.length} tracked files, ${mib(cold.reduce((s, f) => s + f.bytes, 0))}, referenced by nothing. ` +
|
|
551
|
+
'Listed in full because the move that follows has to be checkable against a list, not against a count.',
|
|
552
|
+
);
|
|
553
|
+
L.push('');
|
|
554
|
+
L.push('```text');
|
|
555
|
+
for (const f of cold) L.push(f.path);
|
|
556
|
+
L.push('```');
|
|
557
|
+
L.push('');
|
|
558
|
+
L.push('## The warm set');
|
|
559
|
+
L.push('');
|
|
560
|
+
L.push(
|
|
561
|
+
`${warm.length} tracked files, ${mib(warm.reduce((s, f) => s + f.bytes, 0))}, cited only by archived work ` +
|
|
562
|
+
'or by other evidence. These are not candidates for a move: a citation inside the evidence tree is a link ' +
|
|
563
|
+
'that would break.',
|
|
564
|
+
);
|
|
565
|
+
L.push('');
|
|
566
|
+
if (previousCold !== null) {
|
|
567
|
+
const newly = cold.filter((f) => !previousCold.has(f.path));
|
|
568
|
+
L.push('## Newly cold since the previous report');
|
|
569
|
+
L.push('');
|
|
570
|
+
L.push(`${newly.length} file(s).`);
|
|
571
|
+
L.push('');
|
|
572
|
+
if (newly.length > 0) {
|
|
573
|
+
L.push('```text');
|
|
574
|
+
for (const f of newly) L.push(f.path);
|
|
575
|
+
L.push('```');
|
|
576
|
+
L.push('');
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
return L.join('\n');
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
function usage(): never {
|
|
583
|
+
process.stderr.write(
|
|
584
|
+
'usage: report_evidence_temperature [--write] [--out PATH] [--json] [--since REPORT.md] [--quiet]\n',
|
|
585
|
+
);
|
|
586
|
+
process.exit(2);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export function main(argv: readonly string[]): number {
|
|
590
|
+
let write = false;
|
|
591
|
+
let json = false;
|
|
592
|
+
let quiet = false;
|
|
593
|
+
let out: string | null = null;
|
|
594
|
+
let since: string | null = null;
|
|
595
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
596
|
+
const a = argv[i];
|
|
597
|
+
if (a === '--write') write = true;
|
|
598
|
+
else if (a === '--json') json = true;
|
|
599
|
+
else if (a === '--quiet') quiet = true;
|
|
600
|
+
else if (a === '--out') {
|
|
601
|
+
out = argv[i + 1] ?? null;
|
|
602
|
+
i += 1;
|
|
603
|
+
if (out === null) usage();
|
|
604
|
+
} else if (a === '--since') {
|
|
605
|
+
since = argv[i + 1] ?? null;
|
|
606
|
+
i += 1;
|
|
607
|
+
if (since === null) usage();
|
|
608
|
+
} else usage();
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const census = buildCensus(REPO_ROOT);
|
|
612
|
+
if (json) {
|
|
613
|
+
process.stdout.write(`${JSON.stringify(census, null, 2)}\n`);
|
|
614
|
+
return 0;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const dest = out ?? defaultReportPath(census.generatedAt);
|
|
618
|
+
|
|
619
|
+
let previousCold: Set<string> | null = null;
|
|
620
|
+
if (since === 'latest') {
|
|
621
|
+
// No previous report is a legitimate first run, never an error.
|
|
622
|
+
const dir = path.join(REPO_ROOT, REPORT_DIR);
|
|
623
|
+
const names = fs.existsSync(dir) ? fs.readdirSync(dir) : [];
|
|
624
|
+
const prev = latestReportBefore(names, path.posix.basename(dest));
|
|
625
|
+
previousCold = prev === null ? null : coldPathsIn(fs.readFileSync(path.join(dir, prev), 'utf8'));
|
|
626
|
+
} else if (since !== null) {
|
|
627
|
+
const abs = path.isAbsolute(since) ? since : path.join(REPO_ROOT, since);
|
|
628
|
+
if (!fs.existsSync(abs)) {
|
|
629
|
+
process.stderr.write(`--since: no such report: ${since}\n`);
|
|
630
|
+
return 2;
|
|
631
|
+
}
|
|
632
|
+
previousCold = coldPathsIn(fs.readFileSync(abs, 'utf8'));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const body = renderReport(census, previousCold);
|
|
636
|
+
if (write) {
|
|
637
|
+
const abs = path.isAbsolute(dest) ? dest : path.join(REPO_ROOT, dest);
|
|
638
|
+
fs.mkdirSync(path.dirname(abs), { recursive: true });
|
|
639
|
+
fs.writeFileSync(abs, `${body}\n`, 'utf8');
|
|
640
|
+
if (!quiet) process.stdout.write(`wrote ${path.relative(REPO_ROOT, abs)}\n`);
|
|
641
|
+
} else if (!quiet) {
|
|
642
|
+
process.stdout.write(`${body}\n`);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
const byTemp = totalsBy(census.files, (f) => f.temperature);
|
|
646
|
+
const line = (['hot', 'warm', 'cold'] as const)
|
|
647
|
+
.map((t) => `${t}=${(byTemp.get(t) ?? { files: 0, bytes: 0 }).files}`)
|
|
648
|
+
.join(' · ');
|
|
649
|
+
process.stdout.write(`evidence temperature · ${census.files.length} tracked files · ${line}\n`);
|
|
650
|
+
return 0;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
if (process.argv[1] !== undefined && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
654
|
+
// `process.exitCode`, never `process.exit`: the JSON mode writes megabytes
|
|
655
|
+
// and an immediate exit truncates a pipe mid-string.
|
|
656
|
+
process.exitCode = main(process.argv.slice(2));
|
|
657
|
+
}
|