From 2958acb81e1e0515a6c88409069631b9860fc44a Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Tue, 20 Oct 2026 14:35:20 -0500 Subject: [PATCH] gosalyn: Case 2 design r3 for #416 (T2): playground inside a book folder, persona in a 0600 file with a digest var, creative instances human-only, fenced persona block, host 10.1.1.54 as mosaic (webby-0009, mos-1031, mos-1033, Q181) --- agents/gosalyn/work/case2/DESIGN.md | 247 ++++++++++++++++++++++------ 1 file changed, 196 insertions(+), 51 deletions(-) diff --git a/agents/gosalyn/work/case2/DESIGN.md b/agents/gosalyn/work/case2/DESIGN.md index 05a79cbca..60bc7d682 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 (r2) +# Case 2 design: the creative writing hub (r3) 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,57 @@ 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 r2 + +Mos, mos-1031 (Jason's answer to Q2, DECISIONS Q181). The host is now fixed (9, 10): + +- `` is 10.1.1.54, W-Melanie-OC, VM 1012. `` is `mosaic`, a system user with no sudo, no + groups and no keys. Its home is `/srv/mosaic`, mode 0700. It runs as the system unit + `mosaic-stack.service` with `ProtectHome`, so the config directory and the token slot live under + `/srv/mosaic`. ops-01's plan, section 3, Vikunja #421. +- ops-01 sets VM 1012 to 16 GiB fixed with 6 cores, which covers my 4 GB estimate. The precheck still + prints free memory. +- The B7 residual from `melanie`'s sudo is no longer Jason's to accept. Step 2b of the plan removes her + sudo and lxd. Section 10 step 1 becomes "Jason approves the tightening plan (#421)". + +Webby's review (webby-0009, CHANGES, notes at agents/webby/work/case2-r2/REVIEW.md, 162ce46db). I re-read +each cited line at 9ae8c73ac, and each one holds. + +- B1. A playground prefix now sits inside a universe entry and must lie inside one of that universe's + book folders, so a playground file has a universe and a book. A prefix outside one refuses the + business file. Playground, like canon, is `.md` only, so a `.canvas` under a playground prefix is + unmapped (1.1, 1.2). +- B2. The persona text no longer goes in the business file. It lives in a 0600 file per instance under + `/personas/`, and the business file holds only `persona.name` and `persona.sha256`. So the + tester's `instances` read, the Console's and `changes.jsonl` carry the digest, never the text. The + modes of the business file, `.history` and `changes.jsonl` are stated from code and tested in C2-08, + and the C2-13 precheck checks them and refuses `bus.tester` on her business (4). +- B3. `launch-settings.mjs:140`, `:186` and `:191` stay `THREAD_DEFINITION`, so the chief of staff's + `assistants` key never reaches Bean or Chet, with a C2-08 refusal test. `organization.set`'s + `instances` key refuses a creative instance for any actor. `instances.set` gets one branch, so the + human can add or change a creative instance without a hand edit, and the tester can't. That is how a + model change, a persona change or a later specialist happens (4, 10 step 7). This also moves C2-08 from + Haiku to Opus, because it now changes who may write the business file (M10 in 5.1). +- B4. The persona block goes between the contract and "## This session", fenced like skills, and the + bundle refuses text that holds the end marker or a level 1 or 2 heading (4). +- N1. `library_get`'s `links_out` and `links_in` drop other-universe and unmapped targets (3). +- N2. The setup sheet says moving a note into a book folder makes it canon (7.1). +- N3. M9 sits in `organizationWriter.write` (`organization.mjs:177`), compares all of `projects`, hybrid + entries included, and its test calls `write` directly (1.1, 5.1). +- N4. Obsidian resolves a shortest-path link across her whole vault, `~/src`, and the reader sees one + repo, so the two can resolve one link to different files. Stated in 1.3 and the setup sheet. +- Webby's answers to my three questions: reporting is enough for 1.3, M9 holds today, and the persona + order is B4. I take all three as written. + +Mos, mos-1033. B2 and B3 are required, and the chief of staff gets no path to rewrite Bean's or Chet's +persona, because the personas are Melanie's. With B3 above, only the human changes a persona or a +creative instance, and no organization read or history line holds persona text. C2-20 reads the same +things as in r2. Only its output changes, to the persona file and its digest (9.1). + +A correction to r2. r2 said that with `END_PROTECTED` set to `THREAD_DEFINITIONS`, only the human could +end Melanie's agents. That's wrong. An `END_PROTECTED` instance is ended by the chief of staff or by a +decision the human resolved (`broker.mjs:148-150`, Q169). Ending a run changes no persona (4). + ## Changes from r1 Jason's rulings (DECISIONS Q180, relayed in mos-1014) come first, because they set the slice: @@ -152,13 +203,13 @@ The mapping is a new form of business file project entry. Business files live in "branch": "main", "vaultPrefix": "", "universes": { - "": {"name": "", "books": ["", ""]} + "": {"name": "", "books": ["", ""], + "playground": ["//"]} }, "types": {"": "character", "": "place", "": "research", "": "tracker", "": "summary"}, - "manuscript": [""], - "playground": ["/"] + "manuscript": [""] } } ``` @@ -166,16 +217,24 @@ The mapping is a new form of business file project entry. Business files live in - Every value in `<>` comes from the survey and Melanie's say, through Jason (10 step 7). - `universes` groups her book folders. If she has one world, it is one universe holding every book folder. Jason's `universe.book.{character,place,...}` then reads as universe, book folder, type folder. -- `playground` is a list of path prefixes. Her repo has no playground today as far as I know. The setup - sheet suggests one folder per book, for example `/playground/`, or one at the top. Until she makes - one, everything mapped is canon. +- `playground` is a list of path prefixes inside each universe entry (B1). Each prefix must start with + one of that universe's book folders, and no prefix may hold another. A prefix outside its universe's + book folders refuses the business file with `library-playground-outside-book`, so every playground + file has a universe and a book, and the one-universe cite check (5.2) has something to compare. Her + repo has no playground today as far as I know. The setup sheet suggests one folder per book, for + example `/playground/`. Until she makes one, everything mapped is canon. - `types` maps a type folder name to a type. A file's front matter `type`, if present, is shown next to it but doesn't change scope. Agents can't change this mapping. No organization verb writes `projects` today: `organization.mjs` has no write of it, and `instance-settings.mjs:141` only reads `proposed.projects`. An organization write sends a whole proposed document, though, so C2-04 adds a refusal, `organization-projects-protected`, for any -organization write whose `projects` differ from the file's, with a test (M9). +organization write whose `projects` differ from the file's (M9). Every setter builds its document from +the file and changes only its own keys (`broker.mjs:1261-1271`), so the rule holds today. It goes in +`organizationWriter.write` (`organization.mjs:177`), the one place every setter's write passes +(`instance-settings.mjs:154`, `launch-settings.mjs:207`, `:277`, `plugin-settings.mjs:60`). It compares all +of `projects` in canonical JSON, hybrid entries included, and its test calls `write` directly (N3). The +mapping changes only by a hand edit of the file. ### 1.2 Scope @@ -183,12 +242,13 @@ Each path in her repo, relative to the repo root, gets one scope: | Scope | Rule | |---|---| -| `playground` | Starts with a configured playground prefix | -| `canon` | Otherwise, inside a configured book folder, and a `.md` file | -| `unmapped` | Everything else: top-level notes, `.base`, `.canvas`, attachments, anything outside a book folder | +| `playground` | A `.md` file that starts with a playground prefix of its book's universe | +| `canon` | Otherwise, a `.md` file inside a configured book folder | +| `unmapped` | Everything else: top-level notes, `.base`, `.canvas` (under a playground prefix too), attachments, anything outside a book folder | Lookups default to canon and return playground only on request. Unmapped files are never returned or -citable. The universe of a file is the universe whose `books` list holds its book folder. +citable. The universe of a file is the universe whose `books` list holds its book folder. Because every +playground prefix lies inside a book folder (1.1), that holds for playground files too. ### 1.3 Reading and reporting @@ -215,6 +275,12 @@ and Obsidian's shortest-path form, a base name that matches exactly one indexed is in another of her repos under `~/src` resolves to nothing (R4). `appears_in` and `relationships` values are read as links when they are wikilinks, and as names otherwise. +One known difference (N4). Obsidian resolves a shortest-path link across her whole vault, `~/src`, and +the reader sees only the books repo. A base name that is unique in the repo but also exists in another +of her repos can resolve to different files in Obsidian and in the hub. The reader can't detect that, +because it never sees the other repos. A link written as a full path resolves the same in both, and the +setup sheet says so (C2-12). + Why reporting is enough for separation. The guarantee Jason asked for is that agents keep playground and canon apart. The code that does that is the scope rule, the lookups' default and the cite check in 5.2, which refuses a playground cite on a `fact`, `finding` or `correction`. None of those needs her files to @@ -351,6 +417,8 @@ reads the mirror at its current served commit (7.2) and returns that commit. - Universe isolation is in code. `universe` is required, and results never leave it, except for a hybrid's declared sources minus exclusions. - Every result carries `scope` from the path (D2). Playground is opt-in. Unmapped files never appear. +- `library_get`'s `links_out` and `links_in` drop targets in another universe and unmapped targets, as + the other tools do (N1). A hybrid keeps links into its declared sources. - Replies stay under `REPLY_CAP` (`tools.mjs:271-280`) by paging. They are never cut. - The index (types, names, links and backlinks) is built per commit and cached in memory by commit id. Her repo has 203 Markdown files today. I estimate a rebuild well under a second, and C2-05a's gate @@ -374,17 +442,79 @@ All six have `authority: {withinRole: ["message.send"], crossRole: []}`, `creden `network` is `none`, except `api-only` for `companion` and `research`. Bean and Chet. Jason's ruling (Q180): "She will want to keep those agent names and personalities." They -are two business role instances, `bean` and `chet`, both with definition `companion`. Instance entries -already take agent-layer `vars` (`packages/business/src/business.mjs:89`), and every var key is declared -once in `REGISTRY` (`packages/business/src/vars.mjs:102`). C2-08 declares two keys, agent layer only: +are two business role instances, `bean` and `chet`, both with definition `companion`. The personas are +Melanie's (mos-1033), so only the human changes one, and no organization read or log holds the text. + +Where the persona lives (B2). Instance entries take inline `vars` only +(`packages/business/src/business.mjs:89`). Inline vars go wherever the business file goes. The +`instances` read returns every instance's vars whole (`packages/business/src/instance-settings.mjs:58-63`), +and the tester may call it (`TESTER_ORGANIZATION_VERBS`, `broker.mjs:103`). Every organization write logs +old and new values to `businesses/.history//changes.jsonl` (`organization.mjs:70-80`, `:204`). So the +text stays out of the business file. C2-08 declares two agent-layer keys in `REGISTRY` +(`packages/business/src/vars.mjs:102`): - `persona.name`, a string of at most 40 characters, the name she sees ("Bean", "Chet"). -- `persona.text`, a string of at most 8,000 characters, the personality prompt from Hermes. +- `persona.sha256`, 64 lowercase hex characters, the digest of the instance's persona file. -The bundle's `prompt()` (`packages/harness/src/bundle.mjs:175-177`) puts the role contract first. The -persona text goes after the contract, under a heading that says the contract wins on any conflict. That -order is a hint to the model. The enforcement is the broker guard in 5.2, which doesn't read the persona -at all, so no persona text can lift a guard. A persona var is refused on a non-creative definition. +The persona file is `/personas//.md`, outside `businesses/`. It is UTF-8, +at most 8,000 characters, mode 0600, in 0700 directories owned by ``. The bundle reads it at launch +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. + +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 ``. + +Who can change a persona or a creative instance (B3). Today's paths, 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`). + 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`). + 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. +- `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 + `CREATIVE_DEFINITIONS` instance is accepted from the human and refused from the tester with + `organization-creative-human-only`. That is how her agents' models change, how a new persona digest is + set after the file is replaced, and how a specialist is added later (10 step 7). Tests: the human adds + `ideas` and changes `bean`'s model, the tester's add and change refuse, and `instances.set` still + 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 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: + +``` + + +## Persona + +Your name here is . The persona below sets your voice and manner. The role contract above +wins on any conflict, and the broker checks every reply whatever the persona says. + + + + + +## This session +``` + +It is fenced the way skills are (`SKILL_END`, `bundle.mjs:107`, `:170`). The bundle refuses +`persona-invalid` for text that holds `` or has a line starting `# ` or `## `, so the text +can't close its fence or open a section of its own. C2-08's snapshot pins contract, persona, session, in +that order. The order is a hint to the model. The enforcement is the broker guard in 5.2, which doesn't +read the persona, so no persona text can lift a guard. What changes for her. In Hermes, Bean and Chet answer in free text and may write story text when she asks. In Mosaic they answer in short cited items, and a request for prose gets a `question` or an `idea` @@ -403,9 +533,12 @@ New constants in `vocabulary.mjs`, each changed only by a reviewed commit: - `THREAD_DEFINITIONS = ["assistant", "companion", "ideas", "research", "proofreader", "fact-checker", "continuity"]`. This replaces the single `THREAD_DEFINITION` wherever it means "may hold a thread". The definition the - chief of staff may set stays `assistant` (`tools.mjs:186`). -- `END_PROTECTED` (`vocabulary.mjs:135`) becomes `THREAD_DEFINITIONS`. These agents hold Melanie's - threads, so only the human may end them (N2). + chief of staff may set stays `assistant` (`tools.mjs:186`), and so do the three `assistants` lines in + `launch-settings.mjs` (B3 above). +- `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. - `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 @@ -426,7 +559,8 @@ New constants in `vocabulary.mjs`, each changed only by a reviewed commit: | M6 no free-text decision channel | `decision.raise` from a creative definition refuses `decision-not-allowed` in the broker. The typed tool is also absent (M3) | C2-08: a creative session's raise refuses and no decision row appears. An assistant's raise still works | | 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` | C2-04: a write that changes a playground prefix refuses and the file is unchanged | +| 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 | 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 @@ -545,7 +679,10 @@ She has it working already. `~/src` is her vault, the books repo is a folder in pushes it to git.mosaicstack.dev (mos-1000). The hub changes nothing on her machine and commits nothing to her repo. The setup sheet (C2-12) tells her which folders the agents read, that a folder named in `playground` is her playground, and how to add a graph colour group for that path in her own vault -settings if she wants one. The graph view and backlinks stay Obsidian's own. +settings if she wants one. The graph view and backlinks stay Obsidian's own. The sheet also says two +things she might not expect. Moving a note into a book folder makes it canon, so agents may cite it as +fact (N2). And a short link to a name that also exists in another of her repos can point somewhere else +for the agents than in Obsidian. A full-path link avoids that (N4, 1.3). ### 7.2 Sync (bus side) @@ -657,13 +794,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.text` in `REGISTRY`, and the persona block after the contract in `prompt()` | none | Haiku high | 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, over their caps and on a non-creative definition. A prompt snapshot with the contract before the persona | +| 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 `` or a `# `/`## ` line. A prompt snapshot pinning contract, persona, session. The M10 tests. 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, 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 `persona.name` and `persona.text` into a 0600 file 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, output holds no persona text | -| C2-13 | S | Install script and precheck for `` as ``: bootstrap, release activate, business file with the mapping, `bean` and `chet` with their persona vars, any specialists she picks, her human id, token slot path. Jason or his delegate runs it (10) | C2-04, C2-03a, C2-20 | Opus (Gosalyn), Jason's go | precheck: config validates, `` has no sudo, `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-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 | 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 `` 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, 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 | @@ -692,22 +829,25 @@ compaction count and clear context (GAP-MAP C2.12, C2.13). Until then, a new thr ## 9. Where it runs, Bean and Chet's move, and what can't be ready by 2026-10-27 -Where it runs. The host is a variable, ``, and the instance runs as its own user ``, which -has no sudo. Mos's reading, which he is confirming with Jason, is her VM 10.1.1.54 with RAM added. The -design doesn't depend on the host. The bus, data root, mirror and runners live under ``. It needs -outbound HTTPS to git.mosaicstack.dev and the model providers. It needs nothing from her user, because -her writing reaches it through the Gitea remote she already uses. +Where it runs (Jason, Q181, relayed in mos-1031). `` is her VM 10.1.1.54, W-Melanie-OC, VM 1012 +on pve. `` is `mosaic`, a system user with no sudo, no groups and no keys, home `/srv/mosaic` at +mode 0700. It runs as the system unit `mosaic-stack.service` with `ProtectHome`, so nothing it needs may +live under `/home`. Its config directory, ``, is `/srv/mosaic/.config/mosaic-dev`, and the token +slot and persona files live under it (ops-01's plan, section 3, Vikunja #421). The rest of the design +still names `` and ``, so a later move changes only the install inputs. The bus, data root, +mirror and runners live under ``. It needs outbound HTTPS to git.mosaicstack.dev and the model +providers. It needs nothing from her user, because her writing reaches it through the Gitea remote she +already uses. -Memory, from mos-1000. Her two Hermes services use about 1.8 GB together, and 1.2 GiB was free on a 3.8 -GiB VM. Each Mosaic agent session measured 300 to 390 MB on dragon-lin. With Hermes still running, -Bean and Chet in use at once, and the bus and Console, I estimate at least 2 GB more is needed, and 4 GB -more if she also talks to specialists at the same time. That is an estimate from those numbers, not a -measurement on her VM. C2-13's precheck prints free memory before the start. +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. -Isolation on that host. `` without sudo keeps the hub from touching her files. The reverse doesn't -hold: `melanie` has passwordless sudo (mos-1000), so anything that runs as her, and anyone who can ssh in -as her, can read the hub's data root and its token slot. That is the B7 residual for this host. It is -Jason's to accept (10 step 1). +Isolation on that host. `` 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 +her sudo and lxd, so it is no longer a residual to accept. It depends on Jason approving that plan +(10 step 1). ### 9.1 Moving Bean and Chet (C2-20) @@ -719,9 +859,11 @@ Jason's to accept (10 step 1). profile's own configured name, and prints the pairing for Jason before anything is written. If a profile names neither, it stops and asks. - How it reads. A script, run on `` by whoever Jason names, because the persona files sit in her - home directory and `` can't read them. It writes `persona.name` and `persona.text` to a 0600 file - that the business file entry for `bean` and `chet` takes, and prints names, byte counts and sha256 only. - No agent sees the persona text, and it never enters the repository. + home directory and `` can't read them. It writes each persona text to a 0600 staging file, and a + second step run as `` installs it as `/personas//.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. - 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 @@ -749,16 +891,18 @@ resolves to 10.1.1.222, and STATE.md lists its certificate among the edge certif reachable from the internet. Her writing is already there by her own choice. The hub adds no new remote. What it adds is one bot account with read access to that one repo. -1. The host: `` and ``. If it is her VM 10.1.1.54, the RAM to add (9) and acceptance of the - B7 residual from `melanie`'s sudo (9). +1. Jason approves the tightening plan (#421). The host is set (Q181): 10.1.1.54 as `mosaic` under + `/srv/mosaic`, 16 GiB and 6 cores. Plan step 2b, which removes `melanie`'s sudo and lxd, closes r2's + B7 residual (9). 2. Running the install on `` as ``, by him or his delegate: `scripts/bootstrap.sh`, release package and activate, the pinned harness, and which model accounts its agents use. Model use is spend, so the accounts are his call. 3. A new Gitea bot account for the hub, with read access to the books repo only. Not bean-brain, and not her other three repos. Her repo's access list is hers, so adding the bot means asking her or acting as admin, which is his call. -4. The bot's token in slot `library-gitea` under `~/.config/mosaic-dev/secrets//` of ``, - mode 0600. He puts it there, or says who may, per the Q173 secrets rule. Agents never print it. +4. The bot's token in slot `library-gitea` under `/secrets//`, which is + `/srv/mosaic/.config/mosaic-dev/secrets//` for `mosaic`, mode 0600. He puts it there, or + says who may, per the Q173 secrets rule. Agents never print it. 5. Her Console sign-in from her machine: her Authentik user and `sub` once SSO lands (#412 step 3 is the model), or his ruling on an interim. 6. The C2-20 persona import: who runs it on ``, his check of the printed profile-to-name pairing, @@ -766,7 +910,8 @@ What it adds is one bot account with read access to that one repo. 7. Running `library survey` on `` after step 4, which prints names and counts, never text. Then contacting Melanie: who tells her what changes for Bean and Chet (4), sends the setup sheet (C2-12), and asks her to confirm the mapping, the universes, the playground folders and which specialists to - start with, and when. + start with, and when. A specialist she wants later is added by the human through `instances.set` + (4), with no hand edit and no new row. 8. The residual in 5.1: up to 5 idea items of 200 characters per reply, 1,000 characters of free text within 1,200 total. He or Melanie accepts it or orders lower caps. Mos takes it to him. 9. The instance start. Stopping Hermes comes later, and only when she chooses it.