docs(framing): Stage 1 revision 13 --- 1b ground truth, and Q#S1-11
Adds SS2a, measured at `72da24a`. No B-row contract changes. Awaiting approval; nothing is implemented on this branch yet. Every 1b anchor was stale, which was the expected result --- 1-pre's router extraction moved the wheel path wholesale and `main.rs` is now 21,435 lines. `main.rs:2061` holds `PanelCell`, `:3337` an `attach_client` line, `:3373` a bare brace; `dispatch_mouse` is `editor.rs:3207`, not `:3052`. Those are renumbered in a table. Three findings are not renumbering. **THREE ROWS SAY "NOTHING EXISTS" WHERE SOMETHING DOES.** B3's "no horizontal scroll to clamp" --- the GPU has `code_scroll_left` and its own `horizontal_follow`; what is missing is a WHEEL-driven one, so B3 adds a second writer to an existing field. B5's "no I-beam" is true, but `apply_panel_cursor_icon` already sets `CursorIcon::Default` everywhere that is not the divider, so an I-beam written as its own site would be clobbered by that else branch. B4's "no middle-click path" --- 1-pre built the landing site and named this very row in its doc comment. Each contract is unaffected; each implementation shape is not. **AND B7 RE-OPENS A QUESTION ANOTHER FRAMING DEFERRED.** This is the one that needs a ruling. `horizontal_follow` documents the premise "there are no explicit scroll commands, so every viewport move originates here, and Q#HS4's snap-back hazard cannot arise". B7 IS an explicit horizontal viewport move, and the horizontal-scroll framing says in terms that such commands "are what re-opens Q#HS4". The hazard is not hypothetical --- it is why `scroll_window` carries the cursor with a VERTICAL wheel scroll: without it the auto-scroll pass snaps the origin back and the wheel "would feel stuck after one notch". A wheel-driven `view_left` that does not carry the cursor gets the identical bug one axis over, on BOTH frontends, since the GPU's `horizontal_follow` has the same shape. B7's contract does not mention the cursor, so its mutations cannot detect this: a clamp row and a wrap row both pass against a viewport that snaps back on the next caret event. Q#S1-11 is raised OPEN with two candidate answers, and either way B7 needs a witness the table lacks --- scroll sideways, trigger a caret-follow, assert the ruled behaviour. Also worth having: B7's wrap clause is already implemented for the caret-follow path, and B1/B2 share one witness, the four lines in `apply_wheel` that round to whole lines and discard both the remainder and the x delta. `code_scroll_residual` is NOT a wheel accumulator --- it is the caret-follow pixel residual --- and reusing it would be a defect. Gates: all nine green under `env -u TMPDIR`, log 20260813T202304Z. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
0ec13b3953
commit
ce982e6701
|
|
@ -1,5 +1,13 @@
|
||||||
# GUI arc, Stage 1 — input foundation (framing)
|
# GUI arc, Stage 1 — input foundation (framing)
|
||||||
|
|
||||||
|
**Status: revision 13 — AWAITING APPROVAL.** Revision 13 is §2a's
|
||||||
|
ground-truth re-measurement **for Stage 1b**, taken at the post-#240
|
||||||
|
tip. Every 1b line anchor was stale, which was expected. What was not
|
||||||
|
expected is that **three B-rows describe a field as empty when it is
|
||||||
|
occupied**, and that **B7 re-opens a question another framing
|
||||||
|
deliberately deferred** — so this revision asks for a ruling (Q#S1-11)
|
||||||
|
rather than only renumbering. **No B-row contract is changed here.**
|
||||||
|
|
||||||
**Status: revision 13 — AWAITING APPROVAL with bottom-panel §5a
|
**Status: revision 13 — AWAITING APPROVAL with bottom-panel §5a
|
||||||
revision 16.** Revision 13 changes no previously ruled outcome. It
|
revision 16.** Revision 13 changes no previously ruled outcome. It
|
||||||
makes B1–B3's “per surface” scope explicit for the panel-document
|
makes B1–B3's “per surface” scope explicit for the panel-document
|
||||||
|
|
@ -26,11 +34,13 @@ Protocol slices stay serialized; one was inserted in front.
|
||||||
`ADVERTISED_PROTOCOL_VERSION` remains pinned at **20**.
|
`ADVERTISED_PROTOCOL_VERSION` remains pinned at **20**.
|
||||||
|
|
||||||
**Verification base:** §2 is **re-measured at `4f77491`** (2026-08-12),
|
**Verification base:** §2 is **re-measured at `4f77491`** (2026-08-12),
|
||||||
the tip after 1-pre; it was originally taken at `a994f37`. Sections
|
the tip after 1-pre; it was originally taken at `a994f37`. **§2a is
|
||||||
other than §2 were written against `a994f37` and their *rulings* are
|
measured at `72da24a`** (2026-08-13), the tip after 1a and #240, and it
|
||||||
unaffected by 1-pre, which changed no behaviour — but **any line number
|
is the base for **1b only**. Sections other than §2/§2a were written
|
||||||
outside §2 predates 1-pre and should be re-checked before it is relied
|
against `a994f37` and their *rulings* are unaffected by 1-pre, which
|
||||||
on.**
|
changed no behaviour — but **any line number outside §2 and §2a
|
||||||
|
predates 1-pre and should be re-checked before it is relied on.** The
|
||||||
|
1b table's own anchors are superseded by §2a wholesale; read §2a first.
|
||||||
|
|
||||||
## 1. What this stage closes
|
## 1. What this stage closes
|
||||||
|
|
||||||
|
|
@ -118,6 +128,128 @@ survives A4**: a native close still returns `Exit`, and
|
||||||
keyboard input is truncated to its first scalar, and an IME commit
|
keyboard input is truncated to its first scalar, and an IME commit
|
||||||
produces nothing.
|
produces nothing.
|
||||||
|
|
||||||
|
## 2a. Ground truth for 1b — MEASURED at `72da24a` (2026-08-13)
|
||||||
|
|
||||||
|
§2 above was measured for **1a**, at `4f77491`. 1a (#239) has since
|
||||||
|
merged and #240 landed on top, so **every coordinate the 1b table cites
|
||||||
|
is stale** — `pmacs-gpu/src/main.rs` is now **21,435 lines** and 1-pre's
|
||||||
|
router extraction moved the wheel path wholesale. Renumbering alone
|
||||||
|
would have been routine. It is not what the measurement found.
|
||||||
|
|
||||||
|
**`PROTOCOL_VERSION = 24`** now (1a's `TextInput`). 1b remains
|
||||||
|
non-protocol-bearing.
|
||||||
|
|
||||||
|
### The anchors, re-measured
|
||||||
|
|
||||||
|
| the table cites | holds what now | the real site |
|
||||||
|
|---|---|---|
|
||||||
|
| `main.rs:2061` "minimap is `Elsewhere`" | `PanelCell(CellCoord)` | `enum PointerSurface` `:2057`; **`Elsewhere` `:2067`** |
|
||||||
|
| `main.rs:3337` wheel reads `pointer_pos` | an `attach_client` line | `apply_wheel` `:3090` and `:3113` |
|
||||||
|
| `main.rs:3373` falls to `scroll_by_lines` | a bare `}` | `apply_wheel` `:3126`; `scroll_by_lines` `:8002` |
|
||||||
|
| `editor.rs:3052` `dispatch_mouse` | — | **`:3207`** |
|
||||||
|
| `editor.rs:3203` scroll arms | a doc-comment line | `ScrollUp` `:3358`, `ScrollDown` `:3362` |
|
||||||
|
|
||||||
|
**B6's premise survives the move intact**, and is now stated by the code
|
||||||
|
itself: `Elsewhere`'s doc comment reads *"Not the band: the document,
|
||||||
|
the terminal, the minimap, or the chrome."* The wheel's panel branch
|
||||||
|
(`:3090`) tests `PanelCell` only, so minimap pixels still fall through
|
||||||
|
to `scroll_by_lines`.
|
||||||
|
|
||||||
|
**B1's defect is visible in four lines.** `apply_wheel` rounds to whole
|
||||||
|
lines and returns on zero:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let lines = match delta { LineDelta(_, y) => (-y * WHEEL_LINES_PER_TICK).round() as i64, … };
|
||||||
|
if lines == 0 { return; }
|
||||||
|
```
|
||||||
|
|
||||||
|
`:3075`–`:3084`. The `_` is the **x** delta, discarded at the same site
|
||||||
|
— so **B1 and B2's witnesses are the same four lines**, and a residual
|
||||||
|
accumulator is what both need. **There is no wheel accumulator today.**
|
||||||
|
`code_scroll_residual` (`:1622`) is *not* it: that is the caret-follow
|
||||||
|
pixel residual, buffer-scoped, cleared at `:5928`. **Reusing it would
|
||||||
|
be a defect**, not a shortcut.
|
||||||
|
|
||||||
|
### CORRECTION 3 — three rows say "nothing exists" where something does
|
||||||
|
|
||||||
|
Each of these reads as an empty field in the table and is not one. The
|
||||||
|
contracts are unaffected; the **implementation shape** is.
|
||||||
|
|
||||||
|
- **B3 — "no horizontal scroll to clamp".** The GPU has a horizontal
|
||||||
|
origin: **`code_scroll_left`** (`:1639`), a pixel offset *snapped to
|
||||||
|
the column grid*, moved by its own `horizontal_follow` (`:7702`).
|
||||||
|
What is missing is a **wheel-driven** horizontal scroll. B3 adds a
|
||||||
|
second writer to an existing field, which is a different job from
|
||||||
|
introducing one.
|
||||||
|
- **B5 — "no I-beam".** True as stated, but the cursor already has an
|
||||||
|
owner: **`apply_panel_cursor_icon`** (`:7328`) sets `RowResize` over
|
||||||
|
the divider and **`CursorIcon::Default` everywhere else**. An I-beam
|
||||||
|
written as a separate site would be **clobbered by that else branch**.
|
||||||
|
B5 must extend this function, not join it.
|
||||||
|
- **B4 — "no middle-click path".** 1-pre already built the landing
|
||||||
|
site and named this row in it: `PointerRoute::UnusedButton` (`:3623`)
|
||||||
|
is documented *"Stage 1b's B4 gives the middle button a meaning
|
||||||
|
(PRIMARY-selection paste on Linux) and lands here."* `route_pointer`
|
||||||
|
(`:3631`) sends every non-left, non-right-press button there. B4
|
||||||
|
splits a variant that already exists.
|
||||||
|
|
||||||
|
### CORRECTION 4 — B7 re-opens a deferred question, and the table does not say so
|
||||||
|
|
||||||
|
This is the finding that needs a ruling rather than a renumber.
|
||||||
|
|
||||||
|
The TUI horizontal origin is **`window.view_left`** (`src/window.rs:386`),
|
||||||
|
and `horizontal_follow` (`src/editor.rs:4495`) already **pins it to 0
|
||||||
|
under wrap** — so *B7's wrap clause is implemented today*, for the
|
||||||
|
caret-follow path. Both frontends share the arithmetic
|
||||||
|
(`pmacs_protocol::scroll::follow_left`, `scroll.rs:134`), deliberately.
|
||||||
|
|
||||||
|
But that function's doc states the premise B7 removes:
|
||||||
|
|
||||||
|
> there are no explicit scroll commands, so **every viewport move
|
||||||
|
> originates here**, and Q#HS4's snap-back hazard cannot arise.
|
||||||
|
|
||||||
|
**B7 is an explicit horizontal viewport move.** `docs/horizontal-scroll-framing.md`
|
||||||
|
is explicit that such commands *"are what re-opens Q#HS4"* (`:189`), and
|
||||||
|
Q#HS4 is recorded as **DEFERRED, not answered** (`:202`). The hazard is
|
||||||
|
concrete and already cost this project once — §1.5 there quotes
|
||||||
|
`scroll_window`, which carries the cursor with a **vertical** wheel
|
||||||
|
scroll for exactly this reason:
|
||||||
|
|
||||||
|
> the renderer has an "auto-scroll to keep cursor visible" pass that
|
||||||
|
> would otherwise snap `view_top` straight back … so the user's
|
||||||
|
> mouse-wheel scroll would feel stuck after one notch.
|
||||||
|
|
||||||
|
(That citation is itself stale: `scroll_window` is **`src/editor.rs:3845`**,
|
||||||
|
not `:3628`.)
|
||||||
|
|
||||||
|
So a wheel-driven `view_left` that does **not** carry the cursor will be
|
||||||
|
snapped back by the next `horizontal_follow`, and horizontal wheel
|
||||||
|
scrolling will "feel stuck after one notch" — the identical bug, one
|
||||||
|
axis over, on **both** frontends, since the GPU's `horizontal_follow`
|
||||||
|
(`:7702`) has the same shape.
|
||||||
|
|
||||||
|
**B7's stated contract does not mention the cursor at all**, and its
|
||||||
|
mutations therefore cannot detect this: a clamp row and a wrap row both
|
||||||
|
pass against a viewport that snaps back on the next caret event.
|
||||||
|
|
||||||
|
#### Q#S1-11 — does a horizontal wheel scroll carry the cursor? **OPEN**
|
||||||
|
|
||||||
|
Not answerable from the table as written; Q#HS4 was deferred rather
|
||||||
|
than closed precisely so this would be decided when explicit commands
|
||||||
|
arrived. Two candidate answers, both defensible:
|
||||||
|
|
||||||
|
- **(A) Carry it, mirroring `scroll_window`.** Consistent with the
|
||||||
|
vertical axis, and the snap-back cannot arise. Costs a cursor move
|
||||||
|
the user did not ask for.
|
||||||
|
- **(B) Do not carry it; suppress the follow while a wheel origin is
|
||||||
|
live.** Preserves the cursor, but needs a rule for when the origin
|
||||||
|
stops being authoritative, and that rule is new state on both
|
||||||
|
frontends.
|
||||||
|
|
||||||
|
**Whichever is ruled, B7 needs a witness the current table lacks:**
|
||||||
|
scroll sideways, then trigger a caret-follow, and assert the origin
|
||||||
|
behaves as ruled. Without it the row is green either way.
|
||||||
|
|
||||||
## 3. PR topology
|
## 3. PR topology
|
||||||
|
|
||||||
`1-pre` → `1a`\* → `1b` → `1c` → `1d` → `1e`\* (\* `--protocol`)
|
`1-pre` → `1a`\* → `1b` → `1c` → `1d` → `1e`\* (\* `--protocol`)
|
||||||
|
|
@ -313,6 +445,12 @@ The crate has **exactly one** executable `event_loop.exit()`, in
|
||||||
|
|
||||||
### 1b — pointer and scroll
|
### 1b — pointer and scroll
|
||||||
|
|
||||||
|
> **Read §2a first.** Every line number in this table was measured
|
||||||
|
> before 1-pre and is stale. §2a re-measures them at `72da24a` and
|
||||||
|
> records three rows whose "nothing exists yet" is wrong, plus **Q#S1-11
|
||||||
|
> (open)**, which B7 cannot be implemented without. The contracts below
|
||||||
|
> are unchanged.
|
||||||
|
|
||||||
| # | Contract | Witness | Mutation |
|
| # | Contract | Witness | Mutation |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| B1 | Residual per **axis and surface** — primary document, panel document and minimap are distinct owners | deltas discarded | share one accumulator → primary↔panel or document↔minimap surface-switch jump |
|
| B1 | Residual per **axis and surface** — primary document, panel document and minimap are distinct owners | deltas discarded | share one accumulator → primary↔panel or document↔minimap surface-switch jump |
|
||||||
|
|
@ -621,6 +759,12 @@ and its attachments, Escape only cancels/round-trips · **Q#S1-5** A/`1e`
|
||||||
and the backlog amended by 1-pre's first PR · **Q#S1-8** (A),
|
and the backlog amended by 1-pre's first PR · **Q#S1-8** (A),
|
||||||
`SEMANTIC_BOOTSTRAP_GRID` · **Q#S1-9** precedence per §5 · **Q#S1-10** terminal `OpenTargetResult`.
|
`SEMANTIC_BOOTSTRAP_GRID` · **Q#S1-9** precedence per §5 · **Q#S1-10** terminal `OpenTargetResult`.
|
||||||
|
|
||||||
|
**OPEN: Q#S1-11** — does a horizontal wheel scroll carry the cursor?
|
||||||
|
Raised by §2a; **blocks B7 and B3 only**, and nothing else in 1b. It is
|
||||||
|
the question `docs/horizontal-scroll-framing.md` deferred as Q#HS4,
|
||||||
|
now live because B7 is the explicit horizontal scroll command whose
|
||||||
|
absence was that deferral's stated premise.
|
||||||
|
|
||||||
## 11. Gates
|
## 11. Gates
|
||||||
|
|
||||||
`./scripts/gate --acceptance gpu_invocation_acceptance` plus touched
|
`./scripts/gate --acceptance gpu_invocation_acceptance` plus touched
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue