-
Notifications
You must be signed in to change notification settings - Fork 0
docs: finance/books/ledger boundary contracts + proposals #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
4f20da6
242f11a
16775f0
98bf1d9
dce48c8
e15f239
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| # ChittyBooks ↔ ChittyFinance Contract | ||
|
|
||
| > Engine / UI boundary. Not canon yet — proposal for the pentad. | ||
|
|
||
| ## Status (verified 2026-05-27) | ||
|
|
||
| | Surface | Repo | Deploy | Health | | ||
| |---|---|---|---| | ||
| | ChittyFinance (engine) | `CHITTYAPPS/chittyfinance` | Hono on CF Workers at `finance.chitty.cc` | `200 ok` | | ||
| | ChittyBooks (UI/app) | `CHITTYAPPS/chittybooks` | Python Flask, `main.py`, Dockerfile, `.replit` → `cloudrun` | **Not deployed**. `books.chitty.cc` does not resolve. | | ||
| | ChittyLedger (substrate) | `CHITTYFOUNDATION/chittyledger` | Worker at `ledger.chitty.cc` | `200 ok` | | ||
| | ChittyLedger (legacy fork) | `CHITTYOS/chittybooks` | Express + React, in-memory, not deployed | n/a — DUPLICATE, candidate for retirement or repurpose as ChittyLedger-Evidence seed | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This row labels Useful? React with 👍 / 👎. |
||
|
|
||
| `CHITTYAPPS/chittybooks/CHARTER.md` claims a Cloudflare Worker at `books.chitty.cc`. The repository is a Python Flask application with `deploymentTarget = "cloudrun"`. Charter and code disagree. | ||
|
|
||
| ## Boundary | ||
|
|
||
| - **ChittyFinance is the finance engine.** It owns: tenants, properties, accounts, transactions, allocations, classification (COA L0–L4), reports, valuation, AI summarization, OAuth connectors (Wave, Stripe, Google, Mercury via proxy), and webhooks. It writes evidence-grade entries to ChittyLedger. | ||
| - **ChittyBooks is a bookkeeping UI/app.** It does **not** own bookkeeping records. It is a thin surface that reads ChittyFinance and (where applicable) ChittyLedger-Finance projections. | ||
| - **ChittyLedger is the substrate.** ChittyLedger-Finance and ChittyLedger-Evidence are projections of it. See `docs/chittyledger-finance-design.md` for the canonical projection design. | ||
|
|
||
| ## Source of Truth (no competing writers) | ||
|
|
||
| | Resource | Writer | Reader | | ||
| |---|---|---| | ||
| | `tenants`, `properties`, `accounts`, `transactions`, `allocations`, `classifications` | ChittyFinance | ChittyBooks (read-only), ChittyCommand, exports | | ||
| | `financial_documents`, `financial_facts`, `reconciliation_conflicts` | ChittyLedger-Finance (via ChittyTrace ingest) | ChittyFinance, ChittyBooks | | ||
| | Mercury/Wave/Stripe/Plaid raw events | external | ChittyFinance webhooks | | ||
|
|
||
| ChittyBooks MUST NOT write transactions, allocations, or COA classifications directly. All mutations route to ChittyFinance. | ||
|
|
||
| ## API Surface ChittyBooks consumes | ||
|
|
||
| All paths are under `https://finance.chitty.cc`. Auth: ChittyAuth Bearer token. Tenant: `X-Tenant-ID` header (server-side enforces from JWT claims; path param is not trusted). | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
|
|
||
| | Path (verified mounted) | Purpose for ChittyBooks | | ||
| |---|---| | ||
| | `/api/tenants` | List tenants the caller can read | | ||
| | `/api/properties` | Property list | | ||
| | `/api/accounts` | Account list + balances | | ||
| | `/api/transactions` | Transaction feed (filter by date, account, tenant) | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
| | `/api/allocations` | Allocation rules + history | | ||
| | `/api/classification` | COA assignments + audit trail | | ||
|
Comment on lines
+42
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The code mounts allocation handlers only under Useful? React with 👍 / 👎. |
||
| | `/api/reports/*` | Pre-aggregated bookkeeping views | | ||
| | `/api/integrations/status` | Which connectors are configured | | ||
| | `/api/v1/documentation` | OpenAPI 3.0 spec (note: only the docs route is under `/api/v1`; data routes are under `/api`. See `docs/proposals/api-v1-prefix-fix.md`.) | | ||
|
|
||
| ## ChittyLedger projection paths (read-only) | ||
|
|
||
| ChittyBooks reads ChittyLedger-Finance projection tables via `ChittyFinance` aggregator endpoints — it does not query ChittyLedger directly. This preserves the substrate boundary: ChittyLedger does not know about ChittyBooks. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This says ChittyBooks reads ChittyLedger-Finance projection tables through ChittyFinance aggregator endpoints, but a route search only finds the current finance resources ( Useful? React with 👍 / 👎. |
||
|
|
||
| ## Deploy decision (2026-05-27) | ||
|
|
||
| **Retired: the assumption that `books.chitty.cc` is a live API.** The domain does not resolve and no Worker exists. Code paths that target it MUST be disabled or guarded (see `docs/proposals/ch1tty-connector-revision.md`). | ||
|
|
||
| **Not yet decided: the actual ChittyBooks deploy path.** That decision is an explicit operator gate. Until the operator chooses container / worker-port / merged-into-finance, ChittyBooks stays as repo-only — a candidate UI/workflow surface, not a runtime. | ||
|
|
||
| Proof from repo files for the fake-domain retirement (no inference): | ||
|
|
||
| | Evidence | Source | What it proves | | ||
| |---|---|---| | ||
| | Repo is Python Flask, not Worker | `CHITTYAPPS/chittybooks/main.py` (43 KB), `Dockerfile`, `pyproject.toml`, `wsgi.py` | Charter ↔ code mismatch | | ||
| | Replit config targets Cloud Run, not CF | `CHITTYAPPS/chittybooks/.replit` lines `deploymentTarget = "cloudrun"` and `[[ports]] localPort = 5000 externalPort = 80` | Never built as a Worker | | ||
| | No `wrangler.toml`/`wrangler.jsonc` in repo | `find CHITTYAPPS/chittybooks -maxdepth 2 -name 'wrangler*'` returns empty | No CF deploy path exists | | ||
| | No DNS for the claimed domain | `dig books.chitty.cc` → NXDOMAIN (verified via `curl: Could not resolve host`) | Charter URL is aspirational | | ||
| | Bookkeeping engine already complete in ChittyFinance | `CHITTYAPPS/chittyfinance/server/routes/{allocations,classification,reports,tax,portfolio,charges}.ts` | No engine gap requires a second service | | ||
| | Per-tenant `tenantId NOT NULL` at schema | `database/system.schema.ts:103` | Multi-tenant boundary is in ChittyFinance, not in ChittyBooks | | ||
|
|
||
| Options preserved for the operator (deploy gate — not decided here): | ||
| - **Option A — Cloud Run container.** Matches existing `.replit` config. Justification gap: ChittyFinance already owns bookkeeping engine; risk of competing source of truth. | ||
| - **Option B — Worker rewrite.** Same competition risk + significant rewrite cost. | ||
| - **Option C — Merged UI surface served by ChittyFinance.** Lowest cost, no new runtime. Default if no operator decision is made. | ||
|
|
||
| ## Followup actions (operator-gated, not auto-merged) | ||
|
|
||
| 1. Pick A/B/C above. Each requires explicit approval. | ||
| 2. Reconcile `CHITTYAPPS/chittybooks/CHARTER.md` and `CHITTY.md` once the choice is made (currently both claim a Worker at `books.chitty.cc`, which is false). | ||
| 3. Patch `CHITTYOS/chittycommand` so its `booksClient` does not call the dead `CHITTYBOOKS_URL` (see `docs/proposals/ch1tty-connector-revision.md`). This is the only auto-mergeable action in this branch. | ||
|
|
||
| ## Deploy gates | ||
|
|
||
| - [ ] PR approval before any `books.chitty.cc` DNS or Worker creation. | ||
| - [ ] CHARTER.md in `CHITTYAPPS/chittybooks` must be reconciled with whichever option is chosen (currently aspirational). | ||
| - [ ] `CHITTYOS/chittybooks` legacy fork is renamed, archived, or repurposed as ChittyLedger-Evidence — do not let it shadow the canonical surface. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| # Mercury Multitenant Model | ||
|
|
||
| > How Mercury bank data flows through ChittyFinance with tenant + Legal Person isolation. | ||
|
|
||
| ## Requirements (non-negotiable) | ||
|
|
||
| 1. **`tenant_id` required on every Mercury-derived record.** No row, webhook event, or queue message lacks it. Cross-tenant reads are server-side blocked, not client-trusted. | ||
| 2. **Legal Person ChittyID binding required.** Every Mercury account maps to exactly one Legal Person (`P-Legal` entity), recorded as `legal_person_chittyid` on the account row. Account ↔ Legal Person is many-to-one (one LLC can have many accounts). | ||
| 3. **Wave business mapping required.** Each `(tenant_id, legal_person_chittyid)` pair has at most one Wave business; the mapping lives in `integration_account_links` (source = `wave`, target = `mercury`). Unmapped Mercury accounts produce reconciliation conflicts, not silent omission. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This contract makes Useful? React with 👍 / 👎. |
||
| 4. **ChittyBooks reconciles over Mercury + Wave + Stripe** by reading ChittyFinance's reconciliation views. ChittyBooks never reaches into Mercury directly. | ||
|
|
||
| ## Identity model | ||
|
|
||
| ``` | ||
| Person (P-Legal, e.g. "IT CAN BE LLC") | ||
| │ legal_person_chittyid | ||
| └── Account (Mercury account #1, #2, ...) | ||
| │ tenant_id | ||
| └── Transaction (mercury txn rows) | ||
| │ metadata.mercury_kind, mercury_id | ||
| └── Allocation → Property/Lease (Business surface) | ||
| ``` | ||
|
Comment on lines
+14
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a language tag to the fenced diagram block. This fenced block is missing a language identifier and triggers markdownlint MD040. Suggested fix-```
+```text
Person (P-Legal, e.g. "IT CAN BE LLC")
│ legal_person_chittyid
└── Account (Mercury account `#1`, `#2`, ...)
│ tenant_id
└── Transaction (mercury txn rows)
│ metadata.mercury_kind, mercury_id
└── Allocation → Property/Lease (Business surface)Verify each finding against current code. Fix only still-valid issues, skip the In |
||
|
|
||
| - **Business / Legalink separation**: business operations (property, lease, allocation) live on the Business surface; the Legal Person binding lives on the Authority/Person surface. They join through `legal_person_chittyid`, not by sharing schemas. | ||
|
|
||
| ## Data sources | ||
|
|
||
| | Source | Path | Tenant binding | | ||
| |---|---|---| | ||
| | Mercury read API | `https://api.mercury.com/api/v1` (direct, OAuth tokens scoped per tenant) | Token issued per `(tenant_id, legal_person_chittyid)` via ChittyConnect | | ||
| | Mercury write API | `mercury-proxy` on `chittyserv-dev` (IP-allowlisted by Mercury) | `X-Mercury-Token` per request; proxy is stateless re tenancy | | ||
| | Mercury webhooks | `/api/webhooks/mercury` on `finance.chitty.cc` | Per-business HMAC secret, resolves to `tenant_id` + `legal_person_chittyid` before write | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The HMAC-verified Mercury receiver is mounted at Useful? React with 👍 / 👎. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The native Mercury webhook implementation is keyed only by Useful? React with 👍 / 👎. |
||
|
|
||
| The write proxy at `CHITTYOS/mercury-proxy` is **not** a tenant boundary — it is a network egress shim. Tenancy is enforced by the caller (ChittyFinance) before reaching it. | ||
|
|
||
| ## Reconciliation surface | ||
|
|
||
| ChittyBooks consumes these ChittyFinance endpoints, not raw Mercury: | ||
|
|
||
| - `GET /api/transactions?source=mercury` — Mercury txns scoped to caller's tenant | ||
| - `GET /api/transactions?source=wave` — Wave txns scoped to caller's tenant | ||
| - `GET /api/transactions?source=stripe` — Stripe charges scoped to caller's tenant | ||
|
Comment on lines
+40
to
+42
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Useful? React with 👍 / 👎. |
||
| - `GET /api/reports/reconciliation?tenant_id=...&period=...` — three-way diff (Mercury ↔ Wave ↔ Stripe) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The repo does not mount Useful? React with 👍 / 👎. |
||
| - `GET /api/integrations/status` — per-source connection health + last-sync timestamps | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The mounted Useful? React with 👍 / 👎. |
||
|
|
||
| Conflicts surface as `reconciliation_conflicts` in ChittyLedger-Finance (see `docs/chittyledger-finance-design.md`). | ||
|
|
||
| ## What is explicitly out of scope | ||
|
|
||
| - ChittyBooks may **not** call Mercury directly. | ||
| - ChittyBooks may **not** create Wave invoices/sales directly — those route through ChittyFinance. | ||
| - Credential rotation is owned by ChittyConnect concierge. ChittyBooks never sees a Mercury or Wave token. | ||
|
|
||
| ## Adversarial findings (2026-05-27 verification) | ||
|
|
||
| **CRITICAL — schema gap.** `legal_person_chittyid` column is **NOT present** on `accounts` in `database/system.schema.ts` (verified at lines 101-103; only `id`, `tenantId`, and downstream columns exist). The Legal Person binding is therefore not enforceable at the database level today. Two remediation paths: | ||
|
|
||
| - **Path A (schema migration)** — add `legal_person_chittyid TEXT NOT NULL` to `accounts` with a backfill plan. Coordinated cutover required (no migrations in this repo per CLAUDE.md "Schema Changes"; `drizzle-kit push` is destructive). | ||
| - **Path B (interim metadata)** — store the binding in `accounts.metadata->>'legal_person_chittyid'` until Path A is ready. Adds a runtime check at the storage layer. | ||
|
|
||
| Until one of these lands, the Mercury multitenant contract is **partially enforced** (tenant_id yes, legal_person no). Reconciliation reports MUST flag this in their output until the gap closes. | ||
|
|
||
| **Verified OK.** `tenant_id` is `NOT NULL` on `accounts`, `transactions`, `properties`, `integrations` (`database/system.schema.ts`), so any insert missing `tenantId` fails at the database. The 18 inserts in `server/storage/system.ts` rely on the caller's `data` containing `tenantId`; the schema constraint provides defense-in-depth. No write path can bypass it. | ||
|
|
||
| ## Deploy gates | ||
|
|
||
| - [ ] **BLOCKER**: `legal_person_chittyid` column or metadata interim must exist before reconciliation reports reference it. Until then, the column is contract-only. | ||
| - [ ] `legal_person_chittyid` column present on `accounts` (verify in `database/system.schema.ts` before reconciliation reports go live). | ||
| - [ ] Per-business Mercury webhook secret in place (already shipped — PR #113). | ||
| - [ ] No code path can write a Mercury-derived row with `tenant_id = NULL`. Enforce at the SystemStorage layer, not at the route layer. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| # API path prefix — RESOLVED as non-issue | ||
|
|
||
| ## Original report | ||
|
|
||
| > `https://finance.chitty.cc/api/v1/entities` → `404`. Docs imply `/api/v1/*` everywhere. | ||
|
|
||
| ## Investigation (2026-05-27) | ||
|
|
||
| Two route families exist by design: | ||
|
|
||
| | Family | Mount | Examples | Verified | | ||
| |---|---|---|---| | ||
| | **Operational/meta** | `/api/v1/*` | `/api/v1/status`, `/api/v1/metrics`, `/api/v1/documentation` | `server/routes/health.ts:21,38`; `server/routes/docs.ts:7`. All return `200`. | | ||
| | **Resource data** | `/api/*` | `/api/accounts`, `/api/transactions`, `/api/properties`, `/api/tenants`, `/api/integrations/*`, `/api/reports/*`, etc. | `server/app.ts:74-118`; each route module under `server/routes/`. All return `401` (auth) — routes exist. | | ||
|
|
||
| `/api/v1/entities` returns `404` because **there is no `entities` resource**, not because of a path mismatch. The original concern came from assuming the `/api/v1` prefix applied to data routes; it does not. | ||
|
|
||
| ## Decision: no code change | ||
|
|
||
| - The split (meta under `/v1`, resources unprefixed) is intentional and consistent with the OpenAPI spec at `/api/v1/documentation` which lists `paths` correctly. | ||
| - All 5 in-repo references to `/api/v1` (`.github/workflows/register.yml`, `deploy/registration/chittyfinance.registration.json`, `client/src/pages/Landing.tsx:342`, `.claude/commands/quick-deploy.md:28`, plans) point at real endpoints. **Nothing to fix.** | ||
| - All 2 cross-org consumer references (`chittycommand/src/lib/integrations.ts:249`, `chittyregistry/src/routes/notion-webhooks.ts`) target `/api/<resource>` — correct. | ||
|
|
||
| ## Documentation patch (the only action) | ||
|
|
||
| Add a single sentence to `CLAUDE.md` under "Where Things Live" → routes section: | ||
|
|
||
| > Resource routes are mounted at `/api/<resource>` (no `/v1` prefix). Only operational/meta routes (`status`, `metrics`, `documentation`) live under `/api/v1/`. The OpenAPI spec at `/api/v1/documentation` is authoritative. | ||
|
|
||
| ## Deploy gates | ||
|
|
||
| - None. No routing or worker change. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| # ch1tty Mercury/ChittyBooks connector — revision | ||
|
|
||
| > Revises the canonical draft `CHITTYOS/chittycommand/docs/plans/2026-02-23-mercury-chittybooks-plan.md` to route through real services or gate fake endpoints. Companion to the contracts in `docs/contracts/`. | ||
|
|
||
| ## Fake / dead endpoints in the current draft | ||
|
|
||
| | Reference | Location | Status today | Action | | ||
| |---|---|---|---| | ||
| | `CHITTYBOOKS_URL = "https://books.chitty.cc"` | `chittycommand/docs/plans/2026-02-23-mercury-chittybooks-plan.md:168` | DNS does not resolve | **Disable behind deploy gate** until ChittyBooks deploy decision lands (see `docs/contracts/chittybooks-chittyfinance.md`). | | ||
| | `booksClient(env)` | `chittycommand/src/lib/integrations.ts:618` | Implemented; target URL does not resolve | Wrap in env guard: if `CHITTYBOOKS_URL` is unset or `*.chitty.cc` DNS-lookup fails on cold-start, return `null` like `financeClient` does on missing config. Add log line `[books] disabled — no CHITTYBOOKS_URL`. | | ||
| | Service-list expectation that `chittybooks` is in registry response | `chittycommand/docs/plans/2026-02-23-mercury-chittybooks-plan.md:934` | Registry will not return chittybooks because nothing is registered | Remove `chittybooks` from the "expected services" assertion until it deploys. | | ||
|
|
||
| ## Real services to route through (use these, not stubs) | ||
|
|
||
| | Concern | Existing real service | Path | | ||
| |---|---|---| | ||
| | Mercury read | Direct Mercury API per-tenant token | `https://api.mercury.com/api/v1` via `mercuryClient(token)` in `chittycommand/src/lib/integrations.ts:551` | | ||
| | Mercury write | mercury-proxy on chittyserv-dev | `https://mercury-proxy.chitty.cc` (CF tunnel) — POST `/proxy` with `X-Mercury-Token` | | ||
| | Mercury webhooks | ChittyFinance | `POST https://finance.chitty.cc/api/webhooks/mercury` (PR #113, per-business HMAC secrets) | | ||
| | Bookkeeping reads | ChittyFinance | `GET https://finance.chitty.cc/api/transactions` and `/api/reports/*` | | ||
| | Bookkeeping writes | ChittyFinance | `POST https://finance.chitty.cc/api/transactions`, `/api/allocations`, `/api/classification` — **not** a separate `chittybooks` write API | | ||
| | Credentials | ChittyConnect | `https://connect.chitty.cc` via concierge — never chat-paste | | ||
| | Ledger writes | ChittyLedger | `https://ledger.chitty.cc/entries` (auth-required) | | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
ChittyFinance's ledger client and its test post ledger entries to Useful? React with 👍 / 👎. |
||
|
|
||
| ## Concrete revisions to the canonical plan | ||
|
|
||
| Apply these to `CHITTYOS/chittycommand/docs/plans/2026-02-23-mercury-chittybooks-plan.md` in a follow-up PR on that repo: | ||
|
|
||
| 1. **Replace** `CHITTYBOOKS_URL = "https://books.chitty.cc"` with `CHITTYBOOKS_URL = ""` and add note: "ChittyBooks is a UI-layer surface, not a separate API. For bookkeeping reads/writes, target ChittyFinance (`https://finance.chitty.cc/api/*`)." | ||
| 2. **Mark `booksClient` deprecated** in `chittycommand/src/lib/integrations.ts` until/unless ChittyBooks deploys as its own service. Comment: `// @deprecated: books.chitty.cc does not resolve. Use financeClient for bookkeeping.` | ||
| 3. **Remove the registry-expectation assertion** for `chittybooks` (or change to "optional, present only if deployed"). | ||
| 4. **Add deploy gate** at the top of the plan: "This plan assumes ChittyBooks is a deployed bookkeeping API. As of 2026-05-27 that is false. Do not implement task 3+ until the deploy decision in `chittyfinance/docs/contracts/chittybooks-chittyfinance.md` lands." | ||
|
|
||
| ## Deploy gates | ||
|
|
||
| - [ ] PR against `CHITTYOS/chittycommand` to make `booksClient` lazy + null-returning when target is unset. | ||
| - [ ] No new caller of `booksClient` lands until ChittyBooks deploy choice is made. | ||
| - [ ] Mercury reads keep routing through `mercuryClient` (real); writes through `mercury-proxy` (real). | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Incorrect legacy repo identifier for ChittyLedger.
The contract points to
CHITTYOS/chittybooks, but this proposal set describes the legacy ledger collision asCHITTYOS/chittyledger. Keeping this as-is can send follow-up actions to the wrong repo.Suggested fix
Also applies to: 84-84
🤖 Prompt for AI Agents