# Review: agents/sage launcher files (2026-09-26) Reviewer: Dewey. Assigned by Sage. This was a read-only review. The only file written in the repository is this one. **Verdict: REVISE, small.** The launcher itself is sound: it matches the Researcher pattern, fails closed, reads and prints no secrets, and passes the row 16 launcher test when run for `sage` in a scratch copy. Two text and coverage items must change before the files are committed. One item needs Jason's decision. ## Files reviewed All five are untracked (`??`) and have never been committed on any branch. | File | SHA-256 | mtime | | --- | --- | --- | | `agents/sage/launch.sh` | `0212b59cd457574d837f6ea64734a71754048b9b5c5ca4eac46827dc8b9a5448` | 2026-09-12 | | `agents/sage/README.md` | `a939ba5483fda649e8c74ab4b1dc6c8c2c06c0d1ad2557b5dde79cc845c81719` | 2026-09-10 | | `agents/sage/SOUL.md` | `87123f940bccd72573df5005a044be81b65ea19c4e7b2c1fbe94063c213e8e2d` | 2026-09-08 | | `agents/sage/validate-sessions.mjs` | `79555a53af85a01a45618c6ad78b6d9f9ef3fc781320f1cf120ad45a904b2518` | 2026-09-08 | | `agents/sage/CONTEXT.md` | `8a77126f1a8c329618eb004f7fc048b1449e1fe08f52dc64f0574870a1abb5cc` | 2026-09-26 14:30 | Reference pattern (row 16, #1510, approved by Filbert): `agents/researcher/*` and `scripts/test-darkwing-launch.mjs`. I also compared the other launchers: darkwing, dewey, filbert and rocko. Provenance: SESSIONS.md line 150 (2026-09-09, Codex, "Owner-corrected Sage location"). The 2026-09-10 #1499 i1 verdict (I1-F1) removed `sage` from the launcher test because `agents/sage` was uncommitted. It said the seat had to "land `agents/sage` through its own reviewed change first". No review receipt exists for these files. This review is the first. ## Checks ### 1. Drift from the other launchers - **Registration guard:** the same as darkwing, dewey, filbert, researcher and rocko. It checks `MOSAIC_LAUNCH_REGISTERED` or `--check`, then runs `scripts/mosaic launch --repo … --harness pi sage`. - **Fail-closed config:** the launcher goes through `scripts/agent.sh --host-dev` → `scripts/agent-host-dev.sh`, the same path as the others. The shared helper does all of the following: - calls `load_config` and refuses unless the adapter is pi; - refuses a Pi version that differs from the `package.json` pin; - refuses any missing, unreadable or empty context file; - refuses a missing skill or goal extension; - refuses a non-TTY launch, a second instance (the flock lock) and damaged session history. Sage adds nothing to that path and bypasses nothing. - **Model pin:** `--provider zai --model glm-5.3 --thinking high` is placed **before** `"$@"`. The helper keeps the last value it sees, so a per-launch `--provider`, `--model` or `--thinking` overrides Sage's pin. Filbert's pin comes **after** `"$@"`, so it wins on every launch, including resume (agents/README.md documents this). Sage's order matches its own README ("can be overridden per launch") and the roster line (`zai/glm-5.3:high` retained), so it is intentional and not a defect. Nothing records whether Jason wants Sage's pin fixed like Filbert's or overridable. See N1. - **validate-sessions.mjs:** apart from the agent name, it is identical to the darkwing, dewey, filbert and researcher copies. - **agent.json:** only researcher has one. Darkwing, dewey and filbert do not, so its absence here is not drift. - **Test coverage: drift, required fix R1.** `scripts/test-darkwing-launch.mjs` loops over darkwing, dewey, filbert and researcher. Sage is not in the loop. That was correct while the files were uncommitted (I1-F1). A commit that adds `agents/sage` without adding it to the loop leaves the launcher untested, which the row 16 pattern does not allow. Evidence, with no repository changes: - **Scratch test run.** I copied the test to `/tmp/dewey-sage-launch-icJK/test.mjs` with two changes: the source path set to the checkout, and the loop set to `['sage']`, expecting provider `zai`, model `glm-5.3` and thinking `high`. It passes: 1/1 in 1.37 s (`/tmp/dewey-sage-launch-icJK/run.txt`). It covers: - `--check` with no registration and no state; - refusing an auth argument, a missing skill, broken history, an unknown argument, a non-TTY launch and a held lock; - the registration record; - resume, `--fresh` and `--user` overrides; - a context snapshot that contains "You are Sage". - **Real `--check` in the checkout.** `agents/sage/launch.sh --check` passes and prints: `Pi 0.85.1 | zai/glm-5.3:high`, the workspace, the SOUL, CONSTITUTION and USER paths, the sessions path and the skills list. By design and by test, `--check` writes no state and registers no seat. ### 2. Secrets and credential paths None are read or printed by these files. - **launch.sh** passes only provider, model and thinking flags. - **The shared helper** prints provider/model, paths and skill names. Pi resolves authentication itself, from its own store. That store is not named, read or copied by any of the five files, and it is the same for every Pi seat. - **validate-sessions.mjs** reads session JSONL and, on refusal, prints only the file path and an error message, not the content. - **README.md, SOUL.md and CONTEXT.md** contain no tokens, keys, `auth.json` paths or `~/.config/mosaic-dev` references. I grepped for `mosaic-dev`, `auth.json` and `.pi/agent`, with no hits. ### 3. Anything touching ~/.mosaic - **launch.sh, validate-sessions.mjs and SOUL.md:** nothing. - **README.md lines 23–27 and CONTEXT.md lines 46 and 52–56** direct the launched Sage to read and **keep writing** its strategy records in `/home/jwoltje/.mosaic/fleet/agents/sage/work/dyor-strategy/`. That directory exists (I checked with `ls -d` only; I read nothing in it). This is a standing instruction to write under `~/.mosaic`, and it conflicts with AGENTS.md and agents/README.md in two ways: - Both documents say not to modify `~/.mosaic` state in this bootstrap phase. - Both say the fleet Sage is being decommissioned. The records live inside that fleet seat's tree, so decommissioning could remove or strand them. The files present the location as Jason's choice ("Jason directed this private storage location"). The only repository record I found is the SESSIONS line 150 title, "Owner-corrected Sage location". I did not open the private records to look for his words. **This needs Jason (J1).** - **CONTEXT.md line 36–37** describes the fleet Sage correctly: being decommissioned, does not speak for this seat. ### 4. Whether CONTEXT.md states the current role correctly Mostly yes. Lines 31–37 say: - Sage has led the project since 2026-09-26 by Jason's ruling; - it coordinates assignments, review and integration; - Darkwing is a collaborating seat; - the lead role adds no push, merge or deployment authority; - the DYOR duties in SOUL are retained records whose continuation is Jason's call. This matches AGENTS.md and agents/README.md. Remaining gaps: - **README.md, required fix R2.** The title is "Sage: DYOR strategy in Mosaic Stack", and the body describes only DYOR planning. It does not mention the lead role, seat registration through `scripts/mosaic`, or the lack of push, merge or deploy authority. It contradicts agents/README.md row "Sage | Project lead…" and CONTEXT.md. Researcher's README shows the expected content: role, check/fresh, registration, fail-closed config, nothing copied from a fleet seat, and what tests do and do not prove. - **SOUL.md, N2.** The injected persona is DYOR-only. Line 11 even says "Mosaic Stack's development team lead is not automatically DYOR's business decision-maker", which reads as if the lead were someone else. CONTEXT is injected after SOUL and reconciles this, so a launched Sage gets the right role, but the first identity text it reads is the old one. - **N4, scope.** CONTEXT.md is injected only on the Pi launcher path. The running Sage (T3, Claude Code) never loads it. Its role comes from AGENTS.md and agents/README.md, which are already correct. ## Required before commit - **R1.** Add `sage` to the `scripts/test-darkwing-launch.mjs` loop, expecting `zai`, `glm-5.3` and thinking `high`. Commit it together with `agents/sage/*` so the files and their test land at once, as I1-F1 required. The scratch run above shows the assertions pass as written. - **R2.** Rewrite `agents/sage/README.md` for the current role, on the Researcher README pattern. Keep one line saying the DYOR records are retained and Jason decides whether that work continues. ## Needs Jason - **J1.** Decide where the private DYOR strategy records live now that the fleet Sage is being decommissioned. Either confirm the current location under `~/.mosaic/fleet/agents/sage/work/dyor-strategy/` as an explicit exception to the `~/.mosaic` rule and record it, or move them to a private location outside the fleet tree. Until then, README and CONTEXT should cite where the owner direction is recorded instead of only asserting it. ## Non-blocking - **N1.** Decide whether Sage's model pin should be fixed like Filbert's (after `"$@"`) or stay overridable (before it). Either is consistent if README and agents/README.md say which. - **N2.** Optionally add one line to SOUL.md that points to CONTEXT for the current role, or reword line 11. SOUL is a persona record, so change it only if Jason or Sage wants it changed. - **N3.** CONTEXT.md names personal host paths in a public repository: `/home/jwoltje/src/jarvis-brain/docs/personal/DYOR-HISTORY.md` and the `~/.mosaic` records path. Only the paths are exposed, not the contents. Darkwing's README already names a `/home/jwoltje` path, so there is precedent, but the `docs/personal` one is worth a second look before publishing. - **N4.** See section 4: CONTEXT.md does not reach the running T3 Sage. - **N5.** CONTEXT.md starts with a blank line and its own `=====` banner. The helper already writes a banner per file, so the snapshot shows two. Cosmetic. - **N6 (outside these files).** Researcher's CONTEXT.md (lines 4, 13, 25), SOUL.md (line 4) and README.md (line 3), and rocko's `launch.sh` line 49 ("team lead Darkwing"), still name Darkwing as coordinator. That was true under row 16 and is stale since the 2026-09-26 ruling. It is a follow-up for whoever owns those seats; this review does not change them. ## Not done - No model call and no live TUI launch. Whether `zai/glm-5.3` is reachable with the host credentials is not observed. - `agents/sage/work/` was not opened (`work/coordination/` belongs to the fleet Sage). `DISCORD-USER.md` was out of scope. - The private records under `~/.mosaic` were not read. ## R2 re-review (2026-09-26) **Verdict: APPROVE, source only.** Commit remains Sage's decision; push needs Jason's word as usual. Pins, re-hashed from the working tree and matching Sage's R2 message: | File | SHA-256 | | --- | --- | | `agents/sage/launch.sh` | `0212b59cd457574d837f6ea64734a71754048b9b5c5ca4eac46827dc8b9a5448` (unchanged) | | `agents/sage/README.md` | `cda73142…` | | `agents/sage/SOUL.md` | `29dc428c…` | | `agents/sage/validate-sessions.mjs` | `79555a53af85a01a45618c6ad78b6d9f9ef3fc781320f1cf120ad45a904b2518` (unchanged) | | `agents/sage/CONTEXT.md` | `480bc15f…` | | `scripts/test-darkwing-launch.mjs` | `59bd3596…` | - **R1 closed.** `sage` is in the loop. The filbert-only branch is now a `pinned` map (filbert `openai-codex/gpt-6-astra/low`, sage `zai/glm-5.3/high`); unpinned seats still expect the fixture config's `test/test-model`. `node --test scripts/test-darkwing-launch.mjs`: 6/6 (`/tmp/dewey-sage-r2-launch-test.txt`). `agents/sage/launch.sh --check` passes. Mutation, in a scratch copy only (`/tmp/dewey-sage-r2-mut-JmGb`): changing Sage's model to `glm-5.3-flash` fails the sage test at the model assertion (`'glm-5.3-flash'` vs `'glm-5.3'`), so the new branch has teeth. - **R2 closed.** README states the lead role, no push/merge/deploy authority, that T3 loads neither SOUL nor CONTEXT, registration through `scripts/mosaic`, `--check` side-effect freedom, fail-closed config, no `~/.mosaic` writes, DYOR as records only, and what offline tests don't prove. It now agrees with agents/README.md and CONTEXT.md. - **J1 closed by Sage as lead.** CONTEXT lines 45–53: read the old records, never write under `~/.mosaic`, create no new DYOR records until Jason names a location, never copy them into the repository. This only narrows what the seat may do and moves nothing, so it is within the lead's remit. Jason still owns where new DYOR records go; that is stated in both README and CONTEXT. - **N3 closed.** No `jarvis-brain` or `docs/personal` path remains in `agents/sage/*.md`. The `~/.mosaic` records path stays, which is needed for the read instruction. - **SOUL.** The new top paragraph states the lead role and makes DYOR conditional on Jason assigning it; line 15 now reads "leading Mosaic Stack development does not make you DYOR's business decision-maker". The later "Maintain durable strategy artifacts" line (55) is covered by that condition plus CONTEXT's no-new-records rule. - **N1 kept, one factual correction.** Sage's R2 note says the pin sits before `"$@"` "same as Darkwing and Dewey". Darkwing and Dewey carry no pin at all; they use the config default. Sage is the only seat with an overridable pin, Filbert the only one with a fixed pin. The decision itself is fine and README documents it; only the comparison is wrong. - **N5 kept.** Cosmetic, accepted. Not done: still no model call, so `zai/glm-5.3` reachability is unobserved.