diff --git a/context/code/protocol.md b/context/code/protocol.md index 8a58e2dc..d76a58d2 100644 --- a/context/code/protocol.md +++ b/context/code/protocol.md @@ -218,14 +218,33 @@ its verdict. The list is checked in **both** directions, so an entry that starts being composed fails just as loudly as one that stops (`doctrine(a_stale_list_of_what_is_missing_is_worse_than_no_list)`). -Two limits are worth stating, because the check is a source scan. -`#[cfg(test)]` items are dropped by a line scan that relies on rustfmt putting -an item's closing brace at the attribute's own indentation; over-stripping would -**fail** the test rather than pass it, which is the safe direction. And a -`Command::X` inside `matches!` or `if let` is read as a construction rather than -a pattern, which can only make the check *weaker* for that variant — never raise -a false alarm. A variant nothing composes at all is immune to both, since its -only pattern occurrence is its own fold arm, which `=>` identifies. +The check is a source scan, and **what counts as source has one home**: +`improvise_protocol::source_scan::production_code`, shared with the forward +guard below (improvise-c7mu). It removes three things a scan must not see. + +- **Comments and literals.** Both are blanked, and newlines are kept. Before + this, a `// TODO: Command::SetYanked(...)` left where the real composer had + been deleted still passed all four tests. That is the *weaker* direction, the + one a scan weakness was supposed to be unable to go. It was live in a small + way: a `///` in `project.rs` named `Command::SwitchView`, which has real + composers, so no verdict was wrong yet. It also had a mirror: a comment + explaining that a `NOT_COMPOSED` variant is held back would have raised a + false "stale entry" alarm. +- **Test items inside a file.** A line scan that relies on rustfmt putting an + item's closing brace at the attribute's own indentation. It treats + `cfg(any(test, …))` as test code and `cfg(not(test))` as production. + Over-stripping **fails** the test rather than passing it, which is the safe + direction. +- **Test-only files.** A `#[cfg(test)] mod x;` makes `x.rs` and everything under + `x/` test code, even though nothing inside the file says so. Before + improvise-c7mu, this checker's own file and `improvise-io`'s + `roundtrip_tests.rs` were scanned as production. + +One limit remains. A `Command::X` inside `matches!` or `if let` is read as a +construction rather than a pattern. That can only make the check *weaker* for +that variant; it never raises a false alarm. A variant nothing composes at all +is immune, since its only pattern occurrence is its own fold arm, which `=>` +identifies. ### And forwards: every effect is on the wire or has a verdict (improvise-h3y7) @@ -254,8 +273,9 @@ kinds, and the check verifies each kind: improvise-2mqz). The list is also checked in reverse. An entry that is now on the wire fails, -and so does an entry that names no effect. A test-only fake effect is scanned -on purpose. It fails loudly and never passes silently. +and so does an entry that names no effect. The scan is `production_code`, so +the comments, strings and test fakes that name an effect do not count as +effects. If they did, a stale verdict could outlive the effect it names. What it does **not** check is whether a same-named pair *agrees*: whether `reduce_view`'s arm does what the effect does. The name match is the diff --git a/context/test-evidence/command.md b/context/test-evidence/command.md index efcda9b0..bfbaec6e 100644 --- a/context/test-evidence/command.md +++ b/context/test-evidence/command.md @@ -4903,3 +4903,27 @@ The control for the test above, and it is stated separately rather than folded in so that a change making the fixture's load stop rebasing fails *here*, where the message says what actually broke, rather than turning the rollback test green by emptying it. + +### ui::reduce::vocabulary_is_complete::every_effect_is_on_the_wire_or_has_a_verdict + +improvise-h3y7, the forward direction of the vocabulary derivation. It is the +direction `ViewBack`/`ViewForward` failed: they were missing, so no client ever +had a back history. The test scans this crate's production `src/` for +`impl Effect for X` and `simple_effect!(X`. That covers both spellings, and the +h3y7 audit's brace-matcher missed nine effects by seeing only the first. It +then requires each name to be a `Command` or `ModelCommand` variant (read off +serde's derive) or to carry a `NOT_ON_THE_WIRE` verdict. + +Faults injected, together: `HelpPageNext`'s verdict replaced by one for +`SetSelected`. This test failed naming `HelpPageNext`, and +`the_verdict_list_names_nothing_that_is_on_the_wire` failed naming +`SetSelected`. Separately, `HelpPageSet`'s verdict was pointed at +`Command::HelpPag`, and +`the_verdict_list_names_only_real_effects_and_real_variants` failed. + +`the_scan_reaches_every_way_an_effect_is_declared` pins one effect of each +spelling plus `SetTransientPrefix` (which lives in `command/keymap.rs`). A +broken walk therefore fails that test instead of shrinking the set. +`an_effect_named_only_in_a_comment_or_string_is_not_an_effect` shares +improvise-c7mu's fault. With `code_only` made the identity, it failed with +`["SaveAs", "ExportCsv", "Real", "SetYanked", "AlsoReal"]`. diff --git a/context/test-evidence/protocol.md b/context/test-evidence/protocol.md index e0c49021..b00b88db 100644 --- a/context/test-evidence/protocol.md +++ b/context/test-evidence/protocol.md @@ -969,3 +969,63 @@ That assertion is the discriminating one: a fold arm that removed the key would give `None` and fail. Asserting only that `"command"` survived would pass under either behaviour, which is what the old spelling amounted to for the cleared key. + +### vocabulary_is_composed::a_variant_named_only_in_a_comment_or_string_is_not_composed + +improvise-c7mu, found by the post-merge review of PR #160. `constructed_in` ran +over raw source text, so any `Command::X` counted as a composer, including one +inside a comment or a string. The review deleted the real `Command::SetYanked` +composer from `resolve.rs`, left `// TODO: Command::SetYanked(value)` in its +place, and all four tests passed. The test feeds the whole per-file pipeline a +line comment, a block comment and a string that each name a variant, and +asserts that none of them counts. A positive control asserts that a real +construction still does. + +Fault injected: `code_only` made the identity function. This test failed with +`["SetYanked", "SetSearchMode", "ChangeMode"]`, and so did +`source_scan::tests::comments_and_literals_are_blanked_and_code_is_kept`. The +four whole-tree tests stayed green under that fault. This confirms the review's +finding that no live comment gives a wrong verdict *today*, and it is the +reason the property has to be pinned on a fixture rather than on the tree. + +The fault on the real tree (deleting `resolve.rs`'s composer) was not rerun +here. `resolve.rs` is outside this lane, and the edit was refused as another +agent's ground. + +### source_scan::tests::a_test_only_module_file_is_not_production_code + +improvise-c7mu, the second half. `#[cfg(test)] mod x;` makes a whole file test +code, and nothing inside the file says so. The old scan saw it as production, +and that included this checker's own file. The test builds a fixture tree: +`lib.rs` declares `mod p;` and `#[cfg(test)] mod t;`, and `t.rs` declares a +nested `mod deeper;`. It asserts that `production_code` returns exactly +`lib.rs` and `p.rs`. + +Fault injected: the file filter in `production_code` made always-true. This +test failed with `["lib.rs", "p.rs", "t.rs", "t/deeper.rs"]`. Before this +fixture existed, the `improvise-protocol` and `improvise-command` lib suites +**stayed green** under the same fault, and so did +`test_only_items_and_modules_are_not_production`, which checks the helpers one +by one. The wiring between them was untested until this fixture existed. + +### reduce::tests::every_model_command_field_is_position_free + +improvise-hkbc. improvise-6nsd's "a stale layout tier cannot misdirect a +mutation" holds only while no `ModelCommand` carries a row or column. The guard +is the signature of `position_free`. It destructures every variant with no `..`, +down into `RecordCell`, and hands each field to a function that accepts only +`&str`, a path, a `RecordHash`, a `CellValue`, an `Axis` or an optional name. +The `#[test]` only keeps that signature compiled and reached. + +Faults injected, one per half, each compiling only `improvise-protocol`: + +- A new variant `ScrollTo { row: usize }`, also added to `records_named`'s + or-pattern the way an author would. The lib built. The test build failed + with `non-exhaustive patterns: ScrollTo` **at `position_free` and nowhere + else**. +- The same variant classified as `ScrollTo { row } => name(row)`. The test + build failed with `expected &str, found &usize`. + +A field added to an existing variant is caught too, but not in isolation, +because every constructor of that variant also fails. Reverted with +`git restore`.