@sabaiway/agent-workflow-memory 8.1.2 → 8.1.3

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,18 @@ 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 `4.0.0`).
6
6
 
7
+ ## 8.1.3 — the ADR navigator stamps a cap it honours (AD-167)
8
+
9
+ `references/scripts/archive-decisions.mjs`: `--write-navigator` stamped `maxLines: 200` into `docs/ai/adr/log.md`
10
+ whatever its length, and the navigator grows by one row per governing ADR, so past 162 governing ADRs the docs gate
11
+ refused the generator's own output. The stamp is now 200 while the navigator fits and its own line count past it,
12
+ counted the way `check-docs-size.mjs` counts. A navigator of 200 lines or fewer is written byte for byte as before, and
13
+ `--check` still compares the whole file with a regeneration, so a hand-edited cap reads as stale. PATCH: a navigator
14
+ of 200 lines or fewer is written as before. An upgrade keeps a deployed `scripts/archive-decisions.mjs` byte for byte,
15
+ so a project already on the ADR store takes the fix by copying `archive-decisions.mjs` and `archive-decisions.test.mjs`
16
+ from this package's `references/scripts/` into its `scripts/`; a project whose navigator is past 200 lines then runs
17
+ `node scripts/archive-decisions.mjs --write-navigator` once.
18
+
7
19
  ## 8.1.2 — the templates carry the session close for the user (AD-159)
8
20
 
9
21
  `references/templates/agent_rules.md`: §2.5 Communication gains the last bullet `Two blocks for the user`, and §2.7
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: '8.1.2'
6
+ version: '8.1.3'
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": "8.1.2",
6
+ "version": "8.1.3",
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": "8.1.2",
3
+ "version": "8.1.3",
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",
@@ -2,7 +2,7 @@
2
2
  // One-file-per-ADR store for docs/ai/decisions.md (ADRs) — the durable replacement for the retired
3
3
  // 3-tier cascade (HOT → WARM archive → a single COLD monolith whose cap was raised release after
4
4
  // release). Every ADR beyond the HOT window becomes its OWN immutable record so no artifact is ever
5
- // O(n) and no cap is ever raised again.
5
+ // O(n) and no cap is raised by hand again (the navigator stamps its own count past 200).
6
6
  //
7
7
  // HOT (docs/ai/decisions.md) — the active ADR window (newest at the bottom), self-bounding
8
8
  // under its own frontmatter maxLines.
@@ -95,7 +95,8 @@ export const ADR_DIR_REL = 'docs/ai/adr';
95
95
  export const NAV_REL = 'docs/ai/adr/log.md';
96
96
 
97
97
  // A per-record cap (generous vs the largest real ADR ~74 lines; a body over it is a genuine smell) and
98
- // the navigator cap (bounded — exceeding it is the future plateau-shard trigger, Decision 8).
98
+ // the navigator's FLOOR: the navigator stamps 200 while it fits and its own line count past it, since
99
+ // nothing rotates a governing row out (docs/ai/specs/memory/adr-navigator.md).
99
100
  export const RECORD_CAP = 400;
100
101
  export const NAV_MAXLINES = 200;
101
102
  // How many most-recent ids the navigator's recent window carries (status-annotated, incl. supersessions).
@@ -701,8 +702,8 @@ export const computeGoverningIds = (entries) => {
701
702
  return new Set(entries.filter((e) => e.status === IN_FORCE_STATUS && !superseded.has(e.id)).map((e) => e.id));
702
703
  };
703
704
 
704
- const NAV_FRONTMATTER = (today) =>
705
- `---\ntype: reference\nlastUpdated: ${today}\nscope: permanent\nstaleAfter: never\nowner: none\nmaxLines: ${NAV_MAXLINES}\n---\n`;
705
+ const NAV_FRONTMATTER = (today, cap) =>
706
+ `---\ntype: reference\nlastUpdated: ${today}\nscope: permanent\nstaleAfter: never\nowner: none\nmaxLines: ${cap}\n---\n`;
706
707
 
707
708
  // Build docs/ai/adr/log.md from the WHOLE corpus (HOT entries ∪ adr/ records). Governing heads
708
709
  // (accepted ∧ not-superseded) sorted newest-first; superseded ADRs drop OUT (still reachable by
@@ -744,7 +745,9 @@ export const buildNavigator = (corpus, today) => {
744
745
  '',
745
746
  ...recent,
746
747
  ].join('\n');
747
- return `${NAV_FRONTMATTER(today)}\n${body}\n`;
748
+ // The cap sits on one line whatever its value, so the count with the floor is the final count.
749
+ const count = lineCountOf(`${NAV_FRONTMATTER(today, NAV_MAXLINES)}\n${body}\n`);
750
+ return `${NAV_FRONTMATTER(today, Math.max(NAV_MAXLINES, count))}\n${body}\n`;
748
751
  };
749
752
 
750
753
  // ── snapshot (durable, pre-delete — Decision 5) ─────────────────────────────────────────
@@ -800,6 +800,47 @@ describe('1.5 navigator — governing heads (computed), superseded drop out but
800
800
  });
801
801
  });
802
802
 
803
+ // ── the navigator's stamped cap (docs/ai/specs/memory/adr-navigator.md) ─────────────────
804
+ //
805
+ // The frame is 38 lines (15 ADRs or more), so 162 governing heads make exactly 200, 163 make 201 and 170 make 208.
806
+
807
+ const capOf = (text) => Number(text.match(/^maxLines: (\d+)$/m)[1]);
808
+ const corpusOf = (n) =>
809
+ Array.from({ length: n }, (_, i) => {
810
+ const id = String(i + 1).padStart(3, '0');
811
+ return { id, idNum: i + 1, title: `T${id}`, status: 'accepted', supersedes: [], supersededBy: [], fileName: `AD-${id}-t.md` };
812
+ });
813
+
814
+ describe('the navigator stamps a cap it honours', () => {
815
+ it('200 while it fits, its own line count past it', () => {
816
+ // spec:adr-navigator/S1
817
+ for (const heads of [2, 162]) {
818
+ const nav = buildNavigator(corpusOf(heads), '2026-07-09');
819
+ assert.equal(capOf(nav), 200, `${heads} heads keep the 200 floor`);
820
+ }
821
+ assert.equal(lineCountOf(buildNavigator(corpusOf(162), '2026-07-09')), 200);
822
+ // 165 entries, two superseded: 163 heads, 201 lines — a stamp from the entry count would read 203.
823
+ const corpus = corpusOf(165).map((e, i) => (i < 2 ? { ...e, supersededBy: ['003'] } : e));
824
+ const big = buildNavigator(corpus, '2026-07-09');
825
+ assert.equal(lineCountOf(big), 201);
826
+ assert.equal(capOf(big), 201, 'past the floor the stamp is the file\'s own count');
827
+ });
828
+
829
+ it('a written navigator past 200 lines fits its cap, --check is green, a hand-edited cap is stale', () => {
830
+ // spec:adr-navigator/S2
831
+ const root = makeRoot();
832
+ seedMigrated(root, { hotIds: ['901'], storeIds: corpusOf(169).map((e) => e.id) });
833
+ const nav = readFileSync(join(root, NAV_REL), 'utf8');
834
+ assert.ok(lineCountOf(nav) > 200, 'the fixture is past the floor');
835
+ assert.ok(lineCountOf(nav) <= capOf(nav), 'the docs gate accepts the written navigator');
836
+ assert.equal(run(['--check', '--today=2026-07-09'], root).code, 0);
837
+ writeFileSync(join(root, NAV_REL), nav.replace(/^maxLines: \d+$/m, 'maxLines: 999'));
838
+ const stale = run(['--check', '--today=2026-07-09'], root);
839
+ assert.equal(stale.code, 1);
840
+ assert.match(stale.errText, /log\.md is stale/);
841
+ });
842
+ });
843
+
803
844
  // ── item (h) degrade branches on the REAL default regenerator (no injection) ───────────
804
845
 
805
846
  describe('defaultRegenerateIndex — loud-degrade branches', () => {