# A destination capture any async continuation can use

**Status: revision 7. The mechanism is implemented at `0efc8c0`;
revisions 6 and 7 carry an OPEN correctness blocker that is NOT yet
implemented.**

*(Revisions 2–5 said "Pre-implementation. Awaiting approval" while the
ledger recorded the lane as approved and implemented. Same
contradiction class this document keeps correcting elsewhere, left
standing in its own header.)*

**Revision 7 replaces revision 6's fix, which was unsound for the same
reason revision 6's target was.** Revision 6 moved the panel/document
decision to a **preflight prediction**, arguing nothing could change
before placement because the body cannot `await`. The await refusal
stops *concurrent interleaving*; it does not stop the body — arbitrary
synchronous Lua — from dedicating the side slot itself and causing the
very fallback the preflight just ruled out. **Enforcement moves to the
placement boundary**, where the fallback is a fact rather than a
forecast, and §7 gains the inside-the-body test that the two
pre-established-state tests could never catch.

**Revision 6 fixes an UNSOUND matrix, not a preference.** Q#DC-2 gave
the panel profile only check 1, on the claim that a panel result never
touches a document window. **Panel placement falls back to an ordinary
document window** when the frontend is not panel-capable or its side
slot is dedicated — so a `"panel"` commit could replace a *newer*
document while skipping every stale-intent guard. Reproduced in review.
The relaxation is now conditional on the placement really being a
panel. Revision 6 also closes an invalid-UTF-8 hole in the profile
diagnostic — the same reachability class as revision 5's, one layer
down.

**Revision 5 fixes a binding-level contradiction in revision 4's own
API spec.** It required `profile: Option<String>` *and* a pointed error
naming the accepted values for a non-string — but mlua rejects a
number or table during argument conversion, before the closure runs, so
that message was unreachable. This is the exact trap the existing
binding documents for `dest`, in a comment revision 4 quoted while
repeating the mistake one argument to the right. The profile is now
`mlua::Value`, validated in the body, with `nil` and absence both
meaning `"document"`.

**Revision 4 specifies the call shape the last two revisions kept
referring to without defining.** "The profile is declared at
`commit_to`" named no signature, no value set, no invalid-profile
behaviour, and nothing about the existing two-argument callers — so
#227 had no stable API to adopt and the Journey preservation promise
rested on care rather than contract. Q#DC-5 fixes that:
`commit_to(dest, body [, profile])`, a **closed** two-value set,
**omitted means `"document"`** so every existing call keeps all four
preflight checks by definition, and an unrecognized profile **errors**
rather than falling back.

**Revision 3 decides Q#DC-4, which revision 2 left contradicting
Q#DC-2 — on the primary panel API.** Q#DC-2 concluded a panel needs
only a live frontend; Q#DC-4 still returned `nil` without a document
window and told git to fall back to ambient behaviour, which is the
very bug this lane removes. Resolved: the destination's document pair
is **optional**, `capture_destination()` is **profile-blind and
argument-free**, the profile is declared at `commit_to`, and a
document-profile commit without a document pair is refused. §4 and
Q#DC-1 were updated to match rather than left to disagree.

**Revision 2 takes three review findings.** Q#DC-2's parameterization
was **incomplete** — a panel result does not depend on the captured
document window being live or non-dedicated either, not just on its
buffer, so the question now carries a full **preflight matrix** with
every omission testable. `tests/journey_acceptance.rs` joins dired as a
named **preservation suite and stop signal**; it carries the
`commit_to` scope, forged-userdata, preflight and restoration pins this
lane generalizes, and Journey Stage 1a's framing treats it as a
required gate. And **§5 (coherence impact) was missing entirely**,
which `CLAUDE.md` and `COHERENCE.md` §25 both require of
coherence-affecting work — this lane adds Lua API surface and
generalizes a Journey substrate, so it qualifies twice over.

**A prerequisite lane. PR #227 (git Stage 1) blocks on it**, and its
P1a review finding is the reason this exists.

---

## 1. Why, and why as its own lane

PR #227's review found that git's async completions mutate and display
UI without capturing the initiating frontend
(`builtin/runtime/git.lua:609`, `:854`), so a result can surface in
whichever frontend happens to be active when git exits. Run
`git.status` in frontend A, let frontend B become active, and A's panel
opens in B.

**The finding named the right mechanism.** `pmacs.window.commit_to`
exists for exactly this continuation boundary: Journey Stage 1a's
Q#JR14 built it because "the listing settles a tick or more later, and
by then the ambient frontend, selected window, and active buffer may
all name something else" (`src/editor.rs:1238-1240`).

**But it is not reachable from Lua outside one path**, which is why
this is a lane and not a line in #227:

- `commit_to` takes a `DirectoryDestinationLua`, **nonconstructible
  from Lua** by deliberate design (`src/lua_bindings/mod.rs:4256`) —
  userdata with no constructor and no setters, so a caller cannot
  fabricate a plausible triple.
- The only site that mints one is inside the `path.open-directory`
  listener dispatch (`src/editor.rs:1311`), from
  `capture_directory_destination`, which is `pub(crate)`
  (`src/editor.rs:1241`).

So any async Lua continuation that is **not** a directory open has no
way to say where its result belongs. Git is the first to need it; it
will not be the last.

Landing this inside #227 would put new Lua API surface, over another
lane's merged mechanism, inside a feature branch — the same folding
that was declined for the `scripts/gate` repair, for the same reason.

## 2. Ground truth

- **The captured data is already generic.**
  `DirectoryDestination { frontend, window, buffer }`
  (`src/editor_core.rs:159-166`) contains nothing directory-specific.
  Only its **name** and its **capture site** are.
- **The blast radius of a rename is small**: 8 references across 4
  files (`editor_core.rs`, `editor.rs`, `lua_bindings/mod.rs`,
  `lua_bindings/window_panel.rs`). Checked, not estimated.
- **`commit_to`'s preflight is four checks**
  (`src/lua_bindings/window_panel.rs:488-525`), in order: the
  requesting frontend still has a layout; the destination window is
  still live in it; **the window still shows the captured buffer**
  (Q#JR14c stale intent); and the window is not dedicated (Q#JR14f).
- **`Handle:await` refuses inside a commit scope**
  (`builtin/runtime/async.lua:87-90`) — yielding would restore the
  scope while the coroutine is still parked. Any adopter awaits
  *before* committing, as dired does.
- **Git's two continuations do not have the same shape**, and this is
  the finding that shapes the design:
  - `*git-status*` goes through `listview.open`, which resolves
    `display` with a **`"panel"`** default
    (`builtin/runtime/listview.lua:550`). It lands in the bottom
    panel, **not** in a document window.
  - `*git-diff*` calls `pmacs.window.display(buf, { select = true })`
    — the **document** target, deliberately, "so the status panel it
    was invoked from stays visible beside it"
    (`builtin/runtime/git.lua:852-854`).

## 3. The tension this lane has to resolve

`DirectoryDestination.buffer` exists for one purpose, stated at its
definition: *"what that window held at capture time, so **stale intent
loses to the user**"* — a user who replaced the buffer while work was
in flight is newer information than the request.

**That predicate is right for a document replacement and wrong for a
panel.** The git status panel does not replace the captured window's
buffer; it opens in the bottom panel beside it. Refusing to show it
because the user switched files in the document window would be a
refusal with no relationship to what the continuation actually does —
the panel case would inherit a check about a window it never touches.

Meanwhile the diff case *is* a document replacement, and wants exactly
the dired semantics.

So a single one-size destination either **over-refuses** the panel case
or **under-checks** the document case. Q#DC-2 is where that gets
decided, and it is the substance of this lane.

## 4. The change, in outline

- **A Lua-reachable capture**, returning the same nonconstructible
  userdata for the *current* frontend, **with** its document window and
  buffer when it has one and without them when it does not (Q#DC-4).
  The capture takes no arguments and is profile-blind; the profile is
  declared at `commit_to`.
- **Generic naming.** `DirectoryDestination` becomes something that
  does not lie about a git panel; `capture_directory_destination` and
  the userdata type follow. 8 references (§2).
- **The directory path keeps behaving exactly as it does today** — this
  lane generalizes the capture, it does not change Journey Stage 1a's
  semantics.
- **No adopter in this lane.** Git's adoption is #227's, after this
  lands. A prerequisite that also converts its first consumer makes the
  two impossible to review separately.

## 5. Coherence impact (§20)

**Revision 1 omitted this section entirely, and it is required.**
`CLAUDE.md` and `COHERENCE.md` §25 both say a framing for
coherence-affecting work must cite the section it serves and state its
impact — and this lane adds **new Lua API surface** and generalizes a
Journey-substrate mechanism, which is coherence-affecting on both
counts. Recording the impacts as neutral where they are neutral is part
of the requirement, not a way around it.

- **§16 semantic frontend — the section this serves.** The defect it
  removes is a continuation resolving its target from *ambient* state a
  tick after the request, which is precisely the multi-frontend
  correctness §16 exists to protect. A capture makes "which frontend
  asked" a value rather than a guess.
- **§14 workbench primitives — indirect, and the honest framing is
  *enabling*.** This does not add a primitive. It removes the reason an
  async adopter would hand-roll frontend tracking, which is the
  mechanism by which primitives acquire per-consumer idiosyncrasies.
- **Journey steps touched: none directly, one PROTECTED.** The golden
  journey does not gain a step. But Journey Stage 1a's Q#JR14 substrate
  is what this generalizes, and §7 makes `tests/journey_acceptance.rs`
  a preservation suite precisely so a generalization cannot erode the
  step it came from.
- **Interaction islands (§6): none added.** No key interception, no
  dispatch precedence rung. `dispatch_key` is untouched.
- **Config registry: no setting.** Where a continuation lands is a
  correctness property, not a preference, and a toggle would offer to
  turn correctness off.
- **Background-work attribution (§9): NEUTRAL, and worth stating
  precisely rather than skipping.** This lane adds no background work
  and no new unattributable surface. It also does **not** improve §9 —
  knowing which frontend a result belongs to is not knowing who asked
  for it or why. That is the worker-identity lane's arc, and the two
  should not be confused because both concern async continuations.
- **§10 extension trust — a small positive.** The capture keeps the
  Q#JR14d property that a destination is **nonconstructible from Lua**,
  so generalizing the mechanism does not widen what extension code can
  fabricate. §7 re-asserts the forged-destination refusal after the
  rename for exactly this reason.

## 6. Open questions

### Q#DC-1 — what does the capture take as arguments?

*My vote: **no arguments** — capture the acting frontend and its
document window from the ambient state at call time.* That is what the
existing `capture_directory_destination(frontend, window)` is handed by
its one caller, and a Lua-supplied frontend id would reintroduce the
fabrication hole the userdata design closes.

### Q#DC-2 — one destination shape, or a panel/document distinction? **(the substantive one)**

§3 is the problem. Three candidates:

1. **One shape, all four checks.** Simplest; over-refuses the panel
   case, and the refusal reason would be about a window the panel does
   not touch.
2. **One shape, preflight parameterized by the continuation** — the
   caller declares whether it is replacing the captured window's
   buffer, and the stale-intent check applies only then.
3. **Two capture kinds**, document and panel, with different preflights.

*My vote: **(2)***, with the profiles spelled out below rather than
left to implementation.

**Revision 1 said only "skip the stale-buffer check for a non-replacing
continuation", and that was incomplete.** Review is right: a panel
result does not depend on the captured **document window** at all. It
does not replace that window's buffer, so check 3 is irrelevant; it
does not occupy that window, so check 4 (dedicated) is irrelevant; and
it does not need that specific window to exist, so check 2 is
irrelevant. Retaining any of the three can reject `git.status` for a
document-window change that has nothing to do with where the panel
goes. But dropping them **without an explicit profile** is how document
replacement quietly loses its guarantees.

**The matrix, stated so every omission is deliberate and testable:**

| # | Precondition (`window_panel.rs:488-525`) | Document replacement | Frontend/panel scope |
|---|---|---|---|
| 1 | Requesting frontend still has a layout | **required** | **required** |
| 2 | Destination window still live in it | **required** | not applicable |
| 3 | Window still shows the captured buffer (Q#JR14c stale intent) | **required** | not applicable |
| 4 | Window is not dedicated (Q#JR14f) | **required** | not applicable |

**Check 1 is the entire panel profile ONLY WHEN THE PLACEMENT REALLY IS
A PANEL — revision 5's matrix was unsound, and this is the correction.**

The matrix rested on "the panel never touches the captured window's
buffer". **That is false when panel placement falls back.**
`editor_core.rs:4138-4148` says so in its own comment: *"Reaching
`Ordinary` while a side was REQUESTED means the request fell back (not
panel-capable, or the one slot is dedicated elsewhere)"* — and the
result is then installed into an ordinary **document** window. So a
`"panel"` commit on a non-panel-capable frontend replaces a document
view while skipping every check that exists to stop it replacing a
*newer* one. That reintroduces exactly the stale-intent failure the
API was built to prevent, which makes it a correctness defect and not
a strictness preference.

**The rule, restated:** the panel profile's relaxation is conditional
on the placement actually being a panel. Whenever placement **can**
fall back to a document window, the panel profile runs the **full
document preflight**.

**ENFORCEMENT IS AT THE PLACEMENT BOUNDARY, NOT AT PREFLIGHT —
revision 6 got this wrong too, and the reason is worth stating because
it is a whole class of mistake.**

Revision 6 said the two fallback causes are "predictable at preflight",
because `commit_to` refuses `await` so "nothing can change between
preflight and placement". **The await refusal prevents *concurrent
interleaving* — another coroutine mutating state while this one is
parked. It says nothing about the body itself**, which is arbitrary
Lua running synchronously and perfectly able to change the state the
preflight just measured:

> obtain the existing panel → set it `dedicated = true` → request panel
> display

Preflight sees a reusable panel and relaxes checks 2–4; the body then
causes the fallback; the result replaces a stale document. **No
preflight predicate can close this**, however it is phrased — the
measurement is simply taken before the thing it measures is decided.

**So the check moves to where the fact is known.** Placement resolving
to `PlacementKind::Ordinary` for a request that asked for a side *is*
the fallback (`editor_core.rs:4138-4148`). At that point, under an
active panel-profile commit, the document preconditions are evaluated
against the captured destination and refused if they fail. The commit
scope is already Rust-side app data (`CommitScopeActive`), so the
profile and the destination can ride there for the placement path to
consult.

**And the tempting non-fix, named so nobody reaches for it:** widening
the preflight predicate from "will it fall back" to "*could* it ever".
Since the body can always dedicate the side slot, that predicate is
always true, the panel profile collapses into the document profile, and
the parameterization buys nothing. If collapsing them is genuinely
right, that is a design decision needing its own approval — not a way
to make a broken predicate safe.

**What is NOT the fix: refusing a panel commit that would fall back.**
Falling back to an ordinary window is existing, deliberate behaviour
for a frontend without panel capability; refusing would turn a
graceful degradation into an error and regress consumers that work
today. The panel profile relaxes checks; it does not get to change
where things land.

**Consequence for the capture, which follows and should not be
discovered later:** if the panel profile needs only the frontend, then
a frontend with **no document window** can still host a panel — so
Q#DC-4's "return `nil`" is right for the document profile and possibly
wrong for the panel one. That interaction is settled as part of
answering this, not after it.

**I hold the *choice* loosely, not the matrix.** (1) has a real
argument — a uniform rule is easier to reason about, and over-refusal
is safe — but it would refuse the git panel for reasons unrelated to
it, and "safe" refusals that users cannot explain are how a mechanism
gets worked around. If review prefers (1) or (3), the matrix above is
what changes, and **every cell marked "not applicable" must still be
tested as deliberately omitted** (§7) so a future reader cannot mistake
an omission for an oversight.

### Q#DC-3 — what is the type called?

*My vote: **`ViewDestination`***, with `pmacs.window.capture_destination()`
as the Lua entry point. It names what it is — a place in a view where a
continuation's result belongs — without claiming a directory or a
buffer kind.

The Q#JR14 doc comments should keep their references intact; a rename
that orphans the rationale is worse than a slightly stale name.

### Q#DC-5 — the exact Lua call shape for the profile **(new in rev 4)**

Revisions 2 and 3 said "the profile is declared at `commit_to`" and
never said **how**. That is not a detail: today's binding accepts
exactly `(dest, body)` (`window_panel.rs:453-456`), so without a
specified form #227 has no stable API to adopt against, and the
promise that existing callers keep their semantics is a hope rather
than a contract.

**The signature:**

```lua
pmacs.window.commit_to(dest, body)             -- document profile
pmacs.window.commit_to(dest, body, "panel")    -- panel profile
```

- **`profile` is an OPTIONAL THIRD argument, typed `mlua::Value` at
  the binding — NOT `Option<String>`.**

  **Revision 4 said `Option<String>` and that contradicted its own
  error requirement.** mlua rejects a number or table *during argument
  conversion*, before the closure body runs, so the promised message
  naming `"document"` and `"panel"` would be **unreachable** — a caller
  passing `42` would get mlua's generic conversion error instead. This
  is the identical trap the existing binding already documented for
  `dest`, in a comment revision 4 cited while making the same mistake
  one argument to the right:

  > Typed as `Value` rather than `AnyUserData` so this message is
  > REACHABLE: with the narrower type mlua rejects a table during
  > argument conversion, and a caller who fabricated one got "error
  > converting Lua table to userdata" — true, but it names neither the
  > rule nor how to get a real destination.

  So: accept `Value`, and validate in the body.
  - **`Nil` or absent → `"document"`.** Both spellings, since
    `commit_to(dest, body, nil)` is what a Lua caller threading an
    optional variable produces, and it must not be a third behaviour.
  - **`String` → must be `"document"` or `"panel"`**, else refused,
    naming both accepted values.
  - **Anything else → refused by the SAME message**, which now names
    the accepted values *and* says a string was expected. That message
    only exists if the type is `Value`.
- No arity sniffing and no table-or-function dispatch on argument 2 —
  a polymorphic second argument would put the *destination*'s error
  message back at risk, which is what that comment was protecting.
- **Trailing, and readable in practice.** A profile after a long inline
  closure would read badly, but that is not the call shape in use:
  dired defines `local function commit() … end` and calls
  `commit_to(opts.dest, commit)` (`builtin/runtime/dired.lua:670,717`).
  Against a named body, `commit_to(dest, commit, "panel")` reads fine.
- **The value set is CLOSED: `"document"` and `"panel"`.** Exactly the
  two profiles in Q#DC-2's matrix. Not an open string namespace — a
  third profile is a decision, not a spelling.
- **Omitted means `"document"`.** This is the load-bearing part: every
  existing `commit_to(dest, fn)` call keeps **all four** preflight
  checks, unchanged, by definition of the signature. `journey_acceptance`
  passing untouched (§7) then follows from the API shape rather than
  from care.
- **An unrecognized profile is an ERROR**, naming the accepted values —
  **not** a silent fall back to `"document"`. A fallback would hand a
  caller stricter or looser checks than it asked for, which is the
  failure mode the whole parameterization exists to prevent. A
  non-string profile errors the same way.

**Which profile each of git's continuations takes**, so #227's adoption
is decided here rather than rediscovered: `*git-status*` → **panel**
(it lands in the bottom panel, `listview.lua:550`); `*git-diff*` →
**document** (it replaces a document window deliberately,
`git.lua:852-854`).

### Q#DC-4 — what happens when there is no document window? **(DECIDED in rev 3)**

**Revision 2 left this contradicting Q#DC-2 and it is the primary panel
API, so it is decided here rather than voted on.** Q#DC-2 concluded a
panel profile depends only on a live frontend — so it can commit with
no document window at all — while this question still said the capture
returns `nil` in exactly that case, and told git to fall back to
ambient behaviour. Those cannot both hold, and the fallback advice was
independently wrong: falling back to ambient **is** the P1a bug this
lane exists to remove.

**The decision:**

- **`ViewDestination { frontend, window: Option<WindowId>, buffer:
  Option<BufferId> }`.** The frontend is always present; the document
  pair is optional and absent exactly when the frontend has no document
  window.
- **`capture_destination()` is NOT profile-aware and takes no
  arguments.** It records what is there. Making capture profile-aware
  would force the caller to know at *capture* time what it will do at
  *commit* time, which is the opposite of why capture exists — the
  whole point is to freeze the truth early and decide later.
- **The profile is declared at `commit_to`**, which is where Q#DC-2's
  parameterization already lives. One place makes the decision, and it
  is the place that knows. **Its exact call shape is Q#DC-5**, which
  revisions 2 and 3 left unspecified.
- **A document-profile commit on a destination with no document pair is
  REFUSED**, with a reason naming that, joining the four preflight
  refusals rather than being a separate failure mode.
- **Capture therefore never returns `nil`** while a frontend exists,
  and the "adopter degrades to ambient" advice is **withdrawn**. An
  adopter with nowhere to land gets a refusal it can report; it does
  not get permission to guess.

**What this changes elsewhere, so the decision does not sit alone:**
§4's outline says the capture returns userdata "for the *current*
frontend and its document window" — it returns one for the current
frontend, **with** its document window when there is one. Q#DC-1's "no
arguments" answer is unchanged and now load-bearing rather than
incidental: no arguments is what keeps capture profile-blind.

## 7. Verification

- **A captured destination survives a frontend switch**: capture in A,
  make B active, commit, and assert the result lands in **A**. This is
  P1a's actual failure and the reason the lane exists — asserting only
  that the API returns userdata would pass on a capture that does
  nothing.
- **A fabricated destination is still refused** — the existing Q#JR14d
  guarantee, re-asserted after the rename so the generalization cannot
  quietly open the hole it was built to close.
- **Every preflight refusal is witnessed by its own case, in BOTH
  profiles** (Q#DC-2's matrix): frontend gone, window gone, stale
  buffer, dedicated window — each asserted to **refuse** under the
  document profile, and each of the three marked "not applicable"
  asserted to **NOT refuse** under the panel profile. A deliberately
  omitted check that has no test is indistinguishable from a check
  someone forgot, and the next reader will restore it.
- **A legacy two-argument `commit_to(dest, body)` gets the DOCUMENT
  profile** (Q#DC-5), witnessed by a check the panel profile omits —
  a stale-buffer refusal. Asserting merely that it does not error would
  pass on a call silently downgraded to the panel profile, which is the
  regression that would quietly void Journey Stage 1a's guarantees.
- **A `"panel"` commit that FALLS BACK to a document window is checked
  against the document preconditions**, witnessed for **both** causes
  separately — a non-panel-capable frontend, and a dedicated side slot.
  Each asserts the stale-intent refusal fires: capture A, make B newer,
  commit `"panel"`, observe the refusal rather than B being replaced.
- **THE FALLBACK STATE IS ALSO ESTABLISHED FROM INSIDE THE BODY**, in
  its own test: the callback dedicates the side slot **mid-commit** and
  then requests panel display. This is the case that distinguishes
  placement-time enforcement from preflight prediction, and **the two
  bullets above cannot catch it** — both set up their fallback state
  *before* `commit_to` is entered, so a preflight-snapshot design
  passes them. A design that passes only those two has not been shown
  to work.
- **A `"panel"` commit that really lands in the panel still skips
  checks 2–4** — otherwise the fix has quietly collapsed the two
  profiles into one and the parameterization buys nothing.
- **An unrecognized profile string is REFUSED**, with a message naming
  the accepted values — not silently treated as `"document"`.
- **An invalid-UTF-8 profile is refused by that SAME message.** Lua
  strings are byte strings, so a `string.char(255)` profile reaches
  `to_str()` and produces mlua's generic conversion error *before*
  the documented message is ever constructed — the same reachability
  class as the `Option<String>` defect, one layer deeper. Compare
  bytes, or map the conversion failure onto the message; asserted on
  content, in the bad-profile matrix beside the number and table rows.
- **A non-string profile (a number, a table) is refused by that SAME
  message**, asserted **on its content**, not merely that an error
  occurred. This is the bullet that fails if the argument is ever
  retyped to `Option<String>`: mlua would reject the value during
  conversion and the assertion on the message would stop matching. The
  test is therefore the guard on the type choice, not just on the
  behaviour.
- **An explicit `nil` profile takes the document profile**, identical
  to omitting it — witnessed separately, because a Lua caller threading
  an optional variable produces `nil` rather than absence, and a third
  behaviour there would be invisible until someone hit it.
- **Capture SUCCEEDS with no document window** (Q#DC-4), returning a
  destination whose document pair is absent — asserted as a successful
  capture, not as `nil`.
- **A panel-profile commit on that destination SUCCEEDS**, and a
  **document-profile commit on it is REFUSED** with a reason naming the
  missing document window. Both halves, because asserting only the
  refusal would pass on a capture that refuses everything.
- **The directory path is unchanged** — dired's existing acceptance
  coverage passes untouched.
- **`tests/journey_acceptance.rs` passes UNCHANGED**, as a named
  preservation suite. It carries the established contract this lane
  generalizes — 27 `commit_to` references across nine named pins
  including `commit_to_refuses_a_forged_destination`,
  `commit_to_scopes_and_restores_on_a_normal_return`,
  `commit_to_restores_when_the_callback_raises`,
  `commit_to_refuses_an_await_and_restores`,
  `commit_to_delivers_to_the_requesting_frontend_not_the_ambient_one`,
  `a_declining_listener_cannot_redirect_the_destination`, and two
  rows already named `preservation_*`. Journey Stage 1a's own framing
  treats this suite as a required gate; a lane that generalizes its
  substrate does not get to relax that.
- **STOP SIGNAL, for both suites.** If any existing `dired` or
  `journey_acceptance` test needs editing, the generalization changed
  Journey Stage 1a's semantics. That is cause to stop and report, not
  to adjust the test — a suite edited to accommodate the change under
  test has stopped being evidence.
- **`Handle:await` still refuses inside the scope**, including through
  `pmacs.async.yield_to_next_tick` if the worker-identity lane's Q#W-7
  has landed by then; if it has not, this lane does **not** add that
  guard — it belongs to that lane and duplicating it would produce a
  conflict for no benefit.

**What this will NOT prove:** that git surfaces in the right frontend —
that is #227's adoption, after this lands. This lane ships the
mechanism and one set of tests for the mechanism.

## 8. Not in scope

**Adopting the capture anywhere**, including git (#227 does that) and
including migrating other async continuations that have the same latent
bug — worth an audit, not this lane's work. Changing Journey Stage 1a's
directory semantics. The `commit_to` scope guard for
`yield_to_next_tick` (worker identity Q#W-7). Any protocol change —
this is entirely core + Lua bindings. Panel geometry or placement
policy, which is the bottom-panel arc's.
