@event4u/agent-config 14.4.0 → 14.6.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 +132 -291
- package/dist/agent-src/commands/analyze/decision.md +7 -0
- package/dist/agent-src/commands/optimize/project.md +10 -0
- package/dist/agent-src/commands/roadmap/process-full.md +45 -3
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +130 -1
- package/dist/agent-src/rules/decision-revisit-gate.md +108 -7
- package/dist/agent-src/skills/adr-create/SKILL.md +25 -3
- package/dist/agent-src/skills/decision-review/SKILL.md +18 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli-delegate/{chunk-ZPSXJTLI.js → chunk-P6LQDIWK.js} +15 -1
- package/dist/cli-delegate/cmd_conformance.js +1 -1
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +8 -8
- 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 +419 -94
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/router.json +1 -1
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +49 -0
- package/package.json +1 -1
- package/src/config/estate-count-budget.json +36 -1
- package/src/config/pack-size-budget.json +3 -2
- package/src/scripts/_lib/layer_overlap_notice.ts +85 -0
- package/src/scripts/_lib/scope_guard.sh +62 -3
- package/src/scripts/_lib/subagent_capsule.ts +81 -1
- package/src/scripts/adr/regenerate_index.ts +13 -2
- package/src/scripts/adr_cite_check.ts +424 -0
- package/src/scripts/bench_hook_injection.ts +18 -1
- package/src/scripts/check_adr_frontmatter.ts +187 -1
- package/src/scripts/check_single_delivery.ts +473 -0
- package/src/scripts/hooks/host_semantics.ts +28 -0
- package/src/scripts/hooks/injection_budget.ts +152 -16
- package/src/scripts/hooks/run_continuation_hook.ts +918 -46
- package/src/scripts/install.sh +12 -2
- package/src/scripts/memory_consolidation.ts +189 -0
- package/src/scripts/memory_eviction.ts +384 -0
- package/src/scripts/report_layer_overlap.ts +72 -0
- package/src/scripts/report_memory_pointers.ts +622 -0
- package/src/scripts/session_register_hook.ts +144 -21
|
@@ -237,7 +237,23 @@ function _read_claim_file(file: string): RoadmapClaim | null {
|
|
|
237
237
|
}
|
|
238
238
|
|
|
239
239
|
/**
|
|
240
|
-
*
|
|
240
|
+
* A resolved claim plus the file it was actually read from.
|
|
241
|
+
*
|
|
242
|
+
* The path is not decoration. The defect this whole surface exists to prevent
|
|
243
|
+
* was a WRITER and a READER resolving different trees, and the only fact that
|
|
244
|
+
* falsifies it is the concrete pair of roots — a boolean asserting "worktree
|
|
245
|
+
* started" is another claim by the system under observation, not a check on it
|
|
246
|
+
* (AI council 2026-08-19, 2/2, on the run-continuation observation).
|
|
247
|
+
*/
|
|
248
|
+
export interface ResolvedClaim {
|
|
249
|
+
slug: string;
|
|
250
|
+
/** Absolute path of the claim file this read came from. */
|
|
251
|
+
path: string;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* The roadmap slug THIS session has claimed, and the file it came from — or
|
|
256
|
+
* `null`. Never throws.
|
|
241
257
|
*
|
|
242
258
|
* Two reads, in order, and the second one is why a peer's claim can no longer be
|
|
243
259
|
* inherited: the per-session file first, then the legacy per-worktree file —
|
|
@@ -246,14 +262,14 @@ function _read_claim_file(file: string): RoadmapClaim | null {
|
|
|
246
262
|
* that session's claim, and reading it as this session's is exactly the defect
|
|
247
263
|
* that put one archived slug on four live records.
|
|
248
264
|
*/
|
|
249
|
-
export function
|
|
265
|
+
export function resolve_claim(
|
|
250
266
|
workspace_root: string,
|
|
251
267
|
// REQUIRED, on the reviewer's point: an optional id means a caller that forgets
|
|
252
268
|
// it silently gets legacy semantics instead of a type error, and there is
|
|
253
269
|
// exactly one call site that matters (`build_record`). `null` stays a legal
|
|
254
270
|
// value — it is the host-exports-nothing case — but it has to be passed.
|
|
255
271
|
session_id: string | null,
|
|
256
|
-
):
|
|
272
|
+
): ResolvedClaim | null {
|
|
257
273
|
const own = roadmap_claim_rel(session_id);
|
|
258
274
|
if (own !== ROADMAP_CLAIM_REL) {
|
|
259
275
|
// Per-session, shared dir first, then the pre-fix per-tree path. The
|
|
@@ -262,16 +278,20 @@ export function read_claimed_slug(
|
|
|
262
278
|
for (const p of claim_read_paths(workspace_root, session_id)) {
|
|
263
279
|
if (path.basename(p) === path.basename(ROADMAP_CLAIM_REL)) continue;
|
|
264
280
|
const mine = _read_claim_file(p);
|
|
265
|
-
if (mine !== null) return mine.slug;
|
|
281
|
+
if (mine !== null) return { slug: mine.slug, path: p };
|
|
266
282
|
}
|
|
267
283
|
}
|
|
268
284
|
// The legacy per-WORKTREE file, in the same two locations. Unchanged in
|
|
269
285
|
// meaning: it is a claim on the checkout, not on a session.
|
|
270
286
|
let legacy: RoadmapClaim | null = null;
|
|
287
|
+
let legacy_path = '';
|
|
271
288
|
for (const p of claim_read_paths(workspace_root, session_id)) {
|
|
272
289
|
if (path.basename(p) !== path.basename(ROADMAP_CLAIM_REL)) continue;
|
|
273
290
|
legacy = _read_claim_file(p);
|
|
274
|
-
if (legacy !== null)
|
|
291
|
+
if (legacy !== null) {
|
|
292
|
+
legacy_path = p;
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
275
295
|
}
|
|
276
296
|
if (legacy === null) {
|
|
277
297
|
return null;
|
|
@@ -293,7 +313,21 @@ export function read_claimed_slug(
|
|
|
293
313
|
if (id !== '') {
|
|
294
314
|
return null;
|
|
295
315
|
}
|
|
296
|
-
return legacy.slug;
|
|
316
|
+
return { slug: legacy.slug, path: legacy_path };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* The roadmap slug THIS session has claimed, or `null`. Never throws.
|
|
321
|
+
*
|
|
322
|
+
* Thin projection of `resolve_claim` — kept because every existing caller wants
|
|
323
|
+
* the slug alone, and widening their return type to carry a path none of them
|
|
324
|
+
* reads would be a signature change for no consumer.
|
|
325
|
+
*/
|
|
326
|
+
export function read_claimed_slug(
|
|
327
|
+
workspace_root: string,
|
|
328
|
+
session_id: string | null,
|
|
329
|
+
): string | null {
|
|
330
|
+
return resolve_claim(workspace_root, session_id)?.slug ?? null;
|
|
297
331
|
}
|
|
298
332
|
|
|
299
333
|
/**
|
|
@@ -320,8 +354,17 @@ export function claim_is_stale(
|
|
|
320
354
|
// roadmaps directory" — and it is the same check for a traversal, an absolute
|
|
321
355
|
// path, and an encoding nobody enumerated. A slug that fails it is reported
|
|
322
356
|
// stale rather than resolved, so no read happens at all.
|
|
357
|
+
// Structural, not textual. Round 6 finding 3: the previous form ANDed a
|
|
358
|
+
// `!rel.includes('..')` substring test onto the prefix check, and round 5
|
|
359
|
+
// finding 10 removed exactly that test from `resolveRoadmap` — one-sided. The
|
|
360
|
+
// two functions then disagreed about one claim string in the opposite
|
|
361
|
+
// direction: for a legal slug like `road-to-a..b` the register rendered a live
|
|
362
|
+
// claim as stale (dropping it from the collision set and disabling the
|
|
363
|
+
// duplicate-work warning) while the hook resolved it and engaged. `path.dirname`
|
|
364
|
+
// asks the only question that matters — is the result a file directly inside the
|
|
365
|
+
// roadmaps directory — and is the same check both sides now make.
|
|
323
366
|
const rel = path.normalize(path.join('agents', 'roadmaps', `${base}.md`));
|
|
324
|
-
const inside =
|
|
367
|
+
const inside = path.dirname(rel) === path.join('agents', 'roadmaps');
|
|
325
368
|
if (!inside || path.isAbsolute(base)) {
|
|
326
369
|
return true; // not a slug this repo can hold; never render it as live work
|
|
327
370
|
}
|
|
@@ -375,29 +418,98 @@ export function claim_is_stale(
|
|
|
375
418
|
* conditions, because a wrong anchor is worse than the fallback:
|
|
376
419
|
*
|
|
377
420
|
* 1. it is an existing directory;
|
|
378
|
-
* 2. it
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
* another repo would otherwise write this repo's register with a foreign
|
|
421
|
+
* 2. it resolves to the NEAREST enclosing checkout root, walking up from the
|
|
422
|
+
* reported directory;
|
|
423
|
+
* 3. that root belongs to the SAME repository — identical git common dir. A cwd
|
|
424
|
+
* in another repo would otherwise write this repo's register with a foreign
|
|
383
425
|
* branch, and the register is shared by every worktree here.
|
|
426
|
+
*
|
|
427
|
+
* ## Condition 2 used to reject a subdirectory, and that was wrong here
|
|
428
|
+
*
|
|
429
|
+
* It required the reported directory to BE a checkout root, and defended the
|
|
430
|
+
* rejection as "a session whose cwd is a subdirectory degrades to
|
|
431
|
+
* `workspace_root` — today's behaviour, never something worse". R2 round 3
|
|
432
|
+
* finding 2 measured that claim false in this repository's own layout, where
|
|
433
|
+
* worktrees live at `<parent>/.claude/worktrees/<name>` — NESTED under the
|
|
434
|
+
* parent. For a session standing at `<parent>/.claude/worktrees/wt/src`:
|
|
435
|
+
*
|
|
436
|
+
* - the degraded root is `<parent>`, i.e. equal to `workspace_root`;
|
|
437
|
+
* - so `git_dir` and `git_common_dir` taken from it are both `<parent>/.git`;
|
|
438
|
+
* - and the reported cwd is under `<parent>` as well.
|
|
439
|
+
*
|
|
440
|
+
* Every available signal then reports a healthy same-tree run for a genuine
|
|
441
|
+
* two-tree one. That is not a loss of precision, it is a confident wrong answer,
|
|
442
|
+
* which is strictly worse than the fallback the docblock was defending.
|
|
443
|
+
*
|
|
444
|
+
* The walk is bounded twice over, per the AI council's condition (2026-08-19,
|
|
445
|
+
* 2/2 convergent on this shape): it stops at the FIRST enclosing checkout root,
|
|
446
|
+
* so a nested worktree is found before the parent that contains it, and the
|
|
447
|
+
* same-repository check is retained on whatever it finds — a cwd inside an
|
|
448
|
+
* unrelated repo nested in this tree resolves to that repo's root, fails the
|
|
449
|
+
* identity check, and falls back. Paths are canonicalised before the walk so a
|
|
450
|
+
* symlinked cwd cannot walk a different chain than the one it names.
|
|
384
451
|
*/
|
|
385
452
|
export function session_checkout(
|
|
386
453
|
workspace_root: string,
|
|
387
454
|
payload_cwd: string | null | undefined,
|
|
388
455
|
): string {
|
|
456
|
+
// EVERY branch returns a canonical path, resolve and fallback alike.
|
|
457
|
+
//
|
|
458
|
+
// Round 4 finding 5: the resolve branch realpath-normalised and the four
|
|
459
|
+
// fallback branches returned `workspace_root` verbatim, so under a repo
|
|
460
|
+
// reached through a symlinked ancestor two sessions in the SAME working tree
|
|
461
|
+
// stored different strings for it — `/private/var/…/repo` from the resolver
|
|
462
|
+
// and `/var/…/repo` from the envelope. `foreign_sessions_block` compares the
|
|
463
|
+
// stored forms with `path.resolve`, which does not follow symlinks, so it read
|
|
464
|
+
// them as different worktrees and printed the benign "separate trees" note
|
|
465
|
+
// where the same-worktree COLLISION prompt belongs. The walk-up widened the
|
|
466
|
+
// exposure by moving subdirectory sessions onto the normalising branch.
|
|
467
|
+
const fallback = canonical(workspace_root);
|
|
389
468
|
const cwd = String(payload_cwd ?? '').trim();
|
|
390
|
-
if (cwd === '') return
|
|
469
|
+
if (cwd === '') return fallback;
|
|
470
|
+
let start: string;
|
|
391
471
|
try {
|
|
392
|
-
if (!fs.statSync(cwd).isDirectory()) return
|
|
472
|
+
if (!fs.statSync(cwd).isDirectory()) return fallback;
|
|
473
|
+
start = fs.realpathSync(cwd);
|
|
393
474
|
} catch {
|
|
394
|
-
return
|
|
475
|
+
return fallback;
|
|
395
476
|
}
|
|
396
|
-
|
|
397
|
-
|
|
477
|
+
const root = nearest_checkout_root(start);
|
|
478
|
+
if (root === null) return fallback;
|
|
479
|
+
const mine = git_common_dir(root);
|
|
398
480
|
const theirs = git_common_dir(workspace_root);
|
|
399
|
-
if (mine === null || theirs === null || mine !== theirs) return
|
|
400
|
-
return
|
|
481
|
+
if (mine === null || theirs === null || mine !== theirs) return fallback;
|
|
482
|
+
return root;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
/** `realpath` that falls back to an absolute form when the path is unreadable. */
|
|
486
|
+
function canonical(p: string): string {
|
|
487
|
+
const abs = path.resolve(p);
|
|
488
|
+
try {
|
|
489
|
+
return fs.realpathSync(abs);
|
|
490
|
+
} catch {
|
|
491
|
+
return abs;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* The nearest enclosing directory that is a checkout root, or `null`.
|
|
497
|
+
*
|
|
498
|
+
* The iteration cap is a loop guard, not a policy: `path.dirname` is a fixed
|
|
499
|
+
* point at the filesystem root, so the terminating condition is the unchanged
|
|
500
|
+
* path and the counter only bounds a pathological filesystem. The FIRST hit
|
|
501
|
+
* wins, which is what makes a nested worktree resolve to itself rather than to
|
|
502
|
+
* the checkout it sits inside.
|
|
503
|
+
*/
|
|
504
|
+
function nearest_checkout_root(start: string): string | null {
|
|
505
|
+
let dir = start;
|
|
506
|
+
for (let i = 0; i < 64; i++) {
|
|
507
|
+
if (git_dir(dir) !== null) return dir;
|
|
508
|
+
const up = path.dirname(dir);
|
|
509
|
+
if (up === dir) return null;
|
|
510
|
+
dir = up;
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
401
513
|
}
|
|
402
514
|
|
|
403
515
|
/**
|
|
@@ -585,8 +697,19 @@ export function foreign_sessions_block(
|
|
|
585
697
|
// `session_checkout`. Before that every record claimed the main checkout, so
|
|
586
698
|
// every collision looked like the dangerous one.
|
|
587
699
|
if (branch_hit !== null) {
|
|
588
|
-
|
|
589
|
-
|
|
700
|
+
// Compared through `canonical`, not `path.resolve`, because resolve does not
|
|
701
|
+
// follow symlinks — round 5 finding 11. Round 4 finding 5 made every
|
|
702
|
+
// `session_checkout` branch canonical, which fixes records written from now
|
|
703
|
+
// on; it does nothing for a record already in the register from before the
|
|
704
|
+
// upgrade, and those live for the whole TTL window. Under a symlinked
|
|
705
|
+
// ancestor such a peer holds `/var/…/repo` while this session holds
|
|
706
|
+
// `/private/var/…/repo`, and comparing the raw strings reads two sessions
|
|
707
|
+
// in ONE working tree as separate trees — printing the benign note where
|
|
708
|
+
// the collision prompt belongs. Canonicalising at the comparison closes the
|
|
709
|
+
// upgrade boundary the writer side cannot reach.
|
|
710
|
+
const peer_tree = canonical(branch_hit.record.worktree ?? '');
|
|
711
|
+
const same_tree =
|
|
712
|
+
(branch_hit.record.worktree ?? '') !== '' && peer_tree === canonical(workspace_root);
|
|
590
713
|
if (same_tree) {
|
|
591
714
|
parts.push(
|
|
592
715
|
'',
|