7 Commits

Author SHA1 Message Date
Ed Langley 9325ba5ecf Merge pull request #170 from fiddlerwoaroof/fix/90ea-fmt-root-package
CI / Build all bundles (push) Has been cancelled
CI / Test and lint (host) (push) Has been cancelled
CI / wasm32 check (browser bundle crates) (push) Has been cancelled
CI / Browser JS unit tests (node --test) (push) Has been cancelled
CI / Knowledge base consistency (push) Has been cancelled
CI / Lint the harness crate (push) Has been cancelled
CI / Browser harness (OPFS contract + shell) (push) Has been cancelled
CI / Deploy to GitHub Pages (push) Has been cancelled
fmt-workspaces: a case per package, and refuse a package no root reaches (improvise-90ea)
2026-09-22 21:19:07 -07:00
Ed Langley 97f4b1b648 Merge pull request #174 from fiddlerwoaroof/wire2/gho5
Rolled-back-load tests witness that the load ran (gho5)
2026-09-22 21:17:51 -07:00
Edward Langley d11d2ac1f7 Merge remote-tracking branch 'gh/main' into fix/90ea-fmt-root-package
# Conflicts:
#	context/test-evidence/tooling.md
2026-09-22 21:13:08 -07:00
Edward Langley 625c11319a fmt-workspaces: exit 2 means no verdict, not no output (improvise-ximf)
The unreached-package arm exits 2 and names the package's Cargo.toml, so the
usage text's "NO file is named" was false for it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:09:46 -07:00
Edward Langley 912e25a0ac Rolled-back-load tests witness that the load ran (improvise-gho5)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 21:04:07 -07:00
Edward Langley 808cbb7d92 fmt-workspaces: refuse a package no workspace root reaches (improvise-90ea)
The fixture loop covers every package the fixture has; this covers every
package the tree has. The review's skip-the-root rival now fails the real
repo's own gate (exit 2 naming Cargo.toml), not only the test suite.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 20:55:25 -07:00
Edward Langley 7cc52bdb1d fmt-workspaces-test: one fault case per fixture package, root included (improvise-90ea)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
2026-09-22 20:51:59 -07:00
5 changed files with 225 additions and 55 deletions
+33 -3
View File
@@ -2382,6 +2382,31 @@ keystroke to land in.
Injected fault: dropping `self.view_state = snapshot.view_state` from
`App::restore` fails this test and only this test.
**Addendum (improvise-gho5).** Improvise-gho5 found that this
test and `a_rolled_back_load_tells_nobody_and_puts_every_mirror_back` passed
against a host that **never ran the load**. The review's fault, reproduced
here, was placed at the top of `Host::commands`: refuse any multi-command frame
that contains a `LoadModel` with `Rejected(DocumentReplaced)`, before
`CheckedBatch` runs. Both tests stayed green. A frame that never got that far
leaves the generation, the view, the status line and every mirror untouched,
which is exactly what the tests asserted.
Both tests now assert that the asker's whole reply is
`the_load_ran_then_the_write_broke()`, which is `Rejected(ChainBroken(North))`.
They also assert, before the frame, that `North` resolves. Together these make
the rejection a witness that the load ran. `ChainBroken` comes only from
`reduce::broken_chain`, which runs inside `mutating_frame` just before each
mutation. It names `North` only once the document no longer holds that
record, and only an applied `LoadModel` in this frame can have removed it.
With the same fault re-injected, both tests fail. The left side is
`[Rejected(DocumentReplaced)]` and the right is
`[Rejected(ChainBroken(<North>))]`. The fault was reverted.
The residual: a rogue up-front check that forged `ChainBroken` specifically
would still pass. That is the same forgery a hand-rolled client could commit
against any basis check, and a test cannot guard against it.
### a_refused_mutation_rolls_back_its_predecessor_and_the_abort_flag
The `Rejection::MutationRefused` arm — the one the first hand-back wrongly called
@@ -4885,9 +4910,14 @@ Three things make it non-vacuous:
`LoadModel` silently failed, `Applied::Rebased` would never run and every
assertion here would pass over nothing. The control asserts the generation
moves and that *both* clients are owed a `Snapshot`.
- **The rollback is asserted before anything is concluded from it** — the
`document_generation` equality is checked first, so "the frame was rejected"
cannot be confused with "the frame never got that far".
- **The load is witnessed to have RUN before anything is concluded from the
rollback** (improvise-gho5). The asker's whole reply must be
`Rejected(ChainBroken(North))`. `North` is asserted to resolve before the
frame, so only `broken_chain` can give that answer, and only after the
`LoadModel` has replaced the document. `CheckedBatch` never produces
`ChainBroken`. This bullet used to say the `document_generation` equality
did the job, and that was false: generation equality is exactly what holds
when the frame never got that far.
- **A bystander is seated where a wrong relay could move it.** A single-client
fixture cannot tell "the mirrors were restored" from "there was one mirror and
it happened to be the asker's".
+68 -3
View File
@@ -391,6 +391,8 @@ green suite over a vacuous assertion.
**What is not covered.** The script asks `cargo metadata` for members, so a root
whose metadata succeeds but whose member list is wrong is not detectable here.
*Narrowed by improvise-90ea, below:* a list that **omits** a package in the tree
is now refused; one that names a package it should not is still not detected.
And `harness/opfs-contract`'s own `cargo fmt -p opfs-contract` step in the
`harness-lint` job is left as it is: it resolves correctly because that step
sets `working-directory`, and the ticket's own note confirms CI as written was
@@ -415,9 +417,11 @@ getting wrong. The line the split actually needs is **"did the gate run"**:
- exit 1 — rustfmt did not certify a root clean. A diff or a refusal to parse;
either way **a file is named** and the tree is what is wrong.
- exit 2 — the gate did not run. Usage, no cargo, no rustfmt, `cargo metadata`
failed, no `[workspace]` root. **No file is named.** Every such cause is
checked *above* the loop, which is what makes the narrowing true rather than
hopeful.
failed, no `[workspace]` root. **No source file is named.** Every such cause
is checked *above* the loop, which is what makes the narrowing true rather
than hopeful. (Since improvise-90ea, a package no root reaches is also exit 2,
and it names that package's `Cargo.toml`, which is the reason no verdict
exists. The line is "no verdict", not "no output"; improvise-ximf.)
Both directions are now pinned, and two further rivals were injected:
@@ -439,6 +443,67 @@ stub must fail in the **shape** of the real fault, not merely fail — a stub th
breaks more than the real thing breaks satisfies the test through the wrong arm,
and reads exactly like coverage.
#### The root package, and why the fourth fix is structural — `scripts/fmt-workspaces-test.sh`
A post-merge review of PR #159 found a **fourth** fixture vacuity
(improvise-90ea). The fixture's outer root is a package (`root-pkg`) as well as
a workspace, which is right, but no case ever dirtied **the root package's own
`src/lib.rs`**. Only the member `a/` and `nested/` were dirtied. So a script
that formats every member *except* the root package passed 17/17. In this repo
the root package is `improvise`, the whole TUI. Reproduced at `40b8913` with
the review's rival in `members_of` (drop the package whose manifest sits at
`workspace_root`): the suite passed 17/17 and `--list` left `improvise` out.
Adding one more hand-picked case would have fixed the fourth and not the fifth.
Four vacuities in one checker all have the same cause: **the cases were chosen
by hand, one per hazard somebody had thought of.** So the fix is structural,
and it has two halves because it has to cover two sets of packages:
- **Every package the fixture has.** The two hand-picked fault cases are
replaced by a loop over every `[package]` manifest in the fixture. Each one
gets its own misformatted `src/lib.rs`, must exit 1, and must be named
**exactly once**, so the `--all` boundary check now covers every package
rather than one. The enumeration is a `grep` for `[package]`, deliberately
*not* the script's `[workspace]` + `cargo metadata` discovery, since that
discovery is what is under test. A count assertion (three packages) stops
the loop going vacuous if the fixture loses a package. A package added to
the fixture gets its case automatically.
- **Every package the tree has.** The loop proves only that the script handles
the fixture's *shapes*. A real tree can hold a package no root lists (an
`exclude`d crate, say), and nothing in the fixture would say so. So the gate
itself now checks an invariant **before it formats anything**: every
`[package]` manifest the find reaches must be a member of some root, or it
exits **2** and names the package. That is exit 2 and not 1 because no
verdict was produced for that package, which is what exit 2 means here.
Paths are compared through `realpath`, because on macOS `mktemp` lives under
`/var` while cargo reports `/private/var`.
**Rivals injected, and where each failed** (the suite went from 17 to 21 cases):
- **The review's skip-the-root rival.** It now fails the fixture at `a fault in
src/lib.rs is reported` (exit 0). With the invariant in place it fails
**six** cases (exit 2, "no workspace root reaches"). It also fails the
**real repo** with no fixture involved: `fmt-workspaces.sh` exits 2 naming
`Cargo.toml`. That last one is the property worth having. The gate refuses
its own blindness in CI, not only in its test suite.
- **Disabling the invariant** (`if false`). It fails `a package no workspace
root reaches is exit 2, not a pass` with **exit 0**, even though that
package's `src/lib.rs` is misformatted. That exit 0 *is* the hazard: an
unreached package is silently clean.
- **The two earlier rivals, re-run against the loop.** `--all` for `-p` fails
both `a/src/lib.rs` and `src/lib.rs` "named exactly once". The second of
those was not predicted: the nested root's `--all` also reaches the root
package. The bare manifest-path form fails `a fault in a/src/lib.rs is
reported`.
The real-repo control from the ticket still holds: a misformatted fn appended
to `src/ui/text.rs` is reported (`Diff in …/src/ui/text.rs:72`).
**What is still not covered.** A root whose member list *includes* something it
should not is not detected, and a package in a directory the find excludes
(`target/`, `.claude/`, `node_modules/`) is outside the invariant by
construction.
### The kb tool's completeness guard and atomic write — `scripts/kb-test.sh` cases 18–22
improvise-usus: `scripts/kb add` wrote onto a working-tree `kb.pl` that had lost
+27 -5
View File
@@ -1049,6 +1049,12 @@ mod tests {
let was_view = host.app().view_state.active_view.clone();
let was_status = host.app().view_state.status_msg.clone();
let basis = frame_basis(&host, &["North"]);
assert!(
host.app()
.resolve_record(addressed("North").record)
.is_some(),
"North must resolve BEFORE the load, or a refusal naming it proves nothing"
);
// The load lands, then the write names a record the incoming document
// has never had — so the frame fails at its second command.
@@ -1069,11 +1075,10 @@ mod tests {
]),
);
assert!(
answer
.iter()
.any(|a| matches!(a.msg, ServerFrame::Rejected(_))),
"the fixture must actually refuse the frame: {answer:?}"
assert_eq!(
to(&answer, asker),
vec![the_load_ran_then_the_write_broke()],
"only a load that RAN can refuse the write this way: {answer:?}"
);
assert_eq!(
host.app().view_state.active_view,
@@ -1097,6 +1102,12 @@ mod tests {
);
}
/// `North` resolves in the outgoing document, so only `broken_chain` AFTER the
/// load applied can answer this. Evidence: `context/test-evidence/command.md`.
fn the_load_ran_then_the_write_broke() -> ServerFrame {
ServerFrame::Rejected(Rejection::ChainBroken(addressed("North").record))
}
/// Everything one client was told, flattened to the projections it carries.
fn projections(frames: &[Addressed], id: SessionId) -> Vec<Projection> {
to(frames, id)
@@ -1147,6 +1158,12 @@ mod tests {
let (bystander, _) = host.connect(10, 10);
let was_generation = host.app().document_generation();
let basis = frame_basis(&host, &["North"]);
assert!(
host.app()
.resolve_record(addressed("North").record)
.is_some(),
"North must resolve BEFORE the load, or a refusal naming it proves nothing"
);
// The load lands and rebases every mirror; the write then names a
// record the incoming document has never had, so the frame fails.
@@ -1167,6 +1184,11 @@ mod tests {
]),
);
assert_eq!(
to(&answer, asker),
vec![the_load_ran_then_the_write_broke()],
"only a load that RAN can refuse the write this way: {answer:?}"
);
assert_eq!(
host.app().document_generation(),
was_generation,
+54 -27
View File
@@ -76,36 +76,37 @@ run_case() {
scaffold
run_case "both workspaces clean is a pass" 0 "$work/repo"
# The load-bearing case. `cargo fmt --all` at the outer root cannot reach this
# file, and `cargo fmt -p inner-pkg` from the outer root exits 1 saying `not a
# member of the workspace` -- the usage error this whole script exists to split
# from a real verdict.
# One fault case PER PACKAGE, enumerated from the fixture rather than
# hand-picked: three hand-picked shapes each missed a rival, and the fourth
# missed the root package itself (improvise-90ea). Reasoning:
# context/test-evidence/tooling.md. The enumeration is a find over
# `[package]` manifests, deliberately NOT the script's own `[workspace]` +
# `cargo metadata` discovery, which is the thing under test.
scaffold
printf '%s' "$DIRTY" > "$work/repo/nested/src/lib.rs"
if run_case "a fault in the NESTED workspace is reported" 1 "$work/repo"; then
case "$OUT" in
*nested/src/lib.rs*) ok " and the nested file is named" ;;
*) bad " and the nested file is named" "output did not name it: ${OUT:0:300}" ;;
esac
pkg_manifests=()
while IFS= read -r m; do pkg_manifests+=("$m"); done < <(
cd "$work/repo" && grep -rl --include=Cargo.toml '^\[package\]' . | sort)
# An empty or shrunken enumeration would make the loop below vacuous.
if [ "${#pkg_manifests[@]}" = 3 ]; then
ok "the fixture enumerates all three packages (root, member, nested)"
else
bad "the fixture enumerates all three packages" "found: ${pkg_manifests[*]:-none}"
fi
scaffold
printf '%s' "$DIRTY" > "$work/repo/a/src/lib.rs"
if run_case "a fault in the OUTER workspace is reported" 1 "$work/repo"; then
case "$OUT" in
*a/src/lib.rs*) ok " and the outer file is named" ;;
*) bad " and the outer file is named" "output did not name it: ${OUT:0:300}" ;;
esac
# The rival: spelling the per-root check `--all` instead of `-p <members>`
# walks PAST the nested workspace boundary (measured), so the OUTER file is
# reported a second time under the inner root. Counting is what sees that.
n="$(printf '%s\n' "$OUT" | grep -c 'a/src/lib.rs')"
if [ "$n" = 1 ]; then
ok " and it is named ONCE -- no root reaches past its own boundary"
else
bad " and it is named ONCE" "named $n times; a root is walking past its boundary"
for m in "${pkg_manifests[@]}"; do
rel="$(dirname "${m#./}")/src/lib.rs"; rel="${rel#./}"
scaffold
printf '%s' "$DIRTY" > "$work/repo/$rel"
if run_case "a fault in $rel is reported" 1 "$work/repo"; then
# Counting, not matching: `--all` for `-p` walks PAST the nested boundary
# (measured) and names an outer file a second time under the inner root.
n="$(printf '%s\n' "$OUT" | grep -cF "$work/repo/$rel")"
if [ "$n" = 1 ]; then
ok " and $rel is named exactly once"
else
bad " and $rel is named exactly once" "named $n times; output: ${OUT:0:300}"
fi
fi
fi
done
# A usage error must NOT wear a formatting failure's exit code. This is the
# whole ticket: `cargo fmt -p opfs-contract` from the repo root exits 1.
@@ -121,6 +122,32 @@ run_case "a tree with no workspace root is a usage error, not a pass" 2 "$work/e
run_case "--help is a usage error, so a typo cannot pass" 2 "$work/repo" --help
# The loop above covers every package the FIXTURE has; this covers every
# package the TREE has. A package no root lists is checked by nothing, so the
# gate must refuse a verdict -- and name the package -- rather than pass.
scaffold
mkdir -p "$work/repo/orphan/src"
printf '[package]\nname = "orphan-pkg"\nversion = "0.1.0"\nedition = "2021"\n' \
> "$work/repo/orphan/Cargo.toml"
printf '%s' "$DIRTY" > "$work/repo/orphan/src/lib.rs"
cat > "$work/repo/Cargo.toml" <<'EOF'
[workspace]
members = ["a"]
exclude = ["orphan"]
resolver = "2"
[package]
name = "root-pkg"
version = "0.1.0"
edition = "2021"
EOF
if run_case "a package no workspace root reaches is exit 2, not a pass" 2 "$work/repo"; then
case "$OUT" in
*"no workspace root reaches"*orphan/Cargo.toml*) ok " and the unreached package is named" ;;
*) bad " and the unreached package is named" "output: ${OUT:0:300}" ;;
esac
fi
# The narrowed contract, pinned so it is not merely prose (ChatGPT Codex on
# PR #159). rustfmt exits 1 for a source it cannot parse, exactly as for a
# diff -- so exit 1 is "rustfmt did not certify this root clean", and what
+43 -17
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Format-checks every Cargo workspace root in the repo, from any directory.
# `cargo fmt --all` reaches only ONE of them. Reasoning: context/code/tooling.md
# section 4 -- including why `-p` scoped by `--manifest-path` is the only
# `cargo fmt --all` reaches only ONE of them. Reasoning: context/test-evidence/
# tooling.md, "The two-workspace fmt gate" -- including why `-p` scoped by `--manifest-path` is the only
# spelling that is neither blind nor boundary-crossing (improvise-b4ks).
set -uo pipefail
@@ -18,8 +18,9 @@ exit 1 rustfmt did not certify a root clean: a file differs from rustfmt's
output, or rustfmt refused to parse one. Either way a FILE IS NAMED
and the tree is what is wrong.
exit 2 the gate did not run: usage error, no cargo, no rustfmt, cargo
metadata failed, or no [workspace] root found. NO verdict was
produced and NO file is named.
metadata failed, no [workspace] root found, or a [package] that no
root has as a member. NO verdict was produced. No SOURCE file is
named; the unreached-package case names its Cargo.toml.
The split is "did the gate run", not "is every exit-1 cause a diff". That is
narrower than it sounds and it is deliberate -- see the note on rc=1 below.
@@ -38,8 +39,8 @@ EOF
# The find is RELATIVE to the tree being scanned: an absolute `-path` filter
# matches the ancestors too, and this repo is itself checked out under
# `.claude/worktrees/` in every agent worktree, where that excluded everything.
workspace_roots() {
local root="$1"
manifests_with() {
local root="$1" section="$2"
( cd "$root" && find . -name Cargo.toml \
-not -path './target/*' -not -path '*/target/*' \
-not -path './.git/*' \
@@ -47,11 +48,12 @@ workspace_roots() {
-not -path './.claude/*' ) \
| sort \
| while IFS= read -r rel; do
if grep -q '^\[workspace\]' "$root/${rel#./}"; then
if grep -q "^\\[$section\\]" "$root/${rel#./}"; then
printf '%s\n' "$root/${rel#./}"
fi
done
}
workspace_roots() { manifests_with "$1" workspace; }
# The members of ONE workspace, named by cargo rather than parsed out of the
# manifest: a `members = [...]` list may carry globs and path dependencies.
@@ -61,10 +63,10 @@ members_of() {
printf '%s' "$meta" | python3 -c '
import json, sys
doc = json.load(sys.stdin)
names = [p["name"] for p in doc["packages"]]
if not names:
rows = [p["name"] + "\t" + p["manifest_path"] for p in doc["packages"]]
if not rows:
sys.exit(1)
print("\n".join(names))
print("\n".join(rows))
' || return 1
}
@@ -106,15 +108,39 @@ main() {
return 2
fi
local rc=0 manifest member
for manifest in "${roots[@]}"; do
local -a pkgs=()
while IFS= read -r member; do [ -n "$member" ] && pkgs+=("$member"); done \
< <(members_of "$manifest")
if [ ${#pkgs[@]} -eq 0 ]; then
printf 'fmt-workspaces: cargo metadata failed for %s\n' "$manifest" >&2
# Pass 1: every root's members, before anything is formatted, so that a
# package NO root reaches is exit 2 rather than silently never checked.
local rc=0 manifest member i m
local -a members=() covered=()
for i in "${!roots[@]}"; do
local rows
rows="$(members_of "${roots[$i]}")" || rows=""
if [ -z "$rows" ]; then
printf 'fmt-workspaces: cargo metadata failed for %s\n' "${roots[$i]}" >&2
return 2
fi
members[$i]="$(printf '%s\n' "$rows" | cut -f1 | tr '\n' ' ')"
while IFS= read -r m; do covered+=("$m"); done < <(printf '%s\n' "$rows" | cut -f2)
done
local orphans
orphans="$(manifests_with "$root" package | python3 -c '
import os, sys
covered = {os.path.realpath(p) for p in sys.argv[1:]}
for line in sys.stdin:
m = line.rstrip("\n")
if m and os.path.realpath(m) not in covered:
print(m)
' "${covered[@]}")"
if [ -n "$orphans" ]; then
printf 'fmt-workspaces: no workspace root reaches these packages, so nothing checks them:\n%s\n' \
"$orphans" >&2
return 2
fi
for i in "${!roots[@]}"; do
manifest="${roots[$i]}"
local -a pkgs=()
read -r -a pkgs <<< "${members[$i]}"
if [ "$list" = 1 ]; then
printf '%s: %s\n' "$manifest" "${pkgs[*]}"