Marshal review gate
Gate a candidate diff behind an independent, multi-reviewer consensus before integrating it. Spawn several biased reviewers (architect, quality, tests) that each judge the SAME diff in isolation, then apply a fixed truth table - integrate only when no reviewer rejects and at least one approves. Use when a single-pass review (marshal-orchestrate step 4) is not enough: a high-stakes merge, or when you want a quorum instead of one opinion. The engine runs the agents; this playbook is the judgment.From its SKILL.md
npx -y skills add chiruu12/marshal --skill marshal-review-gateAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.7 KB, as published. Nobody here has run it
Gating a merge behind reviewer consensus
Prefer a declared team when one exists.
run_team(seemarshal-adversarial-review) does step 2 below as one call: it fans the reviewers out in isolation, refuses any reviewer that is not read-only, and returns one report per reviewer plus a unified one. It deliberately does not apply the truth table in step 4 — you read the reports and decide, which is what the sealedREVIEW:line here is a cruder approximation of. Use the hand-rolled loop when you need a one-off panel no team file declares, or want to vary the rubric per run.
You already have a worker run that succeeded and a diff you could merge. succeeded means the
process exited cleanly (plus the workspace's config-level verify: gate, if one is set - that
mechanical gate runs before this review gate ever sees the run), not that the code is correct - so
before a high-stakes integrate you gate it behind several biased, independent reviewers and a
fixed rule, instead of one judgment call. The
loop is collect → review (in isolation) → decide by truth table → fix or integrate.
This is not "run the same review prompt three times and average." It is biased, independent, multi-angle convergence: each reviewer holds one prior, judges one lens, and seals its verdict without seeing the others.
When to use
- After a run succeeded (
marshal-orchestratestep 3) and the change is risky or valuable enough to want more than your own single read - a public API, a migration, a security-sensitive path. - Not a replacement for reading the diff yourself; a structured second/third opinion that a worker cannot hand-wave past.
The three rules that make it work
- Biased. Each reviewer holds ONE prior and judges only that lens. Never ask one agent for "overall quality" - that collapses back to a single opinion.
- Independent. Reviewers must not see each other's verdicts. Spawn them together so they run in parallel isolation; never feed one reviewer another's output.
- Sealed. Each reviewer ends with exactly one parseable line:
REVIEW: approve|reject|comment - <reason>. Arejectmust cite concrete evidence (a rule clause, a named smell, a missing test), not a vibe.
1. Get the candidate diff
collect_run(candidate_run_id) returns the diff + changed files, read-only. This exact diff is what
every reviewer judges.
2. Spawn the reviewers (no-peek, read-only)
Route to read-only clients and put the SAME diff + the lens rubric + the required verdict line into
each reviewer's goal. Run them together with one call so they cannot see each other:
run_many([
{client: <read-only client>, goal: ARCHITECT_RUBRIC + diff, task_id: "review.<candidate>"},
{client: <read-only client>, goal: QUALITY_RUBRIC + diff, task_id: "review.<candidate>"},
{client: <read-only client>, goal: TESTS_RUBRIC + diff, task_id: "review.<candidate>"},
])
Share one task_id across the three so usage() / report() group the review as one unit. The
lenses:
- architect - does it honor the project's rules and scope? Prefer deletion. Reject only with a cited rule/clause, never "architectural smell."
- quality - readability, naming intent, dead code, over/under-engineering, function size.
- tests - real behaviour tests on the changed lines; no skipped tests, no loosened assertions, no mock-only pseudo-coverage.
3. Parse the verdicts
From each run's text take the single REVIEW: line -> approve / reject / comment. A run you
cannot parse, or that failed / timed_out, is not an approval: re-spawn it. Never count a
missing verdict as approve.
4. Apply the truth table
With all three reviewers present and judging the same diff:
| verdicts | decision |
|---|---|
any reject | FIX (go to step 5) |
| 0 reject, 0 approve (all comment) | HOLD - needs your explicit approval; comments alone don't merge |
| 0 reject, >= 1 approve | INTEGRATE (go to step 6) |
A reject is blocking; a comment is advisory. Do not let advisory comments block a merge, and do
not let an all-comment result auto-merge.
5. Fix loop (on any reject)
Dispatch ONE fix task that addresses only the reject reasons (quote them verbatim in the goal); comments are optional context, not demands. Then re-run the gate on the new candidate run. Cap at ~2-3 fix rounds. Stop and bring it to a human if the same reason rejects twice, or if two reviewers demand opposite changes (a conflict no fix can satisfy) - never loop forever.
6. Integrate
Only after INTEGRATE: integrate(candidate_run_id, cleanup?), one run at a time. See
marshal-orchestrate step 5 for handling merged / conflict / blocked / empty / error.
Worktree isolation means main is untouched until this step.
Invariants to respect
- Reviewers are headless - the rubric + diff in the prompt is all they get; no questions are possible.
- Independent means parallel + isolated; never show a reviewer a peer's verdict.
- A reject needs cited evidence; a comment never blocks; an all-comment result never auto-merges.
succeededis notcorrect; integrate one run at a time; main is untouched until integrate.