Reserved Column Inventory
Columns that exist on a table but have no write path yet. Implements P36.
Why this exists
Adding a column to an existing table is cheap. Adding it after the table is full of rows costs a backfill, a coordinated deploy, and a migration window. So when a feature spec describes a future capability that will need a column on an existing foundational table — break-glass, impersonation, email invitations, request correlation — we reserve the column at the table's creation migration. The column ships nullable, no write path, no consumer; the future feature lights it up.
This is a column-not-table addition pattern. It only applies to columns on existing foundational tables. New tables introduced by the feature itself (e.g., webhook_events for Layer 8 webhooks) are not reserved here — they ship with the feature that introduces them.
Inventory
audit_log
Owned by migrations/core/000001_init.up.sql. All four columns exist with partial indexes and are written by internal/core/audit/recorder.go.
| Column | Type | Reserved for | Write status |
|---|---|---|---|
request_id | UUID | Log/audit correlation across one HTTP request (Layer 1.1) | Written today. requestctx.RequestID middleware generates or accepts an inbound X-Request-ID; the audit recorder reads it via requestIDFromContext. |
action_context | TEXT | Tag rows as break_glass, impersonation, gdpr_operation, or normal (P15/P16/Layer 12) | Written today. Defaults to 'normal'; RequireBreakGlass middleware (Foundation 1B.11) overrides to 'break_glass' for the duration of an elevated session. 'impersonation' lights up with 1B.13; 'gdpr_operation' with the GDPR-erasure handler. |
break_glass_id | UUID | Logical FK to break_glass_sessions(id) (P15) | Written today. Foundation 1B.11 lit up the column. The middleware calls set_app_break_glass_session_id(<session.id>) after match; audit_log_insert reads the GUC into this column. Logical FK rather than hard FK so audit rows survive cascade-deletes of the parent (rare). |
impersonation_id | UUID | FK to a future impersonation_sessions table (P16, Foundation 1B.13) | Reserved. Same shape as break_glass_id, lit up when 1B.13 ships. |
The four indexes (idx_audit_request, idx_audit_context, idx_audit_breakglass, idx_audit_impersonation) are partial — they exclude rows where the value is NULL — so they cost ~zero until rows actually carry the values.
organization_memberships
Owned by migrations/core/000002_tenancy_rbac.up.sql. Three invitation columns reserved for the future email-invite flow.
| Column | Type | Reserved for | Write status |
|---|---|---|---|
invited_at | TIMESTAMPTZ | When the invite email was queued | Reserved. Today every membership is created directly (auto-link on first sign-in, or POST /v1/organizations/:id/members). The future "invite member" flow will set this. |
invited_by | UUID | User who issued the invite. ON DELETE SET NULL so the membership row survives if the inviter is later removed | Reserved. Same flow as above. |
accepted_at | TIMESTAMPTZ | When the invitee completed the invite flow | Reserved. A pending invite has invited_at set and accepted_at NULL. |
last_used_at (P35) was reserved up to Layer 1.10 and is now written as of Layer 1.11 — see reference/activity-tracking.md. It moves out of the reserved inventory once it has an active write path; the row above is here for historical context.
Tables that haven't shipped yet (forward-declared)
These are columns that will be reserved when the parent table lands — listed here so the migration author for the parent table doesn't have to rediscover them.
| Table | Column | Type | Reason |
|---|---|---|---|
appointments | appointment_template_id | UUID | Legacy field for a future migration from RestartiX's old system (P36 in patterns.md). Remove after Phase 4 if the migration window passes without use. |
When Layer 6 introduces the appointments table, that migration includes appointment_template_id UUID NULL (no FK — the source table doesn't exist in the new schema) and a partial index WHERE appointment_template_id IS NOT NULL if a query ever needs it.
How to use this catalog
When writing a new migration that adds a foundational table (auth, audit, infra), check whether any feature spec in apps/docs/features/ describes future state that would need a column on that table. If yes, reserve the column now — nullable, no FK that doesn't exist yet, no writer required. Add it to the inventory above with Reserved. status.
When writing a feature that lights up a reserved column, change the status from "Reserved." to "Written today." in the inventory and link the writer (handler, middleware, or repo). Update the relevant pattern doc cross-link if the column completes the pattern.
Don't reserve speculatively. A column reserved without a written-down spec ages into "what was this for?" comments. Every entry above traces back to a feature spec or a documented pattern.