Files
stack/agents/darkwing/work/queue-112/r1/build.md
T
jason.woltjeandClaude Opus 5.5 dbf72a2a3e darkwing: row 112 round 1 packet, decision.raise recommendation fix (#1606)
Candidate on 96656dd21: broker split refuses invalid-recommendation after the
option checks, raise_decision text asks for one key and names the yes default,
bus README codes table, broker and tools tests. Mutants m1 (old shared line)
and m2 (order swapped) killed; full gate passed 15:42:03-15:47:36Z.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-14 10:48:10 -05:00

5.5 KiB

Row 112 (#1606) round 1: decision.raise takes one recommendation key

Brief: docs/plans/2026-10-14_decision-raise-recommendation.md. Five owned files, no schema change.

BASE: 96656dd210. origin/refactor is 28c08328e, which changes only docs/SESSIONS.md after BASE. No manifest path moved. Candidate: build.patch against BASE. candidate-manifest.sha256 lists exactly the five owned files. I applied the patch to a fresh git archive 96656dd21 export and every digest matched.

What changed

packages/bus/src/broker.mjs, decision.raise. The shared check splits in two, options first:

-        if (new Set(choices).size !== choices.length || !choices.includes(a.recommendation))
-          fail('invalid-options');
+        if (new Set(choices).size !== choices.length) fail('invalid-options');
+        if (!choices.includes(a.recommendation)) fail('invalid-recommendation');

The option count and shape checks above it still refuse invalid-options. They run before both new lines, so a bad option list never reports as a bad recommendation. invalid-approval-choice is unchanged. decisions.recommendation stays one key.

packages/harness/src/tools.mjs, raise_decision:

Field Before After
question The question, with enough context to answer it ...with enough context to answer it and the reason for your recommendation
recommendation Your recommended option key, with a reason Exactly one option key, as written in options, with nothing added. The reason goes in question
approvalChoice The option key that approves the action ...approves the action. It defaults to yes, so pass it whenever no option key is yes

packages/bus/README.md. A table after the routing paragraph lists invalid-options, invalid-recommendation and invalid-approval-choice, the field each names, when each refuses, and the check order.

Tests

packages/bus/tests/broker.test.mjs gains one test, "a recommendation that is not exactly one option key refuses invalid-recommendation after the options". It raises prd.approve, the action the PM was trying to raise:

  • "yes: it matches the direction", "maybe" and a missing recommendation each refuse invalid-recommendation.
  • Duplicate yes keys refuse invalid-options with recommendation yes, and also with "yes: a reason", where the recommendation misses as well.
  • A single option refuses invalid-options with a recommendation that isn't a key.
  • The decisions and messages row counts are the same after all of those as before, and the inbox is empty.
  • A plain yes raises, and the stored recommendation is yes.

The existing duplicate-key case at broker.test.mjs:144-154 still passes unchanged, so I left it alone. Its recommendation a is a key, so it only tests duplicates.

packages/harness/tests/tools.test.mjs adds five assertions to the existing PM tool test. The recommendation text starts with "Exactly one option key, as written in options, with nothing added", no longer says "with a reason", and sends the reason to question. The question text asks for that reason, and approvalChoice names the yes default.

Mutants

mutants.py in this packet. Each mutant swaps the two new broker lines, runs the whole bus suite (node --test tests/*.test.mjs in packages/bus), and puts broker.mjs back from its .orig copy. Both report restored=True. The run was 15:41:47-15:41:55Z, with the slot from Sage. mutants-run.log has the summary.

Mutant Change Bus suite Result
m1 the old shared line, so a recommendation miss refuses invalid-options (the brief's mutant) 147 pass, 1 fail killed
m2 the recommendation checked before duplicate keys 147 pass, 1 fail killed

The new test is the only failure each time. m1 fails at broker.test.mjs:168, where "yes: it matches the direction" gets invalid-options (mutant-m1.log). m2 fails at :174, where duplicate keys with "yes: a reason" get invalid-recommendation (mutant-m2.log). On BASE, m1 is the shipped code and every bus test passes on it, which is why the row needed a test.

Gate

gate.sh in this packet. It ran on the candidate with the slot from Sage, 15:42:03-15:47:36Z, on BASE 96656dd21 with DOCKER_HOST unset and docker 29.7.2. It runs every package's node suite and every scripts/test-*.sh. GATE_PASSED, exit 0. gate-run.log has the summary, and gate/ has one log per step. The worktree still matched the manifest after the run.

Step Passed Failed
bus 148 0
business 67 0
cli 184 0
control-board 124 0
conversation 182 0
discord 178 0
harness 80 0
ledger 78 0
mosaic 69 0
queue 156 0
runs 43 0
seat 120 (0 skipped) 0
tasks 92 0
webui 108 0
scripts auth 15, conductor 17, config 24, discord 66, extension-package 18, foundation 44, queue 28 (node 156), release 14, task 98 0

Bus goes from 147 to 148 with the new test. Harness stays at 80 because the new assertions sit in an existing test. The other counts match the row 110 gate.

The real bwrap tester case ran and passed. It did not skip. From gate/pkg-seat.log:77:

✔ a confined session that runs tester-cli.mjs is refused as tester, and the same shim outside the sandbox binds (266.505117ms)

None of #258, #261, #262 or #263 came back. The release step made wt_default at 15:46:49Z with no containers, and I removed it at 15:47:45Z.

Going live

Not part of this row. The brief puts the copy, re-pin and bus restart under Mos's go, shared with 179697a8a (row 110).