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)

This commit is contained in:
jason.woltje committed 2026-10-20 14:35:20 -05:00
1 parent c1913fb07a
commit 2958acb81e
1 file changed
+196 -51
+196 -51
View File
@@ -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`, 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). 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 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. 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):
- `<host>` is 10.1.1.54, W-Melanie-OC, VM 1012. `<user>` 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
`<configDir>/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 ## Changes from r1
Jason's rulings (DECISIONS Q180, relayed in mos-1014) come first, because they set the slice: 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", "branch": "main",
"vaultPrefix": "<repo folder name under ~/src>", "vaultPrefix": "<repo folder name under ~/src>",
"universes": { "universes": {
"<u>": {"name": "<display name>", "books": ["<book folder>", "<book folder>"]} "<u>": {"name": "<display name>", "books": ["<book folder>", "<book folder>"],
"playground": ["<book folder>/<playground folder>/"]}
}, },
"types": {"<characters folder>": "character", "<locations folder>": "place", "types": {"<characters folder>": "character", "<locations folder>": "place",
"<research folder>": "research", "<tracker folder>": "tracker", "<research folder>": "research", "<tracker folder>": "tracker",
"<chapter summaries folder>": "summary"}, "<chapter summaries folder>": "summary"},
"manuscript": ["<chapters folder>"], "manuscript": ["<chapters folder>"]
"playground": ["<prefix>/"]
} }
} }
``` ```
@@ -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). - 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 - `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. 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 - `playground` is a list of path prefixes inside each universe entry (B1). Each prefix must start with
sheet suggests one folder per book, for example `<book>/playground/`, or one at the top. Until she makes one of that universe's book folders, and no prefix may hold another. A prefix outside its universe's
one, everything mapped is canon. 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 `<book>/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 - `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. it but doesn't change scope.
Agents can't change this mapping. No organization verb writes `projects` today: `organization.mjs` has no 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 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 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 ### 1.2 Scope
@@ -183,12 +242,13 @@ Each path in her repo, relative to the repo root, gets one scope:
| Scope | Rule | | Scope | Rule |
|---|---| |---|---|
| `playground` | Starts with a configured playground prefix | | `playground` | A `.md` file that starts with a playground prefix of its book's universe |
| `canon` | Otherwise, inside a configured book folder, and a `.md` file | | `canon` | Otherwise, a `.md` file inside a configured book folder |
| `unmapped` | Everything else: top-level notes, `.base`, `.canvas`, attachments, anything outside a 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 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 ### 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` 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. 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 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, 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 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 - Universe isolation is in code. `universe` is required, and results never leave it, except for a
hybrid's declared sources minus exclusions. hybrid's declared sources minus exclusions.
- Every result carries `scope` from the path (D2). Playground is opt-in. Unmapped files never appear. - 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. - 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. - 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 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`. `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 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 are two business role instances, `bean` and `chet`, both with definition `companion`. The personas are
already take agent-layer `vars` (`packages/business/src/business.mjs:89`), and every var key is declared Melanie's (mos-1033), so only the human changes one, and no organization read or log holds the text.
once in `REGISTRY` (`packages/business/src/vars.mjs:102`). C2-08 declares two keys, agent layer only:
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/<id>/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.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 The persona file is `<configDir>/personas/<business>/<instance>.md`, outside `businesses/`. It is UTF-8,
persona text goes after the contract, under a heading that says the contract wins on any conflict. That at most 8,000 characters, mode 0600, in 0700 directories owned by `<user>`. The bundle reads it at launch
order is a hint to the model. The enforcement is the broker guard in 5.2, which doesn't read the persona with `O_NOFOLLOW`. It refuses a file that isn't regular, isn't owned by the bus's uid or has any group or
at all, so no persona text can lift a guard. A persona var is refused on a non-creative definition. 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 `<host>`.
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:
```
<contract>
## Persona
Your name here is <persona.name>. 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.
<persona-file instance="bean" sha256="<64 hex>">
<persona text>
</persona-file>
## This session
```
It is fenced the way skills are (`SKILL_END`, `bundle.mjs:107`, `:170`). The bundle refuses
`persona-invalid` for text that holds `</persona-file>` 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 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` 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"]`. - `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 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`). chief of staff may set stays `assistant` (`tools.mjs:186`), and so do the three `assistants` lines in
- `END_PROTECTED` (`vocabulary.mjs:135`) becomes `THREAD_DEFINITIONS`. These agents hold Melanie's `launch-settings.mjs` (B3 above).
threads, so only the human may end them (N2). - `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_DEFINITIONS`, the six above.
- `CREATIVE_KINDS`, mapping each definition to the item kinds in the table. - `CREATIVE_KINDS`, mapping each definition to the item kinds in the table.
- `CREATIVE_TOOLS = ["read", "grep", "find", "ls", "web"]`. `validateRoleDocument` refuses a creative - `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 | | 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 | | 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 | | 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 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 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 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 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 `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) ### 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-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-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-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 `</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-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-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-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-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 `<host>` | C2-01 | Luna | doc only, reviewed against the survey's real flags | | 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 `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-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, 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, `<user>` 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-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-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-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-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 | | 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 ## 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, `<host>`, and the instance runs as its own user `<user>`, which Where it runs (Jason, Q181, relayed in mos-1031). `<host>` is her VM 10.1.1.54, W-Melanie-OC, VM 1012
has no sudo. Mos's reading, which he is confirming with Jason, is her VM 10.1.1.54 with RAM added. The on pve. `<user>` is `mosaic`, a system user with no sudo, no groups and no keys, home `/srv/mosaic` at
design doesn't depend on the host. The bus, data root, mirror and runners live under `<user>`. It needs mode 0700. It runs as the system unit `mosaic-stack.service` with `ProtectHome`, so nothing it needs may
outbound HTTPS to git.mosaicstack.dev and the model providers. It needs nothing from her user, because live under `/home`. Its config directory, `<configDir>`, is `/srv/mosaic/.config/mosaic-dev`, and the token
her writing reaches it through the Gitea remote she already uses. slot and persona files live under it (ops-01's plan, section 3, Vikunja #421). The rest of the design
still names `<host>` and `<user>`, so a later move changes only the install inputs. The bus, data root,
mirror and runners live under `<user>`. 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 Memory. From mos-1000, her two Hermes services use about 1.8 GB together, and each Mosaic agent session
GiB VM. Each Mosaic agent session measured 300 to 390 MB on dragon-lin. With Hermes still running, measured 300 to 390 MB on dragon-lin. I estimated at least 2 GB more than the 3.8 GiB her guest saw, and
Bean and Chet in use at once, and the bus and Console, I estimate at least 2 GB more is needed, and 4 GB 4 GB more with specialists running at the same time. ops-01 is setting VM 1012 to 16 GiB fixed with 6
more if she also talks to specialists at the same time. That is an estimate from those numbers, not a cores (mos-1031), which covers both. C2-13's precheck still prints free memory before the start.
measurement on her VM. C2-13's precheck prints free memory before the start.
Isolation on that host. `<user>` without sudo keeps the hub from touching her files. The reverse doesn't Isolation on that host. `<user>` without sudo keeps the hub from touching her files. The reverse was the
hold: `melanie` has passwordless sudo (mos-1000), so anything that runs as her, and anyone who can ssh in B7 residual in r2, because `melanie` had passwordless sudo (mos-1000). Step 2b of ops-01's plan removes
as her, can read the hub's data root and its token slot. That is the B7 residual for this host. It is her sudo and lxd, so it is no longer a residual to accept. It depends on Jason approving that plan
Jason's to accept (10 step 1). (10 step 1).
### 9.1 Moving Bean and Chet (C2-20) ### 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 profile's own configured name, and prints the pairing for Jason before anything is written. If a profile
names neither, it stops and asks. names neither, it stops and asks.
- How it reads. A script, run on `<host>` by whoever Jason names, because the persona files sit in her - How it reads. A script, run on `<host>` by whoever Jason names, because the persona files sit in her
home directory and `<user>` can't read them. It writes `persona.name` and `persona.text` to a 0600 file home directory and `<user>` can't read them. It writes each persona text to a 0600 staging file, and a
that the business file entry for `bean` and `chet` takes, and prints names, byte counts and sha256 only. second step run as `<user>` installs it as `<configDir>/personas/<business>/<instance>.md` (4). It
No agent sees the persona text, and it never enters the repository. 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 - 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 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 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. 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. What it adds is one bot account with read access to that one repo.
1. The host: `<host>` and `<user>`. If it is her VM 10.1.1.54, the RAM to add (9) and acceptance of the 1. Jason approves the tightening plan (#421). The host is set (Q181): 10.1.1.54 as `mosaic` under
B7 residual from `melanie`'s sudo (9). `/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 `<host>` as `<user>`, by him or his delegate: `scripts/bootstrap.sh`, release 2. Running the install on `<host>` as `<user>`, 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, package and activate, the pinned harness, and which model accounts its agents use. Model use is spend,
so the accounts are his call. 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 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 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. admin, which is his call.
4. The bot's token in slot `library-gitea` under `~/.config/mosaic-dev/secrets/<business>/` of `<user>`, 4. The bot's token in slot `library-gitea` under `<configDir>/secrets/<business>/`, which is
mode 0600. He puts it there, or says who may, per the Q173 secrets rule. Agents never print it. `/srv/mosaic/.config/mosaic-dev/secrets/<business>/` 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 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. model), or his ruling on an interim.
6. The C2-20 persona import: who runs it on `<host>`, his check of the printed profile-to-name pairing, 6. The C2-20 persona import: who runs it on `<host>`, 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 `<host>` after step 4, which prints names and counts, never text. Then 7. Running `library survey` on `<host>` 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), 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 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 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. 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. 9. The instance start. Stopping Hermes comes later, and only when she chooses it.