sage: brief for row 175 (#1673, --tester on the window reads, mos-0868); Window B doc after mos-0866 F1-F3 and mos-0868: W4 from the unit template, agents to Mos with --tester, Jason's reads in J1 and J2

This commit is contained in:
jason.woltje committed 2026-10-19 17:07:57 -05:00
1 parent 4b738e2da9
commit f8ac674cc6
2 files changed
+167 -28

No files matched your search

+73 -28
View File
@@ -6,7 +6,9 @@ passed. Mos runs every window step. Jason is present for the whole window,
because it runs `--migrate` (decision 96).
- OLD, live after Window A: `5fa9cc8f2c66b52408b652b59c3070f7210b44d0`. Bus
schema 3h.
schema 3h. Window A passed (mos-0866): activation
`20261019T214524Z-5fa9cc8f2c66`, with plug-ins usage-source `["transcript"]` and
channel `["cli","webui"]` from Jason's W5b.
- NEW: `ca15d73e9fdcdcf66a10f86cdedeb2d6a08f2b00`, on origin `refactor`. Bus
schema 3i. No copy yet; W0 makes it.
- The target stays ca15d73e9 although origin `refactor` is past it. The two code
@@ -17,10 +19,25 @@ because it runs `--migrate` (decision 96).
- REPO: the canonical checkout. Tool blob on the day: `4b16e848a6b9` unless row
166 or 382's fix has landed. mos-0838 D2: whatever the blob, Window B gets a
scratch `activate --migrate` 3h to 3i and a `rollback --restore` on that
scratch root first (P3).
scratch root first (P3). Row 166 was unblocked by mos-0866. If it lands before
this window, the blob is no longer 4b16e848, so P3 runs again on the landed
blob and Mos checks the plan again (mos-0849/0850).
- `P`, `COPIES`, `C_OLD` and `C_NEW` as in Window A, with `C_OLD` on
5fa9cc8f2 and `C_NEW` on ca15d73e9. `names`, `plugins` and `threads` run from
the copy CLI (F1, P19 378).
- `NEW=ca15d73e9fdcdcf66a10f86cdedeb2d6a08f2b00`, the bare sha.
`console-check.sh` takes the sha, not the copy path (mos-0866 F3).
- Who runs the reads (mos-0866 F2, mos-0868). Every bus read without `--tester`
refuses inside an agent run with exit 3 (`cli.mjs:184-186` calls
`refuseInsideAgent`, `transport.mjs:28`). At 5fa9cc8f2 `agents` already takes
`--tester` (`cli.mjs:50`), so Mos runs `mosaic agents --business mosaic-stack
--tester`, as well as `stack-release.sh`, `console-check.sh`, `launches list`
and `talk --tester`. `usage`, `limits`, `names`, `threads` and `plugins` take
no `--tester` at 5fa9cc8f2 or ca15d73e9, so Jason runs them from a human
shell, all in two paste blocks: J1 before activate and J2 after the W7 turn.
With the browser check that makes two touchpoints plus one. Row 175 (#1673)
adds `--tester` to those reads. It goes live no earlier than the first window
past c216f47db, so it doesn't change this window.
## What NEW carries over OLD
@@ -49,45 +66,62 @@ after the window, with a `launch.limits.tokens.daily` entry, on Mos's go.
| # | Who | Step |
|---|-----|------|
| W0 | Mos | `cd $REPO && scripts/stack-copy.sh copy ca15d73e9fdcdcf66a10f86cdedeb2d6a08f2b00`. Note the full id. Then P1 for NEW. |
| W1 | Jason, then Mos | PM-idle check as Window A. Record the start stamp. Baseline reads: `$C_OLD names --business mosaic-stack --json`, `$C_OLD threads --business mosaic-stack --json` and `mosaic usage --business mosaic-stack --json` saved whole, for its `total` and `byRun` (U1, mos-0846). Nobody talks to the chief until W6 passes, so no turn writes a usage record (D3). |
| W1 | Mos, then Jason | PM-idle check: Mos runs `mosaic agents --business mosaic-stack --tester` (the chief must not be `working`) and `mosaic launches list`, and records the start stamp (`date -u +%FT%TZ`). Then Jason pastes block J1 below and sends Mos the output whole. Nobody talks to the chief until W6 passes, so no turn writes a usage record (D3). |
| W2 | Mos | `scripts/stack-release.sh status mosaic-stack`: pointer on 5fa9cc8f2, schema 3h, last line ok. |
| W3 | Mos | `scripts/stack-release.sh activate ca15d73e9fdcdcf66a10f86cdedeb2d6a08f2b00 mosaic-stack --plan`. It must match P3 on from 5fa9cc8f2, to ca15d73e9, store schema 3h read from the store the running bus holds, target 3i, one migration step 3h to 3i, needs `--migrate`, needs Jason present, 8 commits. P3's `--plan` part (`scratch-plan-ca15d73e9.txt`, sha256 eb2f1694…) reads its store schema from the pointer, because that scratch root has no store; the value 3h must match. Any other difference stops the window before W5. |
| W4 | Mos | Drop-ins, as Window A. |
| W4 | Mos | The units, read only (mos-0866 F1). Window A's W4 named a pm.conf drop-in that doesn't exist: the bus unit has no drop-ins, and `--pm` is on the ExecStart line of the template `~/.config/systemd/user/[email protected]`. Run `systemctl --user show [email protected] -p ExecStart -p WorkingDirectory -p DropInPaths -p Environment` and `systemctl --user show mosaic-webui.service -p ExecStart -p WorkingDirectory -p DropInPaths`. DropInPaths is empty on both units. The bus ExecStart carries `--pm`. ExecStart and WorkingDirectory name the 5fa9cc8f2 copy. If the day's tool blob includes row 166, the bus `Environment=` and both ExecStart argv also hold no `"`, `'` or `\`, because activate refuses `unit-flags-drift` on any of them. Any other value ends the window with nothing stopped. |
| W5 | Mos, on Mos's go, Jason present | `scripts/stack-release.sh activate ca15d73e9fdcdcf66a10f86cdedeb2d6a08f2b00 mosaic-stack --pm --migrate`. Exit 0 and a final `result` `ok`. Record the activation id and the backup path and stamp it prints. |
| W6 | Mos | The checks below. |
| W7 | Mos, then Jason | The smoke below. |
| W6 | Mos | Mos's checks below. |
| W7 | Mos, then Jason | Mos's one turn below, then Jason pastes block J2 and sends Mos the output whole, then the browser check. |
### W6 checks
### J1, Jason's block before activate (W1)
```
$C_OLD names --business mosaic-stack --json
$C_OLD threads --business mosaic-stack --json
mosaic usage --business mosaic-stack --json # saved whole, for total and byRun (U1, mos-0846)
```
### W6 checks (Mos)
```
scripts/stack-release.sh status mosaic-stack # pointer on ca15d73e9, schema 3i, last line ok
bash $P/console-check.sh $NEW # CONSOLE_PASS
$C_NEW plugins --business mosaic-stack --json # unchanged from Window A; no limit-check
$C_NEW names --business mosaic-stack --json # same as W1
$C_NEW threads --business mosaic-stack --json # same as W1 ([] unless Chat was used after A)
mosaic limits --business mosaic-stack --json # exit 0
mosaic usage --business mosaic-stack --json # total and byRun equal W1's, field for field
bash $P/console-check.sh $NEW # CONSOLE_PASS ($NEW is the bare sha)
mosaic launches list # one running chief from this start
mosaic agents --business mosaic-stack # pm not running
mosaic agents --business mosaic-stack --tester # pm not running
```
The `usage` read shows that the 3i migration kept the usage records. `mosaic usage
--json` prints no record list, only `total` and the groups `byRun`, `byInstance`,
`byModel`, `byProvider` and `byAccount` (ca15d73e9 `packages/cli/src/usage.mjs`,
the same blob as 5fa9cc8f2's), so `total` and `byRun` must equal W1's field for
field.
### W7
### W7 turn (Mos)
```
mosaic talk chief "[mos test] re-pin ca15d73e9: reply with one line, no staffing request" --tester
mosaic usage --business mosaic-stack --json # one new byRun entry, this start's chief run: input > 0, output > 0, unknownTurns 0
```
A turn counts as known only when the transcript added usage (ca15d73e9
`packages/bus/src/usage.mjs`, the `no-new-usage` return), so nonzero input and
output with `unknownTurns` 0 prove the transcript source still works under 3i. ca15d73e9
also writes an account on new rows, so W7 doesn't compare `byAccount` with W1.
### J2, Jason's block after the W7 turn
```
$C_NEW plugins --business mosaic-stack --json # unchanged from Window A; no limit-check
$C_NEW names --business mosaic-stack --json # same as J1
$C_NEW threads --business mosaic-stack --json # same as J1 ([] unless Chat was used after A)
mosaic limits --business mosaic-stack --json # exit 0
mosaic usage --business mosaic-stack --json # see below
```
One `usage` read covers both checks. `mosaic usage --json` prints no record list,
only `total` and the groups `byRun`, `byInstance`, `byModel`, `byProvider` and
`byAccount` (ca15d73e9 `packages/cli/src/usage.mjs`, the same blob as
5fa9cc8f2's).
- The 3i migration kept the usage records: every `byRun` entry from J1 is in J2
field for field, and `total` is J1's plus the one new entry.
- The transcript source works under 3i: exactly one new `byRun` entry, this
start's chief run, with input > 0, output > 0 and `unknownTurns` 0. A turn
counts as known only when the transcript added usage (ca15d73e9
`packages/bus/src/usage.mjs`, the `no-new-usage` return).
ca15d73e9 also writes an account on new rows, so J2 doesn't compare `byAccount`
with J1. A J2 failure is a stop condition like any W6 failure, and the W7 turn's
usage row is one of the rows counted before any `rollback --restore`.
No pm smoke (F3, P19 380). Jason's browser check: Settings shows Token limits
with no daily quota entry. Nobody presses Save.
@@ -127,8 +161,10 @@ have none.
## Rollback copies
Keep 5fa9cc8f2, 8e7ea89a1 and 52f35e7a0. Don't touch 66ea40580. Nothing is
pruned: the rollback set, the backup and any post-attempt store stay.
Keep 5fa9cc8f2, 8e7ea89a1, 52f35e7a0 and 3190ad762 (mos-0866). Don't touch
66ea40580. Nothing is pruned: the rollback set, the backup and any post-attempt
store stay. A rollback of Window A itself, past W5b, still starts with Jason
setting channel to null through the 5fa9cc8f2 CLI.
## The first window past c216f47db (not yet a plan)
@@ -196,3 +232,12 @@ Read only, on 894ce3f37. Nothing live was changed.
the wrapper runs before W5, and at ca15d73e9, which it runs after. All are on
line 38 at both, checked with stand-in CLIs. `names`, `plugins` and `threads`
already run from the copy CLI. No line changed.
- mos-0866, 2026-10-19, after Window A: W4 rewritten from the template's
ExecStart, WorkingDirectory and DropInPaths (F1). The human bus reads moved to
Jason, from `callFor` at 5fa9cc8f2 `cli.mjs:184-186` (F2; `usage`, `names` and
`limits` take no `--tester`). `$NEW` is the bare sha (F3).
- mos-0868, 2026-10-19: `agents` takes `--tester` at 5fa9cc8f2 (`cli.mjs:50`,
routed through `human()` at :566), so the PM-idle check and the W6 `agents`
read moved to Mos. Jason's reads are in J1 and J2, and one J2 `usage` read
replaces the separate W6 and W7 reads. The other reads get `--tester` in row
175 (#1673), brief `docs/plans/2026-10-19_tester-read-checks.md`.
@@ -0,0 +1,94 @@
# Brief: read-only window checks a tester can run
Gitea #1673. This brief follows Mos's ruling mos-0868, made on Sage's finding F2 from Window A (mos-0866). Owner
Dewey, reviewer Filbert, gate owner Sage. Queued after row 169, so it rides the first window past c216f47db. Line
numbers are at origin/refactor 4b738e2da.
## The problem
Every bus read the CLI makes without `--tester` refuses inside an agent run. `callFor`
(`packages/cli/src/cli.mjs:184-191`) calls `refuseInsideAgent` unless `--tester` is set, and that exits 3. Only
`inbox`, `decide`, `tasks`, `agents` and `trail` (`human()`, :570) and the message verbs take `--tester` today. So
every window read of usage, names, limits, plugins, threads, projects or instances needs Jason at a human shell. In
Window A that was two of his four handoffs.
mos-0868: those reads run under `--tester`, so Jason's part of a window is the plug-in sets, which are writes and stay
his, and the browser check. Read only. No write verb gains `--tester`.
## The change
### CLI
Add `--tester` to these read forms, and to nothing else:
| Command | Parser | Bus verb |
|---|---|---|
| `mosaic usage` | `usage.mjs:3-11` | `usage` |
| `mosaic names` | `names()`, `cli.mjs:311-321` | `names` |
| `mosaic limits` (the read) | `limits.mjs:35-38` | `limits` |
| `mosaic plugins` (the read) | `plugins.mjs:37` | `plugins` |
| `mosaic threads` | `thread.mjs:111` | `thread.list` (and `project.list` when it resolves a project) |
| `mosaic project list` | `project.mjs:38`, through `channelCallFor` | `project.list` |
| `mosaic business instances` (the read) | `instances.mjs:93-101` | `instances` |
- Each parser accepts `--tester` on the read form only and passes it through to `callFor`, which already binds the
tester (row 110). `name`, `limits set`, `plugins set`, `business instances add|set`, `project create`,
`thread create|open|read` and every other write refuse `--tester` with the usual usage error (exit 4).
- `threads` and `project list` go through `channelCallFor` (:196), whose channel caller reads `plugins` first
(`packages/plugins/src/channel.mjs:16`). So the tester needs the `plugins` read for those two to work at all.
- Leave `packages/cli/src/transport.mjs` alone. Row 164 freezes its bytes
(`packages/bus/tests/channel-proof.test.mjs:13`). The same test pins `packages/bus/src/human.mjs`,
`human-cli.mjs` and `packages/webui/src/bus.mjs`. Don't touch those either.
- Update the usage text (`cli.mjs` USAGE and each command's own) and `docs/TOOLS.md` where they list `--tester`.
### Bus (`packages/bus/src/broker.mjs`)
- `TESTER_VERBS` (:95) gains exactly `usage`, `names`, `limits`, `plugins`, `instances`, `project.list` and
`thread.list`. Update the comment above it.
- `limits`, `plugins` and `instances` never reach the `TESTER_VERBS` check at :1279. They take the organization branch
at :1224, which refuses `human-required` for anyone but the human (:1233). In that branch, let a tester session
through for those three reads only. Every write in the branch (`limits.set`, `plugins.set`, `instances.set`,
`routes.set`, `organization.set`) and the `routes` and `organization.revision` reads still refuse a tester, with
`tester-refused`. If `#organization` records an actor for a read, the tester's actor is `tester`, never the human's.
- `usage` (:2087) already lets a reader view through, and a tester's non-message verb runs as a reader (:1281). Check
it, and leave it alone if it works.
- `names` (:2247) sends anyone who isn't the human to `#agent`. Let a tester session read it.
- `project.list` (:2276) and `thread.list` (:2306) call `#human`, which refuses a reader `read-only`. Let a tester
session read both. `thread.list` filters by `owner=s.human`, and a tester has no human, so for a tester it lists
the business human's threads (`this.#business(s.business).human`). That is the same thread list the human sees.
- `project.create`, `thread.create`, `thread.open`, `thread.messages` and `name.set` don't change, so they still
refuse a tester.
- The binding doesn't change. `bindTester` (:521-525) still refuses `tester-off` unless the business var
`bus.tester` is exactly true. `verifyTester` (`packages/bus/src/tester.mjs`) still refuses `tester-required` under
a managed run.
## Tests
Bus tests in `packages/bus/tests/`, CLI tests in `packages/cli/tests/`. Each read is one test that runs on its own.
1. Each of the seven reads, bound as the tester with `bus.tester` true, returns what the human's read returns on the
same business (deep equal).
2. The CLI runs each of the seven with `--tester` and an agent marker in the environment (`AGENT_MARKERS`,
`transport.mjs:28`), and prints the same output as the human's form. Without `--tester` the same command still
exits 3 inside an agent.
3. A tester session refuses `tester-refused` for each of `limits.set`, `plugins.set`, `instances.set`, `routes.set`,
`organization.set`, `routes`, `organization.revision`, `name.set`, `project.create`, `thread.create`,
`thread.open` and `thread.messages`. The organization file's revision and the store don't change.
4. The CLI write forms refuse `--tester` with exit 4 before any bus call.
5. With `bus.tester` unset or not exactly true, the tester binding refuses `tester-off`, and a read never runs.
Under a managed run marker the CLI's tester transport refuses `tester-required`.
6. `TESTER_VERBS` is exactly the old ten plus the seven, pinned as a list.
Mutants, each killed by some gate test (mos-0862):
- drop each of the seven from `TESTER_VERBS`
- let `limits.set` or `plugins.set` through the organization branch for a tester
- `thread.list` for a tester binds `owner` to anything other than the business human
- the CLI accepts `--tester` on `limits set`
- the CLI drops `--tester` before `callFor` on any of the seven reads
## Gate
Filbert approves the candidate manifest. Bus, cli and plugins suites and every `scripts/test-*.sh` green on Sage's
gate. The window-plan docs drop the Jason-only reads in the first window plan whose pinned tree includes this row.
Until then Window B keeps the split in `agents/sage/work/window-plan/repin-ca15d73e9.md`.