Files
stack/agents/rocko/work/queue-73/BUILD.md
T
jason.woltjeandClaude Opus 5.5 2137dd7f5d review(rocko): row 73 round 1 packet, coder role drops task.reassign (#1567)
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]>
2026-10-11 18:20:34 -05:00

8.6 KiB

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.