Repository navigation
/premerge output is unmergeable by /merge — add a distinct premerge trust tier #716
Description
The gap
/premerge always produces a PR that /merge structurally cannot land.
Hit live on stack PR #681. /merge gate-failed with trust_trail_label_missing:
- PATH_1 —
reviewDecisionis empty; no GitHub approval. - PATH_2 — no
uberdev-approvedlabel, noReviewed-by: uberdev/review-pr@<sha>trailer.
Neither is an accident. tests/premerge.test.sh P10 asserts both absences over
commands/premerge.md and skills/premerge-pipeline/SKILL.md. So every /premerge
run ends at a parked PR whose only landing paths are a manual CLI merge or a
hand-forged trail — and the manual path is itself blocked by the PreToolUse deny
hook that ~/.claude/CLAUDE.md §8 relies on.
P10 is correct — do NOT "fix" this by emitting the existing trail
The trailer reads Reviewed-by: uberdev/review-pr@<sha>. Under /premerge,
/review-pr did not run. The review was the built-in code-review skill, which is
a different instrument:
- no seven-lens
post-impl-reviewfanout - no Phase 3 CI-health phase
- no verify pass at all on Opus-family
xhigh(every finding arrives withverdictabsent) - no Phase 2.5
findings-to-issueshalt semantics - no
review-pr-verdict.jsonin the shapetrust-trail-evaluatorparses
/goal reads the same signal for convergence, so a forged trail would not merely
mislead /merge — it would tell /goal a stack converged when nothing verified it.
Proposed: a distinct tier, not a borrowed one
| today | proposed | |
|---|---|---|
| label | — | premerge-approved |
| trailer | — | Reviewed-by: uberdev/premerge@<sha> |
| evidence | .uberdev/premerge/<RUN_ID>/ already written |
trail points at gate-NN.json |
/merge |
no path accepts it | PATH_3, gated on VERDICT=green |
Most of the evidence already exists: manifest.json, classified.json, and — as of
#680 — per-attempt gate-NN.json carrying the clean-gate verdict and its reasons.
What is missing is the emission and a /merge path that reads it.
Acceptance criteria
/premergeemits the trail only when its Phase 3 clean gate returnedRC=0.
Anot_greenrun emits nothing and parks exactly as today.- The trailer prefix is
uberdev/premerge@, neveruberdev/review-pr@. The label is
premerge-approved, neveruberdev-approved. P10 is retargeted to assert the
review-pr forms stay absent, not that no trail exists — it must still catch a
regression that forges the review-pr tier. /mergegains PATH_3 with its ownTRUST_ANCHOR_ENUMmember
(e.g.uberdev_premerge_trail) so audit consumers can tell the tiers apart. PATH_3
re-readsgate-NN.jsonand refuses on anything but a green verdict — the label and
trailer alone must never be sufficient, because they are bare literals with no
payload (the same reasoning behind theReviewed-base:endpoint in fix(review-pr): trust trail records no base identity, so a GREEN survives a base swap #440).- The trail is SHA-bound like PATH_2's: a push after the gate invalidates it.
/goalis unaffected — it owns its own review→merge loop and must not start
accepting a premerge trail as convergence evidence.
What this does NOT solve
It would not have landed #681. A correct PATH_3 is gated on a green clean gate, and
that stack's gate was not_green with 10 open blockers (#701–#715). A properly
implemented premerge tier would have refused it exactly as PATH_2 did. The blocker
there was that the stack was not clean, not that the trail was missing — this issue
closes a structural dead-end, it does not make unclean stacks landable.
Ordering
Implementable cleanly only after #681 lands, since the gate-NN.json artifact the
trail points at arrives with #680.
Steer (2026-08-24) — relax the gate, do not only add a parallel tier
This supersedes the emphasis of "Proposed: a distinct tier, not a borrowed one" above.
The proposal there is additive: PATH_1 and PATH_2 keep their current strictness and a
fourth instrument would need a fourth path. The direction to implement is that the
strictness itself is what shrinks — a generalised gate, which is the same work as
PATH_3 done once instead of once per instrument.
PATH_2 today has sub-conditions (a), (a.5), (b), (b.5), (c) and (d) — label,
release-anchor resolution, trailer, base-identity equivalence, an agent-dispatched
verdict, and audit-JSON shape — plus eight trust-resolution members in
GATE_FAIL_REASON_ENUM. Every one is a way for a genuinely-reviewed stack to be refused.
Three moves:
-
Make the trail's instrument a field, not a hardcoded prefix.
Reviewed-by: uberdev/<command>@<sha>with the command as data, and one label family,
instead of^Reviewed-by: uberdev/review-pr@([a-f0-9]{40})$matched by literal regex and
uberdev-approvedmatched as a bare string./premergethen emits the same artifact
shape with a different value.tests/premerge.test.shP10 keeps its job: assert
/premergenever claims thereview-prinstrument. -
Keep every structural check that answers "is this evidence about THIS code."
Head-SHA binding, base-identity equivalence (fix(review-pr): trust trail records no base identity, so a GREEN survives a base swap #440), force-push detection. Those are not
strictness about which reviewer ran; they are the entire reason a trail means anything,
and relaxing them is out of scope. -
Drop or soften the checks that only encode "review-pr's own phases ran."
The Phase 2.5 halt/override semantics,review-pr-verdict.json's exact shape, and the
--accept-blocker-deferred/--accept-critical-deferred/--i-know-what-im-doing
triad./premergehas equivalent evidence of its own —gate-NN.json,manifest.json,
classified.json— and no way to produce those.
Invariant that survives either design: /merge still never merges without the
operator's explicit command for that specific PR. Relaxing the trust gate changes which
PRs are eligible, never who pulls the trigger. Do not add an auto-merge path, an
auto-chain from /premerge, or a flag that implies one.
Ordering note above is stale. #681 and #680 have landed; gate-NN.json exists.
Siblings: #788 (severity invisible on premerge output), #789 (default review level),
#790 (folded originals stay live discovery candidates), #785 (the simplify phase never runs).
Activity
TheFJK commentedon Aug 24, 2026
Steer from @TheFJK:
/mergeshould accept/premergeoutput — relax the strict trust trail rather than only adding a parallel tier.That changes the emphasis of this issue's proposal, so writing the difference down:
premerge-approved+Reviewed-by: uberdev/premerge@<sha>+ PATH_3 gated onVERDICT=green). That is additive: PATH_1 and PATH_2 keep their current strictness and a fourth instrument would need a fourth path./merge'sGATE_FAIL_REASON_ENUMcurrently carries eight trust-resolution reasons and PATH_2 alone has sub-conditions (a), (a.5), (b), (b.5), (c) and (d) — label, release-anchor resolution, trailer, base-identity equivalence, an agent-dispatched verdict, and audit-JSON shape. Every one of those is a way for a genuinely-reviewed stack to be refused.The two are not incompatible — a generalised gate is the same work as PATH_3, done once instead of per-instrument. Concretely:
Reviewed-by: uberdev/<command>@<sha>with the command as data, and one label family, instead ofuberdev/review-pr@matched by a literal regex anduberdev-approvedmatched as a bare string. Then/premergeemits the same artifact shape with a different value, and P10 keeps its job — asserting/premergenever claims thereview-prinstrument.review-pr-verdict.json's exact shape, the--accept-blocker-deferred/--accept-critical-deferred/--i-know-what-im-doingtriad./premergehas its own equivalent evidence (gate-NN.json,manifest.json,classified.json) and no way to produce those.The invariant that must survive either way:
/mergestill never merges without my explicit command for that specific PR. Relaxing the trust gate is about which PRs are eligible, never about who pulls the trigger.Related: #788 (severity is invisible on premerge output), #789 (default review level), #790 (folded originals stay live candidates), #785 (the simplify phase never runs).
TheFJK commentedon Aug 24, 2026
uberdev:active — claimed for /solve or /turbo dispatch
User: @TheFJK
Host: MacBook-Pro
Branch: worktree-solve-issue-716
Tier: medium
Started: 2026-08-24T14:40:16Z
Auto-clears on /merge or issue close. If this claim is stale, override with:
/turbo 716 --force (or /solve 716 --force)
TheFJK commentedon Aug 24, 2026
Parked behind #792 (12 commits, branch
worktree-turbox-issue-716).The PR says
Refs #716, notCloses— deliberately. Two blockers survive and both mean a/premergestack PR is still unlandable on the default path, which is the condition this issue exists to remove:5aburies the trail. The anchor is emitted at5-trail, thenchore(release)lands on top./mergecan only read a trailer under a release commit whenrelease-anchor.shreturnstolerated; measured against real history it returnsnoneon six for six of this repo's release commits (2c6db441→diff_too_large, others →changelog_shape/non_version_paths). Emitting after the bump is foreclosed too — the gate's HEAD binding returnshead_moved.--accept-critical-deferred, whenever the audit JSON is not locally discoverable. Orthogonal to this issue's scope; it arrived bundled with the instrument generalisation.Eight further contract-drift findings are listed in the PR body. All ten ship unfixed — the fix round could not be dispatched because the review budget hit its ceiling, partly because a session-limit outage charged five review seats twice.
Everything else verified: full 132-row authoritative suite green, the new gate/emit fences extracted and executed under bash and zsh rather than transcribed, and every new assertion mutation-tested to confirm it can actually fail.