gosalyn: Case 2 design r4 for #416 (T2): one human-actor rule on all five creative write paths incl. routes.set, chief's launch.models change on a creative key refuses, C2-20 count-only dry run, prompt passed by file not argv (webby-0017, mos-1038, mos-1051)
This commit is contained in:
1 parent
2958acb81e
commit
5fc296e708
1 file changed
+99
-14
@@ -1,4 +1,4 @@
|
||||
# Case 2 design: the creative writing hub (r3)
|
||||
# Case 2 design: the creative writing hub (r4)
|
||||
|
||||
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,45 @@ 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 r3
|
||||
|
||||
Webby's review (webby-0017, CHANGES, notes at agents/webby/work/case2-r3/REVIEW.md, fcac66f7b), with
|
||||
Mos's rulings (mos-1051). I re-read each cited line at 9ae8c73ac, and each one holds.
|
||||
|
||||
- B1. `routes.set` was a fourth path. The tester may call it (`broker.mjs:103`), and `setRoutes`
|
||||
(`launch-settings.mjs:240-277`) checks no definition before its write, so the tester could change a
|
||||
creative instance's harness, model and thinking. r3's "could change no creative instance" was false
|
||||
with `bus.tester` on. All creative-instance checks are now one rule: the write must come from the
|
||||
business file's `human`. Role sessions write as `<definition>.<instance>` (`broker.mjs:1278`) and the
|
||||
tester as `tester` (`:1272`), so neither passes. The path list in 4 names all five paths, and M10 has a
|
||||
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
|
||||
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
|
||||
persona, the rule changes before the import. Any rewrite of her text needs Melanie's say (9.1).
|
||||
- N3 (mos-1051 item 4). The persona text sits in two more places, and 4 names both. `prompt.md` is
|
||||
written 0600 into each run's bundle and kept until prune (`bundle.mjs:241-244`, `:265`). Both adapters
|
||||
pass it as an argument (`adapters/claude/adapter.sh:79`, `:82`, `adapters/pi/adapter.sh:106`, `:121`),
|
||||
so any local user on the host can read it in `/proc/<pid>/cmdline`. The adapters now pass the file,
|
||||
not its text. Pi 0.85.1 reads `--system-prompt` as a file when the value is an existing path. Claude's
|
||||
help names `--system-prompt-file`. C2-08 does this, and C2-13 checks the run root's mode.
|
||||
- Webby's answers. Keying on the actor is enough, and C2-08 runs the tester cases through the broker
|
||||
with a bound tester session, so a change at `broker.mjs:1272` fails a test. The heading refusal fails
|
||||
closed, and N2 covers its risk.
|
||||
|
||||
Mos, mos-1038 items 2 and 3. The chief of staff keeps ending runs, which matches TARGET.md:66, and a
|
||||
per-agent forbid comes through Jason (4). ops-01 makes the 16 GiB, 6-core change at
|
||||
2026-10-21T09:00Z (9).
|
||||
|
||||
Section 10 step 1 is on hold (mos-1050). Jason isn't pursuing #421 and accepts the risk, and Mos is
|
||||
confirming that reading with him. The step keeps its r3 wording until he does. If he confirms, step 1
|
||||
becomes "Jason's written acceptance of the factory paths to .54 is recorded", and section 9's isolation
|
||||
paragraph changes with it.
|
||||
|
||||
## Changes from r2
|
||||
|
||||
Mos, mos-1031 (Jason's answer to Q2, DECISIONS Q181). The host is now fixed (9, 10):
|
||||
@@ -461,17 +500,37 @@ at most 8,000 characters, mode 0600, in 0700 directories owned by `<user>`. The
|
||||
with `O_NOFOLLOW`. It refuses a file that isn't regular, isn't owned by the bus's uid or has any group or
|
||||
other bit, the same check `organization.mjs:141` makes on `changes.jsonl`. It refuses `persona-mismatch`
|
||||
when the file's sha256 isn't the var's. So the `instances` read, the Console's view of it and
|
||||
`changes.jsonl` show `persona.sha256` and never the text. The persona's own session sees the text in its
|
||||
prompt, which is the point. No verb returns the file, and agent file tools stay inside the workspace
|
||||
(`packages/harness/src/gate.mjs:146-156`), so no other session can read it. A persona var or file is
|
||||
refused on a non-creative definition.
|
||||
`changes.jsonl` show `persona.sha256` and never the text. No verb returns the file, and agent file tools
|
||||
stay inside the workspace (`packages/harness/src/gate.mjs:146-156`). A persona var or file is refused on
|
||||
a non-creative definition.
|
||||
|
||||
Where else the text sits (N3). The persona's own session gets it in its prompt, which is the point, and
|
||||
two copies come with that:
|
||||
|
||||
- `prompt.md` in each run's bundle, mode 0600 in a 0700 directory (`bundle.mjs:241-244`, `:265`), kept
|
||||
until the run is pruned. Only `<user>` can read it. The C2-13 precheck checks the run root's mode.
|
||||
- The harness's argv. Both adapters read `prompt.md` into a variable and pass the text as
|
||||
`--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/<pid>/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
|
||||
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
|
||||
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
|
||||
(`organization.mjs:61`, `:139`) and its `.history` directories with 0700 (`:39`). It refuses a log with any
|
||||
group or other bit (`:141`). C2-08 tests those modes after an `instances.set` write, and the C2-13
|
||||
precheck checks the business file, `.history` and the persona files on `<host>`.
|
||||
|
||||
Who can change a persona or a creative instance (B3). Today's paths, and what C2-08 does to each:
|
||||
Who can change a persona or a creative instance (B3, and r3's B1). The rule is one check, used on every
|
||||
path below: a write that touches a `CREATIVE_DEFINITIONS` instance is accepted only when its actor is
|
||||
the business file's `human`. The broker sets the actor to the human's id for a human session, to
|
||||
`tester` for the tester (`broker.mjs:1272`), and to `<definition>.<instance>` for a role session such as
|
||||
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`),
|
||||
targets only definition `assistant` (`packages/business/src/launch-settings.mjs:140`, `:186`, `:191`).
|
||||
@@ -480,6 +539,19 @@ Who can change a persona or a creative instance (B3). Today's paths, and what C2
|
||||
- `organization.set`'s `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
|
||||
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.
|
||||
- `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.
|
||||
Test: the tester's `routes.set` on `bean` refuses with the file unchanged, and the human's works.
|
||||
- `instances.set` is human only, and the tester may call it too (`broker.mjs:103`, `:1263-1265`). Today it
|
||||
refuses any `END_PROTECTED` definition (`instance-settings.mjs:23`, `:109`), which would make every
|
||||
creative add or change a hand edit. C2-08 adds one branch. An add or change of a
|
||||
@@ -490,7 +562,12 @@ Who can change a persona or a creative instance (B3). Today's paths, and what C2
|
||||
refuses an `assistant` instance.
|
||||
- The tester is off on her business. `bindTester` refuses unless `bus.tester` is exactly true
|
||||
(`broker.mjs:542-545`), and the C2-13 precheck refuses a library business with it on. If it were on,
|
||||
the tester would see digests only and could change no creative instance.
|
||||
the tester would see digests only, and each of the paths above would refuse it.
|
||||
|
||||
C2-08 runs every tester case through the broker with a bound tester session, and every chief of staff
|
||||
case with a role session, so a change to how the broker sets the actor (`:1272`, `:1278`) fails a test.
|
||||
The human id can't be `tester` (`broker.mjs:245`), and no instance may be named `tester`
|
||||
(`packages/business/src/instance-settings.mjs:86`).
|
||||
|
||||
The persona block (B4). `prompt()` puts the contract first (`packages/harness/src/bundle.mjs:177`), then
|
||||
"## This session" (`:179`) and the session lines. The persona block goes between the two:
|
||||
@@ -538,7 +615,9 @@ New constants in `vocabulary.mjs`, each changed only by a reviewed commit:
|
||||
- `END_PROTECTED` (`vocabulary.mjs:135`) becomes `THREAD_DEFINITIONS` (N2). An instance of one is then
|
||||
ended only by the chief of staff or a decision the human resolved (`broker.mjs:148-150`, Q169), not by
|
||||
another agent. r2 said only the human, which was wrong. Ending a run changes no persona, and a thread's
|
||||
session stops after 900 idle seconds anyway.
|
||||
session stops after 900 idle seconds anyway. This matches TARGET.md:66 ("hire/fire authority for agents
|
||||
as needed; unless user forbids per agent"). If Melanie wants it forbidden for an agent, the request
|
||||
comes through Jason (mos-1038).
|
||||
- `CREATIVE_DEFINITIONS`, the six above.
|
||||
- `CREATIVE_KINDS`, mapping each definition to the item kinds in the table.
|
||||
- `CREATIVE_TOOLS = ["read", "grep", "find", "ls", "web"]`. `validateRoleDocument` refuses a creative
|
||||
@@ -560,7 +639,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 | The chief of staff's `assistants` and `instances` keys on a creative instance, and the tester's `instances.set` on one. A persona file that fails its digest, mode or fence check | C2-08: each path refuses with its code (4), and the file is unchanged. No `instances` read or `changes.jsonl` line 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, 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 |
|
||||
|
||||
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
|
||||
@@ -794,13 +873,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 and the `instances.set` creative branch (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 `</persona-file>` or a `# `/`## ` line. A prompt snapshot pinning contract, persona, session. The M10 tests. 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 `</persona-file>` 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-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 `<host>` | 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 | 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 |
|
||||
| C2-13 | S | Install script and precheck for `<host>` as `<user>`: 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, `<user>` 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, 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-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 `<host>` as `<user>`: 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, `<user>` 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-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 |
|
||||
@@ -842,7 +921,7 @@ already uses.
|
||||
Memory. From mos-1000, her two Hermes services use about 1.8 GB together, and each Mosaic agent session
|
||||
measured 300 to 390 MB on dragon-lin. I estimated at least 2 GB more than the 3.8 GiB her guest saw, and
|
||||
4 GB more with specialists running at the same time. ops-01 is setting VM 1012 to 16 GiB fixed with 6
|
||||
cores (mos-1031), which covers both. C2-13's precheck still prints free memory before the start.
|
||||
cores at 2026-10-21T09:00Z (mos-1031, mos-1038), which covers both. C2-13's precheck still prints free memory before the start.
|
||||
|
||||
Isolation on that host. `<user>` without sudo keeps the hub from touching her files. The reverse was the
|
||||
B7 residual in r2, because `melanie` had passwordless sudo (mos-1000). Step 2b of ops-01's plan removes
|
||||
@@ -863,7 +942,13 @@ her sudo and lxd, so it is no longer a residual to accept. It depends on Jason a
|
||||
second step run as `<user>` installs it as `<configDir>/personas/<business>/<instance>.md` (4). It
|
||||
prints `persona.name` and `persona.sha256` for the business file entries of `bean` and `chet`, and
|
||||
otherwise names, byte counts and sha256 only. The text never enters the repository, the business file
|
||||
or `changes.jsonl`, and only Bean's or Chet's own session sees it.
|
||||
or `changes.jsonl`. Outside the persona file, it sits only in Bean's or Chet's own run bundles, 0600
|
||||
until prune, and in the harness argv if the host's claude lacks `--system-prompt-file` (4).
|
||||
- Dry run first (N2, mos-1051 item 3). Before Jason's go to import, the row runs a dry pass that reads the
|
||||
same two persona definitions and prints counts only: files, lines, and lines starting `# ` or `## `.
|
||||
No text and no line numbers. If any count of heading lines is nonzero, the fence rule in 4 would refuse
|
||||
a real persona, so the rule changes, in a reviewed C2-08 follow-up, before the import runs. The text is
|
||||
never rewritten to fit the rule unless Melanie says so through Jason.
|
||||
- Hermes keeps running. The row changes nothing in the Hermes checkout, its profiles, its services or its
|
||||
Discord bots. Bean and Chet in Mosaic are new agents with the same persona, not a move of the Hermes
|
||||
ones. She uses both until she is happy, and stopping Hermes is her choice through Jason, after Bean and
|
||||
|
||||
Reference in new issue
Block a user