kb: browser drill stages identically, because staging and the basis check answer different questions
User ruling. An agent proposed that a browser write through with ordinary SetCell commands rather than staging, since those are already basis-checked. The substitution does not hold: the basis check is anti-staleness coordination between clients and answers whether a picture is CURRENT; staging is an editing-session abstraction for one user and answers whether they want to KEEP what they typed. Write-through would silently remove the ability to abandon a drill -- invisible to any test that asks only whether an edit lands. The convergence that makes it cheap: clause 4 is delivered, so a staged commit is a batch of SetCells taking its atomicity from the transaction that already exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FC2jVn8Au7E9JPkw6bwXXH
This commit is contained in:
@@ -319,6 +319,7 @@ doctrine(a_ticket_can_be_one_level_too_high_and_its_fix_unimplementable,'improvi
|
||||
doctrine(a_cursor_rule_copies_between_panels_only_if_the_list_is_homogeneous,'improvise-uej8. delete-view-at-cursor and delete-formula-at-cursor step the cursor back whenever it is above zero and are RIGHT to, because their lists are one-kind so a cursor that does not move cannot escalate. The category panel interleaves headers and items, and there the unconditional step-back is WORSE THAN THE BUG: it retreats the first item of a category onto its own header -- the same escalation one row earlier -- and a held d walks backwards. The conditional form (step back only when the row below is not a sibling item) is total because CatTreeEntry has two variants. The property to check when copying a cursor rule is HOMOGENEITY, not the panel it came from -- and \"check the other panels for the same shape\" has the answer no, by argument rather than by inspection.').
|
||||
doctrine(deliberate_slack_can_become_load_bearing_with_nobody_editing_it,'Dirty::of(CellData) reporting formulas: true was written as deliberate over-reporting -- gotcha 35 records that over-reporting costs one redundant projection while under-reporting leaves a client wrong forever. improvise-g3no made effect::RemoveItem actually able to remove a formula, so that arm is now LOAD-BEARING and narrowing it would strand every client formula panel permanently. Nothing guards it; the existing test covers the LayoutGeometry twin only (improvise-en4s). The line was not edited and its comment still reads as slack. So a change elsewhere can promote an over-approximation into an invariant, and the comment that justified the slack becomes the thing that invites its removal.').
|
||||
doctrine(two_tickets_at_different_priorities_can_be_one_bug,'improvise-25bf was filed P3 as \"the category panel Space cycles the axis silently\" and improvise-z336 carried, as its third finding, a P2 \"the Filter: X = Y status is stale exactly while the user tries to lift the filter\". They are the SAME bug: the status persists BECAUSE the key that moved the axis writes nothing over it. Fixing the P3 fixed the P2 most user-visible symptom, and the tickets treat them as unrelated. When a cosmetic bead and a mechanism bead touch the same surface, check whether the cosmetic one is the mechanism seen from the user side.').
|
||||
doctrine(a_coordination_check_is_not_an_intent_abstraction,'Two mechanisms can both make a write safe and still not substitute for each other. improvise-gd28: the basis check answers whether a client picture is CURRENT; staging answers whether the USER wants to keep the edit. Proposing write-through because the writes are already basis-checked confuses a concurrency guarantee with a user-facing capability, and the loss -- being able to abandon a drill -- is invisible to any test that asks only whether an edit lands. When arguing that mechanism A makes B unnecessary, name the question each answers before agreeing.').
|
||||
work_done('improvise-8tk',bug_fix,'ZZ/:wq quit even when the save failed, silently discarding the model. Save now aborts the effect batch on failure; save_to_current_path/1 returns Result<String,String>; three tests in ui/effect.rs.').
|
||||
work_done('improvise-907',bug_fix,'Import wizard step machine redesign: validation_error() gate at ReviewProposals (no-category and over-limit), history-stack previous() bound to Left on every step, and Done made a real confirmation step holding the built workbook. Superseded improvise-gfq, improvise-o1c, improvise-c6j.').
|
||||
work_done('improvise-6sq',wont_do,'Merging EditOrDrill and EnterEditAtCursorCmd into one EnterEdit{mode} is not safe: they differ in WHEN the cell value is read, not in a mode parameter. Added ui/app.rs::tests::i_and_o_diverge_on_an_empty_records_grid as executable evidence.').
|
||||
@@ -431,6 +432,7 @@ decision('improvise-zv8t',a_document_category_is_never_inferred,'fit_measure now
|
||||
decision('improvise-e5pf',the_mode_settles_where_the_grid_does,'resolve::changed_mode is DELETED. Every composition site emits the plain AppMode::Normal, and ClientState::fold settles against the grid it HOLDS via improvise_protocol::settled_mode -- one rule, also called by ClientSession::fold and App::settle_normal_mode, with App::normal_mode deleted as its host-side duplicate. Composition-time settling was PREDICTION: switch_view_at composes [SwitchView(target), ChangeMode(Normal)] and read the OUTGOING grid. Read-time derivation was rejected for having the same window and for moving the enumeration from composition sites to READERS, where a missed reader is silent. settled_mode settles the Normal pair ONLY -- Editing/RecordsEditing pass through -- which is why a records decision must ask the grid and never the mode. Same shape the repo already prefers: cursor_epoch over matching on CycleAxis, document_generation over matching on LoadModel, the dirty flag over a table of command variants.').
|
||||
decision('improvise-b8ot',carry_the_answer_on_the_node,'A delegated handler reads its operands off data-row/data-col/data-edit and its branch off the existing editable class, and derives NOTHING from DOM structure. gridPlan already counts DATA rows because a group header is not one, so walking up the table to find the row would be a second derivation of that and would disagree the moment a group header appeared. Ids name SURFACES only -- a per-row id must be unique per document and these are rebuilt every frame. The editor keydown/blur stay direct: one transient node at a time, and blur does not bubble. Five surfaces, one handler each, on the stable roots replaceChildren leaves in place.').
|
||||
decision('improvise-p7lh',the_totals_label_is_spelled_twice_on_purpose,'src/ui/grid.rs and web/shell.js both spell \"Total\", which fails design-principles section 5 -- would a third front end write this again. Kept local on the indicator() precedent, where the collapse glyph is spelled at both ends while the FACT is shared, rather than building a protocol string table for one word. Named as debt at the call site and in context/code/clients.md. ORCHESTRATOR RULING: this stands until a SECOND such string appears, at which point it wants a protocol home; one word is not a table.').
|
||||
decision('improvise-gd28',browser_drill_stages_identically_to_the_tui,'User ruling 2026-09-21: \"the staging scheme should be identical, because they are solving different problems.\" A wave agent proposed that a browser write through with ordinary SetCell commands rather than staging, since those are already basis-checked. The ruling rejects the substitution: the BASIS CHECK is anti-staleness coordination between clients and answers \"is your picture of this record current\" (ADR 001, gotcha 43, explicitly not a security boundary and not about intent); STAGING is an editing-session abstraction for one user and answers \"does this person want to keep what they typed\". Write-through would silently remove the ability to ABANDON a drill -- a user-facing capability, invisible to any test that only asks whether an edit lands. So pending_edits becomes client state, consistent with the view stack being client-local and the drill being ephemeral: all three say a drill is one person transient workspace. The commit is then a batch of SetCells taking its atomicity from clause 4, which improvise-yqpd delivered -- before that the batch could have half-applied, which is exactly what a staged commit must not do.').
|
||||
wire_carries(command,both_directions,'user intent up, projection commands down').
|
||||
wire_carries(snapshot,on_connect_only,'improvise-q08: full ViewState + render cache filled from current viewport').
|
||||
wire_carries(command,both_directions,'user intent client->server, projections server->client').
|
||||
|
||||
Reference in New Issue
Block a user