This head already carries #272 (p6v6), merged at d9c65181.
Conflicts:
- resolve.rs advanced: main's `reopened_at` (decline an aggregate or a
formula's result) now takes this branch's `writes` and answers the
decline arm first, then `awaiting_seed` or `opened_on(state, ..)`, as
the #282 review prescribed for this combination.
- context/test-evidence/{clients,command}.md: both sides' entries kept.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
`fold_frame` wrote the rejection sentence straight into `view.status_msg`,
so it was in neither `settled` nor `in_flight`, and the xd3q rebase erased
it whenever a relay queued behind a frame still in flight: an in-flight
frame's own `SetStatus` was replayed over it.
`ClientState::notify` queues the sentence as a view-only entry at the tail
of `in_flight`, where `fold_outgoing` puts a frame the host never answers.
Frames sent before it replay first, relays queue ahead of it, frames sent
after it may replace it, and it settles when everything ahead is answered.
Writing it into `settled` too was tried as a rival and fails the same way.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The advance no longer reaches the commit door on a fold or a formula's
cell, so the two older advance tests stopped seeing it (measured: the
cell_key rival passes them). Two tests seat the editor there directly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
mdfc put the "any unanswered write" check inside `advanced` alone, so an
editor opened with i/a (`edit_or_drill`) still seeded from the cache the
write had made stale: `i 9 Tab Esc i 5 Enter`, typed faster than the
answer, stored 15 in the browser where the TUI stores 195.
The in-flight half now lives in `opened_on`, so no opener can forget it.
`advanced` keeps the "this frame writes" half, since the commit being
resolved is not in flight yet; dropping it fails the 3tt4/3uzg tests.
Stacked on #227 (fix/fy9o-reopened-editor-reseeded, de4a7d78).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-uln5. The browser painted every help-overlay row plainly,
including six whose keys go upstream to a host that answers nothing:
/, n, H, z, Enter, and search mode's Enter/Esc. Every ex-command row was
painted plainly too, although `:` opens a command line the browser
cannot run. A dead key is worse than a gap: the keys typed after it land
on live bindings (improvise-koh6).
- resolve::NEEDS_THE_HOST is now the one list of names this client has
no arm for. KeyRow keeps the command it was built for.
- The browser's RenderHelpRow draws a listed row, and every ex row while
the command line is stranded, as `dim` with "(not in the browser yet)".
It marks rather than omits: both front ends keep drawing the same rows,
the feature stays findable, and web/ needs no change.
- the_command_names_that_still_need_the_host_are_these now drives that
list rather than a table of its own.
- every_help_row_the_browser_cannot_run_is_one_it_marks holds it from
the other side: silent help rows == listed rows, and no hint line
names one.
- every_advertised_command_answers_in_its_own_mode no longer restates
the black holes.
Red: a_help_row_the_browser_cannot_run_is_drawn_marked at 202e7eac.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
A pruning commit is answered with ResetCursor and an echo of its own
placement. A frame sent before that answer had already been folded
optimistically, and fold applied the relays straight onto view on top of
it. The echo is per frame, so the later placement was never re-sent: the
client sat at (1,0) while its mirror sat at (2,0), and the next write
landed on West where the TUI writes East.
#246 already queues each relay behind the head frame, so the queue holds
the right order. fold now rebuilds view as settled plus everything still
in flight whenever a relay queues, the same replay a refusal uses.
Replaying from settled, rather than re-applying what queued behind the
relay, is pinned by a ViewBack test: re-applying stepped back twice.
Stacked on #246 (fix/hv0r-jpba-relays, 790a33ba).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-mdfc: an untouched commit seeded the reopened editor from the
cache even while an earlier write from this client was unanswered, so
Tab Tab at the rightmost column stored 505 where the TUI stores 5095.
`advanced` now awaits when this frame writes or any in-flight frame is
awaited. The lockstep test sends three Tabs before answering any.
improvise-4cb5: `settle_awaited_seed` also requires an editing mode, so
an Esc before the answer no longer leaves the seed in Normal's buffer.
improvise-mgis: `OpenedEditor::awaiting` loses `serde(default)`; a frame
missing it fails to parse as the missing field (protocol.md §3, 1wl0).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-xoxh, found in the delta review of #246. deliver hands a client
what the HOST sent, so a relay must go through fold, which queues it
directly behind the awaited head. fold_outgoing queues it at the back as
the client's own frame. That is the pre-jpba ordering, so a test built on
deliver could not see a jpba regression. The bug was latent, because
deliver's one caller sent only view-only frames.
deliver_queues_a_host_relay_behind_the_awaited_head was red at 790a33ba.
With deliver fixed, putting the pre-jpba ordering back in settle_relay
fails this test and nothing else in the lockstep binary.
The renaming test that came in through the gh/main merge now folds its
own commands through fold_outgoing. It also asserts that the commit is
the one awaited frame, so the two doors are told apart.
Stacked on #246 (fix/hv0r-jpba-relays).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
socket.rs: EditTarget takes the union (document_generation, cell, version,
opened); typed_target_frame keeps #205's untouched early return, then claims
as read at the captured generation. Evidence appended after gh/main's.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-oplp: EnterTileSelect and the browser's resolver both opened the
tile cursor at 0, which is _Index, so `T Enter` or `T r` put _Index alone
on Row and emptied a pivot grid; the browser worker then persisted it.
The rule is improvise_protocol::opening_tile, over &[TileEntry], called by
both ends. TileEntry gains `regular`, filled host-side from CategoryKind in
project::tiles_of, because a thin client cannot tell _Index from a user
category without sniffing names. _Index/_Dim stay tileable (#192).
A lockstep case presses `T Enter` on both ends and pins which category
moved; it was red at base (_Index on Row), and fails for a TUI-only revert
and for a `regular` filled true everywhere. contract.spec.js counted three
ArrowRights from tile 0 and now asserts `T` alone selects Product.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
EditTarget captures document_generation when the editor opens, and
BasisCache::claim_as_read claims it rather than the current one. The
layout tier stays current, as clients.md argues for 2wu3. A red test
through a real Host over MemoryFs: A opens North/Revenue, B loads a
same-shaped document holding 5555, A commits "42" -> DocumentReplaced,
5555 survives.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-5xbq: resolve::page_rows is now the one home of the page-scroll
rule; the TUI's Move calls it instead of repeating visible_rows * 3 / 4.
A lockstep case pages (PgDn/PgUp, Ctrl-d/Ctrl-u) on a non-square window,
8 rows by 3 columns, and pins the TUI's landing rows as well as comparing
host and client, since one home moves both ends together. Both faults
the ticket injected (page_rows = window_rows / 2; paging by window_cols)
now fail at the first PgDn, before and after the move to one home.
improvise-1c5m: the help overlay draws the mode's own hint (hint_line,
the string #hint carries) in a sticky footer, so a narrow window whose
app bar wraps under #help still says how to page. Playwright case at
600px, red at base, and red again with the footer made non-sticky.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
Enter or e in the formula panel opens FormulaEdit on the formula under the
cursor, seeded with its text. The editor names the formula by a FormulaId
captured when it opens, and commits through a new ReplaceFormula effect and
ModelCommand. The edit keeps the formula's position, renames it in place when
the target changes, and refuses out loud if the formula was removed or
rewritten meanwhile, if the new target belongs to another formula, or if the
text does not parse. A refusal leaves the editor open over the user's text.
FormulaCache now carries each formula's id, so the browser composes the same
edit, and a formula-row click opens the editor on the clicked row.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
Resolved context/test-evidence/clients.md: gh/main's version, whose
improvise-w7qd/5kdr paragraphs continue the entry above the conflict,
followed by this branch's two improvise-0j0f/4img entries.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze