docs: P3 amendment for pi's GLM and GPT routes, and the Case 2 proxy unit brief (mos-1616)

This commit is contained in:
jason.woltje committed 2026-10-24 17:13:28 -05:00
1 parent 58b2bbb3a5
commit c8a9bcf946
2 files changed
+158

No files matched your search

@@ -0,0 +1,82 @@
# Brief: the Case 2 proxy unit, so a hub installs P1's proxy unit
Written 2026-10-24 by Sage for Mos's mos-1616 ruling 1, which approved this row. Mos ruled that Sage
approves the brief if it only adds P1 item 7's unit to `scripts/case2-install.sh` and
`scripts/case2-unit.sh`: the same unit text, the same sha256 refusal and the Wants/After lines. Anything
beyond that goes to Mos. Read the rows doc's P1 brief (`docs/plans/2026-10-24_model-proxy-rows.md`, P1
item 7) and row 209's brief (`docs/plans/2026-10-24_row209-hub-54-window.md`, entry check 2) first. The
rows doc's "Rules for every row" apply here unchanged. Line numbers are at origin/refactor 58b2bbb3a, and
P1's paths are at its round 2 candidate magica/row329 86cb5c221. Check both once P1 lands.
## Brief: Case 2 proxy unit in case2-unit.sh and case2-install.sh
**Why.** A Case 2 hub (.54) installs through `scripts/case2-install.sh` (as the stack user) and
`scripts/case2-unit.sh` (as root, system units). P1 adds `mosaic-proxy.service` only to the user-unit path
(`scripts/instance.sh`, `scripts/proxy-service.sh`, `scripts/bus-service.sh`). `case2-unit.sh` writes the
bus and Console units and nothing else (:3-5, :56-57), and its bus unit has no `Wants=` or `After=` on a
proxy. Without this row, row 209 phase A can't run the proxy on .54.
**What ships.**
1. **The proxy unit, from P1's template.** `case2-unit.sh` renders a third system unit,
`mosaic-proxy.service`, from P1's `packages/cli/systemd/mosaic-proxy.service.in`, which this row doesn't
change.
- It uses the same render as the bus (`render()` :435): the copy for `@REPO@`, the unit PATH, `--node`
for `@NODE@`, and `@CONFIG_ENV@` deleted, because `case2-unit.sh` renders the default instance only.
- It gets the same added lines as the bus: `User=`, `Group=` and the four hardening lines, with no
capabilities and no `EnvironmentFile=`. `WantedBy=multi-user.target` replaces `default.target`, as for
the other two units.
- `check_unit` gets a `proxy` kind. Its fixed lines are those of P1's template: `ExecStart=` exactly
`<node> <copy>/packages/model-proxy/src/run.mjs`, and `NoNewPrivileges=yes`. Every check that applies to
both units today applies to this one too.
- A new test seam `CASE2_PROXY_TEMPLATE`, like `CASE2_BUS_TEMPLATE` (:44, :54).
2. **The sha256 refusal is P1's.** The unit starts P1's `run.mjs`, which checks the binary against PIN
before it spawns it. This row adds no second check and no other way to start the proxy. A test runs the
rendered `ExecStart=` in a temp root, with a made-up config and a binary that doesn't match PIN, and
checks P1's refusal code and exit status.
3. **Only when the instance has a proxy.** Root passes a new bare flag `--proxy` when the instance's config
has a `proxy` section.
- Without it, the render is byte for byte what it is today, and no proxy unit is written.
- With it, the bus unit gets `Wants=mosaic-proxy.service` and `After=mosaic-proxy.service`, in the place
where P1's `bus-service.sh` puts them, right after `Description=`. The Console unit doesn't change.
- Root still never imports copy code or reads the config (:16-21). A wrong flag fails closed: with no
proxy section, P1's runner refuses and the unit stops. With a proxy section and no flag, the proxy
doesn't run and P2's launch preflight refuses. BUILD.md checks both against the landed P1 and P2 code
and names the refusal codes.
4. **Re-install and start.**
- With `--proxy`, the three installed units must name one copy, and each must equal the render once its
copy id is the new one, the same rule as now (:557-571).
- Without `--proxy`, an installed `mosaic-proxy.service` refuses the run. The script never deletes a
unit file.
- Enable all of them, then start the proxy, then the bus, then the Console. A unit that was already
running is reported the way the bus and Console are now (:576-610), and the message names all three as
the units to restart together.
5. **case2-install.sh.** Only its closing "Next:" line (:231) changes, to tell root to add `--proxy` when the
config has a `proxy` section. If the builder finds that line needs no change, BUILD.md says why and the
file stays as it is.
**Out of scope.** P1's template, runner and PIN; the user-unit path; any named-instance render; reading the
config from root; the proxy's config or key; `mosaic proxy init`; any live host, unit or `/etc` write
outside a test's temp root.
**Tests**, in `packages/cli/tests/case2-unit.test.mjs` (and `case2-install.test.mjs` if item 5 changes the
script):
- with no `--proxy`, the bus and Console renders equal today's, byte for byte;
- with `--proxy`, the proxy unit's content, and the bus unit's `Wants=` and `After=` lines;
- each `check_unit` refusal on the proxy kind, by its message;
- P1's PIN refusal through the rendered `ExecStart=`;
- a re-install whose three units name two copies refuses;
- an installed proxy unit with no `--proxy` refuses and deletes nothing;
- the start order through the `CASE2_SYSTEMCTL` seam's log.
**Gate.**
- The builder is a new Codex gpt-6.1-sol thread and worktree off refactor (Q246, mos-1542). The row starts
when P1 (row 329) lands, and the builder rebases on it before any run.
- Boyd gives the first Codex review. Webby gives the Claude review of the root-run unit boundary and the
landing. Sage runs the landing gate.
- The run list for every whole-file run comes from imports and goes in the builder's report before the run.
`packages/cli/tests/case1-accept.test.mjs` stays off unless Mos approves (mos-1612).
- Every touched test file runs whole. Every package suite but queue, plus every `scripts/test-*.sh`, is
green on Sage's landing gate.
- Named mutants: the bus unit loses its `After=` line under `--proxy`; the proxy unit's `ExecStart=` skips
`run.mjs` and starts the binary directly.
@@ -0,0 +1,76 @@
# Brief amendment: row 332 (P3), pi's GLM and GPT routes through the proxy
Written 2026-10-24 by Sage. Mos approved items 4a to 4c in mos-1616 with three conditions, which are written
into the items below. Item 4d is settled there too. This came out of mos-1615 (DECISIONS Q253): on .54 Bean
runs GLM through a Z.ai key and Chet runs GPT through his ChatGPT/Codex login, both through the proxy. The
P3 brief in the rows doc (`docs/plans/2026-10-24_model-proxy-rows.md`, "Brief: P3 harness config from the
proxy", item 4) proves only the Anthropic route. Row 96 proved only that route for pi, too
(`agents/researcher/work/i1-proxy-test/REPORT.md`, S2-pi-A).
The rows doc isn't edited, because rows 329 to 333 pin its blob. This file replaces one line of P3 item 4,
"A non-Anthropic route names that provider's base URL in the same file.", and adds to P3's Tests and Gate.
Everything else in P3 stands. Line numbers are at origin/refactor 58b2bbb3a. Check them before you edit.
## Brief: P3 amendment, pi's GLM and GPT routes
**What pi 0.85.1 does today.** These facts come from the pinned pi's docs and its built-in provider data.
- `models.json` providers take `baseUrl`, `api`, `apiKey` and `models`. A built-in provider can be
overridden with a `baseUrl`.
- `apiKey` takes a literal, a `$ENV` reference, or `!command`, which pi runs as a shell command.
- The built-in `zai` provider is `openai-completions` at `https://api.z.ai/api/coding/paas/v4`, with z.ai
compat fields (`thinkingFormat: "zai"`, `zaiToolStream`).
- The built-in `openai-codex` provider speaks `openai-codex-responses` to chatgpt.com/backend-api with pi's
own OAuth, so it can't be pointed at the proxy.
**4a. One entry per route.** `<run>/home/.pi/agent/models.json` gets one provider entry for each route the
agent's `launch.models` names, each at the proxy's address from P3 item 1.
- Anthropic: as P3 item 4 says now, `anthropic-messages` with no `/v1`.
- GLM: `openai-completions` at `http://127.0.0.1:<port>/v1`, with the model id that the proxy's `/v1/models`
lists for the Z.ai key.
- GPT through the Codex login: a provider of the stack's own, never `openai-codex`, at
`http://127.0.0.1:<port>/v1`, with the model ids that the proxy lists for the Codex credential.
- The builder picks `openai-responses` or `openai-completions` once, at build time, from what the pinned
proxy passes for pi's requests. The harness has no fallback at run time: it writes the one shape the
build chose.
- BUILD.md records the choice and its evidence: the pinned proxy version, the requests tried on each shape
and what came back.
**4b. The key goes by reference.**
- Every entry's `apiKey` is `"$ANTHROPIC_API_KEY"`, the reference P3 item 4 already sets. `ENV_ALLOW`
(`packages/seat/src/session.mjs`:242) doesn't change.
- In proxy mode that variable holds only the session's own proxy key, because P3 item 2 drops the instance's
provider variables.
- `models.json` never holds a key value, and no value in it starts with `!`.
**4c. Tests.** These add to P3's Tests list:
- the `models.json` content for each of the three routes;
- every `apiKey` in `models.json` is the `$ANTHROPIC_API_KEY` reference;
- no key value appears anywhere in the file;
- no value in the file starts with `!`;
- an offline pi turn through the pinned proxy under `unshare -rn` for the GLM route, against a stand-in
upstream, the way row 96 and P1's acceptance run did. It runs from a script outside the gate's
`scripts/test-*.sh` set, and its output goes in BUILD.md as plain text.
The Codex route can't run offline without a real auth file, and no agent touches one. Its first real turn is
row 209 phase A step 7 (`docs/plans/2026-10-24_row209-hub-54-window.md`). BUILD.md says so.
BUILD.md also records whether the z.ai compat fields survive through the proxy, or whether the proxy drops
them, from the offline GLM turn.
**4d. One proxy per user (settled, mos-1616).** Unless P2 item 7 finds that the pinned proxy can limit a
client key to some models, Bean's key can reach Chet's ChatGPT login through the same proxy, and Chet's key
can reach Bean's Z.ai key. Mos accepted that for .54. Q249 sets one proxy per user and their agents, so both
agents and both accounts on .54 are Melanie's. If P1 found no per-key model limit, the bundle's model choice
stays the guard. If P1 found one, P2 narrows each key as P2 item 7 says. This row changes neither.
**Gate additions.** One more named mutant: `models.json` gets the key value instead of the
`$ANTHROPIC_API_KEY` reference. The 4c tests must kill it. The rest of P3's Gate stands: Gladstone's first
Codex review, Dewey's landing review, every touched test file whole, every package suite but queue plus
every `scripts/test-*.sh` green on Sage's landing gate, and the mutant where proxy mode still passes
`CLAUDE_CODE_OAUTH_TOKEN`.
**Start and landing (mos-1616 ruling 5).** P3 may start now on a new Codex gpt-6.1-sol thread and worktree,
building against P2's interface `{ baseUrl, key }` (P3 item 1). It lands only after P1 (row 329) and P2
(row 331) land, and after its own gate (mos-1351). It rebases on both before that gate. The run list for
every whole-file run comes from imports and goes in the builder's report before the run.
`packages/cli/tests/case1-accept.test.mjs` stays off unless Mos approves (mos-1612).