docs(plans): slice 1 brief, rows S0 to S7

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-04 21:50:59 -05:00
co-authored by Claude Opus 5.5
parent dbfaad72c3
commit 43c48d7a23
+540
View File
@@ -0,0 +1,540 @@
# Slice 1: the first working system (2026-10-04)
Status: written by Sage, lead, for Jason's acceptance. Each `##` section
below is the brief for one queue row, in the shape set by
`docs/plans/BRIEF-TEMPLATE.md`. This first section covers what all the
rows share.
## What slice 1 is
The foundation direction page (`docs/plans/2026-10-04_foundation-direction.md`,
lead decisions 43 and 44) describes it. Jason opens `mosaic` in a terminal
and talks to the PM role. The PM files a task in Vikunja, linked to a
PRD requirement, and assigns it to the coder role. The stack launches
that agent with the role's contract, variables and credentials. The agent
works. A gated question reaches Jason's inbox, and he answers it in one
word. The task is reviewed and closed. The CLI and the WebUI both show
the trail, and every step is an event in the bus.
Acceptance is the PRD's: Jason gives one-sentence requests in the CLI,
and Mosaic Stack builds pieces of itself from them, five pieces in a row.
For each piece, only gated decisions reach Jason, and every step leaves a
trail in the CLI and the WebUI (row S7).
### Sources
- PRD: `docs/prd/mosaic-stack.md`, draft 0.4. Every row cites its
requirement ids. If an id changes before Jason approves the PRD, Sage
updates this brief and re-pins the rows.
- Data model: `agents/darkwing/work/slice1-data-model-2026-10-04.md`,
plus addendum A and addendum B. Where they disagree, B wins over A,
and A wins over the note.
- Vikunja facts: `agents/researcher/work/2026-10-04_vikunja-pocketid.md`
and `agents/researcher/work/2026-10-04_vikunja-probes.md`.
- Harness facts: `agents/filbert/work/meta-harness-survey-2026-10-04.md`.
- Schema: `agents/darkwing/work/slice1-proto/schema-v2.sql`, which
becomes schema v3 with addendum B section 5 (lead decision 52, B3)
before row S2 starts.
- Rulings: lead decisions 43 to 53.
### What is not a wall in slice 1
Agents run as Jason's OS user (PRD round 3, 3A). Any agent process can
read or write anything Jason's user can, including the bus file, the
broker's socket and the token files. The broker, the verbs and the hooks
are rules a well-behaved agent follows. They don't stop a hostile one. Two
things do hold in slice 1:
- agents never receive a service token, so a role can act in Gitea or
Vikunja only through the broker, which checks its authority map;
- the harness's tool limit, on the harnesses where row S0 proves it.
Containers come after slice 1. No document in this slice may call a hook
or a broker check "enforcement" without that qualifier.
Model-provider login (Claude, the Pi providers) stays as it is today,
because slice 1 has no container to isolate it in. REQ-CRED-1 and
REQ-CRED-2 cover the service tokens, Gitea and Vikunja.
### Field table
From addendum B section 4. The broker's task verbs enforce it (REQ-TASK-1).
| Field | Writer | Action | Vikunja call |
|---|---|---|---|
| title, description | pm at create; after assignment only through a resolved `task.scope.change` | `task.create`, `task.scope.change` | `POST /projects/{p}/tasks`; `PATCH /tasks/{t}` |
| due date, priority | pm | `task.schedule`, `task.priority.change` | `PATCH /tasks/{t}` |
| percent done | the assigned role | `task.update.assigned` | `PATCH /tasks/{t}` |
| state (bucket) other than `done` | the assigned role | `task.update.assigned` | `PUT /projects/{p}/views/{k}/buckets/{b}/tasks` |
| `done` | pm, after a review verdict | `task.close` | the same move, into the `done` bucket |
| assignee | pm | `task.assign`, `task.reassign` | `POST /tasks/{t}/assignees`; `DELETE /tasks/{t}/assignees/{u}` |
| labels | pm | `task.create`, `task.schedule` | `POST /tasks/{t}/labels`; `DELETE /tasks/{t}/labels/{l}` |
| relations | pm | `task.create`, `task.schedule` | `POST /tasks/{t}/relations`; `DELETE /tasks/{t}/relations/{kind}/{other}` |
| comments | any role on the task, append only | `task.update.assigned`, `message.send` | `POST /tasks/{t}/comments` |
A `PATCH` body carries only `title`, `description`, `due_date`,
`priority` and `percent_done`, and only the keys the action owns. Vikunja
answers 200 to a `bucket_id` in a `PATCH` and doesn't move the task, so
the broker refuses that key before sending. Reopening is a person's act.
### Rows and order
| Row | Piece | Owner | Reviewer | After |
|---|---|---|---|---|
| S0 | Harness probe matrix | filbert | darkwing | none |
| SR | Install runbook for slice 1 identities | sage | darkwing | none |
| S1 | Roles v2, business and project files, variable layers | darkwing | filbert | none |
| S2 | The bus and the broker core | rocko | darkwing | schema v3 |
| S3 | Tasks: the Vikunja adapter, broker task verbs and sync | darkwing | filbert | S1, S2, SR |
| S4 | The `mosaic` CLI: inbox, decide, tasks, agents, trail | rocko | filbert | S2, S3 |
| S5 | WebUI: inbox, tasks, agents and trails; CHAT-03 Gate E | dewey | darkwing, filbert | S4 |
| S6 | Meta-harness and launching; the PM moves off T3 | filbert | darkwing | S0, S1, S3 |
| S7 | Acceptance: five pieces in a row | sage | none | S5, S6 |
Rows S0, SR and S1 start at once. S2 starts once Darkwing lands schema v3
in `slice1-proto/`. Every row's gate is a reviewer's approval on the
row's issue plus the named suites green on an index export, and Sage
commits. Row S7's gate is Jason's.
Every row emits the events its steps produce (REQ-EVT-1), using the
closed kind list in the schema. A row that needs a new kind changes the
schema through row S2's owner and the CTO's review.
## Slice 1 S0: harness probe matrix (Pi and Claude Code hooks, pinned versions)
### Problem
REQ-HARN-2 says a probe run against the live harnesses must pass before
any build relies on a block. Filbert's survey (section 9) checked Pi's
`tool_call` block and Claude's hook docs against source and docs, but ran
nothing. It also found that Claude command hooks at a mistyped path leave
the gate silently off, and that a Codex hook that crashes doesn't block.
Row S6 can't choose which layer stops what until these are measured.
### Owner and reviewer
- Owner: filbert, who wrote the survey.
- Reviewer: darkwing.
### Files owned
- `agents/filbert/work/slice1-probes/` (scripts, outputs, the matrix).
- Nothing outside it. The probes run in a scratch directory under
`$HOME`, not `/tmp`, with no service tokens and no network beyond the
model providers the harnesses already use.
### What ships
A matrix for Pi 0.85.1 and Claude Code 2.1.289, each at the pinned
version, with one row per case:
1. a gate that blocks;
2. a gate that crashes (throws, or exits nonzero other than the block
code);
3. a gate at a missing path;
4. a gate that times out;
5. a `bash` route to the same action as a blocked tool;
6. for Claude Code, an Agent SDK callback hook that throws (the survey's
"not established").
Each cell records the command, the exact outcome (tool ran or didn't,
the message the model saw) and whether the harness fails closed. The
matrix ends with one line per case saying which layer slice 1 can rely on:
the tool limit, the hook, or neither. Codex is out of scope until after
v1.
### Out of scope
- Building the generator or any adapter (row S6).
- The Vikunja points still unverified in addendum B section 7. Row S3
probes those before relying on them.
### Gate
Darkwing approves the matrix on the row's issue. Sage reads the
"rely on" lines and copies them into row S6's brief section before S6
starts.
## Slice 1 SR: install runbook for slice 1 identities
### Problem
REQ-CRED-1 (round 3, 1A and 2A) has Jason create each role's tokens by
hand. No runbook exists, and the identities are new: four Gitea bot users,
four Vikunja `bot-<role>` users and one read-only `bot-<business>-sync`.
Vikunja tokens must expire, and Vikunja accepts a past expiry date.
Gitea tokens don't expire.
### Owner and reviewer
- Owner: sage.
- Reviewer: darkwing, for the Vikunja scope map.
### Files owned
- `docs/guides/slice-1-identities.md`.
- A business file template, `templates/business/mosaic-stack.example.json`,
with credential references only, coordinated with row S1's schema.
### What ships
A runbook Jason follows in about 20 minutes:
- creating pm-bot, cto-bot, coder-bot and reviewer-bot in Gitea, each
with a token at the narrowest scope that does that role's work, and
each token written to a 0600 file outside the repository;
- creating the Vikunja owner account (a deployed bundled instance or an
existing one, REQ-TASK-3), the four role bots and the sync bot, the
project shares (write for roles, permission 0 for sync), and one token
per bot with the scope map from addendum B section 2 and an explicit
expiry date;
- the business file entries that reference each token file;
- how to check each token with `stat` only, and a `mosaic` check that
calls the broker's startup scope probe once row S3 exists;
- rotation: what to do before a Vikunja token expires, and the Gitea
rotation step with a record.
No token value appears in the runbook, a command line or a URL.
### Out of scope
- Any script that creates users or tokens. Minting is out of v1.
- Running the runbook. Jason does that, and row S3's live tests wait for
it.
### Gate
Darkwing approves the scope tables. Jason runs it, and the broker's
startup probe passes for every identity.
## Slice 1 S1: roles v2, business and project files, variable layers
### Problem
`roles/` holds `conductor-policy.json` and `researcher.json` at version
1, with tool and network ceilings only. No business roles exist, no
authority map, no credential needs, and no variable layers beyond
`~/.config/mosaic-dev/config.json`. REQ-ROLE-1 to 4 and REQ-VAR-1 and 2
need all of these.
### Owner and reviewer
- Owner: darkwing (CTO), who designed them (note sections 1 and 2).
- Reviewer: filbert.
### Files owned
- `roles/pm.json`, `roles/cto.json`, `roles/coder.json`,
`roles/reviewer.json` (new, version 2).
- The role validator and `resolve-role` in `scripts/mosaic-task.mjs`, to
accept version 2 while version 1 files keep working.
- `packages/business/` (new): the business file and project file
loaders, the variable key registry (type, allowed layers, merge rule)
and the resolver.
- `contracts/` only if the note's closed action vocabulary must be
image-baked. Darkwing says which in the build packet, and a contract
change needs Sage's review.
- `docs/TOOLS.md` entries for any new command.
### What ships
- The role schema v2: contract, authority map over the closed vocabulary
(unlisted means gated), credential needs by service and scope, and the
version 1 ceilings kept.
- The business file at `~/.config/mosaic-dev/businesses/<id>.json`:
role instances (PM held by Sage, CTO by Darkwing, coder, reviewer),
arbiters, credential references, business variables, and the `launch`
block (lead decision 49). The stack reads it and never writes it. A
missing or invalid file fails closed.
- The project file at `<project>/.mosaic/project.json`.
- The resolver: most specific layer wins for plain values, limits
intersect, an unknown key or a key at a layer it isn't allowed in is
refused.
- Tests in `packages/business/tests/`, plus `test-config.sh` and
`test-task.sh` green.
### Out of scope
- Role claims and the one-holder rule (row S2, they live in the bus).
- Launching anything (row S6).
### Gate
Filbert approves on the row's issue. Suites green: `packages/business`
tests, `test-config.sh`, `test-task.sh`, `test-conductor.sh`.
## Slice 1 S2: the bus and the broker core (decisions, messages, role claims, events)
### Problem
Decisions don't exist as objects. Escalations are chat lines, the
waiting-on-jason state and "Input needed:" lines. Messages are addressed
to T3 thread ids, not roles. REQ-DEC-1 to 3, REQ-MSG-1, REQ-ROLE-2 and
REQ-EVT-1 need a store and a single writer.
### Owner and reviewer
- Owner: rocko. It needs no credentials: the broker's token handling is
built and tested here with fixture tokens, and real tokens arrive in
row S3.
- Reviewer: darkwing, who wrote the schema and the prototype.
### Files owned
- `packages/bus/` (new): the store over `node:sqlite`, the broker
process, its local socket protocol, the verbs, tests.
- `<dataRoot>/bus/` at runtime, created by the broker on first start,
0700.
### What ships
- `bus.sqlite` from schema v3, with the open-time digest check, WAL, and
every table append-only.
- The broker: the only writer. It stamps role and run on every write
from the launch record. It holds tokens in memory only, read from the
business file's references, and never logs or writes them (REQ-VAR-2).
- Verbs: raise, route and resolve a decision; send a message to a role
and deliver it to the current holder; claim and release a role
instance; emit an event. A verb whose action is gated refuses unless
it cites a resolved decision.
- Routing by class (REQ-DEC-2): routine and within-role are logged;
cross-role goes to the business's arbiter; gated goes to the human.
- Human resolution only from a CLI process outside any agent run
(REQ-DEC-3). `launch.revoked` and `launch.restored` are written only on
that path (lead decision 50).
- Tests covering every refusal the prototype shows, plus the broker
protocol, on Node 24 and Node 26.
### Out of scope
- Vikunja (row S3). The `task_snapshots` table ships in the schema, and
row S3 writes it.
- Delivery to Discord and the digest (row S4 owns the outbound side).
### Gate
Darkwing approves on the row's issue. Suites green: `packages/bus` tests
on Node 24 and Node 26, and every `scripts/test-*.sh`.
## Slice 1 S3: tasks (the Vikunja adapter, broker task verbs, sync)
### Problem
Tasks live in the queue, which is built for agents and has no dates,
assignees Jason can see, or UI. REQ-TASK-1 to 4 put tasks in Vikunja
through the broker. The probes showed what Vikunja actually does: wrong
scope names in addendum A, a cursor that misses column moves and
deletions, `If-Match` not enforced, and 401 for both an expired token and
one out of scope.
### Owner and reviewer
- Owner: darkwing, who ran the probes behind addendum B.
- Reviewer: filbert.
### Files owned
- `packages/tasks/` (new): the Vikunja v2 client, the broker's task
verbs, the poller, the reconcile and the startup checks.
- `packages/bus/` only to register the task verbs, coordinated with row
S2's owner.
- `packages/tasks/deploy/vikunja/`: the bundled option, the unmodified upstream
image pinned by digest, bound to 127.0.0.1 (REQ-TASK-3). No Vikunja code
enters the repository.
### What ships
- Before relying on them, probes of addendum B section 7's open points
on a scratch instance, recorded in the build packet. The first is
which labels `GET /labels` shows a bot. Until that's answered, the
business file lists label ids.
- Task verbs that follow the field table, refuse any other `PATCH` key,
compare before writing, and write only with the acting role's token.
- Sync with the read-only sync bot: every 30 seconds, the open-task
kanban listing plus the `updated` cursor; an hourly full reconcile;
`task_snapshots` rows for every read and write; `task.changed.external`
for a person's edit.
- Startup checks: each token's scopes probed against a task id that
doesn't exist, `expires_at` checked locally, the kanban view's
`done_bucket_id` checked. Any failure fails closed.
- A task can't be created without a requirement id (REQ-TASK-1). The
queue keeps the agents' lock, review rounds and audit log, and no field
syncs both ways (REQ-TASK-4).
- Tests against a recorded fake of the v2 routes, plus one live run
against the instance from row SR.
### Out of scope
- Webhooks. Slice 1 has none.
- Pocket ID and SSO (after v1).
### Gate
Filbert approves on the row's issue. Suites green: `packages/tasks`
tests, `packages/bus` tests, every `scripts/test-*.sh`, and the live run's
log in the packet with no token value in it.
## Slice 1 S4: the `mosaic` CLI (inbox, decide, tasks, agents, trail)
### Problem
Jason has no front door (REQ-CLI-1). `scripts/mosaic` dispatches to
`seat` and `queue` only. Blocking gated decisions have no route to him,
and nothing sends a digest (REQ-DEC-4).
### Owner and reviewer
- Owner: rocko.
- Reviewer: filbert.
### Files owned
- `scripts/mosaic` (the dispatcher).
- `packages/cli/` (new): `mosaic inbox`, `mosaic decide <id> <option>`,
`mosaic tasks`, `mosaic agents`, `mosaic trail <task|decision>`.
- The outbound side: a blocking gated decision goes to Jason's Discord DM
through the existing connector (#1509, round 3, 4A), and a daily digest
at 08:00 Central (5A). Changes inside `packages/discord/` need that
package's existing review rules.
### What ships
- Every command reads through the broker, never the SQLite file
directly.
- `mosaic decide` works only outside an agent run, and refuses inside
one (REQ-DEC-3).
- `mosaic trail` shows each event for a task or decision in order:
request, decisions, launches, task changes, review, close.
- Tests in `packages/cli/tests/`, and the connector suite green.
### Out of scope
- `mosaic talk` (row S6, since it needs the launched PM).
- The WebUI (row S5).
### Gate
Filbert approves on the row's issue. Suites green: `packages/cli`
tests, `test-discord.sh`, every `scripts/test-*.sh`.
## Slice 1 S5: WebUI (inbox, tasks, agents and trails; CHAT-03 Gate E)
### Problem
`packages/webui` shows the control board and, through CHAT-03 I1
(243e153c), can drive a sealed Pi against fixtures. It doesn't show
decisions, tasks or trails (REQ-WEB-1). Row 5's Gate E demonstration
happens in this step (round 3, 7B).
### Owner and reviewer
- Owner: dewey, who owns the WebUI and CHAT-03.
- Reviewers: darkwing and filbert.
### Files owned
- `packages/webui/`, `packages/conversation/` and `packages/control-board/`,
within their existing READMEs' rules.
- `agents/dewey/work/` for design and evidence.
### What ships
- Views for the inbox (read-only in slice 1, resolution stays in the CLI
under REQ-DEC-3), tasks, agents and trails, reading the same broker
data as the CLI.
- Conversations through CHAT-03, with the I3 follow-ups in DEFERRED that
the live PM session needs: the seal covering the engine command, and
an explicit engine environment.
- Gate E: the interactive demonstration with all seats, then Jason's
workday ruling. Live cutover still needs its own approval.
### Out of scope
- Resolving decisions from the browser. That needs authentication, which
comes with Pocket ID after v1.
### Gate
Both reviewers approve on the row's issue. Suites green: webui,
conversation, control-board, and every `scripts/test-*.sh`. Row 5's Gate
E is Jason's.
## Slice 1 S6: meta-harness and launching (the PM moves off T3)
### Problem
Seats are launched by hand in T3, with prompts and tools set per
launcher (`scripts/agent-host-dev.sh`, `agents/rocko/launch.sh`).
Nothing generates a session from a role (REQ-HARN-1). The PM can't
launch anyone (REQ-LAUNCH-1), and `mosaic` can't talk to a PM the stack
owns (REQ-CLI-2).
### Owner and reviewer
- Owner: filbert, who wrote the survey and row S0's matrix.
- Reviewer: darkwing.
### Files owned
- `packages/harness/` (new): the launch bundle generator (prompt, policy,
skills, typed tools for the vocabulary actions, manifest).
- `adapters/pi/` and a new `adapters/claude/`, under
`adapters/README.md`'s contract.
- `packages/seat/` for `mosaic launch` and the new `mosaic talk` and
`mosaic stop`.
- `scripts/agent-host-dev.sh`, to stop passing `--approve` (DEFERRED).
### What ships
- Bundles for Pi first, then Claude Code, from the role file and the
resolved variables. Each layer the bundle relies on is one row S0
proved.
- The PM launches role sessions through the broker, within the business
file's `launch` block: role instances only, at most 4 Opus and 4
Sonnet sessions, every launch logged, one word from Jason stops new
launches, and `mosaic stop` ends a running session.
- A session that finds only founder credentials stops (REQ-CRED-2).
- The named step where the PM moves off T3: the stack launches the PM
session without a window, `mosaic talk` reaches it, and Sage's T3
thread hands over. After this step the product doesn't depend on T3.
### Out of scope
- Codex (after v1).
- Containers (after slice 1).
### Gate
Darkwing approves on the row's issue. Suites green: `packages/harness`,
`packages/seat`, every `scripts/test-*.sh`. A recorded run where the PM
launches a coder session and `mosaic agents` shows it.
## Slice 1 S7: acceptance, five pieces in a row
### Problem
The PRD's acceptance can only be shown end to end.
### Owner and reviewer
- Owner: sage, as the PM role.
- Reviewer: none. Jason is the gate.
### Files owned
- `agents/sage/work/slice1-acceptance/` for the run log.
- Queue rows and issues the five pieces create.
### What ships
Five one-sentence requests from Jason in the CLI, each carried to a
closed, reviewed task. For each: the trail in `mosaic trail` and the
WebUI, the decisions that reached Jason (gated only), and the event
counts from the bus. Those counts are the first measures (REQ-EVT-1).
### Out of scope
- Measure targets. Slice 1 produces the first numbers, and targets come
after.
### Gate
Jason, from the five trails: only gated decisions reached him, and every
step left a trail.