create_sample_import_folders refuses with stable codes instead of English
sentences: sample_folder_exists:<name>, import_folder_missing (no longer
carrying the folder's path) and invalid_date. The wizard maps them to
import.empty.sampleErrors.* through the pure sampleFolderError, the folder
name interpolated from the new errorValues of the state; any other message
stays raw. The raw refusal also goes to the Journal as a warning: a code, or
a write error naming a sample folder and the OS error, never a path.
scanFolderInternal now tells whether the scan went through, and the success
banner only follows a rescan that did: a failed rescan leaves its error
alone on screen.
ImportEmptyState keeps folderName alone (null without a folder); the page
passes "" for a folder stored as "", as hasFolder did.
Rust tests: the three codes asserted exactly, the case of the second sample
folder already there (nothing written), and {{M1}} pinned as the most
recent month line by line against the templates.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-review of PR #473: the header still said every React rendering of the
file was bypassed, while its "displayed texts" tests render the control with
its hooks from the shared harness. architecture.md: the slot presets are
commented in each test file (the two direct writes of the onboarding wiring
test are covered by the comment of mountWith).
Resolves#437
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Where a test presets a harness slot by position (PriceFetchConsentToggle,
useOnboardingChecklist's mountWith, SystemsSettingsPage), a comment names
the dependency on the component's hook order: any hook added before shifts
the slots without a word (the `slot` helper of fakeReact).
- docs/architecture.md, Tests: "Limites connues" lists the slot presets, and
links to ADR 0021 for withoutFolders and for the catch-up measurement
instead of repeating them.
- ADR 0021 section 7: the relative-path limit is stated precisely (a run with
two separators or more is still cut to its last segment; only the words of
a folder holding a blank stay), and a second limit is written: a folder
name holding one of the stops (a backtick, `;`, `|`, a closing parenthesis
not followed by a separator...) ends the path there, and that name, or its
start, stays in the line.
- db.ts: the comment of withoutFolders says "a closing parenthesis not
followed by a separator", as the code does, and points to ADR 0021.
No behaviour change.
Resolves#437
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- db.test.ts: `\r` joins the path stops of withoutFolders (removing it from
PATH_STOP now fails).
- The six keys whose "t removed" mutation survived the review of PR #435
(comment 23499) are asserted in their marked form:
transactions.transferIcon.ariaLabel (a row linked to a transfer) and
transactions.notes.placeholder (title of every row's notes button) in
TransactionTable.render.test.tsx; license.keyPlaceholder and
license.activate in LicenseKeyForm.render.test.tsx;
dashboard.kpiSectionTitle in DashboardPage.render.test.tsx;
balance.priceFetching.consent.accept in PriceFetchControl.test.tsx, the
consent dialog opened by presetting the control's first harness slot
(the dialog's title, body and decline texts are pinned with it).
- TransactionTable.wiring.test.ts opens a row's notes through its button
and pins the textarea placeholder, the second call of the notes key.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A deactivated child of the reattached category is not counted in the
number of categories put back, which the Journal line reports.
Resolves#393
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The architecture section said the module had three consumers; the walks
do, and the budget reads displaysAsRoot as well.
Resolves#393
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The reattachment is committed before the orphan list is checked again: if
that check failed, the tree kept showing the categories as they were. The
tree now reloads in a finally block, and the failure shows in the window.
Resolves#393
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG (Added: the "Check hierarchy" window; Fixed: the parent checks
at save time and the budget rows of a category whose parent is deleted or
gone), the Categories section of the user guide and its docs.* keys in
both languages, and docs/architecture.md: the categoryHierarchy module, the
diagnostic, the validation and its typed error, the window, the budget
root rule, and what stays out of scope (drag-and-drop, archive restore).
Resolves#393
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The budget grid and the Budget vs Actual report read active categories
only and started from the categories without a parent, so a category whose
parent was deactivated or no longer exists vanished from both, budget
amounts included. It now stands at the top level, as in the tree and the
category pickers (displaysAsRoot).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
updateCategory checks a new parent before writing: the category itself,
one of its descendants or a category caught in a cycle (cycle), a fourth
level counting its sub-tree (too_deep), a parent holding transactions of
its own (parent_has_transactions). The refusal is a CategoryHierarchyError
that the Categories page shows by the translation of its code. The reads
and the writes, the new parent's is_inputable included, run in one
transaction through its handle; an unchanged parent is not checked, so a
category stored before these rules can still be renamed.
getCategoryDepth keeps the ids it has seen and ends on a cycle.
A "Check hierarchy" button, between "All keywords" and "Re-initialize",
opens a window that lists the orphan categories (caught in a parent loop,
parent deleted, parent gone), one per group, with a new parent for each:
the root by default, or a parent of the category form's list.
reattachCategory applies the same rules; after it, the list and the tree
reload and the Journal gets one line with the reason and the number of
categories put back, no name.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
src/utils/categoryHierarchy.ts holds the depth, height and descent walks
that buildParentOptions had inline, each with a set of the categories it
has seen, so a parent_id cycle ends the walk. A category caught in a cycle,
or under one, now has no depth instead of a made-up one, and the parent
list of the category form never offers it: a child placed there would be
hidden with it. The module also carries the orphan diagnostic, the parent
change refusal and the root rule that the service, the Categories page and
the budget use next.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-review: the Portails paragraph listed StarterAccountsModal next to
LinkTransfersModal as if both had a test of this kind; only
LinkTransfersModal has one. The other windows are named as windows the
same rule applies to.
Resolves#429
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-review: the CHANGELOG entries said the list opens upward when the
room below is short; it does only when it also fits above (in a short
window it stays below). The Portails paragraph of architecture.md now
quotes the two errors a test meets without the mock, both reproduced
on a throwaway test: "Target container is not a DOM element" without
a DOM, "Portals are not currently supported by the server renderer"
with a DOM-like container.
Resolves#429
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHANGELOG (EN and FR, Changed): typing in the category of an adjustment
line and in the category filter of the "Link transfers" window, the
adjustment lines limited to the categories open to input, and the open
list no longer cut by the edge of a window or of the Transactions table.
Guide, section 7, and its in-app twin (docs.adjustments.steps, FR and
EN): type a few letters to filter the category list of a line.
architecture.md, Tests section: how a test renders a window drawn by
createPortal (identity mock, document stub), what of the combobox's
portal is pinned without a DOM (placeListbox.ts) and what is checked in
the app, and that the list no longer sits in its container's DOM.
Resolves#429
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The category filter of the "Link transfers" window was a native select.
It now takes the compact CategoryCombobox, modelled on the Transactions
filter bar: "Any category" first as an extra option, active while the
filter is empty, and picking it sets the filter back to null; the list
of transactions reloads as before.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each line of the adjustment form chose its category in a native select
showing raw names. It now takes the compact CategoryCombobox of the
split window: typing filters, hierarchical order, seed names
translated. A new line still takes the first category of the list.
D2: the hook reads the Transactions list, getAllCategories (active and
open to input), instead of its own query on every active category. The
list feeds two pickers only, the form and the split window of the
Adjustments page; the detail view names a line's category from the
entries' join, so a line saved on a category hidden from input keeps
its name there and its value, its field empty until changed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The open list of CategoryCombobox was positioned `absolute` inside its
input's container, so a scrolling ancestor clipped it: the body of the
split window, the `overflow-x-auto` wrapper of the Transactions table.
It is now rendered in a portal on document.body, `position: fixed`, at
z-[300] (above every window, the highest at z-[210]).
The pure rule lives in placeListbox.ts: under the input, as wide as it,
4 px below; above it when the room below is short of the list's 240 px
and the room above has them; below when neither side has them. The
list is placed in a layout effect before the first paint, then follows
the input on every scroll (capture phase, any scrolling container) and
resize while it is open, skipping scrolls of the list itself; the
listeners are removed with the list (followAnchor returns the cleanup).
The outside press counts the list as inside, since it no longer sits in
the container's DOM. The closed markup is unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-review of PR #470: the System card of the Settings home lists the
page's sections as chips, still with the version history before the logs.
The chips now follow the page (version, update, logs, history), and a test
renders the page and compares its section headings with the card, so the
two cannot drift apart again. SECTIONS is exported for that test, as
DocsContent exports its own. The /settings/systems row of
docs/architecture.md names both unreleased headings and the card.
Resolves#372
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
User guide (section 11) and the in-app docs.settings keys, FR and EN: the
order of Settings → System and how the version history folds. One Changed
entry in both changelogs. docs/architecture.md: the /settings/systems row,
why ChangelogContent keeps a local Set rather than useCollapsibleGroups,
and the utils count (19: startupConnection and rootFiles were not counted).
CLAUDE.md no longer describes /changelog as a page with the full history:
it redirects to Settings → System since #190.
Resolves#372
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Settings → System now orders its sections Version, Update, Logs, version
history, data notice: the Journal is reachable without scrolling past the
whole changelog, which grows at each release.
The version history opens on the installed version's notes, every other
version folded behind its title (a button with aria-expanded, a chevron and
aria-controls), with an Expand all / Collapse all button. An empty
Unreleased heading is hidden; one with content (development build) stays.
State is a local Set seeded by a pure rule, in memory only.
parseChangelog moves unchanged to src/utils/changelog.ts with the rule:
installed version unfolded, or the first published version when the
installed one is unknown or absent; a published version is told from the
Unreleased / Non publié heading by its shape, so both languages read alike.
SystemsSettingsPage mounts ChangelogContent once getVersion() settles; a
failure is written to the Journal and leaves the version unknown.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Release skill, step 2: quote the refusal cargo actually prints, the
same under cargo 1.94.1 and 1.99.1 (`cannot update the lock file ...
because --locked was passed to prevent this`, code 101).
- docs/architecture.md: the relay of --locked is proven for both runners
on a lockfile out of step with Cargo.toml: cargo-xwin 0.23.1 runs
`cargo build ... --locked ...`, and the other cargo calls of the Tauri
CLI and cargo-xwin are `cargo metadata --no-deps`, which never touch
the lockfile.
Resolves#449
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Release skill, step 2: right after the hand edit of Cargo.lock and
before the windows-sys guard, `cargo metadata --format-version 1
--locked` must exit 0, since the tag builds with -- --locked and the
release commit goes to main without a pull request. Step 0 runs cargo
check and cargo test with --locked, like CI, so the pre-flight cannot
fold a lockfile rewrite into the release commit.
- ADR 0023: the windows-sys guard reads the committed lockfile, cargo
check having already refused one it would have had to rewrite.
- CLAUDE.md, REVIEW.md and docs/architecture.md: CI commands with
--locked, release builds on the committed lockfile, cargo-xwin pinned
(REVIEW.md said it was installed unpinned), the stale REVIEW.md
sentence about #449 removed, and why the dormant .github mirror is
not aligned.
Resolves#449
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
check-rust.yml ran cargo check and cargo test without --locked: a
Cargo.lock out of step with Cargo.toml was rewritten in the job's
checkout, and the suppression guard, the windows-sys guard and cargo
audit then read the rewritten lockfile instead of the committed one.
Both now pass --locked, and the windows-sys guard comment says the
lockfile it reads is the committed one.
release.yml built the shipped binaries the same way. Both npx tauri
build calls now end with `-- --locked`, which the Tauri CLI hands to its
runner (cargo, or cargo-xwin for Windows, which passes it to cargo
build).
cargo-xwin, the runner of the Windows build, runs with the updater
signing key in its environment and was installed at whatever version
crates.io served on the day of the tag. It is pinned at 0.23.1, the
version the v0.17.2 release installed (run 570), and its version is
printed in the job log.
Refs #449
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The "Faux t" review pitfall said that until #453 no guard caught a key
present in one language only. It now names src/i18n/locales.keys.test.ts
and what the guard does not see: a value left in the other language, and a
key the code calls that neither file declares. The first rule of the "i18n
et documentation" zone names the guard too, as the migration and release
zones name theirs. The "Niveaux" section is untouched
(scripts/review-levels.py --check still passes).
Resolves#453
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
No test compared the key trees of the two locale files: a key added to one
language only passed the build and the suite, then showed raw to an English
user (missing from en.json) or in English to a French user (missing from
fr.json, English being the fallback).
src/i18n/locales.keys.test.ts imports both files statically, flattens every
node to a dotted path, array indices included (docs.dashboard.features.6),
and fails with the keys missing from each language in a test of its own,
plus a third test for a path held with another kind in each file (an array
against an object, a leaf against an object or an array). Values are never
compared. A canary checks both files were read whole, array items included,
and four tests on small fixtures pin the rules of the comparison.
Run on the base (6e8e157): 1712 leaves on each side, no gap, so no
translation to add.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Max ran the real update cycle v0.17.1 -> v0.17.2 on Linux (.deb):
it worked, and a password prompt appeared (its type was not noted).
The trace is in the v0.17.2 release body. #421 was checked on Linux
and closed by hand (comment 25074). Windows and .rpm cycles remain.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The #458 pilot table (comment 24819) now has four rows: #459, #450,
#464 and #465, the last two judged right at the high level by Max.
#464 and #465 were counted second and third here without #450.
Entries merged since v0.17.1 now say they shipped in v0.17.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of PR #465 (comment 24953), suggestion 1: an alias
(`const c = confirmAction; if (!c(...))`) called confirmAction out of reach
of the "awaited" rule, and window["confirm"](...), confirm?.(...) and
`const { confirm } = window` escaped the native-call rule. Any mention of
confirmAction other than the awaited call, its declaration, a plain named
import or `confirm: confirmAction` is now refused; the three native forms
are flagged. Samples added for each.
Refs #421
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On desktop, tauri-plugin-dialog 2.6.0 replaces window.confirm with an async
function: confirm(...) returned a Promise, always truthy, so every guard
`if (!confirm(...)) return;` let the action run before the user answered.
Deleting a secondary profile removed its database even on "Cancel".
- confirmAction() (src/services/confirmService.ts) calls the plugin's ask()
with a warning icon and buttons labelled in the app language
(common.confirm / common.cancel); a failed call resolves false and is
logged (fail closed). @tauri-apps/plugin-dialog 2.6.0 added, the crate's
version.
- The six sites await it: profile, category, adjustment, budget template and
Balance category deletions, and both confirmations of the license key
removal (performRemoveLicense takes an async confirm).
- native-dialog-guard.test.ts fails on any native confirm/alert in src/ and
on any confirmation not awaited (TypeScript does not flag `!promise`).
- Tests: confirmService, the deletion of a profile in ProfileFormModal, and
the license removal tests with an async confirm (test: reproduces #421:
six of them were red on the old code).
Refs #421
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The product page of la-compagnie-maximus copies the edition matrix into a
comparison table (la-compagnie-maximus#180). Step 0 of the release skill now
says to prepare the site PR before the tag and deploy it only after the
release is published.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow-ups of the review of PR #418.
- check-latest-json.jq wants exactly one document and release.yml calls it
with -s: under jq 1.6, the release container's, an empty file passed the
check (no output, status 0) and two documents were judged on the last one,
so the job would have deleted the published manifest and uploaded the bad
one. Two tests cover both cases.
- build-latest-json.test.mjs runs the text of release.yml's "Publish
latest.json to package registry" step against a fake curl: DELETE then PUT
on a valid manifest, exit 1 and no request on a refused one.
- check-frontend no longer ignores .claude/** and release.yml (decision of
Max): the test reads verify-release.sh and runs the Publish step, so "on
every pull request" holds. REVIEW.md, CLAUDE.md and architecture.md follow.
- release.yml: stale ".deb fallback" comment fixed.
- qa-update-cycle.md: a tauri dev build on Linux shows TargetsNotFound,
expected since #418.
Refs #461Resolves#419
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clears RUSTSEC-2026-0367, RUSTSEC-2026-0368 and RUSTSEC-2026-0369 on
zip 4.6.1, which turned the daily audit.yml red at run 558 (2026-10-17)
without any lockfile change. zip was compiled only for Windows, through
the default `zip` feature of tauri-plugin-updater 2.10.0. It is fixed
only in zip >= 9.0.0, and tauri-plugin-updater 2.14.0 still requires
zip ^4, so no cargo update can reach a fix.
That feature only unpacks archived update packages (.nsis.zip,
.msi.zip, .AppImage.tar.gz, produced by createUpdaterArtifacts
"v1Compatible"); this app ships createUpdaterArtifacts: true, whose
packages are never archives. The updater is now declared without its
default features, keeping rustls-tls. The lockfile loses zip, arbitrary
and derive_arbitrary (666 -> 663 packages, nothing added), and tar
leaves the Linux graph.
tar was the canary of the suppression guard in check-rust.yml. It is
replaced by webkit2gtk, Tauri's Linux webview, which keeps the property
that made tar useful: a dependency present on x86_64-unknown-linux-gnu
only, so finding it also proves that --target resolves conditional
edges. ADR 0018, architecture.md, qa-update-cycle.md and the release
skill are updated to match.
Verified locally: cargo-audit 0.22.2 exits 1 on the old lock with the
three IDs and 0 on the new one (663 crates, the same 12 allowed
warnings); the suppression guard is green, and red with the canary
swapped for tar or zip; windows-sys guard 9/9; cargo check and cargo
test (267 passed) on Linux; cargo xwin check for x86_64-pc-windows-msvc
is green on main (zip compiled) and on this branch (zip gone).
Refs #455
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
REVIEW.md, from the suggestions of the first review of #460:
- stacked-PR pitfall: fs_commands.rs compiles two CSV files of
src/__fixtures__/samples/ through include_str!, outside the paths of
check-rust;
- high level: .claude/settings.json and .mcp.json, a pull_request_target
trigger, an install step that changes the toolchain or the PATH (the
check-workflow list is now given as examples of the principle), a lock
origin whose host or scheme changes even with a new version, and a diff
that removes or bypasses a consent (price consent lives on the front);
- db.ts and dataExportService.ts stay normal (Max's decision of
2026-10-18), and the data zone now names the lock, the profile switch
and the restore;
- release zone: cargo-xwin and the Rust toolchain, installed unpinned at
each tag, also run with the signing key;
- windows-sys guard: EXPECTED joins CRATES and PIN;
- identifier: changing it orphans the databases and the app_data_dir
files (license key, activation token, token fallback); the keychain
service is a constant and does not follow.
scripts/review-levels.py is the calibration script of the PR body, now in
the repo: pages of 50 files read until an empty page (the instance caps
pages at 50), the new rules above, comment lines ignored by the workflow
checks, and --check, which compares its patterns with the Haut table
without network access. Fresh run: 3/28/8 on the 39 reference PRs and
6/33/12 on the 51, unchanged.
Refs #456
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
REVIEW.md sets the review level of a PR from the files its diff touches
(high, normal or low; the highest wins, never the author), lists what
matters in each zone with its source (a CLAUDE.md section or an ADR,
completed by the repo file where the fact lives), the verification
commands of the CI, and the review pitfalls of this repo. The pitfalls
taken from the global pr-review skill are corrected for this repo:
stacked PRs do get CI here since #232.
Calibration on the merged PRs #344 to #452: the 39 reference PRs of the
pilot document come out 3 low / 28 normal / 8 high (20.5 %, target
20 %), the whole range of 51 PRs 6 / 33 / 12 (23.5 %), against 3 / 19 /
17 with the trial patterns. A lockfile-only version bump of a package
already present stays normal, per Max's decision of 2026-10-17.
CLAUDE.md points to the file.
Fixes#456
Note: plan check bypassed after 3 passes, last findings applied (Max's
decision of 2026-10-18).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of PR #452 (comment 24129): "adjustments" was written three
times for the split (the page's gate, the button's edition, the
dialog's locked screen). SPLIT_FEATURE in splitGate.ts is now the one
source, so moving the split to its own key or to Premium is a one-line
change. No behaviour change; setting it to "budget" turns 2 tests red.
Refs #369
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review of PR #452 (comment 24129): the upgrade to Base checked that the
upsell goes away, not that no split window appears in its place, so a
locked click that also armed the split window (R14) left the suite
green. Both edition changes now assert the other window stays closed,
during the license refresh and after it.
R14 now turns 1 test red, and its mirror (a split click that also arms
the upsell) 1 test red.
Refs #369
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Répartition (split) button of the Transactions page now belongs to
the `adjustments` feature, as the unlock screen already promised
(upsell.features.adjustments). On the Free edition the button keeps its
place and its icon, carries a lock and opens the locked screen in a
dialog instead of the split window, for every row, split or not. While
the license is not ready it shows no lock and does nothing. Existing
splits stay in the data, counted by the reports and flagged by the
orange icon.
- splitGate.ts: pure splitAccessFor(gate) -> open | locked | pending,
stricter than profileGate: `allowed` does not short-circuit `ready`.
- SplitButton: stateless button that decides what a click opens.
- UpsellDialog: UpsellGate in a named modal dialog, closed by its X and
by "I already have a key".
- TransactionTable: required splitAccess prop. The split window is not
rendered once the edition resolves to Free, and stays mounted through
a license refresh; the upsell is not rendered once it resolves to Base.
- TransactionsPage reads useEntitlement("adjustments").
- ADR 0017 amendment and matrix, user guide, in-app docs (FR, EN),
architecture, entitlements header comment, both changelogs.
Tests: the predicate, the button's markup and click, the dialog, the
table's wiring through the shared hook harness (#409), the page under
the three gate states, and the new key in both locales.
Fixes#369
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clears RUSTSEC-2026-0346, RUSTSEC-2026-0347, RUSTSEC-2026-0348 and
RUSTSEC-2026-0349, published on 2026-10-14 against rustls 0.23.45. The
daily audit.yml has been red since run 553 (2026-10-15 06:00 UTC) with
the lockfile untouched since v0.17.1: the advisories alone turned it red.
rustls is reached only through tauri-plugin-updater (reqwest 0.13) on
both shipped targets, and none of the four applies to that use: no
custom ClientSessionStore (0346), ECH never enabled (0347), no `brotli`
feature (0348), and the ring provider offers no post-quantum group to
downgrade (0349). Rule 1 of .cargo/audit.toml still asks for a fix
rather than a suppression: the crate is in the graph and a fix exists.
`cargo update -p rustls --precise 0.23.46` changes two lines of
Cargo.lock (version and checksum). No companion bump: 0.23.46 declares
the same dependencies and MSRV. No windows-sys edge moves. The checksum
matches the crates.io index and the downloaded .crate.
The changelog entry scopes "no behaviour change" to the app's own code,
as 9abf474 did for the previous rustls bump: besides the four fixes,
0.23.46 carries about ten other TLS-layer corrections.
Verified locally, cargo-audit 0.22.2 (the CI binary) run from the repo
root: exit 1 with the four advisories before the bump, exit 0 after,
666 crates and the same 12 allowed warnings on both runs. cargo check,
both check-rust guards (2 and 9 checks, exit 0) and cargo test (267
passed, 0 failed) are green.
Refs #447
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Guide and in-app docs name the five card pages, "Got it" on the two
visit cards and the Trends card that also checks the reports step;
architecture lists the new models and targets; the QA list gets the
steps 5 and 8 of the guided walk, the Free padlock variant and the
Trends click.
Resolves#432
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two visit steps get their card: /categories explains keywords and
points at "All keywords" (never "Reset"), /reports explains the hub and
points at the Trends card, with a padlock variant in the Free edition.
"Got it" acknowledges the step; opening the highlighted Trends card does
too (D17), since following the ring leaves the page.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>