Skip to content

auto-fix batch claude/friendly-maxwell-UlJEd 2026-05-03#560

Merged
intendednull merged 11 commits into
mainfrom
claude/friendly-maxwell-UlJEd
May 3, 2026
Merged

auto-fix batch claude/friendly-maxwell-UlJEd 2026-05-03#560
intendednull merged 11 commits into
mainfrom
claude/friendly-maxwell-UlJEd

Conversation

@intendednull
Copy link
Copy Markdown
Owner

Scheduled /resolving-issues sweep. Eight small-scope fixes from the 2026-05-02 general-audit (master ticket #513) landed sequentially. One audit child (#535) coordinator-skipped as ambiguous-fix-path (design call needed); skill edit captures the new pattern.

Fixes

Already-Fixed

None this run. The general-audit at #513 was filed earlier the same day against the same main @ b901575 head this run started from, so by definition no fixes had landed between audit and dispatch (skill now notes this is the expected zero-yield case for same-day audit-to-fix gaps).

Parked

None. One audit child (#535Arc<Mutex> in willow-actor fsm.rs:415) was coordinator-skipped during step 6 picks, NOT parked: the test code at the cited line is already inside #[cfg(test)] mod tests (lines 175-460), so the audit's literal "move into mod tests" prescription is satisfied at HEAD. The audit's underlying concern is real (the !**/tests* exclusion glob doesn't catch in-file test modules) but the fix is a design call — either move the test to an external crates/actor/tests/fsm_tests.rs so the path-based glob catches it, or update the audit glob. Skill edit 5320c8e documents this new "ambiguous-fix-path" coordinator-skip pattern; left open for the next run with fresh eyes / accumulated context.

Skill Evolution

  • 5320c8e docs(skill): coordinator-skip for ambiguous-fix-path issues — adds two notes to step 6 of the Core Loop:
    • Same-day audit-to-fix expects zero already-fixed hits. When the audit was filed against the same HEAD the run starts from, no fixes can have landed in the gap by definition. Don't over-invest in the sweep in this case; one quick git log <audit-ref>..origin/main per pick is enough.
    • Ambiguous-fix-path coordinator-skip pattern. When an audit's prescribed fix is ambiguous-by-design (≥ 2 valid approaches needing a design call) AND the literal premise is already satisfied at HEAD, skip from the dispatch queue without closing. Surface in run-end Lessons Learned. Don't comment on the issue (audit body already captures the concern). audit F35 [debt]: Arc<Mutex> in willow-actor fsm.rs (move into mod tests if test-only) #535 was the canonical example surfaced this run.

Lessons Learned

Test plan

Master-PR CI is the load-bearing gate. Locally, each implementer ran the scoped subset (fmt, native + wasm clippy on touched crates, native test, wasm32 check). No just check workspace-wide run since just is unavailable in some sandboxes; raw cargo equivalents per the skill's fallback.

CI gates to verify on this PR:

  • cargo fmt
  • cargo clippy workspace (native + wasm32)
  • cargo test workspace (state + client + identity + replay + storage + common + web + actor + worker)
  • wasm-pack browser tests (Firefox + geckodriver — only observable on CI; new is_zero_duration parser tests run as native unit tests)
  • cargo audit (no advisory changes this run)
  • Playwright e2e (no behaviour changes — sanity only)

Generated by Claude Code

claude added 11 commits May 3, 2026 00:28
`npm install` can mutate package-lock.json; `npm ci` installs
exactly the lockfile + refuses to mutate it. Deterministic E2E
setup demands the strict variant.

No Rust changes — only fmt run as smoke check (clippy/test skipped).

Refs #530
Bare `cargo install` ignores each tool's Cargo.lock, so any
compromised transitive dep on crates.io silently lands in the
E2E env. `--locked --version X.Y.Z` deterministic.

- trunk 0.21.14 matches deploy.yml workflow pin
- just 1.50.0 latest stable (no existing pin in workflows)

Refs #529
Mirror the storage cap added by PR #507 / b075140
(MAX_AUTHORS_PER_SYNC = 256) on the replay path. Without
the guard, ReplayRole::handle_request(WorkerRequest::Sync)
iterates a peer-supplied HeadsSummary into a BTreeMap and
walks the in-memory DAG once per author — same DoS shape as
storage's sync_since/history before #507.

Approach A (centralize the const in willow-common alongside
SYNC_BATCH_LIMIT) chosen over B (define a local const in
replay): the cap is a wire-protocol invariant that BOTH
workers must agree on. A single source of truth in
willow-common — already a dep of both crates — guarantees
they cannot drift. Cost is one extra crate dep edge for
willow-replay (already had willow-state, willow-identity,
willow-worker, willow-network).

Storage's local pub const is removed; it now imports from
willow-common. No behavioural change to storage — the value
and the bail! sites are byte-identical.

Replay uses WorkerResponse::Denied { reason } (sync handler,
not anyhow::Result) mirroring the existing "unknown server"
branch and the storage error message text.

Tests:
- sync_request_rejects_oversize_heads (MAX+1 → Denied)
- sync_request_accepts_exact_cap_heads (MAX → not Denied)

Refs #514

https://claude.ai/code/session_019HhgeDZ5HCbEUygRRLCjde
is_zero_duration only matched "", "0s", "0ms" — but the global
prefers-reduced-motion rule in style.css forces transition-duration
to 0.01ms !important on every element, which engines serialise as
either "0.01ms" or "0.0001s". Both slipped past the strict matcher,
so mobile_shell, confirm_dialog, bottom_sheet, grove_drawer, and
message reactions all sat waiting for a transitionend that never
fires under reduced-motion — UI hang.

Replace the string-equality check with parse_duration_seconds (parses
both s and ms suffixes) and accept anything ≤ 1ms (epsilon) as zero.
Unparseable input stays conservative (not zero). Sibling-of-closed
audit follow-up to #496 (8d89f18).

Approach A (parse-and-compare) chosen over B (hardcode the two known
strings) because reduced-motion is the authoritative contract — any
sub-millisecond duration is indistinguishable from "no transition"
for transitionend purposes, so a numeric threshold is the durable fix.

Tests added (native, no DOM): parse_duration_seconds_handles_units,
parse_duration_seconds_rejects_malformed,
is_zero_duration_str_recognises_explicit_zero,
is_zero_duration_str_recognises_reduced_motion_override,
is_zero_duration_str_treats_sub_millisecond_as_zero,
is_zero_duration_str_rejects_real_durations,
is_zero_duration_str_multi_value_all_zero,
is_zero_duration_str_multi_value_mixed_is_not_zero,
is_zero_duration_str_unparseable_is_not_zero.

Gates: fmt clean, clippy native + wasm32 clean (-D warnings), 86
willow-web lib tests pass, wasm32 --tests check clean (wasm-pack /
geckodriver not available in env — used cargo check --target
wasm32-unknown-unknown --tests as fallback gate per CLAUDE.md).

Refs #515

https://claude.ai/code/session_019HhgeDZ5HCbEUygRRLCjde
Audit F47 (#547): `evict_stale_removes_expired` slept 10ms after a
1ms TTL — flake risk on slow CI per `condition-based-waiting`.

Fix per audit suggestion: inject a clock. Added
`pub(crate) evict_stale_at(&mut self, now: Instant)`; production
`evict_stale()` delegates with `Instant::now()`. Test now feeds a
synthetic future `now`, removing the timing dependency entirely.

Picked clock-injection (A) over a clock trait (B, too heavy for one
test) and over cutoff-injection (C, equivalent but maps less directly
to "inject a clock" in the audit).

#545 (Instant::now wasm linkage) is a separate ticket — not addressed
here.

Refs #547
Match surrounding let-Ok-else style: log + return instead
of unwrap, so non-window contexts (e.g. worker harness)
do not panic.

Refs #543
No production or test code in willow-identity uses tokio.
`grep -rn "tokio::\|#\[tokio::" crates/identity/src` returns
only a doc-comment reference at lib.rs:99. Lib crates must
stay tokio-free per CLAUDE.md; dev-dep was dead weight.

Refs #537
Replaces a blind 3s sleep before curl with a 15-attempt loop
(2s backoff). The relay can take longer than 3s to bind after a
restart, and combined with StrictHostKeyChecking=no a slow bind
could mask a silent deploy failure as a passing job.

Refs #531
Permission enum (crates/state/src/event.rs:46-69) defines exactly:
SyncProvider, ManageChannels, ManageRoles, SendMessages, CreateInvite
(plus __UnknownLegacy sentinel). Admin status and member kicks live on
the ProposedAction + vote path by design — see
docs/specs/2026-04-01-per-author-merkle-dag-state-design.md:295 ("the
Permission enum does not contain Administrator"). The type system
enforces the governance path; granting admin via GrantPermission is
structurally impossible.

CLAUDE.md and the agentic-peer-api spec listed KickMembers and
Administrator as valid permission values — pure doc drift. Realign
both with the enum and clarify trust_peer/untrust_peer route through
the vote path, not GrantPermission.

Refs #521
Same-day audit runs yield ~zero already-fixed sweep hits — note
explicitly so future runs skip the deeper search. Document the
ambiguous-fix-path pattern (audit premise real, fix is a design
call w/ ≥2 valid approaches): skip from dispatch queue without
closing, surface in Lessons Learned. Surfaced this run by #535
(Arc<Mutex> in fsm.rs is already in mod tests; audit's "move to
mod tests" prescription doesn't apply — fix is either external
test file or glob update, design call).
@intendednull intendednull merged commit 150df97 into main May 3, 2026
8 checks passed
@intendednull intendednull deleted the claude/friendly-maxwell-UlJEd branch May 3, 2026 08:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment