Before this commit, `cargo test --doc --manifest-path src-tauri/Cargo.toml`
failed: the indented formula at return_calculator.rs:12-13 was parsed by
rustdoc as a Rust code block and the pseudo-math (`R = ... sum(CF_i)`)
did not compile. Pre-existing since commit 531624b.
Wrapping the formula in an explicit `\`\`\`text` fence tells rustdoc to
render but not compile-test the block. `cargo test --doc` now passes
(0 doctests, no failures).
Also adds the consolidated #187 entry to CHANGELOG.md and CHANGELOG.fr.md
under Fixed/Corrigé summarizing all six fixes (S1, S2, S3, S4, S5, S7) —
S6 already factorized, S8 deferred to backlog, S9 obsolete.
Suggestion S7 from worker note on #176 (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The internal Step component received `t: TFunction` as a prop while every
other component in the codebase calls useTranslation() directly at the
top of the function. Aligns with the majority pattern.
Suggestion S5 from PR #184 review (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this commit, /balance rendered the BalanceOnboardingCard plus the
period selector + evolution chart + accounts table whenever the user had
no accounts or no snapshot. The lower three components surfaced their
own empty states, producing 3 stacked "no data" messages under the
onboarding card.
Lifts the (accountsCount, hasAnySnapshot) computation out of the inline
IIFE and uses a single isEmpty branch: empty profiles see only the
BalanceOnboardingCard; populated profiles see the full overview.
No logic change — only JSX restructuring. Tests covering useBalanceOverview
and BalanceOnboardingCard remain green (61 tests passing).
Suggestion S2 from PR #184 review (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before this commit, a brand-new profile briefly showed the
StarterAccountsModal even though the 4 starter accounts were already
seeded — the modal rendered 4 collision rows with no actionable choice
before being dismissed. Pre-seeding the pref in consolidated_schema.sql
suppresses the modal on first /balance visit for new profiles entirely.
Existing profiles already running the app are unaffected: they handle
the modal once on their first /balance visit (the pref-write happens on
dismiss). No migration is needed for them.
Suggestion S1 from PR #185 review (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds defense-in-depth: each iteration runs a SELECT COUNT(*) WHERE name=?
AND balance_category_id=? AND archived_at IS NULL inside the BEGIN/COMMIT
block, immediately before its INSERT. On a hit, the iteration skips the
INSERT silently and the returned ids array excludes the skipped starter.
Rationale: balance_accounts has no UNIQUE constraint on (name, category)
and the upstream pre-filter (getStarterCollisions) is best-effort. If a
race or a bypass slips a duplicate through, the in-txn check catches it
without surfacing a confusing error to the user.
Existing two tests in StarterAccountsModal.test.tsx updated to mock the
new SELECT call sequence; new test "skips silently when in-txn collision
check finds an existing account" added.
Suggestion S3 from PR #185 review (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
getStarterCollisions now filters `archived_at IS NULL` so a starter
account the user voluntarily archived no longer blocks re-creation
through the StarterAccountsModal. Matches the rest-of-codebase
convention (active = is_active=1 AND archived_at IS NULL).
Suggestion S4 from PR #185 review (#187).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extends PR #189's fix (one input on /balance/snapshot) to the 7 remaining
native <input type="date"> fields across 4 components:
- transactions/TransactionFilterBar.tsx (dateFrom + dateTo)
- adjustments/AdjustmentForm.tsx (form.date)
- balance/LinkTransfersModal.tsx (from + to)
- dashboard/PeriodSelector.tsx (localFrom + localTo)
Each onChange handler now calls e.currentTarget.blur() after the state
update to dismiss the native date popup on Linux Tauri WebView. The call
is a no-op on Windows WebView2 / macOS WKWebView, where the picker
already auto-closes.
No automated test added: this is a WebKitGTK-specific WebView quirk that
cannot be reproduced in jsdom/vitest. Manual smoke test on Linux Tauri
dev was the validation, mirroring PR #189's approach.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
simpl-resultat n'avait jamais ete opt-in pour le pattern STATE.md
malgre son statut de produit final commercial. Cette commit pose le
seed (3 sections : Position actuelle / Decisions recentes /
Blockers actifs) et ajoute l'import @STATE.md dans CLAUDE.md pour
qu'il soit auto-charge en session.
Step 10.5 de /fix-issue prendra le relais sur les prochaines PR.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Transitive dependency via vite (range ^8.5.3 already accepts the fix).
Lockfile-only change; no package.json modification needed.
Advisory GHSA-qx2v-qp2m-jg93 is a moderate severity XSS via unescaped
</style> in the CSS stringifier output. postcss runs at build time only
and never ships in the Tauri binary, so practical exposure is nil — but
this clears the npm audit warning and the defenseur finding.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The single 12-card SettingsPage is replaced by a hub at /settings linking
to three thematic sub-pages mounted via a shared SettingsLayout (Outlet):
/settings SettingsHomePage (3 cards-cluster + PageHelp)
/settings/users UsersSettingsPage (Account, License, DocsContent)
/settings/data DataSettingsPage (Categories, DataManagement,
PriceFetchConsentToggle)
/settings/systems SystemsSettingsPage (Version, UpdateCard,
ChangelogContent, LogViewer)
DocsPage and ChangelogPage are extracted into reusable DocsContent /
ChangelogContent components and the standalone /docs and /changelog
routes become Navigate redirects to preserve external bookmarks and
release-note links. UpdateCard is extracted from the inline updater
block for symmetry and testability.
TokenStoreFallbackBanner is mounted once in SettingsLayout, surfacing
the OS-keychain-fallback warning across the four main routes only.
The two existing /settings/categories/{standard,migrate} sub-routes
stay flat (siblings of SettingsLayout) to keep their focused flows
free of the banner — their internal back-links now point to
/settings/data.
i18n FR/EN gain settings.{home, users, data, systems, backToHome};
docs/architecture.md and CHANGELOG{,.fr}.md updated. Pure refactor of
presentation: no new business logic, no Tauri commands, no SQL
migrations.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Defenseur secrets-scanner false positive: the truncated example token
in api-contract-prices.md:471 passed the entropy threshold for the
"Bearer Token" pattern. Swap for an explicit <license-token>
placeholder so the next defenseur run no longer flags it.
No user-visible behavior change — doc placeholder only, no CHANGELOG.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
WebKitGTK (Linux Tauri WebView) does not auto-dismiss the native
<input type="date"> popup after a value commit — the user has to
press Esc. Force-blur on change is a no-op on Edge Chromium WebView2
(Windows) and WKWebView (macOS), where the popup already closes.
Scope narrowed to /balance/snapshot per issue body. Six other date
inputs across the app share the same WebKitGTK bug; tracked in a
follow-up issue rather than bundled here.
Diagnostic: Ubuntu 24.04 + libwebkit2gtk-4.1-0 2.50.4-0ubuntu0.24.04.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Follow-up to PR #186 review: tauri icon CLI generates 64x64.png
but it was not declared in bundle.icon, so packagers (deb/rpm)
weren't picking it up. Add it alongside the other Linux sizes.
Part 1 — New profiles: seed 4 starter accounts in
consolidated_schema.sql (Compte chèque/CELI/REER/Compte
non-enregistré, currency CAD, is_active=1) right after the
balance_categories seeds. Categories resolved via SELECT subquery
on the seeded `key` values for robustness.
Part 2 — Existing profiles: StarterAccountsModal proposes the same
4 starters at first /balance visit. Default-checked checkboxes,
collision rule (case-insensitive trim name + matching category)
disables matches with a "Déjà présent" tooltip. The atomic helper
`proposeStarterAccounts` wraps the inserts in BEGIN/COMMIT (rolls
back on error). user_preferences.balance_starter_proposed records
{shown_at, accepted} so the modal never reappears, dismissed or
confirmed.
Part 3 — docs/adr/0012-balance-two-level-model.md (Proposed):
captures the future vehicles × compositions model for reflection,
no code change. Numbered 0012 because 0011 was already taken by
the providers-best-effort-yahoo ADR. Linked from architecture.md
ADR table and Bilan section.
Tests: StarterAccountsModal.test.tsx covers STARTER_ACCOUNTS shape,
getStarterCollisions (case-insensitive trim, category-scoped) and
proposeStarterAccounts (insert order, COMMIT, ROLLBACK on failure).
No render tests — mirrors the BalanceOnboardingCard pattern (no
jsdom configured).
Resolves#179
Replace empty BalanceOverviewCard with BalanceOnboardingCard showing
two steps:
1. Create an account
2. Enter a snapshot
Step 2 is grayed out until at least one account exists; the entire
card is replaced by BalanceOverviewCard once a snapshot is recorded.
Hide "+ New snapshot" button when 0 accounts (it lives inside the
overview card, which is now hidden in that state).
Improve SnapshotEditPage noAccounts copy to clarify account vs
snapshot semantics.
Resolves#178
useSnapshotEditor.save now validates all simple/priced lines in-memory
before any DB write, then delegates to a new saveSnapshotAtomic helper
that wraps INSERT snapshot + INSERT lines in an explicit BEGIN/COMMIT
transaction (ROLLBACK on catch). Pattern matches categorizationService.
Migration v11 cleans existing orphan snapshots in profiles that hit the
old race; new orphans are no longer possible thanks to the transaction.
Resolves#176
SQLite raised "misuse of aggregate function MIN()" because MIN was used
in the WHERE clause of a scalar subquery. Replace with ROW_NUMBER()
OVER (PARTITION BY account_id ORDER BY snapshot_date ASC) filtered on
rn = 1.
Adds vitest coverage and a regression test for /balance load.
Resolves#175
reports/ is used by /autopilot for daily reports and per-worker decision
logs (scratch). spec-decisions-*.md and spec-plan-*.md are produced by
/plan-overnight and committed only when promoted to docs/archive/.
Prevents `git add -A` in workers from sweeping these into PRs.
Phase A of #161: ships the smoke script and README before the
maximus-api prices-proxy endpoint is live in prod. The script will
fail on case 1 (HTTP 404) until /v1/prices is implemented and
deployed — that is expected; running it is gated to Phase B (after
prices-proxy ships, before cutting v0.9.0).
Script covers 4 cases:
1. Stock happy path (AAPL) — HTTP 200, .price > 0
2. Crypto happy path (BTC) — HTTP 200, .price > 0
3. Invalid symbol — HTTP 404, error.code=symbol_not_found
4. Missing auth — HTTP 401, error.code=missing_token
`set -euo pipefail`, exits non-zero on first failure. Reads token
from MAXIMUS_API_TEST_TOKEN env var (never committed). README
documents env vars and the two paths for obtaining a premium test
token (admin endpoint TODO in maximus-api, manual JWT signing as
current workaround).
CSP whitelist for https://api.lacompagniemaximus.com is already in
place in src-tauri/tauri.conf.json — verified, no change needed.
No application code touched; npm test (492) and cargo test --lib
(69) remain green.
Phase A only (#161)
The `push` + `pull_request` combo doubled CI runs on every PR (visible
on #170 with 4 pending checks instead of 2). Drop `push`, keep
`pull_request: branches: [main]`.
Trade-off: branches pushed without an open PR no longer get CI
feedback. Open a draft PR if you want CI to run before requesting
review — `/fix-issue` always opens a PR right after pushing, so the
gap is essentially zero in practice.
Also adds a concurrency group `ci-${{ github.ref }}` with
cancel-in-progress so force-pushes cancel the previous run instead
of stacking.
Same change applied to .github/workflows/check.yml (GitHub mirror)
to keep the two configs in sync.
Fixes#171
Priced balance categories now carry an explicit `asset_type`
('stock' | 'crypto') so PriceFetchControl can route to the right
provider without symbol heuristics. ETH = Ethan Allen NYSE AND
Ethereum crypto are no longer ambiguous.
Migration v10 adds a nullable column and backfills the two seeded
priced categories (key='stock','crypto'). Legacy custom priced rows
stay NULL until the user edits the category — SnapshotLineRow hides
the price-fetch button when asset_type is NULL on a priced row, so
manual entry remains available.
Service-side validation rejects priced creation without asset_type
('asset_type_required') and rejects values outside ('stock','crypto')
('asset_type_invalid'). Simple kind coerces asset_type to NULL.
The CategoryVariant of AccountForm shows the selector only when
kind=priced, requires it on submit, and resets it on kind switch.
i18n keys added under balance.category.assetType.* (FR + EN).
Tests:
- 4 new Rust migration tests in lib.rs (column add, seed backfill,
legacy row stays NULL, CHECK rejects 'gold')
- 6 new vitest cases on createBalanceCategory + listBalanceAccounts
asserts c.asset_type AS category_asset_type in the join
- balance-flow integration test updated to pass asset_type='stock'
No new test for SnapshotLineRow render guard — project lacks
@testing-library/react + jsdom; the guard is one boolean expression
covered by manual QA per autopilot decisions in PR #167.
Fixes#169
The autopilot worker prompt instructed writing decisions to
decisions-log.md at worktree root, but didn't exclude it from git
add — so each worker committed its version, causing add/add merge
conflicts between sibling PRs in the prices milestone.
Add to .gitignore so future autopilot runs leave the scratch file
local only.