@sabaiway/agent-workflow-memory 1.8.0 → 1.10.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 +36 -0
- package/SKILL.md +1 -1
- package/bin/install.mjs +96 -5
- package/capability.json +1 -1
- package/package.json +1 -1
- package/references/templates/agent_rules.md +4 -3
- package/references/templates/handover.md +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,42 @@ 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 `1.3.0`).
|
|
6
6
|
|
|
7
|
+
## 1.10.0 — Installer verb parity (the AD-034 cmp-keyed contract) + the recipe discovery step in the templates
|
|
8
|
+
|
|
9
|
+
A **feature** release (installer messaging + template text; deployment-lineage head stays `1.3.0`
|
|
10
|
+
— content-only, no migration):
|
|
11
|
+
|
|
12
|
+
- **`bin/install.mjs`** — the install verb is now keyed on the OBSERVED version relation, never on
|
|
13
|
+
mere presence (closing the false `updated the substrate to vX` on an already-current machine):
|
|
14
|
+
fresh/legacy-unstamped → `installed`; older → `updated the substrate to`; same →
|
|
15
|
+
`refreshed the already-current substrate` + the fact-only repair-on-rerun note (never a cache
|
|
16
|
+
accusation; conditional `@latest` hint); newer → a loud **never-downgrade refusal** (nothing
|
|
17
|
+
written) unless `--allow-downgrade`, which then says `downgraded the substrate to` plainly. The
|
|
18
|
+
installed version is read from the target SKILL.md `metadata:`-scoped `version` (decoy-proof);
|
|
19
|
+
an existing-but-unreadable SKILL.md **fails closed**, never silently treated as legacy. Helpers
|
|
20
|
+
cloned INLINE (this package references no sibling — the knows-nobody DAG).
|
|
21
|
+
- **`bin/install.test.mjs`** — the full engine-shape contract suite: no-op re-run wording,
|
|
22
|
+
downgrade refusal + `--allow-downgrade`, fail-closed unreadable SKILL.md, legacy no-stamp,
|
|
23
|
+
metadata-decoy version read.
|
|
24
|
+
- **`references/templates/agent_rules.md` §1.1** — new step 2: read `docs/ai/orchestration.json`
|
|
25
|
+
(the CONFIGURED orchestration recipes) BEFORE picking a task; a silent recipe downgrade is a
|
|
26
|
+
forbidden substitution. **`references/templates/handover.md`** — a standing `**Active recipes:**`
|
|
27
|
+
slot line. Both regions byte-identical with the kit template copies, path-neutral (this substrate
|
|
28
|
+
names no sibling skill), guarded by the kit's `template-region-parity.test.mjs` (AD-038).
|
|
29
|
+
|
|
30
|
+
## 1.9.0 — The agent_rules lens carries the checked-vs-unchecked plan boundary
|
|
31
|
+
|
|
32
|
+
A **feature** release (template text only; scripts and installer unchanged; deployment-lineage
|
|
33
|
+
head stays `1.3.0` — content-only, no migration). The §2.6 lens B5 bullet mirrors the engine's §9
|
|
34
|
+
sharpening:
|
|
35
|
+
|
|
36
|
+
- **`references/templates/agent_rules.md` (B5)** — a plan carries only **checked syntax** (a
|
|
37
|
+
Step's commands, run by its own Verification against an explicit expected outcome or gate) plus
|
|
38
|
+
literal fixture/schema fragments a named test copies or validates; **un-run, logic-bearing
|
|
39
|
+
syntax** (control-flow, a regex, a glob, a grammar, an algorithm body, a mini-DSL) never lives
|
|
40
|
+
in plan prose — a fold or draft that wants one is the trigger to write the test instead. The
|
|
41
|
+
line stays byte-identical to the kit template (lens-mirror guarded).
|
|
42
|
+
|
|
7
43
|
## 1.8.0 — ADR-cascade rotation script + the seeded per-project gate declaration
|
|
8
44
|
|
|
9
45
|
A **feature** release (deployment-lineage head stays `1.3.0`; the new surfaces reach existing
|
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: '1.
|
|
6
|
+
version: '1.10.0'
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# agent-workflow-memory
|
package/bin/install.mjs
CHANGED
|
@@ -53,6 +53,55 @@ const readVersion = async () => {
|
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
// Never-downgrade gate helpers (D3 / AD-012, verb contract AD-034) — cloned INLINE from the
|
|
57
|
+
// kit/engine installers (this package references no sibling — the knows-nobody DAG), so a bare
|
|
58
|
+
// `npx … init` serving an OLDER cached build cannot overwrite a NEWER installed substrate.
|
|
59
|
+
// Dependency-free semver: parse the leading x.y.z (prerelease/build ignored). compareSemver →
|
|
60
|
+
// -1 | 0 | 1, or null when either side is unparseable (a legacy install predating any stamp → no gate).
|
|
61
|
+
const parseSemver = (str) => {
|
|
62
|
+
const m = typeof str === 'string' ? str.trim().match(/^(\d+)\.(\d+)\.(\d+)/) : null;
|
|
63
|
+
return m ? [Number(m[1]), Number(m[2]), Number(m[3])] : null;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const compareSemver = (a, b) => {
|
|
67
|
+
const pa = parseSemver(a);
|
|
68
|
+
const pb = parseSemver(b);
|
|
69
|
+
if (!pa || !pb) return null;
|
|
70
|
+
const firstDiff = [0, 1, 2].map((i) => (pa[i] === pb[i] ? 0 : pa[i] < pb[i] ? -1 : 1)).find((c) => c !== 0);
|
|
71
|
+
return firstDiff ?? 0;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// Extract the version that is a DIRECT child of the top-level `metadata:` key — never a top-level or
|
|
75
|
+
// deeper-nested decoy `version:` (mirrors the manifest validator + the kit installer). Pure walk.
|
|
76
|
+
const metadataVersion = (frontmatter) => {
|
|
77
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
78
|
+
const metaIdx = lines.findIndex((l) => /^metadata:[ \t]*$/.test(l));
|
|
79
|
+
if (metaIdx === -1) return null;
|
|
80
|
+
const after = lines.slice(metaIdx + 1);
|
|
81
|
+
const dedent = after.findIndex((l) => /^[^ \t]/.test(l)); // a column-0 line closes the metadata block
|
|
82
|
+
const block = dedent === -1 ? after : after.slice(0, dedent);
|
|
83
|
+
const baseIndent = block.length ? (block[0].match(/^[ \t]*/)?.[0] ?? '') : '';
|
|
84
|
+
const verLine = block.find((l) => l.startsWith(`${baseIndent}version:`));
|
|
85
|
+
return verLine?.match(/version:[ \t]*['"]?(\d+\.\d+\.\d+)['"]?/)?.[1] ?? null;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// The installed version is read from the target's SKILL.md frontmatter metadata.version (the
|
|
89
|
+
// substrate's detect.installed.file). Returns the semver string, or null when ABSENT / unstamped
|
|
90
|
+
// (legacy → no gate). A SKILL.md that EXISTS but cannot be read is NOT swallowed as "legacy": fail
|
|
91
|
+
// closed (throw) so the gate can never be silently bypassed (AGENTS.md: no silent failures).
|
|
92
|
+
const readInstalledVersion = async (target) => {
|
|
93
|
+
const skill = resolve(target, 'SKILL.md');
|
|
94
|
+
if (!existsSync(skill)) return null; // absent → new/legacy install, nothing to compare
|
|
95
|
+
const text = await readFile(skill, 'utf8').catch((err) => {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`[agent-workflow-memory] cannot read the installed SKILL.md (${tildify(skill)}): ${err.message}. ` +
|
|
98
|
+
`Refusing to install over an unreadable substrate — fix permissions/contents or remove it, then re-run.`,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
const fm = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
102
|
+
return fm ? metadataVersion(fm[1]) : null;
|
|
103
|
+
};
|
|
104
|
+
|
|
56
105
|
// lstat without following symlinks; null when the path does not exist. Using lstat (not
|
|
57
106
|
// existsSync, which FOLLOWS symlinks and so reports a *dangling* symlink as absent) is what
|
|
58
107
|
// makes the guard below catch a dangling destination symlink too.
|
|
@@ -114,6 +163,7 @@ const parseArgs = (argv) => {
|
|
|
114
163
|
return {
|
|
115
164
|
help: argv.includes('--help') || argv.includes('-h'),
|
|
116
165
|
version: argv.includes('--version') || argv.includes('-v'),
|
|
166
|
+
allowDowngrade: argv.includes('--allow-downgrade'),
|
|
117
167
|
dir: dirFlag >= 0 ? argv[dirFlag + 1] : undefined,
|
|
118
168
|
};
|
|
119
169
|
};
|
|
@@ -128,13 +178,15 @@ const printHelp = (version) => {
|
|
|
128
178
|
console.log(`agent-workflow-memory ${version}
|
|
129
179
|
|
|
130
180
|
Usage:
|
|
131
|
-
npx @sabaiway/agent-workflow-memory@latest init [--dir <path>]
|
|
181
|
+
npx @sabaiway/agent-workflow-memory@latest init [--dir <path>] [--allow-downgrade]
|
|
132
182
|
npx @sabaiway/agent-workflow-memory --version
|
|
133
183
|
npx @sabaiway/agent-workflow-memory --help
|
|
134
184
|
|
|
135
185
|
Installs/refreshes the memory substrate at ~/.claude/skills/agent-workflow-memory
|
|
136
186
|
(override with --dir <path> or AGENT_WORKFLOW_MEMORY_DIR). init is additive —
|
|
137
|
-
it never deletes your settings and never writes through a symlink.
|
|
187
|
+
it never deletes your settings and never writes through a symlink. If the installed
|
|
188
|
+
substrate is newer than the version you ran, init refuses (no network — it compares
|
|
189
|
+
the version on disk) and points you at @latest; --allow-downgrade overrides that refusal.
|
|
138
190
|
|
|
139
191
|
After install, invoke the skill in your agent, inside a project:
|
|
140
192
|
first time in the project -> /agent-workflow-memory
|
|
@@ -169,13 +221,52 @@ const main = async () => {
|
|
|
169
221
|
console.error(`[agent-workflow-memory] target dir is a symlink — refusing to write through it: ${tildify(target)}`);
|
|
170
222
|
process.exit(1);
|
|
171
223
|
}
|
|
224
|
+
|
|
225
|
+
// Stale-cache defenses (no network — version already on disk vs this runner). Read BEFORE any write
|
|
226
|
+
// so a refusal touches nothing. cmp is null on a legacy/unparseable install → no gate.
|
|
227
|
+
const installedVersion = wasPresent ? await readInstalledVersion(target) : null;
|
|
228
|
+
const cmp = installedVersion ? compareSemver(installedVersion, version) : null;
|
|
229
|
+
|
|
230
|
+
// Never-downgrade gate (D3 / AD-012): a bare `npx … init` can run an OLDER cached build, which
|
|
231
|
+
// would overwrite a NEWER installed substrate with old files. Refuse loudly (nonzero) unless
|
|
232
|
+
// --allow-downgrade — surfacing the cache trap instead of silently regressing (no silent failures).
|
|
233
|
+
if (cmp === 1 && !args.allowDowngrade) {
|
|
234
|
+
console.error(
|
|
235
|
+
`[agent-workflow-memory] refusing to downgrade: the installed substrate is v${installedVersion}, but ` +
|
|
236
|
+
`this runner is the OLDER v${version}.\n` +
|
|
237
|
+
` This is the classic npx cache serving a stale build. To get the newest substrate, bypass the cache:\n` +
|
|
238
|
+
` npx @sabaiway/agent-workflow-memory@latest init\n` +
|
|
239
|
+
` (or pass --allow-downgrade to overwrite the newer install with v${version} anyway).`,
|
|
240
|
+
);
|
|
241
|
+
process.exit(1);
|
|
242
|
+
}
|
|
243
|
+
|
|
172
244
|
await mkdir(target, { recursive: true });
|
|
173
245
|
await Promise.all(
|
|
174
246
|
PAYLOAD.map((entry) => copyRecursive(resolve(PKG_ROOT, entry), resolve(target, entry), target)),
|
|
175
247
|
);
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
);
|
|
248
|
+
// Verb keyed on the OBSERVED version relation (cmp), never on mere presence (AD-034): null (fresh
|
|
249
|
+
// install, or a legacy/unstamped one whose prior version is unknowable) → "installed"; -1 → a real
|
|
250
|
+
// update; 0 → already current (the copy still ran — see the note below); 1 is reachable only under
|
|
251
|
+
// --allow-downgrade (the gate above refused otherwise) and says so plainly.
|
|
252
|
+
const verb =
|
|
253
|
+
cmp === 0 ? 'refreshed the already-current substrate'
|
|
254
|
+
: cmp === 1 ? 'downgraded the substrate to'
|
|
255
|
+
: cmp === -1 ? 'updated the substrate to'
|
|
256
|
+
: 'installed';
|
|
257
|
+
console.log(`[agent-workflow-memory] ${verb} v${version} -> ${tildify(target)}`);
|
|
258
|
+
|
|
259
|
+
// Same-version re-run: state observable facts only. The copy DID run (repair-on-rerun is a feature —
|
|
260
|
+
// it restores locally modified/deleted files), and whether npx served a cached build is NOT
|
|
261
|
+
// observable here (no network), so the note never claims it; the @latest hint is conditional.
|
|
262
|
+
if (cmp === 0) {
|
|
263
|
+
console.log(
|
|
264
|
+
`[agent-workflow-memory] note: the substrate was already v${version} — the copy still ran, restoring ` +
|
|
265
|
+
`any locally modified or deleted skill file to this version's packaged contents. If you ` +
|
|
266
|
+
`expected a NEWER version, invoke the @latest tag explicitly:\n` +
|
|
267
|
+
` npx @sabaiway/agent-workflow-memory@latest init`,
|
|
268
|
+
);
|
|
269
|
+
}
|
|
179
270
|
|
|
180
271
|
console.log(`
|
|
181
272
|
Next — open your agent inside a project and run the skill:
|
package/capability.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sabaiway/agent-workflow-memory",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.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",
|
|
@@ -18,8 +18,9 @@ Every AI agent working on this project **must** adhere to this protocol before w
|
|
|
18
18
|
### 1.1. Start of Session
|
|
19
19
|
Read in order, then confirm before starting:
|
|
20
20
|
1. `docs/ai/handover.md` — where we left off.
|
|
21
|
-
2. `docs/ai/
|
|
22
|
-
3.
|
|
21
|
+
2. `docs/ai/orchestration.json` — the CONFIGURED orchestration recipes (per activity/slot). Honor them: a silent recipe downgrade is a forbidden substitution.
|
|
22
|
+
3. `docs/ai/active_plan.md` — pick ONE task from "Immediate Priority".
|
|
23
|
+
4. Confirm with the user: *"I'm taking task X. Confirm?"*
|
|
23
24
|
|
|
24
25
|
### 1.2. During Work
|
|
25
26
|
**Before any feature:** read the relevant page spec (`docs/ai/pages/<page>.md`). If behaviour changes, update the spec FIRST so docs and code never diverge.
|
|
@@ -74,7 +75,7 @@ Apply this as part of §2 before any user-facing summary:
|
|
|
74
75
|
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.)
|
|
75
76
|
- **Fold by code, not prose.** Before folding a code-touching finding into a plan or change, read the cited `file:line` and cite it — a prose fold drifts from the code and seeds the next bug.
|
|
76
77
|
- **Right altitude.** Pin intent + invariants + acceptance criteria (named tests); leave fine code-mechanics to Execute, where prose cannot diverge from reality.
|
|
77
|
-
- **No code-mechanics in the plan.** A Step still carries its exact paths + commands (the plan-structure / self-review canon); the
|
|
78
|
+
- **No code-mechanics in the plan.** A Step still carries its exact paths + commands (the plan-structure / self-review canon) — checked syntax: the plan's own Verification runs them against an explicit expected outcome or gate; the only other syntax a plan may carry is a literal fixture/schema fragment a named test copies or validates. Un-run, logic-bearing syntax — control-flow, a regex, a glob, a grammar, an algorithm body, a mini-DSL — never lives in plan prose, however plausible or shell-verified it looks: a fold or draft that wants one is the trigger to write the test instead.
|
|
78
79
|
- **Test-as-spec.** Fold a code-touching finding into a red→green TEST, not a prose paragraph — the gate is the only deterministic checker; a paragraph cannot self-check.
|
|
79
80
|
- **Characterize-first.** Before editing UNCOVERED code, pin its current behavior in a green test, then edit — any unintended change goes red. Never edit what has no checker; first give it one. Keep edits atomic/reversible; prefer SUBTRACTIVE folds.
|
|
80
81
|
- **Fold minimally — prose has no checker.** An ephemeral, gitignored plan is prose with no executable checker; fold **minimally, in ONE place** and run a **self-consistency** read across the plan before every re-review — a fold that drifts several prose spots is what turns a 2-round review into churn.
|
|
@@ -14,6 +14,7 @@ maxLines: 80
|
|
|
14
14
|
|
|
15
15
|
**Last session:** {{DATE}}
|
|
16
16
|
**Branch:** {{BRANCH}}
|
|
17
|
+
**Active recipes:** not recorded yet — paste the configured-recipe line (composed from `docs/ai/orchestration.json`) and refresh it whenever the config changes.
|
|
17
18
|
|
|
18
19
|
## What was done last session
|
|
19
20
|
|