@sabaiway/agent-workflow-memory 3.1.0 → 3.2.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/CHANGELOG.md CHANGED
@@ -4,6 +4,17 @@ All notable changes to the memory substrate. Versions are this **package's** npm
4
4
  they are distinct from the **deployment-lineage** stamp written into a project's
5
5
  `docs/ai/.memory-version` (which tracks the shared `agent-workflow` lineage, head `3.0.0`).
6
6
 
7
+ ## 3.2.0 — the ADR rotation can be asked whether a seed is safe, without seeding (AD-083)
8
+
9
+ `archive-decisions.mjs --write-navigator` now honours `--dry-run`. It runs exactly the checks the
10
+ real write runs — the heading parse, the half-migrated guard, and the store-integrity check — and
11
+ then stops before touching anything, reporting how many decisions it validated.
12
+
13
+ Why it exists: a guarded caller (the kit's ADR-store migration) had no way to find out whether
14
+ seeding the store would succeed except by seeding it. A tree with a malformed decisions file could
15
+ therefore be told «go ahead», and only discover the problem after files had been written. The same
16
+ code now answers that question first. Nothing about the normal `--write-navigator` behaviour changes.
17
+
7
18
  ## 3.1.0 — plain-language communication bar in §2.5 of the agent_rules template (AD-061)
8
19
 
9
20
  The `agent_rules.md` template's **§2.5 Communication** section gains a plain-language bar: every
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: agent-workflow-memory
3
3
  description: Deploy or upgrade a portable AI-agent memory substrate in any project — an entry-point `AGENTS.md` (+ `CLAUDE.md` alias) and a structured `docs/ai/` context store with cap/archive/index enforcement. Use when the user wants to bootstrap `docs/ai/`, set up the Memory Map and session protocols, install the docs-rotation pre-commit hook, or run `/agent-workflow-memory` / `/agent-workflow-memory upgrade`. Triggers on "set up the memory system", "deploy the AI memory here", "bootstrap docs/ai", "upgrade the memory substrate". This is the substrate only — the workflow methodology (plan→execute→review, queue, Cleanup) is owned elsewhere and injected into AGENTS.md by the family composition root.
4
4
  disable-model-invocation: true
5
5
  metadata:
6
- version: '3.1.0'
6
+ version: '3.2.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-memory
package/capability.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "schema": 1,
4
4
  "name": "agent-workflow-memory",
5
5
  "kind": "memory-substrate",
6
- "version": "3.1.0",
6
+ "version": "3.2.0",
7
7
  "provides": ["context"],
8
8
  "roles": {},
9
9
  "detect": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sabaiway/agent-workflow-memory",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Portable, cross-agent memory substrate for AI coding agents — an AGENTS.md entry point + docs/ai context with cap/archive/index enforcement, deployable standalone or as part of the agent-workflow family. The memory layer of the agent-workflow family.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -29,7 +29,10 @@
29
29
  // HOT preamble, and only THEN removes the monoliths — gated on conservation AND
30
30
  // the snapshot. Re-run skips byte-identical records (crash-resumable).
31
31
  // --write-navigator regenerate docs/ai/adr/log.md AND re-trigger the index regen (the authoring /
32
- // supersession write-side; the --write-index analog).
32
+ // supersession write-side; the --write-index analog). With --dry-run it runs
33
+ // EXACTLY the same validation (parse, half-migrated guard, store integrity) and
34
+ // stops before every write — the read-only preflight a guarded caller needs to
35
+ // earn a go-ahead without risking a partial write.
33
36
  // --dry-run print the planned rotation move-set, change nothing.
34
37
  // --today=YYYY-MM-DD pin the lastUpdated stamp (tests / reproducible runs).
35
38
  //
@@ -693,7 +696,7 @@ const runMigrate = (root, flags, today, deps, log, logError) => {
693
696
  return 0;
694
697
  };
695
698
 
696
- const runWriteNavigator = (root, today, deps, log, logError) => {
699
+ const runWriteNavigator = (root, flags, today, deps, log, logError) => {
697
700
  if (!existsSync(resolve(root, HOT_REL)) && !existsSync(resolve(root, ADR_DIR_REL))) {
698
701
  log(`[archive-decisions] SKIP — no ADR substrate (neither ${HOT_REL} nor ${ADR_DIR_REL}); nothing to write.`);
699
702
  return 0;
@@ -704,6 +707,14 @@ const runWriteNavigator = (root, today, deps, log, logError) => {
704
707
  const adrEntries = loadAdrStore(root);
705
708
  assertStoreIntegrity(hotEntries, adrEntries); // never emit a duplicate-row / corrupt navigator
706
709
  const corpus = [...hotEntries, ...adrEntries];
710
+ // --dry-run runs EXACTLY the validation above and stops before every write: the parse, the
711
+ // half-migrated guard and the store-integrity check are the same code the write path uses, so a
712
+ // caller (the guarded ADR-store crossing) can earn a go-ahead without a partial write. A separate
713
+ // re-implementation of these checks would be an approximation that can disagree with the writer.
714
+ if (flags.dryRun) {
715
+ log(`[archive-decisions] --write-navigator DRY-RUN — no files will be changed; ${corpus.length} ADR(s) validated.`);
716
+ return 0;
717
+ }
707
718
  writeNavigatorFile(root, corpus, today);
708
719
  const regen = (deps.regenerateIndex ?? defaultRegenerateIndex)(root, today);
709
720
  log(`[archive-decisions] wrote ${NAV_REL} (${corpus.length} ADRs in the corpus).`);
@@ -828,7 +839,7 @@ export const runCli = (argv, deps = {}) => {
828
839
  const today = todayOpt ?? new Date().toISOString().slice(0, 10);
829
840
 
830
841
  if (flags.migrate) return runMigrate(root, flags, today, deps, log, logError);
831
- if (flags.writeNavigator) return runWriteNavigator(root, today, deps, log, logError);
842
+ if (flags.writeNavigator) return runWriteNavigator(root, flags, today, deps, log, logError);
832
843
  if (flags.check) return runCheck(root, today, log, logError);
833
844
 
834
845
  if (!existsSync(resolve(root, HOT_REL))) {
@@ -643,6 +643,33 @@ describe('1.5 navigator — governing heads (computed), superseded drop out but
643
643
  assert.doesNotMatch(gov, /\| AD-002 \|/, 'the Proposed ADR is NOT a governing head (accepted & not-superseded only)');
644
644
  });
645
645
 
646
+ // The read-only preflight a guarded caller needs: the SAME parse / half-migrated guard / store
647
+ // integrity the writer runs, stopping before every write. Without it a caller can only learn that
648
+ // seeding is safe by seeding.
649
+ it('--write-navigator --dry-run validates and writes NOTHING', () => {
650
+ const root = makeRoot();
651
+ seedMigrated(root, { hotIds: ['005', '006'], storeIds: ['001'] });
652
+ const navBefore = readFileSync(join(root, NAV_REL), 'utf8');
653
+ rmSync(join(root, NAV_REL));
654
+ const r = run(['--write-navigator', '--dry-run', '--today=2026-07-09'], root);
655
+ assert.equal(r.code, 0);
656
+ assert.match(r.out.join('\n'), /DRY-RUN/, 'the run states it changed nothing');
657
+ assert.equal(existsSync(join(root, NAV_REL)), false, 'the navigator was NOT written by the dry-run');
658
+ // and the real run still produces exactly what the dry-run validated
659
+ assert.equal(run(['--write-navigator', '--today=2026-07-09'], root).code, 0);
660
+ assert.equal(readFileSync(join(root, NAV_REL), 'utf8'), navBefore);
661
+ });
662
+
663
+ it('--write-navigator --dry-run REFUSES a corrupt store instead of green-lighting it', () => {
664
+ const root = makeRoot();
665
+ seedMigrated(root, { hotIds: ['005', '006'], storeIds: ['001'] });
666
+ // a record whose id also lives in the HOT window — the store would hold the ADR twice
667
+ writeFileSync(join(root, ADR_DIR_REL, 'AD-005-dup.md'), '---\n---\n\n## AD-005 — Dup\n\nBody.\n');
668
+ const r = run(['--write-navigator', '--dry-run', '--today=2026-07-09'], root);
669
+ assert.notEqual(r.code, 0, 'a corrupt store fails the preflight');
670
+ assert.doesNotMatch(r.out.join('\n'), /DRY-RUN/, 'no go-ahead is printed for a tree that cannot converge');
671
+ });
672
+
646
673
  it('authoring a new HOT ADR then --write-navigator keeps --check green; a stale nav with NO write → exit 1, then --write-navigator fixes it', () => {
647
674
  const root = makeRoot();
648
675
  seedMigrated(root, { hotIds: ['005', '006'], storeIds: ['001'] });