docs: frame a general destination capture (revision 1)
PR #227 review found that git async completions surface in whichever frame is active when git exits, and named the right mechanism: commit_to exists for exactly this continuation boundary, built by Journey Stage 1a Q#JR14 because the work settles a tick or more later, by which time the ambient frontend, window and buffer may all name something else. The fix is not available to git, which is why this is a lane rather than a line in #227. commit_to takes a DirectoryDestinationLua that is nonconstructible from Lua by deliberate design, and the only site that mints one is inside the path.open-directory listener dispatch, from a pub(crate) capture. Any async Lua continuation that is not a directory open has no way to say where its result belongs. The captured data is already generic --- frontend, window, buffer, with nothing directory-specific in it. Only the name and the capture site are, and the rename is 8 references across 4 files, counted rather than estimated. The substantive question is Q#DC-2, and scouting is what surfaced it. Git two continuations are different in kind. *git-status* goes to the bottom panel, because listview.open resolves display with a "panel" default. *git-diff* replaces a document window, deliberately, so the status panel it was invoked from stays visible beside it. The stale-intent check that commit_to preflight runs --- the window still shows the captured buffer --- is right for the second and wrong for the first: the panel never touches that window buffer, so refusing because the user switched files there is a refusal with no relationship to what the continuation does. One shape either over-refuses the panel case or under-checks the document case, and the framing votes for a parameterized preflight while holding that vote loosely. No adopter in this lane. Git adoption is #227 work after this lands; a prerequisite that also converts its first consumer makes the two impossible to review separately. Verification carries a stop signal rather than a target: if any existing dired test needs editing, the generalization changed Journey Stage 1a semantics and that is cause to stop, not to adjust a test. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
01901029d6
commit
71ef951535
|
|
@ -265,6 +265,51 @@ also removed: this branch's "R8 NEEDS A LANE" investigation block, and
|
||||||
durable facts are in the retired registry row and the handoff §6
|
durable facts are in the retired registry row and the handoff §6
|
||||||
census.
|
census.
|
||||||
|
|
||||||
|
## Destination capture (Q#JR14 generalization) — BRANCHED, framing in review
|
||||||
|
|
||||||
|
**Written with the lane's first commit**, per the standing correction
|
||||||
|
from #171 and #215.
|
||||||
|
|
||||||
|
**Branch `destination-capture`**, base `githubsucks/main` @ `4bc55e8`
|
||||||
|
(the #225 merge). **`githubsucks/destination-capture` is the
|
||||||
|
authoritative tip** — the ref, not a SHA. Recover with
|
||||||
|
`git fetch githubsucks && git checkout destination-capture`.
|
||||||
|
|
||||||
|
- **Framing `docs/destination-capture-framing.md`, revision 1**, in
|
||||||
|
review.
|
||||||
|
- **A PREREQUISITE LANE. PR #227 (git Stage 1) blocks on it.** #227's
|
||||||
|
P1a review finding is why it exists: git's async completions mutate
|
||||||
|
and display UI without capturing the initiating frontend
|
||||||
|
(`builtin/runtime/git.lua:609`, `:854`), so a result surfaces in
|
||||||
|
whichever frontend is active when git exits.
|
||||||
|
- **The mechanism exists but is not Lua-reachable.**
|
||||||
|
`pmacs.window.commit_to` takes a `DirectoryDestinationLua`, which is
|
||||||
|
**nonconstructible from Lua** by design
|
||||||
|
(`src/lua_bindings/mod.rs:4256`) and minted only inside the
|
||||||
|
`path.open-directory` listener dispatch (`src/editor.rs:1311`) from a
|
||||||
|
`pub(crate)` capture (`:1241`). So no async Lua continuation outside
|
||||||
|
a directory open can say where its result belongs.
|
||||||
|
- **Scope:** a Lua-reachable capture, a generic rename
|
||||||
|
(`DirectoryDestination` → `ViewDestination`, 8 references across 4
|
||||||
|
files — counted, not estimated), and the preflight question below.
|
||||||
|
**No adopter**: git's adoption is #227's work after this lands, since
|
||||||
|
a prerequisite that converts its own first consumer cannot be
|
||||||
|
reviewed separately from it.
|
||||||
|
- **The substantive question (Q#DC-2)** is that git's two continuations
|
||||||
|
differ in kind. `*git-status*` goes to the **bottom panel**
|
||||||
|
(`listview.open` defaults `display` to `"panel"`,
|
||||||
|
`builtin/runtime/listview.lua:550`); `*git-diff*` replaces a
|
||||||
|
**document** window. `commit_to`'s stale-intent check (Q#JR14c) is
|
||||||
|
right for the second and wrong for the first — the panel never
|
||||||
|
touches the captured window's buffer, so refusing on its change is a
|
||||||
|
refusal unrelated to what the continuation does. One shape
|
||||||
|
over-refuses the panel or under-checks the document.
|
||||||
|
- **Stop signal recorded in the framing:** if any existing dired test
|
||||||
|
needs editing, the generalization changed Journey Stage 1a's
|
||||||
|
semantics, and that is cause to stop rather than to adjust the test.
|
||||||
|
- **Gates:** `scripts/gate --acceptance <the new suite>` plus dired's.
|
||||||
|
No `--protocol` — core and Lua bindings only.
|
||||||
|
|
||||||
## LSP LaTeX coverage — IMPLEMENTED, gates green, no PR yet
|
## LSP LaTeX coverage — IMPLEMENTED, gates green, no PR yet
|
||||||
|
|
||||||
**Written with the lane's first commit**, per the standing correction
|
**Written with the lane's first commit**, per the standing correction
|
||||||
|
|
@ -603,6 +648,7 @@ authoritative tip** — the ref, not a SHA. Recover with
|
||||||
— added in the second round — a **rename of either** the build or the
|
— added in the second round — a **rename of either** the build or the
|
||||||
sweep step each fail the suite.
|
sweep step each fail the suite.
|
||||||
||||||| parent of 72bbb96 (docs: LSP LaTeX coverage framing revision 2, on a branch at last)
|
||||||| parent of 72bbb96 (docs: LSP LaTeX coverage framing revision 2, on a branch at last)
|
||||||
|
||||||| parent of ac1d6cc (docs: frame a general destination capture (revision 1))
|
||||||
|
|
||||||
## QoL arc retirement — PR #224 OPEN (docs only)
|
## QoL arc retirement — PR #224 OPEN (docs only)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,198 @@
|
||||||
|
# A destination capture any async continuation can use
|
||||||
|
|
||||||
|
**Status: framing pass, revision 1. Pre-implementation. Awaiting
|
||||||
|
approval.**
|
||||||
|
|
||||||
|
**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 and its document window.
|
||||||
|
- **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. 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)***. The four checks are not equally applicable, and
|
||||||
|
which apply is a property of *what the continuation does*, which only
|
||||||
|
the caller knows. (3) duplicates the liveness checks that both need;
|
||||||
|
(1) ships a refusal that will read as a bug the first time a user hits
|
||||||
|
it.
|
||||||
|
|
||||||
|
**I hold this one loosely.** It is the design decision of the lane, and
|
||||||
|
(1) has a real argument — a uniform rule is easier to reason about than
|
||||||
|
a parameterized one, and over-refusal is at least *safe*.
|
||||||
|
|
||||||
|
### 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-4 — is the capture refused when there is no document window?
|
||||||
|
|
||||||
|
`capture_directory_destination` already returns `None` when the
|
||||||
|
frontend has no document window (`src/editor.rs:1236`). *My vote:
|
||||||
|
**return `nil`, and require every adopter to handle it***, rather than
|
||||||
|
inventing a fallback destination. A continuation with nowhere to land
|
||||||
|
should say so, and #227's adopter should degrade to today's ambient
|
||||||
|
behaviour with a status message rather than silently guessing.
|
||||||
|
|
||||||
|
## 6. 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.
|
||||||
|
- **Each preflight refusal is witnessed by its own case**: frontend
|
||||||
|
gone, window gone, stale buffer, dedicated window — and, under
|
||||||
|
Q#DC-2's answer, that the stale-buffer refusal does **not** fire for
|
||||||
|
a continuation that declared it is not replacing that buffer.
|
||||||
|
- **`nil` when the frontend has no document window** (Q#DC-4).
|
||||||
|
- **The directory path is unchanged** — dired's existing acceptance
|
||||||
|
coverage passes untouched. **If any dired test needs editing, the
|
||||||
|
generalization changed Journey Stage 1a's semantics** and that is a
|
||||||
|
stop signal, not a fixup.
|
||||||
|
- **`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.
|
||||||
|
|
||||||
|
## 7. 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.
|
||||||
Loading…
Reference in New Issue