feat: bitemporal effective date (#7) #16

Open
agent-int wants to merge 1 commit from feat/issue-7-effective-date into main
Collaborator

Bitemporal effective date (effective_at / posted_at)

Third item of the P1 double-entry tier (#7): a transaction now has a booking/entry date (the
existing transactions.date) and an optional effective date — when it economically takes
effect (Modern Treasury effective_at). effective_date is nullable; NULL means effective ==
booking date
.

Purely additive — booking-date behavior is untouched

Existing currentBalance, running-balance, statements, and idempotency/content_hash are
unchanged. Effective date is captured on a separate path and surfaced via a new
effective-date balance query; all pre-existing tests pass unmodified.

Surfaces

  • Migration 0010: ALTER TABLE transactions ADD COLUMN effective_date TEXT (nullable).
  • Domain: DoubleEntryTransaction.effectiveDate: String? + effectiveOrBookingDate helper.
  • Store: insert/hydrate effective_date; transactionsByAccountEffective (filters
    COALESCE(effective_date, date) <= ?); Commands.effectiveBalance(account, asOf).
  • gRPC: effective_date on PostTransaction/EditTransaction requests, the transaction detail,
    and register rows; new read RPC GetEffectiveBalance(account_id, as_of) -> {balance}.
  • Client: postTransaction/editTransaction gain optional effectiveDate; TransactionDetail
    and RegisterEntry expose it; effectiveBalance(account, asOf).
  • MCP: optional effective_date on propose/commit_post_transaction; new read tool
    get_effective_balance.
  • Tests: store (EffectiveDateTests), gRPC (EffectiveDateRPCTests), MCP. make test green.

⚠️ Migration version — cross-branch coordination

This branch is cut from main (migrations 1–8). It deliberately numbers the new migration
version 10 (leaving a gap at 9) so it does not collide with the still-open #15
(attachments), which claims version 9 off the same base. The migrator applies per-recorded-
version (schema_migrations set membership), not max-version, so a gap is safe: a DB that ran v10
will still apply v9 when #15 lands. When merging both #15 and #16, resolve the trivial conflicts
by ordering the Schema.migrations array 9 then 10 and setting the migration-count assertion in
CommonsStoreTests to 10.

Scope

One P1 item (does not close #7). Report/net-worth integration of effective dates is intentionally a
follow-up; this lands the capture + query + wire foundation. Remaining P1 items (budgets,
pending/cleared) stay open.

## Bitemporal effective date (effective_at / posted_at) Third item of the **P1 double-entry tier** (#7): a transaction now has a booking/entry date (the existing `transactions.date`) and an optional **effective date** — when it economically takes effect (Modern Treasury `effective_at`). `effective_date` is nullable; **NULL means effective == booking date**. ### Purely additive — booking-date behavior is untouched Existing `currentBalance`, running-balance, statements, and idempotency/`content_hash` are **unchanged**. Effective date is captured on a separate path and surfaced via a new effective-date balance query; all pre-existing tests pass unmodified. ### Surfaces - **Migration 0010**: `ALTER TABLE transactions ADD COLUMN effective_date TEXT` (nullable). - **Domain**: `DoubleEntryTransaction.effectiveDate: String?` + `effectiveOrBookingDate` helper. - **Store**: insert/hydrate `effective_date`; `transactionsByAccountEffective` (filters `COALESCE(effective_date, date) <= ?`); `Commands.effectiveBalance(account, asOf)`. - **gRPC**: `effective_date` on PostTransaction/EditTransaction requests, the transaction detail, and register rows; new read RPC `GetEffectiveBalance(account_id, as_of) -> {balance}`. - **Client**: `postTransaction`/`editTransaction` gain optional `effectiveDate`; `TransactionDetail` and `RegisterEntry` expose it; `effectiveBalance(account, asOf)`. - **MCP**: optional `effective_date` on propose/commit_post_transaction; new read tool `get_effective_balance`. - **Tests**: store (`EffectiveDateTests`), gRPC (`EffectiveDateRPCTests`), MCP. `make test` green. ### ⚠️ Migration version — cross-branch coordination This branch is cut from `main` (migrations 1–8). It deliberately numbers the new migration **version 10** (leaving a gap at 9) so it does **not collide** with the still-open **#15** (attachments), which claims **version 9** off the same base. The migrator applies per-recorded- version (`schema_migrations` set membership), not max-version, so a gap is safe: a DB that ran v10 will still apply v9 when #15 lands. **When merging both #15 and #16**, resolve the trivial conflicts by ordering the `Schema.migrations` array `9` then `10` and setting the migration-count assertion in `CommonsStoreTests` to `10`. ### Scope One P1 item (does not close #7). Report/net-worth integration of effective dates is intentionally a follow-up; this lands the capture + query + wire foundation. Remaining P1 items (budgets, pending/cleared) stay open.
feat: bitemporal effective date (#7)
Some checks failed
ci / macos (pull_request) Successful in 4m0s
ci / linux (pull_request) Has been cancelled
ci / proto (pull_request) Has been cancelled
ci / purity (pull_request) Has been cancelled
2f11436a40
Add an optional effective date (Modern Treasury effective_at / posted_at) to
transactions, distinct from the existing booking/entry date (`date`). The new
`effective_date` column is nullable; NULL means the effective date equals the
booking date.

Purely additive: existing currentBalance, running-balance, statements, and
content_hash/idempotency are untouched. Effective date is captured on a
separate path and surfaced via a new effective-date balance query; all
pre-existing tests pass unmodified.

- migration 0010 (nullable effective_date column)
- Domain: DoubleEntryTransaction.effectiveDate + effectiveOrBookingDate
- Store: insert/hydrate effective_date; transactionsByAccountEffective
  (COALESCE(effective_date, date) <= asOf); Commands.effectiveBalance
- gRPC: effective_date on post/edit + detail + register rows;
  new GetEffectiveBalance read RPC
- Client: optional effectiveDate on post/edit; effectiveBalance; view models
- MCP: optional effective_date on propose/commit_post_transaction;
  new get_effective_balance read tool
- Tests at the store, gRPC, and MCP layers

Migration is numbered version 10 (gap at 9) to avoid colliding with the
still-open #15 (attachments, version 9) off the same base; the migrator
applies per-recorded-version so the gap is safe. Third item of the P1
double-entry tier (#7); remaining items stay open.
nic force-pushed feat/issue-7-effective-date from 2f11436a40
Some checks failed
ci / macos (pull_request) Successful in 4m0s
ci / linux (pull_request) Has been cancelled
ci / proto (pull_request) Has been cancelled
ci / purity (pull_request) Has been cancelled
to c76adebd42
Some checks failed
ci / macos (pull_request) Successful in 3m55s
ci / linux (pull_request) Has been cancelled
ci / proto (pull_request) Has been cancelled
ci / purity (pull_request) Has been cancelled
2026-06-30 21:26:44 -07:00
Compare
nic referenced this pull request from a commit 2026-06-30 22:00:26 -07:00
Some checks failed
ci / macos (pull_request) Successful in 3m55s
ci / linux (pull_request) Has been cancelled
ci / proto (pull_request) Has been cancelled
ci / purity (pull_request) Has been cancelled
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin "feat/issue-7-effective-date:feat/issue-7-effective-date"
git switch "feat/issue-7-effective-date"

Merge

Merge the changes and update on Forgejo.

Warning: The "Autodetect manual merge" setting is not enabled for this repository, you will have to mark this pull request as manually merged afterwards.

git switch "main"
git merge --no-ff "feat/issue-7-effective-date"
git switch "feat/issue-7-effective-date"
git rebase "main"
git switch "main"
git merge --ff-only "feat/issue-7-effective-date"
git switch "feat/issue-7-effective-date"
git rebase "main"
git switch "main"
git merge --no-ff "feat/issue-7-effective-date"
git switch "main"
git merge --squash "feat/issue-7-effective-date"
git switch "main"
git merge --ff-only "feat/issue-7-effective-date"
git switch "main"
git merge "feat/issue-7-effective-date"
git push origin "main"
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
nic/commons!16
No description provided.