docs: lead decision 70, S4 rulings for Rocko (sage)

human-cli.mjs is the decide transport. S4 owns the broker host and one
notifier for the blocking-decision DM and the 08:00 Central digest.
Darkwing is second reviewer for packages/discord and the host. Trail
prints in broker order. Row 39 no longer waits on S3. SESSIONS gets
Rocko's orientation line and a correction for Sage's estimated stamps.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
2026-10-08 17:26:57 -05:00
co-authored by Claude Opus 5.5
parent bee89d107d
commit 084c3a3cee
3 changed files with 99 additions and 9 deletions
+69
View File
@@ -1281,3 +1281,72 @@ which stay with him. Each item names who decided it and what happened.
launcher today. Its Astra pin conflicts with R26, and the roster
says not to use it until a reviewed change moves it to Claude or
Sol.
70. **S4 rulings: human transport, broker host, DM and digest, trail
(2026-10-08).** Source: Rocko's orientation questions, thread
b84bb264, before row 39 is briefed.
- `mosaic decide` runs `packages/bus/src/human-cli.mjs <socket>` as a
child process, sends `{business, verb, args}` on stdin and reads
the JSON reply. The bus README already names that file as S4's
transport shim, and `verifyHuman` checks its exact path. No bus
change, so no second reviewer for this part.
- Nothing in slice 1 hosts the broker yet. `process.mjs` needs a
trusted host that forks it, boots it over IPC and keeps the
channel open, and every S4 command reads through a running broker.
S4 builds that host: `mosaic bus start|stop|status`, run as the
systemd user unit `mosaic-bus@<business>`, installed by a script in
the `scripts/discord-service.sh` pattern. It reads `dataRoot` and
the business file from config, fails closed, and puts no
capability in arguments, stdout or the environment. It keeps
`bindLaunch` reachable in process so S6 can attach its launcher
without reworking the host. S3's poller runs inside the broker
runtime, so it starts with whatever host forks it.
- The DM and the digest come from one notifier, a child of the host
that gets a reader capability over IPC. Reader capabilities can't
write to the bus, and they stay that way. The notifier records
each send in its own append-only journal,
`<dataRoot>/notify/<business>/sent.jsonl` (0600): decision id,
kind (`dm` or `digest`), time, outcome and the Discord message id.
No channel or user id goes in it. Slice 1's trail doesn't show
deliveries. If Jason wants that, it's a bus verb in a later row.
- The Discord side lives in `packages/discord/src/notify.mjs` and
uses `rest.mjs` with the connector's bot token file, so it is the
existing connector's bot and code (round 3, 4A) without sharing
its gateway process. The binding gains an optional fixed key
naming the DM recipient, which must be one of its `users`. The
README's "Not in this piece" list loses DMs for this path only.
- The notifier polls the human inbox every 30 seconds and DMs each
open blocking decision once, keyed by its journal. A failed send
retries with backoff. A duplicate DM is cheaper than a missed
blocking decision.
- The digest goes out at 08:00 America/Chicago (5A), computed in
the notifier with the IANA zone, so daylight saving moves with
it. If the host starts after 08:00 and the journal has no digest
for that day, it sends one at once. Its contents are the human
inbox: every open decision routed to Jason, blocking ones marked
as already sent by DM. Routine and within-role decisions are
logged and cross-role ones go to arbiters (REQ-DEC-2), so
REQ-DEC-4's "everything else" means the gated decisions that
don't block. An empty inbox sends one line saying so, which also
shows the job ran.
- Review: Filbert reviews the row. Darkwing reviews the
`packages/discord` change and the host as second reviewer,
because they handle the bot token and open a new outbound path to
Jason. rev-code-02 reviewed #1509, but it's a fleet seat and gets
no new work.
- Rocko builds and tests against a fake Discord REST and never
reads the token file or the private binding. The live run is
mine: the binding edit, unit install and first DM, at Jason's go,
with ids masked in the evidence.
- `mosaic trail` prints rows in the broker's order (`at`, then
table, then `seq`) and doesn't re-sort. The brief's list (request,
decisions, launches, task changes, review, close) describes what a
normal trail looks like, not a sort key. A misordered trail is a
finding against `packages/bus`, not something the CLI patches.
- A decision's trail doesn't pull in linked tasks or Vikunja
relations. It names its `task_ref`, and the CLI prints the
`mosaic trail <task>` command to follow it.
- Row 39 waited on row 38 (S3) being done. Only `mosaic tasks`
reads what S3 writes, and the broker's `tasks` view already
exists, so S4 can build and test now. Its `after` becomes row 37
alone. Its live run still follows S3's, because the task view is
empty until S3's sync fills it.
+27 -9
View File
@@ -382,27 +382,44 @@ and nothing sends a digest (REQ-DEC-4).
### Owner and reviewer
- Owner: rocko.
- Reviewer: filbert.
- Reviewer: filbert. Darkwing is second reviewer for the
`packages/discord/` change and the broker host (lead decision 70).
### Files owned
- `scripts/mosaic` (the dispatcher).
- `packages/cli/` (new): `mosaic inbox`, `mosaic decide <id> <option>`,
`mosaic tasks`, `mosaic agents`, `mosaic trail <task|decision>`.
`mosaic tasks`, `mosaic agents`, `mosaic trail <task|decision>`, and
`mosaic bus start|stop|status`, the trusted host that forks
`packages/bus/src/process.mjs` (lead decision 70).
- A script that installs the systemd user unit `mosaic-bus@<business>`,
in the `scripts/discord-service.sh` pattern.
- 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.
at 08:00 Central (5A). One notifier, a child of the host with a reader
capability, sends both. The Discord part is
`packages/discord/src/notify.mjs` plus an optional binding key for the
recipient.
### 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.
one (REQ-DEC-3). It runs `packages/bus/src/human-cli.mjs <socket>` as
the transport, with no bus change.
- `mosaic trail` shows each event for a task or decision in the broker's
order. A normal trail reads request, decisions, launches, task
changes, review, close. A decision's trail names its `task_ref` and
doesn't pull in linked tasks.
- The notifier DMs each open blocking decision once, and sends the
digest of the human inbox at 08:00 America/Chicago, catching up if the
host starts late. Every send goes in
`<dataRoot>/notify/<business>/sent.jsonl` with no Discord channel or
user id.
- Tests in `packages/cli/tests/` against a fake Discord REST, and the
connector suite green. Rocko never reads the bot token or the private
binding. Sage does the live run at Jason's go.
### Out of scope
@@ -411,7 +428,8 @@ and nothing sends a digest (REQ-DEC-4).
### Gate
Filbert approves on the row's issue. Suites green: `packages/cli`
Filbert approves on the row's issue, and Darkwing approves the
`packages/discord/` change and the host. 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)