A formula met at two fold keys in one evaluation (an IF branch naming an
item of another category) must be evaluated at each. A memo keyed by the
formula alone read circular there and passed the whole suite; this test
fails under it and nothing else does. The diamond A1 = B2 + C3 over D is
pinned too.
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
RecordHash drops _Measure, so every column of a row carries the same record,
and with a collapsed _Measure group the first is the fold. aggregate_refusal
now finds the position by the whole key the write names. Rival for the
stored-record test: stored records through cell_id fails it alone.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The host projects Feb x Units as a Record, and #217 refuses the write:
aggregate_refusal judges the record at its first column, the Money fold.
Also pins that a stored record stays writable whatever the host view does.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
Host::mutating_frame rejects on refused || aborted, and deleting the
aborted arm left the workspace green. The wire table cannot see an
abort-without-refuse because that arm is what rejects it, so the new
table applies each failing ModelCommand's effects to a bare App and
asserts it refused, pinning which cases abort so it is not vacuous.
The arm is kept as defence: AbortChain shows an abort need not be an
error, so making abort-without-refuse unconstructible is declined.
Also keeps rtud's three key-path table rows (load, import-json of an
absent JSON, a formula that does not parse) as added coverage; rtud's
prose fix lands separately.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
A browser frame that reframes and then places the cursor was folded whole by
the client before the relayed ResetCursor arrived, so the reset undid the
placement while the mirror (reset, then placed, in frame order) kept it.
Host::mutating_frame now relays to the asker every later cursor-placing
command of the frame after its reset, so the client ends where its mirror
and the TUI do. Excluding the asker from the reset was rejected: a frame
that places nothing would reset the mirror silently (gotcha 24).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
Under Avg/Min/Max the strip sums each row's aggregate, so a Max
measure's total moves on z for the same reason a formula column's does
(improvise-wm20). Docs only, from the #203 review.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-52eq: the AGENTS.md recipe searched `bd show` output for "not
found" and printed "all resolve" when there was none, so a bd that was
missing, failing or never run passed the audit. scripts/bd-audit-ids
keeps the file scan and exclusions, then `comm -23`s the cited ids
against `bd list --all --limit 0 --json`. A dead id is exit 1 and is
named. A bd that does not answer is exit 3, never a pass.
scripts/bd-audit-ids-test.sh has eight cases on a throwaway repo with a
fake bd. The old recipe as a rival fails 7 of the 8. Disabling the
comparison fails the dead-id case, and an empty exclusion list fails the
pass case. CI runs it with the other named suites. The "One environment
note" now reports which forms the guard refused and allowed, as observed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
A regular category's formula target is a real item, and replace_formula added
the new target without removing the old, stranding it as an empty item and
appending the new one. Until formulas have a stable identity (improvise-sboz),
a target change outside _Measure is refused and the editor stays open.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The cell-editing commits own their buffer clear at their head instead of
the keymap welding one after them, so the re-seed survives and Enter,
Enter writes nothing.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-28ri, from the #211 review at 6ccad4b: the recipe's grep has no
-r, so the skills' symlinks arrive as directory operands and are never
read. -d skip only silences "Is a directory", and sort -u would absorb a
duplicate anyway. Measured with BSD grep 2.6.0 and GNU grep 3.12: 3092
matches either way, 462 distinct ids. GNU grep -r follows those
symlinks (3107), so -r must not be added.
improvise-oga4: "Shipping a wave"'s opening and "Repository policy over
the Beads blocks" now point at the ADR exception. The wave SKILL.md
pointer is unwrapped, so a grep for "ADR pull requests" finds it.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-17ak: render_footer hand-wrote l/Tab, h/S-Tab and q/Esc, a key
table no check could read, and already short of the keymap (n, → and ? were
bound and not advertised). It now draws the page indicator followed by
hint_line(Help), truncated in cells.
Derived rather than deleted: the status row carries the same hint, but
draw_status prints status_msg instead of it whenever a message is standing,
and nothing clears the message on entering Help, so deleting the footer's
keys would leave a user who saved and then pressed ? with none.
The guard renders the popup at 140 columns on every page and requires the
footer row to contain hint_line(Help). Red at base; and a literal copy of
today's string fails it as soon as the keymap drops a binding.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
improvise-7epn: two cases on the one-tick-per-read clock. One resolves on
poll 3, and one times out after polling up to the deadline. Replacing the
post-poll deadline check with `break` now fails exactly those two
(23 passed, 2 failed), where before it left the suite 23/23.
improvise-lnsh: when `cargo metadata --locked` refuses the opfs-contract
lock, build.sh now says not to remove --locked and names
`cargo update -w --manifest-path harness/opfs-contract/Cargo.toml`.
Checked by hand against a lock with pest pinned to 2.9.1. Evidence is in
context/test-evidence/tooling.md.
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
App::with_storage now ends with settle_normal_mode, the rule every batch
ends with, so the host's startup mode matches the one Sessions::connect
hands a client. #207's startup-slice test gains a records-opening case.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
The user's ruling: "An ADR isn't approved until I say it is." CI, the
reviews and the adversarial review stay required as gates, but none of
them is approval. SKILL.md's "not on a review" pointer is reworded to
match.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze