Skip to content

v2.0.0

Latest

Choose a tag to compare

@github-actions github-actions released this 15 Sep 04:59
· 1 commit to main since this release
Immutable release. Only release title and notes can be modified.
v2.0.0
ba6262a

Breaking changes

  • (pots) Category-linked pots are retired, and their balances are released on upgrade (#399) (breaking) (f8ccaf6)

Category-linked savings pots are retired, and money they held
is now unallocated.

Envelope budgeting replaces them. The first time v2.0 opens, every pot that
was linked to a budget category is archived and its balance released back to
the unallocated pool of the account it sat on. Your account balance has not
changed and nothing is lost -- the money is simply no longer set aside, and
nothing you did caused it.

Putting it back is yours to do. Open Budgets and assign it to the envelopes
you want it in. The app does not do this for you, on purpose: a pot balance is
an amount accumulated over time and an envelope assignment belongs to one
specific month, so guessing the month would build you a budget you did not
author and cannot audit.

Goal-linked pots, and pots with no link at all, are untouched and keep working
exactly as before.

Nothing is deleted. The archived pots stay on the Pots screen under "Archived
pots", with their names and their whole history, including the release itself.
Restoring one brings the pot back empty and without its category link, which
is the one shape a pot can no longer be made in: from here a pot links to a
goal, or to nothing.

Features

  • GREEN — CalendarQuery service wired into CalendarPage (b575bc4)
  • CalendarPage month-nav, accounts popover, persistence (022d540)
  • Full calendar UI — grid, toolbar, day panel, CSS layer (ade2513)
  • Sidebar + palette test — CalendarPaletteAndSidebarTest (63e415b)
  • Tax module scaffold, provider, routes, migrations, service stubs (a9371dc)
  • DTOs, TransactionTagged event, TagTransaction + UntagTransaction actions (d89562a)
  • Implement TaxYearQuery — grouped per-year read with COALESCE override + totals (2fa8d24)
  • Implement TaxTagQuery + NavCountsService tax_tagged key (70c7e46)
  • Tax corpus YAML (6 countries) + TaxCorpusLoader (4e0dd6b)
  • TaxCategoryWriter — provenance-safe seed + CRUD actions (f0d9ab5)
  • TaxSettingsSection Livewire component + settings-page integration (87206f7)
  • Implement TaxCsvExporter (D-15 audit-extra CSV) (e00d5a9)
  • Implement TaxPdfRenderer + CSS-2.1 table PDF template (D-14) (55b6a25)
  • TaxPage cockpit — year switcher, grouped sections, exports, empty states + CSS (708914b)
  • Dashboard tax card, sidebar entry, ⌘K palette, real cross-user probe (8d771b7)
  • Shared tax-badge + picker + HandlesTaxTagging trait (796c517)
  • Integrate tax badge on all four surfaces + TaxBadgeSurfacesTest (9060fe3)
  • Expose Tax public country-setup surface for cross-module wizard use (cef4960)
  • Add skippable tax-country step to the setup wizard (177b502)
  • Scaffold Search module manifest, service provider, and boundary arch rule (b6bce16)
  • FTS5 migration, public DTOs and contracts for Search module (5f374c1)
  • Implement SearchIndexWriter and IndexTransactionOnImport listener (caa604b)
  • Tax note re-index hook + pint fix on SearchIndexWriter (7b9f906)
  • Add reindex command, FTS health check, and doctor wiring (abf6baa)
  • Implement QueryParser and DidYouMeanSuggester (2a24a10)
  • Implement SearchQuery::search() — FTS MATCH + filters + highlight + summary + cursor (74aeb62)
  • Implement SearchQuery::palette() + EntityNameSearch + SearchResultsProviderImpl (ff16c18)
  • Add URL-bound search/filter to TransactionsList + search UI (7d28808)
  • PaletteSearchEndpoint component, view, route, CommandPaletteModal injection (b93d664)
  • Palette.js server fetch + merge, palette blade sections, token autocomplete, CSS (fec71ab)
  • Sidebar search affordance + mobile magnifier (D-25, D-29, UI-SPEC #8/#9) (fa8c7a9)
  • Scaffold Anomaly module + four migrations (76ad968)
  • Anomaly models + sole-mutator state machine + factories (24efee6)
  • Robust statistics + large-vs-typical detector (4a91255)
  • First-time + duplicate detectors + recurring membership (e06badb)
  • AnomalyEvaluator orchestration + suppression + dispatch (04162f8)
  • AnomalyAlertQuery + DTO/mapper + suppression-rule query (7468b48)
  • Acknowledge/snooze/dismiss Public Actions + cross-user 404 + lifecycle events (31830eb)
  • Dismiss-as-expected (±15% suppression) + remove/undo suppression rules (3bef60c)
  • Reactive DetectAnomaliesJob + TransactionImported listener (6df1ceb)
  • Chunked idempotent BackfillAnomaliesJob with anomaly_backfilled_at guard (3e3c84e)
  • Hourly anomaly snooze-revival + safety-net sweep + scheduler wiring (7bed409)
  • /drift type switch + anomaly alert row consuming AnomalyAlertQuery (0a13153)
  • Dashboard anomaly tile + amber sidebar nav badge (D-03) (96ea638)
  • Settings 'Anomaly detection' section + first-activation backfill (D-11/D-18) (bf2db7e)
  • Scaffold Modules/Sync spike module and register in Pest harness (e8d9aa7)
  • Define Internal contracts — HLC, OpType, OpLogEntry, DeviceKeySigner, OpLogReplayer skeleton (38f833e)
  • Complete OpLogReplayer LWW merge algorithm — D-05..D-08 GREEN (8a6ac1b)
  • Add TriggerAwareRebuildTest + signature gate (3 probes + forged-entry proof) (d331be4)
  • Add CrossUserScopeTest — security proof for T-10-02 (elevation of privilege) (8b685bc)
  • Create three Sync persistence migrations (72f1eca)
  • Add transactions.note column with mandatory trigger re-install (7e065e9)
  • Implement MergeRulesRegistry + 4 CRDT merge strategies (8c5192f)
  • Implement OpLogWriter — persist signed entries + HLC persistence (def18cb)
  • Productionize OpLogReplayer + wire SyncServiceProvider as single owner (acdb0c2)
  • Add FTS5 freshness hook (SearchIndexWriterContract) to OpLogReplayer (ffa33a3)
  • OpLogRebuilder — trigger-safe deterministic full-rebuild from op-log (58425af)
  • TransactionMutated event + SyncCaptureListener + capture at category_id + type edit paths (b679bf0)
  • Note editor + counterparty reassignment + delete on TransactionDetail (1a5aa1a)
  • SyncHealthPage Livewire + view + route + user-scope test (D-07) (8d6bf59)
  • Add device_registry and pairing_tokens migrations (265e3f8)
  • Bundle BIP39 wordlist + immutable DeviceIdentityDto (5a4a6b0)
  • Device identity service + loader + name detector (cef4388)
  • Safety-number deriver + DeviceRegistryService facade (bfb4b28)
  • Wire identity + registry services into SyncServiceProvider (a7400a4)
  • Single-use pairing token service + state machine (568061e)
  • Word-code encoder + QR payload builder (bf5ee79)
  • Complete both-confirm trust transition in confirm() (9545c7f)
  • Devices & Sync settings section + settings-page mount (cf0b5e0)
  • PairingFlowModal bidirectional flow + both-screen safety-number gate (d6e7000)
  • Install amphp stack + deviceX25519Keys() + sync transport migrations (eeb2516)
  • Pre-wire SyncServiceProvider with Phase 13 class_exists guards (8178a2c)
  • NoiseCipherState + NoiseSymmetricState (AEAD + HKDF over BLAKE2b) (dcf4280)
  • NoiseHandshakeState (IK+XX) + NoiseSession + TransportFramer; Wave-2 tests GREEN (8590c9f)
  • Implement RelayMailbox — ZK ciphertext store/drain/confirm (cc564c5)
  • Implement RelayConfig + RelayClient; turn Wave 4 guard GREEN (18fc1b3)
  • SyncSession — Noise auth gate + additive Ed25519 op verify (9e4da2b)
  • Add PeerCatchUpExchanger, SyncWebSocketHandler; turn Wave-3 tests GREEN (064c0dd)
  • Add mDNS discovery (MdnsAdvertiser, MdnsBrowser, DiscoveredPeer) (1cca0c0)
  • SyncServeCommand + RelayServeCommand + provider registration + config/sync.php (205f699)
  • NativePHP ChildProcess host + launchd plist for self-hosters (D-03/D-04) (56d0f90)
  • SyncStatusService + SyncStatusSection + status-section blade + test (bbcd395)
  • Relay-URL setting + sync-status-section wired into Devices & Sync (d3efab9)
  • SaveTransactionSplit action — sum invariant, type gate, PK-preserving edit, unsplit (8201c8e)
  • Add SpendByCategoryQuery shared read model over legs ∪ unsplit-parents (414e89a)
  • Redirect BudgetProgressQuery/TopCategoriesByPeriodQuery/CategorySpendTrendQuery to SpendByCategoryQuery (ff37e4c)
  • Wire transaction_splits op-log capture (Task 1) (3d09b8f)
  • Leg-aware TaxYearQuery amount resolution + TaxTagQuery leg batch-load (fa89caf)
  • TransactionDetail split editor actions + Livewire feature test (1f7db96)
  • Split section Blade markup + new app.css component classes (4a3d6d6)
  • Split-row expand/collapse on the transactions list (96f2cdb)
  • Add the three envelope Public sync-event contracts (e24ca18)
  • Envelope tables + genesis anchor migrations (Pitfall 4 nullability fork) (04ba90d)
  • EnvelopeAssignment/EnvelopeSetting models + EnvelopeRow/EnvelopeMoveRow DTOs (1b2e7e6)
  • BudgetsServiceProvider single-owns envelope bindings (class_exists-guarded) (ecc4786)
  • Extract incomeForPeriod() as the one canonical income sum (d8c45c5)
  • CarryoverQuery genesis-to-target fold (Req 2/4/7/8/9) (0291e46)
  • EnvelopeBalanceQuery net_moved + recent-moves loader (a4e09c1)
  • Add envelope_assignments/moves/settings MergeRulesRegistry entries (ac8e88f)
  • Add SyncCaptureListener handlers for the three envelope tables (886eea9)
  • Wire envelope events + register envelope_moves in UserIdColumnArchTest (d08421c)
  • EnvelopeActivationService idempotent per-user cutover (4332686)
  • One-shot cutover migration invoking EnvelopeActivationService (249c9ca)
  • EnvelopeWriter setAssigned/setOverspendMode/copyFromPeriod (Req 1/4/6) (40e1608)
  • EnvelopeWriter move/undoMove -- paired-row ledger, no balance guard (Req 5/8) (0ddf693)
  • Rebuild /budgets as the zero-based envelope grid (Task 1) (7357392)
  • Move-money modal + per-envelope recent-moves undo (Task 2) (877e7a7)
  • EnvelopeGlanceCard dashboard glance card (Req 12) (3899fa8)
  • Repoint onboarding BudgetsStep onto EnvelopeWriter (Req 12) (3dd1090)
  • Activate + seed envelope budgets in demo:seed (94b63bb)
  • Add Transaction::STATUSES allow-list + D-04 re-import regression test (7fef025)
  • Source-format-aware import default + clearedBalance query (484fbb4)
  • Register status as an LWW-synced field (D-10) (7b874a8)
  • HandlesClearedStatus trait + TransactionDetail::toggleCleared() (7657a9e)
  • Wire cleared badge into transactions list + detail page (b1121d5)
  • ReconciliationWriter::completeReconcile() + unreconcile() (33d0eff)
  • Enforce reconciled lock on TransactionDetail mutators (9d3ce4c)
  • Enforce reconciled lock on SaveTransactionSplit::save/unsplit (958ac0e)
  • ReconcilePage — account picker, target pre-fill, difference, confirm (80f6bdb)
  • Wire /reconcile into the sidebar (calm-slate, no new CSS needed) (ee86ca7)
  • Redesign categorization_rules + add rule_conditions/rule_actions/field_provenance (6227ad9)
  • Forward-backfill legacy rules + RuleCondition/RuleAction models (2698d94)
  • Update MergeRulesRegistry + seeder for the new rules schema; add RuleSchemaMigrationTest (7bbc014)
  • Add RuleEngine multi-condition matching core (78ecdfb)
  • Nested rule DTOs + CategorizationRuleQuery join/mapping (85ad2ba)
  • Multi-condition/multi-action rule authoring with op x value_type matrix + payload ownership validation (d704f75)
  • Referential-integrity listener deactivates rules on category/counterparty delete (00c13c7)
  • Add shared FieldProvenanceWriter Ledger Public service (80a51eb)
  • Promote counterparty-reassign/note writes to Ledger Public actions (7818b2c)
  • Stamp field_provenance='manual' from all four manual write paths (97d235f)
  • RuleApplier import-mode action folding (D-06) (497e070)
  • RuleApplier re-apply mode delegating to Ledger guarded actions (0d167dd)
  • Rewire import categorisation to RuleEngine + RuleApplier (212d29a)
  • Add ReapplyRulesJob — chunked, reconciled+split-guarded re-apply (e7e6119)
  • RuleFormModal multi-condition/multi-action builder (eebec49)
  • RulesPage priority-ordered table + re-apply-to-history trigger (4134cad)
  • Scaffold Migration module + test reachability (2fa4cfa)
  • Add migration_runs + staging + source_map + baseline schema (0c718b1)
  • Register Migration sync entries + registry-columns contract tests (f42f9ca)
  • Migration IR DTOs + parser contract (Task 1) (2cb155c)
  • Shared YNAB parser Support helpers (Task 1) (075d329)
  • Ynab4Parser + NynabParser turn the Plan 02 RED stubs GREEN (Task 2) (6726135)
  • ActualSqliteReader read-only Pdo\Sqlite wrapper over the extracted Actual export (7eeade6)
  • ActualParser + ActualGoalDefInterpreter turn the RED ActualParserTest GREEN (18696c7)
  • StagingWriter lands parsed batch into migration_staging_* in bounded chunks (3b69764)
  • StartMigrationRun action + reject-not-partial cleanup (f5453bd)
  • PreviewSummaryBuilder read model for the wizard preview step (dfa3838)
  • SourceMapWriter + goals staging + migration_runs summary counts (222a6c2)
  • PromoteStagingToDomain — fan-out staged IR to the six public writers (95ab5db)
  • ConfirmMigration + DiscardMigrationRun actions (c352b51)
  • ThreeWayMergeResolver — source/baseline/current merge comparator (47cad0e)
  • CheckForUpdates reconciliation entry point (Req 10 GREEN) (7095161)
  • Migration wizard routes + index/upload components (c796b1d)
  • Preview + results wizard components (stat grid, conflicts, confirm/discard) (60284f6)
  • Scaffold Reports nwidart module + phpunit testsuites + boundary rule (94a02b3)
  • Saved_reports migration + SavedReport model + MergeRulesRegistry entry (7f1e2f6)
  • SavedReportMutated event + SyncCaptureListener::handleSavedReport + provider wiring (bd4b8a6)
  • Implement counterparty filter dimension in Search (42af99a)
  • Wire counterparty URL filter into TransactionsList (fbdfb82)
  • Add Reports public DTOs (ReportDefinition/ReportResultRow/ReportResultDto) (12fc433)
  • PeriodPresetResolver + TimeBucketGenerator (Req 6/7) (893d89e)
  • CategorySpendQuery — canonical type-based, split-leg aware (Req 2/3) (d60b766)
  • CounterpartySpendQuery + AccountSpendQuery + TimeBucketSpendQuery (Req 2/3/6/7) (6e04861)
  • Implement NetWorthSeriesQuery sampled base-currency series (4b48fb8)
  • ReportAggregator dispatch + CurrencyModeApplier (metrics, dimensions, currency, FX exclusion) (9854b61)
  • PeriodComparison — current vs previous-period deltas (Req 13) (ffb1f64)
  • Saved-report write actions (save/update/delete/pin) (d990ffa)
  • Pin cap + ReportCsvExporter + reports.export route (a429347)
  • DrilldownUrlBuilder — single tested Period→transactions.index URL helper (1480002)
  • Live /reports builder — control rail, result panel, bar/line/donut charts, drill-down (499cfa5)
  • Saved-report library index (SavedReportsQuery + ReportsIndex Cards|List CRUD) (758a43c)
  • Reports sidebar nav item + cross-user isolation guard coverage (4ae8e61)
  • Dashboard pinned-reports mini-card row (61839a2)
  • Wire the pinned-reports row into the dashboard additively (c14befa)
  • Add gdk_epoch op-log column + device-local sync_encryption_state table (f03f126)
  • Add GdkEpoch DTO + SensitiveFieldRegistry (pure crypto foundation) (9eae3e2)
  • OpLogFieldCrypto — per-field AEAD with epoch-bound associated data (4c9a63b)
  • GdkKeyring + GdkKeyringService — append-only GDK epoch keyring (c6756d1)
  • SensitiveColumnCodec — rotation-safe projection-column codec (1fb8b4f)
  • OpLogEntry.gdkEpoch + OpLogWriter encrypt-on-write hook (c7eed50)
  • OpLogReplayer decrypt-before-strategy hook + gdk_decrypt_failed quarantine (35f6eab)
  • Encrypt RecordTransactions direct-write import path (CRYPT-01) (758ae92)
  • Encrypt Counterparties creation write + decrypt read sites (CRYPT-01) (f3313a1)
  • Decrypt Ledger transaction detail/list read sites (CRYPT-01) (a4b27bd)
  • SearchIndexWriter decrypts before tokenizing (CRYPT-01 / BLOCKER-2) (55b82ef)
  • Decrypt Tax cockpit/export read sites (CRYPT-01) (5574fa4)
  • Decrypt SearchQuery row-level counterparty_name (CRYPT-01) (df91a27)
  • D-10 GDK rewrap contract/service + event + Sync listener (43eeeda)
  • Wire AppLockProvisioner.changePin to D-10 GDK re-wrap (bb6ecee)
  • GdkRotationService.rotateAndRevoke + sealed-box wrap-per-device (0ecf01c)
  • EncryptionMigrationService — D-09 backup-first atomic migration (997ce8c)
  • Add GdkEpochControlHandler — validate, open, append inbound GDK_EPOCH_WRAP (bf174d7)
  • Wire GDK_EPOCH_WRAP delivery into the live Noise catch-up exchange (6f9abff)
  • Enable-encryption flow (D-07 mandatory) + device remove/revoke UI (143a7c7)
  • Encrypt tax_transaction_tags.note in TagTransaction (CR-01) (8bf6679)
  • Encrypt transaction_splits.note in SaveTransactionSplit (CR-02) (faeac20)
  • Encrypt transactions.note in SetTransactionNote + decrypt-before-compare (D-07 gap) (e0457e8)
  • Backfill note-table sweep (D-08) + docblock correction (5091fd1)
  • Flip request-context dispatch to dispatchSync for CRYPT-01 (e8bdecc)
  • Decrypt-then-substring SearchQuery LIKE fallback (Task 1) (6ef0dbb)
  • Decrypt-then-match EntityNameSearch + DidYouMeanSuggester (Task 2) (328f2b3)
  • Decrypt-for-display in RuleFormModal + CategorizationRuleQuery (8d7302f)
  • Decrypt-for-display in ReportBuilder counterparty filter (40b8203)
  • Decrypt-for-display in Chains + triage D-06 fold-in sites (aec58f7)
  • Decrypt-then-json_decode raw_payload Eloquent cast (D-04/D-05) (9d87aac)
  • Decrypt raw_payload + decrypt-then-match iban in PaypalFundingResolver (CR-05) (2bc61ff)
  • TransferPairer decrypt-then-match counterparty_iban (CR-03/D-02) (66e1bf1)
  • IcsSettlementResolver + RetypeByAliasResolver decrypt-then-match counterparty_iban (CR-03/D-06) (fbe62be)
  • Decrypt counterparty_name/description before ReapplyRulesJob rule matching (CR-04) (7f47a53)
  • Decrypt counterparty_iban before income-series clustering (c5ab929)
  • Skip iban-dependent income detection with a logged warning when KEK-less (0daeabd)
  • Encrypt display_name/merchant_name before save() in CounterpartyTriage (CRYPT-01, Task 1) (ea2b19d)
  • Decrypt candidate description before MerchantNameResolver::resolve() in CounterpartyTriageQueue (CRYPT-01, Task 2) (fc66d05)
  • KEK-aware, encryption-safe CounterpartyGarbageCollectorJob orphan prune (0feddc3)
  • Decrypt-for-display in PreviewSummaryBuilder::transactionLabel() (2b4bb27)
  • Scaffold Mobile module with single-owner provider/routes/phpstan wiring (462c028)
  • Add mobile_sync_progress durable-cursor table + green wiring test (8b31e1a)
  • Add throwaway mobile topology spike probe commands (e845529)
  • Scaffold mobile-app sibling root sharing Modules/ (Option 1) (fd0d34c)
  • UserDataPathService NATIVEPHP_PLATFORM accessor + mobile-branch regression coverage (b1f6442)
  • Reconcile live sqlite path to the single authority + on-device migrate-on-launch (cc63548)
  • NetworkPolicyResolver — D-09/D-10 mobile network policy (f969e39)
  • LanSyncClient — outbound amphp/websocket-client LAN dial-out (c7ec2c7)
  • MobileSyncTriggerService — KEK-gated sync burst orchestrator (345a134)
  • InitialSyncPuller — idempotent resumable full-history pull (c125bb9)
  • SetupProgressScreen — blocking setup screen resuming at true percent (fe30f76)
  • SyncScreen — full-page /sync status surface composing sync-status-section + Sync-now + cellular toggle (ebc05f7)
  • Drawer entry for /sync (D-05) (b33fdb4)
  • Install premium NativePHP mobile plugins into mobile-app/ (28736d3)
  • BiometricUnlockBridge — bool-only native bridge (mobile-biometrics) (d727601)
  • MobileLockScreen — biometric-primary unlock reusing the Auth chain (5b31172)
  • QrScanBridge — hand decoded QR token into the existing pairing validation boundary (e46e1f3)
  • MobilePairingScan — camera-first pairing entry with word-code fallback (a477045)
  • Sync:mobile-pull bounded background pull command (D-07) (eab2642)
  • Add mobile-app/-rooted CI job; fix Desktop-route 500 under the mobile shell (ffebc8f)
  • Mobile first-run gate + welcome screen (create-account / import CTAs) (239d3a1)
  • Add fresh-device import bootstrap Public seams (Task 1) (b65e367)
  • Desktop epoch fan-out on pair-ADD (Task 3 service half, B1) (9e9bd6f)
  • Cross-device pairing token seeding + mutual admission (Task 2, G1/G2) (ef28a2e)
  • Desktop pairing UI fans out epochs on both-confirm (Task 3 UI half) (a096f76)
  • Phone-side epoch receive + history re-projection (Task 4, G3/O1) (28fe34c)
  • MobilePairingScan import mode — self-mint deferral (Task 2 UI, B2) (a60880a)
  • Fresh-device import onboarding screen (Task 5) (aed4cf5)
  • (15-crossdevice-pairing) QR carries the relay endpoint for cross-device bootstrap (C1) (6b299bd)
  • (15-crossdevice-pairing) Cross-device apply methods close the both-confirm gap (HIGH-01 trust core) (9dbc6fa)
  • (15-crossdevice-pairing) PairingRelayCourier carries pairing frames over the ZK relay (HIGH-01 transport) (24cebd7)
  • (15-crossdevice-pairing) Wire the desktop pairing surface to the relay courier (HIGH-01 C4) (1039d7e)
  • (15-crossdevice-pairing) Wire the phone pairing surface to the relay courier (HIGH-01 C5) — full happy path to epoch delivery (d59756d)
  • Wire real iOS/Android release signing + close bundled-.env keystore leak (fc1cd3e)
  • (desktop) Real Developer ID signing + notarization for the macOS build (ef3ab50)
  • Scaffold Modules/Notifications and register its test harness (b97c256)
  • Create notifications table with deterministic string PK + state CHECK triggers (8dc1ef9)
  • DeterministicKeyDeriver + NotificationStateMachine with unit tests (0dd4fa5)
  • Add per-budget threshold_percent + expose on Public seam (436ecd3)
  • Redirect D-20 notify threshold to the live envelope model (ba43efa)
  • Expose localDeviceId/otherDeviceNames on Sync's Public seam (9253cad)
  • Notification_preferences table, model, DTO and Public query (234df89)
  • SuppressionEvaluator — the single delivery-suppression site (d797e8f)
  • Register notifications tables + D-20 envelope threshold sync (e25fb7c)
  • NotificationMutated event + SyncCaptureListener handlers (3c385a1)
  • NotificationCopy — single copy authority for 8 titles + type chips (2a89ef7)
  • NotificationWriter + lifecycle actions + 8 guarded listener slots (68fbc2c)
  • NotificationQuery inbox read model with compound cursor pagination (447c68d)
  • PaymentReminderDue/PaymentSettled events + EmitPaymentRemindersJob (3a470f1)
  • PersistPaymentReminder + ResolveSettledReminder listeners (b24aae2)
  • Add BudgetThresholdCrossed event + EmitBudgetNudgesJob (d3a7f18)
  • Add PersistBudgetNudge listener + Req 6 feature test (a1f8fca)
  • Add SavingsPromptDue event + EmitSavingsPromptsJob (Req 7) (e16cbf1)
  • Scaffold Modules/Position with composed PositionQuery + DTO (9feff1f)
  • PositionDigestDue event + cadence-aware EmitPositionDigestJob (f854fa5)
  • Add batch-altitude TransactionBatchImported event (Req 10 / D-22) (c987105)
  • Persist ONE coalesced import notification per batch (Req 10) (1af8359)
  • Persist drift-alert + forecast-shortfall reactive notifications (5c36605)
  • Collapse DispatchOsNotification onto the single NotificationDeliverable event (100707a)
  • /notifications route, NotificationsPage, and DeepLinkResolver (d43533b)
  • Notifications-page/row Blade, .type-chip CSS, sidebar nav item (544b8c4)
  • Notifications settings section anchored next to Anomaly (D-36) (475d8d7)
  • Wire five notifications schedule entries in routes/console.php (427f59a)
  • Add PruneNotificationsJob — 365-day, KEK-less retention sweep (be11f2a)
  • DemoNotificationsSeeder — all 8 types, real events, delivery suppressed (7550ff7)
  • Swap dashboard position composition onto PositionQuery (5467d94)
  • Install nativephp/mobile-local-notifications v0.0.2 (2079766)
  • Mobile delivery adapter mirroring Desktop's OS notification listener (c2aca6d)
  • Scaffold OpenBanking module, close registration landmines (bb06e45)
  • Add RemoteSourceAdapter contract, credential/window DTOs, consent-failed event (edd9eaa)
  • Add RunsImports::runFromRemoteFetch() with deterministic ImportRun idempotency (20c2895)
  • OpenBankingSecretsRepository — atomic chmod-600 SecretShield-wrapped credential store (0550944)
  • EnableBankingJwtSigner + EnableBankingAccessScope (523a090)
  • EnableBankingHttpClient with dynamic SSRF allow-list (581bb92)
  • Promote LoopbackRedirectUri to Public + OB metadata migration + state repo (4d3c8b6)
  • OpenBanking consent/SCA loopback dance (connect + callback) (751dd64)
  • OpenBanking onboarding wizard (local keypair + institution pick) (8c70dd4)
  • Reconsent-alert listener + wizard/listener provider wiring (e9c6fe7)
  • Typed Enable Banking transaction response DTO (59c1e10)
  • Enable Banking source adapter with CAMT-parity field mapping (6727b67)
  • OpenBankingFetchService + SyncOpenBankingAccountJob (two-timestamp accounting) (b5e4717)
  • OB settings page shell, toggle + B2 loud-warning gate (Task 1) (9a927ac)
  • Transparency panel + shared disconnect action (Task 2) (962c7d4)
  • Consent-expiry banner + re-link flow (Task 3) (23acd09)
  • Open-banking.daily-sync scheduler entry (D-13/Req 9) (094cb49)
  • Manual "Sync now" action + result states (Surface B6) (820d6ee)
  • Guided ICS file-import affordance (Surface B7, Req 13) (3d2b47d)
  • Metadata-only ICS statement-ready detector + tunable pattern + scheduler (cfcd7b7)
  • Notifications nudge listener + 9th trigger constant + guarded registration (6e0af28)
  • HTTPS-loopback TLS listener for Enable Banking consent dance (bd87413)
  • (auth) Cold-start biometric unlock + SecretShield at-rest keychain (e1460fa)
  • Sweep Modules/Forecasting M1-M3+M5 comment shape/token conformance (d572e14)
  • (demo) Seed the surfaces demo:seed left empty (e2105b6)
  • (notifications) Give the digest cadence a type and a constrained column (#71) (f4f3aa2)
  • (recurring) Give the series cadence a type and a constrained column (#72) (15d8116)
  • Multi-language UI support (English + Dutch) (#204) (50b3b3d)
  • (i18n) Localise the operator and cadence enum labels (#214) (f83a70c)
  • (i18n) Localise the Reports and Recurring page titles (#213) (1eb8452)
  • (i18n) Render persisted notification copy in the recipient's locale (#216) (1fb6a2b)
  • (i18n) Render dates and counts in the active locale (#217) (1ee0745)
  • (mobile) Native chrome, the fixes that made it usable, and the dependency majors (#223) (900968a)
  • Twenty-six languages, a corpus you can act on, and a mobile app that survives its own updates (#226) (4a05620)
  • (goals) A goal counts the transactions you attributed to it, not every credit on an account (#239) (72bb11d)
  • (i18n) Laravel's own messages ship in every locale the product does (#254) (4a49115)
  • (core) Your country is a preference like your language, not a tax setting (b1f4311)
  • (sync) The column we matched merchants on was the plaintext we had encrypted (33d0618)
  • (core) A shipped deployment shape the address gate refused, and the record that opens it (#362) (63b8a1b)
  • (sync) An op a new phone can never verify, and the device that can vouch for it (#363) (3c0f716)
  • (mobile) A permission prompt the reader can read (#398) (b5b9e3b)
  • (open-banking) A store that could hold one bank and one person, keyed to both (#403) (3b52585)
  • (core) The zone the machine is in, not the one the packager was in (#431) (f0820a5)
  • (shell) Sample data on a surface a store build carries (#432) (faa6385)
  • (core) A channel a reader can choose, and a preview manifest to choose it for (#438) (732b8d6)
  • (core) A feed a prerelease can actually live on (#443) (0b339b9)
  • (sync) Three facts the sync layer had and never used (#447) (94d425b)
  • (sync,mobile) The typed code had one road to the peer, and iOS never has it (#449) (32e467e)
  • (release,mobile) The shape a store takes, and reading the artifact instead of the list (#450) (f81433f)
  • (desktop) Read a bundle for what the Mac App Store refuses, and measure what the interpreter needs (#453) (de929b3)
  • (release) The App Store lane gets its own target, beside the Developer ID one (#454) (8e6d7a8)
  • (release) A store lane that can be run, and refuses to produce what nobody can submit (#455) (93edfe8)
  • (doctor) Say out loud when no machine could name the zone (#472) (1026f6f)
  • Eleven answers a reader could not reach, across every screen (#504) (9eda816)
  • (search) Find a transaction by the note the reader wrote on it (#512) (1be69bf)
  • (ledger) The doctor says whether a digest still describes its row (#571) (240407f)
  • (ledger) The doctor sees a split its transaction outgrew (#577) (cdb8d0e)
  • (core) A probe that asks whether any row names another reader's row (#595) (d8ae471)
  • (import) A receipt total a cent from the statement is the same transaction (#623) (6c0a690)
  • A converted figure names the rate that made it (#701) (c345e43)
  • (import,ledger) A statement that did not add up now says so (#710) (9b14592)
  • The two figures the rate could not reach (#708) (a581a11)
  • (import) A row that restates one already stored (#711) (fb671f8)
  • (ledger) A reader outside the euro can choose their own reporting currency (#709) (bcb5ca8)
  • (import) A restatement the bank booked on another day (#717) (5dca705)
  • (core) An older backup is brought forward, not refused (#719) (7fd5e91)
  • (tooling) One command that makes a worktree able to run the suite (#729) (0609603)
  • (tooling) A rule for the update that moves a column the digest is over (#856) (8f25f2d)
  • (release) Launch the macOS and Windows bundles too (#888) (2e867ae)
  • (release) Say which build produced each published artefact (#891) (2212fad)

Fixes

  • Cover calendar.index in cross-user guard + refresh sidebar snapshot (83d53e6)
  • Resolve duplicate Pest TestCase binding for Calendar tests (fd4ee91)
  • CR-01 materialize account-popover defaults into explicit id lists (c2a4cb4)
  • CR-02 FX-convert per-currency forecast buckets in the balance line (057d34f)
  • WR-01 batch counterparty/account metadata resolution in buildEntryMap (ec48089)
  • WR-02 clamp the paid-match window to half the cadence interval (81c95e6)
  • WR-03 bound backward projection at the series' inception (9cf1258)
  • WR-04 step occurrences by index from the anchor, not chained (cf1c24b)
  • WR-05 enforce the 12-month ceiling at render time for tampered URLs (6bd81bd)
  • WR-06 validate selectDay with checkdate before Carbon parse (86197e5)
  • WR-07 sanitize client-controlled pref arrays before persisting (8b96508)
  • WR-08 honest start-of-day balance — null sentinel instead of fake 0 (1a4a224)
  • WR-09 implement past-day actual balances (D-07 contract) (46ea8b0)
  • WR-10 announce negative projected balances in the cell aria-label (5918cf5)
  • WR-11 route every 'now' in CalendarPage through the Clock contract (cadc58e)
  • IN-03 update horizon docblocks to reference HORIZON_DAYS (8dd05d0)
  • IN-04 assert the clamped month label in the URL-clamp tests (7c8e905)
  • IN-05 exclude lead-in/lead-out grid days from the risk summary (453e5a1)
  • IN-06 document whole-euro cell rounding as a density trade-off (fc2ef8a)
  • IN-07 remove the unreachable .cal-entry--paid .cal-day-num rule (f470218)
  • IN-08 only persist prefs on Escape when the popover is open (77f429e)
  • Resolve post-merge wave 2 defects — counterparties.display_name + RefreshDatabase on unit suite + single-arg COALESCE (dd4598f)
  • Release drawer-container inline 100dvh on desktop so the sticky sidebar tracks page scroll (13b8b3a)
  • Desktop sidebar joins the page scroll — no separate nav scrollbar (4397407)
  • Batch Tag-all inherits the trigger tag's category + note (D-03) (66f1012)
  • (07-REV-CR-01) Replace bogus @return blade directive with @elseif so first-visit /tax masks the cockpit body (8e77cf9)
  • (07-REV-CR-02) Populate pickerBookedYear so the D-10 year-override picker row is reachable (2e81a7a)
  • (07-REV-CR-03) Preserve tax state on accumulated phone rows + make bare re-tag non-destructive (419063b)
  • (07-REV-CR-04) Make the untagged Tag button reveal on all four desktop surfaces (adf014c)
  • (07-REV-WR-01) Skip corpus entries whose name collides with a user-created category (de87ecc)
  • (07-REV-WR-02) Regression test — created_at survives re-tag (write path split in CR-03 commit) (c497469)
  • (07-REV-WR-03) Use array_key_exists for batch-suggestion snapshot keys (2c8f9ac)
  • (07-REV-WR-04) Reset picker note/category/year-override when the picker opens for a new row (24791f2)
  • (07-REV-WR-05) Key the batch suggestion to the trigger transaction's booked year (f9d66e0)
  • (07-REV-WR-06) Invalidate the nav-counts cache on tag/untag via an InvalidateNavCounts listener (093ca67)
  • (07-REV-WR-07) Make the Public TaxCategoryWriter a real delegating facade; trait no longer leaks the Internal type (0f9c4e1)
  • (07-REV-WR-08) Mitigate spreadsheet formula injection in the tax CSV export (215f6de)
  • (07-REV-WR-09) Freeze the Clock in the batch-suggestion tests (1e4ff58)
  • (07-REV-WR-10) Remove double-escaping of the category name in the PDF export (696c844)
  • (07-REV-WR-11) Wire a rename affordance, guard rename errors, and make archiving reversible (3e6e14e)
  • (07-REV-IN-01) Drop the copy-pasted amber @Class from the booked-year button (d79f33b)
  • (07-REV-IN-02) Remove dead Alpine x-data and inert Livewire-v2 .defer modifiers in the popover body (11d980b)
  • (07-REV-IN-03) Bring all corpus short_name values within the documented 12-char badge budget (cc10f86)
  • (07-REV-IN-04) Seed sort_order continues after the user's existing categories (6ab2ff6)
  • (07-REV-IN-05) Render all available tax years in the year switcher (0b8dce1)
  • (07-REV-IN-06) Route TagTransaction time through the injected Clock; tolerate the upsert race (540ceb9)
  • (07-REV-IN-07) Align empty-year copy with the actual badge — it reads "Tag", not ⌾ (f51fb54)
  • (07-REV-IN-08) Dashboard summary total shows deductions only, matching the /tax headline KPI (6abd986)
  • Add missing Modules/Search/tests/Unit directory referenced by phpunit.xml (d190403)
  • Resolve post-merge conflicts from wave 2 (2a26e65)
  • Resolve UAT findings — XSS in highlights, palette 500, missing category chip (34e744c)
  • CR-02/CR-03 verify actor + atomic FTS upsert in SearchIndexWriter (c04620d)
  • Correctness fixes in SearchQuery read path (WR-01/02/03/04/07/08, IN-02, CR-01) (8b70961)
  • WR-05 search:reindex fails loudly on a partial run (f6099bf)
  • WR-06 deterministic did-you-mean tie-break by corpus frequency (1b3a55d)
  • IN-03 correct stale phpstan-type aliases in SearchResultsProvider (248223e)
  • IN-03 promote AnomalyAlertQuery page-size 26 to a named constant (9520c7d)
  • WR-05 compute madFloorFor in float, cast int at the denominator (c0b8aca)
  • WR-06 compare snooze idempotency via toDateTimeString round-trip (458a5dc)
  • WR-03 chunk snooze-revival candidate scan with lazyById(500) (b30cc85)
  • WR-01 claim anomaly_backfilled_at before the history walk (cb9d511)
  • WR-02 make duplicate-charge window backward-only (d370973)
  • WR-04 make the large p95 boundary tie-inclusive and pin it (87bf704)
  • CR-01 fall back to the charge settled amount for non-large dismissals (b68dbf4)
  • CR-02 don't suppress first-time's synthetic large with a merchant band (8e3c41a)
  • WR-07 make suppression-rule insert idempotent on the natural key (f7a7c38)
  • Type SyncServiceProvider closure param as Application (PHPStan L10) (b45c1a9)
  • Add cross-user isolation probe for dev.sync-health route (0aa467b)
  • CR-01/WR-01 composite-key hlc_clock_state + real monotonic-guard proof (7c59684)
  • CR-02/WR-02/IN-04 encode non-scalar merge results before column write (dfe1f18)
  • CR-03/CR-04/IN-01 durable cascade op + correct, tested rebuild (04636a9)
  • WR-08 persist only verified entries to the authoritative op-log (a64645e)
  • WR-03 apply the 7-day window to the sync-health table query (887a50c)
  • WR-04/WR-07 quarantine index DESC + transactions schema-integrity test (ab9f99e)
  • WR-09/IN-02/IN-03 memoize merge rules, inline indirection, container Clock (c31fc80)
  • WR-05/WR-06 document timestamp-format and emit-after-commit invariants (63f55f2)
  • Surface Sync Health in Dev Console nav + command palette (e94d973)
  • Open Devices & Sync pairing modal + live-refresh app-lock gate (c1e06d6)
  • (core) Stop Alpine init crash from raw if in bottom-sheet x-on (6d8f90d)
  • (desktop) Bounce set-up devices off /welcome to the dashboard (6b8eedd)
  • (core) Restore dashboard top-section spacing lost in 04-06 (6c148af)
  • CR-01 bind pairing confirm side to caller device identity (3615d8c)
  • CR-02 never shorten pairing TTL on early accept (b6fb1e2)
  • WR-01 validate pairing public-key hex at the trust boundary (b9496d7)
  • WR-02 wire pairing-confirmed listener to refresh device list (af14cfd)
  • WR-03 stop cancelPairing expiring a confirmed token on success (48d621c)
  • WR-04 prune stale pairing_tokens + expire abandoned codes (34743ad)
  • WR-05 guard admit against self-row device_id collision (9dec89a)
  • WR-06 stop leaking the OS hostname as the default device name (6e529f0)
  • IN-02 reject wrong-length word-codes with a clear error (8594872)
  • IN-03 document QR raw-echo safety invariant (4f33d52)
  • IN-01 document the UTC-lexical expires_at comparison invariant (888918c)
  • IN-04 make pairing confirm() the single source of truth (3fbff33)
  • CR-01/CR-02/CR-03 align RelayClient↔RelayServeCommand wire contract (65f78ac)
  • CR-04 bind relay drain/confirm auth to the requested mailbox (745672a)
  • CR-05/CR-06 + WR-06 bound catch-up frames and add receive timeouts (6ee85a3)
  • WR-01 gate ephemeral injection; WR-02 audit dropped sync entries (5cf2c07)
  • WR-03 enforce Zulu timestamp form at RelayMailbox write sites (8756908)
  • WR-04 read deviceKeys once as a documented connect-time snapshot (a2a864d)
  • WR-05 fail loudly when the relay token chmod 600 fails (585df55)
  • WR-06 clamp catch-up watermark fields to non-negative (50e6c64)
  • WR-07 give the live-sync replayer the merge registry + FTS writer (7c09091)
  • WR-08 initialize and annotate parse_str target in handleDrain (8c8af5e)
  • WR-09 drop unresolved mDNS peers; document intentional ws:// (7bf159e)
  • IN-01..IN-05 docblock + robustness cleanups (7217ca8)
  • Clean up relay token/config files in RelayRoundTripTest teardown (a4caa3c)
  • Dirty-field diff on split leg edit + concurrent-edit convergence proof (Task 3) (dcae68f)
  • IN-04 extract SPLITTABLE_TYPES into shared Ledger Public source of truth (b76ff13)
  • WR-03 fail safe to parent category when legs do not sum to parent (fa6d21c)
  • IN-01 correct tax_transaction_tags merge rules and add leg scope (360462e)
  • WR-04 + IN-02 canonicalise note to null; datetime strings in op-log (aa3a07c)
  • CR-01 + WR-01 + WR-02 coordinate reclassify/delete/back-out with split legs (24c3bc4)
  • Retire pot category-linking (D-15) and record breaking change (71f3979)
  • CR-01 surface non-EUR spend instead of dropping it (54f3090)
  • WR-01 correct false updateOrCreate docblock on envelope events (17d95ce)
  • WR-02 make cutover retryable via unclaim-on-failure (6f5f462)
  • WR-03 add wire:key to move-history sub-row (c61668b)
  • WR-04 correlate move pairs by move_group_id not created_at (a0e879b)
  • WR-05 make EnvelopeAssignment store bare Y-m-d period_start (14dce97)
  • WR-06 catch RuntimeException in moveMoney handler (1d98e2e)
  • IN-01 batch recent-moves into one query per render (b7a1852)
  • IN-02 log when the fold walk cap trips before the target (f5a80d7)
  • IN-03 gate overspend-mode dispatch on an actual change (ebf3ec3)
  • IN-04 guard copyFromPeriod against overwriting existing target (6028eaa)
  • (tests) Repair 4 pre-existing suite failures (arch boundaries + time-bomb) (0c0f45e)
  • Repoint Budgets nav badge to envelope_assignments (e396855)
  • Update sidebar HTML snapshot for the new Reconcile nav row (aeabc90)
  • CR-01 date-bound reconcile match to statement date (49cef96)
  • WR-01 close reconciled-lock gaps in reclassifyCategory + toggleLegTax (f5c8288)
  • WR-02 dispatch reconciled events only for transitioned rows (bbc9047)
  • WR-03 replace brittle positional STATUSES indexing with literals (9a8a3af)
  • WR-04 honest reconcile toast reflecting rows actually locked (a59ad88)
  • IN-02 bump updated_at in toggleClearedStatus for consistency (c283a59)
  • U-1 guard tax-tag writes against reconciled lock (8916132)
  • U-2 align demo ICS account kind to ics_card (00405b2)
  • (community) Bind NoOpShell outside the NativePHP runtime (b0d4ce1)
  • (ledger) Derive reconcile transitioned set from captured ids, not timestamp re-select (2aee9e3)
  • (categorization) Enforce D-08 reconciled lock on the inline category picker path (f06fd7a)
  • (sync) Enforce table allow-list and authoritative user_id in op-log replay (3cd5214)
  • (sync) Bound the open relay deliver/drain paths against resource exhaustion (382df8a)
  • (tax) Scope forTransactionIds to whole-transaction tags so untag isn't a silent no-op (d719448)
  • (tax) Restore whole-transaction-tag uniqueness via partial unique index (0f229b2)
  • (core) Exclude superseded whole-tx tags from the sidebar tax_tagged count (598541d)
  • (sync) Stage device identity plaintext in the 0700 dir at 0600, not world-readable /tmp (ebc8b96)
  • (core) Stage encrypted-backup plaintext snapshot in a private 0700 dir, not /tmp (8ab6597)
  • Rewire CorrectionDivergenceToast/CategorizationProvenancePanel to the nested rule schema (450d182)
  • Rewire RenameCounterpartyPopover to the new CreateCategorizationRule signature (a659278)
  • (13.4-review) CR-01 scale amount rule-condition values to minor units (6d0fd99)
  • (13.4-review) CR-02 preserve tax-tag note on rule-driven re-apply (ec94141)
  • (13.4-review) CR-03 make migration 000005 down() a safe no-op (8b9ae22)
  • (13.4-review) WR-01 wrap hits_count bump loop in a DB transaction (3c9145f)
  • (13.4-review) WR-02 add id tiebreak to RuleEngine::actionsFor() (c223623)
  • (13.4-review) WR-03 fail-open per row in ReapplyRulesJob (348fa08)
  • (13.4-review) WR-04 catch InvalidArgumentException in rename popover (f604fd4)
  • (13.4-review) IN-01 remove dead RuleEvaluator::evaluate() wrapper (b2d3452)
  • (13.4-review) IN-02 document field_provenance's actual two-state contract (7c3e550)
  • (13.4-uat) Dispatch modal-show so the rule-builder actually opens (7d47615)
  • (13.4-uat) Coerce action ids to int so rule save doesn't 500 (8aa67d8) Add the locked nav entry for the migration wizard (9dd5095) (13.5-review) CR-01 re-derive conflicted budget-assignment skip-list on every Confirm (0a54500) (13.5-review) CR-02 seed distinct bookedAt so migrated same-day transactions don't collide (e757f22) (13.5-review) WR-01 explicitly user_id-scope 4 unscoped writes/reads in PromoteStagingToDomain (96443d7) (13.5-review) WR-02 user_id-scope ThreeWayMergeResolver::baselineValue() (b6df77f) (13.5-review) WR-03 materialize category-group hierarchy as real parent categories (6e6a07e) (13.5-review) WR-04 reject archives containing symlink entries (9f03f04) (13.5-review) WR-05 require opposite-sign amounts before pairing a transfer (49bcb3d) (13.5-review) WR-06 use migration_runs.status, not an all-zero-counts heuristic, to gate PreviewSummaryBuilder (895720b) (13.5-review) WR-07 preserve a negative Budgeted value instead of silently coercing to zero (dfd87d9) (13.5-gapfix) Reconcile transaction amount in 3-way-merge (Req 10) (022f9d9) (13.5-review-deep) CR-01 disjoint cat:/grp: key tags so category-group parents never collide (7b32e77) (13.5-review-deep) WR-02 guard against confirming an already-discarded run (c47cba6) (13.5-review-deep) WR-03 only treat the real fingerprint-unique violation as a collision (de97c21) (13.5-review-deep) WR-01 clean up temp dir on partial extraction; IN-01 sniff upload mime (698e7ec) (13.5-uatfix) Make migration conflict toggle real; format currency + human labels (9a3ccda) (999.6-review-a) CategorySpendQuery — WR-01 capture unsplit $0 rows, WR-05 rename shadowed filter var, wire amount filter (f88ae02) (999.6-review-a) AccountSpendQuery — WR-05 rename shadowed filter var, wire amount filter (8573098) (999.6-review-a) CounterpartySpendQuery — WR-05 rename shadowed filter var, wire amount filter (0a3b549) (999.6-review-a) TimeBucketSpendQuery — wire amount filter (c514501) (999.6-review-a) PeriodPresetResolver — WR-02 reject inverted custom range, INFO-03 strict Y-m-d parsing (c852674) (999.6-review-a) TimeBucketGenerator — WR-03 cap weekly bucket count via widen-to-monthly (b4e3b91) (999.6-review-a) CurrencyModeApplier — CR-02 filtered currency discovery + post-hoc primary-currency selection (9d708ae) (999.6-review-a) PeriodComparison — CR-01 calendar-month previousPeriod, INFO-02 drop redundant round(), CR-03 currency-aware compare() keys, WR-04 return previous-period FX-exclusion metadata (b2915ea) (999.6-review-a) ReportAggregator — wire CR-02 currency filters, WR-04 previous-period FX-exclusion metadata, and the amount filter into every dimension query (fac841c) (999.6-review-deep) WR-01 close TogglePin's pin-cap TOCTOU; WR-02 compact pin_order on delete (4c090c6) (999.6-review-deep) WR-03 normalize filter-array order before UpdateReport's dirty-check (5ff7e96) (999.6-review-deep) WR-04 validate category ownership in SearchQuery, honoring global categories (b02c0e4) (999.6-review-deep) IN-02 dedupe decodeDefinition into shared DefinitionJsonDecoder (611aee7) (999.6-review-deep) IN-01 format CSV Amount column via integer arithmetic, not float division (ca68ef3) (999.6-review-deep) CR-01 edit updates the same saved report; CR-03 charts refresh live; WR-02 export/recompute loading feedback (4f602bc) (999.6-review-deep) WR-01 debounce Amount filter inputs; IN-01 keyboard-operable filter-chip clear controls (13490c9) (999.6-review-deep) WR-03 delete confirm names the report; IN-02 tabular-nums on saved-reports index counts (a2ce2b6) Donut report-updated destroys+recreates chart instead of updateOptions (df4b76b) Keyring-file atomicity vs SQL transaction (D-10 / WR-01) (5107563) GdkRotationService per-method Session (D-11) + CounterpartyIndexQuery ORDER BY (D-12) (f25c726) Decrypt-before-compare in FingerprintStage conflict detection (4ba0829) Encrypt-incoming-before-update on both receipt-conflict write paths (0249ed5) Decrypt-before-compare in Migration 3-way-merge resolver (76cb73d) Encrypt-before-update in Migration EntityChangeApplier (9273631) Extend BoundaryArchTest Auth\Internal exemption to EnablesEncryptionForUser (3a2aa59) CR-04 decrypt tax note before re-tag to prevent double-encryption (3959b3c) CR-05 decrypt transaction description in triage recent-transactions card (0da2095) CR-01/WR-08 preserve staged keyring on finalize failure, randomize staging path (81c3ca5) CR-01/WR-01/WR-02/WR-09 migration finalize-failure atomicity, keyring reconcile, snapshot cleanup (79ce989) CR-02/WR-06/WR-07 rotate-revoke atomicity, is_self guard, seal-auth precondition (b862e43) WR-07 document authenticated-sender precondition on GDK epoch wrap receive path (3ffa6d9) WR-05/WR-06/IN-01 log+surface encryption-activation failure, server-side self-removal guard, document sync progress limitation (111c826) CR-03/WR-19 encrypt pending_enrichment_conflicts at rest, decrypt round-trip, preserve undecodable resolution (d745789) WR-03/WR-04 encrypt raw_payload on Model::save path, log decrypt-failure instead of silent null (df1d89f) WR-16 preserve counterparty when merchant_name fails to decrypt, never wrongful-prune (b7e3eee) WR-14 only warn on missing KEK when encryption is actually enabled (3afcb76) WR-18 skip undecryptable rows in transfer-pairing and search decrypt-then-match sites (3d89a71) WR-13 tag baseline amount leg with current currency to stop spurious conflicts (b0fd1a6) WR-15 report true IBAN-matched count as ambiguous_candidates (75fb34a) IN-06 require both cursor components so pagination matches posted_at ordering (27b1217) WR-12/IN-03 decrypt counterparty_name in cash-book list, document note property (ed82485) WR-17 document recurring_series cluster key as reviewed plaintext-derivative exception (fd61d86) WR-10 document migration baseline as reviewed plaintext-sink exception (d4a3b3a) WR-11 document preview cache as reviewed transient plaintext-staging exception (ffdc8fe) IN-04/IN-07 document per-attempt confirm semantics and tax-tag full-payload contract (5adcd05) CR-05 guard null user_id in triage recent-transactions decrypt (Larastan L10) (5bd7154) SyncScreen — use manual $view->extends() instead of #[Layout] attribute (d39204d) Target the NativePHP persisted store for the on-device DB (no bundle-DB data leak, survives updates) (0310e84) Reconcile on-device storage (db-driven session/cache, view-compiled path) + kill the cold-boot Blade cache-path race (f75a8f4) Register NativePHP mobile plugins in NativeServiceProvider (biometrics/scanner/background-tasks/network) + wiring test (5e445ba) Resolve the on-device DB path per-request via structural mobile detection (a2da1a1) Keep the dev database.sqlite out of the mobile bundle (ec00e20) Pre-create the on-device DB file before providers boot (kills the cold-boot WAL error) (7a9da58) RetryProvisioning() uses server-side stashed credentials, not emptied properties (HIGH-02) (21d7988) Derive mobile self-mint-skip from durable state, not ?mode=import (MEDIUM-01) (d68d3f0) Surface the silent GDK epoch-1 collision on device-ADD fan-out (MEDIUM-02) (e28b35b) Gate import completion on installed keys + guard reproject crash-loop (LOW-01, LOW-02) (3d137c4) Harden cross-device pairing per security review (MED-01, MED-02, LOW-01, LOW-03) (3ad2773) Rename EnableBankingHttpClient::auth() to initiateAuth() (da8ee1b) Guard EB adapter against silent wall-clock date fallback (8dad6e4) Guard OpenBankingFetchService against cross-institution session mismatch (9cee47e) Harden the Req 4 warning-gate against a stale session ack (67d6d1a) Disconnect() disables ALL of a user's OB connections (ce71275) CR-01 disable Guzzle redirect following on EB HTTP boundary (358d269) WR-01 reject non-public SCA host before allow-listing (4f66647) WR-02 assert https + host-match before consent redirect (20980b1) WR-03 skip malformed-money rows instead of aborting fetch (ec50ef2) WR-04 restore prior consent/account_uid on re-link secrets failure (8484ff6) WR-05 lock server-authoritative Livewire props (c572876) WR-09 disable other rows when enabling a connection (90bf274) IN-03 align ICS PDF upload limit with copy + add mimetypes (f056924) WR-11 self-guard enabled + consent in buildFetch() (e98a312) WR-10 same-tab consent redirect + institution_id href fallback (e181ae6) IN-04 move redirectUri to data-* attr, out of inline JS (a700d57) WR-13 skip unparseable internal_date instead of aborting sweep (223072a) WR-14 handle unique-violation race in import create paths (7570bed) WR-15 degrade gracefully when settings.open-banking is absent (0568f36) WR-06 add server-side warningShown gate to confirmWarning (6b7f2ea) WR-07 raise ack TTL to 2h and add re-confirm fallback CTA (b8d3b0b) WR-08 document single-user v1 secrets store and add multi-user log guard (d65ef9a) WR-12 key ICS nudge occurrence on statement-arrival day not month (a6c5cb0) (ingestion) Deny all external entities in CAMT.053 parser (XXE hardening) (4272757) (sync) Alert on GDK keyring rewrap failure after passphrase change (89aaf65) (sync) Correct phantom columns in merge registry to real schema (6339bd2) (auth) Never lock a session that has no lock to unlock with (3b3dfb4) (build) Repair migration against a fresh database (a3af0b6) (onboarding) Derive wizard step numbers and untangle the Dutch copy (6f83a9b) (release) Keep the generated notes within the API's size limit (189ebaa) (security) Clear the two SonarCloud vulnerabilities (#57) (e0de927) (a11y) Repair the controls the accessibility rules were right about (#58) (3c662d3) (a11y) Let the filter chips hold two controls instead of nesting them (#59) (aff54df) (a11y) Associate the orphan labels and drop the roles that add nothing (#60) (b9b8bfb) (a11y) State the live region instead of overriding the element's role (#61) (ceae457) (a11y) Let the announced name start with the words on the button (#65) (cc0450b) (a11y) Enforce label-in-name with a check that can read Blade (#68) (cfee32b) (setup) Report the server the reachability probe actually reached (#70) (3068556) (a11y) Let the landmark elements carry their own roles (#81) (f03cac4) (ledger) Anchor the demo dates to the window instead of the clock (#86) (778cfba) (ci) Let the scorecard result publish, and colour the coverage badge (#89) (0058d80) (core) Make the backup sidecar failure actually raise its alert (#92) (f880831) (sync) Make the secret-staging guard actually fire, and cover the keyring moves (#95) (ec79ea6) (core) Make the filesystem guards that never fired actually decide (#101) (aa10087) (sync) Translate the recipient-key conversion in the add-device fan-out (#110) (87e6d39) (core) Name the update-alert kind, so a writer can't spell it two ways (#185) (4959499) (mobile) Materialize Laravel runtime dirs into the Bifrost tree (#219) (457fc2e) (dev-mode) Apply the argument, redaction and raw-echo guards at every entry point (#220) (5280587) Restore a green main — triage test rot and six commonmark advisories (#222) (bc2f42d) (mobile) Detect the on-device runtime so native capabilities are not silently disabled (#221) (af7b9b5) (tests) Cap paratest workers so composer test finishes (#224) (67e5239) (core) Send baseline security headers on every web response (#225) (d8e9e90) (community) Shared brands return to their countries, and the transfer-pair rule leaves the applier (#227) (ffca543) (mobile) The six defects behind an unusable onboarding — locale, double encryption, slow navigation, and a lock that came late (#228) (1bcb5bc) (mobile) A signed-up account could not open Settings, and iOS wore the framework's icon (#229) (257b983) (sync) Localise the sync timestamp, and write down the mobile release path (#235) (73d0693) (updater) Fetch the running platform's manifest and wire the release feed URL (#238) (1bfe409) (build) Make Windows signing possible, and stop the env template defeating its own defaults (#236) (a8159e5) (auth) Centralize the app-lock 6-digit PIN floor in the provisioner (#245) (ae73a70) (sync) Bind the X25519 sealing key into the PAIR_CONFIRM signature (#248) (372e6c5) (mobile) Android uploads never landed either, and the gate said they did (#249) (6984729) (mobile) The upload signal reached two layouts out of four (#250) (f9ba8c3) (mobile) Ask whether multipart works per request, not once at startup (#251) (2e84a46) (mobile) Be the outermost XHR wrapper, because the shell wraps too (#252) (fb87f35) (import) A statement that failed to parse is not a ready one with no rows (#253) (2984d9e) (mobile) Say when a notification was not delivered, instead of nothing (#255) (0fe81a5) (core) Bottom sheets never closed on save, because nothing sends the event they listen for (#256) (45863ab) (goals) A goal that has been drawn on reported -4% progress into an aria-valuemin of 0 (#257) (b0cf884) (mobile) A phone could not pair at all, and told you the desktop's QR was the problem (#259) (d253881) (sync) Edits to most tables never left the device that made them, and four surfaces lied about why (#260) (a1b4803) (core) The whole Android app 404'd, because the host gate treated "no Host" as an attacker (#261) (144649d) The defects two device passes and a night on real hardware found (#262) (a9e606d) Four defects a clean install on real hardware found (#263) (2c2c022) A budget that could not see its own income, and two conventions made one (#264) (457de4c) The round that came out of the last device pass (#265) (4239cc0) The gate that let twenty issues through, and the twenty issues (#266) (03efcb8) What two phones found, and the bindings that let it happen (#267) (8a5fa09) (auth) A blank username on the first screen returned a stack trace, not a message (2dc39fe) (auth) Two app-lock writes that succeeded without saying so (3404664) (core) Every route 404'd on iPhone, because the loopback gate judged a SAPI that has no socket (160bb78) (sync) Two phones on one wifi could not pair, because the handshake had only one road (9475350) (sync) The road home, and three flaws a security review found in the road out (1ac9da2) (mobile) A picker that greyed out every statement, and a screen that waited on a message it could not show (de32693) (mobile) Every setting reverted on the next screen, because the runtime never let go of the user (e9c8c97) (tax) The country list was ordered by a code the reader cannot see (db9ce00) (community) A corpus token matched inside any word, so a phone bill became a DIY chain (bc5d174) (sync) A confirmation now names the keys it was given for, and both sides keep asking (31c6eb8) (budgets) A month of real spending reported as nothing spent (d452db9) (counterparties) The triage screen showed the ciphertext of the IBAN it exists to identify (9d5bd27) (community) The page that invites you to publish a merchant name was holding ciphertext (5349640) (notifications) A notification was frozen in whatever language it was written in (594552a) Four screens that showed the wrong thing, one of them forever (16b5912) (cashbook) "greater than zero" was the wrong complaint about an amount that could not be read (78203e8) (counterparties) A Dutch health insurer was classified as a Belgian agency (8c0de29) (sync) Opening the pairing screen killed the handshake it was opened to finish (bc9688f) Two more places that read an encrypted column without decrypting it (d23ac96) (auth) The signup form contradicted itself, and a saved file went where nobody could reach it (04f07da) (mobile) A locked phone could spend a one-shot pairing code before being told it could not (6720ab8) (ledger) Money was formatted by its currency, so a German reader got Dutch (f33a59f) (mobile) The iPhone could not speak mDNS at all, so it could never find a peer to pair with (2a05c7e) (goals) The phone got a percentage where the desktop got a goal (22fe60f) (sync) A killed listener left a child holding the port it was killed to free (8d43873) (sync) "type or scan the code" led to a screen with no scanner (361581d) Four defects the code and security review found in this branch's own work (ba266e9) (import) The tier consulted first still matched a token inside any word (e6db201) (ledger) A category kept the language it was created in, for ever (9fbfc04) (auth) The signup form trusted a value it could not see the shape of (754fa5e) (mobile) Five full-screen views padded by zero, on Android only (2459495) (sync) The pull that anyone on the wifi could make, and the timestamp guard that went missing (7deaf5d) The seven a reconciliation found still open, and the one a phone disagreed about (5905b76) (sync) A marker doing two jobs silently handed the ledger's key away (9cb3516) Six ways the app promised one thing about language and country and did another (78bebd8) (search) A Dutch reader could not find a category by the name on their screen (78e095b) (auth) The language select on signup reintroduced the shape that shipped two false ticks (535130e) (import) A file that half-parsed imported half a statement and called it done (cd9e17c) (import) The results screen could only ever show the unhelpful half of what it knew (b0f422b) Trimmed to fit. The complete log for v2.0.0 is on the commit history.