docs: briefs for the PM fresh-state row (#1603, Vikunja #259) and the bus tester identity row (#1604, Q134 A, Vikunja #260)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
# PM testing identity and fresh-state planning
|
||||
|
||||
Two rows filed by Sage on 2026-10-13 after the pinned-copy check (#225
|
||||
receipt, PM run rf73f0192e824). The live bus, Console and CLI run from
|
||||
the pinned copy 465bbdfbb. Code here reaches them only through a new
|
||||
copy, a re-pin and a bus restart, all under Mos's go.
|
||||
|
||||
## Bus tester identity for PM tests
|
||||
|
||||
### Problem
|
||||
|
||||
Jason ruled on Q134 ("Q134: C agreed", Vikunja #260). For now, agents
|
||||
test the PM by typing into a tmux pane, under limits: `talk`, `follow`
|
||||
and read commands only, never `decide`, `launches` or `stop`. Every
|
||||
message starts with `[mos test]`, and the testing happens in its own
|
||||
tmux session, never the design pane. That path exists only because the
|
||||
bus can't tell an agent from Jason there. `verifyHuman` in
|
||||
`packages/bus/src/human.mjs` walks the CLI's process ancestry and
|
||||
refuses on agent markers (`CLAUDECODE`, `MOSAIC_RUN_ID` and others) or
|
||||
agent binaries. A command typed into a tmux pane has the tmux server
|
||||
as an ancestor, not the agent, so it passes as Jason. Its message is
|
||||
stored `from_role 'human'`, `from_run 'human-cli'`, and logged as
|
||||
`human.input` (`broker.mjs` `message.send`). The PM then treats a test
|
||||
message as an instruction from Jason.
|
||||
|
||||
Option A replaces that with an identity the bus records as a tester,
|
||||
so a test message can never pass as Jason's.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Darkwing, after row 106 (both touch `packages/cli`) and after
|
||||
the fresh-state row below (both touch `broker.mjs`, the bus README and
|
||||
the PM's prompt). The fresh-state row goes first because #259 blocks
|
||||
relaunching the coder and reviewer. Reviewers:
|
||||
Filbert (decision 130, not a sandbox row) and Sage, because the row
|
||||
changes who can speak on the bus.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `packages/bus/src/broker.mjs`: a new binding and the verb checks for it
|
||||
- `packages/bus/src/client.mjs`, `server.mjs`, `process.mjs` and
|
||||
`runtime.mjs` (the `bindHuman` call is at `runtime.mjs:73`), the
|
||||
binding's plumbing only
|
||||
- `packages/bus/tests/`: the new tester cases
|
||||
- `packages/cli/src/cli.mjs`, `transport.mjs` and `format.mjs`: `talk`, `follow` and the
|
||||
read verbs under the tester binding, plus how a tester message renders
|
||||
- `packages/cli/tests/`: the tester cases
|
||||
- `packages/bus/README.md` and `packages/cli/README.md`, one paragraph each
|
||||
- `agents/darkwing/work/queue-<row>/`
|
||||
|
||||
### What ships
|
||||
|
||||
- A tester binding next to `bindHuman` and `bindReader`. It needs no
|
||||
human proof and works from inside an agent. The broker records it as
|
||||
tester, never as human.
|
||||
- A tester may send a message to a role, receive and read replies, and
|
||||
use the read verbs (`inbox`, `tasks`, `agents`, `trail`, `decisions`,
|
||||
`messages`, `replies`). Every other verb refuses: `decision.*`,
|
||||
`launch.revoke`, `launch.restore`, the stop path, `review.*`,
|
||||
`role.*` and `event.emit`. A test covers each refused verb, and a
|
||||
mutant that lets one through fails the suite.
|
||||
- A tester message is stored with its own sender (for example
|
||||
`from_role 'tester'`), not `human`, and writes no `human.input` event.
|
||||
A test sends one and finds no `human.input` row and no `human` sender.
|
||||
- The broker refuses a tester message whose body doesn't start with
|
||||
`[mos test]`.
|
||||
- The role's inbox and the CLI show a tester message as a tester
|
||||
message. The PM's prompt text, wherever it renders an incoming
|
||||
message, says it comes from a tester and is never Jason's instruction.
|
||||
- The binding fails closed unless the business turns it on. The packet
|
||||
proposes where that switch lives (business config or a business var),
|
||||
and Sage rules on it before the build. With the switch off, the binding
|
||||
refuses.
|
||||
- `bindHuman`, `verifyHuman` and every human path stay unchanged.
|
||||
The existing human tests pass untouched.
|
||||
|
||||
Each new check is shown to fail on a mutant that removes it.
|
||||
|
||||
Suites: the bus and cli node suites and every `scripts/test-*.sh`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
Closing the tmux gap in `verifyHuman` itself. Retiring the tmux path
|
||||
is Mos's step after this row is live. Any tester authority beyond
|
||||
talk, follow and read.
|
||||
|
||||
### Gate
|
||||
|
||||
Filbert and Sage approve on the row's issue, naming the candidate
|
||||
manifest, and the suites above pass on Sage's gate rerun. Decision 109
|
||||
notices go to Mos before and after landing. It goes live only with a
|
||||
new copy, a re-pin and a bus restart under Mos's go. Vikunja #260
|
||||
closes when it is live and Mos has retired the tmux path.
|
||||
|
||||
## A new PM run reads current launch state and task status before it plans
|
||||
|
||||
### Problem
|
||||
|
||||
Vikunja #259. After the 21:48:52Z bus restart on 2026-10-13, the new PM
|
||||
run rf73f0192e824 said launches stay off. It cited Jason's message
|
||||
0f89640f from 2026-10-12. But Jason ran `launches on` at 19:50:51Z on
|
||||
2026-10-13, and `launch_state` for mosaic-stack reads `allowed`. The run
|
||||
also planned to resume 19/229, 19/230 and 19/231, and all three are
|
||||
done on Vikunja.
|
||||
|
||||
Nothing gives a run the current state:
|
||||
|
||||
- The system prompt (`prompt()` in `packages/harness/src/bundle.mjs`)
|
||||
holds the role contract and the session facts. It is written once at
|
||||
launch, and it has no launch state and no task status.
|
||||
- Turns come only from the inbox. `message.receive` returns every
|
||||
message that no run read, with no age limit, and `role.claim` routes
|
||||
the waiting set to the new run (`#routeWaiting` in `broker.mjs`). A
|
||||
day-old message becomes the new run's first turn, and `turnRequest()`
|
||||
in `packages/harness/src/runner.mjs` shows no date on it.
|
||||
- `launch_state` (a view in `packages/bus/schema.sql`) is read only by
|
||||
the broker's refusal check. No tool or message shows it to a role.
|
||||
- `list_tasks` lists open tasks by default, so a done task drops out
|
||||
only if the PM looks it up. `roles/pm.md` never tells the PM to check
|
||||
the board or launch state before it plans.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Researcher. Reviewer: Filbert (decision 130, not a sandbox row).
|
||||
`roles/pm.md` changes here through the row's reviewed commit.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `packages/bus/src/broker.mjs`, one read verb only
|
||||
- `packages/bus/tests/`: the new verb's cases
|
||||
- `packages/harness/src/runner.mjs`: `turnRequest()` and the per-turn
|
||||
state fetch
|
||||
- `packages/harness/src/tools.mjs`, only if the packet exposes the verb as a tool
|
||||
- `packages/harness/tests/runner.test.mjs` and `tools.test.mjs`
|
||||
- `roles/pm.md`, one short rule
|
||||
- `packages/bus/README.md` and `packages/harness/README.md`, one
|
||||
paragraph each at most
|
||||
- `agents/researcher/work/queue-<row>/`
|
||||
|
||||
### What ships
|
||||
|
||||
- A read-only broker verb, open to role and reader sessions. It returns
|
||||
the business's launch state with the time and id of the event that
|
||||
set it (`allowed` when no event exists), and the open tasks from the
|
||||
broker's tracker snapshot (refs, titles and a count). The task list
|
||||
is capped, and the cap is stated in the output.
|
||||
- The runner calls that verb before each turn and puts a short
|
||||
"Current state" block, marked with the time it was read, at the top of
|
||||
the turn's request. A failed read fails that turn and says so, rather
|
||||
than running the turn without state.
|
||||
- `turnRequest()` adds the message's `at` time to the header, so an old
|
||||
message shows its age.
|
||||
- `roles/pm.md` gets one rule: the "Current state" block is the truth
|
||||
for launch state and task status. An older message that disagrees is
|
||||
out of date. Before planning to resume or launch work on a task, the
|
||||
PM checks that task with `get_task`.
|
||||
- Tests:
|
||||
- With launches revoked and then restored, the block reads `allowed`
|
||||
and gives the restore time.
|
||||
- A task closed on the tracker is missing from the block's open list.
|
||||
- A message received a day after it was sent shows its original time.
|
||||
- A failed state read fails the turn.
|
||||
- A mutant that drops the block, the time or the failure check fails
|
||||
the suite.
|
||||
- The packet reports, read-only, whether the PM's persistent workspace
|
||||
holds planning notes from earlier runs. It lists their names and
|
||||
never prints their contents. The rule above covers notes as well as
|
||||
messages.
|
||||
|
||||
Each new check is shown to fail on a mutant that removes it.
|
||||
|
||||
Suites: the bus and harness node suites and every `scripts/test-*.sh`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
Expiring or dropping old inbox messages. Changing the tracker poller
|
||||
or `list_tasks` defaults. Any change to who may switch launches.
|
||||
|
||||
### Gate
|
||||
|
||||
Filbert approves on the row's issue, naming the candidate manifest, and
|
||||
the suites above pass on Sage's gate rerun. Decision 109 notices go to
|
||||
Mos before and after landing. Role sessions load the runner fresh, but
|
||||
the live sessions run from the pinned copy, so the row goes live only
|
||||
with a new copy and a re-pin under Mos's go. Until then, nobody
|
||||
relaunches the coder and reviewer on the PM's current plan. Vikunja
|
||||
#259 closes when the row is live and a fresh PM run, tested within
|
||||
Q134's limits, reports `allowed` and doesn't plan to resume 229, 230 or 231.
|
||||
Reference in New Issue
Block a user