Triage of Vikunja P19 #228 and its c78, the gaps Jason's first PM request hit. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,244 @@
|
||||
Status: written by Sage, lead. It follows `docs/plans/BRIEF-TEMPLATE.md`.
|
||||
It triages Vikunja P19 #228 (dogfood), which Mos filed from Jason's
|
||||
first PM request after the PM started (talk fef3fa41, PM RESULT e300f8af,
|
||||
about 2026-10-12T19:27Z; Mos sage-0100), and the three gaps Mos added in
|
||||
#228 c78 from the same test (sage-0101). Three rows: the board read, the
|
||||
requirement ids, and the role checkout. All three block S7 (row 42): the
|
||||
PM can't carry a request to a task, or the coder work on the project,
|
||||
until they land. Gitea issues #1583, #1584 and #1585.
|
||||
|
||||
# The PM's task flow on a real board (#228)
|
||||
|
||||
## PM task reads that fit a session
|
||||
|
||||
### Problem
|
||||
|
||||
The PM's `list_tasks` tool takes no arguments
|
||||
(`packages/harness/src/tools.mjs:116`, `obj({})`). The broker's `tasks`
|
||||
verb returns every row of `task_current` with all its fields, description
|
||||
included (`packages/bus/src/broker.mjs:313-329`, `:860`), and the MCP
|
||||
server sends it as indented JSON (`packages/harness/src/mcp-server.mjs:40`,
|
||||
`pi-extension.mjs:45`). On the Mosaic Stack board that reply was about
|
||||
120 KB. Claude Code saved the oversized result to a file outside the
|
||||
PM's workspace, and the run's file guard refused to read it. That refusal
|
||||
is right; the reply is the defect. So the PM couldn't see the board.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Dewey, after row 81 lands. Reviewer: Darkwing.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `packages/bus/src/broker.mjs`, the `tasks` verb and `#taskRows` only
|
||||
- `packages/harness/src/tools.mjs`, `mcp-server.mjs`, `pi-extension.mjs`
|
||||
- `packages/bus/README.md`, `packages/harness/README.md`, the sections
|
||||
on the `tasks` verb and the tool replies
|
||||
- tests under `packages/bus/tests/` and `packages/harness/tests/`
|
||||
- `agents/dewey/work/queue-<row>/`
|
||||
|
||||
### What ships
|
||||
|
||||
- The broker's `tasks` verb takes optional arguments. With none, it
|
||||
returns what it returns today, so the Console and `mosaic` are
|
||||
unchanged. With any, it filters and pages: `done` (true, false; the
|
||||
tool's default is false), `assignee` (a role), `label`, `requirement`,
|
||||
`limit` (default 50, at most 200) and `after` (a task ref, for the next
|
||||
page). A filtered reply is `{ tasks, total, next }`, and each task is
|
||||
the short form: ref, title, done, bucket, assignee roles, labels,
|
||||
priority, requirement and the last change time, with no description.
|
||||
An unknown argument or a bad value refuses with `invalid-request`.
|
||||
- One more argument, `task_ref`, returns that one task in full, as today's
|
||||
row, so the PM reads a description only when it needs one.
|
||||
- `list_tasks` exposes those arguments and says the default is open
|
||||
tasks, 50 at a time. A `get_task` tool takes `task_ref`. Both are
|
||||
typed for the pm role and the same holders as today.
|
||||
- Every tool reply has a size cap, in `mcp-server.mjs` and
|
||||
`pi-extension.mjs` both, set below the size at which the pinned Claude
|
||||
CLI saves a tool result to a file. Dewey finds that size for the
|
||||
version the bus unit runs (`MOSAIC_CLAUDE_BIN`, decision 91), records
|
||||
how in the packet, and sets the cap with margin. Over the cap, the tool
|
||||
returns an error naming the size and the cap and telling the session to
|
||||
narrow the query, never a cut-off reply. Replies go out as compact
|
||||
JSON, not indented.
|
||||
- Tests: a board of 500 fixture tasks with long descriptions, where
|
||||
`list_tasks` with defaults fits under the cap and pages to the end
|
||||
with `next`; each filter; `task_ref`; the refusals; the cap error from
|
||||
both servers; and the no-argument call still matching today's reply.
|
||||
|
||||
Suites: the bus, harness, webui and cli node suites, and every
|
||||
`scripts/test-*.sh`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
Loosening the run's file guard or adding the tool-results directory to
|
||||
the workspace. The Console's task views. Any change to who may read
|
||||
tasks. Server-side search over descriptions.
|
||||
|
||||
### Gate
|
||||
|
||||
Darkwing approves on the row's issue, naming the candidate manifest. The
|
||||
suites above pass on Sage's gate rerun. The broker and harness change is
|
||||
live at the next bus restart, which ends the PM session and launches a
|
||||
new one (decision 109). Sage notifies Mos before the landing and sends
|
||||
the commit after it; Mos restarts the bus and confirms the PM relaunched.
|
||||
|
||||
## The approved requirement ids as data
|
||||
|
||||
### Problem
|
||||
|
||||
`task.create` needs a requirement id, and `verbs.mjs:187` checks only
|
||||
its format, `REQ-[A-Z]+-[1-9][0-9]*`. Nothing tells the PM which ids
|
||||
exist. The PRD (`docs/prd/mosaic-stack.md`, version 1.0, approved
|
||||
2026-10-05, lead decision 61) holds 24 of them, but no tool reads it. So
|
||||
the PM refused to create a task, and asked Jason in its RESULT instead
|
||||
of raising a decision to his Inbox. In the same test it later created
|
||||
P19 #229 and #230 under `REQ-QA-1`, which passes the format check and
|
||||
isn't in the PRD.
|
||||
|
||||
Two more gaps from #228 c78 are words, not missing paths:
|
||||
|
||||
- `task_create`'s `request` says "Id of the message or human input this
|
||||
task answers" (`tools.mjs:41`). `create()` accepts only a `human.input`
|
||||
event id (`verbs.mjs:352`, `broker.mjs:460`), so the PM's message id
|
||||
refused `request-not-found`. Each message `message.receive` returns
|
||||
already carries that id as `request` (`broker.mjs:790-797`); the tool
|
||||
doesn't say to use it.
|
||||
- A blocking decision needs a task (`broker.mjs:596`, `task-required`),
|
||||
and the PM read that as no Inbox path for a missing requirement. A
|
||||
non-blocking decision needs none. `prd.approve` is gated
|
||||
(`broker.mjs:29`), so it routes to the human. Nothing tells the PM
|
||||
this.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Dewey, after the row above (both touch `broker.mjs`). Reviewer:
|
||||
Filbert.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `packages/tasks/requirements.json`, new
|
||||
- `packages/tasks/src/requirements.mjs`, new, and
|
||||
`packages/tasks/src/verbs.mjs`, the `task.create` requirement check only
|
||||
- `packages/bus/src/broker.mjs`, a `requirements` read verb only
|
||||
- `packages/harness/src/tools.mjs`, a `list_requirements` tool and
|
||||
`task_create`'s `request` description only
|
||||
- `roles/pm.md`, the paragraphs on creating tasks and raising decisions
|
||||
only
|
||||
- `packages/tasks/README.md`, `packages/bus/README.md`
|
||||
- tests under `packages/tasks/tests/`, `packages/bus/tests/` and
|
||||
`packages/harness/tests/`
|
||||
- `agents/dewey/work/queue-<row>/`
|
||||
|
||||
### What ships
|
||||
|
||||
- `packages/tasks/requirements.json` lists the PRD version and path and
|
||||
each id with a one-line title. A test reads the PRD's `**REQ-...**`
|
||||
bullets and fails unless the list matches them exactly, ids and order.
|
||||
A later PRD version is a new list in a reviewed commit, like the PRD.
|
||||
- `requirements.mjs` checks the file's shape and loads it frozen, as
|
||||
`fields.mjs` does for `fields.json` (row 61). A bad file refuses at
|
||||
load.
|
||||
- `task.create` refuses with `unknown-requirement` when the id isn't in
|
||||
the list. The format check stays.
|
||||
- The broker's `requirements` verb returns the list, a read for every
|
||||
session that can create tasks. The `list_requirements` tool exposes it
|
||||
to the pm role.
|
||||
- `roles/pm.md`: before creating a task, pick the id from
|
||||
`list_requirements`. When no id fits the request, raise a decision with
|
||||
action `prd.approve`, `blocking` false and no `task_ref`, naming the
|
||||
request and the nearest ids, and don't create the task; don't ask in
|
||||
a RESULT.
|
||||
- `task_create`'s `request` says it is the `request` field of the human
|
||||
message being answered, not the message id. `pm.md` says the same.
|
||||
- Tests: the PM's role session raises that `prd.approve` decision with
|
||||
no task and it routes to the human; `task.create` with a message id
|
||||
still refuses `request-not-found`, and with that message's `request`
|
||||
it passes the request check.
|
||||
|
||||
Suites: the tasks, bus, harness, webui and cli node suites, and every
|
||||
`scripts/test-*.sh`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
Editing the PRD or adding ids. Mapping Jason's past requests to ids.
|
||||
Showing the list in the Console. Letting a blocking decision go without a
|
||||
task. What happens to #229 and #230; their id is the PM's and Jason's to
|
||||
correct.
|
||||
|
||||
### Gate
|
||||
|
||||
Filbert approves on the row's issue, naming the candidate manifest. The
|
||||
suites above pass on Sage's gate rerun. Live at the next bus restart and
|
||||
PM launch, so the same decision-109 notices as the row above.
|
||||
|
||||
## A project checkout in each role workspace
|
||||
|
||||
### Problem
|
||||
|
||||
A launched role session works in
|
||||
`<dataRoot>/workspaces/<business>/<instance>/`
|
||||
(`packages/seat/src/session.mjs:20,46`), which the launcher creates
|
||||
empty (`packages/cli/src/launcher.mjs:159-160`). Nothing puts the
|
||||
project there. In #228's test the coder's workspace held only a fresh
|
||||
`.git` and its own new files, so it worked beside the source rather than
|
||||
in it, and the PM asked Jason for a checkout (dad88d23). Slice 1's goal
|
||||
is the stack building pieces of itself (`2026-10-04_slice-1.md`, line
|
||||
20), which needs the coder in the project. The business file names each
|
||||
project's `root` (`packages/business/src/business.mjs:170-177`); that is
|
||||
the only source the launcher has.
|
||||
|
||||
### Owner and reviewer
|
||||
|
||||
Owner: Darkwing, after row 63 and ahead of row 88. Reviewer: Filbert.
|
||||
|
||||
### Files owned
|
||||
|
||||
- `packages/cli/src/launcher.mjs`, the workspace step in `start()` only
|
||||
- `packages/seat/src/checkout.mjs`, new
|
||||
- `packages/seat/README.md`, `packages/cli/README.md`, the workspace
|
||||
paragraphs
|
||||
- tests under `packages/seat/tests/` and `packages/cli/tests/`
|
||||
- `agents/darkwing/work/queue-<row>/`
|
||||
|
||||
### What ships
|
||||
|
||||
- At every launch, before the bundle is built, the host makes sure each
|
||||
project in the business file has a clone at
|
||||
`<workspace>/<project id>/`. The host runs git, outside the session's
|
||||
sandbox. A missing clone is `git clone --no-hardlinks <root>`, at the
|
||||
root's checked-out branch. An existing clone with a clean tree on that
|
||||
branch fast-forwards from its origin. Anything else (a dirty tree,
|
||||
another branch, a fast-forward that fails) is left exactly as it is,
|
||||
and the launch log records `checkout-kept` with the project id and the
|
||||
reason. The host never resets, cleans, deletes or force-updates a
|
||||
clone.
|
||||
- A clone's origin is the local root path. No credential, token or
|
||||
Gitea remote goes into it. The root is never written.
|
||||
- A clone that can't be made refuses the launch with `checkout-failed`
|
||||
and the project id, through the broker's `refuse()` like the other
|
||||
launch steps. No partial clone is left behind.
|
||||
- The bundle's session text names the clone's path, so the session
|
||||
knows where the project is.
|
||||
- Tests: a first launch clones; a second fast-forwards after a commit
|
||||
to the root; a dirty clone and one on another branch are kept and
|
||||
logged; an unreadable root refuses `checkout-failed` with nothing
|
||||
left; the root's tree and refs are unchanged after each case; no
|
||||
remote URL in a clone carries a credential.
|
||||
|
||||
Suites: the seat, cli, bus and harness node suites, and every
|
||||
`scripts/test-*.sh`.
|
||||
|
||||
### Out of scope
|
||||
|
||||
How the coder's change reaches the reviewer and the repository: pushing
|
||||
a working branch (`git.push.working`), the reviewer reading the coder's
|
||||
change, and landing. That is the next row, which Sage briefs once this
|
||||
one lands. Credentials in a clone. The PM's workspace contents beyond
|
||||
the clone.
|
||||
|
||||
### Gate
|
||||
|
||||
Filbert approves on the row's issue, naming the candidate manifest. The
|
||||
suites above pass on Sage's gate rerun. The launcher runs in the bus
|
||||
host, so the change is live at the next bus restart: the same
|
||||
decision-109 notices as the rows above.
|
||||
Reference in New Issue
Block a user