> ## Documentation Index
> Fetch the complete documentation index at: https://internal.softcrum.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Founding Constitution — RECONSTRUCTION FOR VALIDATION

> This document is a reconstruction, not a recovered original.

> ⚠️ **This document is a reconstruction, not a recovered original.**
>
> The founding session that produced rules R1–R20 and the six port categories is not in this
> repository. Amendment A1, the standards and every ADR from 009 onward reference them, so the gap
> blocks TS-002 and the amendment itself.
>
> Daniel asked for a draft to validate rather than a gap to wait on. Every rule below is therefore
> tagged with where it comes from, so the review can be fast where it is safe and slow where it is
> not:
>
> | Tag             | Meaning                                                                                       | How to review                                       |
> | --------------- | --------------------------------------------------------------------------------------------- | --------------------------------------------------- |
> | **\[derived]**  | Stated or directly implied by material we have — the decision registry, the standards, an ADR | Confirm it matches your memory                      |
> | **\[inferred]** | Deduced from the stack, from how other rules are phrased, or from a rule that references it   | **Read carefully** — this is where I could be wrong |
> | **\[proposed]** | A gap I filled with judgement. No evidence either way                                         | **Decide** — accept, change, or delete              |
>
> Nothing here is binding until you validate it. When you do, this file's content moves into
> `AGENTS.md` and this document becomes the record of how it got there.

Status: **PROPOSED RECONSTRUCTION** · Date: 2026-08-17 · Validates: `AGENTS.md` §9

***

## Part 1 — The rules

### Architecture

**R1. Hexagonal architecture, without exception.** \[derived]
Domain and application code depends on ports. A vendor SDK imported outside an adapter is a defect.
*(Restated and extended by R21 in amendment A1, which is itself evidence that R1 exists in this form.)*

**R2. Ports exist only for named categories.** \[derived]
The list is closed. Inventing a category requires a constitution amendment reviewed line by line by
the owner. *(Amendment A1 exists precisely because this rule made adding `QueuePort` impossible
otherwise.)*

**R3. Adapter bindings are declared in a composition root.** \[derived]
Each module's `makeDeps(ctx)` is the only place a port is bound to an adapter. *(Named explicitly in
amendment A1's R21.)*

**R4. A new vendor or dependency requires an ADR before its first import.** \[derived]
The stack is closed by default. *(Referenced repeatedly across the drop as "the closed-stack rule".)*

**R5. The monorepo is the unit of delivery.** \[inferred]
One repository, one lockfile, one version. Cross-project imports go through workspace packages,
never relative paths across project boundaries.

### Multi-tenancy and data

**R6. Every tenant-scoped table carries `tenant_id` and `cell_id`.** \[derived]
`cell_id` is the seam for future regional partitioning; it is present from the first migration and
unused until multi-region ships. *(Both columns appear across the drop; the "cell-ready seams"
phrase comes from the original design conversation.)*

**R7. Row-level security is mandatory on every tenant-scoped table.** \[derived]
RLS is not a defence in depth on top of application checks — it is the boundary. An application bug
must not be able to cross a tenant.

**R8. Database access goes only through `getDb(tenantCtx)`.** \[derived]
There is no path to a connection that does not carry a tenant context. *(Named verbatim in
`standards/data.md`.)*

**R9. Uniqueness on tenant-scoped data always includes `tenant_id`.** \[derived]
Added to `standards/data.md` §2b on 2026-08-17. Listed here because it belongs with R6–R8 and a
future reader should find the tenancy rules together.

**R10. Never a Postgres enum for a business value.** \[derived]
Closed sets live in parametric tables with a stable `code`. *(`standards/data.md` §2.)*

**R11. Never a monetary amount without its currency.** \[derived]
`amount` BIGINT in minor units plus `currency_code`. Never floats. *(`standards/data.md` §3.)*

### Commands, events and state

**R12. One command, one transaction.** \[derived]
State change plus outbox event plus audit row commit together or not at all. *(ADR-017 ratifies
this as CQRS-lite, which implies the rule predates it.)*

**R13. Domain events leave only through the transactional outbox.** \[derived]
Direct publication is forbidden. *(`standards/events.md`.)*

**R14. Reads are projections with a documented rebuild procedure.** \[derived]
A projection that cannot be rebuilt is a second source of truth. *(ADR-017.)*

**R15. Every state change is audit-logged with a typed actor and a full diff.** \[derived]
*(`standards/data.md` §7, DEC-C2.)*

### API and access

**R16. Every endpoint declares exactly one permission.** \[derived]
`{module}.{resource}.{action}`. Permissions bundle into roles; roles attach to users and to
machines. *(DEC-D5, `standards/api.md`.)*

**R17. Our own surfaces consume only the public API.** \[derived]
Console, portal, widget and mobile go through `packages/api-client`. If our UI needs something the
API lacks, the API is incomplete. *(Called "the golden rule" in `standards/api.md`; DEC-F5.)*

**R18. Latency budgets are Definition-of-Done items.** \[derived]
Not targets, not aspirations. Verified by load tests in certification. *(DEC-D7.)*

### Process

**R19. Documentation is part of the change.** \[inferred]
Touching a schema or an endpoint without updating its spec is an incomplete change. A new decision
goes to an ADR, not to a code comment. *(Inferred from the documentation discipline visible
throughout the drop and from the repository's own CLAUDE.md.)*

**R20. Stop conditions are binding.** \[inferred]
When a task requires a decision not covered by a DEC, a standard or an ADR, the agent stops and
asks. It does not choose and proceed. *(Inferred from the existence of a stop-conditions list and
from how consistently the drop defers rather than guesses.)*

**R21. Per-module adapter binding.** \[amendment A1 — pending your approval]
Different modules may bind different adapters for the same port, with the rationale in that
module's spec.

***

## Part 2 — The six port categories

> ⚠️ **This is the weakest part of the reconstruction.** Amendment A1 tells us there are exactly six
> and that queues were not among them. It does not tell us what they are. What follows is deduced
> from the vendors the suite already uses and from what a port would need to exist for. Treat every
> line as a question.

| # | Category              | Contract                                                               | Adapters implied by the stack | Confidence                                                                                                                                   |
| - | --------------------- | ---------------------------------------------------------------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| 1 | **StoragePort**       | `put(path, bytes, opts)`, `get(path)`, `signedUrl(path, ttl)`          | Supabase Storage              | **\[inferred]** — retention exports write NDJSON to Storage, and that path goes through something                                            |
| 2 | **RealtimePort**      | `publish(channel, event, payload)`, `authorize(channel, subject)`      | Pusher                        | **\[inferred]** — Pusher appears in the sub-processor list and the outbox consumer list                                                      |
| 3 | **CachePort**         | `get`, `set`, `del`, `incr`, token buckets                             | Upstash Redis                 | **\[inferred]** — rate limiting, compiled-rule caches and catalog caches all name Upstash                                                    |
| 4 | **PaymentPort**       | `charge(amount, currency, method)`, `refund`, webhook verification     | Fintoc, Paddle                | **\[inferred]** — DEC-G4 names both behind one rating engine, which is what a port looks like                                                |
| 5 | **ObservabilityPort** | `log`, `metric`, `span`, all carrying `tenant_id` and `correlation_id` | BetterStack, Vercel           | **\[inferred]** — and `packages/observability` was specified as a port for exactly this reason                                               |
| 6 | **InvoicingPort**     | `issue(documentType, lines)`, `void`, status polling                   | LibreDTE for SII              | **\[proposed]** — LibreDTE appears in DEC-G4, but whether it earned a port category or lives inside the payment adapter is genuinely unknown |

**Categories 7 and 8** — `QueuePort` and `NotificationChannelPort` — come from amendment A1 and are
not in question.

### What I could not determine

* Whether **database access** is a port category or deliberately excluded. R8's `getDb(tenantCtx)`
  suggests the database is *not* behind a port — it is a direct, tenant-scoped dependency, which is
  a defensible choice given Drizzle is a query builder rather than a vendor SDK. **\[inferred]**
* Whether **authentication** is a port. Better Auth is configured, not called through an interface,
  which argues no. **\[inferred]**
* Whether the sixth slot is `InvoicingPort` at all, or something I have not thought of.

If any of these six is wrong, the fix is cheap right now and expensive after TS-002 defines
adapters against them.

***

## Part 3 — What is deliberately NOT reconstructed

**Standards v1.** Six standards in this repository are marked "v2" and say they amend a v1 baseline.
I propose **declaring the v2 documents self-contained and retiring the idea of a v1** rather than
reconstructing six more documents from inference. They already state their rules completely; the
"v2" label describes their history, not a dependency. If you remember a v1 rule that is missing,
adding it to the v2 document is a one-line change.

**ADR-001 … ADR-008.** Reconstructed as separate files, each with the same tagging as this document.
See [`../adr/`](/adr/overview) — they are numbered 001 to 008 and their status is
`Proposed (reconstruction)`.

**The `createInitiative` slice.** Reconstructed at
[`../slices/create-initiative.md`](/slices/create-initiative), since every task spec declares
an archetype and half of them declare that one.

***

## How to validate this

Read Part 1 first and only slow down on the four **\[inferred]** and **\[proposed]** rules — R5, R19,
R20 and everything in Part 2. The **\[derived]** ones you can confirm by recognition.

Then tell me, per item: *correct*, *wrong and here is the real one*, or *there was no such rule*.
Anything you say was never a rule gets deleted rather than kept "just in case" — a constitution with
invented rules is worse than one with gaps, because agents obey it either way.

Once validated, Part 1 moves into `AGENTS.md`, `AGENTS.md` §9 disappears, and TS-002 unblocks.

## Changelog

| Version | Date       | Change                                | Why                                                                                    | Author                 |
| ------- | ---------- | ------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- |
| 0.1.0   | 2026-08-17 | Initial reconstruction for validation | The gap blocks TS-002 and amendment A1; the owner asked for a draft rather than a wait | daniel + claude-opus-5 |
