Skip to content

fix(#851): home written parameters on aarch64 — and fix the conditional-param-write silent miscompile - #971

Merged
avrabe merged 9 commits into
mainfrom
feat/aarch64-param-homing-851
Aug 14, 2026
Merged

fix(#851): home written parameters on aarch64 — and fix the conditional-param-write silent miscompile#971
avrabe merged 9 commits into
mainfrom
feat/aarch64-param-homing-851

Conversation

@avrabe

@avrabe avrabe commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Closes the aarch64 local.set/local.tee-on-a-parameter decline by HOMING written params — and fixes a silent miscompile found while writing the fixture.

Two changes

1. Param homing (the requested capability). crates/synth-backend-aarch64/src/selector.rs widens the homing predicate from is_non_leaf && references_param to (is_non_leaf && references_param) || writes_param. A written param gets an 8-byte home slot filled from its argument register in the prologue; local.get loads a fresh copy, so a param write cannot alias a stacked value — the exact hazard the old decline guarded. writes_param ⊆ references_param, so exactly one class changes: leaf functions that write a param, all of which declined before. Non-leaf homing is byte-identical. The now-unreachable decline arms are kept as loud internal-invariant errors rather than deleted.

2. A silent miscompile in backend.rs (not in the original scope). num_params was min(count_params(ops), declared), and count_params counts only indices read before written in linear op order. A param written before it is read — but only conditionally — was demoted to a zero-init non-param local and compiled wrong instead of declining. Now min(highest REFERENCED index + 1, declared); the min preserves the >8-declared-params leniency that plain declared would have broken (verified: a hard num_params > 8 guard sits above the indexing, so plain declared would turn a lenient compile into a decline).

Evidence

Negative control — the most important artifact here. Reverting only the referenced_locals line and rebuilding:

BUG cond_write_param('0x0', '0x2a'): wasmtime=42 unicorn=0 native=0
BUG cond_write_param('0x0', '0xffffffff'): wasmtime=-1 unicorn=0 native=0
BUG cond_write_param('0x0', '0x2a') [poisoned upper halves]: wasmtime=42 unicorn=0
41/44 cases matched wasmtime — RESULT: FAIL (3)

All three failures are cond_write_param, the exact shape the fix guards; the other 41 stay green, so the control is specific rather than a blanket break. They are miscompiles, not declines — which is the point: a decline-matrix probe could never have caught this, only an execution oracle. Both oracles (unicorn and the native arm64 MAP_JIT path) agree on the wrong value. Restored, and back to 44/44.

Pre-edit RED on both gates (captured before touching them — each gate self-reds, no new assertion needed):

  • cross_backend_op_parity.rs flagged both entries stale by its own check:
    local.set+get(param) — ledgered as an ARM/aarch64 divergence but both now agree (arm_ok=true, aarch64_ok=true); delete the entry (same for local.tee(param)).
  • aarch64_m2_decline_538.py printed BUG local.set on a param: compiled — expected a LOUD declineRESULT: FAIL (1 silent).

Both are green after the ledger edits.

Ledger, decline honesty, and floors

Deleted the two stale parity entries; "leaving the FOUR below" is now TWO (both halves of bulk memory). Removed the closed local.set on a param decline entry.

The FLOAT-param decline MOVED rather than disappeared. Homing on writes_param makes a leaf function that writes a param hit the float check too, so a declared float param now declines from a second direction. Added as its own decline-probe entry and a unit test, both asserting the FLOAT parameter machine reason. The old label ("homing needs an FP store") was false — the encoder has str s/d since v0.54 L2; the real reason is that the home-slot model is single-register-file. Corrected in the probe, the selector message, and the module doc.

Three floors were vacuous — each tolerated a large silent regression. Raised to measured values:

gate old new
aarch64 parity leg (at_parity) 60 108
decline matrix 12 14
matrix accepted ops 32 61

The old parity floor was ~half the real count, so the entire param-write class could have regressed to a decline unnoticed. Caveat: the matrix floor was measured locally by running the script (61 accepted, 355 checks, PASS, empty declined frontier), but the CI-side sed extraction around it is unchanged and not exercised locally.

Also wires aarch64_param_homing_851_differential.py into CI — it was marked ci-status: wired but had no step — and corrects a stale comment block in selector.rs that still read "params stay read-only, see the LocalSet/LocalTee decline below".

Decline list re-verified, not assumed

I removed call_indirect and "reading own params across a call" from the selector module doc's decline list. Both verified by compiling the shapes (exit 0 each), not by inspection.

Residual

The None branch of current_func_param_count still uses the unsound count_params heuristic. Unreachable from the CLI — func_arg_counts is populated for every function index (imports first, then local), so .get(func.index) is always Some — but the direct compile_function API can still reach it. Documented at the function; not fixed here.

Related finding — filed separately as #970

The same count_params(ops).min(declared) shape exists in ARM (arm_backend.rs:393) and RISC-V (effective_num_params). RISC-V is confirmed broken by execution: on the fall-through path it reads an uninitialised stack slot (returns the 0xDEADBEEF poison under unicorn), which is an information-disclosure shape on top of the miscompile — worse than the aarch64 instance, which returned a deterministic 0. ARM is incidentally correct on the simple shape and miscompiles once the param register is clobbered by a call. Not fixed here — each backend has a different local/slot model and needs its own differential.

Gates

  • cargo test --workspaceexit 0, 2909 tests, 0 failures (re-run without a pipe; the earlier ambiguous result was tail's exit code, not cargo's)
  • cargo clippy --workspace --all-targets -- -D warningsexit 0, no warnings
  • cargo fmt --checkexit 0
  • python3 scripts/claim_check.py claims.yaml43/43
  • param-homing differential — 44/44, unicorn + native arm64
  • decline matrix — 14/14
  • aarch64 matrix — 61 ops, 355 checks, PASS, empty declined frontier

Base is current with origin/main (merged eea92e11, #967).

🤖 Generated with Claude Code

https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

avrabe and others added 8 commits August 13, 2026 20:30
Adds `aarch64_param_homing_851.wat` + its wasmtime/unicorn/native-MAP_JIT
differential. Every exported function WRITES one of its own parameters, which
the aarch64 selector loud-declines in a LEAF function today, so `--all-exports`
exits non-zero — the RED state, captured before any implementation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
… hit the API session limit

Committed by the hub coordinator so the work survives the interruption. NOT
reviewed and NOT complete: the lane stopped mid-task, so this is a restore
point, not a claim that anything is verified. The lane resumes from here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ixture

`cond_write_param` / `cond_write_param_loop` cover the #457 param-count
inference hole found while writing this differential: a param written before it
is read in LINEAR op order — but only CONDITIONALLY — was reclassified as a
zero-init non-param local and compiled SILENTLY WRONG. Measured on c2f9d72:
`cond_write_param(0, 42)` returns 0, wasmtime returns 42.

`param_write_across_call` now evaluates its `call` with an EMPTY value stack: a
live temp across a call is a separate, still-live aarch64 decline, so the old
shape would have read as a regression of this lane rather than a guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
… lower

Two changes, both in the param-homing mechanism:

1. `selector.rs` — widen the homing predicate from `is_non_leaf &&
   references_param` to `(is_non_leaf && references_param) || writes_param`. A
   written param now gets an 8-byte home slot filled from its argument register
   in the prologue, and `local.get` loads a fresh copy, so the aliasing hazard
   the decline was guarding against (a param write clobbering a value already on
   the value stack) is structurally impossible. `writes_param` is a SUBSET of
   `references_param`, so exactly one class changes behaviour: leaf functions
   that write a param, every one of which loud-declined before. Non-leaf homing
   is byte-identical. The now-unreachable decline arms are kept as loud INTERNAL
   invariant errors rather than deleted.

2. `backend.rs` — fix a silent miscompile in the param-count inference. With a
   declared count in hand the bound is now `min(highest REFERENCED index + 1,
   declared)`, not the read-first heuristic. The old rule demoted a param that
   was written before it was read in LINEAR op order to a ZERO-INITIALIZED
   non-param local, so a CONDITIONAL write emitted wrong code instead of
   declining. Measured on c2f9d72:

     cond_write_param(0, 42): wasmtime=42 synth=0

   `min` (rather than plain `declared`) preserves the existing leniency for a
   function with >8 declared params that only touches the first few.

Differential: 44/44 cases match wasmtime under unicorn AND the native arm64
MAP_JIT oracle, floor `emulations >= 44` measured exactly 44.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
RQ-57-A64PARAM landed param homing; this is the ledger/gate half that the
implementation forces, plus the doc-honesty corrections the change makes due.

Ledger closes (each was RED before this commit, by the gate's own stale-entry
check — captured before editing):
  * cross_backend_op_parity.rs — `local.set+get(param)` and `local.tee(param)`
    reported STALE with `arm_ok=true, aarch64_ok=true`. Deleted; the "FOUR
    below" prose is now TWO (both halves of bulk memory).
  * aarch64_m2_decline_538.py — "local.set on a param" printed
    `BUG ...: compiled — expected a LOUD decline`. Deleted.

Decline honesty (moved, not deleted). The FLOAT-param decline is now reachable
from a SECOND direction: a LEAF function homes as soon as it WRITES a param, so
a declared float param declines there too. Added as its own probe entry AND a
unit test, both asserting the "FLOAT parameter" machine reason. The old label
("homing needs an FP store") was FALSE — the encoder has `str s/d` since v0.54
L2; the real reason is that the home-slot model is single-register-file.

Three floors were vacuous — each tolerated a large silent regression:
  * aarch64 parity leg   60 -> 108 (measured; old floor was ~half the real count)
  * decline matrix       12 ->  14 (measured)
  * matrix accepted ops  32 ->  61 (measured; gale's stale 0.50.x baseline)

Also wires the param-homing differential into CI (it was marked
`ci-status: wired` but had no step) and corrects a stale comment block in
selector.rs that still said "params stay read-only, see the LocalSet/LocalTee
decline below" — that decline no longer exists.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
…t parsing

An UNQUOTED GitHub Actions step name containing " #" is truncated by YAML at
the comment marker. 14 other steps in this file already display as a bare
"Run" in the Actions UI for exactly this reason, which makes a red
aarch64-oracle job hard to attribute to a specific oracle. Quote the step this
lane adds so at least it stays legible; sweeping the other 14 is cosmetic and
deliberately left out of this lane.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
@codecov

codecov Bot commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 89.39394% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/synth-backend-aarch64/src/selector.rs 89.09% 6 Missing ⚠️
crates/synth-backend-aarch64/src/backend.rs 90.90% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

…ming-851

# Conflicts:
#	crates/synth-backend-aarch64/src/selector.rs
@avrabe
avrabe merged commit cb80e60 into main Aug 14, 2026
57 checks passed
@avrabe
avrabe deleted the feat/aarch64-param-homing-851 branch August 14, 2026 13:31
avrabe added a commit that referenced this pull request Aug 14, 2026
…MS (#970) (#972)

Six artifacts still read `proposed` while their work is on main. Re-graded to
`implemented`, so the release-readiness query means something:

  RQ-57-SKIPEXIT   #952  (#960)   declined REQUESTED export exits non-zero
  RQ-57-PROVGAP    #944  (#967)   verified origins for introduced branches
  RQ-57-DOCSWEEP   #946  (#968)   Tiers 2+3 honesty sweep
  RQ-57-SPWILD     #946  (#969)   writes_sp 175-of-222 wildcard expanded
  RQ-57-A64PARAM   #851  (#971)   aarch64 written-param homing
  RQ-561-ZEROMEM   #953  (v0.56.1) shipped two releases ago, never re-graded

NEW: RQ-57-COUNTPARAMS (#970). It has no artifact because nobody planned it —
the #851 lane found it while fixing the aarch64 instance of the same shape.
An unplanned finding with no artifact is invisible to the release query, which
is exactly how #933 slipped a release, so it gets one now.

What it records that the issue alone does not:

  * The severity differs by BACKEND and both halves are stated at the
    confidence they were established. RISC-V is EXECUTED-confirmed: with a
    poisoned stack the function returns 0xDEADBEEF — an UNINITIALISED stack
    slot, i.e. previous frame contents. That is information disclosure, not a
    wrong value. ARM's exact wrong value was INFERRED FROM DISASSEMBLY, never
    executed; the artifact says so, because the #851 lane's first draft stated
    it as measured and the advisor caught it.
  * ARM is only INCIDENTALLY correct on the simple shape (a merge-point
    `str r1,[sp]` catches the still-live param) and breaks once a call
    clobbers the param register — so "ARM looks fine" is not evidence.
  * Red-first must be per-backend BY EXECUTION. Assuming one backend's
    evidence transfers is what left this latent after the aarch64 fix.
  * The named residual survives: the `None` branch of
    `current_func_param_count` still uses the unsound heuristic — unreachable
    from the CLI, reachable via the direct `compile_function` API.

Per the user's direction this ships in v0.57 rather than as a v0.56.3 patch.

rivet: errors unchanged (50 before and after); warnings 164 -> 166, which is
the two every artifact in this file carries (trailer-reference naming, and the
`verifies` link that lands with the test) — verified identical for
RQ-57-SPWILD and RQ-57-GPIO, so this is one more artifact of the same shape,
not a new defect. claim_check 43/43.

Refs #970


Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
avrabe added a commit that referenced this pull request Aug 14, 2026
…of them mine

Cold review of the assembled release. Nothing blocked the tag; everything below
is accuracy. Four of the eight were errors in the CHANGELOG I had just written,
which is the reason the review exists.

THE GENERALIZABLE FINDING, and it is pointed given this release's theme:
`check_generated_fresh` byte-compares the RENDERED FEATURE_MATRIX against the
TEMPLATE. `render_feature_matrix` only substitutes `{{...}}` fields, so the gate
proves the render is faithful to the template — and NEVER that the template is
faithful to the code. Every stale number below lives in template prose no
substitution touches. In a release titled "the checkers were the defects", that
is the checker that cannot fail. Three independent stale numbers survived a
green 43/43.

USER-FACING FALSE, verified by compiling rather than by reading:
  FEATURE_MATRIX listed "writing a PARAM local in a LEAF function" as a LOUD
  DECLINE on aarch64. #971 shipped exactly that. A leaf `local.set` on a param
  compiles: 32 bytes of machine code, exit 0. Also corrected in the same row:
  homing is no longer non-leaf-only, and the float-param decline widened with
  it. Fixed in the TEMPLATE (the render is generated) + regen.

MY CHANGELOG ERRORS:
  * "145 of the 175 pre-declined / 30 reachable" matched no partition. The
    shipped source (wcet_loops.rs:1232) says 142 give up with `true`, leaving
    33. Re-derived: 142/33. Corrected.
  * "demoted to a zero-initialised local" is wrong for the two backends the
    entry is about — zero-init is gated on first-access-being-a-READ, and in
    the cond-write shape the first access IS the write, so nothing initialises
    the slot. That is WHY it reads poison; the old wording made an
    information-disclosure bug sound like a benign wrong value, and contradicted
    the entry's own next sentence.
  * "Nine artifacts" — there are ten, and RQ-57-DOCSWEEP (#946/#968) had NO
    CHANGELOG entry at all despite touching CLAUDE.md, coq/STATUS.md,
    PROJECT_STATUS.md, the matrix template and eight source files. Added.
  * "Five in-tree oracles took that opt-in" — eight scripts plus three Rust
    tests. All eight carry floors, but `i64_param_518_riscv_loudskip`'s is
    `compiles >= 1`, which is a floor and NOT the "tight" one the paragraph
    claimed for the set. Named rather than folded into the claim.

STALE COUNTS (the template-prose class above):
  ORACLE_WIRING.md, the matrix template and claims.yaml all said "137 oracles /
  295,621 emulator entries". Re-derived independently — and the reviewer's
  number and mine agree exactly: 144 oracles / 296,059. Both `count-min` pins
  moved 137 -> 144 with them (same `emulations >=` pattern, two sibling claims);
  the pinned verbatim texts moved too, or the ledger would have gone red
  against its own corrected doc.

REVERSE STALENESS (a doc calling SHIPPED work missing):
  `synth verify` declines shift rules citing "SMT modeling of the variable-shift
  register encoding is an open gap". #975 CLOSED that gap — it modelled
  LslReg/LsrReg/AsrReg/RorReg as Rm<7:0> (ARMv7-M A7.7.68/70/12/117) and moved
  five lowerings Invalid -> Verified. Both comments corrected to say what is
  true: the modelling gap is closed, the remaining decline is a WIRING residual.
  Behaviour deliberately unchanged — rewiring the rule table is a
  verification-surface change, not release assembly. Filed as #981.

ARTIFACT:
  RQ-57-PROVGAP still asserted "9 object branches with no WASM origin" as fact
  while its own PR disproved it. Outcome recorded, as RQ-57-BACKFILL already did.

  docs/architecture/CRATE_STRUCTURE.md said 18 crates; there are 19. A
  RECURRENCE — PROJECT_STATUS.md cites this exact drift as why it was gutted in
  the #946 sweep, and one file over it was live again.

Gates after: claim_check 43/43, check_version_pins OK at 0.57.0,
cargo check -p synth-cli rc=0.

Refs #980, #981

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L
avrabe added a commit that referenced this pull request Aug 14, 2026
* chore(release): v0.57.0 assembly — "the checkers were the defects"

Nine artifacts. In five of them the bug was in the machinery that checks the
compiled code, not in the compiled code:

  #975  ArmSemantics silently no-oped 87 of 222 ops — a Rocq-proved,
        default-on rotl rule was "validated" by a model that executed neither
        of its instructions
  #976  the gpio differential CANNOT discriminate the miscompile it guards —
        complementary conditions, so no input to that driver can
  #969  writes_sp claimed exhaustiveness over a wildcard absorbing 175 of 222
  #967  the "9 unattributed branches" were manufactured by witness's own
        hardcoded divergence text
  #979  the prescribed release: back-fill would have written 32 false entries,
        with the one correct pre-existing value beside them as the disproof

The unifying property is that each of those checks COULD NOT FAIL. This
release makes them able to fail and proves it by making them fail on purpose.

Also fixed, and the most severe item: #974 — a conditionally-written parameter
was demoted to a zero-init local on ARM and RISC-V. Exit 0, no decline, wrong
code; on RISC-V it reads an UNINITIALISED stack slot (0xDEADBEEF under a
poisoned stack), an information-disclosure shape. ARM behaves identically —
which the issue predicted otherwise, and only execution settled.

Release surfaces, all four swept and checker-confirmed at 0.57.0:
  Cargo.toml [workspace.package] + 10 path-dep pins
  MODULE.bazel, npm/package.json, Cargo.lock (cargo metadata)
  scripts/check_version_pins.py: OK

Derived artifacts regenerated (--emit-status): artifacts/status.json,
docs/status/FEATURE_MATRIX.md. Claim gate: 43/43.

Open by design, named not hidden: #973 (ARM select miscompile, found only
because a lane compiled ARM fixtures — which CI never does), #977 (ELF-magic
flake, second sighting), #938 (breaking object 0.x-minor bump, auto-merge
disabled), #912 (open with four remaining: items).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

* fix(release): act on the v0.57.0 cold review — 8 accuracy defects, 4 of them mine

Cold review of the assembled release. Nothing blocked the tag; everything below
is accuracy. Four of the eight were errors in the CHANGELOG I had just written,
which is the reason the review exists.

THE GENERALIZABLE FINDING, and it is pointed given this release's theme:
`check_generated_fresh` byte-compares the RENDERED FEATURE_MATRIX against the
TEMPLATE. `render_feature_matrix` only substitutes `{{...}}` fields, so the gate
proves the render is faithful to the template — and NEVER that the template is
faithful to the code. Every stale number below lives in template prose no
substitution touches. In a release titled "the checkers were the defects", that
is the checker that cannot fail. Three independent stale numbers survived a
green 43/43.

USER-FACING FALSE, verified by compiling rather than by reading:
  FEATURE_MATRIX listed "writing a PARAM local in a LEAF function" as a LOUD
  DECLINE on aarch64. #971 shipped exactly that. A leaf `local.set` on a param
  compiles: 32 bytes of machine code, exit 0. Also corrected in the same row:
  homing is no longer non-leaf-only, and the float-param decline widened with
  it. Fixed in the TEMPLATE (the render is generated) + regen.

MY CHANGELOG ERRORS:
  * "145 of the 175 pre-declined / 30 reachable" matched no partition. The
    shipped source (wcet_loops.rs:1232) says 142 give up with `true`, leaving
    33. Re-derived: 142/33. Corrected.
  * "demoted to a zero-initialised local" is wrong for the two backends the
    entry is about — zero-init is gated on first-access-being-a-READ, and in
    the cond-write shape the first access IS the write, so nothing initialises
    the slot. That is WHY it reads poison; the old wording made an
    information-disclosure bug sound like a benign wrong value, and contradicted
    the entry's own next sentence.
  * "Nine artifacts" — there are ten, and RQ-57-DOCSWEEP (#946/#968) had NO
    CHANGELOG entry at all despite touching CLAUDE.md, coq/STATUS.md,
    PROJECT_STATUS.md, the matrix template and eight source files. Added.
  * "Five in-tree oracles took that opt-in" — eight scripts plus three Rust
    tests. All eight carry floors, but `i64_param_518_riscv_loudskip`'s is
    `compiles >= 1`, which is a floor and NOT the "tight" one the paragraph
    claimed for the set. Named rather than folded into the claim.

STALE COUNTS (the template-prose class above):
  ORACLE_WIRING.md, the matrix template and claims.yaml all said "137 oracles /
  295,621 emulator entries". Re-derived independently — and the reviewer's
  number and mine agree exactly: 144 oracles / 296,059. Both `count-min` pins
  moved 137 -> 144 with them (same `emulations >=` pattern, two sibling claims);
  the pinned verbatim texts moved too, or the ledger would have gone red
  against its own corrected doc.

REVERSE STALENESS (a doc calling SHIPPED work missing):
  `synth verify` declines shift rules citing "SMT modeling of the variable-shift
  register encoding is an open gap". #975 CLOSED that gap — it modelled
  LslReg/LsrReg/AsrReg/RorReg as Rm<7:0> (ARMv7-M A7.7.68/70/12/117) and moved
  five lowerings Invalid -> Verified. Both comments corrected to say what is
  true: the modelling gap is closed, the remaining decline is a WIRING residual.
  Behaviour deliberately unchanged — rewiring the rule table is a
  verification-surface change, not release assembly. Filed as #981.

ARTIFACT:
  RQ-57-PROVGAP still asserted "9 object branches with no WASM origin" as fact
  while its own PR disproved it. Outcome recorded, as RQ-57-BACKFILL already did.

  docs/architecture/CRATE_STRUCTURE.md said 18 crates; there are 19. A
  RECURRENCE — PROJECT_STATUS.md cites this exact drift as why it was gutted in
  the #946 sweep, and one file over it was live again.

Gates after: claim_check 43/43, check_version_pins OK at 0.57.0,
cargo check -p synth-cli rc=0.

Refs #980, #981

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YJK5LZZEkV5smCY1jKn18L

* style: rustfmt the #975 decline-reason comment (indent 14 -> 12)

My own miss: I ran cargo check on the edited file but not cargo fmt, and
Format is a required context. The comment content is unchanged — only the
indentation rustfmt wanted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant