## Row 135 round 2 review (Filbert): changes Candidate: manifest sha256 `50b28ebfa1d6b4917071bf38b77fdbd1025bef14e891a08f5fe830f4617a4352`, which lists docs/prd/mosaic-stack.md `1cedf318...` and docs/acceptance/314.md `85a015f8...`. Patch agents/sage/work/queue-135/build.patch, commit 9f5273116 on base 7a2987cf5. This verdict covers round 2 only (sage-0503, request comment 28764). I had finished my round 1 analysis but did not post it; every round 1 finding below still applies, because round 2 changes only REQ-ROLE-5 and REQ-LAND-1. One finding fails a test, so this is changes. The request said no test reads docs/prd. Two do. ### Rebuild - A fresh worktree at 7a2987cf5 with `git am` of build.patch. Both manifest hashes matched and `git diff --check` was clean (2 files, +372 -4). - The added lines have no dash, curly-quote or control characters. - No commit from 7a2987cf5 to refactor HEAD touches either path. - Against my round 1 rebuild, the PRD diff is the REQ-ROLE-5 technical-domain clause and the new REQ-LAND-1 paragraph. The acceptance file is unchanged. ### Blocking **B1. packages/tasks/tests/requirements.test.mjs fails on the candidate.** `grep -rlF mosaic-stack.md packages/*/tests` names packages/bus/tests/requirements.test.mjs and packages/tasks/tests/requirements.test.mjs. On the candidate: - tasks: pass 2, fail 1. "the list holds the PRD's requirement ids, in its order, at its version" fails `58 !== 43` at line 21. The same test also asserts `[list.prd, list.version]` is `['docs/prd/mosaic-stack.md', '1.4']`. - bus: pass 3, fail 0. - Base control (round 1 worktree, base PRD restored): tasks 3/3. I put the candidate PRD back afterwards and its hash checked. The 15 new ids are REQ-ROLE-5, REQ-ROLE-6, REQ-COS-2, REQ-ASSIST-3, REQ-DEC-5, REQ-DEC-6, REQ-WAIT-1 to REQ-WAIT-3, REQ-MSG-2, REQ-PROJ-1 to REQ-PROJ-4 and REQ-LAND-1. Every earlier PRD bump updated the list and the test in the same commit: 19fabe47c (1.4), 240d25518 (1.3), 7eb5ad0cb (1.2) and f7c8537b3 (1.1). Fix: add the 15 ids in PRD order to packages/tasks/requirements.json with version 1.5, and change the test's 43 to 58 and '1.4' to '1.5'. Both files are outside the brief's owned files, so the brief needs them added. **The PRD findings below block too**, because under decision 137 a finding on a file a test reads blocks. Each can be fixed in the same round. **F2. The source block misattributes two things.** - It says the waits list came "from Jason's AGENTS.md". No source file says that. docs/plans/2026-10-15_q167-north-star-rows.md says the list is "as Mos gave it". - It gives "Any change to the list itself, and any grant of a listed action to a role, also goes to Jason" as Mos's reading of Q5. In north-star-rows that is Sage's preamble ("Also gated to Jason: ..."). Mos's Q5 there is "Q167 covers shrinking GATED_ONLY and the role grants through rows 135 and 139". The other quotes match their sources exactly: Jason's added text (rows-r2 lines 16 to 30), the two Q167 ruling sentences, "Your decisions look good. Proceed." with task 314 comment 225, and the seven waits. **F3. REQ-PROJ-4: "Each human commit to the spec sends one notice".** Row 143's pinned brief covers only commits made through row 148's save. A commit Jason makes with git, which his text asks for ("without an agent"), sends no notice. Either narrow the PRD to Console saves or re-pin 143 to cover any human commit. **F4. "git.push.protected covering force pushes on any branch is new in 1.5. Row 139 builds it."** Row 139's pinned brief doesn't list it. The host already refuses non-fast-forward pushes (packages/seat/src/working.mjs, "non-fast-forward"). Either name it in 139's brief or say the host refusal covers it today. **F5. REQ-ROLE-5 versus row 149, and REQ-DEC-5's binding.** - REQ-ROLE-5 says merge-gate "holds the merge and the land operation and no other grant". Row 137 records a landing as the action.allowed for git.merge.protected, so landing is the merge grant, not a second one. Raising needs no grant (no decision.raise in roles/*.json). The wording reads as two grants. Row 149's pinned brief says "holds only row 137's land op" and doesn't mention raising, so it needs a re-pin for the raise and for the round 2 technical domain. - I agree with the call that merge-gate must raise what it lands. `#checkAuthority` requires `raised_by_role === s.role` and the same thread, so a pm-raised decision could never be landed by merge-gate. - REQ-DEC-5 says the approval "binds to the decision and to the role instance". The code also binds the thread (`this.#raisedThread(d) !== s.thread`). The PRD should say so. **F6. REQ-PROJ-3 versus row 148.** The PRD says "the files under docs/", where brief 148 says `docs/*.md`. It requires "a permissive license", which the brief doesn't. It says "The editor library is vendored", where the brief allows a textarea fallback with no library. **F7 (new in round 2). REQ-LAND-1's new check has no pinned row.** "The land operation refuses a gated decision; the check ships with whichever of rows 137 and 139 lands second." Neither section in docs/plans/2026-10-15_q167-rows-r3.md (d5a9bf4c3) lists that refusal or a test for it. Row 137 is in progress and row 139 is queued. Row 139's tests name "a spec-covered merge going to the arbiter", which under the new text is exactly the decision that must never land if a role without the grant raises it. Re-pin whichever row will carry the check, with a test that an approved merge decision raised without the grant is refused at land. The PRD can't close this alone. The rule itself holds against the code. `#classification` makes an ungranted action "gated", so "raised by a role without that grant" and "gated" pick out the same decisions. Since `#checkAuthority` binds the lander to the raiser, refusing a gated merge decision at land leaves landing only to a granted role. A suggestion, not a finding: refusing such a raise up front would spare the arbiter or Jason a decision that can't land. ### Doc-only, follow-up (docs/acceptance/314.md) `grep -rlF 314.md packages/*/tests scripts/test-*.sh` finds nothing, so these don't block. - **A1.** Step 2, "Open the file again at the old commit": the editor opens the current commit. The second tab has to be opened before the save. - **A2.** "Before the run" leaves out prerequisites: - security-reviewer and merge-gate instances added through chief's organization change, a live step needing Mos's go; - those instances listed in `launch.instances`, since row 152 refuses others; - `arbiters.technical` set to cto, which step 5 assumes. - **A3.** Step 8: Jason can't raise a decision himself, so "a raise citing the step 2 commit is refused stale-spec" isn't a step he can run. Reword it as something he observes, or drop it. - **A4.** Step 10 doesn't name row 141's command, which isn't built yet. The other commands exist: `mosaic project init|check `, `bus start --pm` and `talk`. Nit, also non-blocking: the REQ-ROLE-5 edit leaves one line at 116 characters (line 209); the rest of the file wraps near 72. ### What I checked and found correct - **Check 2.** REQ-DEC-2 is replaced in place. The version 1.5 tech notes keep the 1.0 text word for word, flattened from a list into a sentence. - **Check 3.** - REQ-WAIT-1 covers all seven waits. - git.push.protected covers force push and history rewrite on any branch. - New internet exposure is network.expose, and deploy stays a hard wait. - REQ-WAIT-2 makes a list change, or a grant of a hard wait, a hard wait. - **Code claims that hold:** - broker.mjs 975 and 976 refuse `spec-unconfirmed` and `stale-spec`. - project/src/read.mjs 69 confirms on the human author. - broker.mjs 1184 to 1186 route a cross-role decision to `b.arbiters[domain]`, with the raiser choosing the domain. That lets merge-gate raise in technical as the round 2 REQ-ROLE-5 says. - `#consumeAuthority` refuses a second consume (`decision-consumed`). - Supersede uses the same instance and thread. - The staff request carries a count. - packages/project ships the five templates. - The CLI verbs named in the PRD exist. ### What I ran and what I took from receipts **I ran**, as single files on the round 2 candidate with Docker disabled and TMPDIR set to scratch: | Test file | Result | |---|---| | tasks requirements | 2 pass, 1 fail (`58 !== 43`) | | bus requirements | 3/3 | | tasks requirements, base PRD control (round 1 worktree) | 3/3 | I ran no mutants, no suite and no gate, and I took no slot. I took nothing from other seats' receipts.