docs: Stage 1a --- re-measure the framing's ground truth, and open the lane
Docs only, and the first commit on the 1a branch. **No ruling changes.**
**1a needs no framing of its own** ---
`docs/gui-stage1-input-framing.md` already governs every Stage 1 slice,
and A1-A9, the eight Q#S1-9 precedence rules, §8's wire contract and
§11's gates are ruled there. What 1a does need is §2 to be TRUE, and it
was measured at `a994f37`, before 1-pre moved almost every GPU-side
coordinate in it. A framing whose ground truth points at the wrong lines
is how an implementation ends up arguing with the tree.
Re-measured at `4f77491`, and split by kind rather than renumbered
wholesale, because the two are not the same claim:
STILL TRUE `FrontendEvent`'s sixteen variants, `PROTOCOL_VERSION =
23`, IME's zero occurrences, the TUI wheel arms (1-pre
touched only `pmacs-gpu`), the handshake preceding the
window, and `Outbox::enqueue`'s closed-return.
MOVED `window_event` `:2734`/655 lines -> `:4450`/FOUR lines;
`translate_key` `:10975` -> `:12053`; "eight arms, rest
fall to `_`" -> three family decision functions over nine
variants. **1a edits `apply_keyboard` and
`translate_key`, not `window_event`.**
**TWO CLAIMS WERE WRONG AT BOTH ANCHORS, so they are corrections rather
than drift.**
**"`KeyEvent.text` is never read" is false, and was false at `a994f37`
too** (`:2800` there, `main.rs:3251` now): the AltGr rule reads it via
`is_layout_text(key.text.as_deref(), pmods)`. The true claim is
narrower and is the one that matters --- **`text` is never read as the
text a keypress INSERTS**, only as a discriminator separating AltGr from
a command chord.
That is load-bearing for A5 rather than cosmetic. §5's rule 2 already
exempts "printable Ctrl+Alt recognized by the existing AltGr rule", so
the precedence table depends on code the section claimed did not exist.
**1a widens `text` from discriminator to payload, and that is the actual
change of kind** --- which "text is never read" hid, along with the one
place the new payload must leave undisturbed.
**A4's exit site moved without its behaviour changing.** 1-pre routed
the idle-Escape quit through `EventOutcome`, so **A4 edits a branch and
a return type in `apply_keyboard` (`main.rs:3219`) and does not touch
`window_event`**, which holds the crate's only executable
`event_loop.exit()` (`main.rs:4452`). And **`EventOutcome` survives
A4**: a native close still returns `Exit`.
The lane records the v24 serialization constraint and the bump's blast
radius up front --- eight version-sensitive failures across six suites
on the last bump, of which CI showed one, because cargo stops at the
first failing target --- together with the rule for sorting them and the
pin that must never fire, `ADVERTISED_PROTOCOL_VERSION == 20`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai
This commit is contained in:
parent
4f77491bca
commit
cd888bed5e
|
|
@ -270,6 +270,56 @@ hazard in a shape that looks committed. **A documented error message
|
||||||
that never appears is worse than no documentation**, because the reader
|
that never appears is worse than no documentation**, because the reader
|
||||||
waits for a signal that is not coming.
|
waits for a signal that is not coming.
|
||||||
|
|
||||||
|
## GUI arc Stage 1a — `TextInput` at v24 — branch OPEN, no PR yet
|
||||||
|
|
||||||
|
**Written with the branch's first commit**, per the standing correction
|
||||||
|
from #171 and #215.
|
||||||
|
|
||||||
|
- **Branch `gui-stage1a-textinput`**, base `githubsucks/main` @
|
||||||
|
`4f77491` exactly. **`githubsucks/gui-stage1a-textinput` is the
|
||||||
|
authoritative tip** — the ref, not a SHA. Recover with
|
||||||
|
`git fetch githubsucks && git checkout gui-stage1a-textinput`.
|
||||||
|
- **No new framing.** `docs/gui-stage1-input-framing.md` already governs
|
||||||
|
every Stage 1 slice; A1–A9, the eight Q#S1-9 precedence rules, §8's
|
||||||
|
wire contract and §11's gates are ruled there. Writing a 1a framing
|
||||||
|
would duplicate an approved document.
|
||||||
|
- **First commit is a GROUND-TRUTH RE-MEASUREMENT of §2 (revision 12),
|
||||||
|
not code.** §2 was taken at `a994f37`, before 1-pre moved almost every
|
||||||
|
GPU-side coordinate in it. It is refreshed here rather than in its own
|
||||||
|
PR so the contract and the code that depends on it are reviewed
|
||||||
|
together. **No ruling changes.**
|
||||||
|
- Moved: `window_event` `:2734`/655 lines → **`:4450`/four lines**;
|
||||||
|
`translate_key` `:10975` → **`:12053`**; the eight-arms description
|
||||||
|
→ three family decision functions over nine variants.
|
||||||
|
- **CORRECTION, wrong at BOTH anchors: "`KeyEvent.text` is never
|
||||||
|
read" is false** — the AltGr rule reads it (`a994f37:2800`, now
|
||||||
|
`main.rs:3251`). The true claim is narrower: `text` is never read as
|
||||||
|
the text a keypress **inserts**, only as a *discriminator*. This is
|
||||||
|
load-bearing for A5, because §5's rule 2 already exempts "printable
|
||||||
|
Ctrl+Alt recognized by the existing AltGr rule" — **1a widens `text`
|
||||||
|
from discriminator to payload, and that is the change of kind the
|
||||||
|
old wording hid.**
|
||||||
|
- **CORRECTION: A4's exit site.** 1-pre moved the mechanism without
|
||||||
|
changing behaviour. **A4 edits `apply_keyboard`'s branch and return
|
||||||
|
type, not `window_event`**, and **`EventOutcome` survives A4** — a
|
||||||
|
native close still returns `Exit`.
|
||||||
|
- **PROTOCOL-BEARING at v24 and therefore SERIALIZED.** `TextInput` is
|
||||||
|
an **appended** `FrontendEvent` variant; never widen a field in place,
|
||||||
|
because postcard is positional. `PROTOCOL_VERSION` is **23** at this
|
||||||
|
base and `ADVERTISED_PROTOCOL_VERSION` stays pinned at **20** and must
|
||||||
|
not be edited to chase it. A frozen-byte pin goes on `FrontendEvent`'s
|
||||||
|
**previous final variant**, since an appended variant's own round-trip
|
||||||
|
cannot detect a discriminant shift.
|
||||||
|
- **A `PROTOCOL_VERSION` bump's blast radius is every version-sensitive
|
||||||
|
test and NONE of them appear in the diff** — handoff §1a records eight
|
||||||
|
such failures across six suites on the last bump, of which CI showed
|
||||||
|
one because cargo stops at the first failing target. **Sort them:** a
|
||||||
|
tripwire `assert_eq!(PROTOCOL_VERSION, N)` is meant to fire; an
|
||||||
|
absolute contract expressed as arithmetic on a moving constant is a
|
||||||
|
defect. **`ADVERTISED_PROTOCOL_VERSION == 20` must NOT fire.**
|
||||||
|
- **Gates:** `./scripts/gate --protocol` plus the touched input suites.
|
||||||
|
**`--protocol` is required here** — 1a changes the wire.
|
||||||
|
|
||||||
## GUI arc Stage 1 — 1-pre MERGED as #237 (`d038f71`); 1a is next, NOT STARTED
|
## GUI arc Stage 1 — 1-pre MERGED as #237 (`d038f71`); 1a is next, NOT STARTED
|
||||||
|
|
||||||
**The lane is rewritten, not removed.** Rule 4 removes a lane when its
|
**The lane is rewritten, not removed.** Rule 4 removes a lane when its
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,108 @@
|
||||||
# GUI arc, Stage 1 — input foundation (framing)
|
# GUI arc, Stage 1 — input foundation (framing)
|
||||||
|
|
||||||
**Status: revision 11 — APPROVED.** Revisions 1–8 rejected; revision 9
|
**Status: revision 12 — APPROVED.** Revision 12 is §2's ground-truth
|
||||||
|
re-measurement for Stage 1a and changes no ruling; it carries two
|
||||||
|
corrections to claims that were wrong at the original anchor too.
|
||||||
|
|
||||||
|
**Previously, revision 11 — APPROVED.** Revisions 1–8 rejected; revision 9
|
||||||
is the approved design. Revision 10 recorded a scope correction found
|
is the approved design. Revision 10 recorded a scope correction found
|
||||||
against the 1-pre implementation and **also made a claim about P2 that
|
against the 1-pre implementation and **also made a claim about P2 that
|
||||||
review overturned; revision 11 retracts it and P2 is implemented as
|
review overturned; revision 11 retracts it and P2 is implemented as
|
||||||
written** (§6). **Q#S1-8, Q#S1-9 and Q#S1-10 are RULED.** **1-pre is
|
written** (§6). **Q#S1-8, Q#S1-9 and Q#S1-10 are RULED.** **1-pre is
|
||||||
IMPLEMENTED**; 1a onward may begin from this document.
|
IMPLEMENTED**; 1a onward may begin from this document.
|
||||||
|
|
||||||
**Verification base:** checked in the `gui-arc-stage0` worktree at
|
**Verification base:** §2 is **re-measured at `4f77491`** (2026-08-12),
|
||||||
`a994f37`, whose tree for these files is what `f8ad3e7` merged.
|
the tip after 1-pre; it was originally taken at `a994f37`. Sections
|
||||||
|
other than §2 were written against `a994f37` and their *rulings* are
|
||||||
|
unaffected by 1-pre, which changed no behaviour — but **any line number
|
||||||
|
outside §2 predates 1-pre and should be re-checked before it is relied
|
||||||
|
on.**
|
||||||
|
|
||||||
## 1. What this stage closes
|
## 1. What this stage closes
|
||||||
|
|
||||||
Journey **step 5**. **Not step 12** (Stage 4b, P2-gated). Five of nine
|
Journey **step 5**. **Not step 12** (Stage 4b, P2-gated). Five of nine
|
||||||
§3.1 blockers die here.
|
§3.1 blockers die here.
|
||||||
|
|
||||||
## 2. Ground truth at `a994f37`
|
## 2. Ground truth — RE-MEASURED at `4f77491` (2026-08-12)
|
||||||
|
|
||||||
`App::window_event` (`main.rs:2734`) is **655 lines**; **eight**
|
**Originally taken at `a994f37`, before 1-pre.** 1-pre (#237) moved
|
||||||
`WindowEvent` arms handled, the rest fall to `_`.
|
almost every GPU-side coordinate below, so the section is re-measured
|
||||||
|
rather than left to rot — **a framing whose ground truth points at the
|
||||||
|
wrong lines is how an implementation ends up arguing with the tree**.
|
||||||
|
Two claims were *wrong at both anchors* and are corrected, not merely
|
||||||
|
renumbered; they are marked **CORRECTION**.
|
||||||
|
|
||||||
**Two text producers.** `translate_key(logical: &Key, …)`
|
### Still true, re-checked
|
||||||
(`main.rs:10975`) reads the **logical key** and truncates via
|
|
||||||
`chars().next()`; **`KeyEvent.text` is never read.**
|
|
||||||
`WindowEvent::Ime::Commit(String)` is a **separate event, ignored
|
|
||||||
entirely** (`set_ime_allowed`/`WindowEvent::Ime`: zero occurrences).
|
|
||||||
**Today's two failures are therefore: multi-scalar keyboard input is
|
|
||||||
truncated to its first scalar, and an IME commit produces nothing.**
|
|
||||||
|
|
||||||
**`FrontendEvent`: sixteen variants, none carrying an open path or
|
- **`FrontendEvent`: sixteen variants**, none carrying an open path or
|
||||||
command invocation.** `PROTOCOL_VERSION = 23`.
|
command invocation. **`PROTOCOL_VERSION = 23`.**
|
||||||
|
- **`WindowEvent::Ime` is ignored entirely** —
|
||||||
|
`set_ime_allowed`/`WindowEvent::Ime` still **zero occurrences**, so
|
||||||
|
`Ime::Commit(String)` produces nothing. (1d's D1.)
|
||||||
|
- **TUI wheel arms unmoved**: `EditorState::dispatch_mouse`
|
||||||
|
(`src/editor.rs:3052`), `ScrollUp`/`ScrollDown` at **`:3203`** — 1-pre
|
||||||
|
touched only `pmacs-gpu`.
|
||||||
|
- **The handshake precedes any window**: the client is constructed and
|
||||||
|
the handshake done before `run_app` (connect `main.rs:702`, `run_app`
|
||||||
|
`main.rs:733`; the old citation `:696` was the enclosing block).
|
||||||
|
- **1c is producer-side only for Focus/Detach** — those variants exist
|
||||||
|
on the wire. **Title, Bell and `Goodbye` are GPU consumer work.**
|
||||||
|
- **`Outbox::enqueue` returns `false` once closed** and **coalesces by
|
||||||
|
kind** (`attach.rs:415`; the old `:414` was off by one at both
|
||||||
|
anchors).
|
||||||
|
|
||||||
**The handshake precedes any window** — `EventLoop` built, client
|
### Moved by 1-pre
|
||||||
constructed, handshake done **before `run_app`** (`main.rs:696`).
|
|
||||||
|
|
||||||
**TUI wheel arms**: `EditorState::dispatch_mouse` (`src/editor.rs:3052`),
|
- **`App::window_event` is `main.rs:4450` and is FOUR lines**, not 655
|
||||||
`ScrollUp`/`ScrollDown` at **`:3203`**.
|
at `:2734`. It calls `dispatch_window_event` and performs the exit;
|
||||||
|
**routing lives in `route_event`, and the bodies in seven `apply_*`
|
||||||
|
methods.**
|
||||||
|
- **"Eight arms handled, the rest fall to `_`" is now three family
|
||||||
|
decision functions** — `route_lifecycle`, `route_keyboard` (plus
|
||||||
|
`route_key_action`), `route_pointer` — over **nine** named
|
||||||
|
`WindowEvent` variants, with `Route::Unrouted` as the wildcard. **1a
|
||||||
|
edits `apply_keyboard` and `translate_key`, not `window_event`.**
|
||||||
|
- **`translate_key(logical: &Key, …)` is `main.rs:12053`**, not
|
||||||
|
`:10975`. It still reads the **logical key** and still truncates via
|
||||||
|
`chars().next()`, and `_ => return None` is still there — so **A1's
|
||||||
|
witness holds**.
|
||||||
|
|
||||||
**1c is producer-side only for Focus/Detach** — those variants exist on
|
### CORRECTION 1 — `KeyEvent.text` IS read, and always was
|
||||||
the wire. **Title, Bell and `Goodbye` are GPU consumer work.**
|
|
||||||
|
|
||||||
**`Outbox::enqueue` returns `false` once closed** (`attach.rs:414`) and
|
The original section said *"`KeyEvent.text` is never read."* **That is
|
||||||
**coalesces by kind**.
|
false, and was false at `a994f37` too** (`:2800` there, `main.rs:3251`
|
||||||
|
now): the AltGr rule reads it, as `is_layout_text(key.text.as_deref(),
|
||||||
|
pmods)`.
|
||||||
|
|
||||||
|
The claim the section meant, and which is true: **`KeyEvent.text` is
|
||||||
|
never read as the text a keypress INSERTS.** It is consulted only as a
|
||||||
|
*discriminator* — Ctrl+Alt plus printable text means AltGr rather than a
|
||||||
|
command chord — and the inserted character always comes from
|
||||||
|
`translate_key`'s logical key, truncated to one scalar.
|
||||||
|
|
||||||
|
**This matters to A5, not just to accuracy.** §5's rule 2 exempts
|
||||||
|
"printable Ctrl+Alt recognized by the existing AltGr rule", so the
|
||||||
|
precedence table already depends on the code the section claimed did not
|
||||||
|
exist. **1a widens `text` from a discriminator to a payload**, and that
|
||||||
|
is the actual change of kind — stating it as "text is never read" hides
|
||||||
|
the one place the new payload must not disturb.
|
||||||
|
|
||||||
|
### CORRECTION 2 — A4's exit site
|
||||||
|
|
||||||
|
A4's witness cited *"exits (`main.rs:2771`)"*. 1-pre moved the mechanism
|
||||||
|
without changing the behaviour: an idle Escape still exits, but
|
||||||
|
`apply_keyboard` (`main.rs:3219`) now returns `EventOutcome::Exit` and
|
||||||
|
**`window_event` (`main.rs:4452`) performs the only executable
|
||||||
|
`event_loop.exit()` in the crate.**
|
||||||
|
|
||||||
|
**A4 therefore deletes a branch in `apply_keyboard` and changes its
|
||||||
|
return type — it does not touch `window_event`.** And **`EventOutcome`
|
||||||
|
survives A4**: a native close still returns `Exit`, and
|
||||||
|
`dispatch_window_event` must still distinguish it from `Continue`.
|
||||||
|
|
||||||
|
**Today's two failures are unchanged by any of this:** multi-scalar
|
||||||
|
keyboard input is truncated to its first scalar, and an IME commit
|
||||||
|
produces nothing.
|
||||||
|
|
||||||
## 3. PR topology
|
## 3. PR topology
|
||||||
|
|
||||||
|
|
@ -229,7 +290,7 @@ The crate has **exactly one** executable `event_loop.exit()`, in
|
||||||
| A1 | `F1`–`F35` → `F(1..=35)` | `_ => return None` | map `F13+` → `None` → F13–F35 rows |
|
| A1 | `F1`–`F35` → `F(1..=35)` | `_ => return None` | map `F13+` → `None` → F13–F35 rows |
|
||||||
| A2 | Shift+Tab → `BackTab` with `Shift` set | produces `Tab` | drop `Shift` → A2 only |
|
| A2 | Shift+Tab → `BackTab` with `Shift` set | produces `Tab` | drop `Shift` → A2 only |
|
||||||
| A3 | `ContextMenu` → `Menu` | produces nothing | map to `Char('\0')` → A3 only |
|
| A3 | `ContextMenu` → `Menu` | produces nothing | map to `Char('\0')` → A3 only |
|
||||||
| A4 | Idle Escape reaches the daemon, never exits | exits (`main.rs:2771`) | restore the quit branch → A4 only |
|
| A4 | Idle Escape reaches the daemon, never exits | exits — `apply_keyboard` (`main.rs:3219`) returns `EventOutcome::Exit`, performed at `main.rs:4452` | restore the quit branch → A4 only |
|
||||||
| A5 | Precedence per §5 (1–8) | multi-scalar truncated; IME ignored | move rule 1 (control text → text) → the `Enter`-in-dired row |
|
| A5 | Precedence per §5 (1–8) | multi-scalar truncated; IME ignored | move rule 1 (control text → text) → the `Enter`-in-dired row |
|
||||||
| A6 | One commit = one edit, undo unit, hook, eligible CRDT op | commit truncated to one scalar | one edit per scalar → undo-unit row (**and D3 surfaces here**) |
|
| A6 | One commit = one edit, undo unit, hook, eligible CRDT op | commit truncated to one scalar | one edit per scalar → undo-unit row (**and D3 surfaces here**) |
|
||||||
| A7 | Prompts consume scalars **in order** | multi-scalar never arrives | reverse order → A7's prompt transcript |
|
| A7 | Prompts consume scalars **in order** | multi-scalar never arrives | reverse order → A7's prompt transcript |
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue