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>
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.
- Adds PriceFetchConsentToggle to SettingsPage Privacy section
- Reads/writes user_preferences.price_fetching_consent for active profile
- Confirmation dialog before revoke (DELETE the key entirely so next click re-opens consent modal)
- Disabled (with notPremium tooltip) when license is not premium
- Adds deletePreference() to userPreferenceService
- Adds settings.privacy.title i18n key (FR + EN)
- 10 vitest tests covering all paths
Closes#159
- New component renders button + consent modal + spinner + attribution
- Best-effort warning shown once per session for stock categories
- Hidden if not premium or category kind != 'priced'
- Consent persisted per-profile in user_preferences.price_fetching_consent
- Manual unit_price input remains active in all paths
- 17 vitest tests (no RTL/jsdom — logged MEDIUM in decisions-log.md)
- Wired into SnapshotLineRow/SnapshotEditor/SnapshotEditPage
- asset_type hardcoded to 'stock' pending category schema extension (MEDIUM)
Closes#158
- prices.fetchPrice wraps invoke('fetch_price', ...) with local rate-limit (1/2s), in-flight dedup, exp backoff on 5xx (2/4/8s, max 3 retries), no retry on 4xx/429, hard 100/session cap
- 9 vitest tests with vi.useFakeTimers() (happy, 401/403/404, 429 no-retry, 5xx retries, dedup, pacing, session cap)
- Annexe B i18n mapping wired (PriceError → balance.priceFetching.errors.* keys)
- Session cap checked before rate-limit/dedup; failures do not consume budget (MEDIUM decision)
Closes#156
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>