plans: Case 2 v1 briefs for C2-09b rooms, C2-10c Console room view and C2-15 hybrid universes, Webby-approved round d2 (Gosalyn 72ceda935 and 99b1e0859 on gosalyn/case2-v1-briefs, webby-0089, mos-1233, Q211, Vikunja 464); codes room-not-open, addressee-ambiguous and R8 accepted by Sage (sage-2218)

This commit is contained in:
jason.woltje committed 2026-10-21 17:36:25 -05:00
1 parent 3dcd3651ed
commit aa772d731d
1 file changed
+253
+253
View File
@@ -1165,6 +1165,259 @@ Dewey runs the script and approves. It is green on Sage's gate with every `scrip
---
## v1 rows past the slice: rooms and hybrid universes (mos-1233)
Gosalyn, 2026-10-21 (from `date -u`), for mos-1233 (Vikunja 464). Mos reads Q211 to put rooms with @tags
(TARGET.md:99) and hybrid universes (TARGET.md:82) in v1. Design 9.2's "Not ready by 10-27" is a
capacity estimate, not a cut. If one misses 10-27 it is reported late. These three briefs follow the
form above and keep every rule listed at the top of this file. Bus writes and the editor rows (C2-02,
C2-03b, C2-11a, C2-11b, C2-19) stay as they are, since Q195 makes the editor an add-on. C2-16 to C2-18
stay later. Owners follow design 8 and Q177. Where a brief names only a model, Sage picks the seat.
Base: `refactor` at cca206cdc. Each row builds on its parents' candidates and rebases onto their
landings (Q177). C2-09a is row 219, C2-05a row 216, C2-05b row 224, C2-06 row 217 and C2-10b row 225,
all briefed and not landed. Line cites are at cca206cdc. A builder re-reads them on its base.
New refusal codes below go to Sage with these briefs, as the rule at the top asks: `room-not-open` and
`addressee-ambiguous` (C2-09b), and the problem code R8 (C2-15).
---
## C2-09b rooms: room.open, room.send with @tags, room.read
### Problem
TARGET.md:99 asks that she "converse with N agents in one chat session with @tagged messages to specify
only one addressee where desired". After C2-09a she can hold one thread per creative instance, but each
thread is a separate chat. `thread.send` reaches one thread's instance
(`packages/bus/src/broker.mjs:3511-3536`), and nothing sends one message to several threads or reads
them as one timeline. Design 6 sets out the room verbs.
### Owner and reviewer
- Owner: Rocko, gpt-6.1-sol (design 8).
- Reviewer: Dewey.
- After: C2-09a (row 219).
### Files owned
`packages/bus/src/broker.mjs` (the three room verbs only), a new pure module
`packages/bus/src/address.mjs` with the @tag parse, its export, `packages/bus/README.md` (the verb table
rows), and tests in `packages/bus/tests`.
### What ships
Design 6, C2-09b. A room is a project. Its members are the project's thread instances, the
`THREAD_DEFINITIONS` instances that C2-09a's `thread.create` accepts.
- `room.open {project}`. Human only. For each thread instance it creates a thread named after the
instance key, owned by the caller, through the same insert as `thread.create`. A same-name thread that
the caller owns on the same instance is reused. A same-name thread on another instance, or another
owner's, makes `room.open` refuse `room-thread-name-taken` and create nothing. Returns
`{project, threads: [{id, instance, name}]}`, sorted by instance key.
- `room.send {project, body}`. Human only. `parseAddress(body)` in `address.mjs` reads leading
`@name` tokens, each ended by whitespace, up to the first token that doesn't start with `@`. Names
resolve through `#named` (`broker.mjs:1480-1488`) or as instance keys, and must be members. A tag
that is one member's current name and another member's instance key refuses `addressee-ambiguous`
and inserts nothing. A tag that names the same member both ways resolves to it. With no
tag every member gets the message. Repeated tags count once. An unknown tag refuses
`unknown-addressee` and inserts nothing. A body of tags only refuses `invalid-request`. A room
whose member threads aren't all there refuses `room-not-open`. The body is stored as written, tags
included. Each target gets what `thread.send` writes today: `#checkMessaging(business, 'human',
instance)`, one message row, `#route` and one `human.input` event. All inserts use one `#now()` and
run inside the dispatch transaction, so one refused target leaves nothing inserted. Returns
`{ids: [...], to: [...]}`.
- `room.read {project, after?, limit?}`. Human only. The caller's member threads' messages, oldest
first by `messages.seq`, each with its `thread` and `instance`. One `room.send` inserts its rows in one
transaction, so their seqs are consecutive. `room.read` groups only a run of consecutive seqs from the
human with the same `at` and body into one entry, with `to` listing the instances once each. Two sends
of the same body in one millisecond, such as a double click, stay two entries.
Paging follows `thread.messages` (`THREAD_PAGE`, `:260`): `after` is a message id in one of the
room's threads, else `message-not-found`.
- The tester gets none of the three verbs, so they aren't added to `TESTER_VERBS` (`:117-119`). A tester
session refuses as for any verb outside that list. An agent session refuses `human-required`.
- No schema change. Thread names stay unique per project (`packages/bus/schema.sql:249`).
Tests, from design 6: `@continuity x` makes exactly one message, in continuity's thread. No tag makes
one per member. `@nobody x` refuses with the `messages` count unchanged. A tag after the first word is
plain text. `room.open` against a same-name thread on another instance refuses and creates nothing, and a
second `room.open` reuses the threads. Also: a `#named` display name resolves, repeated tags send once, a
tag that is one member's name and another's key refuses `addressee-ambiguous` with the `messages` count
unchanged, two identical sends at one `at` read back as two entries, a tags-only body refuses, a member whose messaging list refuses `human` makes the whole send insert
nothing, `room.read` merges one send into one entry, pages with `after`, and refuses an `after` from
another project. `parseAddress` has a table test, which C2-10c's parity test reuses.
### Out of scope
The Console room view (C2-10c). Agents reading each other's replies (C2-16). A Discord channel bound to
a room (C2-18). A CLI verb for rooms.
### Gate
Dewey approves the manifest. The bus suite is green on Sage's gate, along with every
`scripts/test-*.sh`. Grep `packages/*/tests` for `broker.mjs` and `TESTER_VERBS` before in-review,
since `packages/bus/tests/tester-writes.test.mjs` reads the tester list.
### Handoffs
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner pushes a build or a new round,
or the reviewer posts a verdict, that seat messages the next seat's T3 thread and Sage's with
`mcp__t3__t3_send_message`. Copy Mos's thread only when Mos has a step.
---
## C2-10c Console room view: merged timeline and @name completion
### Problem
The Console's chat lists threads one at a time. Its seam calls `thread.*` only
(`packages/webui/src/chat.mjs:19-27`), and the webui channel plugin allows only those verbs
(`packages/plugins/channel/webui/index.mjs:4-12`). So she can't see a room or send to it from the Console
(design 6, 7.4).
### Owner and reviewer
- Owner: a Haiku 5.5 subagent at high effort, dispatched by Sage (design 8, lead decision 129).
- Reviewer: Dewey.
- After: C2-09b, C2-10b (row 225).
### Files owned
`packages/webui/src/chat.mjs` (three room calls), `packages/webui/src/chat-http.mjs` (the room routes and
their codes), `packages/plugins/channel/webui/index.mjs` (the three room verbs in `VERBS`),
`packages/webui/src/public/chat.js` and `chat.css` (the room view), and tests in `packages/webui/tests`
and `packages/plugins/tests`.
### What ships
- A room view per project: an "Open room" control calls `room.open`. The timeline reads `room.read`
and shows every member's messages in one list, each marked with its agent's `persona.name` or
instance name. A human room message shows once with its addressees. Agent replies render through
C2-10b's cited-item renderer, with its scope badges and notices.
- The composer completes `@` against the room's members. The broker stays the authority: the browser
sends the body as typed through `room.send`, and shows the broker's `unknown-addressee` refusal as a
fixed line.
- `chat-http.mjs` maps all four room codes, `unknown-addressee`, `addressee-ambiguous`,
`room-thread-name-taken` and `room-not-open`, to 409, with fixed text for each, as it does for the
thread codes (`:15-23`).
Tests: webui tests for the merged timeline, one entry per room send, the reply renderer in the room,
completion against members, and each refusal's fixed line. A parity test runs C2-09b's `parseAddress`
case table through the browser's tag reader and asserts the same addressees for every case. The plugin
test asserts the webui channel allows the three room verbs and still refuses others.
### Out of scope
The broker verbs (C2-09b). Agents seeing each other's replies (C2-16). Library pages (C2-11b). Her sign-in
from her machine waits on SSO (#412) or Jason's interim ruling (design 10 step 5).
### Gate
Dewey approves the manifest. The webui and plugins suites are green on Sage's gate, along with every
`scripts/test-*.sh`. Grep for hash pins on `chat.js`, `chat-http.mjs` and the channel plugin before
in-review.
### Handoffs
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner pushes a build or a new round,
or the reviewer posts a verdict, that seat messages the next seat's T3 thread and Sage's with
`mcp__t3__t3_send_message`. Copy Mos's thread only when Mos has a step.
---
## C2-15 hybrid universes: exclusions, pins and the end-to-end crossover
### Problem
TARGET.md:79-84 allows a crossover only as a deliberate, user-sanctioned hybrid universe that reads the
character info of its sources and doesn't break her constraints. The parts are spread out. C2-01's
validator reads a hybrid entry (`packages/library/src/mapping.mjs:69-120`), and its reader skips R6 for a
link into a source (`reader.mjs:206-209`). C2-05a's queries and C2-06's guard take hybrids as a shape. M9
in C2-04 keeps agents off the mapping, so only her hand edit makes a hybrid. Three things are missing:
an `exclude` isn't checked against the sources, a link into an excluded path isn't reported, and nothing
checks a `pin` (design 1.4). C2-05a's index drops "exclusions" (its brief above, What ships) without a
rule for reading them. Design 8 lists C2-15 with no model.
### Owner and reviewer
- Owner: an Opus 5.5 seat that Sage picks. Under Q177 Opus and gpt-6.1-sol share the harder builds. I
name Opus because the row crosses three rows' code and the universe boundary that keeps her worlds
apart.
- Reviewer: Filbert, who reviews C2-05a.
- After: C2-05a (row 216), C2-05b (row 224), C2-06 (row 217).
### Files owned
`packages/library/src/mapping.mjs` (the `exclude` and `pin` checks), `packages/library/src/scope.mjs`
(an `excluded` helper), the exclude read in C2-05a's `packages/library/src/index-build.mjs` or
`query.mjs`, `packages/library/src/problems.mjs` (R8),
`packages/library/src/reader.mjs` (the excluded-link R6 and the pin check), the guard file from C2-06
(`packages/library/src/guard.mjs`, the excluded-path cite only), `docs/case2/SETUP-SHEET.md` from C2-12 (one
hybrid section), and tests in `packages/library/tests` and `packages/bus/tests`.
### What ships
- `exclude` keeps both forms of design 1.4. It is the type form when its first segment is one of the
hybrid's sources and the rest is exactly one key of `types`, as `second/Characters` is in
`mapping.test.mjs:57`. That form excludes the type folder in each of that source's books. Otherwise it
is the path form, and it must lie inside, or equal, a book folder of one of the sources. If both
readings fit, the validator refuses `library-mapping-invalid`. A path-form `exclude` or a `pin.entity`
that lies inside no source book folder, or under one of a source's playground prefixes, refuses
`library-mapping-invalid`, since the hybrid never reads it (REQ-LIB-3). Whether the path exists at a
commit is the reader's to check.
- One helper decides exclusion: `excluded(mapping, hybrid, path)` in `scope.mjs`, true when either form
covers the path. C2-05a's index, the reader's R6 and the guard all call it. If C2-05a has landed with
its own reading of `exclude`, this row replaces that reading with the helper, and C2-05a's query
tests stay green.
- The reader adds R6 for a link from a hybrid file into an excluded path. The index already drops it
from the hybrid's results (C2-05a).
- R8, "pinned value changed", reported only, like R5 and R6. At each commit the reader reports R8 on the
pinned entity when the file is missing, or when its front matter `field` doesn't equal `value`. It also
reports R8, with the line, on a hybrid file that is a version of the entity and sets `field` to another
value in its front matter. A hybrid file is a version of the entity when `classify` gives it the
entity's type and its front matter `name` equals the entity's. When either file has no `name`, the
file names compare instead. A link alone doesn't count: a hybrid character that links `[[Mara]]` in
`relationships` and has its own `age` is someone else, and so is a place file with `age: 300`. Values
compare as the reader's parsed text against `String(value)`. A hybrid can't edit a source's file, so
this is how this brief reads design 1.4's "when a hybrid file changes it" (Webby d1 N1 agreed with the
reading and asked for the narrowing).
- The guard refuses `citation-cross-universe` for an item that cites a hybrid file together with a
source path that `excluded` covers, in either form. An item that cites only one source universe is that universe's item, as now.
- The setup sheet gets one section on making a hybrid by hand: the entry's keys, `exclude`, `pin` and
`notes`, and that the continuity role cites the notes.
Tests, through a business-file hybrid on a bare repo in her layout's shape, and not only a hand-built
mapping: `library_find` and `library_search` in the hybrid return its own files and its sources' canon,
each with its source universe, and never a source's playground or an excluded path. `library_get` keeps
`links_out` into sources and drops excluded targets. A source universe's lookups never return hybrid
files. A cite to source canon in a hybrid item passes the guard, and one beside an excluded path
refuses with the `messages` count unchanged. `library_universe` returns sources, constraints and notes.
Each `exclude` form gets its own case on the lookups, R6 and the guard, and `second/Characters` from
`mapping.test.mjs:57` still checks. One case per validator refusal, including both readings fitting and a
pin or exclude under a source playground. For R8: a missing entity, a changed entity, a same-name hybrid
version with no link, and as non-R8 a place file with `age: 300` that links the entity and a different
character that links it. Also the excluded-link R6, and an agent's organization
write that adds a hybrid refuses `organization-projects-protected` (M9).
### Out of scope
Promotion and every write path (C2-02, C2-19). The Console's crossover button (C2-11b). Any check of a
plain-word constraint in code: `notes` stay for the continuity role to read and cite (design 1.4). Draft
universes as hybrid sources are refused by C2-01c (design ruling after r11, three levels).
### Gate
Filbert approves the manifest. The library and bus suites are green on Sage's gate, along with every
`scripts/test-*.sh`.
### Handoffs
Every seat hands off directly (DECISIONS Q210, mos-1221). When the owner pushes a build or a new round,
or the reviewer posts a verdict, that seat messages the next seat's T3 thread and Sage's with
`mcp__t3__t3_send_message`. Copy Mos's thread only when Mos has a step.
---
## Brief: row A, ZAI_API_KEY in the session env allowlist
### Problem