@sabaiway/agent-workflow-memory 1.1.2 → 1.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,27 @@ 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.2.0 — Seeds the per-project orchestration config
8
+
9
+ The substrate now seeds a new **per-project, user-editable recipe config** —
10
+ `references/templates/orchestration.json` — deployed into `docs/ai/orchestration.json` by the bootstrap
11
+ template loop. It declares the orchestration **recipe** each named activity/slot uses (the composition
12
+ root's read-only `procedures` advisor reads it); the recipe **canon** and the slot **vocabulary** still
13
+ live in the engine / composition root, never here (the substrate keeps knowing nobody — the seed's
14
+ self-documentation uses generic "composition root" phrasing, naming no sibling). The shipped default is
15
+ conservative: **`solo` everywhere**, with an onboarding `_README` explaining how to raise a slot.
16
+
17
+ ### Added
18
+ - **`references/templates/orchestration.json`** — strict JSON (no comments), byte-identical to the kit's
19
+ fallback copy (kit↔memory template parity guard). Seeded on bootstrap; on upgrade it is **ensured
20
+ stamp-independently** (create-if-missing / **preserve-if-edited** — a user's edits are never clobbered),
21
+ so even an equal-head (`1.3.0`) deployment gains it **without a lineage-head bump or a migration file**.
22
+ - An **ownership-table** row distinguishing the seeded, editable recipe **CONFIG** (memory) from the
23
+ recipe **CANON** + slot vocabulary (engine / composition root).
24
+
25
+ The deployment-lineage head stays **`1.3.0`** (no `docs/ai` structural change; no migration file). The
26
+ npm package version is a separate axis.
27
+
7
28
  ## 1.1.2 — Entry-point template headroom for the orchestration pointer
8
29
 
9
30
  A **docs/prose** release (no new executable, the `1.1.1`/`1.9.1` precedent). The bundled entry-point
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.1.2'
6
+ version: '1.2.0'
7
7
  ---
8
8
 
9
9
  # agent-workflow-memory
@@ -47,6 +47,7 @@ What this substrate owns vs what it only points at. The methodology + orchestrat
47
47
  | Deployment-lineage stamp | **memory** | `docs/ai/.memory-version` |
48
48
  | Plan→Phase→Step vocabulary, lifecycle, `queue.md`, mandatory Cleanup | **methodology** (not this skill) | the empty `workflow:methodology` slot — filled by the composition root |
49
49
  | Orchestration recipes (Solo / Reviewed / Council / Delegated) | **methodology engine** (not this skill) | the empty `workflow:orchestration` slot — filled by the composition root |
50
+ | Per-project recipe **CONFIG** (which recipe each activity/slot uses) | **memory** seeds an *editable default* | `docs/ai/orchestration.json` (hand-edited; the recipe **canon** + the slot **vocabulary** live in the engine / composition root, never here) |
50
51
 
51
52
  ---
52
53
 
@@ -95,7 +96,9 @@ bootstrapping over a live system, but the user makes the final call.
95
96
  (`ln -s AGENTS.md CLAUDE.md`). **Leave BOTH pointer slots (`workflow:methodology` +
96
97
  `workflow:orchestration`) exactly as shipped — empty.** Filling them is the composition root's job.
97
98
  6. **Deploy `docs/ai/`.** Create the files + `pages/` from
98
- `${CLAUDE_SKILL_DIR}/references/templates/`. Keep each file's frontmatter.
99
+ `${CLAUDE_SKILL_DIR}/references/templates/` (every non-`AGENTS.md` template, including the seeded,
100
+ **user-editable** `docs/ai/orchestration.json` config — strict JSON, the per-project recipe defaults
101
+ the composition root's `procedures` advisor reads). Keep each file's frontmatter.
99
102
  7. **Fill templates** per the table below.
100
103
  8. **Install enforcement (Node projects).** Copy `${CLAUDE_SKILL_DIR}/references/scripts/*.mjs`
101
104
  (+ `*.test.mjs`) into the project's `scripts/`. **No Node runtime** → skip this + the hook;
@@ -110,7 +113,7 @@ bootstrapping over a live system, but the user makes the final call.
110
113
  10. **Stamp the deployment lineage.** Write the **deployment-lineage head** into
111
114
  `docs/ai/.memory-version` (one semver line). The lineage head is **`1.3.0`** (the
112
115
  `LINEAGE_HEAD` constant in `scripts/stamp-takeover.mjs`) — the shared `agent-workflow`
113
- lineage, **not** this package's npm version (`1.1.0`). Use the atomic writer in
116
+ lineage, **not** this package's npm version (`1.2.0`). Use the atomic writer in
114
117
  `scripts/stamp-takeover.mjs` (write-temp + rename).
115
118
  11. **Report & ask.** Show `tree docs/ai/`, 2–3 lines on filled-vs-TODO, then **ask before
116
119
  committing** — never auto-commit. **Exception — delegated mode (below): skip this gate.**
@@ -118,7 +121,8 @@ bootstrapping over a live system, but the user makes the final call.
118
121
  > **Delegated mode (invoked by the composition root) — applies to BOTH bootstrap and upgrade.**
119
122
  > When the family composition root drives this substrate as part of a family bootstrap **or
120
123
  > upgrade**, do the write steps (bootstrap 1–10 / upgrade 1–7: write `docs/ai/` + `AGENTS.md` +
121
- > `.memory-version`) but **do NOT** run **any** commit gate and **do NOT** ask to commit — the
124
+ > `.memory-version`, **including seeding / stamp-independently ensuring `docs/ai/orchestration.json`**)
125
+ > but **do NOT** run **any** commit gate and **do NOT** ask to commit — the
122
126
  > composition root owns the **single** commit gate, raised after it injects the two pointer slots.
123
127
  > The three setup answers and the target dir are passed in by the composition root; you perform no
124
128
  > commit and no slot injection. **Standalone** invocation keeps its own commit gate (bootstrap step
@@ -156,8 +160,14 @@ Fill strategy:
156
160
  block and removes it only after the user's explicit consent — never by default); if it is
157
161
  **untracked AND not ignored** → **AMBIGUOUS** → **ASK** the user before writing. This visibility check
158
162
  runs on **every** in-range upgrade, even at head — it is not gated by the stamp delta, but it is gated
159
- **behind** the never-downgrade STOP above. **Then**, if the stamp **equals** the head → report "up to
160
- date" (plus any footprint move just made) and stop.
163
+ **behind** the never-downgrade STOP above. **Also stamp-independent (same gate, before the equal-head
164
+ short-circuit): ensure `docs/ai/orchestration.json`** **create it from
165
+ `${CLAUDE_SKILL_DIR}/references/templates/orchestration.json` if missing**, **preserve it byte-for-byte
166
+ if it already exists** (a user may have edited it; never clobber it). This is why an equal-head (`1.3.0`)
167
+ deployment still gains the config seed **without a lineage-head bump or a migration file** (the
168
+ stamp-independent-reconcile precedent — like the pointer slots + the hidden-mode footprint). **Then**,
169
+ if the stamp **equals** the head → report "up to date" (plus any footprint move / config seed just
170
+ made) and stop.
161
171
  3. Show the relevant `${CLAUDE_SKILL_DIR}/CHANGELOG.md` context (entries newer than the stamp).
162
172
  4. Apply `${CLAUDE_SKILL_DIR}/migrations/<version>-<slug>.md` in **semver order**, only those
163
173
  newer than the stamp. Migrations are **idempotent**.
@@ -183,7 +193,7 @@ Fill strategy:
183
193
  - **Source vs target directory.** Templates/scripts are read from the skill's own dir; the
184
194
  **working directory is the target project** — never write substrate files back into the skill.
185
195
  - **Stamp = lineage head, not package version.** `.memory-version` carries `1.3.0` (the shared
186
- `agent-workflow` lineage), not the npm `1.1.0`. They are independent axes.
196
+ `agent-workflow` lineage), not the npm `1.2.0`. They are independent axes.
187
197
  - **Both pointer slots ship empty and stay the user's.** Never author methodology or orchestration
188
198
  text into them; on upgrade, preserve their content byte-for-byte. The composition root is their only
189
199
  writer.
@@ -213,7 +223,8 @@ The three setup choices each have a full contract in
213
223
 
214
224
  - [`references/contracts.md`](references/contracts.md) — the three setup contracts in full.
215
225
  - [`references/templates/`](references/templates/) — stack-agnostic `AGENTS.md` (with the two empty
216
- pointer slots — methodology + orchestration), `agent_rules.md`, and all `docs/ai/` files to deploy.
226
+ pointer slots — methodology + orchestration), `agent_rules.md`, the seeded user-editable
227
+ `orchestration.json` config, and all `docs/ai/` files to deploy.
217
228
  - [`references/scripts/`](references/scripts/) — the Node enforcement scripts (caps + staleness +
218
229
  index-freshness gate, 3-tier archive, hook installer) and their unit tests.
219
230
  - [`scripts/stamp-takeover.mjs`](scripts/stamp-takeover.mjs) — the upgrade-time lineage state
@@ -33,6 +33,7 @@ describe('memory installer — payload + symlink-traversal hardening', () => {
33
33
  'scripts/stamp-takeover.mjs',
34
34
  'references/contracts.md',
35
35
  'references/templates/AGENTS.md',
36
+ 'references/templates/orchestration.json',
36
37
  'migrations/legacy-stamp-takeover.md',
37
38
  ]) {
38
39
  assert.ok(existsSync(join(target, f)), `missing installed entry: ${f}`);
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": "1.1.2",
6
+ "version": "1.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": "1.1.2",
3
+ "version": "1.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",
@@ -139,7 +139,10 @@ export const parseStaleAfter = (value) => {
139
139
  return Number(m[1]);
140
140
  };
141
141
 
142
- const walkMarkdownFiles = async (dir) => {
142
+ // Discover the docs to validate: ONLY `*.md` files (recursively). Non-`.md` files — e.g. a hand-edited
143
+ // `docs/ai/orchestration.json` config — are inherently skipped, so they are never subject to the
144
+ // frontmatter / maxLines caps. Exported so that skip is pinned by a regression test.
145
+ export const walkMarkdownFiles = async (dir) => {
143
146
  const entries = await readdir(dir, { withFileTypes: true });
144
147
  const files = [];
145
148
  for (const entry of entries) {
@@ -10,6 +10,7 @@ import {
10
10
  inspectFile,
11
11
  buildIndex,
12
12
  checkIndexFreshness,
13
+ walkMarkdownFiles,
13
14
  } from './check-docs-size.mjs';
14
15
 
15
16
  describe('parseFrontmatter', () => {
@@ -126,6 +127,29 @@ describe('inspectFile', () => {
126
127
  });
127
128
  });
128
129
 
130
+ // The cap-validator discovers ONLY `*.md` files, so a non-`.md` doc — e.g. a hand-edited
131
+ // `docs/ai/orchestration.json` config — is inherently skipped: never validated for frontmatter / caps.
132
+ // This belt-and-suspenders regression pins that skip so adding a config `.json` under docs/ai can never
133
+ // start failing the docs gate.
134
+ describe('walkMarkdownFiles — only *.md is discovered (a config .json is skipped)', () => {
135
+ let dir;
136
+ beforeEach(async () => {
137
+ dir = await mkdtemp(join(tmpdir(), 'walk-md-test-'));
138
+ });
139
+ afterEach(async () => {
140
+ await rm(dir, { recursive: true, force: true });
141
+ });
142
+
143
+ it('returns the .md file and NOT a sibling orchestration.json', async () => {
144
+ await writeFile(join(dir, 'doc.md'), '---\ntype: reference\nmaxLines: 100\n---\n\nbody.\n');
145
+ await writeFile(join(dir, 'orchestration.json'), '{ "plan-authoring": { "review": "reviewed" } }\n');
146
+ const found = await walkMarkdownFiles(dir);
147
+ expect(found.some((f) => f.endsWith('doc.md'))).toBe(true);
148
+ expect(found.some((f) => f.endsWith('.json'))).toBe(false);
149
+ expect(found.some((f) => f.endsWith('orchestration.json'))).toBe(false);
150
+ });
151
+ });
152
+
129
153
  // Synthetic row matching the shape produced by `inspectFile` + `formatRow`.
130
154
  const makeRow = (path, overrides = {}) => ({
131
155
  path,
@@ -0,0 +1,5 @@
1
+ {
2
+ "_README": "Per-project orchestration config: the recipe used at each step (slot) of each named activity. Hand-edit this file — it is never written for you. Each activity is configured independently (e.g. plan-authoring, plan-execution), and so is each slot within it. A slot's value is a recipe: a 'review' slot accepts solo | reviewed | council (you self-review / one backend reviews / both review and you synthesize); an 'execute' slot accepts solo | delegated (you implement / a backend runs a bounded sub-task). The default below is 'solo' everywhere — no execution backend required. Raise a slot to reviewed or council for a second opinion, or to delegated to hand off execution; those need an execution backend set up first. Remove a slot's line to fall back to the computed default (reviewed when a review backend is ready, otherwise solo). Run the read-only procedures advisor to see an activity's steps plus the recipe resolved for your environment, and pass a per-run override to change one slot just once. Strict JSON — no comments.",
3
+ "plan-authoring": { "review": "solo" },
4
+ "plan-execution": { "execute": "solo", "review": "solo" }
5
+ }
@@ -18,6 +18,7 @@ import {
18
18
  cpSync,
19
19
  readdirSync,
20
20
  readFileSync,
21
+ writeFileSync,
21
22
  existsSync,
22
23
  symlinkSync,
23
24
  } from 'node:fs';
@@ -122,6 +123,45 @@ describe('standalone substrate bootstrap (end-to-end, real temp project)', () =>
122
123
  assert.equal(orch.trim(), '', 'the orchestration slot is empty as shipped');
123
124
  });
124
125
 
126
+ it('seeds docs/ai/orchestration.json from the template (the bootstrap loop deploys it)', () => {
127
+ const project = makeProject();
128
+ const docsAi = bootstrap(project);
129
+ const seeded = join(docsAi, 'orchestration.json');
130
+ assert.ok(existsSync(seeded), 'the orchestration.json config is seeded into docs/ai');
131
+ assert.equal(
132
+ readFileSync(seeded, 'utf8'),
133
+ readFileSync(join(TEMPLATES, 'orchestration.json'), 'utf8'),
134
+ 'the seeded config is byte-identical to the template',
135
+ );
136
+ // strict JSON valid + the conservative all-solo default the maintainer chose.
137
+ const config = JSON.parse(readFileSync(seeded, 'utf8'));
138
+ assert.equal(typeof config._README, 'string', 'an onboarding _README is present');
139
+ assert.equal(config['plan-authoring'].review, 'solo', 'default review recipe is solo');
140
+ });
141
+
142
+ // The stamp-independent upgrade "ensure" (SKILL.md upgrade step 2): create-if-missing /
143
+ // preserve-if-edited. Modeled here the way the documented prose performs it — so an equal-head
144
+ // re-run never clobbers a user's edited config, and a deleted one is re-seeded.
145
+ it('the upgrade ensure preserves an edited config and re-creates a deleted one', () => {
146
+ const project = makeProject();
147
+ const docsAi = bootstrap(project);
148
+ const dest = join(docsAi, 'orchestration.json');
149
+ const ensureConfig = () => {
150
+ if (!existsSync(dest)) cpSync(join(TEMPLATES, 'orchestration.json'), dest);
151
+ };
152
+
153
+ // A user edits the deployed config.
154
+ writeFileSync(dest, '{ "plan-authoring": { "review": "council" } }\n');
155
+ ensureConfig(); // an equal-head upgrade re-runs the ensure
156
+ assert.match(readFileSync(dest, 'utf8'), /council/, 'an edited config is preserved (never clobbered)');
157
+
158
+ // A missing config is re-seeded.
159
+ rmSync(dest);
160
+ ensureConfig();
161
+ assert.ok(existsSync(dest), 'a missing config is re-created from the template');
162
+ assert.equal(readFileSync(dest, 'utf8'), readFileSync(join(TEMPLATES, 'orchestration.json'), 'utf8'));
163
+ });
164
+
125
165
  it('stays ≤ the cap when the composition root fills BOTH pointer slots (D-CAP headroom)', () => {
126
166
  const project = makeProject();
127
167
  bootstrap(project);