Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ server/
app.ts Hono factory
worker.ts CF Workers entry (prod)
index.ts Legacy Express entry (standalone dev — kept for reference)
routes/ 22 resource-per-file modules. OpenAPI at /api/v1/documentation
routes/ 22 resource-per-file modules. Resource routes are mounted at /api/<resource> (no /v1 prefix). Only operational/meta routes (status, metrics, documentation) live under /api/v1/. OpenAPI spec at /api/v1/documentation is authoritative.
middleware/ auth (hybridAuth), tenant, error
storage/ SystemStorage — single source of DB access
db/ Neon HTTP connection
Expand Down
84 changes: 84 additions & 0 deletions docs/contracts/chittybooks-chittyfinance.md
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 |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Incorrect legacy repo identifier for ChittyLedger.

The contract points to CHITTYOS/chittybooks, but this proposal set describes the legacy ledger collision as CHITTYOS/chittyledger. Keeping this as-is can send follow-up actions to the wrong repo.

Suggested fix
-| ChittyLedger (legacy fork) | `CHITTYOS/chittybooks` | Express + React, in-memory, not deployed | n/a — DUPLICATE, candidate for retirement or repurpose as ChittyLedger-Evidence seed |
+| ChittyLedger (legacy fork) | `CHITTYOS/chittyledger` | Express + React, in-memory, not deployed | n/a — DUPLICATE, candidate for retirement or repurpose as ChittyLedger-Evidence seed |
...
-- [ ] `CHITTYOS/chittybooks` legacy fork is renamed, archived, or repurposed as ChittyLedger-Evidence — do not let it shadow the canonical surface.
+- [ ] `CHITTYOS/chittyledger` legacy fork is renamed, archived, or repurposed as ChittyLedger-Evidence — do not let it shadow the canonical surface.

Also applies to: 84-84

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/contracts/chittybooks-chittyfinance.md` at line 12, The table row that
lists "ChittyLedger (legacy fork)" currently references the wrong repository
identifier `CHITTYOS/chittybooks`; update that repo string to
`CHITTYOS/chittyledger` so the legacy ledger entry points to the correct
repository (look for the row containing "ChittyLedger (legacy fork)" and replace
the `CHITTYOS/chittybooks` token with `CHITTYOS/chittyledger`), and make the
same fix for the duplicate occurrence noted around the other mentioned line.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Point the legacy ledger row at the ledger repo

This row labels CHITTYOS/chittybooks as the legacy ChittyLedger fork, while the companion naming plan identifies CHITTYOS/chittyledger as the collision and rename target. Since the deploy gate later tells operators to rename/archive this legacy fork, following this contract could repurpose the ChittyBooks UI repo instead of the legacy ledger repo.

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).
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document an auth mode the API accepts

server/middleware/auth.ts only treats an Authorization: Bearer ... header as the service token (CHITTY_AUTH_SERVICE_TOKEN); ChittyAuth JWTs are accepted via the session cookie path, not as bearer tokens. If ChittyBooks sends a ChittyAuth Bearer token as this contract specifies, protected routes will return 401 before tenant resolution. Please document service-token auth plus the required caller header, or the cookie/JWT flow that actually works.

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) |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not promise unsupported transaction filters

GET /api/transactions currently reads only the optional limit query before calling storage.getTransactions(tenantId, limit), so date or account filters documented here will be silently ignored and the full tenant feed will be returned. If ChittyBooks builds list views or exports around these filters, users can see unrelated transactions for the selected date/account context; either document the current query shape or add the filters before advertising them.

Useful? React with 👍 / 👎.

| `/api/allocations` | Allocation rules + history |
| `/api/classification` | COA assignments + audit trail |
Comment on lines +42 to +43
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document the mounted allocation/classification endpoints

The code mounts allocation handlers only under /api/allocations/rules, /api/allocations/preview, /api/allocations/execute, and /api/allocations/runs, and classification handlers under subpaths plus /api/coa; there is no base GET/POST /api/allocations or base /api/classification handler. Because this table is labeled “verified mounted” and defines what ChittyBooks should consume, clients following it will hit 404s. Please list the actual mounted paths or explicitly mark these as prefixes.

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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Define the projection aggregator endpoints before relying on them

This says ChittyBooks reads ChittyLedger-Finance projection tables through ChittyFinance aggregator endpoints, but a route search only finds the current finance resources (/api/reports/consolidated, classification/allocation routes, MCP resources) and no mounted endpoint for financial_documents, financial_facts, or reconciliation_conflicts. A ChittyBooks UI built against this contract has no API path for the promised read-only projection data, so the contract should name existing endpoints or gate this section until the aggregators are implemented.

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.
70 changes: 70 additions & 0 deletions docs/contracts/mercury-multitenant.md
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.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not point Wave mapping at a nonexistent table

This contract makes integration_account_links the required persistence point for the Wave↔Mercury mapping, but a repo-wide search only finds that name in this new document and database/system.schema.ts defines integrations without any link table. Until a real table/metadata field is named or added, implementers cannot satisfy the non-negotiable mapping requirement and reconciliation work will be built against a storage object that does not exist.

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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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)
</details>

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 markdownlint-cli2 (0.22.1)</summary>

[warning] 14-14: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

</details>

</details>

<details>
<summary>🤖 Prompt for AI Agents</summary>

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/contracts/mercury-multitenant.md around lines 14 - 22, Add a language
tag to the fenced diagram block that begins with "Person (P-Legal, e.g. "IT CAN
BE LLC")" so it stops triggering markdownlint MD040; update the opening
triple-backtick to include a language identifier (e.g., ```text) for that
diagram block.


</details>

<!-- fingerprinting:phantom:triton:hawk -->

<!-- This is an auto-generated comment by CodeRabbit -->


- **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 |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the native Mercury webhook URL for HMAC

The HMAC-verified Mercury receiver is mounted at POST /api/webhooks/mercury/:tenantId; the base POST /api/webhooks/mercury path is the legacy ChittyConnect-normalized receiver authenticated only by service token. Registering Mercury webhooks against the base URL as documented here will bypass the per-tenant HMAC flow and fail for native Mercury event payloads.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Do not claim webhooks resolve legal persons

The native Mercury webhook implementation is keyed only by :tenantId and reads webhook:mercury:secret:${tenantId}, then resolves at most a local account by Mercury account ID; it never derives or persists legal_person_chittyid. For tenants with more than one Mercury-backed legal person, this documented per-business resolution cannot happen, so downstream reconciliation would lose the legal-person binding even if the tenant is identified.

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
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't promise transaction source filters

GET /api/transactions currently reads only the optional limit query and calls storage.getTransactions(tenantId, limit), which filters by tenant but not by source. In reconciliation contexts that use ?source=mercury, ?source=wave, or ?source=stripe as documented here, the API will return the full tenant transaction feed instead of the requested source-specific set.

Useful? React with 👍 / 👎.

- `GET /api/reports/reconciliation?tenant_id=...&period=...` — three-way diff (Mercury ↔ Wave ↔ Stripe)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Don't advertise an unmounted reconciliation route

The repo does not mount /api/reports/reconciliation; server/routes/reports.ts currently exposes only GET /api/reports/consolidated under /api/reports. If ChittyBooks implements this contract for the Mercury/Wave/Stripe diff, the documented request will 404. Either add the reconciliation route before publishing this contract or point the doc at an existing endpoint.

Useful? React with 👍 / 👎.

- `GET /api/integrations/status` — per-source connection health + last-sync timestamps
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Narrow the integration status contract

The mounted GET /api/integrations/status handler in server/routes/integrations.ts only returns per-provider configured booleans derived from environment variables; it does not read tenant integration rows or include lastSynced values. A ChittyBooks reconciliation UI following this contract will expect connection health and last-sync timestamps that the API never returns, so either the docs should describe the current shape or the endpoint should be extended before consumers depend on it.

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.
32 changes: 32 additions & 0 deletions docs/proposals/api-v1-prefix-fix.md
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.
38 changes: 38 additions & 0 deletions docs/proposals/ch1tty-connector-revision.md
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) |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the ledger write path Finance exercises

ChittyFinance's ledger client and its test post ledger entries to ${base}/api/entries, but this proposal tells the follow-up ChittyCommand work to write to https://ledger.chitty.cc/entries. If that connector follows this doc, its ledger writes will use a different path than the existing Finance integration and can fail against the currently exercised contract.

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).
Loading
Loading