@event4u/agent-config 10.1.0 → 10.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +110 -167
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/reference-repo.md +62 -0
- package/dist/agent-src/commands/roadmap/create.md +4 -1
- package/dist/agent-src/commands/roadmap/next.md +38 -0
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +22 -1
- package/dist/agent-src/contexts/contracts/frugality-charter.md +1 -1
- package/dist/agent-src/personas/advisors/first-principles.md +6 -0
- package/dist/agent-src/rules/active-remediation.md +53 -2
- package/dist/agent-src/rules/code-provenance.md +66 -4
- package/dist/agent-src/rules/content-quoting-floor.md +6 -0
- package/dist/agent-src/rules/context-hygiene.md +39 -0
- package/dist/agent-src/rules/design-fidelity.md +49 -3
- package/dist/agent-src/rules/token-budget-discipline.md +3 -2
- package/dist/agent-src/rules/token-efficiency.md +22 -0
- package/dist/agent-src/skills/ai-council/SKILL.md +28 -6
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/design-review/references/verification-automation.md +23 -1
- package/dist/agent-src/skills/fe-design/SKILL.md +27 -3
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +30 -0
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +11 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +9 -9
- package/dist/agent-src/skills/skill-writing/SKILL.md +4 -0
- package/dist/agent-src/skills/token-optimizer/SKILL.md +5 -1
- package/dist/agent-src/templates/agent-settings.md +12 -28
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +2 -2
- package/dist/agent-src/templates/persona.md +26 -0
- package/dist/agent-src/templates/roadmaps.md +27 -7
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -1
- package/dist/agent-src/templates/skill.md +35 -1
- package/dist/cli/commands/commands.candidates.test.js +257 -0
- package/dist/cli/commands/commands.candidates.test.js.map +1 -0
- package/dist/cli/commands/commands.js +199 -0
- package/dist/cli/commands/commands.js.map +1 -1
- package/dist/cli/main.js +1 -0
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/registry.js +2 -2
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-3JUTT26I.js → chunk-6ENSXSBW.js} +1 -1
- package/dist/cli-delegate/{chunk-TVGXH7VA.js → chunk-6LOPHJLD.js} +1 -1
- package/dist/cli-delegate/{chunk-XT2YTJCZ.js → chunk-AYKQGIBG.js} +4 -0
- package/dist/cli-delegate/{chunk-HJSKTMIF.js → chunk-BV3WR67D.js} +3 -19
- package/dist/cli-delegate/{chunk-HTIU3W56.js → chunk-BWMXLD4B.js} +6 -2
- package/dist/cli-delegate/{chunk-2H5SE7DU.js → chunk-FV34EGEX.js} +166 -39
- package/dist/cli-delegate/{chunk-XTC5ARB6.js → chunk-IR3IVRVC.js} +7 -2
- package/dist/cli-delegate/{chunk-LRXJK6GN.js → chunk-JMQZ7MM5.js} +1 -1
- package/dist/cli-delegate/{chunk-NX5AXSLD.js → chunk-X3VOARZD.js} +3 -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 +881 -204
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +6 -5
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +2 -2
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +44 -9
- package/dist/cli-delegate/cmd_settings_get.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +21 -21
- 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 +204 -44
- package/dist/install/install.mjs +9 -21
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1337 -490
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +25 -2
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/_lib/compile_time_toggles.js +4 -3
- package/dist/scripts/_lib/compile_time_toggles.js.map +1 -1
- package/dist/server/io/yamlIO.js +0 -2
- package/dist/server/io/yamlIO.js.map +1 -1
- package/dist/server/schemas/settings.js +3 -9
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/settingsClasses.js +64 -2
- package/dist/shared/settingsClasses.js.map +1 -1
- package/dist/ui/assets/{index-B5qGYOJo.js → index-5fXOHoVh.js} +2 -2
- package/dist/ui/assets/index-5fXOHoVh.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/contracts/settings-classes.md +307 -145
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +54 -0
- package/docs/guidelines/agent-infra/layered-settings.md +1 -2
- package/docs/guidelines/agent-infra/symptom-driven-harvest-loop.md +104 -0
- package/docs/guidelines/design-antipatterns.md +79 -10
- package/docs/guidelines/design-fidelity-mechanics.md +120 -2
- package/package.json +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -1
- package/src/config/agent-settings.template.yml +12 -30
- package/src/config/gate-coverage.yml +42 -8
- package/src/config/gate-violation-baselines.json +15 -3
- package/src/config/profiles/balanced.ini +0 -2
- package/src/config/profiles/full.ini +1 -3
- package/src/config/profiles/minimal.ini +0 -2
- package/src/config/review-prompt-binding-baseline.json +19 -0
- package/src/config/rule-enforcement-baseline.json +0 -1
- package/src/scripts/_cli/cmd_session_recycle.ts +66 -9
- package/src/scripts/_cli/handoff_sessions.ts +3 -2
- package/src/scripts/_dispatch.bash +1 -0
- package/src/scripts/_lib/agent_settings.ts +25 -2
- package/src/scripts/_lib/bench_telegraph.ts +1 -1
- package/src/scripts/_lib/cc_transcript.ts +25 -0
- package/src/scripts/_lib/compile_time_toggles.ts +4 -3
- package/src/scripts/ai_council/clients.ts +243 -34
- package/src/scripts/cache_realization_report.ts +3 -2
- package/src/scripts/check_ci_local_parity.ts +60 -6
- package/src/scripts/check_review_prompt_binding.ts +553 -0
- package/src/scripts/check_rule_projection_integrity.ts +3 -1
- package/src/scripts/check_suppression_hygiene.ts +19 -3
- package/src/scripts/conformance_scan.ts +282 -9
- package/src/scripts/cost/track.mjs +10 -4
- package/src/scripts/council_cli.ts +33 -3
- package/src/scripts/design_slop_rules.test.ts +37 -6
- package/src/scripts/design_slop_rules.ts +150 -22
- package/src/scripts/dispatch_r2_reviewer.ts +15 -1
- package/src/scripts/external_sources_denylist.json +3 -1
- package/src/scripts/hook_manifest.yaml +17 -6
- package/src/scripts/hooks/block_unauthorized_git.ts +54 -8
- package/src/scripts/hooks/delegation_nudge_hook.ts +33 -8
- package/src/scripts/hooks/evidence_independence.ts +61 -9
- package/src/scripts/hooks/session_eol_hook.ts +123 -3
- package/src/scripts/hooks/turn_end_gate_hook.ts +31 -0
- package/src/scripts/hooks/ui_route_nudge_hook.ts +55 -2
- package/src/scripts/lint_design_antipattern_parity.ts +201 -0
- package/src/scripts/lint_harvest_provenance.ts +483 -0
- package/src/scripts/lint_settings_classes.ts +273 -11
- package/src/scripts/lint_skill_router_head.ts +254 -0
- package/src/scripts/lint_symptom_intake.ts +241 -0
- package/src/scripts/report_consultation_rate.ts +128 -2
- package/src/scripts/report_skill_activation.ts +6 -4
- package/src/scripts/report_skill_obligation_violations.ts +2 -1
- package/src/scripts/run_skill_evals.ts +10 -5
- package/src/scripts/schemas/persona.schema.json +8 -0
- package/src/scripts/schemas/provenance-harvest.schema.json +39 -0
- package/src/scripts/sweep_dead_scan_roots.ts +51 -0
- package/src/server/io/yamlIO.ts +0 -2
- package/src/server/schemas/settings.ts +3 -19
- package/src/shared/settingsClasses.ts +93 -2
- package/dist/ui/assets/index-B5qGYOJo.js.map +0 -1
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$comment": "Violation ratchet for gates whose dead scan root was repaired. A repair REVEALS pre-existing debt; it does not create it. Each entry records the count revealed at repair time — the gate fails only when its count RISES. Lowering a count is a normal commit; raising one is a defect. Entries expire: a baseline that has not dropped within 56 days of `landed` (or of `reaffirmed.date`) fails its gate, because a number that never moves is suppression with extra steps. Mechanism + rationale: src/scripts/_lib/gate_baseline.ts. Decision: AI council 2026-08-02, roadmap road-to-gates-that-can-fail blocker `dead-gate-finding-triage`.",
|
|
3
3
|
"gates": {
|
|
4
|
+
"ci-parity:local-only": {
|
|
5
|
+
"count": 167,
|
|
6
|
+
"landed": "2026-08-13",
|
|
7
|
+
"note": "Gates that `task ci` runs and no workflow does — the direction this manifest's own header calls the one that let real defects merge (16 stale index rows reached the trunk behind a local-only check-index). Revealed, not created, by repairing the checker's extraction: check_ci_local_parity built its CI-side set by regexing RAW workflow text for `task <name>` and expanding each closure, comments included. Several workflow comments contain the literal string `task ci` while stating that no workflow invokes it, so `ci` was read as an invocation and its full 247-gate closure entered the 'runs in CI' set. The count was therefore 0 BY CONSTRUCTION, and the prose documenting the gap was what suppressed it. Measured at the repair: CI-side 273 → 106, undeclared_local_only 0 → 167 of 247 local gates (68%). Baselined rather than hard-failed because 167 findings nobody can clear in the change that reveals them is the gate-that-lands-as-N-instant-blockers failure this repository has refused before; shrink-only keeps the property that matters, namely that a NEW gate registered in `task ci` with no workflow reds immediately. Lowering happens by wiring gates into workflows or declaring them under `local_only:` with a reason. Note the punchline, because it bounds the risk of this entry: check_ci_local_parity ITSELF runs in no workflow, so it is its own finding and this ratchet has no remote reach either — clearing the backlog is what gives it teeth.",
|
|
8
|
+
"falsifier": "./scripts-run src/scripts/check_ci_local_parity"
|
|
9
|
+
},
|
|
4
10
|
"check_no_new_legacy_path:hardcoded-scan-roots": {
|
|
5
11
|
"count": 48,
|
|
6
12
|
"landed": "2026-08-09",
|
|
@@ -22,9 +28,9 @@
|
|
|
22
28
|
"note": "Markdown links between source artefacts that cross a pack boundary the source pack's `requires` graph does not reach — revealed when the scan root moved off the deleted packages/ tree to the live corpus (0 -> 753 artefacts scanned). 82 distinct pack pairs across 131 source files; largest single class is engineering-base -> meta (73), skills in a consumer-installable pack citing this package's own maintenance skills. Each is a link that dangles for a consumer who installs the source pack without the target's. Judged real drift rather than an over-strict gate because it is long-tailed — top bucket 22%, no single systematic cause — unlike the lint_artefact_frontmatter case, where one required shape had 0-of-288 adoption. Fix per finding: add the edge to packs.yml requires, retarget the link, or move the artefact."
|
|
23
29
|
},
|
|
24
30
|
"check_gate_completeness": {
|
|
25
|
-
"count":
|
|
26
|
-
"landed": "2026-08-
|
|
27
|
-
"note": "Ledger adoption ratchet.
|
|
31
|
+
"count": 216,
|
|
32
|
+
"landed": "2026-08-12",
|
|
33
|
+
"note": "Ledger adoption ratchet. 216 of 244 registered gates do not yet carry per-target completeness accounting (`_lib/gate_ledger.ts`) or a `// ledger-exempt:` marker. This is deliberately NOT a sweep: road-to-skill-ecosystem-gate-integrity Phase 1 adopts exactly three gates (lint_framework_leakage, check_references, lint_roadmap_complexity) plus the checker itself to prove the interface, and the remainder migrates as gates are touched. Shrink-only from here — the number may not grow, so a NEW gate must adopt or exempt.",
|
|
28
34
|
"falsifier": "./scripts-run src/scripts/check_gate_completeness"
|
|
29
35
|
},
|
|
30
36
|
"check_depth_budget": {
|
|
@@ -37,6 +43,12 @@
|
|
|
37
43
|
"landed": "2026-08-06",
|
|
38
44
|
"note": "Self-test adoption ratchet, expressed as a shrink-only NON-adopter count. 24 of the 32 gates registered enforced in gate-coverage.yml carry neither a `--self-test` (`_lib/gate_self_test.ts`) nor a `// self-test-exempt: <reason>` marker, so nothing proves they DISCRIMINATE — an enforced `scanned:` floor proves a gate read something, only a self-test proves the reading changes the verdict. Deliberately NOT an adoption column ratcheted upward: check_gate_coverage.ts's own report_hardening_ratchet rejects that shape in this same file (\"grades the solution instead of the problem\"), and the sibling adoption ratchet (check_gate_completeness) is already a non-adopter count. Population is the manifest, not every script under src/scripts: padding the manifest to move this number would mean adding a floor per row. Shrink-only from here — a NEW registered gate must adopt or exempt. Migration mechanism, same as the ledger ratchet above: gates adopt as they are touched, not in a sweep — the eight current adopters were each added alongside a real change to that gate. The 56-day non-stagnation clause applies unchanged, so a count parked here fails on its own.",
|
|
39
45
|
"falsifier": "./scripts-run src/scripts/check_gate_coverage"
|
|
46
|
+
},
|
|
47
|
+
"lint_settings_classes:derivable-surface": {
|
|
48
|
+
"count": 83,
|
|
49
|
+
"landed": "2026-08-12",
|
|
50
|
+
"note": "Anti-regrowth ratchet for the settings disposition axis. 83 of 134 template leaves are classified `derivable` — the mechanism can decide from the situation, so the key should not exist — and each names the replacement that would decide instead (`lint_settings_classes` rejects the bare label, which is what keeps the class from becoming a synonym for \"inconvenient to keep\"). This is a DELETION QUEUE, not 83 deletions: a row stays until the mechanism it names actually exists, and deleting a key ahead of its mechanism is a silently-changed default rather than a simplification. Shrink-only from here, which is the anti-regrowth half — a NEW key classified `derivable` raises the number and reds, so a key whose answer the situation already carries cannot be added at all. The 56-day non-stagnation clause is the other half and is wanted here rather than tolerated: a queue that never drains is a stated direction nobody is walking. The queue opened at 88 of 140 on 2026-08-12 and drained to 83 of 134 the same day: six keys had no reader whatsoever (telegraph.speak_scope, chat_history.max_size_kb, chat_history.on_overflow, quality.wait_for_remote_ci, screenshots.data_bearing_gate, legal_review_prep.consented_at), five of them `derivable` and one a `consent` row reclassified because a published-egress confirmation is a Hard Floor no setting may lift. An unread key is the only deletion that cannot silently change a default, which is why that batch went first. The classification itself is docs/contracts/settings-classes.md.",
|
|
51
|
+
"falsifier": "./scripts-run src/scripts/lint_settings_classes"
|
|
40
52
|
}
|
|
41
53
|
}
|
|
42
54
|
}
|
|
@@ -13,7 +13,5 @@ rule_loading_tier=full
|
|
|
13
13
|
; via the legacy mapping (minimal->off, balanced->essential, full->full).
|
|
14
14
|
discipline_profile=full
|
|
15
15
|
|
|
16
|
-
; Chat history (crash recovery) — full = tool-level traceability
|
|
16
|
+
; Chat history (crash recovery) — full = tool-level traceability
|
|
17
17
|
chat_history_frequency=per_tool
|
|
18
|
-
chat_history_max_size_kb=512
|
|
19
|
-
chat_history_on_overflow=condense
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema_note": "Consumed by src/scripts/check_review_prompt_binding.ts. An entry EXEMPTS one committed review prompt from the sha256(prompt.md) == prompt_hash check. Both hashes are pinned on purpose: if either moves, the entry stops matching and the gate reds, so a later repair and a further corruption are equally visible. A pre-loaded verdict is NEVER suppressed by the hash exemption alone — it needs its own `steered_ack` block on the entry, which pins the exact matched phrase, so growth of the shared steering predicate cannot turn committed prompts into permanent unfixable blocks and cannot be waved through silently either.",
|
|
3
|
+
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"slug": "zcs-close-2026-08-09",
|
|
6
|
+
"declared": "48eae5ad829b375c4667fcd72ecc482cbd62ae90788134ca46a81eacce267d49",
|
|
7
|
+
"actual": "f83ee959abb7f8c333bc89f117d3dee93f0e7b2a816c4593471aeda9b18c302d",
|
|
8
|
+
"reason": "Explained and closed at the cause: the archival sweep rewrote a roadmap path INSIDE this committed reviewer prompt while migrating inbound references. The cause was fixed on 2026-08-11 by _FROZEN_RECORD_PREFIXES in the sweep. Not repaired because repair is not available here: the recorded hash was produced by the ORIGINAL prompt text, so rewriting the hash to match the corrupted text would fabricate a binding that never existed, and restoring the text from history would erase the only evidence that the sweep reached inside a committed round at all. An earlier revision of this entry cited section 2.7 as forbidding the edit; that citation was wrong and is corrected here, because section 2.7 says the rename is an archival step and never an edit ban on a live artefact.",
|
|
9
|
+
"falsifier": "npx tsx src/scripts/check_review_prompt_binding.ts --json # the recorded actual hash stops matching the committed prompt.md, i.e. the record moved after all"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"slug": "feat-parallel-session-collision-hardening",
|
|
13
|
+
"declared": "d996e8e86a6e21a7cad66963f70219e59fe8cb96bca0bd1adceeea7ad2565c4b",
|
|
14
|
+
"actual": "dae1f14e90be94e71415e520b08222ea819488188d670de280d874803de1035c",
|
|
15
|
+
"reason": "Current and UNEXPLAINED, and it is the empirical justification for this gate. The prompt.md has a single commit dated 2026-08-12, after the sweep fix, so the sweep did not cause it; the declared hash never matched the committed text. CRLF/LF and trailing-newline variants were all tried and none reproduces the declared hash. Not repaired because no repair is knowable: with the cause unidentified, neither side can be established as the correct one, and editing either to make them agree would assert a binding nobody has evidence for. Recording it keeps the discrepancy visible until someone can explain it.",
|
|
16
|
+
"falsifier": "npx tsx src/scripts/check_review_prompt_binding.ts --json # a cause is found and the entry is replaced by the repair, or the recorded actual hash stops matching"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* agent-config session:recycle --file <envelope.json> # read from file
|
|
21
21
|
* agent-config session:recycle < envelope.json # read from stdin
|
|
22
22
|
* agent-config session:recycle --template # print a skeleton
|
|
23
|
+
* agent-config session:recycle --project <path> # name the repo explicitly
|
|
23
24
|
*
|
|
24
25
|
* Exit codes: 0 written (or template printed) · 1 invalid / refused · 2 usage.
|
|
25
26
|
*/
|
|
@@ -29,7 +30,7 @@ import * as path from 'node:path';
|
|
|
29
30
|
import process from 'node:process';
|
|
30
31
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
31
32
|
|
|
32
|
-
import { resolve_project_root } from '../_lib/agent_settings.js';
|
|
33
|
+
import { ORIGIN_CWD_FALLBACK, resolve_project_root } from '../_lib/agent_settings.js';
|
|
33
34
|
import {
|
|
34
35
|
RECYCLE_ENVELOPE_MAX_BYTES,
|
|
35
36
|
RECYCLE_ENVELOPE_REL,
|
|
@@ -77,6 +78,7 @@ interface ParsedArgv {
|
|
|
77
78
|
ok: boolean;
|
|
78
79
|
message?: string;
|
|
79
80
|
file?: string;
|
|
81
|
+
project?: string;
|
|
80
82
|
template?: boolean;
|
|
81
83
|
verify?: boolean;
|
|
82
84
|
}
|
|
@@ -94,6 +96,11 @@ export function parseArgv(argv: readonly string[]): ParsedArgv {
|
|
|
94
96
|
if (!value) return { ok: false, message: '--file requires a path' };
|
|
95
97
|
parsed.file = value;
|
|
96
98
|
i += 1;
|
|
99
|
+
} else if (a === '--project') {
|
|
100
|
+
const value = argv[i + 1];
|
|
101
|
+
if (!value) return { ok: false, message: '--project requires a path' };
|
|
102
|
+
parsed.project = value;
|
|
103
|
+
i += 1;
|
|
97
104
|
} else if (a === '-h' || a === '--help') {
|
|
98
105
|
return { ok: false, message: 'usage' };
|
|
99
106
|
} else {
|
|
@@ -110,11 +117,50 @@ export function parseArgv(argv: readonly string[]): ParsedArgv {
|
|
|
110
117
|
*/
|
|
111
118
|
export function runSessionRecycle(
|
|
112
119
|
input: string,
|
|
113
|
-
opts: { cwd: string; now?: Date; verify?: boolean },
|
|
120
|
+
opts: { cwd: string; now?: Date; verify?: boolean; project?: string },
|
|
114
121
|
): RecycleResult {
|
|
115
122
|
const out: string[] = [];
|
|
116
123
|
const err: string[] = [];
|
|
117
|
-
|
|
124
|
+
|
|
125
|
+
// `--project` is validated by the resolver (existence + directory), which
|
|
126
|
+
// signals by THROWING. Uncaught, that would surface as a stack trace from
|
|
127
|
+
// a command whose entire subject is legible failure.
|
|
128
|
+
let projectRoot: string;
|
|
129
|
+
let origin: string;
|
|
130
|
+
try {
|
|
131
|
+
[projectRoot, origin] = resolve_project_root(opts.project ?? null, { cwd: opts.cwd });
|
|
132
|
+
} catch (exc) {
|
|
133
|
+
return { code: 1, out, err: [`recycle envelope refused — ${String(exc)}`] };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// An unanchored cwd is the ONE case where a successful write is worse than
|
|
137
|
+
// a refusal. `resolve_project_root` falls back to the cwd itself when it
|
|
138
|
+
// reaches the filesystem root without finding an anchor, so a call from
|
|
139
|
+
// outside any repo — the shape a consumer with a toolchain pin is forced
|
|
140
|
+
// into — writes a valid envelope into a directory the successor session
|
|
141
|
+
// never looks at, and then prints a resume instruction whose next step is
|
|
142
|
+
// `/clear`. The exit code says it worked; the session is gone anyway.
|
|
143
|
+
//
|
|
144
|
+
// Refused BEFORE the parse: everything downstream reads this root, so a
|
|
145
|
+
// wrong one also produces a wrong `workspace` field and grounding read
|
|
146
|
+
// from the wrong git tree. `--verify` is refused too — validating against
|
|
147
|
+
// a root the write would not use answers a question nobody asked.
|
|
148
|
+
if (origin === ORIGIN_CWD_FALLBACK) {
|
|
149
|
+
return {
|
|
150
|
+
code: 1,
|
|
151
|
+
out,
|
|
152
|
+
err: [
|
|
153
|
+
`recycle envelope refused — no project anchor at or above ${projectRoot}`,
|
|
154
|
+
'Writing here would put the envelope where the successor session never reads it,',
|
|
155
|
+
'and the resume instruction would then advise /clear on a session that cannot resume.',
|
|
156
|
+
'Name the repository explicitly — it must be the WORKSPACE ROOT OF THE SESSION',
|
|
157
|
+
'that will resume, because that is the directory the successor reads the envelope',
|
|
158
|
+
'from; a path that merely happens to be a project is not enough:',
|
|
159
|
+
` agent-config session:recycle --project <session-workspace-root> …`,
|
|
160
|
+
` AGENT_CONFIG_PROJECT_ROOT=<session-workspace-root> agent-config session:recycle …`,
|
|
161
|
+
],
|
|
162
|
+
};
|
|
163
|
+
}
|
|
118
164
|
|
|
119
165
|
let parsed: unknown;
|
|
120
166
|
try {
|
|
@@ -179,23 +225,28 @@ export function runSessionRecycle(
|
|
|
179
225
|
};
|
|
180
226
|
}
|
|
181
227
|
|
|
228
|
+
// Absolute, not `RECYCLE_ENVELOPE_REL`: the relative form is identical for
|
|
229
|
+
// every root, so it cannot tell the reader WHICH tree was written — the
|
|
230
|
+
// one thing in doubt at the moment this line is read.
|
|
231
|
+
const target = path.join(projectRoot, RECYCLE_ENVELOPE_REL);
|
|
232
|
+
|
|
182
233
|
// `--verify` stops HERE: every rejection above has already run, and the
|
|
183
234
|
// only thing skipped is the write. Validating through a different path
|
|
184
235
|
// than the one that writes would make the check a second implementation
|
|
185
236
|
// to keep in sync — the failure this suite refuses elsewhere.
|
|
186
237
|
if (opts.verify === true) {
|
|
187
238
|
out.push(`recycle envelope VALID — ${bytes} bytes, not written (--verify).`);
|
|
239
|
+
out.push(`Target would be: ${target}`);
|
|
188
240
|
return { code: 0, out, err };
|
|
189
241
|
}
|
|
190
242
|
|
|
191
|
-
const target = path.join(projectRoot, RECYCLE_ENVELOPE_REL);
|
|
192
243
|
try {
|
|
193
244
|
atomic_write_json(target, envelope);
|
|
194
245
|
} catch (exc) {
|
|
195
246
|
return { code: 1, out, err: [`could not write ${target}: ${String(exc)}`] };
|
|
196
247
|
}
|
|
197
248
|
|
|
198
|
-
out.push(`recycle envelope written — ${
|
|
249
|
+
out.push(`recycle envelope written — ${target} (${bytes} bytes)`);
|
|
199
250
|
out.push('');
|
|
200
251
|
out.push('Resume instruction:');
|
|
201
252
|
out.push(' 1. End this session now: run /clear (or start a fresh session in this workspace).');
|
|
@@ -210,10 +261,15 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
210
261
|
const parsed = parseArgv(argv);
|
|
211
262
|
if (!parsed.ok) {
|
|
212
263
|
const usage = [
|
|
213
|
-
'usage: agent-config session:recycle [--file <envelope.json>] [--template] [--verify]',
|
|
214
|
-
' --file <path>
|
|
215
|
-
' --
|
|
216
|
-
'
|
|
264
|
+
'usage: agent-config session:recycle [--file <envelope.json>] [--project <path>] [--template] [--verify]',
|
|
265
|
+
' --file <path> read the envelope JSON from a file (default: stdin)',
|
|
266
|
+
' --project <path> the workspace root of the session that will resume — required',
|
|
267
|
+
' when the working directory is outside any project (the command',
|
|
268
|
+
' refuses rather than writing an envelope the successor cannot',
|
|
269
|
+
' find). The successor reads the envelope from ITS OWN workspace',
|
|
270
|
+
' root, so a path that is merely a valid project is not enough.',
|
|
271
|
+
' --template print a skeleton envelope and exit',
|
|
272
|
+
' --verify validate only — same rejections, no write',
|
|
217
273
|
].join('\n');
|
|
218
274
|
if (parsed.message === 'usage') {
|
|
219
275
|
process.stdout.write(`${usage}\n`);
|
|
@@ -246,6 +302,7 @@ export function main(argv: string[] = process.argv.slice(2)): number {
|
|
|
246
302
|
const result = runSessionRecycle(input, {
|
|
247
303
|
cwd: process.cwd(),
|
|
248
304
|
...(parsed.verify === true ? { verify: true } : {}),
|
|
305
|
+
...(parsed.project !== undefined ? { project: parsed.project } : {}),
|
|
249
306
|
});
|
|
250
307
|
for (const line of result.out) process.stdout.write(`${line}\n`);
|
|
251
308
|
for (const line of result.err) process.stderr.write(`${line}\n`);
|
|
@@ -30,6 +30,7 @@ import * as fs from 'node:fs';
|
|
|
30
30
|
import * as os from 'node:os';
|
|
31
31
|
import * as path from 'node:path';
|
|
32
32
|
|
|
33
|
+
import { projectStoreSlug } from '../_lib/cc_transcript.js';
|
|
33
34
|
import { derive_session_tag, list_sessions, read_entries } from '../chat_history.js';
|
|
34
35
|
import { eolSessionKey, readEolCounters, type StoredEolCounters } from '../_lib/session_eol.js';
|
|
35
36
|
|
|
@@ -102,9 +103,9 @@ export interface ListOptions {
|
|
|
102
103
|
workspaceRoot?: string;
|
|
103
104
|
}
|
|
104
105
|
|
|
105
|
-
/** Mirrors the Claude Code project-dir naming
|
|
106
|
+
/** Mirrors the Claude Code project-dir naming — see `_lib/cc_transcript.projectStoreSlug`. */
|
|
106
107
|
export function encode_project_path(cwd: string): string {
|
|
107
|
-
return cwd
|
|
108
|
+
return projectStoreSlug(cwd);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
111
|
function _snippet(text: string, max: number = SUMMARY_SNIPPET_CHARS): string {
|
|
@@ -80,6 +80,7 @@ TS-shell native (run via the installed `agent-config` binary):
|
|
|
80
80
|
Names the degraded case (settings file without a
|
|
81
81
|
profile.id → zero packs) that nothing else reports.
|
|
82
82
|
commands [ls|explain] List/explain the command surface from the discovery manifest
|
|
83
|
+
(ls --candidates: surface-reduction report, report-only)
|
|
83
84
|
mcp-server Turnkey read-only stdio MCP server (no repo clone; ADR-085)
|
|
84
85
|
doctor-shell Probe the TS-shell environment
|
|
85
86
|
rtk:detect rtk (Rust Token Killer) presence + identity readout (--json)
|
|
@@ -283,7 +283,6 @@ export const MERGEABLE_KEYS: readonly string[] = [
|
|
|
283
283
|
'personal.bot_icon',
|
|
284
284
|
'personal.pr_comment_bot_icon',
|
|
285
285
|
'personal.autonomy',
|
|
286
|
-
'telegraph.speak_scope',
|
|
287
286
|
// Knowledge-card global cross-project sharing is a USER-GLOBAL setting
|
|
288
287
|
// (ADR-100 / road-to-structure-grounding-v2). Whitelisted so the
|
|
289
288
|
// ~/.event4u/agent-config/agent-settings.yml values are honoured.
|
|
@@ -908,8 +907,27 @@ export function load_agent_settings(
|
|
|
908
907
|
* made the soak impossible; whether the gate fires is now decided by each
|
|
909
908
|
* detector's own trigger conditions. See `turn_end_gate_hook.ts` § "Always
|
|
910
909
|
* armed".
|
|
910
|
+
*
|
|
911
|
+
* The five `derivable` leaves below (2026-08-12, road-to-zero-settings Phase
|
|
912
|
+
* 2.1) are a different case from every entry above them, and the difference is
|
|
913
|
+
* the whole safety argument: no code path ever *consulted* them. Each appeared
|
|
914
|
+
* in the template, the schema and the reference page, and several in the setup
|
|
915
|
+
* wizard, while the decision they looked like they governed was taken
|
|
916
|
+
* elsewhere. A key nothing reads cannot change a default by leaving — so this
|
|
917
|
+
* is the one deletion batch that needs no replacement mechanism, only the
|
|
918
|
+
* per-key statement of what already decides.
|
|
919
|
+
*
|
|
920
|
+
* A sixth unread key, `screenshots.data_bearing_gate`, was found in the same
|
|
921
|
+
* pass and deliberately NOT deleted — it is `consent`, and the open question is
|
|
922
|
+
* whether the repair is to build its missing reader or to delete a promise a
|
|
923
|
+
* Hard Floor can never honour. See `settings-classes.md` § The six unread keys.
|
|
924
|
+
*
|
|
925
|
+
* Every reason string must name what decides INSTEAD, never just "removed".
|
|
926
|
+
* Exported so `lint_settings_classes` can check the deletion side of the
|
|
927
|
+
* surface: a reason that names no replacement, or an entry whose key is live in
|
|
928
|
+
* the template again, is a contradiction the loader itself cannot notice.
|
|
911
929
|
*/
|
|
912
|
-
const REMOVED_KEYS: ReadonlyMap<string, string> = new Map([
|
|
930
|
+
export const REMOVED_KEYS: ReadonlyMap<string, string> = new Map([
|
|
913
931
|
['subagents.enabled', 'always-on orchestration'],
|
|
914
932
|
['subagents.auto', 'always-on orchestration'],
|
|
915
933
|
['subagents.host_capabilities', 'always-on orchestration'],
|
|
@@ -919,6 +937,11 @@ const REMOVED_KEYS: ReadonlyMap<string, string> = new Map([
|
|
|
919
937
|
['hooks.turn_end_gate.promissory', 'the turn-end gate is always armed'],
|
|
920
938
|
['hooks.turn_end_gate.language', 'the turn-end gate is always armed'],
|
|
921
939
|
['hooks.turn_end_gate.verification', 'the turn-end gate is always armed'],
|
|
940
|
+
['telegraph.speak_scope', 'the rule body states its own scope; compile_router gates on telegraph.speak alone'],
|
|
941
|
+
['chat_history.max_size_kb', 'the rotate command takes --max-kb from argv; session-count pruning bounds the file'],
|
|
942
|
+
['chat_history.on_overflow', 'the overflow mode comes from the rotate command --mode argv'],
|
|
943
|
+
['quality.wait_for_remote_ci', 'whether to poll follows from the push plus a detectable remote pipeline'],
|
|
944
|
+
['legal_review_prep.consented_at', 'the provenance sidecar settings:set writes and consentVerdict reads'],
|
|
922
945
|
]);
|
|
923
946
|
|
|
924
947
|
/** Keys already warned about in THIS process — the "once per run" dedupe. */
|
|
@@ -23,7 +23,7 @@ import YAML from 'yaml';
|
|
|
23
23
|
|
|
24
24
|
// ── system prompts per arm ──────────────────────────────────────────────
|
|
25
25
|
|
|
26
|
-
export const SYSTEM_PROMPT_CONDENSED = `You are speaking in TELEGRAPH-SPEAK mode (
|
|
26
|
+
export const SYSTEM_PROMPT_CONDENSED = `You are speaking in TELEGRAPH-SPEAK mode (condense aggressively).
|
|
27
27
|
|
|
28
28
|
Condense all body prose to telegraph grammar:
|
|
29
29
|
- Drop articles (the, a, an).
|
|
@@ -35,6 +35,31 @@ import { homedir } from 'node:os';
|
|
|
35
35
|
/** Default base directory holding one subdirectory per Claude Code project. */
|
|
36
36
|
export const DEFAULT_PROJECTS_ROOT = path.join(homedir(), '.claude', 'projects');
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* The store directory name Claude Code derives from a working directory.
|
|
40
|
+
*
|
|
41
|
+
* Every character outside `[A-Za-z0-9-]` becomes `-`: `/Users/x/.claude` →
|
|
42
|
+
* `-Users-x--claude`, and a worktree `feat+turn-end-gate-always-on` →
|
|
43
|
+
* `feat-turn-end-gate-always-on`.
|
|
44
|
+
*
|
|
45
|
+
* This lives here because it was wrong in six places at once. The sweep on
|
|
46
|
+
* 2026-08-12 found `replace(/[/.]/g, '-')` copied across every transcript-reading
|
|
47
|
+
* report; each one computed a directory that does not exist for any path
|
|
48
|
+
* carrying a `+` and returned an empty corpus — indistinguishable from a clean
|
|
49
|
+
* result. One of those copies already carried a comment about the round BEFORE
|
|
50
|
+
* that, when only `/` was replaced and dotted segments missed. Widening the
|
|
51
|
+
* class by one character per incident is how a defect survives two fixes, so
|
|
52
|
+
* the class is closed instead: anything not `[A-Za-z0-9-]` is slugged.
|
|
53
|
+
*/
|
|
54
|
+
export function projectStoreSlug(cwd: string): string {
|
|
55
|
+
return cwd.replace(/[^A-Za-z0-9-]/g, '-');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** Absolute path to a working directory's transcript store. */
|
|
59
|
+
export function projectStoreDir(cwd: string, root: string = DEFAULT_PROJECTS_ROOT): string {
|
|
60
|
+
return path.join(root, projectStoreSlug(cwd));
|
|
61
|
+
}
|
|
62
|
+
|
|
38
63
|
export type TranscriptBucket = 'main' | 'subagent';
|
|
39
64
|
|
|
40
65
|
/** The six token counters every downstream metric (cost, weight, share) is built from. */
|
|
@@ -60,9 +60,10 @@ export const COMPILE_TIME_TOGGLES: Record<string, (s: ToggleSettings) => boolean
|
|
|
60
60
|
// family master switch, and flipping it would silence sibling telegraph settings
|
|
61
61
|
// that were never measured negative.
|
|
62
62
|
//
|
|
63
|
-
//
|
|
64
|
-
// rule's
|
|
65
|
-
//
|
|
63
|
+
// The predicate gates on `telegraph.speak` because that is the only lever that
|
|
64
|
+
// removes the rule's ~982-token body from the router and the projection. A knob
|
|
65
|
+
// that merely narrowed the grammar at runtime would leave the token cost in place
|
|
66
|
+
// — the trap the measurement exposed.
|
|
66
67
|
'telegraph-speak': (s: ToggleSettings): boolean => {
|
|
67
68
|
const tg = (s['telegraph'] as ToggleSettings | undefined) ?? {};
|
|
68
69
|
const enabled = tg['enabled'] === undefined ? true : tg['enabled'];
|