@event4u/agent-config 14.5.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.
Files changed (45) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +128 -0
  3. package/dist/agent-src/commands/analyze/decision.md +7 -0
  4. package/dist/agent-src/commands/optimize/project.md +10 -0
  5. package/dist/agent-src/commands/roadmap/process-full.md +45 -3
  6. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +130 -1
  7. package/dist/agent-src/rules/decision-revisit-gate.md +108 -7
  8. package/dist/agent-src/skills/adr-create/SKILL.md +25 -3
  9. package/dist/agent-src/skills/decision-review/SKILL.md +18 -0
  10. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  11. package/dist/cli-delegate/{chunk-ZPSXJTLI.js → chunk-P6LQDIWK.js} +15 -1
  12. package/dist/cli-delegate/cmd_conformance.js +1 -1
  13. package/dist/cli-delegate/cmd_handoff.js +1 -1
  14. package/dist/cli-delegate/cmd_session_recycle.js +1 -1
  15. package/dist/discovery/deprecation-report.md +1 -1
  16. package/dist/discovery/discovery-manifest.json +8 -8
  17. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  18. package/dist/discovery/discovery-manifest.summary.md +1 -1
  19. package/dist/discovery/dormancy-report.md +1 -1
  20. package/dist/discovery/orphan-report.md +1 -1
  21. package/dist/discovery/packs.json +2 -2
  22. package/dist/discovery/trust-report.md +1 -1
  23. package/dist/discovery/workspaces.json +2 -2
  24. package/dist/hooks/dispatch.js +334 -77
  25. package/dist/mcp/registry-manifest.json +2 -2
  26. package/dist/mcp/server.json +2 -2
  27. package/dist/router.json +1 -1
  28. package/docs/guidelines/agent-infra/engineering-memory-data-format.md +49 -0
  29. package/package.json +1 -1
  30. package/src/config/estate-count-budget.json +29 -1
  31. package/src/config/pack-size-budget.json +3 -2
  32. package/src/scripts/_lib/layer_overlap_notice.ts +85 -0
  33. package/src/scripts/_lib/scope_guard.sh +62 -3
  34. package/src/scripts/_lib/subagent_capsule.ts +81 -1
  35. package/src/scripts/adr/regenerate_index.ts +13 -2
  36. package/src/scripts/adr_cite_check.ts +424 -0
  37. package/src/scripts/check_adr_frontmatter.ts +187 -1
  38. package/src/scripts/check_single_delivery.ts +473 -0
  39. package/src/scripts/hooks/run_continuation_hook.ts +884 -68
  40. package/src/scripts/install.sh +12 -2
  41. package/src/scripts/memory_consolidation.ts +189 -0
  42. package/src/scripts/memory_eviction.ts +384 -0
  43. package/src/scripts/report_layer_overlap.ts +72 -0
  44. package/src/scripts/report_memory_pointers.ts +622 -0
  45. package/src/scripts/session_register_hook.ts +104 -15
@@ -354,8 +354,17 @@ export function claim_is_stale(
354
354
  // roadmaps directory" — and it is the same check for a traversal, an absolute
355
355
  // path, and an encoding nobody enumerated. A slug that fails it is reported
356
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.
357
366
  const rel = path.normalize(path.join('agents', 'roadmaps', `${base}.md`));
358
- const inside = rel.startsWith(`agents${path.sep}roadmaps${path.sep}`) && !rel.includes('..');
367
+ const inside = path.dirname(rel) === path.join('agents', 'roadmaps');
359
368
  if (!inside || path.isAbsolute(base)) {
360
369
  return true; // not a slug this repo can hold; never render it as live work
361
370
  }
@@ -409,29 +418,98 @@ export function claim_is_stale(
409
418
  * conditions, because a wrong anchor is worse than the fallback:
410
419
  *
411
420
  * 1. it is an existing directory;
412
- * 2. it is the ROOT of a checkout (`git_dir` looks for `<dir>/.git` and does not
413
- * walk up, so a session whose cwd is a subdirectory degrades to
414
- * `workspace_root` — today's behaviour, never something worse);
415
- * 3. it belongs to the SAME repository — identical git common dir. A cwd in
416
- * 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
417
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.
418
451
  */
419
452
  export function session_checkout(
420
453
  workspace_root: string,
421
454
  payload_cwd: string | null | undefined,
422
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);
423
468
  const cwd = String(payload_cwd ?? '').trim();
424
- if (cwd === '') return workspace_root;
469
+ if (cwd === '') return fallback;
470
+ let start: string;
425
471
  try {
426
- if (!fs.statSync(cwd).isDirectory()) return workspace_root;
472
+ if (!fs.statSync(cwd).isDirectory()) return fallback;
473
+ start = fs.realpathSync(cwd);
427
474
  } catch {
428
- return workspace_root;
475
+ return fallback;
429
476
  }
430
- if (git_dir(cwd) === null) return workspace_root;
431
- const mine = git_common_dir(cwd);
477
+ const root = nearest_checkout_root(start);
478
+ if (root === null) return fallback;
479
+ const mine = git_common_dir(root);
432
480
  const theirs = git_common_dir(workspace_root);
433
- if (mine === null || theirs === null || mine !== theirs) return workspace_root;
434
- return cwd;
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;
435
513
  }
436
514
 
437
515
  /**
@@ -619,8 +697,19 @@ export function foreign_sessions_block(
619
697
  // `session_checkout`. Before that every record claimed the main checkout, so
620
698
  // every collision looked like the dangerous one.
621
699
  if (branch_hit !== null) {
622
- const peer_tree = path.resolve(branch_hit.record.worktree ?? '');
623
- const same_tree = peer_tree !== '' && peer_tree === path.resolve(workspace_root);
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);
624
713
  if (same_tree) {
625
714
  parts.push(
626
715
  '',