From e1635308bed2415d5a6d185a45f9671d4f14747b Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 20 Oct 2026 14:53:29 -0500 Subject: [PATCH] gosalyn: Case 2 design r5 for #416 (T2): limits.set named as the human's path to launch.models with an actor-not-verb test, pi missing-file fallback covered by [ -r ], marker run of --system-prompt-file at C2-13 (webby-0020) --- agents/gosalyn/work/case2/DESIGN.md | 63 ++++++++++++++++++++--------- 1 file changed, 43 insertions(+), 20 deletions(-) diff --git a/agents/gosalyn/work/case2/DESIGN.md b/agents/gosalyn/work/case2/DESIGN.md index 7c62c40cb..fd5df75ce 100644 --- a/agents/gosalyn/work/case2/DESIGN.md +++ b/agents/gosalyn/work/case2/DESIGN.md @@ -1,4 +1,4 @@ -# Case 2 design: the creative writing hub (r4) +# Case 2 design: the creative writing hub (r5) Gosalyn, 2026-10-20 (from `date -u`). Track T2 in jason-master-restart `projects/mosaic-mvp/PLAN.md`, Vikunja #416, DECISIONS Q176-Q178. The requirement is Jason's text in TARGET.md (addendum 2026-10-20). @@ -8,6 +8,25 @@ Base: `refactor` at 9ae8c73ac. r1 cited 5d019d876. `git diff --stat 5d019d876 9a adapters roles scripts` is empty (Mos in mos-0980, Webby in webby-0001), so every `file:line` holds at 9ae8c73ac. Paths are relative to the repository root. +## Changes from r4 + +Webby's review (webby-0020, CHANGES, notes at agents/webby/work/case2-r4/REVIEW.md, 0597cc198). I +re-read each cited line at 9ae8c73ac, and each one holds. + +- B1. r4 said no verb writes `launch.models` for the human, and that Jason edits it by hand. That was + wrong, and I told Mos the same wrong thing in gosalyn-0014. `limits.set` is a human verb + (`broker.mjs:1248`) with the same `ORGANIZATION_KEYS`, `models` included (`:145`). `organization.set` + is `limits.set` for a role (`:1249-1251`), and both reach the same `set()` (`:1284`, + `launch-settings.mjs:150-169`). The actor rule was already right, because it keys on the actor and + not the verb. Section 4 now names both verbs on the `assistants`, `instances` and `models` paths. M10 + and C2-08 add the case that shows the check keys on the actor: the human's `limits.set` change to + Bean's key works, next to the chief of staff's `organization.set` refusal of the same change. +- N1. Pi also uses the path as the prompt when the file doesn't exist (`existsSync` false, + `resource-loader.js:21`, `:30`), with no warning. The adapter's check is `[ -r ]`, which covers both + cases, and C2-08 tests a missing file as well as an unreadable one. +- N2. C2-13 runs one marker prompt through the host's claude with `--system-prompt-file` and checks + that the default prompt is replaced, not appended to. It doesn't rely on `--help` alone. + ## Changes from r3 Webby's review (webby-0017, CHANGES, notes at agents/webby/work/case2-r3/REVIEW.md, fcac66f7b), with @@ -22,8 +41,8 @@ Mos's rulings (mos-1051). I re-read each cited line at 9ae8c73ac, and each one h test for each (4, 5.1). - N1, ruled refuse (mos-1051 item 2). `organization.set`'s `models` key (`launch-settings.mjs:169`) could change the model behind a `launch.models` key that Bean or Chet names. A change or removal of - such a key now refuses, with a test and a mutant. `organization.set` is agent-only, so the human never - reaches that path, and her model changes go through `instances.set` and `routes.set`. Ending a run stays with + such a key now refuses, with a test and a mutant. r4 said the human never reaches this write. She + does, through `limits.set`, and r5 corrects it. Ending a run stays with the chief of staff (mos-1038 item 2). - N2 (mos-1051 item 3). Before Jason's go, C2-20 makes a dry run over her persona files that reports only counts: files, lines, and lines starting `# ` or `## `. If the heading rule would refuse a real @@ -513,11 +532,12 @@ two copies come with that: `--system-prompt` (`adapters/claude/adapter.sh:79`, `:82`, `adapters/pi/adapter.sh:106`, `:121`). Any local user on the host can read that in `/proc//cmdline`. C2-08 makes both adapters pass the file instead. Pi 0.85.1, the pinned version (`package.json:8`), reads `--system-prompt` as a file when - the value is an existing path (`dist/core/resource-loader.js:17-30`). If the read fails it falls back - to using the path itself as the prompt (`:24-27`), so the adapter checks the file is readable first - and refuses otherwise. Claude Code 2.1.304's `--help` names `--system-prompt-file`. I read the help + the value is an existing path (`dist/core/resource-loader.js:17-30`). It uses the path string itself + as the prompt when the file doesn't exist (`:21`, `:30`, no warning) or can't be read (`:24-27`). So + the adapter checks `[ -r ]` on the file first, which covers both, and refuses otherwise. Claude Code 2.1.304's `--help` names `--system-prompt-file`. I read the help but didn't run it, and the repo doesn't pin the claude version. C2-08's adapter tests check that argv - holds the path and not a marker from the prompt, and C2-13 checks the flag on the host's claude. If the + holds the path and not a marker from the prompt. C2-13 runs one marker prompt through the host's claude + with `--system-prompt-file` and checks that it replaces the default prompt rather than adding to it. If the host's claude lacks the flag, the argv copy is a residual for Jason, stated with that version. Modes, from code. The writer creates the business file's candidate and `changes.jsonl` with mode 0600 @@ -532,22 +552,25 @@ the business file's `human`. The broker sets the actor to the human's id for a h the chief of staff (`:1278`). Otherwise it refuses `organization-creative-human-only`. The five paths today, and what C2-08 does to each: -- `organization.set`'s `assistants` key, which the chief of staff holds (`broker.mjs:145`, `:1266-1271`), +`limits.set` is the human's verb and `organization.set` is the same write for a role (`broker.mjs:1248-1251`). +Both take `ORGANIZATION_KEYS` (`:145`) and reach the same `set()` (`:1284`, `launch-settings.mjs:150-169`), +so the first three paths below are reached by both verbs, and the check keys on the actor, never the verb. + +- The `assistants` key, which the chief of staff holds (`broker.mjs:145`, `:1266-1271`), targets only definition `assistant` (`packages/business/src/launch-settings.mjs:140`, `:186`, `:191`). Those three lines stay `THREAD_DEFINITION`, not `THREAD_DEFINITIONS`. Test: `assistants: {bean: ...}` from the chief of staff refuses `organization-not-assistant`, and the file is unchanged. -- `organization.set`'s `instances` key sets the `model` var of any instance (`launch-settings.mjs:170-177`). +- The `instances` key sets the `model` var of any instance (`launch-settings.mjs:170-177`). It now refuses a creative instance with `organization-creative-instance`, for every actor. Test: the - chief of staff's and the human's `instances: {bean: ...}` both refuse. -- `organization.set`'s `models` key replaces `launch.models` whole (`launch-settings.mjs:169`), which can + chief of staff's `organization.set` and the human's `limits.set` with `instances: {bean: ...}` both + refuse. She changes Bean's model with `instances.set` or `routes.set`. +- The `models` key replaces `launch.models` whole (`launch-settings.mjs:169`), which can change the model behind a key Bean or Chet names without touching the instance. A write that changes or removes a `launch.models` entry named by a creative instance's `model` or `fallback.model` now - refuses `organization-creative-human-only` (N1, mos-1051 item 2). `organization.set` takes only an - agent session. Its authority check (`broker.mjs:1277`) refuses a human with `agent-required` (`:370`), so no verb writes - `launch.models` for the human today. She changes Bean's model with `instances.set` or `routes.set`, - pointing at a key she owns, and Jason edits `launch.models` itself by hand. Test: the chief of staff's - change to Bean's key refuses with the file unchanged, and its change to an unnamed key works. Mutant: - dropping the check makes the first case fail. + refuses `organization-creative-human-only` unless the actor is the human (N1, mos-1051 item 2). Test: + the chief of staff's `organization.set` change to Bean's key refuses with the file unchanged, its change + to an unnamed key works, and the human's `limits.set` change to Bean's key works. The last case shows + the check keys on the actor, not the verb. Mutant: dropping the check makes the first case fail. - `routes.set` replaces an instance's harness, model and thinking vars (`launch-settings.mjs:240-277`). It is human only, but the tester may call it too (`broker.mjs:103`, `:1258`). `setRoutes` checks no definition before its write at `:277`. It now refuses a creative instance unless the human calls it. @@ -639,7 +662,7 @@ New constants in `vocabulary.mjs`, each changed only by a reviewed commit: | M7 no raw session view | On a business with a library project, the conversation reader leaves creative sessions out of `catalogue()`, and `open()` refuses them with `conversation-not-listed` | C2-10a: a creative session file under the root isn't listed and won't open by id. An assistant session still lists | | M8 no raw Discord engine | On a business with a library project, `discord check` and `discord run` refuse a binding whose engine isn't `mosaic-bus` (`discord-engine-not-allowed`), and a `mosaic-bus` binding with `tools` | C2-10a: both refuse on a library business. A pi binding on a business without a library still checks clean | | M9 no agent write to the mapping | An organization write whose `projects` differ from the file's refuses `organization-projects-protected`, in `organizationWriter.write` | C2-04: `write` called directly with a changed playground prefix, and again with a changed hybrid entry, refuses, and the file and `changes.jsonl` are unchanged | -| M10 personas and creative instances are the human's | Any write by an actor other than the business file's `human` that touches a creative instance: the chief of staff's `assistants`, `instances` and `models` keys, and the tester's `instances.set` and `routes.set`. A persona file that fails its digest, mode or fence check. A harness argv that holds the prompt text | C2-08: each path refuses with its code (4) and the file is unchanged. The human's `instances.set` and `routes.set` on `bean` work. Tester cases through a bound tester session. A mutant without the `models` check fails. No `instances` read, `changes.jsonl` line or adapter argv holds a marker string from a fixture persona | +| M10 personas and creative instances are the human's | Any write by an actor other than the business file's `human` that touches a creative instance: the chief of staff's `assistants`, `instances` and `models` keys through `organization.set`, and the tester's `instances.set` and `routes.set`. A persona file that fails its digest, mode or fence check. A harness argv that holds the prompt text | C2-08: each path refuses with its code (4) and the file is unchanged. The human's `instances.set` and `routes.set` on `bean` work, and so does her `limits.set` change to Bean's `launch.models` key. Tester cases through a bound tester session. A mutant without the `models` check fails. No `instances` read, `changes.jsonl` line or adapter argv holds a marker string from a fixture persona | Residual, in numbers. One reply can carry up to 5 `idea` items of 200 characters, which is 1,000 characters of free suggestion text inside a total of 1,200 characters of item text. Each item must quote @@ -873,13 +896,13 @@ Chains follow Q177 merge-chaining. S marks a slice row. | C2-05b | S | Broker lookup verbs and typed tools. Sync before a read older than 60 s | C2-05a, C2-03a | Sol (Rocko) | broker verb tests. Typed tool name snapshot per creative role (M3) | | C2-06 | S | Cited-reply guard (5.2) in `packages/library`, the broker's pre-check on agent-to-human `message.send`, the page store, the three notices | C2-05a | Opus | one pass and one refusal per check in M5 and 5.2, through the RESULT path and `send_message`, and with a non-creative sender. `messages` count unchanged on each refusal | | C2-07 | S | Runner retry on a guard refusal (at most 2), then `no-cited-answer`. `no-answer` and `turn-failed` in a library business | C2-06 | Haiku high | fake harness: free text then a valid reply stores one message. An empty and a failed turn store the fixed notices | -| C2-08 | S | Six role files and contracts, the new constants, M1, M6, the `webPlan` refusal, gate tests (M2), messaging test. `persona.name` and `persona.sha256` in `REGISTRY`, the persona file read and checks, and the fenced persona block between the contract and "## This session" in `prompt()`. M10: the `organization.set` refusals, the `models` check, the `routes.set` and `instances.set` creative branches (4). Both adapters pass the prompt as a file, not its text (4) | none | Opus | role tests, gate tests, a creative raise refused, `harness-builtin` refused for `research` and `companion`, `scripts/test-config.sh`. Persona vars refused at other layers, malformed and on a non-creative definition. The persona file refused when missing, a symlink, another uid's, group or other readable, over 8,000 characters, off its digest, holding `` or a `# `/`## ` line. A prompt snapshot pinning contract, persona, session. The M10 tests, tester cases through a bound tester session, the `models` mutant. Adapter tests: argv holds the prompt path and no marker from the prompt, and pi's adapter refuses an unreadable prompt file. Business file, `.history` and `changes.jsonl` modes after a write | +| C2-08 | S | Six role files and contracts, the new constants, M1, M6, the `webPlan` refusal, gate tests (M2), messaging test. `persona.name` and `persona.sha256` in `REGISTRY`, the persona file read and checks, and the fenced persona block between the contract and "## This session" in `prompt()`. M10: the `organization.set` refusals, the `models` check, the `routes.set` and `instances.set` creative branches (4). Both adapters pass the prompt as a file, not its text (4) | none | Opus | role tests, gate tests, a creative raise refused, `harness-builtin` refused for `research` and `companion`, `scripts/test-config.sh`. Persona vars refused at other layers, malformed and on a non-creative definition. The persona file refused when missing, a symlink, another uid's, group or other readable, over 8,000 characters, off its digest, holding `` or a `# `/`## ` line. A prompt snapshot pinning contract, persona, session. The M10 tests, tester cases through a bound tester session, the `models` mutant. Adapter tests: argv holds the prompt path and no marker from the prompt, and pi's adapter refuses a missing and an unreadable prompt file. M10's actor case: the human's `limits.set` change to Bean's `launch.models` key works next to the chief's refusal. Business file, `.history` and `changes.jsonl` modes after a write | | C2-09a | S | `#threaded` and launcher read `THREAD_DEFINITIONS`. `thread.create` takes `instance`. The Console new-thread control picks it | C2-08 | Sol (Rocko) | the C2-09a tests in section 6 | | C2-10a | S | M7 in the conversation reader. M8 in `discord check` and `discord run` | C2-08 | Haiku high | the M7 reader test, the M8 tests | | C2-10b | S | Console chat: cited-item renderer through `renderItems`, the status line | C2-06, C2-09a, C2-03a | Haiku high | webui tests for items, scope badges, notices, "changed since" and the status line | | C2-12 | S | The setup sheet for Melanie (7.1, including N2 and N4, and what changes for Bean and Chet in 4) and the survey run sheet for `` | C2-01 | Luna | doc only, reviewed against the survey's real flags | | C2-20 | S | Persona import (9.1): a script that reads only the persona definitions of the two Hermes profiles, confirms profile to name, and writes each persona file (4) with its `persona.name` and `persona.sha256` for the business file. Prints names, byte counts and sha256 only. A `--dry-run` mode, run before Jason's go, prints only counts of files, lines and lines starting `# ` or `## ` | C2-08 | Opus (Gosalyn), run under Jason's go | fixture Hermes profile trees: the right fields are taken, memory and session files are never opened (a read trace on the fixture), a profile with no persona field refuses, a persona that would fail C2-08's fence check refuses, output holds no persona text, each written file is 0600 and its digest matches, `--dry-run` writes nothing and prints the right counts for a fixture with two heading lines and no marker string from it | -| C2-13 | S | Install script and precheck for `` as ``: bootstrap, release activate, business file with the mapping, `bean` and `chet` with their persona vars and files, any specialists she picks, her human id, token slot path, the `mosaic-stack.service` unit. Jason or his delegate runs it (10) | C2-04, C2-03a, C2-20 | Opus (Gosalyn), Jason's go | precheck: config validates, `` has no sudo and no groups, `bus.tester` is not on, the business file, `.history`, `changes.jsonl` and persona files have the modes in 4 and each persona digest matches, the run root is 0700, the host's claude accepts `--system-prompt-file` (its version printed), free memory printed, `library.sync` fetches her repo read-only, `library.status` lists R-problems, no Discord binding exists. Tested against a scratch data root and a local bare repo | +| C2-13 | S | Install script and precheck for `` as ``: bootstrap, release activate, business file with the mapping, `bean` and `chet` with their persona vars and files, any specialists she picks, her human id, token slot path, the `mosaic-stack.service` unit. Jason or his delegate runs it (10) | C2-04, C2-03a, C2-20 | Opus (Gosalyn), Jason's go | precheck: config validates, `` has no sudo and no groups, `bus.tester` is not on, the business file, `.history`, `changes.jsonl` and persona files have the modes in 4 and each persona digest matches, the run root is 0700, one marker prompt run through the host's claude with `--system-prompt-file` shows it replaces the default prompt (its version printed), free memory printed, `library.sync` fetches her repo read-only, `library.status` lists R-problems, no Discord binding exists. Tested against a scratch data root and a local bare repo | | C2-14 | S | End-to-end acceptance on a scratch data root and a bare repo in her layout: sync, ask a creative agent in its thread, a reply whose cites verify, a playground cite as canon refused, a force-push held as diverged, the Console shows the reply as items | slice rows | Opus | one script, which the reviewer also runs | | C2-02 | | Bus writes: `writeLibraryFile`, `commitFile` refactor, r1's S0 rule on the working tree | C2-04 | Sol (Rocko) | writer tests, `packages/project/tests` unchanged and green | | C2-03b | | Push path, race retry, `unpushed`, `library-remote-invalid` on writes | C2-02, C2-03a | Opus | push tests against a bare repo |