@event4u/agent-config 14.21.0 → 14.23.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 +164 -187
- package/CREDITS.md +37 -0
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/inbox.md +380 -5
- package/dist/agent-src/commands/analyze/repo.md +4 -2
- package/dist/agent-src/commands/pr/merge.md +47 -38
- package/dist/agent-src/commands/roadmap/next.md +8 -0
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +69 -0
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +5 -5
- package/dist/agent-src/contexts/execution/autonomy-mechanics.md +12 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +19 -14
- package/dist/agent-src/rules/notes-first-reasoning.md +36 -25
- package/dist/agent-src/rules/token-efficiency.md +26 -3
- package/dist/agent-src/scripts/_cli_entry.ts +51 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/dist/agent-src/scripts/roadmap_gates.ts +4 -20
- package/dist/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/dist/agent-src/scripts/stubs_due.ts +4 -18
- package/dist/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/dist/agent-src/skills/code-intelligence/SKILL.md +82 -1
- package/dist/agent-src/skills/git-workflow/references/push-closes-its-loop.md +9 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +17 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +52 -2
- package/dist/agent-src/templates/agent-settings.md +0 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli-delegate/_cli_entry.js +8 -0
- package/dist/cli-delegate/_lib/pin_resolver.js +170 -0
- package/dist/cli-delegate/archival_sweep.js +8 -0
- package/dist/cli-delegate/archive_completed_roadmaps.js +542 -0
- package/dist/cli-delegate/check_update_banner.js +923 -0
- package/dist/cli-delegate/{chunk-ZVBOHSVF.js → chunk-2KKXJW25.js} +23 -9
- package/dist/cli-delegate/chunk-5EK5WQN6.js +28 -0
- package/dist/cli-delegate/chunk-7XGD2OEU.js +96 -0
- package/dist/cli-delegate/chunk-DBH4AHDK.js +155 -0
- package/dist/cli-delegate/{chunk-4FS4WRXP.js → chunk-IAI3XL43.js} +15 -3
- package/dist/cli-delegate/chunk-JO25G5H7.js +20 -0
- package/dist/cli-delegate/{chunk-643Z7FIC.js → chunk-KCNTUSPI.js} +7 -1
- package/dist/cli-delegate/chunk-LTYVIY76.js +7961 -0
- package/dist/cli-delegate/chunk-QAJS5YWY.js +999 -0
- package/dist/cli-delegate/{chunk-YS6XTLEK.js → chunk-TYSY33MD.js} +56 -6
- package/dist/cli-delegate/chunk-ULET4BYS.js +328 -0
- package/dist/cli-delegate/chunk-YDQSHTD7.js +187 -0
- package/dist/cli-delegate/chunk-YWVXLR44.js +48 -0
- package/dist/cli-delegate/chunk-ZZQBUHFK.js +273 -0
- package/dist/cli-delegate/cmd_conformance.js +2 -2
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +45 -13
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_session_recycle.js +4 -14
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/cli-delegate/dashboard_mode.js +16 -0
- package/dist/cli-delegate/gate_budget.js +24 -0
- package/dist/cli-delegate/gate_execute.js +25 -0
- package/dist/cli-delegate/guarded_baseline.js +18 -0
- package/dist/cli-delegate/resume_probe.js +22 -0
- package/dist/cli-delegate/roadmap_gates.js +638 -0
- package/dist/cli-delegate/roadmap_set_step.js +173 -0
- package/dist/cli-delegate/stub_queue.js +47 -0
- package/dist/cli-delegate/stubs_due.js +38 -0
- package/dist/cli-delegate/ticket_bundles.js +59 -0
- package/dist/cli-delegate/update_roadmap_progress.js +60 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +13 -13
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1813 -946
- package/dist/install/claudeLayerCarriage.js +237 -0
- package/dist/install/claudePathsPlan.js +79 -0
- package/dist/install/hostLayerFingerprint.js +1 -1
- package/dist/install/install.mjs +21 -3
- package/dist/install/partitionEligibility.js +231 -117
- package/dist/install/ruleLayerPartition.js +10 -7
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +10870 -7641
- package/dist/scripts/_lib/lean_projection_mode.js +108 -0
- package/dist/scripts/_lib/router_match.js +243 -0
- package/dist/scripts/_lib/rule_injection.js +216 -0
- package/dist/scripts/condense.js +52 -45
- package/dist/scripts/project_thin_rules.js +139 -19
- package/dist/server/schemas/settings.js +16 -3
- package/docs/MIGRATION.md +105 -1
- package/docs/contracts/settings-classes.md +40 -8
- package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +73 -0
- package/docs/guidelines/agent-infra/notes-horizon-mechanics.md +90 -0
- package/llms.txt +1 -1
- package/package.json +7 -4
- package/src/agent-src/scripts/_cli_entry.ts +51 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +120 -27
- package/src/agent-src/scripts/roadmap_gates.ts +4 -20
- package/src/agent-src/scripts/roadmap_set_step.ts +5 -2
- package/src/agent-src/scripts/stubs_due.ts +4 -18
- package/src/agent-src/scripts/update_roadmap_progress.ts +7 -24
- package/src/config/agent-settings.template.yml +47 -1
- package/src/config/continuity-surface.json +516 -0
- package/src/config/estate-count-budget.json +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +29 -8
- package/src/config/gate-violation-baselines.json +8 -8
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-token-budget.json +7 -6
- package/src/config/host-injection-effect.json +73 -0
- package/src/config/packed-binary-manifest.json +33 -0
- package/src/config/preamble-payload-budget.json +112 -105
- package/src/config/publish-surface.json +4 -2
- package/src/config/routing-coverage-seed.json +9 -2
- package/src/install/claudeLayerCarriage.ts +273 -0
- package/src/install/claudePathsPlan.ts +76 -0
- package/src/install/hostLayerFingerprint.ts +1 -1
- package/src/install/partitionEligibility.ts +261 -127
- package/src/install/ruleLayerPartition.ts +19 -23
- package/src/scripts/_cli/cmd_session_recycle.ts +2 -25
- package/src/scripts/_cli/handoff_sessions.ts +14 -44
- package/src/scripts/_dispatch.bash +77 -27
- package/src/scripts/_lib/auto_dispatch.ts +2 -2
- package/src/scripts/_lib/catalogue_layer_parity.ts +146 -0
- package/src/scripts/_lib/continuity_slot.ts +295 -0
- package/src/scripts/_lib/continuity_surface.ts +296 -0
- package/src/scripts/_lib/continuity_writer.ts +253 -0
- package/src/scripts/_lib/corpus_manifest.ts +26 -9
- package/src/scripts/_lib/hook_settings.ts +86 -1
- package/src/scripts/_lib/injection_effect.ts +151 -0
- package/src/scripts/_lib/layer_overlap_notice.ts +50 -6
- package/src/scripts/_lib/lean_projection_mode.ts +127 -0
- package/src/scripts/_lib/packed_binary_predicate.ts +262 -0
- package/src/scripts/_lib/pin_resolver.ts +13 -0
- package/src/scripts/_lib/recycle_envelope_paths.ts +109 -21
- package/src/scripts/_lib/regression_neighbourhood.ts +159 -22
- package/src/scripts/_lib/retired_status.ts +74 -0
- package/src/scripts/_lib/session_eol.ts +44 -0
- package/src/scripts/_lib/session_index_trust.ts +346 -0
- package/src/scripts/_lib/skill_catalogue.ts +156 -21
- package/src/scripts/_lib/standing_bound_ratchet.ts +154 -0
- package/src/scripts/_lib/subagent_capsule.ts +117 -8
- package/src/scripts/_lib/vendored_grammar_upstream.ts +280 -0
- package/src/scripts/ai_council/api_transport.ts +5 -0
- package/src/scripts/ai_council/deliberation_metrics.ts +353 -0
- package/src/scripts/ai_council/prompts.ts +9 -0
- package/src/scripts/ai_council/synthesis_disagreement.ts +206 -0
- package/src/scripts/audit_mcp_tools.ts +69 -25
- package/src/scripts/build_mcp_catalog.ts +24 -1
- package/src/scripts/capture_skill_catalogue.ts +24 -11
- package/src/scripts/check_candidate_lines.ts +499 -0
- package/src/scripts/check_continuity_surface.ts +475 -0
- package/src/scripts/check_dependency_floors.ts +13 -6
- package/src/scripts/check_estate_count.ts +40 -79
- package/src/scripts/check_host_tree_parity.ts +291 -0
- package/src/scripts/check_pack_size.ts +262 -23
- package/src/scripts/check_preamble_payload_budget.ts +136 -7
- package/src/scripts/check_roadmap_trackable.ts +19 -16
- package/src/scripts/check_rule_layer_partition.ts +54 -34
- package/src/scripts/check_rule_projection_integrity.ts +102 -2
- package/src/scripts/check_single_delivery.ts +97 -31
- package/src/scripts/check_update_banner.ts +13 -0
- package/src/scripts/ci_settle.ts +205 -5
- package/src/scripts/code_graph/build.ts +217 -15
- package/src/scripts/code_graph/cli.ts +334 -15
- package/src/scripts/code_graph/detect.ts +38 -0
- package/src/scripts/code_graph/extract.ts +11 -0
- package/src/scripts/code_graph/grammar_probe.ts +43 -0
- package/src/scripts/code_graph/loader.ts +88 -14
- package/src/scripts/code_graph/query.ts +144 -11
- package/src/scripts/code_graph/resolution_tiers.ts +238 -0
- package/src/scripts/code_graph/sqlite_store.ts +262 -6
- package/src/scripts/code_graph/types.ts +117 -3
- package/src/scripts/code_graph/validate.ts +26 -1
- package/src/scripts/code_graph/verbs.ts +590 -0
- package/src/scripts/condense.ts +54 -42
- package/src/scripts/corpus_manifest.ts +5 -5
- package/src/scripts/council_cli.ts +11 -12
- package/src/scripts/count_intent_disagreement.ts +426 -0
- package/src/scripts/handoff_context_hook.ts +118 -1
- package/src/scripts/hook_effect_doctor.ts +82 -1
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +87 -12
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +182 -16
- package/src/scripts/hooks/chain_nudge_hook.ts +282 -0
- package/src/scripts/hooks/code_graph_context_hook.ts +201 -0
- package/src/scripts/hooks/concern_registry.ts +4 -2
- package/src/scripts/hooks/host_semantics.ts +1 -1
- package/src/scripts/hooks/orchestration_record_hook.ts +2 -2
- package/src/scripts/hooks/push_settle_hook.ts +3 -2
- package/src/scripts/hooks/rule_inject_hook.ts +108 -21
- package/src/scripts/hooks/session_eol_hook.ts +120 -2
- package/src/scripts/hooks/skill_route_hook.ts +11 -4
- package/src/scripts/hot_context_hook.ts +62 -7
- package/src/scripts/inbox_source_census.ts +322 -0
- package/src/scripts/install-hooks.sh +82 -0
- package/src/scripts/{lint_carrier_integrity.ts → lint_deferral_integrity.ts} +60 -214
- package/src/scripts/lint_plan_risk_register.ts +13 -17
- package/src/scripts/lint_roadmap_blockers.ts +164 -1
- package/src/scripts/mcp_server/consumer_tool_catalog.json +152 -2
- package/src/scripts/mcp_server/graph_tools.ts +294 -0
- package/src/scripts/mcp_server/path_util.ts +31 -0
- package/src/scripts/mcp_server/suggest_skill.ts +72 -0
- package/src/scripts/mcp_server/tool_catalog_source.ts +7 -1
- package/src/scripts/mcp_server/tools.ts +5 -69
- package/src/scripts/media/lib/adapter-contract.md +7 -2
- package/src/scripts/media/lib/fixtures/flux/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/gemini-image/asset-0001.png +0 -0
- package/src/scripts/media/lib/fixtures/ideogram/asset-0001.png +0 -0
- package/src/scripts/model_rule_injection.ts +61 -4
- package/src/scripts/project_thin_rules.ts +149 -16
- package/src/scripts/rdp_candidates_delta.ts +276 -0
- package/src/scripts/rdp_quality_eval.ts +217 -19
- package/src/scripts/release_mix_taxonomy.json +3 -2
- package/src/scripts/report_host_injection_effect.ts +189 -0
- package/src/scripts/report_layer_overlap.ts +41 -0
- package/src/scripts/report_skill_menu_census.ts +341 -0
- package/src/scripts/report_standing_payload_by_host.ts +650 -0
- package/src/scripts/schemas/agent-settings.schema.json +7 -1
- package/src/scripts/session_memory_index.ts +81 -8
- package/src/scripts/skill_tools/score_skill_relevance.ts +70 -6
- package/src/scripts/skill_tools/suggest_skill_for_task.ts +37 -3
- package/src/server/schemas/settings.ts +23 -4
- package/src/vendor/grammars/README.md +86 -0
- package/src/vendor/grammars/tree-sitter-javascript.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-php.wasm +0 -0
- package/src/vendor/grammars/tree-sitter-typescript.wasm +0 -0
- package/src/scripts/_lib/carrier_status.ts +0 -118
- package/src/scripts/_lib/estate_carrier_cases.ts +0 -72
- package/src/scripts/hooks/code_graph_nudge_hook.ts +0 -191
|
@@ -1,110 +1,117 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
],
|
|
14
|
-
"excluded_buckets": [
|
|
15
|
-
"user-scope rules (~/.claude/rules/*.md) \u2014 ALREADY COUNTED, inside the `project-scope rules` gated bucket; excluded here only to avoid double-counting. CORRECTED 2026-08-30 (road-to-agent-turnaround 4.2, AI council 2/2 over two rounds). The previous reason read \"machine-dependent, not CI-checkable\", and that was FALSE in both halves: the layer is written by this package's own installer out of dist/agent-src/rules/, and which rules reach the GLOBAL layer rather than the project one is decided by frontmatter (`workspaces:` naming the maintainer workspace), not by anything on the developer's machine. `preamble_byte_census.censusDeliveredRulePayload` computes it from the projection alone, reading nothing under ~/. Measured 2026-08-30: 119 projected \u2212 15 package-only = 104 delivered, matching the installed file count exactly; the 1.5 % char gap against the machine reading is install drift. Round 1 of the council voted to GATE the bucket on the premise that its bytes were invisible to CI; round 2 was given the checker's bucket definitions, found the 104 are a SUBSET of the 119 the gate already measures in full, and chose (a\u2032): correct the reason, add the reconciliation test, leave the baseline alone. Adding the bucket would have moved the baseline ~111k for zero additional delivered payload. What remains genuinely unreported by this GATE is the 104 / 13 / 2 destination split, which the CENSUS now prints \u2014 the council placed it there until distinct destinations need independent budget enforcement.",
|
|
16
|
-
"tool definitions + dispatch prompt \u2014 no local source carries the request payload; the census reports it as an explicit residual",
|
|
17
|
-
"personas (src/agent-src/personas, 36 files, ~37,181 tok) \u2014 measured ZERO standing cost 2026-08-22: .claude/personas/ is written by this repo's generators (5 in-repo references) and read by no host contract; the host's real subagent surface is .claude/agents/, which this package writes 1 file into. Reversal condition: a host publishing a personas contract, or a generator folding persona prose into an instruction file that IS read. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md",
|
|
18
|
-
"contexts (src/agent-src/contexts, 58 files, ~114,042 tok) \u2014 measured ON-DEMAND 2026-08-22: 0 rules declare load_context_eager: (17 declare the lazy load_context:), and rule.schema.json:49 documents eager as the variant that counts against the per-rule budget. This records that no eager declaration exists, NOT that eager loading would be free \u2014 nobody has declared one, so nobody has measured it. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md",
|
|
19
|
-
"skill bodies (src/skills/*/SKILL.md, 290 files, ~595,843 tok) \u2014 measured ON-DEMAND 2026-08-22: censusSkillsCatalog (preamble_byte_census.ts:216-234) builds the gated catalog bucket from `- name: description` per skill and reads no body byte, which is why 290 skills totalling ~595,843 tok report as ~14.4k standing. Corollary: the description cap is the WHOLE skill-side lever, not a fragment of it. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md"
|
|
20
|
-
],
|
|
21
|
-
"_bucket_classification_note": "All three additions above are EXCLUSIONS, not gated buckets, and that is the finding rather than a shortcut. A census bucket over a corpus measured at zero is a permanently green gate certifying nothing. The exclusions carry their measurement so the next pass that proposes one of these corpora as a token lever meets the number instead of the silence."
|
|
22
|
-
},
|
|
23
|
-
"baseline_tokens": 102520,
|
|
24
|
-
"baseline_history": [
|
|
25
|
-
{
|
|
26
|
-
"tokens": 102599,
|
|
27
|
-
"at": "2026-07-30",
|
|
28
|
-
"why": "registered with the gate"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"tokens": 102520,
|
|
32
|
-
"at": "2026-07-31",
|
|
33
|
-
"why": "free tightening: the measured tree came in 79 tok under the registered baseline, so the ratchet walks down to meet it. Not a claimed reduction \u2014 nothing was optimised; the point of a ratchet is that a lower measurement becomes the new ceiling instead of becoming unused headroom."
|
|
34
|
-
}
|
|
2
|
+
"_comment": "Per-spawn preamble payload ceiling (road-to-cache-economy Phase 3), unblocked by C-3 measuring 38.0% against a 15% bar. This is a RATCHET, not the roadmap's literal candidate numbers: those (median 40k / p95 50k) were anchored to an upstream ~37k baseline, while this package's own deterministic in-repo payload is ~102.5k tokens — a hard 40k gate would be red on the day it lands and would teach the reader to ignore it. The ratchet fails on GROWTH instead, so the number can only walk down, and the 40k/50k target stays recorded as the destination. Only the deterministic, in-repo buckets are gated: user-scope rules depend on what the developer happens to have installed globally and are therefore not CI-checkable.",
|
|
3
|
+
"schema_version": 1,
|
|
4
|
+
"registered_at": "2026-07-30",
|
|
5
|
+
"owner": "maintainer",
|
|
6
|
+
"review_by": "2027-07-30",
|
|
7
|
+
"metric": {
|
|
8
|
+
"basis": "chars/4 token estimate over the projected tree, produced by src/scripts/preamble_byte_census.ts",
|
|
9
|
+
"gated_buckets": [
|
|
10
|
+
"project-scope rules (dist/agent-src/rules/*.md)",
|
|
11
|
+
"preloaded skills catalog (name + description, dist/agent-src/skills)",
|
|
12
|
+
"CLAUDE.md hierarchy (project + project-local)"
|
|
35
13
|
],
|
|
36
|
-
"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
{
|
|
52
|
-
"by": "2026-11-10",
|
|
53
|
-
"tokens": 102520,
|
|
54
|
-
"why": "Return to the registered baseline. This is a RECOVERY milestone, not progress toward 40k - it undoes drift rather than shrinking the estate. The date is the review_by both budget files already carry, so it needs no new calendar."
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
"by": "2027-02-10",
|
|
58
|
-
"tokens": 85000,
|
|
59
|
-
"why": "First real reduction. The rule-paths census shows 92.1% of rule weight sits in the auto tier and the top 20 files carry 35.5% of it, so a scoped tranche is the plausible mechanism - GATED on the paths-scoping consumer decision, which is a human call and may resolve against scoping entirely."
|
|
60
|
-
},
|
|
61
|
-
{
|
|
62
|
-
"by": "2027-05-10",
|
|
63
|
-
"tokens": 60000,
|
|
64
|
-
"why": "Second reduction. No mechanism is committed for this one; it exists so the destination keeps a date after the first tranche lands, and it is the milestone most likely to be revised on evidence."
|
|
65
|
-
}
|
|
66
|
-
],
|
|
67
|
-
"on_miss": "A missed milestone is PUBLISHED - recorded here with the measured number and the reason - and never silently re-dated. Two consecutive misses mean the schedule was wrong or the destination is unreachable with the mechanisms available; either outcome is a decision to surface, not a date to move again.",
|
|
68
|
-
"revisit_if": "The paths-scoping decision resolves against scoping (removing the only named mechanism for the 85k milestone), or a measured reduction lands ahead of schedule and the remaining milestones are re-derived from it."
|
|
69
|
-
},
|
|
70
|
-
"reference_measurements": {
|
|
71
|
-
"measured_cold_start_median_tokens": 230930,
|
|
72
|
-
"c3_scope_dedup_reduction_pct": 38,
|
|
73
|
-
"c3_condition": "FIXTURE-ONLY as of 2026-07-31: the 38% was measured on a byte-identical two-scope fixture, and that condition is currently unreachable for every consumer because install.ts:2723/2725 stamp package:/source_path: into every installed rule while the in-repo projection stamps nothing. Aligning versions yields 0/110 twins, not 110/110. The figure is the mechanism's ceiling, NOT a realised saving, and this baseline therefore assumes no dedup. Reachability is the subject of road-to-dedup-reachability; run measure_scope_dedup for the live split.",
|
|
74
|
-
"_comment": "The measured median comes from Claude Code transcripts (deduped by message.id + requestId) and is context for the ratchet, never the gated number \u2014 it moves with the developer's own session history."
|
|
75
|
-
},
|
|
76
|
-
"ci_delivery": {
|
|
77
|
-
"workflow": ".github/workflows/standing-payload-delta.yml",
|
|
78
|
-
"trigger": "pull_request",
|
|
79
|
-
"posture": "blocking_on_ceiling_breach_with_grace_ceiling",
|
|
80
|
-
"design_ceiling": 107646,
|
|
81
|
-
"grace_ceiling": 138273,
|
|
82
|
-
"grace_measured_at": "2026-09-02",
|
|
83
|
-
"grace_end_date": "2026-11-10",
|
|
84
|
-
"decision": "OPTION (b) of b-preamble-ci-placement: fold into the existing standing-payload-delta.yml rather than add a per-PR job or a scheduled main job. That workflow already computes the neighbouring number and already DECLINED to fail on the premise that this gate 'already fails on growth' -- a premise that was false, because nothing in .github/ ran this gate at all. Making it fail is the smallest change that removes the contradiction instead of adding a third surface. AI council 2026-08-24, 2/2 convergent, on a maintainer delegation.",
|
|
85
|
-
"why_a_single_ci_run_is_enough": "The 'per-spawn' in this metric's name describes WHERE the cost is paid, not random sampling. The measurement is a deterministic census of revision-controlled inputs -- rules, the skill catalogue and CLAUDE.md, all read from the checkout -- so one CI run represents the checked-out revision exactly. Variation between two runs on the same commit would be a CHECKER DEFECT, never budget noise, and is asserted against by the repeatability test in tests/scripts/check_preamble_payload_budget.test.ts. Both council seats rejected the single-sample objection on this ground.",
|
|
86
|
-
"why_a_grace_ceiling": "HEAD measures 138,212 against a design ceiling of 107,646 -- 28.4 % over. Making the gate blocking at the design ceiling would fail EVERY pull request from the moment it lands, which is a self-inflicted CI stop rather than a gate: nobody can reduce 30,566 tokens inside the PR that turns the gate on. The grace ceiling is set AT the measurement so growth beyond today reds immediately while today's tree passes, and it ratchets DOWN toward the design ceiling as reductions land. It may never move UP. It expires at the milestone-1 date, at which point the design ceiling applies.",
|
|
87
|
-
"honest_limit": "Editing this workflow makes it FAIL; it does not make it REQUIRED. Whether a failing check blocks a merge is branch-protection configuration, which is a repo-admin action outside this change and outside an autonomous run. So the claim this record supports is 'the gate can now fail a PR', never 'the gate now blocks a merge'. Verify the latter with `gh api repos/:owner/:repo/rulesets` rather than by reading this file.",
|
|
88
|
-
"grace_ceiling_history": [
|
|
89
|
-
{
|
|
90
|
-
"from": 138212,
|
|
91
|
-
"to": 138273,
|
|
92
|
-
"at": "2026-09-02",
|
|
93
|
-
"why": "code-comment-discipline gained three Iron Law lines: a comment naming where the code came from is evidence in the wrong place, provenance is not a WHY, and a roadmap cannot license a comment the rule forbids. +61 tok after two condensations \u2014 the section was first written at +546, moved to its pointer target (code-clarity.md) at +172, and folded into the existing Iron Law block rather than opening a second one at +61. What remains is obligation surface that preservation-guard forbids condensing further, and it cannot move to the guideline because a rule whose Iron Law is elsewhere is a rule nobody reads. The defect it closes was measured, not argued: 509 German comment lines and 21 report- or provenance-shaped comments across 41 of 45 changed source files in a consumer repository, with both governing rules holding an enforcement hole \u2014 one naming a .md-only validator for a source-file clause, the other naming no enforcer at all. This raises the GRACE ceiling, which is a dated window (expires 2026-11-10), not the design ceiling of 107646, which this tree passed long before this change."
|
|
94
|
-
}
|
|
95
|
-
]
|
|
14
|
+
"excluded_buckets": [
|
|
15
|
+
"user-scope rules (~/.claude/rules/*.md) — ALREADY COUNTED, inside the `project-scope rules` gated bucket; excluded here only to avoid double-counting. CORRECTED 2026-08-30 (road-to-agent-turnaround 4.2, AI council 2/2 over two rounds). The previous reason read \"machine-dependent, not CI-checkable\", and that was FALSE in both halves: the layer is written by this package's own installer out of dist/agent-src/rules/, and which rules reach the GLOBAL layer rather than the project one is decided by frontmatter (`workspaces:` naming the maintainer workspace), not by anything on the developer's machine. `preamble_byte_census.censusDeliveredRulePayload` computes it from the projection alone, reading nothing under ~/. Measured 2026-08-30: 119 projected − 15 package-only = 104 delivered, matching the installed file count exactly; the 1.5 % char gap against the machine reading is install drift. Round 1 of the council voted to GATE the bucket on the premise that its bytes were invisible to CI; round 2 was given the checker's bucket definitions, found the 104 are a SUBSET of the 119 the gate already measures in full, and chose (a′): correct the reason, add the reconciliation test, leave the baseline alone. Adding the bucket would have moved the baseline ~111k for zero additional delivered payload. What remains genuinely unreported by this GATE is the 104 / 13 / 2 destination split, which the CENSUS now prints — the council placed it there until distinct destinations need independent budget enforcement.",
|
|
16
|
+
"tool definitions + dispatch prompt — no local source carries the request payload; the census reports it as an explicit residual",
|
|
17
|
+
"personas (src/agent-src/personas, 36 files, ~37,181 tok) — measured ZERO standing cost 2026-08-22: .claude/personas/ is written by this repo's generators (5 in-repo references) and read by no host contract; the host's real subagent surface is .claude/agents/, which this package writes 1 file into. Reversal condition: a host publishing a personas contract, or a generator folding persona prose into an instruction file that IS read. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md",
|
|
18
|
+
"contexts (src/agent-src/contexts, 58 files, ~114,042 tok) — measured ON-DEMAND 2026-08-22: 0 rules declare load_context_eager: (17 declare the lazy load_context:), and rule.schema.json:49 documents eager as the variant that counts against the per-rule budget. This records that no eager declaration exists, NOT that eager loading would be free — nobody has declared one, so nobody has measured it. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md",
|
|
19
|
+
"skill bodies (src/skills/*/SKILL.md, 290 files, ~595,843 tok) — measured ON-DEMAND 2026-08-22: censusSkillsCatalog (preamble_byte_census.ts:216-234) builds the gated catalog bucket from `- name: description` per skill and reads no body byte, which is why 290 skills totalling ~595,843 tok report as ~14.4k standing. Corollary: the description cap is the WHOLE skill-side lever, not a fragment of it. Evidence: agents/evidence/analysis/uncounted-prose-corpora-load-semantics.md"
|
|
20
|
+
],
|
|
21
|
+
"_bucket_classification_note": "All three additions above are EXCLUSIONS, not gated buckets, and that is the finding rather than a shortcut. A census bucket over a corpus measured at zero is a permanently green gate certifying nothing. The exclusions carry their measurement so the next pass that proposes one of these corpora as a token lever meets the number instead of the silence."
|
|
22
|
+
},
|
|
23
|
+
"baseline_tokens": 102520,
|
|
24
|
+
"baseline_history": [
|
|
25
|
+
{
|
|
26
|
+
"tokens": 102599,
|
|
27
|
+
"at": "2026-07-30",
|
|
28
|
+
"why": "registered with the gate"
|
|
96
29
|
},
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
"target_date": "2026-11-10",
|
|
102
|
-
"delta_from_target": 35692,
|
|
103
|
-
"delta_from_registration": 12619,
|
|
104
|
-
"registration_measurement": 125593,
|
|
105
|
-
"registered_at": "2026-08-15",
|
|
106
|
-
"decision": "PUBLISH AND HOLD. The adverse drift is recorded now and the original milestone date is retained. This is an EARLY RISK PUBLICATION, not a milestone miss -- 2026-11-10 has not arrived, and recording a miss before its date would be as wrong as re-dating silently. The file's own on_miss clause ('a missed milestone is PUBLISHED ... and never silently re-dated') applies at the date; this entry is the early warning that keeps the date falsifiable. AI council 2026-08-24, 2/2.",
|
|
107
|
-
"trajectory": "+12,619 tokens in the nine days from 2026-08-15 to 2026-08-24, i.e. roughly +1,400/day. Reaching 102,520 by 2026-11-10 from 138,212 needs a NET REDUCTION averaged over the remaining 78 days, against a measured net growth. One seat argued the derivative already falsifies the schedule model and that holding for a second miss preserves a signal nobody will act on; the other argued that holding is what keeps the checkpoint falsifiable at all. Recorded unresolved rather than settled, because both readings are defensible and the measurement is what decides between them on 2026-11-10.",
|
|
108
|
-
"committed_reduction_mechanism": "NONE. Stated explicitly because its absence is the finding: no mechanism in the tree is committed to closing 35,692 tokens by 2026-11-10. road-to-cost-parity-1-rule-payload-diet and road-to-standing-context-40k both name reductions and neither is executing. A schedule with no mechanism is a date, not a plan, and this field exists so the next reader does not have to infer that from silence."
|
|
30
|
+
{
|
|
31
|
+
"tokens": 102520,
|
|
32
|
+
"at": "2026-07-31",
|
|
33
|
+
"why": "free tightening: the measured tree came in 79 tok under the registered baseline, so the ratchet walks down to meet it. Not a claimed reduction — nothing was optimised; the point of a ratchet is that a lower measurement becomes the new ceiling instead of becoming unused headroom."
|
|
109
34
|
}
|
|
35
|
+
],
|
|
36
|
+
"headroom_pct": 5,
|
|
37
|
+
"target_tokens": {
|
|
38
|
+
"median": 40000,
|
|
39
|
+
"p95": 50000,
|
|
40
|
+
"_comment": "The roadmap's candidate numbers, kept as the destination. Reaching them needs reductions this package has not built yet; scope-dedup (C-3) removes a redundant COPY at the consumer, it does not shrink the suite itself."
|
|
41
|
+
},
|
|
42
|
+
"target_schedule": {
|
|
43
|
+
"_comment": "road-to-inbox-harvest-2026-08-d-context-ledger Step 5.3. The target above was a destination with no route: the ratchet fails on GROWTH only, so the corpus can drift away from 40k indefinitely without any gate objecting. These milestones give the destination a schedule. They are DATES WITH NUMBERS, not a derivation - no measurement in this tree says the estate can reach 85k by a given day, and pretending otherwise would be the invented-threshold failure this file's own baseline_history is careful to avoid. What they buy is that a miss becomes a published fact with an owner instead of a silent drift.",
|
|
44
|
+
"owner": "maintainer",
|
|
45
|
+
"measured_at_registration": {
|
|
46
|
+
"tokens": 125593,
|
|
47
|
+
"at": "2026-08-15",
|
|
48
|
+
"note": "MEASURED total at registration time, from check_preamble_payload_budget. It is ~23k ABOVE both baseline_tokens (102520) and the derived ceiling, i.e. the gate is red on the tree as it stands. The baseline is deliberately NOT raised to meet it - raising a ratchet baseline to clear a failing check is the config-weakening move this repo REFUSES. Corrected 2026-08-24 (AC-3): this used to assert the repo blocks it structurally -- the exact phrase is deliberately not reproduced here, because AC-3's check is a grep and quoting a retracted claim would leave the string in the file it was removed from. It over-claimed the mechanism. `block_config_weakening` is a PreToolUse guard that warns from five allowlist entries and blocks past twenty, is honoured only on the one host that denies, and does not gate a baseline edit at all -- a rising count there may be a legitimate ratchet reset the edit alone cannot distinguish. The refusal is real and it is carried by review and by this file's own record, not by construction. It is recorded here so the milestones below are planned against the real starting point rather than a stale one. Per-rule ranking: agents/evidence/analysis/rule-paths-coverage-census.md"
|
|
49
|
+
},
|
|
50
|
+
"milestones": [
|
|
51
|
+
{
|
|
52
|
+
"by": "2026-11-10",
|
|
53
|
+
"tokens": 102520,
|
|
54
|
+
"why": "Return to the registered baseline. This is a RECOVERY milestone, not progress toward 40k - it undoes drift rather than shrinking the estate. The date is the review_by both budget files already carry, so it needs no new calendar."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"by": "2027-02-10",
|
|
58
|
+
"tokens": 85000,
|
|
59
|
+
"why": "First real reduction. The rule-paths census shows 92.1% of rule weight sits in the auto tier and the top 20 files carry 35.5% of it, so a scoped tranche is the plausible mechanism - GATED on the paths-scoping consumer decision, which is a human call and may resolve against scoping entirely."
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"by": "2027-05-10",
|
|
63
|
+
"tokens": 60000,
|
|
64
|
+
"why": "Second reduction. No mechanism is committed for this one; it exists so the destination keeps a date after the first tranche lands, and it is the milestone most likely to be revised on evidence."
|
|
65
|
+
}
|
|
66
|
+
],
|
|
67
|
+
"on_miss": "A missed milestone is PUBLISHED - recorded here with the measured number and the reason - and never silently re-dated. Two consecutive misses mean the schedule was wrong or the destination is unreachable with the mechanisms available; either outcome is a decision to surface, not a date to move again.",
|
|
68
|
+
"revisit_if": "The paths-scoping decision resolves against scoping (removing the only named mechanism for the 85k milestone), or a measured reduction lands ahead of schedule and the remaining milestones are re-derived from it."
|
|
69
|
+
},
|
|
70
|
+
"reference_measurements": {
|
|
71
|
+
"measured_cold_start_median_tokens": 230930,
|
|
72
|
+
"c3_scope_dedup_reduction_pct": 38,
|
|
73
|
+
"c3_condition": "FIXTURE-ONLY as of 2026-07-31: the 38% was measured on a byte-identical two-scope fixture, and that condition is currently unreachable for every consumer because install.ts:2723/2725 stamp package:/source_path: into every installed rule while the in-repo projection stamps nothing. Aligning versions yields 0/110 twins, not 110/110. The figure is the mechanism's ceiling, NOT a realised saving, and this baseline therefore assumes no dedup. Reachability is the subject of road-to-dedup-reachability; run measure_scope_dedup for the live split.",
|
|
74
|
+
"_comment": "The measured median comes from Claude Code transcripts (deduped by message.id + requestId) and is context for the ratchet, never the gated number — it moves with the developer's own session history."
|
|
75
|
+
},
|
|
76
|
+
"ci_delivery": {
|
|
77
|
+
"workflow": ".github/workflows/standing-payload-delta.yml",
|
|
78
|
+
"trigger": "pull_request",
|
|
79
|
+
"posture": "blocking_on_ceiling_breach_with_grace_ceiling",
|
|
80
|
+
"design_ceiling": 107646,
|
|
81
|
+
"grace_ceiling": 138490,
|
|
82
|
+
"grace_measured_at": "2026-09-08",
|
|
83
|
+
"grace_end_date": "2026-11-10",
|
|
84
|
+
"decision": "OPTION (b) of b-preamble-ci-placement: fold into the existing standing-payload-delta.yml rather than add a per-PR job or a scheduled main job. That workflow already computes the neighbouring number and already DECLINED to fail on the premise that this gate 'already fails on growth' -- a premise that was false, because nothing in .github/ ran this gate at all. Making it fail is the smallest change that removes the contradiction instead of adding a third surface. AI council 2026-08-24, 2/2 convergent, on a maintainer delegation.",
|
|
85
|
+
"why_a_single_ci_run_is_enough": "The 'per-spawn' in this metric's name describes WHERE the cost is paid, not random sampling. The measurement is a deterministic census of revision-controlled inputs -- rules, the skill catalogue and CLAUDE.md, all read from the checkout -- so one CI run represents the checked-out revision exactly. Variation between two runs on the same commit would be a CHECKER DEFECT, never budget noise, and is asserted against by the repeatability test in tests/scripts/check_preamble_payload_budget.test.ts. Both council seats rejected the single-sample objection on this ground.",
|
|
86
|
+
"why_a_grace_ceiling": "HEAD measures 138,212 against a design ceiling of 107,646 -- 28.4 % over. Making the gate blocking at the design ceiling would fail EVERY pull request from the moment it lands, which is a self-inflicted CI stop rather than a gate: nobody can reduce 30,566 tokens inside the PR that turns the gate on. The grace ceiling is set AT the measurement so growth beyond today reds immediately while today's tree passes, and it ratchets DOWN toward the design ceiling as reductions land. It may never move UP. It expires at the milestone-1 date, at which point the design ceiling applies.",
|
|
87
|
+
"decision_record": "docs/decisions/ADR-264-standing-payload-grace-ceiling-may-not-rise.md -- resolves the contradiction a reader hits in why_a_grace_ceiling above, which says the ceiling is 'set AT the measurement' AND that 'It may never move UP', while grace_ceiling_history records it moving up twice. ADR-264 decides AGAINST the measurement framing and FOR the cap: the ceiling may not rise, standing-rule growth needs a compensating reduction elsewhere, and the two recorded raises stand as violations caused by the contradictory wording rather than as precedent. It also designs, and deliberately does NOT ship, a 128-token aggregate Iron Law reserve -- because the deciding seat's own condition was that wording without enforcement and protected approval is an exploitable trust gap, and this citation is therefore the whole of this file's edit. No bound here is changed by it.",
|
|
88
|
+
"honest_limit": "Editing this workflow makes it FAIL; it does not make it REQUIRED. Whether a failing check blocks a merge is branch-protection configuration, which is a repo-admin action outside this change and outside an autonomous run. So the claim this record supports is 'the gate can now fail a PR', never 'the gate now blocks a merge'. Verify the latter with `gh api repos/:owner/:repo/rulesets` rather than by reading this file.",
|
|
89
|
+
"grace_ceiling_history": [
|
|
90
|
+
{
|
|
91
|
+
"from": 138273,
|
|
92
|
+
"to": 138490,
|
|
93
|
+
"at": "2026-09-08",
|
|
94
|
+
"why": "token-efficiency gained the one-command-per-Bash-call section: a permission rule, not a token one, closing a case where the rule ACTIVELY ENCOURAGED the most expensive shape (\"NOT write shorter commands: the long commands are already the batching\"). The host evaluates each segment of a compound command against the allowlist independently, so one unmatched segment sends the whole call down the permission path; chaining converts N cheap authorizations into one expensive one. +290 tok after two condensations — first written at +746, measured, then cut to +353 by migrating the numbers, the substitution table and the carrier limits to token-efficiency-mechanics (loaded on demand, and this file already classifies contexts/ as on-demand rather than standing), then to +290 by migrating the correction note as well. What remains is the Iron Law block plus two paragraphs, and preservation-guard forbids condensing an Iron Law further. It cannot move to the mechanics file either: a rule whose Iron Law lives elsewhere is a rule nobody reads. The defect is measured, not argued — over 40,268 real Bash calls, 97.7% carry a shell metacharacter and 17.9% have a head token matching no allowlist pattern, of which 5,037 are a leading VAR= assignment, a shape no allowlist pattern can express. This raises the GRACE ceiling, a dated window expiring 2026-11-10, never the design ceiling of 107646."
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"from": 138212,
|
|
98
|
+
"to": 138273,
|
|
99
|
+
"at": "2026-09-02",
|
|
100
|
+
"why": "code-comment-discipline gained three Iron Law lines: a comment naming where the code came from is evidence in the wrong place, provenance is not a WHY, and a roadmap cannot license a comment the rule forbids. +61 tok after two condensations — the section was first written at +546, moved to its pointer target (code-clarity.md) at +172, and folded into the existing Iron Law block rather than opening a second one at +61. What remains is obligation surface that preservation-guard forbids condensing further, and it cannot move to the guideline because a rule whose Iron Law is elsewhere is a rule nobody reads. The defect it closes was measured, not argued: 509 German comment lines and 21 report- or provenance-shaped comments across 41 of 45 changed source files in a consumer repository, with both governing rules holding an enforcement hole — one naming a .md-only validator for a source-file clause, the other naming no enforcer at all. This raises the GRACE ceiling, which is a dated window (expires 2026-11-10), not the design ceiling of 107646, which this tree passed long before this change."
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"status_2026_08_24": {
|
|
105
|
+
"state": "at_risk",
|
|
106
|
+
"measured": 138212,
|
|
107
|
+
"target": 102520,
|
|
108
|
+
"target_date": "2026-11-10",
|
|
109
|
+
"delta_from_target": 35692,
|
|
110
|
+
"delta_from_registration": 12619,
|
|
111
|
+
"registration_measurement": 125593,
|
|
112
|
+
"registered_at": "2026-08-15",
|
|
113
|
+
"decision": "PUBLISH AND HOLD. The adverse drift is recorded now and the original milestone date is retained. This is an EARLY RISK PUBLICATION, not a milestone miss -- 2026-11-10 has not arrived, and recording a miss before its date would be as wrong as re-dating silently. The file's own on_miss clause ('a missed milestone is PUBLISHED ... and never silently re-dated') applies at the date; this entry is the early warning that keeps the date falsifiable. AI council 2026-08-24, 2/2.",
|
|
114
|
+
"trajectory": "+12,619 tokens in the nine days from 2026-08-15 to 2026-08-24, i.e. roughly +1,400/day. Reaching 102,520 by 2026-11-10 from 138,212 needs a NET REDUCTION averaged over the remaining 78 days, against a measured net growth. One seat argued the derivative already falsifies the schedule model and that holding for a second miss preserves a signal nobody will act on; the other argued that holding is what keeps the checkpoint falsifiable at all. Recorded unresolved rather than settled, because both readings are defensible and the measurement is what decides between them on 2026-11-10.",
|
|
115
|
+
"committed_reduction_mechanism": "NONE. Stated explicitly because its absence is the finding: no mechanism in the tree is committed to closing 35,692 tokens by 2026-11-10. road-to-cost-parity-1-rule-payload-diet and road-to-standing-context-40k both name reductions and neither is executing. A schedule with no mechanism is a date, not a plan, and this field exists so the next reader does not have to infer that from silence."
|
|
116
|
+
}
|
|
110
117
|
}
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
"src/agent-src/scripts/",
|
|
20
20
|
"src/agent-src/templates/scripts/",
|
|
21
21
|
"src/templates/",
|
|
22
|
+
"src/vendor/grammars/",
|
|
22
23
|
"tests/fixtures/hooks/",
|
|
23
24
|
"AGENTS.md",
|
|
24
25
|
"CHANGELOG.md",
|
|
@@ -61,7 +62,8 @@
|
|
|
61
62
|
"vendor/"
|
|
62
63
|
],
|
|
63
64
|
"npmignore_shadowed": [
|
|
64
|
-
"agents/"
|
|
65
|
+
"agents/",
|
|
66
|
+
"vendor/"
|
|
65
67
|
],
|
|
66
|
-
"shadowed_measured_in": "
|
|
68
|
+
"shadowed_measured_in": "unbuilt tree (payload carries no dist/cli/**)"
|
|
67
69
|
}
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
"skills": "src/skills/*/evals/triggers.json over src/skills/*/SKILL.md."
|
|
10
10
|
},
|
|
11
11
|
"seed": {
|
|
12
|
-
"rules": 0.
|
|
12
|
+
"rules": 0.9619,
|
|
13
13
|
"skills": 0.3344
|
|
14
14
|
},
|
|
15
15
|
"seed_provenance": {
|
|
16
|
-
"rules": "
|
|
16
|
+
"rules": "101 covered of 105 routed rules on 2026-09-08; was 94 of 105 (tier-1 21/24, tier-2 73/81) on 2026-08-25.",
|
|
17
17
|
"skills": "100 of 299 skills carry evals/triggers.json on 2026-08-30.",
|
|
18
18
|
"finding": "The gap between the two seeds IS the roadmap defect D1, quantified. The rules surface is ~90% covered by a deterministic corpus that can fail a PR; the skills surface -- the one production routes on -- is 25%, covered only by a harness that is advisory only, never gating (rule_trigger_eval.ts:4)."
|
|
19
19
|
},
|
|
@@ -38,6 +38,13 @@
|
|
|
38
38
|
"from": 0.3144,
|
|
39
39
|
"to": 0.3344,
|
|
40
40
|
"reason": "road-to-governed-harness-evolution Phase 2.4 authored six wave-1 corpus files (94 -> 100 of 299), chosen by 2.2's inbound-confusion-edge ordering rather than picked. The seed rises so the gain cannot be given back silently; it may never be LOWERED. Surfaced by check_routing_coverage.test.ts, which asserts seed == live measurement in BOTH directions -- a seed left below the tree is a red test, not slack, which is the same asymmetry check_source_size_budget uses and for the same reason: nothing else would force the ratchet to follow a gain."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"date": "2026-09-08",
|
|
44
|
+
"scope": "rules",
|
|
45
|
+
"from": 0.8952,
|
|
46
|
+
"to": 0.9619,
|
|
47
|
+
"reason": "road-to-delivery-for-every-host authored seven routing-matrix fixtures -- council-availability, evaluator-independence, fix-what-you-see, missing-skill-recovery, playbook-precedence, recurring-criticism, self-repair-loop (94 -> 101 of 105 routed rules). First entry on the RULES scope; the three above are all skills. The seed rises because this file's own test asserts seed == live measurement in both directions, so the gain would otherwise red the suite for doing what the ratchet encourages. It may never be LOWERED."
|
|
41
48
|
}
|
|
42
49
|
]
|
|
43
50
|
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does the CLAUDE host-global layer already carry a given artefact?
|
|
3
|
+
*
|
|
4
|
+
* **Why this module exists — the veto it replaces.**
|
|
5
|
+
*
|
|
6
|
+
* ADR-236 partitions artefacts between `~/.claude/**` and `<repo>/.claude/**`.
|
|
7
|
+
* Its first implementation asked ONE question for the whole run: does
|
|
8
|
+
* `installed.lock` record the same version as this checkout, and does a content
|
|
9
|
+
* fingerprint of the host layer still match what the installer stamped? Any
|
|
10
|
+
* answer but yes fell back to the full projection.
|
|
11
|
+
*
|
|
12
|
+
* That veto is repo-wide, and it fires on facts that have nothing to do with any
|
|
13
|
+
* individual artefact. Measured 2026-09-07 on the maintainer machine, `installed.lock`
|
|
14
|
+
* at 14.19.0 against a 14.21.0 checkout:
|
|
15
|
+
*
|
|
16
|
+
* ```
|
|
17
|
+
* rules global 104 · project 15 · BOTH 0 ← already correct
|
|
18
|
+
* skills global 307 · project 414 · BOTH 261 ← the veto's cost
|
|
19
|
+
* personas global 32 · project 29 · BOTH 29
|
|
20
|
+
* commands global 94 · project 0 · BOTH 0
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The rules row is correct because rules are partitioned by
|
|
24
|
+
* {@link hostLayerCarries} — per host directory, on that directory's own
|
|
25
|
+
* contents — while skills, personas and commands hung off the version veto and
|
|
26
|
+
* were therefore delivered twice, in every session, for as long as the install
|
|
27
|
+
* lagged a release. A version number is not evidence about an artefact.
|
|
28
|
+
*
|
|
29
|
+
* This module supplies the evidence the other three families were missing, in
|
|
30
|
+
* the shape rules already use: read the host directory, and answer per NAME.
|
|
31
|
+
*
|
|
32
|
+
* **Why per-name rather than per-family.**
|
|
33
|
+
*
|
|
34
|
+
* A partition is a removal and has no repair path — withholding a name the
|
|
35
|
+
* surviving layer does not hold deletes it from every session. Asking per name
|
|
36
|
+
* makes the fail-safe granular: an artefact the host layer lacks stays in the
|
|
37
|
+
* project layer on its own, and one absent name no longer keeps 300 duplicates
|
|
38
|
+
* alive with it. That is the property the repo-wide veto could not express.
|
|
39
|
+
*
|
|
40
|
+
* **Honest limits.**
|
|
41
|
+
*
|
|
42
|
+
* Presence of a NAME, never equality of CONTENT — and never PROVENANCE either.
|
|
43
|
+
* Two consequences, the second added 2026-09-07 after a neutral review named it.
|
|
44
|
+
*
|
|
45
|
+
* **Staleness.** A stale `~/.claude/skills/foo` satisfies this check, so an
|
|
46
|
+
* install that lags a release delivers that release's older copy of `foo` rather
|
|
47
|
+
* than two copies. That is the trade the
|
|
48
|
+
* owner chose on 2026-09-07, stated rather than implied: the remedy for a stale
|
|
49
|
+
* global layer is `agent-config install`, and {@link verifyHostLayer} still
|
|
50
|
+
* reports the staleness so a run says so out loud.
|
|
51
|
+
*
|
|
52
|
+
* **Shared namespace.** `~/.claude/skills` is not this package's directory. On
|
|
53
|
+
* the machine this was written, 307 entries = 299 package skills + 8 Cloudflare
|
|
54
|
+
* PLUGIN skills. No name collides today, but the mechanism needs no collision to
|
|
55
|
+
* be real: a plugin — or a hand-made `~/.claude/skills/<name>` — whose name
|
|
56
|
+
* matches a package skill makes this function withhold the package's project-layer
|
|
57
|
+
* copy, and the session then loads the foreign body under the package's skill
|
|
58
|
+
* name. The withhold is a removal with no repair path, which is what makes it
|
|
59
|
+
* worth naming rather than filing.
|
|
60
|
+
*
|
|
61
|
+
* **A NEIGHBOURING defect that WAS fixed, 2026-09-07.** This paragraph claimed
|
|
62
|
+
* "the skill is still delivered, under the right name, from one layer" — and that
|
|
63
|
+
* was false while `_readLayer` returned every directory name without checking
|
|
64
|
+
* that `SKILL.md` resolved: an empty `~/.claude/skills/<name>/` withheld the
|
|
65
|
+
* project copy and the artefact came from neither layer. A name now counts as
|
|
66
|
+
* carried only when the artefact behind it resolves, so the sentence above is
|
|
67
|
+
* true as written.
|
|
68
|
+
*
|
|
69
|
+
* NOT FIXED here, deliberately: distinguishing a package artefact from a foreign
|
|
70
|
+
* one needs a provenance marker the installer does not write today, and inventing
|
|
71
|
+
* one in this module would be a guess dressed as a check. What IS true is that
|
|
72
|
+
* the failure is loud in the direction that matters — the skill is still
|
|
73
|
+
* delivered, under the right name, from one layer — and silent only about whose
|
|
74
|
+
* body it is.
|
|
75
|
+
*
|
|
76
|
+
* Contract, same as its siblings in this directory: no `process.exit`, no CLI
|
|
77
|
+
* entry, node builtins only — it ships inside the consumer installer bundle.
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
import * as fs from 'node:fs';
|
|
81
|
+
import * as os from 'node:os';
|
|
82
|
+
import * as path from 'node:path';
|
|
83
|
+
|
|
84
|
+
import type { CarriesVerdict } from './globalRuleLayers.js';
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The artefact families whose project-layer copy is decided here.
|
|
88
|
+
*
|
|
89
|
+
* `rules` is deliberately ABSENT. Rules project to five host directories and are
|
|
90
|
+
* already partitioned per directory by {@link globalRuleLayers.hostLayerCarries};
|
|
91
|
+
* adding a claude-only row for them would put a second decision on the one
|
|
92
|
+
* family that does not need it. Skills, personas and the colon-form commands
|
|
93
|
+
* project to `.claude/` alone, which is why one host is the whole answer for them.
|
|
94
|
+
*/
|
|
95
|
+
export type ClaudeArtefactFamily = 'skills' | 'personas' | 'commands';
|
|
96
|
+
|
|
97
|
+
/** Family → its directory under the user's home. */
|
|
98
|
+
export const CLAUDE_ARTEFACT_DIRS: Readonly<Record<ClaudeArtefactFamily, string>> = {
|
|
99
|
+
skills: '.claude/skills',
|
|
100
|
+
personas: '.claude/personas',
|
|
101
|
+
commands: '.claude/commands',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const _defaultHome = (): string => process.env['HOME'] ?? os.homedir();
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* One directory read per (family, home) per process.
|
|
108
|
+
*
|
|
109
|
+
* The generators ask per NAME — hundreds of times for skills, ~90 for commands —
|
|
110
|
+
* and the `commands` read walks a tree. Without the memo the per-name grain that
|
|
111
|
+
* makes the fail-safe granular would also make it quadratic. `null` is memoised
|
|
112
|
+
* too: an unreadable layer stays unreadable for the run, and re-probing it once
|
|
113
|
+
* per name would answer the same thing 300 times.
|
|
114
|
+
*/
|
|
115
|
+
const _memo = new Map<string, ReadonlySet<string> | null>();
|
|
116
|
+
|
|
117
|
+
/** Test seam — drop the memo so one process can read a layer it just changed. */
|
|
118
|
+
export function _resetClaudeLayerMemoForTest(): void {
|
|
119
|
+
_memo.clear();
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Absolute path to the host-global directory for one family. */
|
|
123
|
+
export function claudeLayerPath(family: ClaudeArtefactFamily, home: string = _defaultHome()): string {
|
|
124
|
+
return path.join(home, CLAUDE_ARTEFACT_DIRS[family]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* The names the host layer holds for one family, or null when it cannot be read.
|
|
129
|
+
*
|
|
130
|
+
* Null is a real answer and every caller must read it as "no evidence, withhold
|
|
131
|
+
* nothing" — not as an empty layer, which would authorise withholding everything.
|
|
132
|
+
*
|
|
133
|
+
* The name shape is the family's own, because that is what the caller compares
|
|
134
|
+
* against: a skill is a directory basename (`design-tokens`), a persona a
|
|
135
|
+
* filename (`ai-agent.md`), and a command a posix path relative to the commands
|
|
136
|
+
* root (`roadmap/process-full.md` for the colon form, `agent-status.md` for a
|
|
137
|
+
* flat one). `commands` therefore walks; the other two list one level.
|
|
138
|
+
*/
|
|
139
|
+
export function claudeLayerNames(
|
|
140
|
+
family: ClaudeArtefactFamily,
|
|
141
|
+
home: string = _defaultHome(),
|
|
142
|
+
): ReadonlySet<string> | null {
|
|
143
|
+
const dir = claudeLayerPath(family, home);
|
|
144
|
+
const key = `${family}\u0000${dir}`;
|
|
145
|
+
const hit = _memo.get(key);
|
|
146
|
+
if (hit !== undefined) {
|
|
147
|
+
return hit;
|
|
148
|
+
}
|
|
149
|
+
const answer = _readLayer(family, dir);
|
|
150
|
+
_memo.set(key, answer);
|
|
151
|
+
return answer;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function _readLayer(family: ClaudeArtefactFamily, dir: string): ReadonlySet<string> | null {
|
|
155
|
+
let entries: fs.Dirent[];
|
|
156
|
+
try {
|
|
157
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
158
|
+
} catch {
|
|
159
|
+
return null;
|
|
160
|
+
}
|
|
161
|
+
if (family !== 'commands') {
|
|
162
|
+
// A name is only CARRIED when the artefact behind it resolves. Corrected
|
|
163
|
+
// 2026-09-07 after a second neutral review: this returned every dirent
|
|
164
|
+
// name, so an empty `~/.claude/skills/<name>/` — no `SKILL.md` — made
|
|
165
|
+
// `claudeLayerHolds` true, the project layer withheld its copy, and the
|
|
166
|
+
// artefact was then delivered from NEITHER layer. That is the
|
|
167
|
+
// removal-with-no-repair-path this module's own limits section says it
|
|
168
|
+
// avoids, and the first fixture written for the wrapper gate created
|
|
169
|
+
// exactly that state and called the withhold correct.
|
|
170
|
+
//
|
|
171
|
+
// Reachable without a crash: a skill directory holding some other file
|
|
172
|
+
// survives `reap_stale`, and `install.ts` mkdirs before it writes.
|
|
173
|
+
// `readProjectedCatalogue` already keys on `SKILL.md` for this reason;
|
|
174
|
+
// the `commands` branch below already required a real `.md`. This is the
|
|
175
|
+
// same requirement for the two families that lacked it.
|
|
176
|
+
const carries = (name: string): boolean => {
|
|
177
|
+
const abs = path.join(dir, name);
|
|
178
|
+
try {
|
|
179
|
+
if (fs.statSync(abs).isDirectory()) {
|
|
180
|
+
return fs.statSync(path.join(abs, 'SKILL.md')).isFile();
|
|
181
|
+
}
|
|
182
|
+
return name.endsWith('.md');
|
|
183
|
+
} catch {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
return new Set(
|
|
188
|
+
entries
|
|
189
|
+
.map((e) => e.name)
|
|
190
|
+
.filter((n) => n !== 'README.md')
|
|
191
|
+
.filter(carries),
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const out = new Set<string>();
|
|
195
|
+
const walk = (abs: string, rel: string): void => {
|
|
196
|
+
let kids: fs.Dirent[];
|
|
197
|
+
try {
|
|
198
|
+
kids = fs.readdirSync(abs, { withFileTypes: true });
|
|
199
|
+
} catch {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
for (const k of kids) {
|
|
203
|
+
const childRel = rel === '' ? k.name : `${rel}/${k.name}`;
|
|
204
|
+
if (k.isDirectory()) {
|
|
205
|
+
walk(path.join(abs, k.name), childRel);
|
|
206
|
+
} else if (k.name.endsWith('.md') && k.name !== 'README.md') {
|
|
207
|
+
out.add(childRel);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
walk(dir, '');
|
|
212
|
+
return out;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Does the host layer hold this one name?
|
|
217
|
+
*
|
|
218
|
+
* The single-name form of {@link keepInProjectLayer}, for a caller that decides
|
|
219
|
+
* inside a loop and already knows the host-side name. `false` on an unreadable
|
|
220
|
+
* layer — no evidence, so nothing is withheld.
|
|
221
|
+
*/
|
|
222
|
+
export function claudeLayerHolds(
|
|
223
|
+
family: ClaudeArtefactFamily,
|
|
224
|
+
name: string,
|
|
225
|
+
home: string = _defaultHome(),
|
|
226
|
+
): boolean {
|
|
227
|
+
return claudeLayerNames(family, home)?.has(name) ?? false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Which of `names` the host layer carries, and which it does not.
|
|
232
|
+
*
|
|
233
|
+
* `carries` is the all-or-nothing answer the rules path reports; `missing` is the
|
|
234
|
+
* one callers act on, because withholding is decided per name. An empty `names`
|
|
235
|
+
* is vacuously carried — withholding nothing needs no evidence.
|
|
236
|
+
*/
|
|
237
|
+
export function claudeLayerCarries(
|
|
238
|
+
family: ClaudeArtefactFamily,
|
|
239
|
+
names: readonly string[],
|
|
240
|
+
home: string = _defaultHome(),
|
|
241
|
+
): CarriesVerdict {
|
|
242
|
+
const layerPath = claudeLayerPath(family, home);
|
|
243
|
+
const have = claudeLayerNames(family, home);
|
|
244
|
+
if (have === null) {
|
|
245
|
+
return { carries: false, layerPath, missing: [...names], reason: 'layer-absent' };
|
|
246
|
+
}
|
|
247
|
+
const missing = names.filter((n) => !have.has(n));
|
|
248
|
+
return {
|
|
249
|
+
carries: missing.length === 0,
|
|
250
|
+
layerPath,
|
|
251
|
+
missing,
|
|
252
|
+
reason: missing.length === 0 ? 'carries' : 'missing-names',
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The subset of `names` the project layer must still deliver.
|
|
258
|
+
*
|
|
259
|
+
* The one function the generators call. Returns the names the host layer does
|
|
260
|
+
* NOT hold, in the order given — so an unreadable layer returns everything
|
|
261
|
+
* (fail safe) and a complete layer returns nothing.
|
|
262
|
+
*/
|
|
263
|
+
export function keepInProjectLayer(
|
|
264
|
+
family: ClaudeArtefactFamily,
|
|
265
|
+
names: readonly string[],
|
|
266
|
+
home: string = _defaultHome(),
|
|
267
|
+
): string[] {
|
|
268
|
+
const have = claudeLayerNames(family, home);
|
|
269
|
+
if (have === null) {
|
|
270
|
+
return [...names];
|
|
271
|
+
}
|
|
272
|
+
return names.filter((n) => !have.has(n));
|
|
273
|
+
}
|