Address review: check relay first, and pin the scope primitives where they live
Commit

Five findings from review, none a correctness bug. All five stand.
Relay capability is now checked before the empty-source intercept rather than after planning. The converged result claims Relay: true on the grounds that "replicate refuses a non-relay target outright" — but the intercept returned above that check, so a target whose receive-pack advertisement carries no capabilities got a success asserting a relay the ordinary path would have refused. Nothing moves either way, so convergence is arguably still the right answer, but the field was fabricated in a change whose subject is honest reporting. s.target.policy is populated in newSession, so the check simply moves up.
ProbeResult.SourceHeadUnborn makes a claim in the PR body true rather than
restating it. The body defended sending unborn on every v2 ls-refs partly
because it lets Probe report an unborn HEAD without a convergence policy —
which was not implemented: HeadUnborn had exactly one reader, behind
AllowEmptySource. Probe is the diagnostic surface and the argument is already
on the wire, so an operator asking why a mirror will not converge can now see
whether the source reported unborn at all. Diagnostic only; it carries none of
the policy's weight, and its doc says so.
TargetScope and PruneTarget get planner-local tests. Their semantics were wrong in three consecutive commits and PruneTarget now sits on the live prune path, yet every assertion about them lived in internal/syncer. The table covers mapping target, excluded-but-mapped, short-form mapping names, tags under both AllRefs and IncludeTags, other-kind under AllRefs, unmapped branches with and without mappings, both exclusion forms, prune disabled, and an un-normalized Branches filter — plus the PruneTarget-is-narrower property whose conflation caused two of the three regressions.
Manages' doc no longer promises to track cfg.Prune. It deliberately does not: the question is whose ref this is, not what this run would do to it, and "source empty, target holds refs" is divergence whether or not this run would have pruned. Behaviour unchanged; the doc was overpromising.
buildProbeConfig joins the reflection guard. It is the one request-edge builder outside it, and while ProbeRequest has no ExcludeRefs today, it is exactly where that bug class could recur unseen. CollectStats reaches syncer.Config as ShowStats, so it is skipped with a reason and covered by its own assertion — which also makes the previously-dead skip branch live.
Finally, resolveEmptyDesiredSet's doc no longer implies both entry points are live. The pre-planning intercept means its delegation is currently unreachable; it is kept so the paths cannot drift if that gate is loosened, and now says so.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01JvpGRBapBppY4xh2x38kDL Entire-Checkpoint: 01M0SHVJW7ZX6S8F925MKM07TM