Skip to content

build: bump async-trait 0.1.89 -> 0.1.92 for clippy::double_must_use - #92

Closed
bkataru wants to merge 45 commits into
mainfrom
fix-async-trait-double-must-use
Closed

bkataru wants to merge 45 commits into
mainfrom
fix-async-trait-double-must-use

Conversation

@bkataru

@bkataru bkataru commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

Bump async-trait 0.1.89 -> 0.1.92 to fix clippy::double_must_use on CI.

The problem

clippy::double_must_use denies a #[must_use] attribute that carries no message
on a function returning a type that is already #[must_use]. Under -D warnings
that is a hard error, and ci.yml runs:

cargo clippy --workspace -- -D warnings

Four sites fail, all in pawan-core:

crates/pawan-core/src/agent/backend/mod.rs:18
crates/pawan-core/src/agent/pool.rs:44
crates/pawan-core/src/coordinator/mod.rs:444
crates/pawan-core/src/tools/mod.rs:42

Why a source grep finds nothing

None of those four items carries #[must_use]. #[async_trait] generates it:

#[async_trait]
pub trait LlmBackend: Send + Sync {
    async fn generate(...);
}
//  ^ the diagnostic points here, at the macro

In async-trait 0.1.89, src/expand.rs:69:

method.attrs.push(parse_quote!(#[must_use]));

The expansion returns a Future, which is already #[must_use] — hence "a type
already considered as #[must_use]".

async-trait 0.1.92 removes that line. must_use occurrences across src/:
0.1.89 = 1, 0.1.92 = 0, and the expand.rs diff is exactly that deletion. The
attribute is never emitted, so the diagnostic cannot arise.

Why not #[allow]

An #[allow(clippy::double_must_use)] on the four traits would mute a symptom whose
cause is a single line in a dependency, and would need re-litigating at every new
#[async_trait] use. This removes the cause.

Change scope

Lockfile-only. The workspace pin is already async-trait = "0.1" (caret), so no
manifest edit is needed or wanted — pinning to 0.1.92 would narrow the accepted
range.

-cargo update  async-trait 0.1.89 -> 0.1.92
               async-trait dep: syn 2.0.117 -> syn 3.0.6

The syn 3.x addition is forced by async-trait 0.1.92's own manifest
(syn = "3"), not collateral: 0.1.89 required syn = "2.0.46". The lockfile holds
all three (syn 1.0.109, 2.0.117, 3.0.6) for different dependents; no duplicates.

Diff: 14 insertions, 3 deletions, Cargo.lock only.

Verification

Under 1.99.0, which is what CI's floating @stable resolves to:

check result
cargo clippy -p pawan-core -- -D warnings EXIT=0, 0 errors
cargo test -p pawan-core EXIT=0 — 1037 passed, 0 failed

The syn 2.x -> 3.x major bump is covered by the test run, not just the lint gate.

Before this change, the same toolchain on the same crate produced 4 errors at exactly
the four sites above.

Limits worth stating

  • 1.95.0 is green either way and proves nothing: that toolchain does not fire this
    lint. Green on the local default toolchain is not evidence this is fixed — only a
    1.99.0 run is. CI's floating @stable is what actually catches it.
  • Verified on pawan-core, the crate that failed. I have not run a clean
    full-workspace -D warnings pass on this exact lockfile.
  • Because @stable floats, a still-newer stable may surface unrelated lints. This
    addresses this lint only.

Refs #86

bkataru added 30 commits May 30, 2026 02:33
T45–T49: TUI slash handler fixes, test fixes, cleanup
- Remove unused imports (PawanConfig, ToolDefinition) in execute.rs
- Remove dead duplicate functions from construction.rs (already in mod.rs)
- Gate test-only re-exports behind #[cfg(test)] in agent/mod.rs, config/mod.rs
- Remove unused QUEUE_DONE_TTL_MS constant from tools/task.rs
- Add #[allow(dead_code)] on future-API items in pawan-cli (theme, layout, etc.)
- Add v0.5.7 changelog entry with refactoring, features, and test summary
- Update test counts from 988+ to 1627+ across README, crate READMEs, and docs
- pawan-core: remove unused imports, fix mut variables
- pawan-cli: fix clamp, sort_by_key, checked_div, needless borrows
- rename ExportFormat::from_str to parse to avoid std trait confusion
- fix unused variables in tests with .. rest patterns
fix: resolve all clippy -D warnings across workspace
- README.md, crate READMEs, guide.md, triage.md, CHANGELOG.md
docs: update test count and version refs for v0.5.7
…#27)

* fix: remove maki refs, fix NVIDIA model catalog auth, trim fallback list

- Remove 'inspired by maki-ui' from theme.rs and layout.rs comments
- Send Authorization Bearer header when NVIDIA_API_KEY is set
- Replace 500+ hardcoded models with 13 curated fallback models
- Fix clippy sort_by_key and redundant import warnings

* fix: update model catalog tests and snapshot for curated fallback list

- Lower test_default_models_not_empty threshold from 50 to 10
- Accept new model selector snapshot with 13 curated models

* refactor: decompose high-CRAP functions into focused helpers

CRAP Reduction (execute.rs):
- execute_with_all_callbacks: 730 lines → 179 lines (75% reduction)
- Extracted 9 helpers: inject_eruka_context, build_user_prompt,
  apply_iteration_budgets, call_llm_with_retry, accumulate_token_usage,
  strip_thinking_blocks, handle_no_tool_response, check_tool_permissions,
  execute_pending_tools, collect_tool_results

CRAP Reduction (session_ops.rs):
- export_as_html: CC 46 → ~3 (4 helpers: build_html_header, build_html_footer,
  render_message_html, render_tool_calls_html)
- generate_handoff_prompt: CC 49 → ~4 (5 helpers: extract_recent_context,
  extract_mentioned_files, extract_task_items, extract_constraints,
  build_prompt_header)

All 1643 tests pass. Clippy clean.

* refactor: decompose main.rs run(), parse_compaction_summary, quality_score_for

CRAP Reduction:
- main.rs run(): CC 60 → ~30 via setup_config, resolve_session_resume extraction
- parse_compaction_summary: CC 35 → 1 via 6 helper functions (extract_section_content,
  parse_summary_field, parse_token_counts, parse_compressed_sections,
  parse_preserved_sections, parse_numbered_items)
- quality_score_for: CC 37 → 1 via 3 helpers (score_by_vendor, score_by_capability,
  score_by_size)

Coverage baseline captured: 77.28% lines / 79.08% functions / 76.83% branches
All 1643 tests pass. Clippy clean.

* chore(pawan): sync 3 file(s)

* test: add unit tests for pure TUI functions

- scrollbar.rs: 9 tests (construction, render edge cases, color handling)
- fuzzy_search.rs: 12 tests (filter, navigation, command_prefix)
- status_bar.rs: 11 tests (abbrev_model, format_tokens, context_bar)

All 1655 tests pass. Clippy clean.

* refactor(tui): convert render.rs to render/ directory module

* chore(pawan): sync 3 file(s)

* refactor(cli): decompose high-complexity main.rs run functions

Break down run_headless (CC=51), run_commit (CC=40), run_doctor (CC=31),
and run_tasks (CC=31) into focused helper functions to reduce cyclomatic
complexity and CRAP scores. Behavior preserved verbatim.

* test(tui): relocate render snapshots and make CWD redaction width-stable

Move render snapshots into render/snapshots/ after the render module split
and regenerate them against current TUI output. Pad the [CWD] redaction to
preserve line width so the welcome box border stays aligned regardless of
working-directory length, making the snapshot portable across environments.

* refactor: decompose high-complexity functions across cli and core

Decompose App::main_loop, MiseTool::execute, OpenAiCompatBackend
streaming/generate, App::slash_route, and handle_input_panel_key into
focused helper functions. Behavior preserved verbatim.
- LspTool::execute: 10 tests (error paths, arg validation, schema)
- InjectionDetector: 22 tests (scan_file, scan_file_streaming, helpers)
- main.rs helpers: 20 tests (parse_git_status, commit prompt, headless)
- TUI model picker: 8 tests (navigation, input, selection)

Total workspace tests: 1715+
- Workspace version 0.5.8, workspace deps pawan-core/pawan-mcp 0.5.8
- CHANGELOG: add v0.5.8 entry (CRAP reduction, render decomposition, 60 tests)
- Fix CHANGELOG corruption (embedded line numbers, snapshot tags)
- Update test counts 1643→1779 across README, guide, triage, crate READMEs
- Regenerate insta welcome snapshots for v0.5.8
- Update pawan-api health endpoint version in README
TUI redesign + animate-core value tweens (v0.5.9)
chore: v0.5.9 version bump + docs update
Replaces the unmaintained `atty 0.2.14` crate with the stdlib `std::io::IsTerminal` trait (stable since Rust 1.70). Resolves RUSTSEC-2024-0375 (unmaintained) and RUSTSEC-2021-0145 (Windows unaligned read) for pawan-cli's direct dependency on `atty`.

**Changes**
- `crates/pawan-cli/src/main.rs`: 3 call sites switched from `atty::is(Stream::*)` to `std::io::{stdin,stderr}().is_terminal()` (one new `use std::io::IsTerminal;` import)
- `crates/pawan-cli/Cargo.toml`: drop `atty = "0.2.14"`
- `Cargo.lock`: atty and its transitive `hermit-abi` are no longer resolved
- `.cargo/audit.toml`: document accepted-risk suppressions for the 11 advisories (5 vulnerabilities + 6 unmaintained warnings) that remain in the lockfile but are out of reach or already patched
- `cargo fmt --all`: bring workspace into compliance with the current rustfmt (35 files, 446+/396-, no semantic changes)
- Refresh stale welcome-screen snapshot (v0.5.8 → v0.5.9) for the lib and bin targets

**Verification**
- `cargo check -p pawan`: clean
- `cargo test -p pawan`: 269/269 pass locally
- `cargo audit`: clean (0 advisories with the new `.cargo/audit.toml` suppressions)
- `cargo fmt --all -- --check`: clean

**Note on CI**
The 4 `test_render_welcome_snapshot` / `test_session_tags_persist_on_save` failures in CI are pre-existing on `main` (the welcome snapshot still has v0.5.8 but the code is v0.5.9, and `test_session_tags_persist_on_save` is environment-flaky on the session-dir path). The atty fix is unrelated to those failures. They should be addressed in a separate PR.

Closes #4 (RUSTSEC-2024-0375) and #13 (RUSTSEC-2021-0145) via auto-close on merge.
Adds RMUX-backed terminal workflow support, /rmux typed slash command, /model key-release fix, and PTY-backed TUI QA.
Bump workspace crates to v0.5.10 and finalize release notes after RMUX terminal workflow integration.
Adds RMUX live roundtrip testing, kill_session support, /rmux kill routing, and stricter RMUX validation/diagnostics.
Bump workspace crates to v0.5.11 and finalize release notes for RMUX hardening.
Render RMUX snapshot tool results as terminal-focused TUI cards and switch fixed TUI statics to LazyLock.
Bump workspace crates to v0.5.12 and finalize release notes for RMUX snapshot cards.
Adds rmux list_sessions support, /rmux list routing, live roundtrip list assertions, and docs.
Bump workspace crates to v0.5.13 and finalize release notes for RMUX session discovery.
Adds rmux list_panes support, /rmux panes routing, live pane inventory assertions, and docs.
Bump workspace crates to v0.5.14 and finalize release notes for RMUX pane discovery.
Renders rmux list_panes tool results as active-pane inventory cards while preserving generic tool rendering.
Bump workspace crates to v0.5.15 and finalize release notes for RMUX pane-list cards.
Renders rmux list_sessions tool results as active-session inventory cards while preserving other tool rendering.
Bump workspace crates to v0.5.16 and finalize release notes for RMUX session-list cards.
Renders rmux send, key, wait, and kill tool results as action-focused TUI cards.
Bump workspace crates to v0.5.17 and finalize release notes for RMUX status cards.
bkataru added 7 commits June 14, 2026 07:38
Track and render model picker catalog source metadata for fallback vs live NVIDIA catalogs.
clippy::double_must_use denies #[must_use] with no message on a function
returning a type already #[must_use]. The four firing sites
(agent/backend/mod.rs:18, agent/pool.rs:44, coordinator/mod.rs:444,
tools/mod.rs:42) hold no #[must_use] themselves: #[async_trait] generates it.

async-trait 0.1.89/src/expand.rs:69 pushed method.attrs.push(#[must_use])
unconditionally, and the expansion returns a Future, which is already
#[must_use]. 0.1.92 removes that line, so the attribute is never emitted and
the diagnostic cannot arise.

Lockfile-only; the workspace pin is already async-trait = "0.1". The
transitive syn 2.0.117 -> 3.0.6 is forced by async-trait 0.1.92's own
manifest (syn = "3"), not collateral.

Verified with cargo clippy -p pawan-core -- -D warnings under 1.99.0
(EXIT=0, 0 errors). 1.95.0 does not fire this lint, so green on the default
toolchain is not evidence -- CI's floating @stable is what catches it.

Refs #86
@bkataru

bkataru commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

CI note: cargo audit is red for a pre-existing reason, not from this change

cargo audit fails on this PR with 7+ vulnerabilities, but none of them are
this change's:

advisory package
RUSTSEC-2026-0204 crossbeam-epoch
RUSTSEC-2026-0258 (x2) h2
RUSTSEC-2026-0195 / -0194 quick-xml
RUSTSEC-2026-0185 quinn-proto
RUSTSEC-2026-0285 rustls
RUSTSEC-2026-0249 smartstring
RUSTSEC-2026-0190 anyhow
RUSTSEC-2026-0306 faster-hex
RUSTSEC-2026-0253 lru
RUSTSEC-2026-0186 memmap2
RUSTSEC-2026-0205 scc

async-trait and syn are not among them. This bump adds only async-trait
0.1.89 -> 0.1.92 and syn 3.0.6; neither package appears in the audit output.

Why "main is green" does not contradict this

main's most recent ci.yml runs report success, but they ran on 2026-06-14
(de893a97, created 10:05Z), while the advisory database this run used was last
updated 2026-10-03. Those runs are green against a June advisory snapshot and say
nothing about the current one. This is the pre-existing condition tracked in #83 —
audit fails on main independent of any PR.

So the red gate here is a known-red baseline, not a regression from this branch. It
should not be read as evidence that this fix is broken; likewise, its red state should
not be used to conclude anything about this change in either direction. The
authoritative signal for this PR is Check & Lint, which is what this change targets.

Refs #86

@bkataru

bkataru commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Retraction: my previous CI note was wrong on two counts

My note above said cargo audit is red "for a pre-existing reason, not from this
change." I defended that with the wrong evidence and mis-stated the counts. Correcting
both, because a wrong claim on a public PR is worse than no claim.

Error 1 — the advisory table was wrong

I published a table of 13 advisory/package rows. The actual vulnerabilities.list
is 7 entries — 6 distinct IDs across 5 distinct packages:

advisory package
RUSTSEC-2026-0204 crossbeam-epoch
RUSTSEC-2026-0258 (listed twice) h2
RUSTSEC-2026-0195 quick-xml
RUSTSEC-2026-0194 quick-xml
RUSTSEC-2026-0185 quinn-proto
RUSTSEC-2026-0285 rustls

The extra packages I listed — smartstring, anyhow, faster-hex, lru, memmap2,
scc — do not appear in vulnerabilities.list. They came from grepping the whole
run log, which swept in unrelated output. My table overstated the count and
misattributed packages that are not findings in this run.

Error 2 — my "main is green" reasoning was structurally invalid

I compared against ci.yml runs dated 2026-06-14 and concluded "main was green against
a June advisory snapshot." That was wrong twice over: the audit job is not in ci.yml
at all. It is a separate workflow, audit.yml ("Security Audit"), which runs on
push to main, on pull_request, and on a schedule. So those ci.yml runs contained
no audit job and could not serve as a baseline either way.

The correct baseline, and the actual conclusion

audit.yml on main at de893a97, run today at 2026-10-05T13:03Z:

MAIN count = 7   found = True   ignore-list size = 10
  RUSTSEC-2026-0204 crossbeam-epoch
  RUSTSEC-2026-0258 h2 (x2)
  RUSTSEC-2026-0195 quick-xml
  RUSTSEC-2026-0194 quick-xml
  RUSTSEC-2026-0185 quinn-proto
  RUSTSEC-2026-0285 rustls

Identical to this PR's audit output — same count, same IDs, same packages, same
ignore list.

And the lockfile diff against origin/main is exactly what I claimed it was:

REMOVED         async-trait 0.1.89
ADDED           async-trait 0.1.92
ADDED           syn 3.0.6
VERSION CHANGED async-trait 0.1.89 -> 0.1.92

Nothing else moved. syn 3.0.6 and async-trait 0.1.92 are absent from the advisory
list, so this change introduces no finding.

Conclusion stands, but now on the right evidence: the audit failure is the
pre-existing condition in #83 and is unrelated to this branch. It is not visible as
a red main only because audit.yml has no continue-on-error yet still reports
success on main — which I have not explained and am not going to guess at. What
matters for this PR is unchanged: Check & Lint, the job this change targets, passes.

Refs #86

@bkataru

bkataru commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Follow-up: why the audit shows success on main and failure here (resolved)

My previous comment left this unexplained. It is explained, and the mechanism matters
because it means the audit gate has effectively never gated main.

audit.yml in full is 33 lines; the relevant parts are the triggers and the single step:

on:
  push:
    branches: [main]
    paths:
      - "**/Cargo.toml"
      - "**/Cargo.lock"
      - ".github/workflows/audit.yml"
  pull_request:
    branches: [main]
    paths: [ ... same three ... ]
  schedule:
    - cron: "0 6 * * 1"      # Mondays 06:00 UTC
jobs:
  audit:
    steps:
      - uses: actions/checkout@v4
      - uses: rustsec/audit-check@v2.0.0
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

There is no continue-on-error, no on failure, and no deny/strict input. So
two things follow.

1. push to main barely triggers this workflow at all

The paths: filter means a commit touching only .rs files produces no audit run.
Dependencies can drift into a vulnerability on a code-only commit and the gate never
evaluates them. In 8 recent runs there is no push event at all — only
pull_request and schedule.

2. audit-check@v2 reports findings without failing the run

Recent runs, all with the same 7 findings (vulnerabilities.found = true):

run event conclusion
main de893a97 (37313858210) schedule success
PR b8a35b6 (37344072173) pull_request failure
2026-10-04 14:53Z pull_request failure
2026-10-04 12:37Z pull_request failure
2026-09-28 / 09-21 schedule success

Seven runs, split perfectly by event and never by data: every schedule run is
success, every pull_request run is failure, all reporting identical findings.
On the pull_request path the action posts a failing check status onto the PR, which
is a different surface from the run conclusion — that, not a change in vulnerability
data, is why this PR is red.

Consequence

main is not gated by this workflow in practice: its scheduled runs pass while 7
advisories are open, and code-only commits do not run it. The advisory surface is real
(it has issues: write and checks: write, so it should be filing), but the run-level
gate is not enforcing. Tracking that on #83; this PR remains unaffected either way,
since its own audit output is identical to main's and its lockfile diff is only
async-trait 0.1.89 -> 0.1.92 plus syn 3.0.6, neither of which is flagged.

Refs #86

@bkataru

bkataru commented Oct 5, 2026 •

Copy link
Copy Markdown
Member Author

Audit-gate mechanism, hardened (and one alternative falsified)

This supersedes my previous note on the mechanism. Two corrections to how I supported it.

Correction 1 — "identical findings" is not an accident of the event

It was reasonable to suspect main's success was simply an older advisory database that
had not yet seen these 7. That is falsified. Both runs used the same snapshot:

run advisory-db commit last-updated advisory-count
main de893a97 (schedule, 37313858210) ef6173cb 2026-10-03T10:14:03+02:00 1290
PR b8a35b6 (pull_request, 37344072173) ef6173cb 2026-10-03T10:14:03+02:00 1290

Identical database, identical commit, identical 7 findings — and opposite conclusions.
So the conclusion is decided by the event, not by what was found or when it was found.

Correction 2 — my earlier "no push run exists" was wrong; here is the real history

I claimed push to main "does not appear at all." Across the last 60 runs there are
20 push, 23 pull_request, 17 schedule. Push-triggered runs do exist. The
accurate statement is narrower:

  • All 20 push runs are dated 2026-06-14 or earlier — they predate de893a97,
    which has been main's tip since.
  • Since then main has been audited only by the weekly schedule, and every one of
    those is success.
  • Every pull_request run in the window is failure (23/23).

So main's tip has never been audited by a push. The last time a push-triggered audit
ran on this tip's ancestry was in June, against a four-month-old advisory database.

The resulting statement about enforcement

The paths: filter means a commit touching only .rs files produces no audit run at
all, so dependency drift introduced by a code-only commit is never evaluated. Combined
with audit-check@v2 reporting findings without failing the run, main has effectively
not been gated since June — while all 23 PR audits in the window failed, which is why
this looked PR-specific.

Unchanged and separately verified: this PR's audit output is identical to main's, and
its lockfile diff against origin/main is only async-trait 0.1.89 -> 0.1.92 plus
syn 3.0.6, neither of which is flagged.

Refs #86

@bkataru
bkataru force-pushed the fix-async-trait-double-must-use branch from b8a35b6 to 98da6c4 Compare October 6, 2026 13:27
@bkataru

bkataru commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

Closing as duplicate — the async-trait 0.1.92 bump landed via #94 (merged, cleared the clippy wall). Thanks for the equivalent work.

@bkataru bkataru closed this Oct 6, 2026
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