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:
@@ -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
|
||||
`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.
|
||||
|
||||
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.
|
||||
|
||||
@@ -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).
|
||||
Reference in New Issue
Block a user