docs: lead decision 142 and the PRD 1.2 build-row briefs (#1613-#1623)

Decision 142 records the PRD 1.2 approval, Mos's rulings on choices 6 to
9, the token-unit draft position, the sage-0188 proxy facts and 9b left
unfiled. The brief file holds one section per build row, steps 2 to 11
with 9a and 9c split out, each tied to its Gitea issue.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-14 13:41:04 -05:00
co-authored by Claude Opus 5.5
parent 7eb5ad0cb8
commit f7ef24f0eb
2 changed files with 392 additions and 0 deletions
+38
View File
@@ -3171,3 +3171,41 @@ which stay with him. Each item names who decided it and what happened.
interaction for the user." Sage's PRD 1.2 draft is interaction for the user." Sage's PRD 1.2 draft is
`agents/sage/work/prd-v1-2/2026-10-14_prd-1.2-amendment-draft.md`. `agents/sage/work/prd-v1-2/2026-10-14_prd-1.2-amendment-draft.md`.
No rows, role files or business-file edits until Jason approves it. No rows, role files or business-file edits until Jason approves it.
142. **PRD 1.2 approved: the chief of staff, the assistant, threads and
agent limits (2026-10-14, Jason, relayed by Mos in sage-0185 to
sage-0187).** Jason approved the draft in Q149 on one condition:
"approved as long as the agent limits are tunable via "Settings" in
the WebUI and via the CLI tooling." He set the limits in Q150, quoted
in full under Launching in the PRD: "No hard cap on agent limits. All
tunable." Mos read Q150 back as Q151, with the default that launches
over a limit are refused and logged and a run that hits its token
limit is stopped, and Jason answered "Q151: yes". Mos checked the
final draft against Q147 to Q151 and gave the go for build step 1
(sage-0187).
Mos ruled on the draft's choices 6 to 9, within the approved spec,
and is reporting them to Jason, who can change any of them:
- 6. Count every token kind the harness reports, apply limits to the
sum, and show each kind in Settings.
- 7. "Absolute" means a running total that resets only by hand.
- 8. Stops land at the end of a turn. The 900-second turn clock
bounds the overrun. Stops inside a turn (build step 9b) stay
optional and aren't filed until the token rows show a need.
- 9. Only the stack's own use counts, and Settings says so.
Mos added one note: REQ-LIMIT-2 measures the daily quota in tokens
as a draft position, since Q150 names no unit and OAuth plans meter
by usage windows. The PRD says so.
Mos's proxy facts (sage-0188, CLIProxyAPI v8.0.29 public source):
per-client-key tokens exist only in the usage queue, which is off by
default, keeps 60 seconds, drains on read, carries the client key in
plaintext and sits behind the management API. The proxy has no
per-key quota. So stack-side counting (step 7) stays, and the proxy
route for step 9c is an open design point riding on #237. Management
API access is Jason's call.
Mos's first paragraph in sage-0187 also listed 9b as its own row.
Ruling 8 is the later and more specific instruction, so Sage left 9b
unfiled. Decision 123 applies to build steps 5 and 11.
+354
View File
@@ -0,0 +1,354 @@
# PRD 1.2 build rows (2026-10-14)
Filed by Sage after Mos's go for build step 1 (sage-0187, lead decision
142). The requirements are PRD version 1.2, `docs/prd/mosaic-stack.md`,
landed in 7eb5ad0cb. Each section below is one queue row's brief. The
steps keep the PRD 1.2 draft's numbering (`agents/sage/work/prd-v1-2/`).
## Rules for every row in this file
- Cite the PRD 1.2 requirement ids the row meets in the packet, and in a
test's name or comment where a requirement drives it.
- Targeted builder gate (decision 136). Sage reruns the full gate at the
landing target. Nothing goes live except through a re-pin on Mos's go
(decision 109). A landing that changes `packages/webui/src/public/`
waits for Mos's go too (decision 123).
- No row edits Jason's organization file or role workspaces. If a row
needs his file changed, the packet gives the exact change and Sage
sends it to Mos.
- A bus schema change ships a migration, tested on a copy of a
schema-current database, and lands with a re-pin that has downtime.
- One mutant per check, listed in the packet. Each fails a test.
- If a row needs a decision its brief doesn't make, stop and ask Sage.
Don't widen the row.
- Limits have no ceiling in code (Q150). Don't add one back as a
constant, a validator bound or a default that can't be changed.
## Role files for the chief of staff and the assistant
Build step 2, small. Gitea #1613.
- Owner: researcher (wrote the chief-of-staff role file in row 84).
- Reviewer: filbert (decision 130: role files, no sandbox change).
- Requirements: REQ-COS-1, REQ-ASSIST-1, REQ-ROLE-4, REQ-LAUNCH-1.
**Files owned.** `roles/assistant.json` and `roles/assistant.md` (new),
`roles/chief-of-staff.json` and `.md`, `roles/pm.json` and `.md`, and the
tests that pin role files (`packages/business/tests/chief-of-staff.test.mjs`
and any other the owner finds).
**What ships.**
1. `roles/assistant.json` and `.md`. The contract is REQ-ASSIST-1: the
user's main agent, talks with the user in a thread, passes work for
the organization to the chief of staff, and never directs other
agents, creates or assigns tasks, or launches sessions. Authority is
`message.send` within role and nothing else. No credentials. Tools as
read-only as the chief of staff's.
2. `roles/chief-of-staff.json` gains `role.launch` (decision 139, Q144).
Its contract is rewritten to REQ-COS-1: hires and terminates, owns the
organization's structure, launches, takes work from the assistant and
never talks to the user. Remove every "main point of interaction" duty
left from decision 140.
3. `roles/pm.json` drops `role.launch`, and `roles/pm.md` no longer
launches. The PM takes work from the chief of staff.
4. The packet lists the change Jason's organization file needs: the
assistant and chief-of-staff instances, and `launch.by` naming the
chief of staff. Until Jason makes it, the broker still gates the chief
of staff's launches (`broker.mjs:305`), and that's expected.
**Not in this row.** The broker's send limits (step 3), ending runs and
the organization-file verb (step 6), threads (step 4).
**Acceptance.** Every role file loads through the business validator.
Tests pin that the assistant's authority is `message.send` only, that
the chief of staff holds `role.launch`, and that the PM doesn't. All
existing suites pass.
## The broker limits who the assistant sends to and hears from
Build step 3, small to medium. Gitea #1614.
- Owner: researcher, after step 2.
- Reviewer: filbert (broker message check, no sandbox change).
- Requirements: REQ-ASSIST-2.
**Files owned.** `packages/business/src/role.mjs` (one new optional role
field), `packages/bus/src/broker.mjs` (`message.send` only),
`roles/assistant.json`, and tests in both packages.
**What ships.**
1. An optional role field naming the roles a holder may send to and the
roles that may send to it. The owner picks its name and shape and
records why. `human` names the user.
2. In `message.send`, the broker refuses a send when the sender's role
has the field and the target isn't in it, or when the target's role
has the field and the sender isn't in it. Each refusal is logged with
its reason, the way the broker logs other refusals.
3. `roles/assistant.json` sets both lists to the user and the chief of
staff.
4. A role without the field behaves exactly as today.
5. The packet says what happens to a tester's send to the assistant. The
default is refused, per REQ-ASSIST-2.
"In the thread" in REQ-ASSIST-2 needs threads, so it lands in step 4.
**Acceptance.** Tests for both directions, for an allowed send each way,
and for a role without the field. Mutants: drop each direction's check
and drop the refusal log.
## Projects and threads in the bus
Build step 4, large. Gitea #1615.
- Owner: darkwing, after step 3. It also follows darkwing's rows 116 and
118.
- Reviewer: dewey (decision 130: a per-thread session store reaches the
run's HOME and the sandbox).
- Requirements: REQ-ORG-2, REQ-THREAD-1, REQ-THREAD-2, REQ-USER-2,
REQ-ROLE-2 (one assistant holder per thread), REQ-MSG-1, REQ-LAUNCH-1
(thread sessions start on demand and stop when idle).
**Design first.** Before code, post a design packet on the issue for
Sage's approval. It covers:
- the schema: projects and threads, and the thread on messages, claims
and deliveries;
- the migration and its downtime;
- how REQ-ORG-2 projects relate to the organization file's `projects`
key, which today names git repositories to clone
(`packages/seat/src/checkout.mjs`);
- the per-thread session store. Claude Code's session directory and HOME
are per run today (`launcher.mjs:204-206`, `sandbox.mjs:157`). Choose a
store that outlives the run, or a rebuild of context from the thread's
messages, and say why;
- on-demand start and idle stop, with the idle time set in the
organization file;
- how REQ-USER-2 holds with the one user there is today.
**Files owned.** Set by the approved design, within
`packages/bus/src`, `packages/seat/src`, `packages/cli/src/launcher.mjs`,
`packages/business/src` (the idle setting) and their tests.
**Not in this row.** `mosaic thread` (step 10), the WebUI chat area
(step 11), limits (steps 5, 7 to 9a).
**Acceptance.**
- The migration runs on a copy of a schema-current database, and the
bus loads the result.
- Tests: one holder per thread; a message names its thread; the chief of
staff's reply comes back to the thread that sent the work; a thread's
history survives an idle stop and a restart (stub harness); one
thread's messages never reach another; an idle thread holds no session
and counts against no limit.
- Mutants for each of those. Dewey approves on the issue naming the
candidate manifest.
## Agent limits as settings, with the organization-file write path
Build step 5, medium to large. Gitea #1616. Decision 123 applies to the
Settings form.
- Owner: rocko.
- Reviewer: filbert (business, launcher, CLI and Console; no sandbox
change).
- Requirements: REQ-ROLE-3 (the write path), REQ-LIMIT-1, REQ-WEB-1
(Settings), REQ-LAUNCH-1 (refusals and launch logging).
**Design first.** A short design note on the issue before code: the
`launch` schema with its model table, and the migration of Jason's file
from `{ opus: 4, sonnet: 4 }`.
**What ships.**
1. One host module for organization-file writes. It validates the whole
file with the existing validator, writes atomically, keeps the
previous version, and logs who changed what with old and new values.
An invalid change is refused, and the old file stays.
2. A `launch` schema with a model table, a provider and a model per
entry, and limits in total, per provider, and per provider and model.
It replaces family matching (`packages/seat/src/session.mjs:52-57`)
and `LAUNCH_CEILING` (`packages/business/src/vocabulary.mjs:86`). Any
limit may be unset (no limit). A value is a whole number of 0 or more,
and 0 stops new launches of that kind. 4 Opus and 4 Sonnet become
defaults.
3. A migration command for an organization file in the old shape. It
prints the new file, and writes it only when a human runs it. The row
doesn't run it on Jason's file.
4. The launcher re-reads limits at each launch (today once at start,
`launcher.mjs:123`). It refuses a launch over any limit and logs the
limit it hit. Each launch is logged with harness, provider and model.
Lowering a limit doesn't end running sessions.
5. `mosaic limits` to view and set limits and each role instance's
model, behind the human proof.
6. A Settings form in the Console that shows every limit and each
instance's model and changes them through the Console's send checks.
Today `/api/settings` is GET-only (`serve.mjs:233-236`).
**Not in this row.** The chief of staff as a caller (step 6), token
counts and limits (steps 7 to 9a).
**Acceptance.** Tests for the write path (atomic, previous version kept,
change log, invalid refused), each limit kind (unset, 0, at limit, over
limit), the re-read at launch, the migration, `mosaic limits` and the
form's send checks. Mutants for each. Filbert approves. The landing waits
for Mos's go (decision 123).
## The chief of staff ends runs and changes the organization file
Build step 6, small to medium. Gitea #1617.
- Owner: researcher, after steps 2 and 5.
- Reviewer: filbert (broker authority, no sandbox change).
- Requirements: REQ-ROLE-2, REQ-ROLE-3, REQ-LIMIT-1, REQ-COS-1.
**What ships.**
1. `role.revoke` by the chief of staff needs no resolved decision. It
takes a reason and logs it. For every other role, revoke stays gated
and cites a decision (`broker.mjs:442`, `:738`).
2. A broker verb that lets the chief of staff change the organization
file, limits included, through step 5's write path. The change log
names the chief of staff as the actor. The owner names the verb.
3. `roles/chief-of-staff.json` grants both.
**Acceptance.** Tests: the chief of staff ends a run without a decision,
logged with its reason; another role still needs one; a chief-of-staff
change is validated, written and logged like a human's; an invalid
change is refused. Mutants for each.
## Token accounting per run
Build step 7, medium. Gitea #1618.
- Owner: rocko, after step 5.
- Reviewer: dewey (decision 130: the host reads files inside the run's
HOME, which the session can write).
- Requirements: REQ-LIMIT-2 and REQ-LIMIT-3 (their base), REQ-WEB-1 (the
readout).
**What ships.**
1. After each turn, the host reads usage from the Claude Code transcript
in the run's HOME (`home/.claude/projects/<dir>/<session id>.jsonl`)
or from the Pi session file. It records each kind separately (input,
output, cache write, cache read) per run, role instance, provider,
model and account (lead decision 142, ruling 6). The account is null
until step 9a names one.
2. A bus schema migration for the records.
3. `mosaic usage`, a view by instance, model, provider and account.
4. A read-only readout in Settings, with each kind shown. Decision 123
applies.
5. The host treats these files as untrusted data: bounded reads, no
symlinks followed, a parse failure logged and the turn's usage
recorded as unknown, never as zero. The packet says plainly that a
session can edit its own transcript, so the count is the stack's best
reading, not proof.
**Acceptance.** Fixture transcripts for both harnesses, including a cut
line, a symlink and an oversized file. The migration runs on a copy of a
schema-current database. Mutants for each guard. Dewey approves.
## Token limits per instance, model, provider and key
Build step 8, medium. Gitea #1619.
- Owner: researcher, after steps 6 and 7.
- Reviewer: filbert (launcher and broker checks, no sandbox change).
- Requirements: REQ-LIMIT-3.
**What ships.**
1. Token limits per role instance, model, provider and API key, set
through step 5's write path and the chief of staff's verb (step 6).
A count is the sum of every kind since the limit was set or last
reset (lead decision 142, rulings 6 and 7).
2. A reset that the user (`mosaic limits`, Settings) and the chief of
staff can make, logged.
3. A launch whose subject is at its limit is refused. A running session
that reaches a limit stops at the end of its current turn. Both are
logged with the limit and the count.
**Acceptance.** Tests for each subject kind, the reset, the refusal and
the end-of-turn stop (stub harness). Mutants for each.
## Daily quota for the credential the stack uses today
Build step 9a, small to medium. Gitea #1620.
- Owner: dewey, after step 7.
- Reviewer: filbert (organization file and launcher, no sandbox change).
- Requirements: REQ-LIMIT-2.
**What ships.**
1. The organization file names the one account the bus uses today
(`SESSION_CREDENTIALS`, `packages/seat/src/session.mjs:76`, decision
119) by a name and a credential reference, never the secret
(invariant 3). Launches log the account name, and step 7's records
carry it.
2. A daily quota on that account, in tokens (a draft position, PRD
REQ-LIMIT-2), with a reset time and time zone.
3. At the quota, new launches on the account are refused and running
sessions stop at the end of their current turn. Both are logged.
4. Settings and `mosaic limits` show the quota with the line that it
counts only the stack's own use (lead decision 142, ruling 9).
**Acceptance.** Tests for the count, the day boundary in a non-UTC time
zone, the refusal and the stop. Mutants for each.
## Quota for more than one account or per proxy key (parked on #237)
Build step 9c, large, not sized. Gitea #1621. Parked until the #237
design settles how the stack hands a credential to each session.
- Requirements: REQ-LIMIT-2.
Mos's facts (sage-0188, CLIProxyAPI v8.0.29 public source):
- Per-client-key token counts exist only in the proxy's usage queue,
which is off by default, keeps records 60 seconds (at most 3600),
drains on read, carries each client key in plaintext and sits behind
the management API.
- The proxy has no per-key quota or rate limit.
So stack-side counting (step 7) stays. A proxy collector or proxy-side
enforcement needs management API access, which agents don't use and
which is Jason's call. The proxy route is an open design point. Nobody
reads the proxy's config or key files for this row.
## mosaic talks to the assistant in a named thread
Build step 10, small to medium. Gitea #1622.
- Owner: darkwing, after step 4.
- Reviewer: filbert (CLI, no sandbox change).
- Requirements: REQ-CLI-1, REQ-CLI-2, REQ-THREAD-2.
**What ships.** `mosaic` lists projects and threads, creates and names
them, and talks to the assistant in the thread the user picks, with
`--follow` as `talk` has today. Plain `mosaic talk` asks for a thread or
names the default one. The packet records which and why.
**Acceptance.** Tests for list, create, talk and follow in a thread, and
for a send to a thread the user doesn't own (REQ-USER-2). Mutants for
each.
## The WebUI chat area
Build step 11, medium to large. Gitea #1623. Decision 123 applies.
- Owner: rocko, after steps 4, 5 and 10.
- Reviewer: filbert (Console, no sandbox change).
- Requirements: REQ-WEB-1, REQ-THREAD-2, REQ-USER-2.
**What ships.** A chat area in the Console that lists the user's
projects and the named threads in each, creates and names them, and has
one conversation view per thread where the user talks to the assistant.
Sends go through the Console's send checks.
**Acceptance.** Tests for list, create, send and the per-thread view,
including a thread the user doesn't own. Mutants for each. Filbert
approves. The landing waits for Mos's go (decision 123).