@sabaiway/agent-workflow-memory 8.1.1 → 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 +22 -0
- package/SKILL.md +1 -1
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/scripts/archive-decisions.mjs +8 -5
- package/references/scripts/archive-decisions.test.mjs +41 -0
- package/references/templates/agent_rules.md +2 -1
- package/references/templates/changelog.md +1 -0
- package/references/templates/handover.md +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ 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
|
+
|
|
19
|
+
## 8.1.2 — the templates carry the session close for the user (AD-159)
|
|
20
|
+
|
|
21
|
+
`references/templates/agent_rules.md`: §2.5 Communication gains the last bullet `Two blocks for the user`, and §2.7
|
|
22
|
+
Story sessions gains one closing sentence on one task per carrier session; both regions stay byte-identical to the
|
|
23
|
+
kit's copies, which are their one source. `references/templates/handover.md` gains a `## For the user` section with
|
|
24
|
+
the labels `What was done, and for what` and `What is next, and why`, each with one bootstrap line;
|
|
25
|
+
`references/templates/changelog.md`'s bootstrap entry gains a `**For the user:**` line after its Goal. The kit's
|
|
26
|
+
`tools/session-close-check.mjs` (kit 14.13.0) judges the handover section. No script changed. PATCH: only a new
|
|
27
|
+
bootstrap's text changes.
|
|
28
|
+
|
|
7
29
|
## 8.1.1 — the gate-migration's checker screen takes an argument tail (AD-158)
|
|
8
30
|
|
|
9
31
|
`references/scripts/migrate-gates.mjs`: the guarded `checker-claim canon` region is byte-identical to the kit's
|
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.
|
|
6
|
+
version: '8.1.3'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-memory
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-memory",
|
|
3
|
-
"version": "8.1.
|
|
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
|
|
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
|
|
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: ${
|
|
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
|
-
|
|
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', () => {
|
|
@@ -75,6 +75,7 @@ Apply this as part of §2 before any user-facing summary:
|
|
|
75
75
|
- **Live host/session facts are tool-composed only.** Any claim about the current host or session state (prompts fired, sandbox scope, whether a bypass was needed, network reachability, approval counts) must trace to **live tool output** from **this session**; a memory/handover snapshot is **context, never report facts**, and a claim with no live signal is **omitted or explicitly marked unverified** — never asserted from recollection.
|
|
76
76
|
- **The closing state block answers three DIFFERENT questions.** Close a user-facing message with three labelled slots — *now* · *what I need from you* · *what's next*. The slot LABELS stay ENGLISH — an English label is what lets a state-block checker FIND the block and its slots at all; everything written INTO a slot is in the project's dialogue language; when that language is not English, the checker's English phrase sets do not judge those values. **Now** = the state at this instant: what is RUNNING, or what the work is stopped on. It is **never a report of finished work** — what you completed goes in the message BODY, above the block. **From you** = the real unblocker, named; a turn that is ENDING always has one. **Next** = what follows. A *now* slot that opens with what was completed buries the one fact the reader opened the message for, and the three slots collapse into one restatement.
|
|
77
77
|
- **A skip that contradicts the tree is a finding.** A tool-composed `skipped-*` line whose stated reason the observed tree disproves (a "no Node" skip beside deployed Node scripts) is raised as a FINDING in the report, never pasted as a neutral outcome — and a tool may not emit a skip whose reason it could itself disprove.
|
|
78
|
+
- **Two blocks for the user.** At a plan's approval, at a release and at every session close, the message BODY — above the closing state block — carries two short blocks in the dialogue language. *What was done, and for what*: per item the user-facing problem, then what now exists; a command name is allowed, an internal id is not. *What is next, and why*: each proposal names its benefit, plus the one hand step if any. No internal ids, no review-round tallies. The same two blocks are the handover's `## For the user` section, which the kit's `session-close-check` checker judges at the close, where the kit is installed.
|
|
78
79
|
|
|
79
80
|
### 2.6. Planning, review & process-fidelity invariants
|
|
80
81
|
Apply these when authoring a plan, reviewing, folding a finding, or editing code — the layer read **before any code change**. (Full canon: the project's planning / workflow-methodology + orchestration canon. This section is rendered from that canon and refreshed on upgrade; a custom edit is preserved verbatim, but flagged.)
|
|
@@ -95,7 +96,7 @@ Apply these when authoring a plan, reviewing, folding a finding, or editing code
|
|
|
95
96
|
- **Cost lanes.** Route every step to the **cheapest adequate executor** — L0 deterministic script (the batched gate matrix over `gates.json`, the rotation `--check`s) · L1 cheap subagent (extraction/drafting only; the orchestrator verifies) · L2 subscription bridge · L3 frontier judgment. A step with **no named guardrail does not move down** a lane, and the **red lines never move down** (council review models · real code · ADR/handover/changelog-entry wording · persuasive copy · go/no-go · the approval asks). Own-error repair: salvage recorded state first (L0/L1, batched), never frontier re-derivation. **Prompt economy:** read-only fan-out (research/sweeps/extraction) runs ONLY on restricted-tool vehicles — a full-tool subagent for read-only work is a forbidden lane downgrade (invisible prompt-flood + blast radius), and a subagent is never told to shell out for facts obtainable read-only; the orchestrator's own shell form is ONE plain pipeline per call (a `;`/`&&` chain or env-prefixed invocation never matches a prefix allow rule); a fan-out launcher that gates per call yields to the agent-spawn lane — capability-gated: without restricted-tool vehicles (generic full-tool spawning does not count), read-only research stays in the orchestrator's own context, never a vehicle mandate a host cannot satisfy. Judgment, code, synthesis stay at the frontier lane (a task that genuinely runs/writes keeps a full-tool subagent); honest limit: no deterministic gate classifies a dispatch — canon at the point of use + placed vehicles + the retro loop. **Writer economy:** a stage's repeated WRITER commands batch — evidence declarations ride consecutive plain invocations of ONE allow-listed tool, other stage writers combine via one launcher per stage; never an unbatched writer scatter (each gated write is its own prompt).
|
|
96
97
|
|
|
97
98
|
### 2.7. Story sessions
|
|
98
|
-
A story — one row of an epic's ledger, carried by one plan — runs as five sessions, each ending at its own review checkpoint: **spec** (the contract under `docs/ai/specs/`, drafted and reviewed on the `plan-authoring` recipe), **plan** (the ledger, same recipe), **tests** (one task per ledger row, red first), **code** (one task per row, to green), and **diff review, release and record** (the review of the staged tree on the `plan-execution` recipe, the release where the story ships one, then the changelog and handover entries and the plan's Phase: Cleanup). Tests and code never share a session, and a spec and its plan never share one either. A storyless plan runs the same five. **Exception — a split:** moving code and its existing cases into modules, with no new logic and no new case, is one session: a short spec (the Module list), the split, the diff review, the release, Cleanup.
|
|
99
|
+
A story — one row of an epic's ledger, carried by one plan — runs as five sessions, each ending at its own review checkpoint: **spec** (the contract under `docs/ai/specs/`, drafted and reviewed on the `plan-authoring` recipe), **plan** (the ledger, same recipe), **tests** (one task per ledger row, red first), **code** (one task per row, to green), and **diff review, release and record** (the review of the staged tree on the `plan-execution` recipe, the release where the story ships one, then the changelog and handover entries and the plan's Phase: Cleanup). Tests and code never share a session, and a spec and its plan never share one either. A storyless plan runs the same five. **Exception — a split:** moving code and its existing cases into modules, with no new logic and no new case, is one session: a short spec (the Module list), the split, the diff review, the release, Cleanup. Inside the tests and code sessions it is one task per session: each task runs as its own carrier session — one brief, one run, never one run for a whole wave — and the orchestrator's session briefs, checks, verifies and folds.
|
|
99
100
|
|
|
100
101
|
---
|
|
101
102
|
|
|
@@ -17,6 +17,7 @@ maxLines: 700
|
|
|
17
17
|
## {{DATE}} — Bootstrap
|
|
18
18
|
|
|
19
19
|
**Goal:** Initialise the AI-agent memory system.
|
|
20
|
+
**For the user:** what was done, and for what — the project now has an AI-agent memory, so each session starts from the last one's record; what is next, and why — the first real task, whose entry says what it did for you.
|
|
20
21
|
**Changes:**
|
|
21
22
|
- Created `AGENTS.md` (entry point) + `CLAUDE.md` symlink.
|
|
22
23
|
- Created `docs/ai/` with the spec files + `pages/`.
|
|
@@ -16,6 +16,14 @@ maxLines: 80
|
|
|
16
16
|
**Branch:** {{BRANCH}}
|
|
17
17
|
**Active recipes:** not recorded yet — paste the configured-recipe line (composed from `docs/ai/orchestration.json`) and refresh it whenever the config changes.
|
|
18
18
|
|
|
19
|
+
## For the user
|
|
20
|
+
|
|
21
|
+
**What was done, and for what:**
|
|
22
|
+
- Bootstrap — the project now has an AI-agent memory, so each session starts from what the last one left.
|
|
23
|
+
|
|
24
|
+
**What is next, and why:**
|
|
25
|
+
- The first real task — its close replaces these two lines with what it did for you and what comes next.
|
|
26
|
+
|
|
19
27
|
## What was done last session
|
|
20
28
|
|
|
21
29
|
- Bootstrap — AI-agent memory system initialised.
|