23 KiB
Slice 1: the first working system (2026-10-04)
Status: accepted by Jason as written, 2026-10-05 (lead decision 62).
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.mdandagents/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:
- a gate that blocks;
- a gate that crashes (throws, or exits nonzero other than the block code);
- a gate at a missing path;
- a gate that times out;
- a
bashroute to the same action as a blocked tool; - 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
statonly, and amosaiccheck 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-roleinscripts/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.mdentries 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 thelaunchblock (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/, plustest-config.shandtest-task.shgreen.
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 overnode: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.sqlitefrom 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.revokedandlaunch.restoredare 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_snapshotstable 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 /labelsshows a bot. Until that's answered, the business file lists label ids. - Task verbs that follow the field table, refuse any other
PATCHkey, 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
updatedcursor; an hourly full reconcile;task_snapshotsrows for every read and write;task.changed.externalfor a person's edit. - Startup checks: each token's scopes probed against a task id that
doesn't exist,
expires_atchecked locally, the kanban view'sdone_bucket_idchecked. 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 decideworks only outside an agent run, and refuses inside one (REQ-DEC-3).mosaic trailshows 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/andpackages/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 newadapters/claude/, underadapters/README.md's contract.packages/seat/formosaic launchand the newmosaic talkandmosaic 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
launchblock: role instances only, at most 4 Opus and 4 Sonnet sessions, every launch logged, one word from Jason stops new launches, andmosaic stopends 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 talkreaches it, and Sage's T3 thread hands over. After this step the product doesn't depend on T3.
What S6 can rely on (row S0)
Copied unchanged from agents/filbert/work/slice1-probes/MATRIX.md at
4b7405b8, which Darkwing approved in round 2 (#1516 comment 26729). No case
probes the N + K boundary in line 4, so leave a few seconds of margin
below the hook's timeout.
One line per case. "Tool limit" means Pi --tools and Claude --tools or
--disallowedTools. These are rules the harness applies, not walls
(agents run as Jason's OS user).
- A gate that blocks: the hook, on both harnesses: a Pi
tool_callblock, a Claude command hook (exit 2 or JSON deny, which holds underbypassPermissions), or an SDK callback deny. The launcher must not pass--bare, which turns Claude settings hooks off. - A gate that crashes: on Pi, the hook, because a throw blocks and a
process.exitends the run. On Claude Code, a command hook wrapped as<gate> || exit 2, because the wrapper turns a crash into a block (cc-7d); an unwrapped hook fails open (cc-2). An SDK callback fails open; see line 6. - A gate at a missing path: on Pi, the hook, because a missing or
unloadable
-erefuses to start. On Claude Code, a command hook wrapped as<gate> || exit 2, because/bin/shfails on a missing or non-executable command and the wrapper turns that into a block (cc-7e, cc-7f); an unwrapped one is silently skipped (cc-3, cc-3b). - A gate that times out:
- On Pi, the hook, which never lets the tool run, but only with an
external wall clock and an
agent_endcheck. Pi has no handler timeout, and a gate whose pending promise holds nothing open lets Pi exit 0 mid-turn. - On Claude Code, a command hook wrapped as
timeout -k K N <gate> || exit 2, with the hook'stimeoutabove N + K (cc-7g, cc-7h). An unwrapped hook whose timeout expires (explicit, or the 600 s default) lets the tool run, and so does a wrapped one without-kwhose gate ignores SIGTERM (cc-7i) or whose inner timeout is too long (cc-7j). - For an SDK callback, the hook, because an expired timeout (explicit, or the 600 s default) means the tool is not run.
- On Pi, the hook, which never lets the tool run, but only with an
external wall clock and an
- A
bashroute to the same action: the tool limit, on both harnesses. A hook keyed on a tool name doesn't stop the same effect throughbash. - An Agent SDK callback that throws: the tool limit; the callback fails open. A callback is usable as a hook only if it catches its own errors and returns deny, which is what sdk-6a shows.
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.