Skip to content

chore(epic): v0.7.x hardening — close P2/P3 audit findings + tray-panel UX#37

Merged
rsecss merged 20 commits into
mainfrom
chore/v0-7-x-hardening
May 25, 2026
Merged

chore(epic): v0.7.x hardening — close P2/P3 audit findings + tray-panel UX#37
rsecss merged 20 commits into
mainfrom
chore/v0-7-x-hardening

Conversation

@rsecss

@rsecss rsecss commented May 25, 2026

Copy link
Copy Markdown
Owner

Summary

v0.7.x hardening epic — 14 work commits + 2 trellis chore commits closing every P2/P3 finding from the v0.7.0 audit that was deferred into v0.7.x, plus a tray-panel focus-loss UX bug surfaced during v0.7.0 manual testing.

Single-PR by design (per epic PRD): each commit is independently reviewable but they share the v0.7.x quality-bar theme, so splitting into 10 micro-PRs would just be churn. Final PR will be squash-merged like all hardening work.

Closed findings

# Commit Finding Type
1 58f1eb4 refactor(stat): split stat.rs into 6 cohesive modules F17
2 b42269c refactor(ui): split SettingsPage and StatisticsPage into sub-components F18
3 01c6af8 refactor(i18n): canonicalize locale to en F19
4 ba336b2 refactor(ipc): hoist IPC event names to shared constants F20
5 a72fce6 perf(ipc): graded IPC timeouts (5s / 10s / 60s) F16
6 c8a1719 chore(capabilities): scope tray-panel emit to navigate_tab only F22
7 5723431 docs: sync project state to v0.7.x hardening epic in progress F25
8 530ee6c docs(platform): document cross-platform path caveats F29
9 7709305 fix(platform): real macOS fullscreen detection via CGWindowList + CGDisplay F03 + F28
10 feffdda fix(tray): auto-hide tray panel when it loses focus NEW (UX bug)

F21 was originally on the list but the audit turned out stale — `shell:default` is still live for `AboutPage` external links, so we kept it. Documented inline in the F22 commit message.

README overhaul (4 follow-up commits)

After implementation finished, the README — stuck at v0.3.0 for four releases — got a full pass:

  • `ff55c7e` — v0.3.0 → v0.7.0 catch-up, new feature taxonomy, mark v0.7.x epic complete in CLAUDE.md + `.claude/index.json`
  • `73ad575` — 3 new screenshots (statistics-overview / statistics-trend / settings-pomodoro), 2-column grid layout instead of cramped 4-in-a-row
  • `c848fa0` — drop ProjectEye comparison / Download / Roadmap / Credits, add Quick Start + Architecture sections
  • `2a0249a` — slim Highlights from 17 bullets to 7, add emoji section anchors (👀 ✨ 📸 🚀 🛠️ 🏗️ ⚙️ 🤝 📄)

Net README: -86 / +36 (just the last pass); ~40% shorter overall while covering more.

Architectural notes

  • `stat.rs` (1439 lines → 6 modules) — `mod` / `writer` / `migration` / `export` / `trends` / `health`. No surface API change. Largest file now `writer.rs` at ~340 lines.
  • macOS fullscreen — replaced `Ok(DegradedFalse)` stub with `CGGetActiveDisplayList` + `CGWindowListCopyWindowInfo` (layer==0 + bounds ±1px tolerance). Pure helper `covers_any_display` is exercised by 6 cross-platform unit tests; the macOS-only path verifies "FFI binds without panic" since CI cannot exercise a real fullscreen window. `supports_fullscreen_detection` flips back to `true`, Settings "Fullscreen Skip" toggle is no longer greyed on macOS.
  • Tray-panel blur-hide — extended the global `on_window_event` handler in `lib.rs` with a `WindowEvent::Focused(false) if window.label() == "tray-panel"` arm that calls `window.hide()`. No new port trait needed — the WindowEvent callback already provides a `&Window` reference, matching the existing `main-window` `CloseRequested` intercept pattern.
  • Graded IPC timeouts — `INVOKE_TIMEOUT_DEFAULT_MS=5000`, `IO=10000`, `EXPORT=60000`. `exportStatistics` no longer 5s-timeouts on large databases.

Test plan

  • `cargo test --manifest-path src-tauri/Cargo.toml` — 266 backend tests pass locally
  • `cargo clippy --manifest-path src-tauri/Cargo.toml --all-targets -- -D warnings` — clean
  • `npm test` (vitest --coverage) — passes coverage gates (lines 90% / functions 85% / branches 80% / statements 90%)
  • `cargo llvm-cov --fail-under-lines 90 --fail-under-functions 85` — pass (run via `npm run ci`)
  • `npx svelte-check --tsconfig ./tsconfig.json` — clean
  • `npm run format:check` (prettier) — clean
  • `cargo fmt --check` — clean
  • `npm run build` (vite) — clean
  • All 8 steps via `npm run ci` — green locally (Windows host)
  • Manual verification: `npm run tauri dev` boots cleanly, all 9 services initialize, tray pause/resume + tray panel auto-hide on blur verified end-to-end
  • CI must verify on Linux + macOS runners (macOS fullscreen real impl can only be exercised on the macOS runner)

Out of scope (pushed to v1.0.0 or unscheduled)

  • Coverage 90% → 95% (diminishing returns)
  • tip-window mini / corner notification UX
  • F15 stat fetch full-table-scan optimization (3万行毫秒返回,无真实痛点)
  • F06/F07 API rename + Beta tag removal (saved for v1.0.0 SemVer freeze)

🤖 Generated with Claude Code

rsecss and others added 20 commits May 24, 2026 16:29
- PRD with 10-commit plan covering F17/F18/F19/F20/F16/F21/F22/F25/F29/F03+F28
  + tray-panel auto-hide
- Brainstorm decisions captured: 6-mod stat split / fine-grained UI split /
  en canonical / 3-tier IPC timeout / CGWindowList macOS fullscreen
- Research: macOS fullscreen API survey persisted to research/
- implement.jsonl + check.jsonl curated with relevant specs and audit report

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes audit finding F17. The 2214-line stat.rs is replaced by 6
focused modules under src-tauri/src/services/stat/ (mod / writer /
migration / export / trends / health), each owning a single concern:

- mod.rs (393): StatService struct, Service trait impl, the writer
  command channel, and the two row-fetch helpers that orchestrate
  trends/health queries
- writer.rs (384): dedicated writer task loop + persistence helpers
  + the cfg(test) session_at / record_taken fixtures used by sibling
  test modules
- migration.rs (375): v0->v1 and v1->v2 SQLite migrations with their
  transactional rollback contracts
- export.rs (336): VACUUM INTO path validation + IANA timezone
  resolution
- trends.rs (238): pure aggregate_sessions plus the outcome/reason
  string parsers
- health.rs (395): pure compute for ECI, ribbon, rhythm, today
  counts, longest-work-segment approximation

No behavior changes. Same public surface (statistics_trends,
cycle_outcomes, export_to, enqueue_*, record_*_cfg-test). Sibling
test fixtures are factored to keep each file <= 400 lines per the
PRD AC; ECI special cases are consolidated into one parameterized
test which trades 5 named tests for a single behavior table.

Verification:
- cargo test --lib: 257 passed / 0 failed
- cargo clippy --all-targets -D warnings: clean
- cargo llvm-cov: 92.68% lines / 89.13% functions (>= 90/85 gate)
- Per-file lines after rustfmt: mod=393, health=395, writer=384,
  migration=375, export=336, trends=238 (all <= 400)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
F18 from v0.7.x hardening audit. Decomposes two monolithic page files
(1147 + 1121 lines) into orchestrators (49 + 252 lines) plus 14 cohesive
sub-components, each <=300 lines per the AC.

Settings (orchestrator + 7 sections + utils):
- TimerSection / HotkeySection / BehaviorSection (embeds AutoStartSection)
- WhitelistSection / ScheduleSection / DisplaySection / AutoStartSection
- settings/utils.ts shared errorMessage(err) helper

Statistics (orchestrator + 7 components):
- EciDisplay / SuppressedDetails / ActivityRibbon / RhythmCards
- TrendChart (chart=$state for reactive tracking after async ECharts init)
- RangeSwitcher (TranslationKey-typed labelKey)
- ExportControls (callback pattern; banners owned by orchestrator)

Verification: 131/131 tests pass, svelte-check clean, coverage
93.99% lines / 91.72% functions (above 90/85 gate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Older configs (v0.7.0 and earlier) wrote the BCP-47 long form en-US
while the runtime tray/i18n code already aliased it to the short en.
Two surfaces (validator allow-list + Settings page select) kept the
en-US literal alive in the canonical path. This commit drives the
canonical form down to en everywhere:

- New canonicalize_language() helper rewrites en-US -> en at config
  load (alongside sanitize_process_whitelist). Legacy TOML files load
  unchanged; the next save flushes en to disk.
- Tighten validate_display_config allow-list to ["zh-CN", "en"]
  (en-US no longer accepted via update_display_config).
- Simplify DisplaySection derived value: the legacy alias check is
  no longer needed because the backend canonicalizes on load.
- Update spec table in ipc-and-state.md to drop en-US from the
  accepted values column.

Defensive aliases preserved:
- I18nService::normalize_locale keeps "en" | "en-US" => ENGLISH_LOCALE
  for tray translation lookups (any caller, not just config).
- src/lib/i18n/index.svelte.ts setLocale keeps the en-US case for
  runtime setLocale() callers outside the config store.

Verification: 260 backend + 131 frontend tests pass, svelte-check
clean, clippy --all-targets -D warnings clean, frontend coverage
94.00% lines / 91.72% functions (above the 90/85 gate).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The 5 IPC event identifiers were referenced by 7 call sites across
the codebase, with the backend already using crate::events
constants for 4 of them. Two surfaces still had naked string
literals: tray.rs (emit + test assertion) and the entire frontend
src/lib/events.ts (5 listen + 1 emit). This commit closes the
gap so adding or renaming an event is a one-touch change.

Backend:
- Add NAVIGATE_TAB constant to src-tauri/src/events/mod.rs
  alongside the existing 4 constants.
- Remove the #[cfg(not(test))] gate on the events module so the
  tray.rs test that asserts the emit call can reference
  events::NAVIGATE_TAB. The module is pure const strings with no
  Tauri runtime imports, so widening visibility costs nothing.
- Replace naked "navigate_tab" in tray.rs (emit call + test
  assertion) with events::NAVIGATE_TAB.

Frontend:
- Hoist the 5 event identifiers in src/lib/events.ts to top-level
  exported consts (EVENT_STATE_CHANGED / EVENT_CONFIG_CHANGED /
  EVENT_HOTKEY_STATUS_CHANGED / EVENT_STAT_PERSISTENCE_ERROR /
  EVENT_NAVIGATE_TAB) and reuse them from the listen/emit wrappers.

Intentionally left as naked strings:
- Tray menu ids ("pause"/"settings"/"about"/"quit") — per PRD,
  scope is IPC events only.
- src/lib/__tests__/events.test.ts — these assertions are the
  wire-contract verification, replacing them with the same const
  on both sides would be tautological.
- Capability JSONs (main-window.json / tray-panel.json) — JSON
  doesn't support comments and the loader needs literal strings.

Verification: 260 backend + 131 frontend tests pass, svelte-check
clean, clippy --all-targets -D warnings clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous 5s timeout fired on every IPC call regardless of expected
duration, causing the SQLite-backed exportStatistics (VACUUM INTO +
filesystem write) to error in the UI for legitimate large databases.
This commit introduces three explicit tiers:

- INVOKE_TIMEOUT_DEFAULT_MS = 5_000  fast in-memory reads + state flips
- INVOKE_TIMEOUT_IO_MS      = 10_000 SQLite + system capability probes
- INVOKE_TIMEOUT_EXPORT_MS  = 60_000 VACUUM INTO + filesystem export

invokeWithTimeout now accepts an optional third argument (defaults to
the 5s tier), so callers opt in to a longer budget at the call site:

- getStatisticsTrends         -> IO
- getStatisticsCycleOutcomes  -> IO
- getDetectorCapabilities     -> IO
- updateHotkeysConfig         -> IO (OS hotkey registration round-trip)
- exportStatistics            -> EXPORT
- everything else             -> default 5s (unchanged)

Spec ipc-and-state.md updated to document the three tiers and the
call-site choice obligation.

Verification: 133 frontend tests pass (2 new fake-timer tests cover
the 10s IO tier and 60s EXPORT tier), svelte-check clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
tray-panel.json previously granted unscoped core:event:allow-emit,
letting the tray webview emit any event. The only emit call in
src/pages/tray/TrayApp.svelte:161 is emitNavigateTab('Settings'), so
narrow the scope to {event: "navigate_tab"} — any future emit from
the tray panel must now be added explicitly here.

F21 (delete dead shell:default from main-window.json) deliberately
skipped: the audit was outdated. AboutPage.svelte:12 calls
open(RELEASES_URL) from @tauri-apps/plugin-shell to open the GitHub
releases page, and that requires shell:default (the PRD AC itself
allows for "if shell is actually used, keep it").

Verification: cargo build succeeds (capability schema validation
runs at build time).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The v0.1.0-era .claude/index.json and the v0.7.0-shipped CLAUDE.md
both drifted from current reality during the post-v0.7.0 epic:

.claude/index.json:
- services list now enumerates all 9 services + stat/ and timer/
  sub-modules (was 7 services, stat/hotkey labelled "planned"),
  plus the tray_tooltip / window_layout pure helpers
- pages list includes StatisticsPage + 7 settings/ sub-sections
  + 7 statistics/ sub-components (was a Phase-1-era list with
  StatisticsPage marked "planned")
- bindings, stores, commands, capabilities, permissions, frontend
  tests sections updated to actual file inventory (28 ts-rs files,
  17 commands, 17 permission tomls, 16 test files, etc.)
- gaps + nextSteps rewritten around the in-flight v0.7.x epic and
  the deferred v1.0.0 / coverage-95% items

CLAUDE.md:
- "当前阶段" line now reflects v0.7.x hardening epic in progress
  (was claiming hardening complete, which conflated v0.7.0 with
  v0.7.x).
- "下一步" lists the 10-commit v0.7.x epic with finding IDs (F17-
  F22 / F25 / F29 / F03+F28 / NEW tray-panel UX) and the post-
  epic v0.7.1 plus deferred items.

memory/MEMORY.md sync (out-of-repo, recorded here for context):
- Project-state "current stage" line updated to in-progress epic.
- IPC line now reads "3 graded timeout tiers" instead of "5s".
- v0.7.x epic + 10-commit breakdown added to release-history bullets.

Verification: JSON parses cleanly (14_849 chars); CLAUDE.md edits
are descriptive prose only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three platforms have low-probability but real degradation modes when
extracting basenames for the process whitelist, and they were not
captured anywhere. F29 from the v1.0.0 audit asked for these to be
documented as known limitations rather than papered over with
defensive shims.

Added "Known limitations" section to
.trellis/spec/backend/platform-storage.md spelling out:

- Windows: paths longer than MAX_PATH (260) return Ok(None) from
  QueryFullProcessImageNameW with the PROCESS_NAME_WIN32 flag, so
  the process is treated as not-whitelisted. Source pointer:
  src-tauri/src/platform/windows.rs:175-194.
- Linux: /proc/{pid}/exe targets containing non-UTF-8 bytes flow
  through OsStr::to_str() which returns None, short-circuiting the
  basename chain to None. Source: linux.rs:330-338.
- macOS: kCGWindowOwnerName with invalid UTF-16 surrogates returns
  a U+FFFD-substituted (lossy) string via CFString::to_string(),
  which is then unlikely to equal any sanitized whitelist entry.
  Source: macos.rs:195-202.

The spec explicitly forbids adding caller-side fallback logic to
hide these — they all already manifest as Ok(None), so the
whitelist's "not in list -> show reminder" branch handles them
correctly.

CHANGELOG: added an [Unreleased] section pre-staging this entry
plus the other commits already landed in the v0.7.x hardening epic
(F16-F22 / F25 docs / F17/F18 refactors). The section gets renamed
to [0.7.1] when the release is cut.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…isplay (F03+F28)

v0.7.0 PR #29 set supports_fullscreen_detection() to false and made
detect_fullscreen_macos() return DegradedFalse. That kept the
Settings "Fullscreen Skip" toggle disabled on macOS even though
CGWindowListCopyWindowInfo was already being called successfully
for the foreground-process probe. F03+F28 from the v1.0.0 audit
asked for a real implementation; research/macos-fullscreen-apis.md
picked CGWindowList + CGGetActiveDisplayList (Option 1) over the
NSWindow / Accessibility / private-CGS alternatives because it
needs no entitlements, no TCC prompt, and reuses the existing
CF dictionary parsing scaffold.

Algorithm (matches the research doc):
1. CGGetActiveDisplayList -> Vec<u32> -> CGDisplayBounds for each
   -> Vec<DisplayRect>. Zero displays maps to DegradedFalse.
2. CGWindowListCopyWindowInfo with kCGWindowListOptionOnScreenOnly
   | kCGWindowListExcludeDesktopElements -> CFArray of window dicts.
3. For each dict with kCGWindowLayer == 0, parse kCGWindowBounds
   via CGRectMakeWithDictionaryRepresentation into a DisplayRect
   and check it covers any display rect within 1px.

Architecture:
- DisplayRect + covers_any_display() live in platform/mod.rs as a
  pure helper so the bounds-comparison logic can be unit-tested on
  any host (CI mac runner cannot exercise a real fullscreen window).
  Both are cfg_attr-gated to allow(dead_code) on non-macOS to keep
  Win/Linux clippy --all-targets clean while still compiling the
  helper for tests.
- macos.rs declares the CG FFI directly (CGGetActiveDisplayList,
  CGDisplayBounds, CGRectMakeWithDictionaryRepresentation) mirroring
  the existing CGEventSourceSecondsSinceLastEventType extern block.

Tests (6 new cross-platform tests in platform/mod.rs covering
covers_any_display):
- exact match
- 1px tolerance acceptance (Retina sub-pixel scaling)
- maximized-with-titlebar rejection (40px gap)
- multi-monitor match on secondary display
- zero-display vector returns false
- off-bounds overlay rect rejection

Plus macos.rs replaces its 2 obsolete stub-state tests with:
- supports_fullscreen_detection_is_true_after_real_impl
- is_fullscreen_app_active_runs_without_panic (the achievable CI
  contract: FFI binds + no panic; positive match needs human host)

Docs:
- spec platform-storage.md table updated to spell out the new
  macOS algorithm in the fullscreen detection row.
- CLAUDE.md "项目状态" + epic progress note both updated.
- CHANGELOG [Unreleased] adds the fix entry.

Verification: 266 backend tests pass (260 prior + 6 new cross-
platform helper tests), clippy --all-targets clean. CI cannot
exercise real fullscreen; the integration test verifies the FFI
binds cleanly on macos-latest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously the always-on-top tray-panel stayed visible after clicking
elsewhere, blocking content underneath like an unmovable widget.
Extend the global on_window_event handler in lib.rs with a new arm:
WindowEvent::Focused(false) on the 'tray-panel' label calls hide(),
matching the existing dismiss-on-tray-icon-click behavior so the
panel behaves like a native popover. Re-clicking the tray icon
re-opens it via the unchanged toggle_tray_panel path.

The handler is registered at Tauri Builder level (same place as the
main-window CloseRequested intercept), so no port/trait abstraction
is needed — the WindowEvent callback already gets a &Window we can
hide directly. The existing TrayService tests are unaffected.

AC met:
- 266 backend tests pass
- clippy --all-targets clean

Closes the last item of the v0.7.x hardening epic.
README has been stuck at v0.3.0 for four releases (v0.4 process
whitelist, v0.5 pomodoro + export, v0.6 health analysis, v0.7
hardening release). Rewrite both English and Chinese READMEs:

- Version badge 0.3.0 -> 0.7.0, add coverage badge (93%)
- Restructure Features into 5 groups: Core Timer, Smart Skipping,
  Analytics, Interface, Engineering — surfaces pomodoro, statistics,
  health analysis, process whitelist, export, hotkeys, tray-panel
  blur-hide, and the macOS fullscreen real impl
- Comparison table adds Statistics + Pomodoro rows
- Roadmap marks v0.4-v0.7 done, keeps v1.0 pending
- Build section swaps long ad-hoc command list for "npm run ci"
  one-liner (still preserves the individual checks for transparency)
- Statistics + Health screenshots noted as in-app exploration since
  the existing 4-screenshot set predates v0.6.0

CLAUDE.md + .claude/index.json: flip v0.7.x epic from "in progress"
to "complete (single PR pending review)". Drop F03/F28 and tray-panel
items from gaps (now implemented in this branch). Add the Wayland
limitation as the standing P3 gap so the picture stays honest.
Old 4-screenshot single-row layout cramped each image to ~25% of
README width, making them illegible. Reorganize into three thematic
groups with 2-column tables so each image gets ~50% width, and put
the new Pomodoro settings shot in a centered 720px callout to
highlight the v0.5.0 mode addition.

New screenshots (manually captured against `npm run tauri dev`):
- statistics-overview.png — Statistics page top: rest rhythm card,
  Eye-Care Index ribbon (41/100 demo state), counts + 24h ribbon
- statistics-trend.png — Trend card switched to weekly view with
  ECharts bar+line composite over W21 vs W22
- settings-pomodoro.png — Settings → Pomodoro section showing focus
  duration, short/long break duration, long-break interval steppers

Both README.md (English) and .github/README.zh-CN.md (Chinese)
updated symmetrically.
Remove sections that don't serve a first-time reader of the repo:
- ProjectEye comparison table — competitor positioning belongs in
  release notes / blog posts, not the project README
- Download section — superseded by the GitHub Releases link in the
  status badge at the top, the duplicate platform table was just noise
- Roadmap — already covered by CHANGELOG.md + git tags, kept drifting
  out of sync with reality (saw it stuck at v0.3 for four releases)
- Credits — ProjectEye inspiration is honored in CHANGELOG / docs,
  no need to re-state every time someone opens the README

Replace the verbose "Build from Source" + scattered command lists
with a focused "Quick Start" — clone, install, dev/build, and one
"npm run ci" for everything else.

Add a new "Architecture" section after Tech Stack: a 3-layer ASCII
diagram (Frontend / Services / Platform) + 4 bullets explaining
service composition, platform degrade flags, ts-rs IPC bindings,
and the .trellis/spec/ canonical rules. Deliberately high-level —
detailed module map already lives in CLAUDE.md.

Drop the "added in v0.X.0" tags from the screenshot section headers
per follow-up feedback — the README documents current state, not
release timeline.

Net effect: 169 lines deleted, 79 added; README is now ~40% shorter
while covering more of what readers actually need (what it does,
how it looks, how to run it, how it's built).
- Replace the verbose "What is the 20-20-20 Rule?" explainer with a
  single-paragraph Introduction (covers 20-20-20, Pomodoro, smart
  skipping, and analytics in one breath)
- Drop the redundant "Status: v0.7.0 released" line above the rule —
  the version badge already conveys that
- Compress Features from 5 categories / 17 bullets down to a tight
  7-bullet Highlights list keyed by an emoji each: ⏱️ timer modes,
  🎯 smart skipping, 📊 statistics, 🖥️ multi-monitor, 🌓 theme+i18n,
  ⌨️ hotkeys+tray, ⚡ lightweight+tested
- Add semantic emoji anchors to every section heading (👀 ✨ 📸 🚀
  🛠️ 🏗️ ⚙️ 🤝 📄) — makes scanning the README on GitHub easier
  without leaning into cosmetic clutter

Net: 86 lines deleted, 36 added — same coverage of what readers need,
faster to scan.
…arkdown

Hoist OPTIONS const above active_display_bounds() call site (fixes
clippy::items_after_statements) and wrap CFDictionary in backticks in
the rect_from_dict doc comment (fixes clippy::doc_markdown). Both
lints only fire on the macOS CI runner since the surrounding code is
gated behind cfg(target_os = "macos").
@rsecss rsecss merged commit f62e2a1 into main May 25, 2026
6 checks passed
@rsecss rsecss deleted the chore/v0-7-x-hardening branch May 25, 2026 11:35
@rsecss rsecss mentioned this pull request May 25, 2026
4 tasks
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