{"body":"## Filbert review, row 165 round 1: approve\n\nCandidate manifest `89ca3fcf04f8c84d75710b15ef1d3b6b1e4cb0a4c0353b5360c93a51bd40f5b3` (24 paths). I applied `build.patch` with `--index` to `70c9131f0`. All 24 manifest lines matched, `git diff --cached --check` is clean, and the diff is +1319 -18 over 24 files. I read it against brief blob `b3cf580a`, request 29034 and context 29035.\n\n### mos-0826 condition 2, the server-side writer\n\nEach item holds in the writer, not only in the Console or the CLI:\n\n- **Credential refusal.** `instance-settings.mjs:25-27` refuses an added instance, or a change to a definition, that declares credentials, with `organization-credentials-out-of-scope`. `:104-107` refuses a definition change away from one that declares them. Both throw before `writer.write`, so the bytes stay. A vars or launch change on an instance that holds its binding passes and leaves `credentials`, `tracker` and `holder` as they were. Tests: `packages/business/tests/instance-settings.test.mjs:132`, `:139` and `:146`.\n- **Stale revision.** `set()` passes the revision to the organization writer, which checks it under the lock (`organization.mjs:185` and `:212`). The broker passes `revision-conflict` through (`broker.mjs:1184`). Tests: business `:178`, CLI `:101`, bus runtime `instances-verb.test.mjs:56`.\n- **Human proof.** `broker.mjs:1164` refuses `instances` and `instances.set` with `human-required` for any session without `s.human`, and the actor is `s.human`, never an argument. The chief of staff's `organization.set` takes `ORGANIZATION_KEYS` only, so instance keys get `invalid-request` before the writer. Test: `instances-verb.test.mjs:24` (reader, tester, launch and role sessions, the chief, and forged actor, holder and credentials keys).\n- **Assistant protection.** An END_PROTECTED definition is refused for add and for a change to it, and any change to a protected instance is refused, with `organization-definition-refused`. Test: business `:159`.\n- **Name bounds.** Instance and definition must match `/^[a-z0-9][a-z0-9._-]{0,63}$/` before anything else is read. Test: business `:107`.\n- **503 outcome-unknown.** `serve.mjs:308-311`: only a refusal with `sent === false` maps to its status with \"Nothing changed.\"; anything else is 503 `outcome-unknown` with `sent: \"unknown\"`. `:313-315`: a 200 needs a 64-hex revision, a boolean `restart`, object `definitions` and `instances`, and `instances[instance]`, or it is 503 too. Test: `packages/webui/tests/instances.test.mjs:132` (missing `restart`, missing and array `definitions`, and a lost answer).\n\nThe writer also runs `validateBusinessDocument`, the unknown-model check and each project file's `loadProject` and `resolveInstance` before it writes, as `mosaic business validate` does (business `:107` and `:168`). It logs before and after with the human as actor (business `:59` and `:83`).\n\n### The rest\n\n- **Broker.** `INSTANCE_KEYS` is strict (`broker.mjs:138`, `:1170`). Errors pass through as code only, BusinessError and `unknown-model` become `invalid-request`, and anything else is `outcome-unknown`.\n- **Runtime.** `instances` routes to `read` and `instances.set` to `set` (`runtime.mjs:99-100`).\n- **CLI.** Usage errors exit 4 before any bus call, an agent session exits 3, refusals exit 2 with the code and a reason, and `scripts/mosaic` sends only `business instances` and `business instance` to `packages/cli` (CLI tests `:61` to `:145`).\n- **Console.** POST `/api/instances` exists only under `--send` with the writer bound. It checks the method (405), the user header (401), the exact Origin and `Sec-Fetch-Site` (403), a JSON object of at most 16 KiB, and strict keys and types (400), all before the bus. The log line names the instance and the outcome, never the vars. `public/bus.js` escapes through `txt`/`esc`, doesn't offer credentialed definitions, and checks the answer's whole shape. Tests: webui `:42` (real broker), `:101`, `:119`, `:132`, `:151`, plus the browser test. The harness `tools.test` shows no tool sends `instances`.\n- **Decision 123 diff.** In the applied tree, `git diff --cached` of `packages/webui/src/public/` hashes to `4181b7f6...a7f7`. That is byte-identical to `public-70c9131f0-warn.diff`, and its `+`/`-` lines (without the `+++`/`---` headers) hash to `939ecd28f80bc2f4a36a9468be0ccc2b0f08c8a630508060572491357f1f9444`. Those are the digests sage-0948 names.\n- **Hash pins.** The only 64-hex pin in `packages/*/tests` or `scripts/test-*.sh` that names a non-test manifest path is `channel-proof.test.mjs:14` on `packages/webui/src/bus.mjs`. It matches the manifest's `bus.mjs` digest, and the bump is its own commented hunk.\n- **Docs.** `docs/TOOLS.md`, `packages/cli/README.md`, `packages/webui/README.md`, `packages/bus/README.md`, `packages/business/README.md` and `docs/acceptance/314.md` match the code: the codes, the exit codes, the 16 KiB bound, the status mapping, and the restart that needs Mos's go or Jason's hand and that no command runs.\n\n### Notes (not blocking)\n\n1. The writer's credential message names the services (`instance-settings.mjs:27`), but the broker's `fail(e.code)` (`broker.mjs:1185`) drops it, so the human sees the generic reason from the CLI and Console tables. That still says why, as the brief asks. The services are on the read (`instances` lists the credentialed definitions), so I don't count it as a gap.\n2. The reason for `organization-definition-refused` in `packages/cli/src/instances.mjs:19` (\"an assistant instance changes only through `mosaic limits set`...\") and `packages/webui/src/serve.mjs:282` (\"an assistant instance changes only in the Assistant section\") also covers adding an instance of an assistant definition, or changing a non-assistant instance to one. In those cases the text names the wrong subject. The CLI README table says \"an assistant definition or instance\", which fits. Wording only. A follow-up could say \"an assistant definition or instance\" in both tables.\n3. The webui route test has no explicit `revision-conflict` case through `/api/instances`. The 409 mapping is the shared `busStatus`, and business, CLI and runtime tests cover the conflict itself.\n\n### Ran by me\n\nNothing that needed the slot. I applied the patch, ran `sha256sum -c` (24/24), hashed the public diff and its `+`/`-` lines, and ran greps.\n\n### Not mine\n\nDarkwing's runs as BUILD.md records them: singles run 2 (pass 1419, fail 0, with `test-config.sh` 24, `test-queue.sh` 28 and `test-task.sh` 98), and mutants run 2 (29 of 29 killed). The W1 test case in `webui/tests/instances.test.mjs` came after singles run 2. That file passed singly afterwards (5 pass, 0 fail), and it is in the candidate I checked. I did not rerun any of it. The full gate on the candidate as it stands is Sage's at landing.\n"}