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
Both PRs added ClientState::notify, so the merge failed with E0592. #266's
is kept: it queues the sentence through fold_outgoing, which runs
settle_status, so the owner is still recorded. #279's direct-write notify
is deleted.
#266's rebase() rebuilt the view without settle_status, so a relay that
moved the view off the editor left the message owned, and an equal
editor reopened afterwards took it back. rebase() now settles it, pinned
by a_rebase_off_the_editor_ends_the_message_standing (red before).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The two generator strategies re-parsed the grammar inside their
prop_map, once per generated case: the same defect improvise-8shu fixed
in improvise-io. A OnceLock takes the lib test binary from ~0.9s to
~0.06s on the same machine and load.
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 toast hid every status after six seconds, so a refusal the user was
still fixing vanished with its prompt open: su4u's bug on a timer. The
browser now asks the TUI's question. EditorSession moves into
improvise-protocol, ClientState records which editor a status was
written in, and RenderModel::status_stands tells the toast to hold it
until that editor closes.
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
EnterEditAtCursor asks core::uneditable, the commit door's own question,
now shared with committable_cell_key. The browser's advance asks the
projected id at the landing cell; lockstep pins both ends on a fold and
on a formula's cell.
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
improvise-nacd and improvise-pr8t.
The b114 rule refused a coordinate that is a group. The invariant it
protects (improvise-gw18) is positive: every coordinate names an item.
Two writes onto a stored record fell in the gap and were accepted: a
measure that is no item at all (Bogus), and a parent group that holds no
item directly (Totals), where items_in_group is empty.
App::group_refusal becomes App::non_item_refusal. It refuses any
coordinate that is not in Model::effective_item_names for its category.
Formula targets count as _Measure items, so a write under a formula is
still refused by the Derived rule, with the true reason.
Tests:
- pr8t pins the same-name case: an item named like a group of its
category is writable. Removing the old exemption fails it.
- nacd is red at 202e7eac for both Bogus and Totals.
- A third test pins that a formula cell is refused for the formula.
Two reduce.rs fixtures wrote _Measure=Revenue without the item. They now
add it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
landed_seed assumed the client's cache was stale only at the cell the frame
wrote. Under prune_empty a write can take its row away and renumber the rest,
so the advance landed on a position the cache still filled with another
record. The advance after a write now opens the editor awaiting its seed, and
the client settles it from the grid it holds once nothing it sent is
unanswered. A seed never overwrites typing that arrived first.
This also removes CellValue::edit_text from the client bundle, so the
nontrapping-float-to-int wasm-opt flag a093d126 added is no longer needed and
is reverted (improvise-xbgm).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze