Skip to content

Commit 7d9ee21

Browse files
committed
docs: add CONTRACTS.md and update plan/README for milestones 1-3
- docs/CONTRACTS.md: CLI JSON contract specification - IMPLEMENTATION_PLAN.md: mark milestones 1-3 steps as complete - README.md: update quick start and command list - docs/PRD.md, docs/ARCHITECTURE.md: minor corrections
1 parent 6aff0c6 commit 7d9ee21

5 files changed

Lines changed: 213 additions & 89 deletions

File tree

IMPLEMENTATION_PLAN.md

Lines changed: 81 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -472,17 +472,17 @@ Goal: stable local read contracts against encrypted SQLite without any live Prov
472472

473473
Steps:
474474

475-
1. Add `internal/config`.
476-
2. Add encrypted SQLite dependency.
477-
3. Implement store open/close and migrations.
478-
4. Create minimal tables: institutions, provider_items, accounts, transactions, transaction_tags, categories, tags, recurring, sync_runs.
479-
5. Support manual accounts with nullable Provider Item provenance and local source metadata.
480-
6. Support a non-persistent demo store seeded with small, inspectable synthetic data for local testing and trial use without Provider credentials.
481-
7. Implement store methods for listing accounts, creating manual accounts, listing/searching transactions, listing categories, listing tags, and listing recurring items against both real and demo stores.
482-
8. Add fixture seed helpers for tests.
483-
9. Implement CLI skeleton for `accounts list`, `accounts create-manual`, `demo <command...>`, `transactions list`, `transactions search`, `tx` aliases, `categories list`, `tags list`, and `recurring list`.
484-
10. Add JSON contract tests for all first-milestone read commands, manual account dry-run/confirm behavior, and demo isolation/reset behavior.
485-
11. Run `go test ./...`.
475+
- [x] 1. Add `internal/config`.
476+
- [x] 2. Add encrypted SQLite dependency.
477+
- [x] 3. Implement store open/close and migrations.
478+
- [x] 4. Create minimal tables: institutions, provider_items, accounts, transactions, transaction_tags, categories, tags, recurring, sync_runs.
479+
- [x] 5. Support manual accounts with nullable Provider Item provenance and local source metadata.
480+
- [x] 6. Support a non-persistent demo store seeded with small, inspectable synthetic data for local testing and trial use without Provider credentials.
481+
- [x] 7. Implement store methods for listing accounts, creating manual accounts, listing/searching transactions, listing categories, listing tags, and listing recurring items against both real and demo stores.
482+
- [x] 8. Add fixture seed helpers for tests.
483+
- [x] 9. Implement CLI skeleton for `accounts list`, `accounts create-manual`, `demo <command...>`, `transactions list`, `transactions search`, `tx` aliases, `categories list`, `tags list`, and `recurring list`.
484+
- [x] 10. Add JSON contract tests for all first-milestone read commands, manual account dry-run/confirm behavior, and demo isolation/reset behavior.
485+
- [x] 11. Run `go test ./...`.
486486

487487
Acceptance:
488488

@@ -546,16 +546,16 @@ Goal: prepare Plaid and Bridge as replaceable Provider adapters.
546546

547547
Steps:
548548

549-
1. Add official Go Plaid SDK.
550-
2. Add Bridge HTTP client implementation.
551-
3. Add Plaid config fields with `PLAID_` environment variable names.
552-
4. Add Bridge config fields with `BRIDGE_` environment variable names.
553-
5. Add provider registry with Plaid and Bridge adapters.
554-
6. Define canonical provider records for Institution, ProviderItem, FinancialAccount, Transaction, and SyncResult.
555-
7. Write fixture-based mapper tests before any live Provider calls, including canonical amount sign normalization and liability balance normalization.
556-
8. Implement Plaid client construction without managed proxy behavior.
557-
9. Implement Bridge client construction without managed proxy behavior.
558-
10. Implement provider error classification.
549+
- [x] 1. Add official Go Plaid SDK.
550+
- [x] 2. Add Bridge HTTP client implementation.
551+
- [x] 3. Add Plaid config fields with `PLAID_` environment variable names.
552+
- [x] 4. Add Bridge config fields with `BRIDGE_` environment variable names.
553+
- [x] 5. Add provider registry with Plaid and Bridge adapters.
554+
- [x] 6. Define canonical provider records for Institution, ProviderItem, FinancialAccount, Transaction, and SyncResult.
555+
- [x] 7. Write fixture-based mapper tests before any live Provider calls, including canonical amount sign normalization and liability balance normalization.
556+
- [x] 8. Implement Plaid client construction without managed proxy behavior.
557+
- [x] 9. Implement Bridge client construction without managed proxy behavior.
558+
- [x] 10. Implement provider error classification.
559559

560560
Acceptance:
561561

@@ -573,22 +573,22 @@ Goal: allow the user to create a Plaid item and store its access token inside th
573573

574574
Steps:
575575

576-
1. Implement `money link` for institution-first linking through Plaid when Plaid is the selected Provider.
577-
2. Implement `money providers plaid link` for explicit Plaid linking.
578-
3. Separate Provider support from Provider availability: list all Providers known to support the selected institution, mark locally unavailable Providers as missing credentials, and block selecting unavailable Providers with generated configure guidance.
579-
4. Determine Provider availability from local config/env required fields, following Ray's configured-state pattern but using `money`'s explicit `env:` model.
580-
5. Use Plaid `/institutions/search` with configured products and country codes for Plaid institution discovery.
581-
6. Use a short-lived localhost Link page and callback server only for Plaid Link.
582-
7. Create Plaid Link token with explicit products and country codes.
583-
8. Serve a local page that loads Plaid Link with the link token and posts `public_token`, selected institution/account metadata, and random state back to the localhost helper on success.
584-
9. Exchange public token for access token.
585-
10. Store access token only after the encrypted store is open.
586-
11. Store provider item, institution metadata, products, and initial cursor state.
587-
12. Use random state/nonce values, timeout callback sessions, bind only for the active link flow, and shut down the helper after completion.
588-
13. Follow GitHub CLI browser ergonomics in human mode: print the local Link URL, wait for Enter, then open the browser.
589-
14. Support `--no-open` to print the URL without opening a browser for SSH, cron, and headless environments.
590-
15. Do not automatically run the first sync after link; print help-derived guidance for running `money sync`.
591-
16. Add tests for encrypted-store requirements and link exchange logic using fakes.
576+
- [x] 1. Implement `money link` for institution-first linking through Plaid when Plaid is the selected Provider.
577+
- [x] 2. Implement `money providers plaid link` for explicit Plaid linking.
578+
- [x] 3. Separate Provider support from Provider availability: list all Providers known to support the selected institution, mark locally unavailable Providers as missing credentials, and block selecting unavailable Providers with generated configure guidance.
579+
- [x] 4. Determine Provider availability from local config/env required fields, following Ray's configured-state pattern but using `money`'s explicit `env:` model.
580+
- [x] 5. Use Plaid `/institutions/search` with configured products and country codes for Plaid institution discovery.
581+
- [x] 6. Use a short-lived localhost Link page and callback server only for Plaid Link.
582+
- [x] 7. Create Plaid Link token with explicit products and country codes.
583+
- [x] 8. Serve a local page that loads Plaid Link with the link token and posts `public_token`, selected institution/account metadata, and random state back to the localhost helper on success.
584+
- [x] 9. Exchange public token for access token.
585+
- [x] 10. Store access token only after the encrypted store is open.
586+
- [x] 11. Store provider item, institution metadata, products, and initial cursor state.
587+
- [x] 12. Use random state/nonce values, timeout callback sessions, bind only for the active link flow, and shut down the helper after completion.
588+
- [x] 13. Follow GitHub CLI browser ergonomics in human mode: print the local Link URL, wait for Enter, then open the browser.
589+
- [x] 14. Support `--no-open` to print the URL without opening a browser for SSH, cron, and headless environments.
590+
- [x] 15. Do not automatically run the first sync after link; print help-derived guidance for running `money sync`.
591+
- [x] 16. Add tests for encrypted-store requirements and link exchange logic using fakes.
592592

593593
Acceptance:
594594

@@ -608,17 +608,17 @@ Goal: allow the user to create or reconnect Bridge items.
608608

609609
Steps:
610610

611-
1. Extend `money link` to choose Bridge when Bridge supports the selected Institution.
612-
2. Implement `money providers bridge link` for explicit Bridge linking.
613-
3. Use the same supported-versus-available Provider selection rules as Plaid.
614-
4. Create or reuse a Bridge external user ID.
615-
5. Create a Bridge connect session.
616-
6. Print the connect session URL, wait for Enter, then open the browser unless `--no-open` is set.
617-
7. Poll Bridge items until a new or reconnected item is available.
618-
8. Store provider item, institution metadata, status, and cursor state in the encrypted store.
619-
9. Do not automatically run the first sync after link; print help-derived guidance for running `money sync`.
620-
10. Add tests for Bridge connect/session/state logic using fakes.
621-
11. If Bridge institution discovery cannot be implemented cleanly in the first adapter, keep `money providers bridge link` working and defer institution-first Bridge selection rather than inventing unsupported discovery.
611+
- [x] 1. Defer Bridge institution-first selection until Bridge institution discovery is implemented cleanly; explicit `money providers bridge link` remains supported.
612+
- [x] 2. Implement `money providers bridge link` for explicit Bridge linking.
613+
- [x] 3. Use the same supported-versus-available Provider selection rules as Plaid.
614+
- [x] 4. Create or reuse a Bridge external user ID.
615+
- [x] 5. Create a Bridge connect session.
616+
- [x] 6. Print the connect session URL, wait for Enter, then open the browser unless `--no-open` is set.
617+
- [x] 7. Poll Bridge items until a new or reconnected item is available.
618+
- [x] 8. Store provider item, institution metadata, status, and cursor state in the encrypted store.
619+
- [x] 9. Do not automatically run the first sync after link; print help-derived guidance for running `money sync`.
620+
- [x] 10. Add tests for Bridge connect/session/state logic using fakes.
621+
- [x] 11. If Bridge institution discovery cannot be implemented cleanly in the first adapter, keep `money providers bridge link` working and defer institution-first Bridge selection rather than inventing unsupported discovery.
622622

623623
Acceptance:
624624

@@ -633,17 +633,17 @@ Goal: sync Plaid and Bridge accounts and transactions into canonical encrypted S
633633

634634
Steps:
635635

636-
1. Implement `money sync`.
637-
2. Load linked provider items from the store.
638-
3. Load provider access tokens from the encrypted store per item.
639-
4. Sync accounts first.
640-
5. Sync transactions using Plaid Transactions Sync cursors.
641-
6. Sync Bridge transactions using Bridge updated-at cursor/state.
642-
7. Upsert provider transactions by Provider Item plus provider-native transaction ID.
643-
8. Handle added, modified, and removed transactions.
644-
9. Store next cursor only after successful transaction processing.
645-
10. Record a sync run with counts and status.
646-
11. Add fixture-backed sync tests with fake Plaid client.
636+
- [x] 1. Implement `money sync`.
637+
- [x] 2. Load linked provider items from the store.
638+
- [x] 3. Load provider access tokens from the encrypted store per item.
639+
- [x] 4. Sync accounts first.
640+
- [x] 5. Sync transactions using Plaid Transactions Sync cursors.
641+
- [x] 6. Sync Bridge transactions using Bridge updated-at cursor/state.
642+
- [x] 7. Upsert provider transactions by Provider Item plus provider-native transaction ID.
643+
- [x] 8. Handle added, modified, and removed transactions.
644+
- [x] 9. Store next cursor only after successful transaction processing.
645+
- [x] 10. Record a sync run with counts and status.
646+
- [x] 11. Add fixture-backed sync tests with fake Plaid client.
647647

648648
Acceptance:
649649

@@ -668,29 +668,29 @@ Goal: make the first-milestone read Command Contracts reliable enough for extern
668668

669669
Steps:
670670

671-
1. Document `accounts.list`, `transactions.list`, `transactions.search`, `categories.list`, `tags.list`, and `recurring.list` schemas.
672-
2. Add pagination metadata.
673-
3. Add stable error codes.
674-
4. Add deterministic ordering:
671+
- [x] 1. Document `accounts.list`, `transactions.list`, `transactions.search`, `categories.list`, `tags.list`, and `recurring.list` schemas.
672+
- [x] 2. Add pagination metadata.
673+
- [x] 3. Add stable error codes.
674+
- [x] 4. Add deterministic ordering:
675675
- Accounts: hidden flag, type, name, id.
676676
- Transactions: date descending, pending status, id.
677-
5. Add filters for account, category, merchant, tag, date range, needs-review, pending, recurring status, and limit.
678-
6. Add command examples.
679-
7. Add compatibility notes for Monarch migration.
677+
- [x] 5. Add filters for account, category, merchant, tag, date range, needs-review, pending, recurring status, and limit.
678+
- [x] 6. Add command examples.
679+
- [x] 7. Add compatibility notes for Monarch migration.
680680

681681
### Phase 6: Sync Contract Hardening
682682

683683
Goal: promote sync JSON from provisional to stable after real Provider behavior is understood.
684684

685685
Steps:
686686

687-
1. Document sync summary schema.
688-
2. Define provider error taxonomy.
689-
3. Define reconnect-required behavior.
690-
4. Define removed transaction policy as soft delete plus explicit confirmed cleanup.
691-
5. Define partial failure semantics and exit codes.
692-
6. Add fixture tests for Plaid and Bridge partial failures.
693-
7. Add command examples for cron usage.
687+
- [x] 1. Document sync summary schema.
688+
- [x] 2. Define provider error taxonomy.
689+
- [x] 3. Define reconnect-required behavior.
690+
- [x] 4. Define removed transaction policy as soft delete plus explicit confirmed cleanup.
691+
- [x] 5. Define partial failure semantics and exit codes.
692+
- [x] 6. Add fixture tests for Plaid and Bridge partial failures.
693+
- [x] 7. Add command examples for cron usage.
694694

695695
Acceptance:
696696

@@ -710,17 +710,18 @@ Acceptance:
710710

711711
Goal: prove the Provider seam is real with a second adapter.
712712

713-
Choose one:
713+
Selected path:
714714

715-
- Monarch import from `monarchmoney-cli --json`.
716-
- CSV import.
717-
- MX or Finicity provider adapter.
715+
- [x] Bridge provider adapter as the second live Provider adapter alongside Plaid.
716+
- [x] Monarch import from `monarchmoney-cli --json` deferred.
717+
- [x] CSV import deferred.
718+
- [x] MX or Finicity provider adapter deferred.
718719

719720
Acceptance:
720721

721-
- A second adapter can write the same canonical tables.
722-
- Existing account and transaction contracts do not change.
723-
- Source/provider-specific fields remain namespaced or internal.
722+
- [x] A second adapter can write the same canonical tables.
723+
- [x] Existing account and transaction contracts do not change.
724+
- [x] Source/provider-specific fields remain namespaced or internal.
724725

725726
## Initial Schema Direction
726727

README.md

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
<a href="https://github.com/thedavidweng/money/actions"><img src="https://img.shields.io/github/actions/workflow/status/thedavidweng/money/ci.yml?branch=main&style=flat-square" alt="CI"></a>
1313
<a href="https://github.com/thedavidweng/money/releases"><img src="https://img.shields.io/github/v/release/thedavidweng/money?style=flat-square" alt="Release"></a>
1414
<a href="LICENSE"><img src="https://img.shields.io/github/license/thedavidweng/money?style=flat-square" alt="License"></a>
15-
<img src="https://img.shields.io/badge/go-%3E%3D1.23-blue?style=flat-square" alt="Go">
15+
<img src="https://img.shields.io/badge/go-%3E%3D1.25-blue?style=flat-square" alt="Go">
1616
</p>
1717

1818
---
@@ -38,13 +38,10 @@ Existing personal finance tools either lock data behind a paid SaaS, embed opini
3838
# Install
3939
go install github.com/thedavidweng/money/cmd/money@latest
4040

41-
# Interactive setup — creates config, encryption key, and database
42-
money setup
43-
4441
# Try it without real credentials
4542
money demo accounts list --json
4643

47-
# After configuring a provider
44+
# After creating ~/.money/config.yaml and ~/.money/.env
4845
money link
4946
money sync
5047
money accounts list --json
@@ -62,13 +59,13 @@ money categories list List transaction categories
6259
money tags list List transaction tags
6360
money recurring list List recurring transactions
6461
money link Link a financial institution
62+
money providers plaid link Link a Plaid Provider Item
63+
money providers bridge link Link a Bridge Provider Item
6564
money sync Sync linked provider data
6665
money demo <command> Run against non-persistent sample data
67-
money doctor Check configuration health
68-
money setup Guided first-time configuration
6966
```
7067

71-
All commands support `--json` for machine-readable output. Write operations require `--dry-run` or `--confirm`.
68+
Read commands and provisional sync diagnostics support `--json` for machine-readable output. Manual write operations require `--dry-run` or `--confirm`.
7269

7370
## Architecture
7471

@@ -98,6 +95,7 @@ Read commands use local data only. Sync is the explicit boundary where outbound
9895
|----------|---------|
9996
| [`docs/PRD.md`](docs/PRD.md) | Product requirements |
10097
| [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) | Module boundaries and data flow |
98+
| [`docs/CONTRACTS.md`](docs/CONTRACTS.md) | Current CLI JSON contracts |
10199
| [`docs/SCHEMA.md`](docs/SCHEMA.md) | Database schema contract |
102100
| [`docs/CONFIG.md`](docs/CONFIG.md) | Configuration loading rules |
103101
| [`docs/ROADMAP.md`](docs/ROADMAP.md) | Development phases |

docs/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ If a configured DB file already exists but cannot be opened, commands must repor
8787

8888
The concrete encrypted SQLite implementation is an engineering decision. Select the approach through a short spike using these criteria: single-binary feasibility on the target platforms, active maintenance, predictable Go driver behavior, compatibility with SQLite migrations and in-memory demo tests, no plaintext fallback, and clear handling of key material. If no mature single-binary encrypted SQLite option satisfies those constraints, document the trade-off before accepting an external native dependency.
8989

90-
The first implementation uses `github.com/ncruces/go-sqlite3` with its `database/sql` driver and the `github.com/ncruces/go-sqlite3/vfs/adiantum` encrypted VFS. This is the selected MVP path because it is cgo-free, supports app-managed 32-byte key material, keeps the CLI close to a single-binary distribution, works with normal SQLite migrations, and supports the in-memory demo path through the same SQLite implementation. Open encrypted databases with the encrypted VFS and set the key immediately after opening the connection using a PRAGMA rather than putting the key in the URI. Set `PRAGMA temp_store = memory` for encrypted real stores so temporary files do not become an accidental plaintext surface.
90+
The first implementation uses `github.com/ncruces/go-sqlite3` with its `database/sql` driver and the `github.com/ncruces/go-sqlite3/vfs/adiantum` encrypted VFS. This is the selected MVP path because it is cgo-free, supports app-managed 32-byte key material, keeps the CLI close to a single-binary distribution, works with normal SQLite migrations, and supports the in-memory demo path through the same SQLite implementation. Open encrypted databases with the encrypted VFS, pass the configured 32-byte key as the VFS `hexkey`, and set `temp_store(memory)` on the encrypted connection so temporary files do not become an accidental plaintext surface.
9191

9292
This choice provides encryption at rest, not tamper-proof database authentication. The first version treats local tamper detection and signed backups as later hardening work, not a reason to add a custom encryption layer or ship plaintext fallback behavior. If the encrypted VFS fails the implementation spike on the target platforms, the next option is a SQLCipher-backed driver with the native dependency documented explicitly before adoption; do not replace whole-database encryption with ad hoc field encryption.
9393

0 commit comments

Comments
 (0)