Commit Graph

9 Commits

Author SHA1 Message Date
Levi Neuwirth a1fada45e8 docs(bottom-panel): record framing revision 7
Review round 1 widened two contracts this document had stated narrowly, and
a narrow statement is what let the implementation drift inside it:

R7-1 — the stable-probe decision was written about the geometry DECLARATION,
which let painting and hit-testing keep using the document-dependent advance.
Three grids, one asserted. It now covers all three consumers, resolved once
and cached behind the declaration so they agree by construction.

R7-2 — the x=0 full-width contract was stated about `total.cols` and read as
a claim about the declaration alone; it governs the band's content rectangle
too, remainder included.

R7-3 records that splitting the advertised baseline from PROTOCOL_VERSION
makes the VersionMismatch server field load-bearing rather than incidental.

R7-4 names criterion 54's fixture and why it drives the real display="panel"
adopter opt-in instead of opening a terminal and moving it.

A2B-3 and criterion 48 gain the halves that were implicit and therefore
skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Lv428Fth9LRtffwJSsqH7T
2026-07-29 22:37:41 -04:00
Levi Neuwirth e05ac819b5 docs: sync the durable records to landed main @ 6bee09d
PR #184 (bottom-panel Stage 2B-1) merged, but the four documents that
new work is supposed to start from still described it as open. Advance
every canonical anchor and close the one obligation that was blocking a
lane removal.

docs/agent-handoff.md
- §1's head-of-main anchor moves 7fd646d -> 6bee09d.
- State schema support and production advertisement as two separate
  facts: SUPPORTED is now v6..=v21, the server-first Hello still says
  v20, and #184 landed only the first.
- New §1 bullet for Stage 2B-1 carrying its durable substrate: the
  server-first incompatibility argument, the shared wire_grid boundary
  and why per-axis caps are a parameter, authoritative Absent, and the
  panel_epoch / geometry_epoch split.
- New §1 bullet for the PTY terminate diagnostic (#176), plus two §5
  ops lessons: a drain that ticks is not an observation, and how to
  prove a child exited without waitid or unsafe.

docs/active-work.md
- Canonical base and the recovery floor both advance to 6bee09d.
- Remove the #176 lane. It was retained only because rule 4 requires
  its facts to reach the handoff first; that move is in this commit, so
  the lane goes.
- Rewrite the bottom-panel lane as 2B-1 MERGED with 2B-2 next, branching
  fresh from landed main rather than stacking.
- Annotate the documentation lane with measured staleness (1 ahead, 320
  behind) without deleting it, since nothing has decided its fate.

docs/bottom-panel-stage2-framing.md
- Status header records 2A and 2B-1 as merged and 2B-2 as next. No
  revision bump: the header is landed state, and no design decision in
  the document changed.

COHERENCE.md
- §16 names #184 as the PR that landed the reserved v21 family.

No runtime code, no protocol change, no gate suite implicated.
2026-07-28 16:28:21 -04:00
Levi Neuwirth ab7c207904 Keep the v21 panel wire dark for v20 clients
Reserve the additive v21 panel schema without advertising it in the
server-first production handshake. Pin a real shipped-v20 client attach,
make the two aggregate-budget ratchets exactly one byte over, and update
the framing, coherence audit, handoff, and volatile lane record.
2026-07-28 14:08:17 -04:00
Levi Neuwirth d88d60eba6 Merge canonical main into bottom-panel Stage 2B-1
Integrate the Journey Stage 1a merge without rewriting the already
reviewed protocol branch. Record the approved three-way Stage 2B split,
advance the canonical recovery anchor, remove the landed Journey lane,
and put 2B-1 into its full-gating state.
2026-07-27 22:16:11 -04:00
Levi Neuwirth 56301eda02 docs(panel): Stage 2 framing rev 5 — the three-way slice of 2B
Rev 4 §9 scoped Stage 2B as a single PR: v21 protocol, daemon panel
projection, GPU band, and the negotiated `panel_capable` flip.
Implementation showed that to be roughly four thousand lines across
three crates with three different failure modes. Rev 5 splits it into
2B-1 (the wire layer), 2B-2 (the daemon projection and epoch machine),
and 2B-3 (the GPU band and the flip), on the rule that a slice ends
where the next thing to build has a different authority.

No decision changes. What changes is the allocation:

- §7.2 becomes three subsections, and criteria that span a boundary are
  named in every slice they touch with their half stated, rather than
  assigned wholesale to one. Parent 39 is the clearest case: its
  shared-validation and transport-budget halves are wire properties
  provable in 2B-1, while "the previous valid frame is retained" and "a
  duplicate does no work" need the epoch machine and are 2B-2. A2B-1
  splits the same way — grid exhaustion in 2B-2, the frontend latch in
  2B-3.
- §9 lists four serial PRs instead of two, each cut from `main`, and
  states that every slice runs the full gate set rather than the subset
  its own crate suggests.
- §6 records which slice pays the coherence debt. The journey claim
  belongs to 2B-3 alone: with `panel_capable = false`, a GPU user still
  gets the Stage 1 non-side fallback after 2A, 2B-1 and 2B-2 have all
  landed. Three quarters of this stage is preparation.

Two things recorded because they are easy to inherit silently:

- This revision is retroactive for slice 1. `bottom-panel-stage2b`
  already carried the v21 protocol layer, written before the revision
  existed, which inverts framing -> approval -> branch -> implement. The
  slicing was sound; taking it in code rather than in the document is
  how a stage's scope drifts without anyone deciding that it should.
- 2B-1 and 2B-2 ship dark. The bump advertises a capability whose only
  distinguishing feature is unreachable until 2B-3, so the arc must not
  stall between them. Safe for compatibility — appended variants,
  extended ladder, a v20 peer still negotiates 20 — but a stall should
  be visible as a decision, not inherited as a default.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011LFvC4FQtux4y32KuevZ7B
2026-07-26 20:02:19 -04:00
Levi Neuwirth 49757e51a8 docs: bottom-panel Stage 2 framing (revision 4)
Closes review round 3 — 1 blocking, 1 high, 1 medium.

**R3-1 (blocker) — the call-site table contradicted the source.** The
three-boundary model was right; five rows of its classification were
not, and each was a real defect:

- `:6140` is `completion_dropdown_layout` — DOCUMENT completion
  placement, deriving the space below the anchor line. Classified
  status-owned, it would let completion overlap the panel.
- `:7195` and `:7212` are the `status_buffer` / `status_left_buffer`
  `TextBounds.top` — status text bounds, classified document-owned.
- `:7351` clips global minibuffer CANDIDATE glyphs to the dropdown's
  band anchor; classified document-owned, they would be clipped
  against a boundary the dropdown does not sit above.
- `:8561` (`edge_scroll_direction`, document edge scrolling) was
  missing entirely, leaving it tied to the old bottom.
- `:8077` is `code_caret_rect_in_clip` — caret clipping, not
  completion placement. Its class was right, its label wrong.

Every production site is now individually verified against the source
and tabulated with what it actually is. The census is stated as
arithmetic a reader can check: 29 matches = 20 production + 1
definition + 8 test sites.

Root cause recorded in the revision history: rev 3's table was built
from a `grep | head -20` over 29 matches, which is precisely why
`:8561` vanished. The minibuffer's status-owned status is now argued
from Q#BP14b rather than assumed — it is global, bufferless chrome
anchored to the status band, so all four of its sites stay with the
band.

**R3-2 (high) — clamps preserved.** The three equations permitted
negative coordinates on a surface shorter than its chrome, where
today's `text_area_bottom` clamps with `.max(0.0)`. All three now
clamp at zero, which keeps the "exact formula" exact exactly where it
matters most.

**R3-3 (medium) — attachment rejection classified SHARED.**
`validate_cells` also rejects `cell.attachment.is_some()`
(`terminal.rs:305`), whose error text reads "which terminals never
use" (`:190-191`) — phrased as a terminal-specific fact, which is why
rev 3's "exact split" missed it. Panels implement no attachment
rendering in Stage 2, so a `PanelFrame` carrying one describes a
surface the GPU would silently not draw; shared rejection fails closed
on the producer side instead. The message is reworded grid-neutral
when it moves, and giving panels attachment rendering later moves the
rejection back deliberately rather than by default.

A2B-4 now names the counts on both sides (twelve document-owned move,
eight status-owned do not) and carries the three symptom-bearing rows
that a plausible misclassification produces. §9 records that the GPU
three-boundary split belongs to 2B, not 2A — it is only observable
once a band can be installed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:36:58 -04:00
Levi Neuwirth 857a86ff59 docs: bottom-panel Stage 2 framing (revision 3)
Closes review round 2 — 1 blocking, 2 high, 1 medium — decides both
remaining open items, and re-integrates canonical `main` @ `ccf29e3`
(#172 + #157; documentation plus one `src/buffer.rs` regression test,
no protocol or Stage 2 source anchor moved).

**R2-1 (blocker) — the seam is three boundaries, not one.** Rev 2 asked
for a single document-bottom accessor. That is wrong: once a panel is
installed the present single value must DIVERGE, because several of its
consumers must not move at all. `text_area_bottom`
(`pmacs-gpu/src/main.rs:8490`) is today `status_band_top`,
`geometry_capacity_bottom`, and `document_text_bottom` at once. Rev 3
defines all three, classifies every one of its ~19 call sites as
status-owned / document-owned / geometry, and records that four sites
rev 2 named (`:3175`, `:3185`, `:6601`, `:6607`) consume a status-band
HEIGHT and no bottom coordinate at all, while the status background
`:5908` and status text `:7134`/`:7922` must stay at the physical
window bottom.

The acceptance is now a contrast assertion: installing a panel moves
every document-owned consumer WHILE the status band stays
pixel-identical. "Everything moved" alone is passed by a blanket
rewrite of the helper, which is exactly the wrong implementation.

**R2-2 (high) — epoch exactness.** `accept_frame_geometry` returns
`Advanced | Duplicate | Rejected` instead of a boolean that cannot
separate reconcile-needed from already-current from stale; if a boolean
is ever kept internally it must be named `advanced`, since `Duplicate`
is also accepted. Rev 2's exhaustion wording permitted retaining stale
geometry, which is not fail-closed — a real resize after exhaustion
would keep painting a panel sized to disowned geometry. The grid path
now clears `frame_geometry` to unknown and reconciles hidden, and the
frontend takes a terminal latch so a retained matching `Present` cannot
resurrect the band; only a fresh session clears it.

**R2-3 (high) — parent acceptance 52 splits.** 2A has no semantic panel
projection, so it can only prove the extracted painter honors an
explicit `None` map plus the `src/window.rs:562` comment fix. The real
contract is production-reachable only in 2B and is reasserted there
beside 42/43/44.

**R2-4 (medium) — touched gates named**: `statusline_segments_acceptance`,
`m11_5_semantic_acceptance`, `gpu_initial_target_acceptance`,
`gpu_font_acceptance`, beside the vterm, folding, and GPU suites.

Open items decided: `BASE_DIVIDER_HEIGHT = 4.0` at scale 1.0, scaled by
`FontMetrics::scale`, whole strip painted `ui.divider` and used as the
exact hover/drag hit rect; `TEXT_TOP` stays `16.0` unscaled, with
Q#BP15a's "all quantities use the frontend's current scale" narrowed to
font-derived metrics and the divider. Wholesale surface-inset/DPI
scaling is recorded as separate work, not smuggled in.

The ledger's bottom-panel lane keeps its census correction and gains
the three-boundary one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:23:49 -04:00
Levi Neuwirth 69f9a1bdf9 docs: bottom-panel Stage 2 framing (revision 2)
Closes review round 1 — 2 blocking, 3 high, 3 revision points — and
rebases the ground truth onto `main` @ `d152120`.

Both blockers were rev 1 asserting something the parent framing
already decided otherwise:

- **R1-1.** Rev 1 said all 23 census reads route through
  `primary_document_window`. Q#BP14 routes only the **Projection**
  class that way; focus/input (#13-#15, #23), focus chrome and
  surface-routed (#16-#19), and focus/session (#20) keep their own
  authorities. Rev 1's rule would have broken remote-op validation and
  application, `DispatchIdle`, presence, focused search/menu/completion
  routing, and terminal bell ownership. §3.2 restores the four classes
  as a table and the acceptance asserts each separately — the
  focus-class assertions are the load-bearing half, since a test that
  only proves "the document is used" passes with them wrongly
  rerouted.
- **R1-2.** The three `src/statusline.rs` active reads have two
  dispositions, not one. Only `:644` selects the wrong window; `:629`
  and `:675` must keep tracking actual focus, because grid contexts
  need a truthful `active`, revalidation must notice a focus change,
  and parent acceptance 42 requires a document provider to be able to
  observe `active = false` while the panel is focused.

The three high findings:

- Q#BP2S1 resolves to frontend-owned epochs (option 1) — a font or
  scale transaction can need to invalidate an old `PanelFrame` while
  the derived `CellSize` is identical, which daemon value dedup cannot
  detect. Rev 2 adds the four-row transition table, splits grid
  allocation from semantic acceptance into two APIs rather than one
  ambiguous method, moves the grid allocator off `saturating_add` to
  checked-with-fail-closed, and defines the initial epoch and both
  exhaustion behaviors. Rev 1's "rejects a lower-or-equal epoch
  carrying different data" was itself wrong: a lower epoch carrying
  identical data is still stale.
- The `panel_capable` flip is narrowed to an authenticated semantic
  session negotiated at **v21 or later**. Denying a v20 peer the new
  events is insufficient if the daemon still places its window in a
  side panel it cannot render — the gate is on placement.
- Parent acceptance criteria 37-55 are declared authoritative and
  mapped to slices 2A/2B, with rev 1's eleven drafts demoted to
  refinements. The painter-extraction criterion now pins cells, the
  returned cursor, the focused window's `view_top` mutation, and
  passive-window state.

All four scout obligations are closed (§5), and the pixel formula is
treated as contract work, not implementation detail:

- The shared/terminal-only validator boundary is named exactly.
- Four new outbox tail-coalescing tags beside the existing four.
- **`State::mono_advance` is unsafe to adopt**: absent a `FontFacts`
  probe it samples the document's first shaped glyph, which would make
  panel columns document-dependent. The declaration uses the existing
  stable normal-face `probe_mono_advance` instead, and declares zero
  usable geometry when it returns `None`.
- `BASE_DIVIDER_HEIGHT` does not exist. Rev 2 decides its scaling and
  requires **one** document-bottom accessor routing every consumer
  (caret, hits, minimap, terminal geometry, clipping, edge scrolling)
  — a second unrouted seam is precisely the Stage 1 `Layout::compute`
  two-caller defect. The concrete base value is left open for round 2.

Also: the coherence statement now names journey steps 7-10 instead of
claiming none, and drops rev 1's overclaim that this advances
background-work visibility — a panel gives output a placement but adds
no join key to COHERENCE §9's four disjoint activity planes.

The ledger's bottom-panel lane is updated from "no branch and no
framing yet" to the framing's real state, and carries an explicit
correction: that entry was itself the source of rev 1's census
mis-statement.

Factual corrections: `InitialTargetResult` is at `message.rs:1145`;
`primary_document_window` has four references and two production paths
(`daemon.rs:1639`, and `daemon.rs:2998` via `primary_document_buffer`,
which is census #22); fifteen PRs merged since the parent's last
re-scout, not eleven.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:13:44 -04:00
Levi Neuwirth c2eeb60dc7 docs: bottom-panel Stage 2 framing (revision 1)
The re-framing `docs/bottom-panel-framing.md` rev 4 §2 requires before
Stage 2 (the GPU panel band) is implemented. Re-scouted against
canonical `main` @ `5aa9044`, protocol v20.

It does not restate the parent's decisions; it records what the
re-scout found. Every source anchor Stage 2 inherits had moved, but
none of the parent's mechanical model was falsified. Two facts held
and are load-bearing: protocol is still v20, so Q#BP9 resolves to
**v21** with no reservation needed, and both byte pins
(`InstanceMessage::InitialTargetResult`,
`FrontendEvent::TerminalPointer`) are still their enums' final
variants.

Four findings:

- **Q#BP2S1, new and open.** Stage 1 landed a daemon-side geometry
  epoch allocator (`declare_frame_geometry`), but Q#BP15a specifies a
  frontend-owned epoch echoed by every `Present`. The landed allocator
  also dedups on value and uses `saturating_add`, which is neither
  wrapping nor the fail-closed the framing asks for. Three resolutions
  are stated with a recommendation.
- **The §1.3 census is essentially unrouted.** Stage 1 built the
  `primary_document_window` seam but it has one production caller;
  ~80 direct `.active` reads remain. This is Stage 2's bulk, not its
  tidy-up, and the stage plan sequences it first.
- **The statusline active read is three sites, not one.**
- **Four scout obligations are still open** and are named rather than
  papered over, including the GPU-side pixel formula inputs.

Also carries the staged plan, draft acceptance criteria, the coherence
impact per `COHERENCE.md` §20 (§14 is the section it serves), and four
questions for the user.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-25 19:13:44 -04:00