Merge pull request #93 from fiddlerwoaroof/docs/wave-j-facts
docs+kb: the map pointed at the wrong file, and that manufactured a false claim
This commit is contained in:
+11
-3
File diff suppressed because one or more lines are too long
+25
-12
File diff suppressed because one or more lines are too long
+19
-1
@@ -120,6 +120,8 @@ invariant(key_matching_is_variant_equality_never_unification,'scripts/kb-merge.p
|
||||
invariant(duplicate_category_sections_merge_in_pass_one,'Two ## Category: sections naming one category are ONE category, concatenated by ParsedFile::push_category in pass 1 (improvise-349m). Merging in pass 2 was not idempotent: build_category creates its own section item-groups before its own > lines, while format_md writes every item before every > line, so the reparse built the other order. push_category destructures PCategory so a fourth field cannot be silently dropped on the duplicate path. No grammar change was needed -- this is the tree-walker, not the format, and duplicate headers stay legal.').
|
||||
invariant(where_and_is_share_a_word_boundary,'where_clause, inline_where and is_expr are all compound-atomic with sp+ after the keyword (improvise-r08u). Non-atomic, pest auto-inserts WHITESPACE and SUM(Rev WHEREAS x = y) reads as a WHERE on a category AS. Note compound-atomicity ALONE refuses that input, since AS x cannot be one identifier, so the residue sp+ actually closes is WHEREAS=y -- a negative test needs both shapes.').
|
||||
invariant(evaluate_aggregated_takes_a_named_state,'Model::evaluate_aggregated takes Aggregation::{Exact, OverHidden}, not none_cats: &[String] (improvise-5jm5). The list was consulted once, for emptiness, in every revision since Axis::None was introduced -- the aggregation runs over key.0 via matching_values. Aggregation::of reduces at the boundary and the regression guard is the SIGNATURE. recompute_formulas and aggregate_raw keep &[String] because they walk the elements.').
|
||||
invariant(normal_mode_is_resolved_not_carried,'AppMode::Normal is resolved through App::normal_mode() when the mode is SET -- ChangeMode and ReturnToNormalUnlessMoved are the only two places a batch sets it, and CloseWizard reads the same function -- so no keymap binding and no commit command carries an exit mode, and none can forget one (improvise-sihr). It reads the batch OPENING layout (gotcha 23), which is the property rather than a defect: that is the grid the user was looking at, and toggle-records-mode works in both directions because of the staleness rather than despite it. Carrying the entry mode on MinibufferConfig was the ticket proposal and was declined -- carried generically it also returns CategoryAdd to CategoryPanel. Known gap, filed as improvise-e5pf: Command::ChangeMode(Normal) is on the wire and reduce_view folds it without the resolution.').
|
||||
invariant(the_frame_basis_follows_active_view,'The cursor frame basis re-bases AT THE SWITCH, in the loop, as soon as active_view moves -- not at the next geometry effect. Guarding the COMPARISON on the two names agreeing (PR #78 round 2) exempted the first geometry effect after a mid-batch SwitchView from the check altogether, which is under-reporting and gotcha 35 expensive side. SwitchView stays ViewOnly: the basis is App own bookkeeping and none of it crosses the wire, so this needed no protocol change. Costs one extra view_frame per batch that both switches and moves geometry, DrillIntoCell paying most.').
|
||||
doctrine(bug_first,'write a failing test demonstrating the bug before fixing it').
|
||||
doctrine(unit_over_e2e,'prefer small unit tests over integration/e2e').
|
||||
doctrine(suite_speed,'Two numbers, because the ambiguity between them is what let this drift: NO SINGLE TEST BINARY may exceed 2 seconds, and the WORKSPACE WALL CLOCK is the sum of them. Measured at 22d43b2: 1309 tests over 33 binaries, ~2.2s wall, longest single binary 1.42s -- so the workspace is already past 2s and the rule that bites is the per-binary one. Re-MEASURE rather than re-assert: cargo test --workspace prints a \"finished in\" per binary. Excludes compilation, and excludes harness/ (Playwright, minutes, its own CI job).').
|
||||
@@ -260,13 +262,27 @@ doctrine(a_narrowing_optimisation_owes_the_correctness_test_its_predecessor_pass
|
||||
doctrine(a_count_in_an_assertion_is_not_a_totality_guard,'assert_eq!(cases.len(), 13) with a comment reading \"a fourteenth declaring LayoutGeometry must come here and say so\" is false: a reviewer added a fourteenth pub struct declaring that kind, pointed at by nothing, and the whole 1439-test suite stayed green. Adding a variant is compiler-forced only where an EXISTING ARM MUST POINT AT THE NEW THING -- answering for it is forced, being reached is not. Same shape as improvise-5ubs (a cyclic-successor all() omitting a variant nothing points back to) and it appeared INSIDE the PR that found 5ubs. A count over a hand-written list asserts that the list has not shrunk, never that it is complete.').
|
||||
doctrine(a_measurement_that_a_change_is_free_is_not_evidence_that_it_is_correct,'The sharpest lesson of the backlog campaign, in the author own words: \"I asked what does this cost, got nothing, and reported it as though it had also answered is this right.\" improvise-mcq4 narrowed an all-views frame observation to the active view, measured the narrowing at 12.41ms to 12.56ms -- free -- and kept it as \"the honest shape\". It cost a multi-client invalidation no test named: a CellData write pruning a row out of another client view became invisible, and gotcha 24 says that is strictly worse than doing nothing, because the diff then believes the bystander is current and it stays stale permanently. A performance measurement answers ONE question. Running it does not discharge the other, and a change that costs nothing is the easiest kind to keep for bad reasons, since there is no budget forcing anyone to justify it.').
|
||||
doctrine(a_bug_can_hide_a_bug_and_the_tell_is_a_failing_precondition,'The reviewer P1 probe for improvise-mcq4 would not run: its precondition -- that the bystander grid really loses a row -- failed. Cause was a SECOND bug: project::grid_in builds a NON-ACTIVE view with GridLayout::new, which does not apply prune_empty, so the bystander projected grid never pruned. Measured: 3 rows via grid_in against 2 via with_frozen_records. The first bug was real and logically reachable and COMPLETELY UNOBSERVABLE because the second one masked it. Fixing grid_in is what made the P1 demonstrable; the P1 test is what found grid_in. So a precondition that will not hold is not a broken test to be relaxed -- it is a finding. Third instance of the GridLayout::new versus with_frozen_records trap (view_frame was the second): improvise-t6db.').
|
||||
doctrine(a_list_checked_for_length_reads_like_a_list_checked_for_completeness,'assert_eq!(cases.len(), 13) carried the comment \"a fourteenth declaring LayoutGeometry must come here and say so\". A reviewer added a fourteenth pub struct declaring that kind, pointed at by nothing, and the 1439-test suite stayed green. Both checks are written as an assert_eq over a number and are indistinguishable at the call site, but a length check asserts only that the list HAS NOT SHRUNK. Completeness needs something that makes the new case unrepresentable-unless-declared -- an exhaustive match, or a derivation off the thing being enumerated. Fourth instance of the improvise-5ubs shape, and it appeared INSIDE the PR that found 5ubs.').
|
||||
doctrine(it_broke_no_other_test_answers_observability_not_correctness,'The sibling of a_measurement_that_a_change_is_free_is_not_evidence_that_it_is_correct, one level up and in the same PR. Fixing project::grid_in to honour prune_empty for a non-active view broke exactly ONE test -- the probe written that same hour -- and that was offered as evidence nothing depended on the old behaviour. It is equally consistent with NOTHING HAVING TESTED IT, which is what it was. The honest answer came from asking WHY instead: the unpruned projection was internally consistent (seed and steady diff both went through grid_in, so they agreed with each other) and externally wrong (a browser client on a non-active view was shown phantom rows its own config prunes). The author was right, and right for a reason not yet established when they said it -- the difference between those two states is the entire value of the review loop.').
|
||||
doctrine(a_property_test_over_two_configs_must_make_the_configs_actually_differ,'Writing the improvise-t6db host-view-independence property -- grid_in(app_on_A, \"B\") == grid_in(app_on_B, \"B\") -- the first version PASSED ON THE BUGGY TREE. It made the two views differ by turning prune_empty OFF in the second: plenty of differing config, and completely vacuous, because with nothing to prune the two code paths agree. The fixture must contain an ACTUALLY EMPTY ROW and leave BOTH views pruning. A test written the first way looks thorough and asserts nothing -- this repo characteristic defect arriving inside the very test meant to close it. Same trap named in improvise-iot6, whose fix needs the same non-vacuous fixture.').
|
||||
doctrine(a_gate_that_can_exit_zero_answers_every_question_it_gates,'A flag ADDS a question; it never selects one away. A disclaimer line saying a check did not run still exits 0, and exit 0 is what CI branches on and what a human reads as clean -- so the rule is stated over the EXIT CODE rather than over the log: a tree with a long comment run must not pass ANY invocation that can exit 0. improvise-ijql: --check-evidence early-returned past the comment cap, and three agents hit it in one campaign. Note the orchestrator pointed at the improvise-rm4s precedent (where the fix was to make the success line NAME ITS SOURCE) and the author argued against it correctly: a provenance line qualifies an answer the run GAVE, a disclaimer qualifies one it DID NOT. --self-test and --list-doctests are excluded by decision, not exempted -- the first scans no tree, the second renders no verdict.').
|
||||
doctrine(a_dispatch_bug_is_invisible_to_unit_self_tests,'When every unit in a file is correct and the defect is in main() argument dispatch, no fixture-level table can reach the wiring -- the guard must INVOKE THE PROGRAM, as a subprocess over a temporary tree. improvise-ijql was exactly that shape: every function in check-comment-runs.py was right, and the early return in main() was the bug. Its guard needs both arms: restore the early return and the clean-tree case must fail, AND force the cap to always report a violation, or an always-failing cap satisfies the first arm.').
|
||||
doctrine(job_metadata_outlives_the_artifacts_it_describes,'An artifact-lifetime incident is still diagnosable after the artifacts 404: run, job, step and log APIs retain about 90 days where artifacts here retain 1. So a post-hoc artifact COUNT is not evidence about the incident, it is a reading of retention -- improvise-864a 0/1/0 table could not have been produced by expiry at all, since the newest-finalized expires LAST and expiry can only yield 0/1/1 or 0/0/1. Corollary that did the real work: widen the sample before theorising. The ticket framing of \"two of three artifacts\" was a SAMPLING ARTEFACT that made a two-day, sixteen-failure pattern look like a three-run concurrency race, which is the wrong shape entirely.').
|
||||
doctrine(a_control_you_already_have_is_cheaper_than_the_hypothesis_you_were_about_to_test,'Diagnosing improvise-864a, the single observation that eliminated five competing explanations at once was a NEGATIVE one already sitting in a log: a run lookup reporting \"Found 2 artifact(s)\" -- its own harness-serve and wasm-bundles present, its own github-pages absent. Same run, same token, same call, same retention, so service flakiness, eventual consistency, retention, token scope and a v3/v4 backend mismatch all die together, since any of them would have taken the siblings too. The agent nearly did not fetch that log, having already established the timeline. Before testing the next hypothesis, ask what is already in hand that would discriminate.').
|
||||
doctrine(an_injection_that_does_not_fail_names_an_untested_path,'check_the_checker is written as \"inject the fault and watch it fail\" -- and the case where it PASSES is the more informative result, because it is evidence about the TEST rather than about the code. On improvise-sihr, forcing ReturnToNormalUnlessMoved alone back to AppMode::Normal left the three-mode characterization test GREEN. That is how the untested path was found: an empty colon line takes ExecuteCommand is_empty branch and settles through ChangeMode, so the effect that settles every colon line which actually RUNS had no test at all. Two tests exist rather than one because of it. When an injection passes, do not move on -- ask which path the test never reached.').
|
||||
doctrine(a_macro_effect_cannot_carry_a_derivation,'ChangeMode was a simple_effect!, which declares a one-field newtype writing one place -- so there was NOWHERE for a rule to live, and the exit mode had to be a constant in the keymap and in each commit command instead. That is why improvise-sihr could hide: every minibuffer mode returned to a hard-coded Normal and nothing owned the question. Gotcha 16 already warns the macro kind argument must be mandatory; this is a second cost of the same shape. Reaching for simple_effect! forecloses any answer that needs a derivation, so if an effect ever might have to RESOLVE what it writes rather than copy it, write it out.').
|
||||
doctrine(a_ticket_that_pre_diagnoses_its_fix_as_a_protocol_change_deserves_checking,'improvise-9c9o was filed saying the honest fix needs the basis to follow active_view, which SwitchView being ViewOnly prevents -- i.e. a protocol change, not a one-liner. It was neither. The basis is App own bookkeeping and none of it is on the wire, so \"the basis follows active_view\" was achieved by moving WHERE the re-base happens -- into the loop, at the switch -- and SwitchView stayed ViewOnly. A pre-diagnosis in a ticket is a hypothesis wearing the authority of a decision, and the author of the ticket is usually the person with the least time to check it.').
|
||||
doctrine(prose_that_calls_a_change_free_is_a_claim_and_is_checkable,'The sibling of prose_that_calls_something_permanent_forecloses_the_ticket, found one paragraph below it in the same section. gp.md section 11 said improvise-zv8t cost nothing -- \"no document that fitted before stops fitting\" -- reasoning that a tree splitting on a numeric category was refused as an artefact anyway. FALSE: the refusal is conditioned on used_features, so a fit that never split on the column SUCCEEDED, and the wide columns are exactly the ones a fit has least reason to split on. Measured with a throwaway ignore-probe on both sides: 100 records, a 100-item numeric Year beside a Cost that predicts Score alone -- pre-fix the fit lands on IF(Cost < 48.015..) with Year never touched, post-fix the preflight refuses at 101 features. The suite was green on either reading. A cost claim is as checkable as a permanence claim and is checked the same way: construct the document the claim says does not exist.').
|
||||
doctrine(forcing_by_name_while_budgeting_by_index_defeats_the_budget,'improvise-zv8t made fit_measure force header[..category_columns] to Forced::Categorical keyed by NAME, while encoded_width decides category-ness by INDEX. A document may legally name a regular category and a raw measure the same thing (a_category_and_a_measure_of_the_same_name_are_refused ships that fixture), so the MEASURE column was forced categorical by name match while the preflight still counted it numeric by index: measured 41 against 80 at 40 records. no_ambiguous_column_names ran AFTER preprocess, so the expansion the preflight exists to prevent was allocated and only then refused -- guard_the_cost_not_the_result defeated by an inconsistency the fix itself introduced, NOT the bounded approximation a_budget_check_is_advisory_or_exact_and_must_say_which licenses. Repair is a data dependency, not a moved line: as_categories runs the check and returns Result<Overrides>, so the Overrides preprocess needs cannot exist until the header is accepted. Guard is the SIGNATURE -- reordering the two statements is error[E0425], verified. Deliberate deletion of the check from as_categories is still possible and is stated rather than implied.').
|
||||
doctrine(a_mitigation_that_names_the_wrong_file_is_not_a_mitigation,'PR #91 deferred a wire-level gap on the ground that \"keys.rs emits no ChangeMode at all\". True, and irrelevant: keys.rs holds only BrowserKey, and the LIVE browser key resolver is improvise_protocol::resolve, re-exported as improvise_wasm_client::resolve -- which emits Command::ChangeMode(AppMode::Normal) at five sites. So the browser front end kept the very bug the PR fixed for the TUI, and the suite was green over a divergence the PR itself created. The sentence was in context/, which design-principles section 6 names as exactly the place a reason must not be wrong, because nothing can find it later. Before writing \"X does not do Y\" as the reason a gap may be deferred, GREP FOR Y rather than for X -- the claim that matters is about the behaviour, and the file you happen to have open is not the only place it can live. The orchestrator copied the same false clause into the bead it filed.').
|
||||
doctrine(an_example_that_works_for_another_reason_defends_nothing,'improvise-sihr resolved AppMode::Normal against a pre-batch layout (gotcha 23) and defended the staleness in prose with the case of toggle-records-mode, which does work in both directions. It works for a DIFFERENT REASON: ToggleRecordsMode emits change_mode(RecordsNormal) explicitly on entry, and on exit ViewBack restores the mode SwitchView pushed, so normal_mode() answer is overwritten in both directions and the staleness is neutralised there rather than exercised. The case that would actually test it -- a batch that CROSSES records-ness and then sets Normal -- was untested, and regressed: from a records grid, \":toggle-records-mode . enter-mode normal\" landed in RecordsNormal where it was right before. An example chosen because it passes is not evidence; ask WHY it passes.').
|
||||
doctrine(a_list_checked_for_length_reads_like_a_list_checked_for_completeness,'assert_eq!(cases.len(), N) catches a case being DELETED from a list and never one being ABSENT from it, and the two are written identically. Four instances here: AppMode::all(), Rejection::all(), and twice inside one PR. A cyclic total after() does NOT close it -- answering after() FOR a new variant is compiler-forced, pointing an existing arm AT it is not, so a variant nothing names is never walked (all().len() came back 17 against 18, suite green). Closing it needs an enumeration INDEPENDENT of the derivation, and stable Rust has none for an enum carrying data: variant_count is nightly, and strum EnumIter cannot construct a variant holding a MinibufferConfig. SERDE CAN -- the Deserialize derive builds the complete variant-name list from the enum definition and publishes it in its unknown-variant message. improvise-5ubs reads it back in crates/improvise-protocol/src/variants.rs (cfg(test)) and diffs it against what the cycle reached. The dependency on serde message format is made LOUD rather than silent: it panics naming the message if the split fails, and asserts the parsed list has more than one name, so a format change cannot degrade into a vacuous pass.').
|
||||
doctrine(a_consolidations_frozen_behaviour_is_a_date_not_a_decision,'A consolidation must PRESERVE the behaviour it moves, or its agreement test cannot tell \"both ends agree\" from \"both ends changed together\" -- so the moved rule is frozen FOR THE DURATION OF THE MOVE. Afterwards a single home reads as decided, and nothing in the tree says otherwise. improvise-8qlr froze name-only group coalescing because both prior copies did it; improvise-h0iv later changed it once for both ends, and found the test still pinning the old rule justifying itself with \"because that is what src/ui/grid.rs uses\" -- a clause 8qlr had already made false, since grid.rs calls group_run_heads now. When a consolidation lands, say in context/ WHICH of its behaviours were preserved for evidence rather than on merit; otherwise the next reader inherits a frozen date as a decision.').
|
||||
doctrine(a_claim_in_context_must_name_the_file_it_was_checked_against,'PR #91 wrote \"keys.rs emits no ChangeMode\" into context/code/command.md to argue a divergence was latent. True and irrelevant: keys.rs holds only BrowserKey, and the live resolver is improvise_protocol::resolve, which emits it five times (improvise-e5pf). Nothing checks a claim in context/, which design-principles section 6 names as exactly why a reason must not be wrong there. So a NEGATIVE claim grepped from one file must name that file, and a reviewer must re-derive it from the CALL GRAPH rather than from the name. ROOT CAUSE, and it was the orchestrator: repo-map row \"Change what a browser resolves without the host\" pointed at wasm-client/src/keys.rs, so the author consulted the map, opened the named file and grepped it honestly. A stale pointer in the map does not merely fail to help -- it manufactures confident false claims, because the reader has done the work the map told them to do.').
|
||||
doctrine(a_per_element_resolution_cannot_fix_a_per_batch_property,'improvise-sihr round 1 resolved AppMode::Normal inside each effect that sets it, and got the CROSSING case wrong -- a batch that leaves records and then sets Normal landed in RecordsNormal. Not an oversight: gotcha 23 means any SET-TIME read sees the batch OPENING layout, so no per-effect resolution can answer a question about where the batch ENDS. Round 2 moved it to one call, settle_normal_mode after rebuild_layout in apply_effects, and DELETED MORE THAN IT ADDED: three writers became one, ChangeMode went back to the simple_effect! macro, and the uncovered-edit finding dissolved rather than being patched. Ask \"where is the one place this can be answered\" BEFORE \"which writers must I teach\" -- and note the tell that the first answer was wrong: the author was writing a DEFENCE of the staleness rather than a test of it.').
|
||||
doctrine(a_guard_seeded_on_agreement_cannot_detect_drift,'lockstep.rs records coverage sets host.view_state.mode = AppMode::records_editing() BY ASSIGNMENT and then seeds the client with client.view = app.view_state.clone(), so it begins from a state where the mode and the layout already AGREE. Every divergence it exists to catch requires them to DISAGREE, so the whole class is invisible to it by construction (improvise-e5pf). Same vacuity shape as gotcha 36 bystander already sitting on the relayed value, and as the improvise-t6db property whose two views differed only by having nothing to prune. A lockstep or agreement test must DRIVE THE INPUTS that produce the state, never hand-place the state -- hand-placing is what makes the fixture agree, and agreement is the thing under test.').
|
||||
doctrine(a_substitution_is_a_defect_at_every_site_that_makes_it,'improvise-e5pf was filed as one command folded without a resolution. A sweep found the real fault is ONE SUBSTITUTION AT THREE DECISION POINTS: wherever the host asks layout.is_records_mode(), the client asks state.view.mode.is_records(). The wire command is only how the client REACHES the bad state; the substitution is why the bad state does damage, and it decides whether enriching the projection is even sufficient. Worse, the first site inverts its own failure mode: commit() is written to DECLINE a records commit it cannot compose, and with the mode wrong it does not decline -- it composes a typed_cell_command and a Basis claim against a records grid whose columns are [coords, +cat, measures, +], which is a WRONG WRITE rather than the intended black hole. When a ticket names a symptom, grep for the PROXY the symptom rests on; a proxy used once is used everywhere the same question is asked.').
|
||||
doctrine(comparing_a_function_against_itself_tests_nothing,'improvise-jmjz fused three grid passes into one and guarded the width half with \`one_row.col_widths == compute_col_widths(model, &app.layout, false, 0)\`. But compute_col_widths IS grid_body with an empty window -- the same function twice -- so windowing the widths degrades BOTH SIDES IDENTICALLY (both collapse to the MIN_COL_WIDTH clamp) and the assertion cannot fail. Injecting \`if drawn { widen(..) }\` left 1476 tests passing. The fixture hid it a second way: the column header \"Revenue\" is wider than the value \"300021\", so the width assertion passed on the header alone. A check whose two sides share an implementation is a tautology wearing the shape of a comparison -- and a consolidation is exactly when one gets written, because the second implementation has just been deleted. The discriminating fixture puts the widest DATA value outside the window and makes it exceed both the header and the total (values 1, 888888888, -888888888: the total cancels to 1.0 so it cannot supply the answer).').
|
||||
doctrine(a_negative_result_is_only_as_good_as_the_setup_that_produced_it,'PR #92 reported that a none_cats hazard was \"argued and unobserved at this shape\" after widening a fixture to probe it. Printing the actual axes showed Product on COLUMN in every view -- add_category defaults it there and nothing moved it -- so none_cats was {_Index, _Dim} identically everywhere and THE STATED INPUT TO THE HAZARD WAS ABSENT. The path was unprobed, reported as a negative result. This is an_injection_that_does_not_fail_names_an_untested_path applied to a SETUP rather than to an injection, and it is worse, because a recorded negative stops the next person looking. Before writing \"probed and not reproduced\", ASSERT THE PRECONDITION IN THE TEST -- print or assert the condition the hazard needs, so a fixture that drifted out of the shape fails loudly instead of reporting absence.').
|
||||
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.').
|
||||
@@ -297,6 +313,7 @@ work_done('improvise-x4wv',bug_fix,'web/assemble.sh\'s stamped guard exited on t
|
||||
work_done('improvise-2f97',feature,'kb_error(dangling_citation/3): a name cited inside another clause\'s text must resolve as a doctrine/2, invariant/2 or decision/3. The token rule is narrow on purpose -- only a name-shaped token (lowercase, digits, underscores, at least one underscore) directly inside doctrine(, invariant( or decision( -- so prose cannot false-positive. Retiring doctrine(an_advisory_gate_is_not_a_gate) is now refused by name rather than silently dangling its citers. kb_text/2 is hand-maintained, like gate_predicate/1.').
|
||||
work_done('improvise-dj1l',bug_fix,'scripts/kb-merge.pl matched clause keys by unification, which lost and corrupted clauses: two variant-headed rules in one delta went two in, one out (536 before, 2 in delta, 537 after), and a non-ground rule over a predicate holding facts deleted one AND was written with its variable bound. See invariant(key_matching_is_variant_equality_never_unification). Five regression cases added to scripts/kb-test.sh, including one that injects a dropping fold to prove check_total fires; scripts/kb gained a KB_MERGE seam so a broken merge can be injected.').
|
||||
work_done('pr82-fingerprint-guards-2026-09-20',test,'PR #82, follow-up to #76. Added assert!(app.view().prune_empty) to selecting_the_page_already_selected_invalidates_no_cursor (paged_model(false) plus deleted moved-conjunct had left it GREEN); moved its page-selection precondition ahead of the effect and onto App::view(); corrected the test-evidence entry for every_layout_geometry_effect_is_visible_to_the_narrow_fingerprint, whose stated mechanism measurement contradicts, and recorded which two of its four prepare steps are load-bearing. 1425 passing, unchanged. Key is a dated slug and not an improvise- id, because bd issued none: an ID-shaped non-ID is worse than a dead id, the diagnosis being identical and the repair not.').
|
||||
work_done('pr90-io-gp-correctness-2026-09-21',fix,'PR #90, wave K. improvise-iot6: render_csv built GridLayout::new, which skips prune_empty, so :export and the CLI export path wrote rows AND columns the grid does not draw -- FOURTH instance of the trap and the first a user could see; fixed with with_frozen_records(m, v, None), the PR #78 grid_in fix line for line. A sweep of the remaining GridLayout::new call sites found all four are test code, so this was the last production instance. improvise-zv8t: fit_measure handed preprocess an Overrides::default(), so a numeric- or date-valued document category was inferred rather than forced categorical and the fit refused as an encoding artefact. Two self-review rounds and one adversarial round on the branch; the adversarial round found F2 (forcing by name, budgeting by index), which this PR itself introduced. 1458 -> 1464 passing.').
|
||||
work_followup('improvise-8tk','improvise-lae','no-file-path UX: prompt for a path rather than only erroring. ExportPrompt is CSV-only, so a new SavePrompt TextEntry mode committing to SaveAs is the likely shape.').
|
||||
work_followup('improvise-6sq','improvise-ct5','i on an empty records grid reports \'cannot drill\', leaking a pivot-mode concept into records mode').
|
||||
work_followup('improvise-h0b','improvise-a2x','ViewState field types (AppMode, MinibufferConfig, ViewFrame, KeyCode, KeyModifiers) still derive no Serialize, and improvise-command depends only on serde_json. Deferred: nothing serializes ViewState until improvise-cqi builds a Snapshot, so the derives would have no consumer.').
|
||||
@@ -374,6 +391,7 @@ decision('improvise-x7cq',every_check_job_gates_the_deploy_is_a_gate,'pages-depl
|
||||
decision('improvise-mcq4',cursor_frame_is_one_view_on_geometry_and_every_view_on_cell_data,'The LayoutGeometry arm hashes only the active view frame -- that is the held-key path the optimisation exists for, and other views cannot reframe there because view_mut() writes only the active view, with AddCategory/RemoveCategory/AddItem/RemoveItem already covered by view_shapes. The CellData arm hashes EVERY view, because a cell write can prune a row out of a view the host is not on. Measured: 0 percent on the held-key path, +5.7 percent on an 18k two-view SetCell, linear in view count and accepted. Absolute stated rather than only the ratio: the PR still roughly doubles held-key latency on large pruned documents, 3.04ms to 5.69ms at 18k, and 56ms at 90,000 cells is 3.4 frames.').
|
||||
decision('improvise-ni6k',cursor_invalidation_is_observed_not_declared,'No effect calls reset_cursor. apply_effects diffs the grid frame after every model effect, INSIDE the loop, because a batch that reframes and then places the cursor loses the placement otherwise (four records-mode tests pin that position; two after the arms were split). Total over EffectKind::Model(_) by construction rather than by a maintained list -- the thirteen per-effect calls it replaced are gone, and the \"a geometry effect that forgets to reset is invisible in both directions\" hazard is closed for model effects. Totality remains conditional on CLASSIFICATION, which nothing makes total: a ViewOnly effect that renumbers the grid is invisible to all of it.').
|
||||
decision('improvise-864a',pages_artifact_reaped_by_a_foreign_successful_deploy,'Measured over 100 push-to-main runs 2026-09-19..2026-09-21: 12 of 12 artifact-not-found deploy failures had ANOTHER run SUCCESSFUL Pages deployment complete inside their own upload-to-deploy window, against 2 of 56 successes. Direction proved by the oldest run of the 17:47Z batch failing BEFORE the next deploy started and before any deployment existed, since deploy-pages fetches artifact metadata before creating one. NOT queue depth, NOT supersession, NOT retention, NOT the action deleting it. PR #46 tip-of-main guard aimed at the right quantity for the WRONG REASON and still must not ship: the association is not a mechanism, and 5f871f7 survived a foreign deploy inside its own window. Reproduce deliberately before fixing.').
|
||||
decision('improvise-zv8t',a_document_category_is_never_inferred,'fit_measure now builds its Overrides from the same header[..category_columns] slice it already handed Features::of, forcing each to Forced::Categorical, so the one place the document own answer was known is the only place either caller reads it from. Closes Artefact::NumericCategory and DatePart-over-a-category from the only production entry point. The Artefact arms STAY: crate::dataset is pub and takes the caller Overrides, so a caller that builds one without forcing still produces a Numeric role over a category column -- deleting the arm would derive the classification from one caller habits rather than from the roles. Cost accepted and filed as improvise-e8no: a wide numeric category now one-hots past MAX_FEATURES and refuses a fit that used to ignore it. Do NOT close e8no by making the forcing conditional on cardinality -- that is inference by another name, and makes the ENCODING depend on a budget rather than on what the document says.').
|
||||
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