Skip to content

chore(msrv): feature-gate eframe/egui behind opt-in gui feature#39

Merged
hyperpolymath merged 2 commits into
mainfrom
chore/gui-feature-gate-msrv
May 26, 2026
Merged

chore(msrv): feature-gate eframe/egui behind opt-in gui feature#39
hyperpolymath merged 2 commits into
mainfrom
chore/gui-feature-gate-msrv

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

eframe = "0.34" (and the egui chain it pulls) raises MSRV above 1.85.0, which conflicts with the project's declared rust-version = "1.85.0" and breaks the MSRV CI job.

Move eframe into a new opt-in gui feature so default + non-GUI optional builds stay MSRV-clean. Users who want the native viewer build with --features gui on a newer toolchain.

Changes (4 files, +12/-3)

File Change
Cargo.toml eframe = { ..., optional = true }; add gui = ["eframe"] feature
src/report/mod.rs cfg-gate pub mod gui + pub use gui::ReportGui
src/main.rs:472 cfg-gate the Commands::Gui clap variant
src/main.rs:1703 cfg-gate the Commands::Gui { .. } match arm
.github/workflows/rust-ci.yml MSRV check: cargo check --features signing,http (was --all-features)

Why --features signing,http not --all-features

The MSRV job should test what users actually consume on the declared MSRV. signing (ed25519-dalek 2.1) and http (ureq 3.3) are both 1.85-compatible. The new gui feature explicitly raises MSRV — building it on 1.85 will fail, which is correct behaviour.

Test plan

  • cargo check — default build OK
  • cargo check --features signing,http — the new MSRV check command OK
  • cargo check --features gui — opt-in build still works
  • cargo clippy --all-features --all-targets -- -D warnings — clean
  • cargo test --features signing,http --no-fail-fast — all 17 suites pass
  • cargo fmt --check — clean
  • Signed commit, MPL-2.0 headers untouched

Follow-up

Future improvement: add a cargo check --features gui job pinned to a recent stable Rust so the GUI path stays buildable. Out of scope for this PR (single-concern unbreakage).

🤖 Generated with Claude Code

`eframe = "0.34"` (and the egui chain it pulls) raises MSRV above
1.85.0, which conflicts with the project's declared `rust-version =
"1.85.0"`. The MSRV CI job ran `cargo check --all-features` against
1.85, so any push silently passed (the job is currently red on main)
or would fail once toolchain caches refresh.

Move eframe into a new opt-in `gui` feature. The default and existing
optional features (`signing`, `http`) stay MSRV-clean; users who want
the native viewer build with `--features gui` on a newer toolchain.

Changes:
- Cargo.toml: `eframe = { ..., optional = true }`; add `gui = ["eframe"]`.
- src/report/mod.rs: cfg-gate `mod gui` and the `pub use ReportGui`.
- src/main.rs: cfg-gate the `Commands::Gui` clap variant and its match arm.
- .github/workflows/rust-ci.yml: MSRV `cargo check` now uses
  `--features signing,http` (drops `--all-features` to exclude `gui`).

Verified locally:
* `cargo check`                        — OK (default)
* `cargo check --features signing,http` — OK (the new MSRV check command)
* `cargo check --features gui`         — OK (opt-in build still works)
* `cargo clippy --all-features --all-targets -- -D warnings` — clean
* `cargo test --features signing,http --no-fail-fast`       — all 17 suites OK
* `cargo fmt --check`                  — clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 50 issues detected

Severity Count
🔴 Critical 4
🟠 High 16
🟡 Medium 30

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Nickel file missing SPDX-License-Identifier header (1 occurrences, CWE-1104)",
    "type": "ncl_missing_spdx",
    "file": "/home/runner/work/panic-attack/panic-attack/reports/panic-attack-20260211180017.ncl",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (2 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/chain.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/evidence.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/ambush/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/kanren/strategy.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/axial/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "expect() in hot path (4 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/assail/analyzer.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (4 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/panic-attack/panic-attack/benches/scan_bench.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "expect() in hot path (2 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/benches/scan_bench.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 49 issues detected

Severity Count
🔴 Critical 4
🟠 High 16
🟡 Medium 29

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Nickel file missing SPDX-License-Identifier header (1 occurrences, CWE-1104)",
    "type": "ncl_missing_spdx",
    "file": "/home/runner/work/panic-attack/panic-attack/reports/panic-attack-20260211180017.ncl",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "expect() in hot path (2 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/chain.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/attestation/evidence.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (1 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/ambush/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/kanren/strategy.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "unwrap_or(0) with dangerous default (3 occurrences, CWE-754)",
    "type": "unwrap_dangerous_default",
    "file": "/home/runner/work/panic-attack/panic-attack/src/axial/mod.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "critical"
  },
  {
    "reason": "expect() in hot path (4 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/src/assail/analyzer.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  },
  {
    "reason": "unwrap() without prior check -- DoS via panic (4 occurrences, CWE-754)",
    "type": "unwrap_without_check",
    "file": "/home/runner/work/panic-attack/panic-attack/benches/scan_bench.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "high"
  },
  {
    "reason": "expect() in hot path (2 occurrences, CWE-754)",
    "type": "expect_in_hot_path",
    "file": "/home/runner/work/panic-attack/panic-attack/benches/scan_bench.rs",
    "action": "flag",
    "rule_module": "code_safety",
    "severity": "medium"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath enabled auto-merge (squash) May 26, 2026 09:08
@hyperpolymath hyperpolymath merged commit 2e7b1ab into main May 26, 2026
26 of 28 checks passed
@hyperpolymath hyperpolymath deleted the chore/gui-feature-gate-msrv branch May 26, 2026 09:14
hyperpolymath added a commit that referenced this pull request May 26, 2026
## Summary

Root-cause fix for the CI failures that survived PR #39's merge. The
readiness test \`readiness_d_gui_headless_runs\` and the \`Readiness\`
workflow have been red on main since #39 hid \`Commands::Gui\` behind
\`#[cfg(feature = \"gui\")]\` — the subcommand no longer exists in
default builds, but the readiness test (which runs against default
features) still invokes \`panic-attack gui --headless\`.

The headless renderer is **pure text output** — it touches
\`ReportFormatter\` and the report data structures, never \`eframe\` or
\`egui\`. Moving it into a feature-independent module fixes the
underlying contract: the subcommand should always exist; only the
windowed renderer needs the feature.

## Changes

| File | Change |
|------|--------|
| \`src/report/gui_text.rs\` (new) | \`run_headless\` lifted verbatim
from \`gui::ReportGui::run_headless\`. Always compiled. |
| \`src/report/gui.rs\` | \`run_headless\` removed (now in
\`gui_text\`); header docstring updated. |
| \`src/report/mod.rs\` | \`pub mod gui_text;\` always; \`pub mod gui;\`
stays \`#[cfg(feature = \"gui\")]\` |
| \`src/main.rs:472\` | drop \`#[cfg(feature = \"gui\")]\` from
\`Commands::Gui\` variant |
| \`src/main.rs:1703\` | match arm: \`--headless\` always calls
\`gui_text::run_headless\`; non-headless path is feature-gated with a
\`#[cfg(not(feature = \"gui\"))]\` arm that does
\`anyhow::bail!(\"rebuild with --features gui\")\` |

## User-visible result

Default build (\`cargo build\`):
- \`panic-attack gui --headless report.json\` ✅ works (prints text
panels)
- \`panic-attack gui report.json\` → clear error: *"windowed GUI
requires the \`gui\` feature; rebuild with \`cargo build --features
gui\`, or pass --headless"*

GUI build (\`cargo build --features gui\`):
- Both modes work as before

## Test plan

- [x] \`cargo build\` — default, OK
- [x] \`cargo build --features gui\` — opt-in, OK
- [x] \`cargo clippy --all-targets --features signing,http -- -D
warnings\` — clean
- [x] \`cargo clippy --all-targets --all-features -- -D warnings\` —
clean
- [x] \`cargo test --test readiness\` — **20 passed, 0 failed**
(including \`readiness_d_gui_headless_runs\`)
- [x] \`cargo fmt --check\` — clean
- [x] Signed commit

## Why this is the principled fix

- The readiness test stays meaningful on the MSRV-clean default build
(it actually exercises the headless path).
- The windowed GUI stays opt-in because eframe raises MSRV.
- A user who tries the windowed mode without the feature gets a precise
actionable error instead of clap's "unrecognized subcommand" stack
trace.
- No conditional skip, no \`#[ignore]\`, no workflow-level allow — the
test runs every time and exercises real code.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.7 (1M context) <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