@awebai/oats 0.22.4 → 0.22.5
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/bin/oats.mjs +24 -5
- package/capabilities/oats-okf/agents/memory-harvest/soul.yaml +0 -1
- package/capabilities/oats-okf/bin/oats-okf.mjs +57 -16
- package/capabilities/oats-okf/oats.json +12 -2
- package/capabilities/oats-okf/skills/memory-harvest/SKILL.md +3 -0
- package/docs/integrations.md +27 -0
- package/docs/operating-team-migration.md +81 -17
- package/docs/packages.md +5 -1
- package/docs/release-notes/v0.22.5.md +36 -0
- package/lib/core.mjs +2 -0
- package/package-catalog.json +1 -1
- package/package.json +1 -1
package/bin/oats.mjs
CHANGED
|
@@ -2206,8 +2206,20 @@ function migrateCmd() {
|
|
|
2206
2206
|
/** oats update <package> — transactional package update with diff + trust reset. */
|
|
2207
2207
|
function updatePackageCmd(id) {
|
|
2208
2208
|
const dir = dirFlag();
|
|
2209
|
+
// --to <selector>: move a catalog-sourced lock to another catalog ref
|
|
2210
|
+
// (tag) through the same transactional update. A lock with an explicit
|
|
2211
|
+
// selector keeps it on a plain update by design; this is the operator's
|
|
2212
|
+
// way to advance it without remove + reinstall.
|
|
2213
|
+
// Two spellings: `oats update <id> <id>@<selector>` (the engine's own spec
|
|
2214
|
+
// form) or `oats update <id> --to <selector>`.
|
|
2215
|
+
const to = flag("to");
|
|
2216
|
+
if (to === true) { cmdFail("E_BAD_ARGS", "--to needs a catalog selector, e.g. --to v1.10.1"); return; }
|
|
2217
|
+
if (to !== undefined && !/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(to)) { cmdFail("E_BAD_ARGS", `--to selector ${JSON.stringify(to)} is not a catalog ref`); return; }
|
|
2218
|
+
const positional = args[2] && !args[2].startsWith("--") ? args[2] : undefined;
|
|
2219
|
+
if (positional && to !== undefined) { cmdFail("E_BAD_ARGS", "give either <id>@<selector> or --to <selector>, not both"); return; }
|
|
2220
|
+
const spec = positional || (to !== undefined ? `${id}@${to}` : undefined);
|
|
2209
2221
|
let r;
|
|
2210
|
-
try { r = updatePackage(dir, id); } catch (e) { cmdFail(e.code || "invalid-lock", e.message || e); return; }
|
|
2222
|
+
try { r = updatePackage(dir, id, spec ? { spec } : {}); } catch (e) { cmdFail(e.code || "invalid-lock", e.message || e); return; }
|
|
2211
2223
|
if (JSON_MODE) { jsonOk(r); return; }
|
|
2212
2224
|
// A moved package root is reported even when the bytes are identical: the
|
|
2213
2225
|
// lock now points somewhere else in the repository, and that is exactly the
|
|
@@ -3386,7 +3398,13 @@ else if (cmd === "doctor") {
|
|
|
3386
3398
|
args.includes("--json") ? doctorJson(doctorDir) : doctor(doctorDir);
|
|
3387
3399
|
}
|
|
3388
3400
|
else if (cmd === "use") use();
|
|
3389
|
-
else if (cmd === "update") {
|
|
3401
|
+
else if (cmd === "update") {
|
|
3402
|
+
const t = args[1] && !args[1].startsWith("--") ? args[1] : undefined;
|
|
3403
|
+
// A selector without a package must never fall through to the kernel
|
|
3404
|
+
// self-update (a different product) with the flag silently ignored.
|
|
3405
|
+
if (!t && flag("to") !== undefined) { cmdFail("E_BAD_ARGS", "oats update --to needs a package: oats update <package> --to <ref> (or <package> <package>@<ref>)"); process.exit(1); }
|
|
3406
|
+
t ? updatePackageCmd(t) : updateCmd();
|
|
3407
|
+
}
|
|
3390
3408
|
else if (cmd === "type") typeCmd();
|
|
3391
3409
|
else if (cmd === "inject") injectCmd();
|
|
3392
3410
|
else if (cmd === "install") install();
|
|
@@ -3488,9 +3506,10 @@ Usage:
|
|
|
3488
3506
|
report under error.details)
|
|
3489
3507
|
oats list [--dir <d>] [--json] installed packages, exported capabilities,
|
|
3490
3508
|
scopes, trust state
|
|
3491
|
-
oats update <package> [
|
|
3492
|
-
|
|
3493
|
-
all capability approvals invalidated
|
|
3509
|
+
oats update <package> [<package>@<ref>] transactional package update: temp fetch,
|
|
3510
|
+
[--to <ref>] [--dir <d>] closure validation, diff, lock replace,
|
|
3511
|
+
all capability approvals invalidated; a
|
|
3512
|
+
spec or --to moves a catalog lock to <ref>
|
|
3494
3513
|
oats remove <package> [--dir <d>] remove a package (refuses while config or
|
|
3495
3514
|
dependent packages reference it)
|
|
3496
3515
|
oats migrate [--dry-run] [--dir <d>] map this scope's v1 capability locks to
|
|
@@ -52,10 +52,6 @@ catch (e) {
|
|
|
52
52
|
if (JSON_MODE) jsonFail("E_HARVEST_FAILED", `malformed OATS_SETTINGS: ${e.message || e}`);
|
|
53
53
|
process.stderr.write(`oats-okf: malformed OATS_SETTINGS (ignoring): ${e.message || e}\n`);
|
|
54
54
|
}
|
|
55
|
-
/** Model for the memory-harvest agent — promotion judgment is cheap-but-good
|
|
56
|
-
* work; default gpt-5.5, overridable via okf settings { "harvest-model": ... }. */
|
|
57
|
-
const DEFAULT_HARVEST_MODEL = "github-copilot/gpt-5.5";
|
|
58
|
-
|
|
59
55
|
function runtimeError(code, message) {
|
|
60
56
|
return Object.assign(new Error(message), { code });
|
|
61
57
|
}
|
|
@@ -148,8 +144,19 @@ function planRecordHarvest(instanceHome) {
|
|
|
148
144
|
// The exact next watermark is written beside the current one by the
|
|
149
145
|
// package; the harvester's delivery is a rename, nothing retyped.
|
|
150
146
|
const nextPath = join(instanceHome, RECORD_WATERMARK.replace(/\.json$/, ".next.json"));
|
|
151
|
-
|
|
152
|
-
|
|
147
|
+
const windows = threads.map(({ thread, afterTurnId, untilTurnId }) => ({ thread, afterTurnId, untilTurnId }));
|
|
148
|
+
let pending;
|
|
149
|
+
try { pending = JSON.parse(readFileSync(nextPath, "utf8")).pendingHarvest; } catch { /* no prior plan */ }
|
|
150
|
+
const sameWindows = pending?.instance && JSON.stringify(pending.windows) === JSON.stringify(windows);
|
|
151
|
+
const warnings = sameWindows ? [
|
|
152
|
+
`previous harvester ${pending.instance} did not advance the watermark for ${windows.map((w) => `${w.thread} (${w.afterTurnId || "start"} -> ${w.untilTurnId})`).join(", ")}; inspect its outcome and use oats okf harvest --from-record --force to retry`,
|
|
153
|
+
] : [];
|
|
154
|
+
// Reuse the existing prepared watermark; no extra journal. Stamp it only
|
|
155
|
+
// after a successful spawn, and never overwrite a stalled plan on a skip.
|
|
156
|
+
if (sameWindows && !process.argv.includes("--force")) return { stalled: true, warnings };
|
|
157
|
+
const prepared = JSON.stringify(next, null, 2) + "\n";
|
|
158
|
+
writeFileSync(nextPath, prepared);
|
|
159
|
+
return { threads, windows, watermarkPath, nextPath, watermark: next, prepared, unattributed: (report.unattributed || []).length, problems, warnings };
|
|
153
160
|
}
|
|
154
161
|
|
|
155
162
|
/** Briefing block for record-fed candidates, appended to the harvest task. */
|
|
@@ -198,11 +205,27 @@ async function spawnHarvester(spawnArgs, task) {
|
|
|
198
205
|
}
|
|
199
206
|
}
|
|
200
207
|
|
|
201
|
-
function
|
|
202
|
-
const
|
|
208
|
+
function harvestRuntime() {
|
|
209
|
+
const runtime = settings["harvest-runtime"] ?? "pi";
|
|
210
|
+
if (!["pi", "claude", "codex"].includes(runtime)) {
|
|
211
|
+
throw runtimeError("E_HARVEST_SETTINGS", "harvest-runtime must be pi, claude or codex");
|
|
212
|
+
}
|
|
213
|
+
const configured = settings["harvest-model"];
|
|
214
|
+
if (configured != null && (typeof configured !== "string" || !configured.trim())) {
|
|
215
|
+
throw runtimeError("E_HARVEST_SETTINGS", "harvest-model must be a nonempty model name");
|
|
216
|
+
}
|
|
217
|
+
const model = configured?.trim() || undefined;
|
|
218
|
+
if (runtime !== "pi" && model?.includes("/")) {
|
|
219
|
+
throw runtimeError("E_HARVEST_SETTINGS", `harvest-model for ${runtime} must be a native model name, without a Pi provider/ prefix`);
|
|
220
|
+
}
|
|
221
|
+
return { runtime, model };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function harvestSpawnArgs({ slug, parent, repo, work, workDir, branch, runtime, model }) {
|
|
225
|
+
const args = ["--purpose", slug, "--parent", parent, "--repo", repo, "--work", work, "--runtime", runtime];
|
|
203
226
|
if (workDir) args.push("--work-dir", workDir);
|
|
204
227
|
if (branch) args.push("--branch", branch);
|
|
205
|
-
args.push("--model", model);
|
|
228
|
+
if (model) args.push("--model", model);
|
|
206
229
|
return args;
|
|
207
230
|
}
|
|
208
231
|
|
|
@@ -331,7 +354,7 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
331
354
|
}
|
|
332
355
|
}
|
|
333
356
|
const notesDir = join(home, "notes");
|
|
334
|
-
const skip = (why) => (JSON_MODE ? jsonOk({ harvest: "skipped", reason: why }) : out({ meta: { harvestSpawn: "skipped", why } }));
|
|
357
|
+
const skip = (why, warnings = []) => (JSON_MODE ? jsonOk({ harvest: "skipped", reason: why, ...(warnings.length ? { warnings } : {}) }) : out({ meta: { harvestSpawn: "skipped", why }, ...(warnings.length ? { warnings } : {}) }));
|
|
335
358
|
if (String(agName).startsWith("memory-harvest")) skip("self (loop guard)");
|
|
336
359
|
const notes = existsSync(notesDir) ? readdirSync(notesDir).filter((f) => f.endsWith(".md")) : [];
|
|
337
360
|
|
|
@@ -358,6 +381,7 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
358
381
|
// write few notes). --from-record asks for the record even with notes.
|
|
359
382
|
// Planned only now, after every skip above: a capture pass is a real
|
|
360
383
|
// write and index, and "calling it too often is safe" must stay true.
|
|
384
|
+
const execution = harvestRuntime();
|
|
361
385
|
let recordPlan = null;
|
|
362
386
|
if (notes.length === 0 || process.argv.includes("--from-record")) {
|
|
363
387
|
let planned = null;
|
|
@@ -366,13 +390,14 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
366
390
|
process.stderr.write(`oats-okf: record unavailable${notes.length ? ", harvesting notes only" : ""}: ${planned.unavailable}\n`);
|
|
367
391
|
if (notes.length === 0) skip("no pending notes");
|
|
368
392
|
} else recordPlan = planned;
|
|
393
|
+
for (const warning of recordPlan?.warnings || []) process.stderr.write(`oats-okf: record: ${warning}\n`);
|
|
394
|
+
if (recordPlan?.stalled) skip("previous harvester did not advance the watermark", recordPlan.warnings);
|
|
369
395
|
for (const line of recordPlan?.problems || []) process.stderr.write(`oats-okf: record: ${line}\n`);
|
|
370
396
|
if (recordPlan?.unattributed) process.stderr.write(`oats-okf: record: ${recordPlan.unattributed} session file(s) carry no working directory and cannot be attributed to any home (oats capture --home <home> lists them)\n`);
|
|
371
397
|
if (notes.length === 0 && !recordPlan) skip("no pending notes");
|
|
372
398
|
}
|
|
373
399
|
// Effective command settings are injected by capability dispatch. No
|
|
374
400
|
// resolved-config read crosses the public package boundary.
|
|
375
|
-
const harvestModel = settings["harvest-model"] || DEFAULT_HARVEST_MODEL;
|
|
376
401
|
const workDir = realpathSync(join(home, "work"));
|
|
377
402
|
const realSoul = realpathSync(sDir);
|
|
378
403
|
const harvName = `memory-harvest-${slug}`;
|
|
@@ -386,7 +411,7 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
386
411
|
// version. It must not touch the owner's work tree.
|
|
387
412
|
const task = `Harvest the pending notes of live LOCAL-SOUL instance "${inst}" (agent "${agName}") into its soul — by direct edits, no commit.\n\n- Source notes: ${notes.length ? `${notesDir} (${notes.join(", ")})` : "none pending"}\n- Soul knowledge bundle to update: ${join(realSoul, "knowledge")}\n- Soul skills dir (for procedure-shaped notes): ${join(realSoul, "skills")}\n- This soul is LOCAL (uncommitted, gitignored): edit those soul files IN PLACE. Do NOT run git commit — not for the soul, and not in ./work (the shared tree belongs to the working instance; leave it untouched).\n- Follow your memory-harvest skill for everything else: promote/merge/drop each note, knowledge vs skill routing, index + log discipline, validate the bundle, DELETE processed notes from the source notes/ dir.\n${recordBrief(recordPlan, packageRuntimeCli())}\n- Then run \`oats retire ${harvName} --self\`.`;
|
|
388
413
|
r = await spawnHarvester(harvestSpawnArgs({
|
|
389
|
-
slug, parent: inst, repo: context, work: "attached", workDir,
|
|
414
|
+
slug, parent: inst, repo: context, work: "attached", workDir, ...execution,
|
|
390
415
|
}), task);
|
|
391
416
|
} else if ((process.env.OATS_WORK || meta.work) === "workspace") {
|
|
392
417
|
// WORKSPACE-MODE instance: ./work is the whole workspace, not a git repo —
|
|
@@ -399,7 +424,7 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
399
424
|
const task = `Harvest the pending notes of live WORKSPACE-MODE instance "${inst}" (agent "${agName}") into its soul — delivered as a PR.\n\n- Source notes: ${notes.length ? `${notesDir} (${notes.join(", ")})` : "none pending"}\n- Your ./work is a dedicated worktree of the soul's home repo (${soulRepo}), branch memory-harvest/${slug}.\n- Soul knowledge bundle to update: ./work/${join(relSoul, "knowledge")}\n- Soul skills dir (for procedure-shaped notes): ./work/${join(relSoul, "skills")}\n- Follow your memory-harvest skill: promote/merge/drop each note, knowledge vs skill routing, index + log discipline, validate the bundle, DELETE processed notes from the source notes/ dir, and commit once (prefixed "memory-harvest:") if anything changed.${recordBrief(recordPlan, packageRuntimeCli())}\n- If you changed anything: push the branch and open a PR (\`git push -u origin memory-harvest/${slug}\` then \`gh pr create --fill\`). Do NOT merge it; the humans/owners of ${soulRepo} review soul changes. If gh is unavailable, push the branch and report the compare URL. A harvest that promoted nothing has nothing to commit, push or open; that is a completed harvest, not a failed one.\n- Finally run \`oats retire ${harvName} --self\` (keep the branch: --self only).`;
|
|
400
425
|
r = await spawnHarvester(harvestSpawnArgs({
|
|
401
426
|
slug, parent: inst, repo: soulRepo, work: "worktree",
|
|
402
|
-
branch: `memory-harvest/${slug}`,
|
|
427
|
+
branch: `memory-harvest/${slug}`, ...execution,
|
|
403
428
|
}), task);
|
|
404
429
|
} else {
|
|
405
430
|
// Repo-resident souls: write to the soul AS SEEN FROM THE WORK TREE, so the
|
|
@@ -410,11 +435,27 @@ _(the single next action — keep this current; a fresh session on any model res
|
|
|
410
435
|
: realSoul;
|
|
411
436
|
const task = `Harvest the pending notes of live instance "${inst}" (agent "${agName}") into its soul.\n\n- Source notes: ${notes.length ? `${notesDir} (${notes.join(", ")})` : "none pending"}\n- Soul knowledge bundle to update: ${join(soulTarget, "knowledge")}\n- Soul skills dir (for procedure-shaped notes): ${join(soulTarget, "skills")}\n- You are ATTACHED to the instance's work tree (./work) — commit your promotions there as a single commit, prefixed "memory-harvest:".\n- Follow your memory-harvest skill: promote/merge/drop each note, knowledge vs skill routing, index + log discipline, validate the bundle, DELETE processed notes from the source notes/ dir (so they are not re-harvested).${recordBrief(recordPlan, packageRuntimeCli())}\n- Commit if you changed anything (a harvest that promoted nothing has nothing to commit), then run \`oats retire ${harvName} --self\`.`;
|
|
412
437
|
r = await spawnHarvester(harvestSpawnArgs({
|
|
413
|
-
slug, parent: inst, repo: context, work: "attached", workDir,
|
|
438
|
+
slug, parent: inst, repo: context, work: "attached", workDir, ...execution,
|
|
414
439
|
}), task);
|
|
415
440
|
}
|
|
416
|
-
if (
|
|
417
|
-
|
|
441
|
+
if (recordPlan) {
|
|
442
|
+
try {
|
|
443
|
+
// A fast harvester may already have renamed the prepared file. Never
|
|
444
|
+
// recreate that consumed plan or replace a different plan's contents.
|
|
445
|
+
if (readFileSync(recordPlan.nextPath, "utf8") === recordPlan.prepared) {
|
|
446
|
+
writeFileSync(recordPlan.nextPath, JSON.stringify({ ...recordPlan.watermark, pendingHarvest: { instance: r.instance, windows: recordPlan.windows } }, null, 2) + "\n");
|
|
447
|
+
}
|
|
448
|
+
} catch (e) {
|
|
449
|
+
if (e.code !== "ENOENT") {
|
|
450
|
+
const warning = `harvester ${r.instance} spawned but its retry marker could not be recorded: ${e.message}`;
|
|
451
|
+
recordPlan.warnings.push(warning);
|
|
452
|
+
process.stderr.write(`oats-okf: ${warning}\n`);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
const warnings = recordPlan?.warnings?.length ? { warnings: recordPlan.warnings } : {};
|
|
457
|
+
if (JSON_MODE) jsonOk({ harvest: "spawned", instance: r.instance, window: r.tmux?.window || null, ...warnings, ...(recordPlan ? { record: { threads: recordPlan.threads.map((t) => t.thread), ...(recordPlan.unattributed ? { unattributed: recordPlan.unattributed } : {}), ...(recordPlan.problems?.length ? { problems: recordPlan.problems } : {}) } } : {}) });
|
|
458
|
+
out({ meta: { harvestSpawn: r.instance, window: r.tmux?.window }, ...warnings });
|
|
418
459
|
} catch (e) {
|
|
419
460
|
if (JSON_MODE) jsonFail(e.code || "E_HARVEST_FAILED", `harvest spawn failed (notes are safe on disk): ${e.message || e}`);
|
|
420
461
|
warn(`harvest spawn failed (notes are safe on disk): ${e.message || e}`);
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"capability": "oats.okf",
|
|
3
3
|
"command": "okf",
|
|
4
|
-
"version": "1.5.
|
|
5
|
-
"compatibility": { "oats": ">=0.22.
|
|
4
|
+
"version": "1.5.1",
|
|
5
|
+
"compatibility": { "oats": ">=0.22.3" },
|
|
6
6
|
"layer": "knowledge",
|
|
7
7
|
"description": "Knowledge layer via OKF: soul bundles, instance memory (STATE.md/log.md/notes/), continuous post-commit harvest into the soul (commit, PR, or direct-edit for local souls), craft + memory skills, validator.",
|
|
8
8
|
"requires": [],
|
|
9
|
+
"settings": {
|
|
10
|
+
"harvest-runtime": {
|
|
11
|
+
"default": "pi",
|
|
12
|
+
"values": ["pi", "claude", "codex"],
|
|
13
|
+
"description": "Harness for the memory harvester, independent of the source instance's runtime."
|
|
14
|
+
},
|
|
15
|
+
"harvest-model": {
|
|
16
|
+
"description": "Optional model pin for the selected harvest runtime, for example to use a cheaper model: a Pi provider/model or a native Claude/Codex model. When omitted, each harness uses its configured default."
|
|
17
|
+
}
|
|
18
|
+
},
|
|
9
19
|
"agents": [
|
|
10
20
|
"agents/memory-harvest"
|
|
11
21
|
],
|
|
@@ -92,6 +92,9 @@ is how what they learned reaches the soul.
|
|
|
92
92
|
one-line title, the claim, and its provenance as the turn ids it came from.
|
|
93
93
|
A candidate is something the instance learned or decided, stated in the
|
|
94
94
|
turns, not something you infer it should have learned.
|
|
95
|
+
- Never promote a secret or credential, however it appears in the record.
|
|
96
|
+
- Never promote third-party message content verbatim. A lesson may be about a
|
|
97
|
+
received message; unverified sender content is not soul knowledge by transcription.
|
|
95
98
|
- Then judge every candidate exactly as a note: promote, merge, or drop
|
|
96
99
|
against the same bar. Expect most to drop: session trivia, tool noise,
|
|
97
100
|
restated repo facts and task-scoped decisions all fail it. Promoted
|
package/docs/integrations.md
CHANGED
|
@@ -140,6 +140,33 @@ Test an integration as a capability package: acquire, lock, trust, activate,
|
|
|
140
140
|
spawn, retire, with the golden fixtures as the behavior oracle for the kernel
|
|
141
141
|
side.
|
|
142
142
|
|
|
143
|
+
## oats.okf harvest settings (1.5.1)
|
|
144
|
+
|
|
145
|
+
The harvester can use a different harness from the source instance. Select one
|
|
146
|
+
that is installed and authenticated on the host where the harvest runs:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
oats use oats.okf --settings harvest-runtime=claude
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
- `harvest-runtime: pi | claude | codex` defaults to `pi`.
|
|
153
|
+
- `harvest-model` is an optional pin, for example to use a cheaper model.
|
|
154
|
+
When omitted, each harness uses its configured default. Pi accepts
|
|
155
|
+
provider/model patterns. Claude and Codex require a native model name
|
|
156
|
+
(for example `sonnet` or `gpt-5.5`), without a Pi provider prefix.
|
|
157
|
+
|
|
158
|
+
These settings apply to note and record harvests, including deferred retirement
|
|
159
|
+
and remote harvests. For a remote instance, configure its host's knowledge
|
|
160
|
+
binding; the local viewer does not supply its own provider credentials.
|
|
161
|
+
|
|
162
|
+
If a record harvester was spawned but did not advance its watermark, planning
|
|
163
|
+
the same windows again warns with that instance and the boundary IDs and skips
|
|
164
|
+
another spawn. Inspect the previous attempt first. `oats okf harvest
|
|
165
|
+
--from-record --force` retries those windows explicitly; it still refuses to
|
|
166
|
+
start a second harvester while the first one's home exists. The check uses the
|
|
167
|
+
existing prepared watermark file and does not treat a successful spawn as
|
|
168
|
+
completed learning.
|
|
169
|
+
|
|
143
170
|
## oats.aweb settings (1.10.0)
|
|
144
171
|
|
|
145
172
|
Set with `oats use oats.aweb --settings <key>=<value>` at a scope, or per
|
|
@@ -9,7 +9,9 @@ The cjr runbook is owned by Merlin at
|
|
|
9
9
|
`~/cjr/agents/docs/2026-09-05-oats-migration.md`. This document records the
|
|
10
10
|
shared framework work and the wider rollout.
|
|
11
11
|
|
|
12
|
-
Fresh identities are authorized for
|
|
12
|
+
Fresh identities are authorized for continuing seats as well as specialists and
|
|
13
|
+
reviewers. Preserve the accepted retained-identity choice where it is useful.
|
|
14
|
+
Merlin retains
|
|
13
15
|
both `cjr.aweb.ai/merlin` and his existing durable DID. Aweb clarified that
|
|
14
16
|
re-minting the same address changes identity and breaks continuity; the supported
|
|
15
17
|
path is an explicit transfer of his existing authority with one live process.
|
|
@@ -31,19 +33,22 @@ Every remembering role must have a tested learning path. Preserve each role's
|
|
|
31
33
|
explicit policy: Cjr reviewers exclude accumulated memory; Themis uses
|
|
32
34
|
reviewed learning. Config discovery alone establishes none of this.
|
|
33
35
|
|
|
34
|
-
The installed baseline is published OATS 0.22.
|
|
36
|
+
The installed CLI baseline is published OATS 0.22.4 on this Mac and `aweb-agents`,
|
|
35
37
|
including native Pi/Claude/Codex, tmux/Herdr, shared `yolo`, remote Desktop
|
|
36
38
|
roster/actions, retained-authority binding and corrected deferred retirement.
|
|
37
|
-
The
|
|
38
|
-
launch checks
|
|
39
|
+
The installed Mac Desktop 0.22.4 passed published ZIP checksum, strict deep
|
|
40
|
+
codesign, packaged renderer and PTY launch checks; the previous 0.22.3 app
|
|
41
|
+
is preserved for rollback. Official oats.okf 1.5.0 provides record-fed harvesting; each
|
|
39
42
|
deployment must select its authenticated provider. Version 1.5.1, adding
|
|
40
43
|
harvest-runtime selection and detection of unadvanced record plans, remains
|
|
41
44
|
under independent review and is not yet published.
|
|
42
45
|
|
|
43
|
-
No standing seat has transferred yet. Cjr's
|
|
44
|
-
code and knowledge
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
No standing seat has transferred yet. Cjr's workers have landed reviewed
|
|
47
|
+
code and knowledge. Two ordinary cycles on published 0.22.3 completed automatic
|
|
48
|
+
harvester retirement, independent knowledge review, integration and home-only
|
|
49
|
+
worker retirement. A successor read promoted indexed knowledge at startup and
|
|
50
|
+
identified how it shaped its implementation; both acceptance checks passed.
|
|
51
|
+
The reviewed aweb broker candidate passed
|
|
47
52
|
real harness delivery; installation of its published release as the normal
|
|
48
53
|
host service remains a prerequisite for standing cutover.
|
|
49
54
|
|
|
@@ -55,10 +60,10 @@ of continuing seats.
|
|
|
55
60
|
|
|
56
61
|
| Scope | Starting point | Required disposition |
|
|
57
62
|
| --- | --- | --- |
|
|
58
|
-
| `~/awebai/oats` | Live Claude coordinator and Codex lead; managed review workers also running | Oats owns coordinator handover; lead owns lead handover;
|
|
59
|
-
| `~/cjr` | Preparation `5afb3e8b`; developer pilot landed on master `062e2c75`; legacy Merlin and Minerva live | Merlin owns safe handovers; preserve his DID/address;
|
|
60
|
-
| `~/awebai/aweb` | Live Claude coordinator and frontend in legacy homes | Oats owns coordinator handover; lead coordinates frontend with aweb after its current work;
|
|
61
|
-
| `~/tsm` | Five live seats: Zeus, Prometeo, Argos, Themis on Claude; Hermes on Codex.
|
|
63
|
+
| `~/awebai/oats` | Live Claude coordinator and Codex lead; managed review workers also running | Oats owns coordinator handover; lead owns lead handover; follow the explicit fresh or retained identity choice |
|
|
64
|
+
| `~/cjr` | Preparation `5afb3e8b`; developer pilot landed on master `062e2c75`; legacy Merlin and Minerva live | Merlin owns safe handovers; preserve his DID/address; automatic harvest completion and successor use are proven; prepare standing seats |
|
|
65
|
+
| `~/awebai/aweb` | Live Claude coordinator and frontend in legacy homes | Oats owns coordinator handover; lead coordinates frontend with aweb after its current work; handover task responsibility and cover child repositories |
|
|
66
|
+
| `~/tsm` | Five live seats: Zeus, Prometeo, Argos, Themis on Claude; Hermes on Codex. Themis and Argos config/souls integrated, official capabilities installed and trusted | Zeus prepared the five-seat handover plan; begin with Themis at a safe boundary, Zeus last; preserve session-local schedules and production authority |
|
|
62
67
|
| `~/prj/beadhub-all` | Live Codex session, despite stale offline roster | Beadhub accepted preparation and is at a safe boundary; retain its global identity, native Codex and separate canonical code roots under `~/awebai/beadhub`; billing remains separately gated |
|
|
63
68
|
| `~/prj/docflow` | Live Claude seat identified itself as local `juan.aweb.ai/alice` on `docflow:juan.aweb.ai` | Owner Juan; finish running mail backfill and register checks before transfer; retain identity, memory and Minerva route; accountant-sync remains deliberately unloaded |
|
|
64
69
|
| `ai.aweb` on `aweb-agents` | Aweb confirms Athena intentionally inactive; remote legacy home retained | Aweb and oats own archival inspection; do not resurrect as a continuing seat |
|
|
@@ -253,16 +258,21 @@ the shared rollout record.
|
|
|
253
258
|
- TSM's owner plan is `~/tsm/history/2026-09-06-tsm-oats-handover-plan.md`.
|
|
254
259
|
Preserve the five seats' worktrees, skills and knowledge; re-arm Zeus's
|
|
255
260
|
session-local schedules. Production credentials remain solely with the
|
|
256
|
-
authorized production operator.
|
|
257
|
-
|
|
261
|
+
authorized production operator. Zeus remains last. Juan's current direction
|
|
262
|
+
authorizes completing the migration while he is away; the earlier
|
|
263
|
+
presence-only pause does not override it. Actual job and production boundaries
|
|
264
|
+
remain. Themis's ten-commit packet landed at `46cf2083`; its installed
|
|
265
|
+
oats.aweb 1.10.1 and oats.okf 1.5.0 passed the actual doctor. The other three
|
|
266
|
+
specialist briefs are being prepared at their work checkpoints.
|
|
258
267
|
- BeadHub configuration/soul commit `3ee13a8` in `awebai/beadhub-saas`
|
|
259
268
|
(`~/awebai/beadhub/beadhub-saas`) was independently ACKed by lead and
|
|
260
269
|
landed on `main`. Its tracked deployment template is materialized at the canonical
|
|
261
270
|
parent workspace, with trusted official capabilities and a clean actual
|
|
262
271
|
doctor result. The soul retains SaaS Git provenance and uses workspace mode
|
|
263
272
|
as a coordinator across the three canonical repositories. Its legacy holder
|
|
264
|
-
remains live; authenticated harvest-model configuration
|
|
265
|
-
broker service
|
|
273
|
+
remains live; authenticated harvest-model configuration is installed and
|
|
274
|
+
verified. The published broker service precedes activation. Stripe and
|
|
275
|
+
production gates remain separate.
|
|
266
276
|
- Docflow supplied its own identity and handover through its terminal. Its
|
|
267
277
|
backfill and register verification define the safe boundary. Preserve its
|
|
268
278
|
Claude memory and existing credentials in place; verify filesystem/TCC
|
|
@@ -291,7 +301,61 @@ the shared rollout record.
|
|
|
291
301
|
Published aw installation and standing-seat acceptance are still separate.
|
|
292
302
|
- Qualification found that tmux output loses tab separators in a minimal
|
|
293
303
|
launchd environment without a UTF-8 locale. Independently reviewed kernel
|
|
294
|
-
|
|
304
|
+
fixes `a0b0e14` and `0260ea9` shipped in 0.22.4, adding UTF-8 mode to
|
|
305
|
+
lifecycle/input and viewer calls. The host service also specifies
|
|
295
306
|
`LC_ALL=en_US.UTF-8`. Existing Claude plugin 1.7.8 also consumed mail before
|
|
296
307
|
the broker wake; updating to 1.7.9 eliminated that competing delivery path.
|
|
297
308
|
The deployed oats.aweb 1.10.1 floor now rejects the incompatible version.
|
|
309
|
+
|
|
310
|
+
- Cjr's second and third worker cycles completed on published kernel 0.22.3
|
|
311
|
+
and oats.okf 1.4.1 (note-fed). The
|
|
312
|
+
second promoted three reviewed concepts; the third read them before working,
|
|
313
|
+
produced the normal host-service health reader (`3dfe3acf`) and completed
|
|
314
|
+
its own reviewed harvest. Both harvesters self-retired without operator
|
|
315
|
+
completion. Each worker's ordinary retirement took four seconds and removed
|
|
316
|
+
its temporary alias. A post-merge fixture mismatch was corrected forward in
|
|
317
|
+
`e8a87c71`, with 47 tests green on a clean export. The reader's real service
|
|
318
|
+
check awaits installation of `ai.aweb.wake`.
|
|
319
|
+
- A record-only harvest of the fresh Claude wake-test session completed with
|
|
320
|
+
official oats.okf 1.5.0. It read its 27-turn source window, found no durable
|
|
321
|
+
lesson to promote, advanced the completed watermark and self-retired. The
|
|
322
|
+
first attempt exposed an unnecessary SQLite index write during pure journal
|
|
323
|
+
reads; reviewed fix `f100320` shipped in 0.22.4. The shared capture service
|
|
324
|
+
and its large index were preserved. Completion is proven; this run does not
|
|
325
|
+
claim a knowledge promotion.
|
|
326
|
+
- Docflow's two-commit preparation at `4458097` is independently ACKed: native
|
|
327
|
+
Claude, retained authority, session delivery and 18 valid curated OKF concepts.
|
|
328
|
+
Its owner is integrating and acquiring official packages. The running backfill
|
|
329
|
+
and FY2025 register checks still determine its activation boundary.
|
|
330
|
+
- The tracked aweb coordinator soul at `0a3a9a91` is independently ACKed; the
|
|
331
|
+
frontend soul at `b3985edb` is owner-reviewed and landed. They preserve the
|
|
332
|
+
coordinator's workspace and the frontend's managed primary SaaS worktree plus
|
|
333
|
+
explicitly assigned secondary OSS tree. Lead and Oats coordinator souls are
|
|
334
|
+
also reviewed and landed. Concrete deployment configuration and final
|
|
335
|
+
checkpoints precede each launch.
|
|
336
|
+
- OATS 0.22.4 is published at tag `0260ea9`, with npm packages and six Desktop
|
|
337
|
+
installers. CI retried once after a disappearing Git maintenance lock in a
|
|
338
|
+
fixture; kernel and Desktop gates then passed. A manual reviewed version-bump
|
|
339
|
+
PR completed the bot's permission-blocked post-publication step. Published
|
|
340
|
+
npm JavaScript bytes match the tag. Aweb 1.36.1 publication is still pending
|
|
341
|
+
a gateway build dependency download; the private candidate broker is not
|
|
342
|
+
being represented as the permanent published service.
|
|
343
|
+
|
|
344
|
+
- Cjr's retained declaration/runbook packet is independently ACKed through
|
|
345
|
+
`6dc6efc8` (three commits); its source authority remains in place and operator
|
|
346
|
+
stop-before-start applies to rollback as well as cutover. Its two soul startup
|
|
347
|
+
corrections also landed after independent review at `bd981b3a`. Official
|
|
348
|
+
oats.aweb 1.10.1 and oats.okf 1.5.0 are now installed and trusted for both
|
|
349
|
+
standing souls. These newer pins do not relabel the earlier worker proofs.
|
|
350
|
+
- TSM's Argos packet is integrated at `f20c54bb` after both independent and
|
|
351
|
+
owner ACKs; actual doctor resolves the two retained reviewer seats. BeadHub
|
|
352
|
+
and Themis supplied final private startup briefings and idle checkpoints.
|
|
353
|
+
Hermes and Prometeo were explicitly woken to read unseen followups and prepare
|
|
354
|
+
their continuation briefs; their legacy channels had not delivered those
|
|
355
|
+
requests reliably.
|
|
356
|
+
- A locked-selector update gap found during actual team setup is corrected
|
|
357
|
+
on main at `d95018e` (two independently reviewed commits). The next kernel
|
|
358
|
+
patch supports `oats update <package> --to <ref>` or a positional catalog
|
|
359
|
+
spec. A hermetic CLI test exercises the version transition, trust reset and
|
|
360
|
+
invalid arguments, including refusal to enter the kernel self-updater when
|
|
361
|
+
a package is missing. This change is not yet part of installed 0.22.4.
|
package/docs/packages.md
CHANGED
|
@@ -30,7 +30,11 @@ default. Only the selected subtree is installed and hashed, so repository docs,
|
|
|
30
30
|
CI configuration, owner souls, and sibling packages stay outside the package's
|
|
31
31
|
payload and integrity. One repository may ship several packages at different
|
|
32
32
|
paths. The lock pins the selected root in its own `path` field, and only an
|
|
33
|
-
explicit `oats update <package>` may move it.
|
|
33
|
+
explicit `oats update <package>` may move it. A catalog lock with an explicit
|
|
34
|
+
selector (`catalog:oats.aweb@v1.8.0`) keeps that selector on a plain update;
|
|
35
|
+
to advance it to another published ref, give the spec or `--to`:
|
|
36
|
+
`oats update oats.aweb oats.aweb@v1.10.1` or `oats update oats.aweb --to v1.10.1`
|
|
37
|
+
(same transactional path, approvals invalidated, then `oats trust`). See
|
|
34
38
|
[`design/package-engine-contract.md` §1.1](design/package-engine-contract.md).
|
|
35
39
|
|
|
36
40
|
Ground truth for the contract: [`oats-package.schema.json`](oats-package.schema.json),
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# OATS v0.22.5
|
|
2
|
+
|
|
3
|
+
A small release: the knowledge package 1.5.1 pin, a way to move a pinned
|
|
4
|
+
package lock from the CLI, and the operating records from the first team
|
|
5
|
+
rollouts.
|
|
6
|
+
|
|
7
|
+
## Bundled: oats.okf 1.5.1
|
|
8
|
+
|
|
9
|
+
`harvest-runtime` chooses the harness that runs the memory harvester (pi,
|
|
10
|
+
claude or codex; default pi), so a host authenticated for one harness need
|
|
11
|
+
not authenticate another. No harness gets a vendor default: with no
|
|
12
|
+
`harvest-model` set, the harvester inherits the harness's own configured
|
|
13
|
+
model; `harvest-model` remains the way to pin a cheaper one. The promotion
|
|
14
|
+
instructions exclude secrets, credentials and verbatim third-party message
|
|
15
|
+
content from promotion. `okf harvest` warns instead of respawning when a
|
|
16
|
+
plan's window boundaries did not move since the previous harvester was
|
|
17
|
+
spawned.
|
|
18
|
+
|
|
19
|
+
## Move a pinned package lock
|
|
20
|
+
|
|
21
|
+
`oats update <package> <package>@<ref>` or `oats update <package> --to <ref>`
|
|
22
|
+
moves a catalog-sourced lock to another published ref through the same
|
|
23
|
+
transactional update (closure validation, lock replace, approvals
|
|
24
|
+
invalidated, then `oats trust`). A plain `oats update <package>` keeps an
|
|
25
|
+
explicit selector, as before, and `--to` without a package is refused rather
|
|
26
|
+
than falling through to the kernel self-update (`oats update` with no
|
|
27
|
+
package).
|
|
28
|
+
|
|
29
|
+
## Also
|
|
30
|
+
|
|
31
|
+
- The managed oats coordinator soul (`agents/oats-coordinator/soul`) is
|
|
32
|
+
tracked, with playbooks for the release lane, payload publication and
|
|
33
|
+
review routing.
|
|
34
|
+
- `docs/operating-team-migration.md` records the installed 0.22.4 rollout,
|
|
35
|
+
the completed record-fed and note-fed harvest cycles, and the standing
|
|
36
|
+
seats prepared for cutover.
|
package/lib/core.mjs
CHANGED
|
@@ -3343,6 +3343,8 @@ export function updatePackage(startDir, packageId, opts = {}) {
|
|
|
3343
3343
|
// the user's own selection, so it is re-appended and stays sticky across
|
|
3344
3344
|
// updates; a catalog entry OWNS its path, so an update deliberately re-reads it
|
|
3345
3345
|
// and may adopt a moved root (reported below).
|
|
3346
|
+
if (opts.spec && src.kind !== "catalog") throw oatsError("invalid-source", `package "${packageId}" is locked from a ${src.kind} source; a selector (--to) applies to catalog-sourced packages only (its source is ${entry.source})`);
|
|
3347
|
+
if (opts.spec && parsePackageSource(opts.spec).id !== src.id) throw oatsError("invalid-source", `selector spec "${opts.spec}" names a different catalog package than the lock's ${src.id}`);
|
|
3346
3348
|
const spec = opts.spec || (src.kind === "catalog" ? (src.selector ? `${src.id}@${src.selector}` : src.id)
|
|
3347
3349
|
: src.kind === "git" ? `${src.ref && !/^[0-9a-f]{40}$/.test(src.ref) ? `${src.url}@${src.ref}` : src.url}#${entry.path}`
|
|
3348
3350
|
: src.path);
|
package/package-catalog.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@awebai/oats",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.5",
|
|
4
4
|
"description": "OATS (Open Agent Team Specification) — durable souls, disposable instances, targetable capability packages, and the runtime-neutral oats CLI/kernel.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|