@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
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
* ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus routing-matrix
|
|
44
44
|
* ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus all
|
|
45
45
|
* ./scripts-run src/scripts/measure_skill_ranker_baseline --ranker keyword-v2
|
|
46
|
+
* ./scripts-run src/scripts/measure_skill_ranker_baseline --corpus routing-matrix --slice sealed
|
|
46
47
|
*/
|
|
47
48
|
|
|
48
49
|
import * as fs from 'node:fs';
|
|
@@ -50,6 +51,7 @@ import * as path from 'node:path';
|
|
|
50
51
|
import { fileURLToPath } from 'node:url';
|
|
51
52
|
|
|
52
53
|
import { wilsonInterval } from './_lib/capture_rate.js';
|
|
54
|
+
import { fnv1a } from './_lib/fnv.js';
|
|
53
55
|
import { rank } from './skill_tools/score_skill_relevance.js';
|
|
54
56
|
import type { RankOptions } from '../shared/skillRanking.js';
|
|
55
57
|
|
|
@@ -204,6 +206,84 @@ export function promptsForCorpus(corpus: CorpusName, repo = REPO): LabelledPromp
|
|
|
204
206
|
return [...readLabelledPrompts(repo), ...readMatrixLabelledPrompts(repo)];
|
|
205
207
|
}
|
|
206
208
|
|
|
209
|
+
/**
|
|
210
|
+
* Which half of the corpus a row belongs to.
|
|
211
|
+
*
|
|
212
|
+
* `road-to-a-ranker-that-routes` 1.2. Tuning and reading on one corpus of 390
|
|
213
|
+
* overfits it: a signal chosen because it lifted the number on the rows it was
|
|
214
|
+
* chosen from will lift the number on exactly those rows, and the interval
|
|
215
|
+
* printed beside it then describes the fitting rather than the ranker. Every
|
|
216
|
+
* lift claim in Phases 2 and 3 is read on `sealed` only.
|
|
217
|
+
*/
|
|
218
|
+
export type SliceName = 'all' | 'tuning' | 'sealed';
|
|
219
|
+
export const SLICE_NAMES: readonly SliceName[] = ['all', 'tuning', 'sealed'];
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* One row in five — nominally 20 %, measured 19.2 % (75 of 390) on the live
|
|
223
|
+
* corpus, because a hash is not a shuffle and the exact count is a property of
|
|
224
|
+
* the ids.
|
|
225
|
+
*
|
|
226
|
+
* Stated as a modulus rather than a fraction because the partition has to be a
|
|
227
|
+
* pure function of the id: a share applied to a shuffled list would move every
|
|
228
|
+
* row the moment a prompt is added, and the sealed slice would stop being the
|
|
229
|
+
* same slice between two runs of the same measurement.
|
|
230
|
+
*/
|
|
231
|
+
export const SEALED_MODULUS = 5;
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* The slice one case id falls in — deterministic and order-independent.
|
|
235
|
+
*
|
|
236
|
+
* STABILITY UNDER CORPUS CHANGE IS PARTIAL, and an earlier version of this block
|
|
237
|
+
* claimed otherwise. The matrix id is `rule#section[ordinal]` and the ordinal is
|
|
238
|
+
* POSITIONAL — it counts `- prompt:` entries within a section. So:
|
|
239
|
+
*
|
|
240
|
+
* - APPENDING a prompt to the end of a section is stable: every existing row
|
|
241
|
+
* keeps its ordinal, its id, and its side of the seal. This is the workflow
|
|
242
|
+
* the matrix README documents, which is why the partition survives the
|
|
243
|
+
* release-to-release growth the `history` array is for.
|
|
244
|
+
* - INSERTING or DELETING mid-section renumbers every later row in THAT
|
|
245
|
+
* section, which re-hashes them and moves roughly one in five across the
|
|
246
|
+
* seal.
|
|
247
|
+
*
|
|
248
|
+
* A content key (`rule|section|prompt`) would close the second case and is the
|
|
249
|
+
* right shape for the next corpus version. It is deliberately NOT applied here:
|
|
250
|
+
* the signals in Phase 2 were chosen by reading the TUNING rows of this
|
|
251
|
+
* partition, so re-drawing the boundary now would move ~20 % of those rows into
|
|
252
|
+
* the sealed slice and silently contaminate the very claims the seal exists to
|
|
253
|
+
* protect. Changing a partition after choosing on it is worse than a partially
|
|
254
|
+
* stable partition, so the key moves when the corpus does, not before.
|
|
255
|
+
*/
|
|
256
|
+
export function sliceForId(id: string): Exclude<SliceName, 'all'> {
|
|
257
|
+
return fnv1a(id) % SEALED_MODULUS === 0 ? 'sealed' : 'tuning';
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** The rows of `prompts` that fall in `slice`; `all` is the identity. */
|
|
261
|
+
export function partitionBySlice<T extends { id: string }>(
|
|
262
|
+
prompts: readonly T[],
|
|
263
|
+
slice: SliceName,
|
|
264
|
+
): T[] {
|
|
265
|
+
if (slice === 'all') return [...prompts];
|
|
266
|
+
return prompts.filter((p) => sliceForId(p.id) === slice);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** How the corpus divides, printed on every run so a reader never assumes it. */
|
|
270
|
+
export interface SliceSizes {
|
|
271
|
+
all: number;
|
|
272
|
+
tuning: number;
|
|
273
|
+
sealed: number;
|
|
274
|
+
sealed_modulus: number;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function sliceSizes(prompts: readonly { id: string }[]): SliceSizes {
|
|
278
|
+
const sealed = prompts.filter((p) => sliceForId(p.id) === 'sealed').length;
|
|
279
|
+
return {
|
|
280
|
+
all: prompts.length,
|
|
281
|
+
tuning: prompts.length - sealed,
|
|
282
|
+
sealed,
|
|
283
|
+
sealed_modulus: SEALED_MODULUS,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
207
287
|
/**
|
|
208
288
|
* The packs a skill declares, read from its own `packs:` frontmatter block.
|
|
209
289
|
*
|
|
@@ -278,6 +358,10 @@ export interface Interval {
|
|
|
278
358
|
|
|
279
359
|
export interface AccuracyArm {
|
|
280
360
|
corpus: CorpusName;
|
|
361
|
+
/** Which half of the corpus this arm read. `all` is the whole of it. */
|
|
362
|
+
slice: SliceName;
|
|
363
|
+
/** The division of the FULL corpus, printed whichever slice was read. */
|
|
364
|
+
slice_sizes: SliceSizes;
|
|
281
365
|
corpus_prompts: number;
|
|
282
366
|
top1: number;
|
|
283
367
|
top1_ci95: Interval;
|
|
@@ -350,8 +434,11 @@ export function measureAccuracy(opts: {
|
|
|
350
434
|
repo: string;
|
|
351
435
|
skillsDir: string;
|
|
352
436
|
rankOpts: RankOptions;
|
|
437
|
+
slice?: SliceName;
|
|
353
438
|
}): AccuracyArm {
|
|
354
|
-
const
|
|
439
|
+
const slice = opts.slice ?? 'all';
|
|
440
|
+
const whole = promptsForCorpus(opts.corpus, opts.repo);
|
|
441
|
+
const labelled = partitionBySlice(whole, slice);
|
|
355
442
|
let top1 = 0;
|
|
356
443
|
let top3 = 0;
|
|
357
444
|
const misses: string[] = [];
|
|
@@ -365,6 +452,8 @@ export function measureAccuracy(opts: {
|
|
|
365
452
|
const census = packCensus(labelled, opts.skillsDir);
|
|
366
453
|
return {
|
|
367
454
|
corpus: opts.corpus,
|
|
455
|
+
slice,
|
|
456
|
+
slice_sizes: sliceSizes(whole),
|
|
368
457
|
corpus_prompts: n,
|
|
369
458
|
top1: n ? round3(top1 / n) : 0,
|
|
370
459
|
top1_ci95: ci(top1, n),
|
|
@@ -381,20 +470,80 @@ export function measureAccuracy(opts: {
|
|
|
381
470
|
};
|
|
382
471
|
}
|
|
383
472
|
|
|
473
|
+
/**
|
|
474
|
+
* The ranker label → the options it means.
|
|
475
|
+
*
|
|
476
|
+
* One table rather than a conditional at each call site, because a measurement
|
|
477
|
+
* harness that resolves `--ranker` differently from the report that quotes it
|
|
478
|
+
* is a comparison of two things under one name. Every entry past `keyword-v1`
|
|
479
|
+
* is a candidate configuration under `road-to-a-ranker-that-routes` Phase 2,
|
|
480
|
+
* each a single flag so it can be measured alone, plus the combinations the
|
|
481
|
+
* same phase names.
|
|
482
|
+
*
|
|
483
|
+
* AN UNKNOWN LABEL THROWS. It used to resolve to `keyword-v1` — the baseline —
|
|
484
|
+
* which is the exact failure this table exists to prevent: every consumer
|
|
485
|
+
* echoes the requested label back into its output, so a typo published a
|
|
486
|
+
* BASELINE number under another configuration's name, in the report's own title
|
|
487
|
+
* and in the regenerate command it prints beside it. The two sibling parsers in
|
|
488
|
+
* this file refuse an unknown value and so does the sweep; the lenient branch
|
|
489
|
+
* was the inconsistent one.
|
|
490
|
+
*/
|
|
491
|
+
export const RANKER_LABELS: Readonly<Record<string, RankOptions>> = {
|
|
492
|
+
'keyword-v1': {},
|
|
493
|
+
'keyword-v2': { includeTriggers: true },
|
|
494
|
+
'when-to-use': { includeWhenToUse: true },
|
|
495
|
+
headings: { includeHeadings: true },
|
|
496
|
+
idf: { idfWeighting: true },
|
|
497
|
+
'idf+when-to-use': { idfWeighting: true, includeWhenToUse: true },
|
|
498
|
+
};
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* The message carries NO tool name.
|
|
502
|
+
*
|
|
503
|
+
* Three scripts resolve `--ranker` through this module now, so a hardcoded
|
|
504
|
+
* prefix would print one tool's name when another was the failing command. The
|
|
505
|
+
* caller's own `process.stderr` line is where the command is identified.
|
|
506
|
+
*/
|
|
507
|
+
export class UnknownRankerLabel extends Error {
|
|
508
|
+
constructor(label: string) {
|
|
509
|
+
super(
|
|
510
|
+
`unknown --ranker ${label || '(nothing)'}; ` +
|
|
511
|
+
`known: ${Object.keys(RANKER_LABELS).join(' | ')}`,
|
|
512
|
+
);
|
|
513
|
+
this.name = 'UnknownRankerLabel';
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/** Whether `label` is one of the table's OWN keys — never an inherited one. */
|
|
518
|
+
export function isKnownRanker(label: string): boolean {
|
|
519
|
+
return Object.hasOwn(RANKER_LABELS, label);
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export function rankOptionsFor(ranker: string): RankOptions {
|
|
523
|
+
// `Object.hasOwn`, not a truthiness check on the lookup: `RANKER_LABELS`
|
|
524
|
+
// is an object literal, so `constructor`, `toString` and `valueOf` resolve
|
|
525
|
+
// to inherited FUNCTIONS rather than to undefined. A plain `=== undefined`
|
|
526
|
+
// guard lets those three through, scores them as the baseline, and echoes
|
|
527
|
+
// the label into the report title — which is the whole defect this guard
|
|
528
|
+
// was added to close, surviving in its own fix.
|
|
529
|
+
if (!isKnownRanker(ranker)) throw new UnknownRankerLabel(ranker);
|
|
530
|
+
return RANKER_LABELS[ranker] as RankOptions;
|
|
531
|
+
}
|
|
532
|
+
|
|
384
533
|
export function measure(opts: {
|
|
385
534
|
ranker: string;
|
|
386
535
|
commit: string;
|
|
387
536
|
corpus?: CorpusName;
|
|
537
|
+
slice?: SliceName;
|
|
388
538
|
skillsDir?: string;
|
|
389
539
|
repo?: string;
|
|
390
540
|
}): RankerBaseline {
|
|
391
541
|
const repo = opts.repo ?? REPO;
|
|
392
542
|
const skillsDir = opts.skillsDir ?? SKILLS_DIR;
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
const rankOpts: RankOptions = opts.ranker === 'keyword-v2' ? { includeTriggers: true } : {};
|
|
543
|
+
// Resolved through the one table, which throws on a label it does not know.
|
|
544
|
+
const rankOpts: RankOptions = rankOptionsFor(opts.ranker);
|
|
396
545
|
const corpus = opts.corpus ?? 'labelled';
|
|
397
|
-
const accuracy = measureAccuracy({ corpus, repo, skillsDir, rankOpts });
|
|
546
|
+
const accuracy = measureAccuracy({ corpus, repo, skillsDir, rankOpts, slice: opts.slice ?? 'all' });
|
|
398
547
|
|
|
399
548
|
const cases = readMatrixCases(repo);
|
|
400
549
|
let withResult = 0;
|
|
@@ -446,17 +595,76 @@ function parseCorpus(argv: readonly string[]): CorpusName {
|
|
|
446
595
|
return raw as CorpusName;
|
|
447
596
|
}
|
|
448
597
|
|
|
598
|
+
/**
|
|
599
|
+
* The `--ranker` value, or the baseline when the flag is absent.
|
|
600
|
+
*
|
|
601
|
+
* A PRESENT flag with no value after it is an error, not an omission — the
|
|
602
|
+
* sibling `--slice` has always refused that shape, and substituting the
|
|
603
|
+
* baseline for a malformed flag is the same silent-fallback defect one level
|
|
604
|
+
* up from the label table.
|
|
605
|
+
*/
|
|
606
|
+
export function parseRanker(argv: readonly string[]): string {
|
|
607
|
+
const i = argv.indexOf('--ranker');
|
|
608
|
+
if (i === -1) return 'keyword-v1';
|
|
609
|
+
const raw = argv[i + 1];
|
|
610
|
+
if (!raw || raw.startsWith('--')) {
|
|
611
|
+
throw new Error(
|
|
612
|
+
`--ranker expects one of ${Object.keys(RANKER_LABELS).join(' | ')}, got ${raw ?? '(nothing)'}`,
|
|
613
|
+
);
|
|
614
|
+
}
|
|
615
|
+
return raw;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* A flag's value, or `fallback` when the flag is absent.
|
|
620
|
+
*
|
|
621
|
+
* A PRESENT flag followed by nothing — or by the next flag — is an error. Both
|
|
622
|
+
* `--commit` and `--date` are echoed verbatim into published artifacts (the
|
|
623
|
+
* baseline's provenance field, the confusion report's own header), so
|
|
624
|
+
* `--commit --ranker idf` silently publishing `--ranker` as the commit is the
|
|
625
|
+
* same silent-substitution class `parseRanker` was written to close, two flags
|
|
626
|
+
* over in the same two functions.
|
|
627
|
+
*/
|
|
628
|
+
export function parseFlagValue(argv: readonly string[], flag: string, fallback: string): string {
|
|
629
|
+
const i = argv.indexOf(flag);
|
|
630
|
+
if (i === -1) return fallback;
|
|
631
|
+
const raw = argv[i + 1];
|
|
632
|
+
if (!raw || raw.startsWith('--')) {
|
|
633
|
+
throw new Error(`${flag} expects a value, got ${raw ?? '(nothing)'}`);
|
|
634
|
+
}
|
|
635
|
+
return raw;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
export function parseSlice(argv: readonly string[]): SliceName {
|
|
639
|
+
const i = argv.indexOf('--slice');
|
|
640
|
+
if (i === -1) return 'all';
|
|
641
|
+
const raw = argv[i + 1];
|
|
642
|
+
if (!raw || !SLICE_NAMES.includes(raw as SliceName)) {
|
|
643
|
+
throw new Error(
|
|
644
|
+
`measure_skill_ranker_baseline: --slice expects one of ${SLICE_NAMES.join(' | ')}, got ${raw ?? '(nothing)'}`,
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
return raw as SliceName;
|
|
648
|
+
}
|
|
649
|
+
|
|
449
650
|
export function main(argv: readonly string[]): number {
|
|
450
|
-
const ranker = argv.includes('--ranker') ? (argv[argv.indexOf('--ranker') + 1] ?? 'keyword-v1') : 'keyword-v1';
|
|
451
|
-
const commit = argv.includes('--commit') ? (argv[argv.indexOf('--commit') + 1] ?? 'unknown') : 'unknown';
|
|
452
651
|
let corpus: CorpusName;
|
|
652
|
+
let slice: SliceName;
|
|
653
|
+
let ranker: string;
|
|
654
|
+
let commit: string;
|
|
655
|
+
let out: RankerBaseline;
|
|
453
656
|
try {
|
|
454
657
|
corpus = parseCorpus(argv);
|
|
658
|
+
slice = parseSlice(argv);
|
|
659
|
+
ranker = parseRanker(argv);
|
|
660
|
+
commit = parseFlagValue(argv, '--commit', 'unknown');
|
|
661
|
+
// Inside the same guard as the two parsers above: an unknown --ranker is
|
|
662
|
+
// the same class of mistake and now gets the same exit code.
|
|
663
|
+
out = measure({ ranker, commit, corpus, slice });
|
|
455
664
|
} catch (err) {
|
|
456
665
|
process.stderr.write(`${(err as Error).message}\n`);
|
|
457
666
|
return 2;
|
|
458
667
|
}
|
|
459
|
-
const out = measure({ ranker, commit, corpus });
|
|
460
668
|
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
461
669
|
return 0;
|
|
462
670
|
}
|
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
* PROMOTED lesson is surfaced as a one-line visibility marker pointing
|
|
23
23
|
* at the existing /memory:propose flow. Nothing auto-writes curated
|
|
24
24
|
* `agents/memory/*.yml`.
|
|
25
|
+
* - Measured: every run appends one line to the gitignored dogfood ledger
|
|
26
|
+
* (`agents/runtime/state/learning-dogfood.jsonl`) BEFORE the early
|
|
27
|
+
* returns, so the 30-day window the default flip is gated on can read a
|
|
28
|
+
* zero rather than a silence. See `runLearn` for why that distinction is
|
|
29
|
+
* the whole point.
|
|
25
30
|
*
|
|
26
31
|
* Exit codes (dispatcher contract): 0 always (allow).
|
|
27
32
|
*/
|
|
@@ -44,8 +49,9 @@ import type { SettingsClass } from '../shared/settingsClasses.js';
|
|
|
44
49
|
import { consentVerdict, type ConsentVerdict } from '../shared/settingsConsent.js';
|
|
45
50
|
|
|
46
51
|
import {
|
|
47
|
-
|
|
52
|
+
buildSidecarFromSignals,
|
|
48
53
|
LESSONS_NAME,
|
|
54
|
+
readSignals,
|
|
49
55
|
renderLessonsMd,
|
|
50
56
|
SIDECAR_NAME,
|
|
51
57
|
} from './learning_sidecar.js';
|
|
@@ -175,23 +181,102 @@ export function enabled(root: string): boolean {
|
|
|
175
181
|
return learnConsent(root) === 'granted';
|
|
176
182
|
}
|
|
177
183
|
|
|
178
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* The dogfood ledger: repo-relative POSIX path, one JSON object per line.
|
|
186
|
+
*
|
|
187
|
+
* Under `agents/runtime/` — gitignored wholesale — because it is one machine's
|
|
188
|
+
* timings and counts, not a shared artefact. Its continuity-surface row records
|
|
189
|
+
* it as `excluded`: nothing restores a session from it.
|
|
190
|
+
*/
|
|
191
|
+
export const DOGFOOD_LEDGER_POSIX = 'agents/runtime/state/learning-dogfood.jsonl';
|
|
192
|
+
|
|
193
|
+
/** One session end, as the ledger records it. */
|
|
194
|
+
export interface DogfoodLine {
|
|
195
|
+
/** The `now` the aggregation ran against. */
|
|
196
|
+
at: string;
|
|
197
|
+
/** Wall-clock ms for read + aggregate — the portion the 2 s budget governs. */
|
|
198
|
+
wall_ms: number;
|
|
199
|
+
/** Well-formed signal records read from the intake. */
|
|
200
|
+
signals_in: number;
|
|
201
|
+
/** Lessons the aggregation produced. */
|
|
202
|
+
lessons_out: number;
|
|
203
|
+
/** Of those, how many carry the `preferred` verdict. */
|
|
204
|
+
preferred: number;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Append one measurement line. Never throws: the ledger is a measurement, and
|
|
209
|
+
* a measurement that can break the thing it measures is worse than no ledger.
|
|
210
|
+
*/
|
|
211
|
+
export function appendDogfoodLine(root: string, line: DogfoodLine): void {
|
|
212
|
+
try {
|
|
213
|
+
const file = path.join(root, ...DOGFOOD_LEDGER_POSIX.split('/'));
|
|
214
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
215
|
+
fs.appendFileSync(file, `${JSON.stringify(line)}\n`, 'utf8');
|
|
216
|
+
} catch {
|
|
217
|
+
// fail-open, same contract as the aggregation itself.
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/** Read the ledger back. Malformed lines are skipped, an absent file is empty. */
|
|
222
|
+
export function readDogfoodLines(root: string): DogfoodLine[] {
|
|
223
|
+
let text: string;
|
|
224
|
+
try {
|
|
225
|
+
text = fs.readFileSync(path.join(root, ...DOGFOOD_LEDGER_POSIX.split('/')), 'utf8');
|
|
226
|
+
} catch {
|
|
227
|
+
return [];
|
|
228
|
+
}
|
|
229
|
+
const out: DogfoodLine[] = [];
|
|
230
|
+
for (const raw of text.split('\n')) {
|
|
231
|
+
const line = raw.trim();
|
|
232
|
+
if (!line) continue;
|
|
233
|
+
try {
|
|
234
|
+
out.push(JSON.parse(line) as DogfoodLine);
|
|
235
|
+
} catch {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return out;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Run the aggregation. Returns the one-line marker to emit, or null.
|
|
244
|
+
*
|
|
245
|
+
* MEASURE FIRST, THEN DECIDE. The flip condition this hook's default is gated
|
|
246
|
+
* on (`src/config/agent-settings.template.yml:1376-1378`) names two numbers —
|
|
247
|
+
* non-trivial signal AND session-end p95 < 2 s — and until the ledger below
|
|
248
|
+
* existed, neither was recorded: every early return here fired before any
|
|
249
|
+
* measurement point, so a checkout with an empty intake produced an unbroken
|
|
250
|
+
* silence. A silence and a measured zero read identically to whoever opens the
|
|
251
|
+
* window, which is the one failure a 30-day window cannot recover from. So the
|
|
252
|
+
* read, the aggregation and the ledger line all happen before the first return.
|
|
253
|
+
*/
|
|
179
254
|
export function runLearn(root: string, nowIso: string, budgetMs: number = BUDGET_MS): string | null {
|
|
180
255
|
const started = Date.now();
|
|
181
256
|
const intakeDir = path.join(root, 'agents', 'memory', 'intake');
|
|
182
257
|
const outDir = path.join(root, 'agents', 'memory');
|
|
183
|
-
|
|
184
|
-
const
|
|
258
|
+
// An absent intake directory is a zero-signal session, not an absent one.
|
|
259
|
+
const haveIntake = fs.existsSync(intakeDir);
|
|
260
|
+
const signals = haveIntake ? readSignals(intakeDir) : [];
|
|
261
|
+
const sidecar = buildSidecarFromSignals(signals, nowIso);
|
|
262
|
+
const preferred = sidecar.lessons.filter((l) => l.verdict === 'preferred').length;
|
|
263
|
+
appendDogfoodLine(root, {
|
|
264
|
+
at: nowIso,
|
|
265
|
+
wall_ms: Date.now() - started,
|
|
266
|
+
signals_in: signals.length,
|
|
267
|
+
lessons_out: sidecar.lessons.length,
|
|
268
|
+
preferred,
|
|
269
|
+
});
|
|
270
|
+
if (!haveIntake) return null;
|
|
185
271
|
if (Date.now() - started > budgetMs) return null; // over budget — skip the write
|
|
186
272
|
if (sidecar.lessons.length === 0) return null;
|
|
187
273
|
fs.writeFileSync(path.join(outDir, SIDECAR_NAME), `${JSON.stringify(sidecar, null, 2)}\n`, 'utf8');
|
|
188
274
|
fs.writeFileSync(path.join(outDir, LESSONS_NAME), renderLessonsMd(sidecar), 'utf8');
|
|
189
|
-
const promoted = sidecar.lessons.filter((l) => l.verdict === 'preferred').length;
|
|
190
275
|
const deadEnds = sidecar.lessons.filter((l) => l.verdict === 'dead_end').length;
|
|
191
276
|
// Memory-visibility contract shape: one line, ids/counters only, no bodies.
|
|
192
277
|
return (
|
|
193
278
|
`🧠 Memory: sidecar refreshed — ${sidecar.lessons.length} lesson(s) ` +
|
|
194
|
-
`(${
|
|
279
|
+
`(${preferred} preferred · ${deadEnds} dead-end); promote via /memory:propose`
|
|
195
280
|
);
|
|
196
281
|
}
|
|
197
282
|
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
triggerlessRuleIds,
|
|
66
66
|
type Router,
|
|
67
67
|
} from './_lib/rule_injection.js';
|
|
68
|
+
import { ruleBody } from './_lib/rule_law_section.js';
|
|
68
69
|
import {
|
|
69
70
|
renderArmExperiment,
|
|
70
71
|
runArmExperiment,
|
|
@@ -682,6 +683,34 @@ export function runEndpoints(corpusDir: string): EndpointResult[] {
|
|
|
682
683
|
]);
|
|
683
684
|
const thinnedAndPathOnly = pathOnlyReach.filter((id) => !alwaysEager.has(id));
|
|
684
685
|
|
|
686
|
+
// PROMPT-LEVEL beside RULE-LEVEL — step 0.4 of
|
|
687
|
+
// `road-to-a-rule-carrier-that-works-outside-the-repo`.
|
|
688
|
+
//
|
|
689
|
+
// The two numbers above are per RULE: a rule counts as reachable when ANY
|
|
690
|
+
// of its labelled positives matches. That is the right unit for the bar —
|
|
691
|
+
// "no labelled rule left unreachable" — and it is the wrong unit for the
|
|
692
|
+
// question a reader actually asks next, which is how much of the corpus the
|
|
693
|
+
// shipped binding delivers. A rule reachable on one of twelve prompts and a
|
|
694
|
+
// rule reachable on all twelve are the same row in `byRule`.
|
|
695
|
+
//
|
|
696
|
+
// So the pair is reported in both units. The SUMMARY view has computed the
|
|
697
|
+
// prompt-level figures since the two-readings split landed; what was missing
|
|
698
|
+
// is that `--endpoints`, which is the view a claim cites, printed only the
|
|
699
|
+
// rule-level half. A reader comparing the two views saw 99/102 and 305/335
|
|
700
|
+
// and had no way to tell they measure different things.
|
|
701
|
+
const promptHonoured = positives.filter((c) => reach(c, true)).length;
|
|
702
|
+
const promptIgnored = positives.filter((c) => reach(c, false)).length;
|
|
703
|
+
|
|
704
|
+
// The SUMMARY view prints a LOWER pair over the same corpus, and the gap is
|
|
705
|
+
// not noise. `scoreExact` calls `matchTierRules(router, c.prompt, of)` with
|
|
706
|
+
// three arguments and drops the case's `command`, so a positive whose rule
|
|
707
|
+
// is reached by a `command:` trigger scores as a miss there and as a hit
|
|
708
|
+
// here. Recorded rather than repaired: this phase changes no behaviour, and
|
|
709
|
+
// the SUMMARY figures are cited in a roadmap and in the prereg. Both
|
|
710
|
+
// readings are printed so neither view has to be trusted over the other.
|
|
711
|
+
const summaryHonoured = scoreExact(router, cases, true);
|
|
712
|
+
const summaryIgnored = scoreExact(router, cases, false);
|
|
713
|
+
|
|
685
714
|
// (c) A near-miss must never deliver its labelled rule.
|
|
686
715
|
const exact = scoreExact(router, cases, true);
|
|
687
716
|
|
|
@@ -716,7 +745,15 @@ export function runEndpoints(corpusDir: string): EndpointResult[] {
|
|
|
716
745
|
`${byRule.size - pathOnlyReach.length}/${byRule.size}; reachable only via a ` +
|
|
717
746
|
`path trigger: ${pathOnlyReach.length === 0 ? 'none' : pathOnlyReach.join(', ')}` +
|
|
718
747
|
` (of which thinned, i.e. at no scope on that route: ` +
|
|
719
|
-
`${thinnedAndPathOnly.length === 0 ? 'none' : thinnedAndPathOnly.join(', ')})
|
|
748
|
+
`${thinnedAndPathOnly.length === 0 ? 'none' : thinnedAndPathOnly.join(', ')})` +
|
|
749
|
+
` | per-prompt reach: ${promptIgnored}/${positives.length} with open_files ` +
|
|
750
|
+
`IGNORED against ${promptHonoured}/${positives.length} honoured — the same ` +
|
|
751
|
+
`corpus in prompts rather than in rules, because a rule reachable on one of ` +
|
|
752
|
+
`twelve positives and one reachable on all twelve are one row above` +
|
|
753
|
+
` | SUMMARY prints ${summaryIgnored.hits}/${summaryIgnored.positives} and ` +
|
|
754
|
+
`${summaryHonoured.hits}/${summaryHonoured.positives} for the same corpus: ` +
|
|
755
|
+
`scoreExact drops the case's command trigger, so a command-reached positive ` +
|
|
756
|
+
`misses there and hits here`,
|
|
720
757
|
bar: 'unreachable == 0 (a rule with zero matched positives is a rule the mode removed)',
|
|
721
758
|
passed: unreachable.length === 0,
|
|
722
759
|
},
|
|
@@ -781,13 +818,23 @@ export function runSelftest(corpusDir: string): SelftestCase[] {
|
|
|
781
818
|
});
|
|
782
819
|
} else {
|
|
783
820
|
const id = injection.rules[0] as string;
|
|
784
|
-
|
|
785
|
-
|
|
821
|
+
// THE DELIVERED FORM, not the raw file. Since step 1.4 the concern emits
|
|
822
|
+
// `ruleBody` of the projection — frontmatter and HTML comments stripped
|
|
823
|
+
// by the same parser `project_thin_rules` uses — so comparing against
|
|
824
|
+
// the raw file asserts a substring the payload no longer contains. The
|
|
825
|
+
// endpoint's claim is unchanged: the payload carries the body verbatim
|
|
826
|
+
// and rejects a mutation of it. What "the body" means moved, and this
|
|
827
|
+
// moved with it.
|
|
828
|
+
const projected = ruleBody(loadRuleBody(REPO_ROOT, id) ?? '');
|
|
829
|
+
const mutated = `${projected.slice(0, -1)}~MUTATED~`;
|
|
786
830
|
out.push({
|
|
787
831
|
endpoint: 'a-delivery',
|
|
788
832
|
name: 'one-byte mutation rejected',
|
|
789
|
-
passed:
|
|
790
|
-
|
|
833
|
+
passed:
|
|
834
|
+
projected !== ''
|
|
835
|
+
&& injection.body.includes(projected)
|
|
836
|
+
&& !injection.body.includes(mutated),
|
|
837
|
+
detail: `${id}: the concern's payload carries the delivered body verbatim and rejects a mutation of it`,
|
|
791
838
|
});
|
|
792
839
|
}
|
|
793
840
|
|