Skip to content

Project review: security hardening, bug fixes, UI and translation fixes - #2

Closed
wbgcoding wants to merge 5 commits into
mainfrom
claude/wizardly-hamilton-f7mdhu
Closed

wbgcoding wants to merge 5 commits into
mainfrom
claude/wizardly-hamilton-f7mdhu

Conversation

@wbgcoding

@wbgcoding wbgcoding commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

What changes

Fixes from a full project review covering security and privacy, code quality and performance, UI and localization. No feature was removed or redesigned.

Security and privacy

  • No secrets in the tree or the git history. The only tokens are test fakes; the update key is public.
  • Runtime setup script: before starting the .NET installer with admin rights, it checks the download folder holds only that installer, so a DLL planted beside it is never loaded. It also refuses a temp folder that is a link.
  • The elevated uninstall no longer removes app files from a moved data folder under ProgramData.
  • A data folder the app creates when moving data is now private: current user, SYSTEM and Administrators only.
  • Logs and crash text also strip the 8.3 short form of the profile path.
  • Signer certificates are disposed after the chain is read.
  • .gitignore now also covers binlogs, key stores, IDE state and the app's own backups.

Bugs and crashes

  • Codex: a huge window_minutes value threw and broke every local read. Window lengths are now bounded.
  • Codex: session-file reset times are read in seconds or milliseconds, like the web reader.
  • Claude: the token sum and the waiting mark followed a running subagent's file instead of the main session.
  • Claude and Codex: a fetch that timed out could run alongside its retry and corrupt shared state. Reads now honour cancellation and run one at a time.
  • Antigravity: a revoked stored token showed "not signed in" until it expired. It is now refreshed once first.
  • Claude discovery reports rate limiting as blocked.
  • Installer: FindWindowExA never matched the app window, so a silent upgrade never relaunched the app. It now uses FindWindowExW.
  • Installer: after an elevated in-app update the app came back with admin rights. It is now relaunched through the shell.
  • Uninstall now also removes Task Manager's StartupApproved marker.
  • Tray: an icon the shell refused at sign-in is retried.
  • Status file: writes are serialised, so an older document can no longer land after the final one.
  • Plan names: fused tokens like max5x are recognised.
  • History: the main, settings and stats windows now share one history store. Before, deleted history could be written back and a data-folder move left the stats window reading the old folder.
  • History: setting the clock back no longer stops history writes.
  • Stats: a damaged stats.db is set aside and rebuilt from the session logs instead of failing forever.
  • Stats: negative token counts are ignored.
  • Stats: records without a project and the pooled "other" models are grouped by a neutral key, not their translated label. A project folder named like the label (e.g. "Ohne Projekt") or a model called "Other" no longer merges into those groups or toggles the wrong filter.
  • Esc inside the custom-range popup no longer hides the whole stats window.
  • A second sign-out while one is running no longer reports a false failure.
  • The main widget no longer appears behind the open Welcome dialog.
  • Settings rows are moved, not rebuilt, when tiles are reordered.

Performance

  • The first stats index walk no longer scans the whole table once per new file.
  • Importing another PC's history unpacks the backup's index once instead of twice.
  • ~/.claude.json is cached by file stamp instead of parsed on every refresh.
  • The gh output regex is generated instead of rebuilt on every call.
  • Bursts of Windows colour notices apply the theme once.
  • A resize drag measures the tallest content once and coalesces layout passes.

UI and localization

  • The limit-reached notification no longer reads "resets in resets in".
  • Day and month names follow the app language, while number and time formats keep the Windows regional format.
  • Hotkey names are localized (Strg/Umschalt).
  • WebView2 failure details are translated.
  • New singular forms for two texts that read "1 days".
  • EN percent spacing is consistent; German abbreviations are Mio./Mrd.
  • German ellipsis style and the Konto/Zugang terms are consistent; several wording fixes in both languages.
  • Installer messages use the formal "Sie", like Inno's own German wizard text.
  • The update notice wraps instead of being cut off in German.
  • Filter chips trim long text and show it as a tooltip; padding and icon dimming are consistent; sign-in buttons have accessible names.
  • Stale or misplaced comments corrected.

How it was tested

  • dotnet build tests/AiUsage.Tests -c Release: 0 warnings, 0 errors, with warnings as errors and analyzers on.

  • Windows CI on the current head: all tests pass, and CodeQL is clean.

  • WPF-free tests were also run on Linux against the built app assembly. The JS discovery scripts were run in Node against stubbed routes.

  • New tests cover:

    • Providers and installer: Claude discovery rate-limit, max5x plans, the short profile path, the ProgramData exclusion.
    • Statistics and history: a negative token count, a corrupt index, the clock set back, project and model names that match the pooled labels.
    • UI and text: Esc in a popup, row reorder, limit-reached wording, hotkey display and display culture.
  • No screenshots: the visible changes are small (wrapping, trimming, padding).

  • dotnet test tests/AiUsage.Tests passes (CI)

  • New user-facing text is in both Strings.resx and Strings.de.resx

  • No call to a chat or completion endpoint, no write to another tool's files

Left open on purpose

  • German duration units ("2d 3h"): changing them is a style choice, so it waits for a decision.
  • High-contrast colours for charts.
  • The whole index is reloaded after each walk.
  • The hover dot sits slightly off the averaged line on long ranges.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NFFCwGNZt9EaKWfAt9JXjp

Providers
- Codex: bound window lengths to a plausible range so an absurd
  window_minutes can no longer throw and break every local read; read
  reset instants in seconds or milliseconds in both parsers.
- Claude: the session token sum and waiting mark follow the main
  transcript, never a running subagent's file.
- Claude/Codex: local reads honour cancellation and are serialised, so a
  timed-out read still running cannot corrupt the retry's state.
- Antigravity: a revoked stored token is refreshed once before the tile
  reports "not signed in".
- Claude discovery reports a rate-limited site as blocked.
- Cache the parsed ~/.claude.json label; generated regex for gh output;
  share the empty snapshot helper; correct misplaced doc comments.

Installer and security
- Use FindWindowExW: the A form never matched, so a silent upgrade never
  relaunched the app.
- Relaunch after an elevated in-app update through the shell, not with
  the admin token.
- Remove Task Manager's StartupApproved marker on uninstall.
- Refuse ProgramData as a moved data folder during elevated cleanup.
- Runtime setup checks its download folder holds only the installer
  before starting it elevated.
- Sanitize the 8.3 short profile path in logs; dispose signer
  certificates; extend .gitignore (binlogs, key stores, IDE state,
  backups).

Services
- Retry a tray icon the shell refused at sign-in.
- Serialise status file writes so an older document never lands last.
- Debounce theme re-application on bursts of colour notices.
- Recognise fused plan tokens such as max5x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFFCwGNZt9EaKWfAt9JXjp
Statistics and storage
- One HistoryStore is shared by the main, settings and statistics view
  models, so deleted history cannot be written back by another instance
  and a data-folder move redirects every reader.
- The first index walk uses the loaded source-file list instead of a
  full-table query per new file.
- A damaged stats.db is set aside and rebuilt from the session logs.
- Negative token counts are ignored; a clock set back no longer stops
  history writes; leftover cache temp files are swept; the project
  colour cache is written atomically; the indexer's previous
  cancellation source is disposed.
- A moved data folder created by the app is private to the user.

UI
- Esc inside an open popup closes the popup, not the statistics window.
- The update notice wraps instead of clipping in German.
- Resizing measures the tallest content once per drag and coalesces
  density passes.
- A second sign-out while one runs no longer reports a false failure.
- The widget stays hidden while the welcome dialog is open.
- Settings rows are moved, not rebuilt, when tiles are reordered.
- Thread-safe access to user-started fetches; frozen caption pen;
  trimmed filter chips; consistent padding and dimming; shared
  label/value format; accessible sign-in button names.
- Localization work in progress: hotkey names, culture-aware day and
  month names, notification and WebView2 texts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFFCwGNZt9EaKWfAt9JXjp
The custom setup messages now match Inno's own German wizard text
("Sie"), the update folder page no longer repeats the Next/Browse
instruction shown beside it, and the source link says "Fehlerberichte".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFFCwGNZt9EaKWfAt9JXjp
- Update the tests that pinned "fast aufgebraucht" and the sign-in
  show sequence, and keep the sanitizer's doc comment free of a path
  the ship-clean scan rejects.
- Release the held status file from its own thread so a busy test run
  cannot starve it past the writer's retries.
- Move doc comments back onto the members they describe and dispose
  the Windows identity used for the private data folder.
- Read each session file once per walk even when configured roots
  overlap, now that new files no longer ask the index first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NFFCwGNZt9EaKWfAt9JXjp
Importing another PC's history unpacked the token index twice, once for
the schema check and once more for the import; the checked copy is now
the import source.

Records without a project and models past the top ones were grouped
and told apart by their translated labels, so a project folder named
"Ohne Projekt" or a model called "Other" merged into those groups or
toggled the wrong filter. Grouping now uses a key no folder can carry,
and the pooled rows and slices are marked as such; the labels shown are
unchanged.

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

Copy link
Copy Markdown
Owner Author

Merged into main as 760d50b (squashed), with follow-up fixes on top.

@wbgcoding wbgcoding closed this Oct 17, 2026
@wbgcoding
wbgcoding deleted the claude/wizardly-hamilton-f7mdhu branch October 23, 2026 05:16
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.

2 participants