Portfolio Manager Tauri Refactor — Completion Report
Interim report. Every figure below was measured, not estimated; items that
could not be verified say so explicitly.
| Field |
Value |
| Release version |
2.0.0 (in progress) |
| Primary platform |
macOS (Apple Silicon verified) |
| Sidecar Python version |
3.11+ (validated on 3.12) |
| Tauri version |
2.x (requires Rust ≥ 1.85; verified on 1.97.1) |
| React version |
18.3 |
| Database schema version |
v4 |
| Latest docs refresh |
2026-08-08 |
Executive Status
| Area |
Status |
Notes |
| Python domain preservation |
Complete |
Scoring, week, slug, migrations ported verbatim |
| SQLite compatibility |
Complete |
Legacy v1–v4 databases open and upgrade with backup |
| FastAPI sidecar |
Complete |
All /api/v1 routes plus health/ready |
| Sidecar security |
Complete |
Token auth, loopback bind, docs disabled in production |
| Tauri supervision |
Complete, compiles |
Supervisor/forwarder/commands build and unit-test on Rust 1.97 |
| React workspaces |
Complete |
All six views, week navigator, degraded banner |
| Markdown and Mermaid |
Complete |
Bundled, offline, sanitized, error-preserving |
| Settings |
Complete |
Bounds plus restart-required on database path change |
| Migrations and backups |
Complete |
Backup-before-migrate preserved |
| Automated testing |
All four suites run green |
See below |
| Documentation |
Complete (core) |
README, 11 ADRs, traceability, toolchain notes |
| macOS packaging |
Executed end to end |
.app and .dmg both build; signed and installed to /Applications |
| Running application |
Verified |
Launches, sidecar starts, all six views load real data |
| Windows/Linux packaging |
Deferred |
Phase 16 |
Test Results
| Suite |
Result |
Notes |
| Python unit (domain + services) |
Pass |
Week boundaries, slug, scoring 59/60/79/80, service rules |
| Python integration (repos + migrations) |
Pass |
CRUD, cascade, counts, backup, idempotent migrations |
| API contract |
Pass |
Auth (missing/invalid/valid), workflow, production hardening |
| Legacy DB compatibility |
Pass |
v2 → v4 upgrade preserves data across restart |
| Backend total |
84 passed, ~85% line coverage |
Target ≥ 80% met |
| React unit/component |
22 passed (7 files) |
Vitest + Testing Library, jsdom |
| TypeScript typecheck |
0 errors |
tsc --noEmit, strict |
| Rust unit |
9 passed, 1 flaky |
See below |
| Legacy Tkinter suite |
144 passed, 92.34% coverage |
Root suite for the legacy app |
Known flaky test
security::port::tests::picks_a_nonzero_loopback_port fails roughly 1 run in
10 under parallel execution and never under --test-threads=1 (10 runs each
way). It asserts that a released ephemeral port is immediately re-bindable —
exactly the race security/port.rs documents as unavoidable and handles with
readiness polling. The production code is correct; the assertion is stronger
than the design guarantees. Not yet changed.
Security Verification
| Control |
Result |
| Dynamic startup port |
Verified (security/port.rs) |
| Per-run random token |
Verified (security/token.rs) |
| Loopback-only binding |
Verified (cli/sidecar.py, HOST=127.0.0.1) |
| Token absent from renderer and logs |
Verified (redaction filters, SecretString) |
| Renderer cannot call sidecar directly |
Verified (verify_no_renderer_http.py passes) |
| Protected routes reject missing/invalid token |
Verified (contract tests) |
| Production API docs disabled |
Verified (test_docs_disabled_in_production) |
| Sidecar terminates with app |
Verified in unit tests (SidecarHandle::shutdown); confirmed in a running app |
| Unexpected failures omit stack traces |
Verified (sanitized INTERNAL_ERROR) |
Token absent from sidecar argv |
Verified against the live process (--port <n> --production only) |
Bugs found by packaging and running the app
The first end-to-end build surfaced three defects that no unit test could have
caught, because each only exists in a packaged, running application.
| Defect |
Cause |
Fix |
| Sidecar died on launch |
build_sidecar.py used --collect-submodules, which bundles Python modules but not data files, so the frozen binary shipped without schema.sql and crashed reading it. Every frozen sidecar ever produced was dead on arrival; invisible in development because the file is on disk. |
Added a DATA_FILES list and --add-data wiring, with a hard error when a listed file is missing |
| Dashboard showed "Could not load" on every cold start |
The window opens immediately, but the frozen sidecar needs seconds to boot. The first render's queries hit a closed port, and with retry: false on the query client they never recovered. |
useSidecarHealth now refetches every query on the transition into ready; covered by a regression test |
connect-src missing from the CSP |
The production CSP had no connect-src, so it fell back to default-src 'self'. |
Added connect-src 'self' ipc: http://ipc.localhost |
| ~10 s startup on every launch |
The sidecar was frozen with PyInstaller --onefile, which re-extracts the whole interpreter to a temporary directory each time it runs. |
Switched to --onedir, shipped through bundle.resources. Measured cold start dropped to ~1.5 s |
| Development tooling shipped inside the app |
Dependency analysis reached mypy through pydantic's optional mypy plugin. |
Added EXCLUDED_MODULES to the build script; bundle went from 90 MB to 83 MB |
Open Issues
| Item |
Impact |
| No signing identity available |
Ad-hoc signing works for local installation; Gatekeeper assessment returns rejected and notarization needs a Developer ID |
npm install fails on network mounts |
ENOTEMPTY on rename; needs a local-disk clone or prefix. Also makes beforeBuildCommand fail, since it runs npm run build |
| Two apps named "Portfolio Manager" |
The legacy launcher in ~/Applications and the V2 bundle in /Applications share a display name |
Deviations and Deferred Work
| Item |
Reason |
| Cancelled milestones excluded from score denominator |
SRS explicit rule; the Tkinter app counted them. See ADR-002 |
| macOS signing and notarization |
Requires Apple credentials — deferred |
| Windows/Linux CI lanes, performance benchmarks |
Deferred per agreed scope |
Acceptance Criteria (this pass)
| Criterion |
Status |
| Existing Python behavior preserved and tested |
Met |
| Sidecar starts on dynamic port, loopback only |
Met (code plus smoke test) |
| Unauthenticated sidecar requests fail |
Met |
| Six workspace views available |
Met (code and component tests) |
| Project / session / milestone / review / dashboard flows |
Met (backend contract tests; UI wired) |
| Plan plus Mermaid offline |
Met |
| Dashboard score matches SRS formula |
Met |
| Legacy database opens without manual conversion |
Met |
| Production API docs disabled |
Met |
| Coverage target met (backend) |
Met (~85%) |
| End-to-end application run |
Met — built, signed, installed, launched; all six views load real data from the user's existing database with no errors and no auth failures |