Rocko's packet agents/rocko/work/queue-73/ (candidate manifest bf4e5ed6, packet manifest 41ffcd5c, 53 files), their BUILD-LOG entry and SESSIONS line. Sage's SESSIONS line for the review request (comment 27467) and row 79 (#1573) for the stale text the packet names. Co-Authored-By: Claude Opus 5.5 <[email protected]>
141 lines
8.6 KiB
Markdown
141 lines
8.6 KiB
Markdown
# Row 73 (#1567): drop the coder's task.reassign grant, candidate packet, round 1
|
|
|
|
Author: Rocko. Reviewer: Dewey. Brief:
|
|
`docs/plans/2026-10-11_claude-pin-and-coder-grant.md`, section "Coder role:
|
|
drop the task.reassign grant the broker always refuses". Base: `324c064b`
|
|
(`base.txt`). At 23:16Z the checkout HEAD `f6b9ea4e` has no change since the
|
|
base to any candidate path or to `packages/{bus,business,tasks}/src` or
|
|
`packages/harness/src/tools.mjs`. `build.patch` applies to it cleanly. The
|
|
packet is uncommitted. I made no commits, pushes or Gitea calls and read no
|
|
token or private binding.
|
|
|
|
## Files (`files.txt`, `candidate-manifest.sha256`)
|
|
|
|
| File | Change |
|
|
|---|---|
|
|
| `roles/coder.json` | `crossRole`: `["task.reassign", "task.scope.change"]` → `["task.scope.change"]`. Nothing else in the file changes. |
|
|
| `packages/business/tests/role.test.mjs` | "shipped authority follows the note's table": asserts that the coder's `crossRole` is exactly `["task.scope.change"]` |
|
|
| `packages/business/tests/resolve.test.mjs` | "classify": the coder's `task.reassign` is now `gated` and its `task.scope.change` is `cross`. "limits.authority narrows cross-role actions too" moves from the coder to the pm (see below). |
|
|
| `packages/bus/tests/broker.test.mjs` | +1 test built on the shipped role files: "the shipped coder role raises task.reassign as gated to the human; task.scope.change stays cross-role" |
|
|
| `packages/harness/tests/tools.test.mjs` | +1 test: "a coder with a tracker gets its own task tools and no task_reassign" |
|
|
| `packages/tasks/tests/world.mjs` | the fixture's coder `crossRole` now matches the shipped role |
|
|
| `packages/tasks/tests/verbs.test.mjs` | in "assign and reassign …": a coder's `task.reassign` refuses `field-writer` and records no `action.allowed` |
|
|
| `agents/rocko/work/queue-73/mutants.sh` | the mutant rerun: `mutants.sh <tree>` |
|
|
|
|
No change to `broker.mjs`, `PM_VERBS`, the slice 1 field table or any other
|
|
role's grants.
|
|
|
|
### Why the narrowing test moved to the pm
|
|
|
|
Before this change, the test limited the coder to `task.scope.change` and
|
|
watched `task.reassign` drop out of `crossRole`. With one cross-role action
|
|
left, that limit drops nothing from `crossRole`. I checked this: with the new
|
|
role file and the old test, the mutant `resolve-cross-unnarrowed` (crossRole
|
|
ignores `limits.authority`) passes the test (`out/old-narrow-test-under-mutant.txt`).
|
|
The pm still holds two cross-role actions. The test now limits the pm to
|
|
`task.create`, `task.close` and `task.priority.change`. It checks that
|
|
`crossRole` keeps `task.priority.change` and drops `task.scope.change`, and
|
|
that `withinRole` narrows too.
|
|
|
|
## Every place that read the old grant, and what it says now
|
|
|
|
Probe (`out/probe-base.txt`, `out/probe-candidate.txt`): the shipped roles
|
|
through `validateBusinessDocument`, `resolveInstance`, `busBusiness` and a
|
|
real `Broker`, as `bootConfig` assembles them.
|
|
|
|
| Reader | Before (base) | After (candidate) |
|
|
|---|---|---|
|
|
| `business/src/resolve.mjs` `resolveInstance` | coder `crossRole` `[task.reassign, task.scope.change]` | `[task.scope.change]`. The resolved digest of every coder instance changes too. Only the bundle manifest records it, and nothing compares it. |
|
|
| `business/src/resolve.mjs` `classify` | `cross` | `gated` |
|
|
| `bus/src/broker.mjs` `#classification` (via `cli/src/config.mjs` `bootConfig` → `busBusiness`) | `cross-role`; `decision.raise` routes to the domain arbiter, `pm` by default (`cto` for technical) | `gated`; routes to `human` |
|
|
| `harness/src/tools.mjs` `typedTools`, with a tracker | the coder gets a `task_reassign` tool | no `task_reassign` tool; `task_scope_change` and `task_update_assigned` remain |
|
|
| `harness/src/tools.mjs` `untypedActions` → `bundle.mjs` prompt, without a tracker | the prompt lists `task.reassign` among "Actions you hold that have no bus tool yet" | not listed |
|
|
| `tasks/src/verbs.mjs` `PM_VERBS` | `field-writer` before authority | unchanged: `field-writer` before authority |
|
|
| `tasks/tests/world.mjs` fixture | copied the old grant | matches the shipped role |
|
|
| business, bus, harness and tasks tests | see Files | see Files |
|
|
|
|
Readers I did not change (outside the brief's files; Sage's call):
|
|
|
|
- `packages/business/README.md`, lines 30-43: the version 2 example is the
|
|
shipped coder file and still shows `"crossRole": ["task.reassign",
|
|
"task.scope.change"]`. It goes stale with this change. Suggested fix: drop
|
|
`"task.reassign", ` from the example.
|
|
- `packages/tasks/README.md`, lines 193-196: "A coder role's `crossRole`
|
|
grant of `task.reassign` can't be used. The writer check refuses it before
|
|
authority is looked at. …" This will be stale too. Suggested text: "The
|
|
coder role holds no `task.reassign` grant (row 73). The writer check
|
|
refuses a non-pm `task.reassign` before authority is looked at anyway. If
|
|
a coder should hand work on, that's a change to the field table, not to
|
|
the role file."
|
|
- `packages/tasks/live/run.mjs`, line 32: the live run's own copy of the
|
|
slice 1 authority still gives the coder `task.reassign`. Nothing runs it in
|
|
the suites. It never has the coder call `task.reassign`, so its result
|
|
doesn't change. It should match `world.mjs`.
|
|
- Unaffected: `scripts/test-task.sh` line 279 and
|
|
`business/tests/role.test.mjs` line 115 are role-validation fixtures, not
|
|
the coder's grant. Historical notes are left as they are: the slice 1
|
|
plan, the Darkwing and Filbert work notes, and lead decision 90.
|
|
|
|
## What a coder's task.reassign does end to end after the change
|
|
|
|
1. A pi coder session has no `task_reassign` tool, so it can't make the
|
|
call through the harness.
|
|
2. It can still `raise_decision` with `action: task.reassign`, because the
|
|
tool's action enum is every action. The broker classes it `gated` and
|
|
routes it to the human, where before it went to the pm as cross-role. The
|
|
human is then asked a question that can only end in `field-writer`.
|
|
3. If the human approves and the coder calls the verb directly on the bus,
|
|
the broker's `authorize` passes as `gated`; the bus test shows that. The
|
|
task verbs refuse first with `field-writer`, before `authorize`, so nothing
|
|
is consumed and nothing is written (tasks test).
|
|
|
|
The brief names the follow-up but doesn't make it, and nor does this row. To
|
|
refuse a non-pm `task.reassign` (or any `PM_VERBS` action) at
|
|
`decision.raise`, before a decision opens, is a broker change and would be
|
|
its own row.
|
|
|
|
A bus that is already running keeps the authority it loaded at boot. The
|
|
change reaches it on its next restart.
|
|
|
|
## Mutants (`out/mutants-candidate.txt`, `out/mutants-base.txt`)
|
|
|
|
| Mutant | Edit | Base `324c064b` | Candidate |
|
|
|---|---|---|---|
|
|
| `role-keeps-reassign` | `roles/coder.json` puts `task.reassign` back | n/a (the base is this mutant) | killed by role, classify, bus and harness tests |
|
|
| `broker-unlisted-cross` | `#classification`: an action in neither list → `cross-role` | **survived** | killed by the new bus test |
|
|
| `resolve-cross-unnarrowed` | `resolveInstance`: `crossRole` ignores `limits.authority` | killed | killed (by the narrowing test and the harness test "an action outside the instance's authority has no tool") |
|
|
| `classify-unlisted-cross` | `classify`: an unlisted action → `cross` | killed | killed |
|
|
| `tools-all-task-verbs` | `typedTools`: a task tool for every verb, held or not | killed | killed (now also by the new harness test) |
|
|
|
|
The script saves each file, applies the sed and checks it changed something.
|
|
It then runs the five test files and restores the file from the saved copy,
|
|
so it works on an uncommitted tree. `out/mutants-*/` holds each diff and log.
|
|
I ran the candidate twice. The run recorded here uses the final script; the
|
|
first run used the same script before a comment-only reflow, with the same
|
|
results.
|
|
|
|
## Gate (`out/gate/`, sequential, scratch worktree at 324c064b with the candidate, `node_modules` linked, 23:09:50Z-23:14:34Z)
|
|
|
|
| Suite | Result |
|
|
|---|---|
|
|
| tasks `node --test 'packages/tasks/tests/*.test.mjs'` | 71 pass, 0 fail |
|
|
| bus | 81 pass, 0 fail |
|
|
| business | 60 pass, 0 fail |
|
|
| harness | 58 pass, 0 fail, 0 skipped |
|
|
| cli | 112 pass, 0 fail |
|
|
| webui | 50 pass, 0 fail |
|
|
| seat | 27 pass, 0 fail |
|
|
| test-auth | 15 passed, 0 failed |
|
|
| test-conductor | 17 passed, 0 failed |
|
|
| test-config | 24 passed, 0 failed |
|
|
| test-discord | 66 passed, 0 failed |
|
|
| test-extension-package | 18 passed, 0 failed |
|
|
| test-foundation | 44 passed, 0 failed |
|
|
| test-queue | 27 passed, 0 failed |
|
|
| test-release | 14 passed, 0 failed |
|
|
| test-task | 98 passed, 0 failed (Docker) |
|
|
|
|
cli, webui and seat go beyond the gate the brief names. I ran them because
|
|
they read the shipped roles. After the gate I removed the `rocko-r73_default`
|
|
network.
|