feat(service): M2a — account gate, quotas, sessions #4

Merged
charles merged 1 commit from feat/m2a-service into main 2026-06-17 12:23:18 +00:00
Owner

Roadmap M2a. The transport-free service layer with the account gate, quotas, and session migration — hard-rule #1 (uncircumventable gate), tested over in-memory fakes. Still offline/std-only (Postgres + async land in M2b).

Changes

  • domain: add TreeId.
  • service:
    • Actor (Anonymous/User) + require_user()AuthRequired (FR-OV-02).
    • ports: TreeStore/QuotaStore/SessionStore/TripBuilder (sync now; async in M2b).
    • Trees.validate_tree (anon-ok, attaches to session), Trips.build_trip/refine_trip (gate → places check → quota consume → refund on failure), Accounts.quota_remaining/adopt_session (FR-IMP-18).
    • hand-rolled ServiceError (no thiserror — keeps it dependency-free).
    • mem: in-memory fakes (MemTrees/MemQuota/MemSessions/StubBuilder).

Verification

  • just qa green. 5 invariant tests: anon build rejected; anon validate + adopt; build needs usable places (checked before quota); quota consumed→exceeded; failed build refunds.

Traces: FR-OV-01/02, FR-ACC-13/16, FR-IMP-18/19 · tech-spec 01.

Roadmap **M2a**. The transport-free service layer with the **account gate**, **quotas**, and **session migration** — hard-rule #1 (uncircumventable gate), tested over in-memory fakes. Still **offline/std-only** (Postgres + async land in M2b). ## Changes - **domain**: add `TreeId`. - **service**: - `Actor` (Anonymous/User) + `require_user()` → `AuthRequired` (FR-OV-02). - **ports**: `TreeStore`/`QuotaStore`/`SessionStore`/`TripBuilder` (sync now; async in M2b). - `Trees.validate_tree` (anon-ok, attaches to session), `Trips.build_trip`/`refine_trip` (gate → places check → quota consume → **refund on failure**), `Accounts.quota_remaining`/`adopt_session` (FR-IMP-18). - hand-rolled `ServiceError` (no `thiserror` — keeps it dependency-free). - **mem**: in-memory fakes (`MemTrees`/`MemQuota`/`MemSessions`/`StubBuilder`). ## Verification - `just qa` green. 5 invariant tests: anon build rejected; anon validate + adopt; build needs usable places (checked before quota); quota consumed→exceeded; failed build refunds. Traces: FR-OV-01/02, FR-ACC-13/16, FR-IMP-18/19 · tech-spec 01.
feat(service): M2a — account gate, quotas, sessions (in-memory fakes)
All checks were successful
qa / qa-1 (pull_request) Successful in 12s
qa / qa (pull_request) Successful in 0s
qa / qa-1 (push) Successful in 39s
qa / qa (push) Successful in 0s
c39dbfb7e2
- domain: add TreeId
- service: transport-free capability layer
  - Actor (Anonymous/User) + require_user → AuthRequired (FR-OV-02)
  - ports: TreeStore/QuotaStore/SessionStore/TripBuilder (sync; async in M2b)
  - Trees.validate_tree (anon-ok, attaches to session), Trips.build/refine
    (gate → places check → quota consume → refund on failure), Accounts
    (quota_remaining, adopt_session migration — FR-IMP-18)
  - hand-rolled ServiceError (no thiserror; offline)
  - mem: in-memory fakes (MemTrees/MemQuota/MemSessions/StubBuilder)

Invariants tested (5): anon build rejected; anon validate + adopt; build needs
usable places (before quota); quota consumed→exceeded; failed build refunds.

just qa green. Still offline/std-only (no external deps yet).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
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
charles/geneatrip!4
No description provided.