The column.* family (6 verbs). Every verb enters through the one front door: it is capability-checked, permission-checked, scope-checked and audited before it runs (see Security & permissions). Each entry below lists its capability, risk class (which fixes the minimum autonomy level), the minimum caller permission, its availability tier, whether it is idempotent, and any outbound egress.
Addcolumn.add
| Property | Value |
|---|---|
| Capability | data.table.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Adds a column.
A COMPUTED column (type FORMULA/LINK/LOOKUP/ROLLUP) is is_computed (the GENERATED flag) and is NEVER
writable (INV-6) - a FORMULA column must carry a config.formula (the DB CHECK + this verb). The key
is the STABLE binding for Facts (C7) and is prototype-pollution-fenced; uq_data_column_table_key is a
friendly Conflict on a duplicate key.
Input { tableId, key, name?, type, config?, required?, isUnique?, isPii?, displayOrder? } (fields ending in ? are optional)
Call POST /v/column.add with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Deletecolumn.delete
| Property | Value |
|---|---|
| Capability | data.table.write |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Admin |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
Drops a column (its Facts cascade by the FK, migration 0026). A LINK column is
BIDIRECTIONAL: it deletes its auto-created reverse-LINK column (named in config.reverseColumnId, the
harvested Opbox contract - “deleting one deletes its reverse, in a $transaction”) in the SAME tx, so
the relation is never left half-defined. The reverse delete cascades its own Facts + edges (the 0026
entity_edge.source_column_id ON DELETE CASCADE keeps the derived edge index consistent, INV-2/C9).
Input { tableId, key } (fields ending in ? are optional)
Call POST /v/column.delete with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Hidecolumn.hide
| Property | Value |
|---|---|
| Capability | data.table.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Admin |
| Availability | On demand (MCP tier 2) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
Per-COLUMN visibility, recorded on the column’s own settings
(a hiddenForRoles array). This is per-column authz AT THE VERB (INV-11) - the render/read layer
reads settings.hiddenForRoles and omits the column for a caller in one of those roles. It is NOT a
second role map: the authz table stays the registry; this is a column-scoped render flag the verb owns.
hidden=false (or an empty roles) clears the hide. Folding the read here keeps the keyspace in one
place (no __hidden__ Fact, no parallel store).
Input { tableId, key, hidden?, roles? } (fields ending in ? are optional)
Call POST /v/column.hide with a JSON body; returns JSON (or { error, code }). Also exposed as the MCP tool column.hide.
Listcolumn.list
| Property | Value |
|---|---|
| Capability | data.column.read |
| Risk class | Read - autonomy L0 |
| Min. permission | Member |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | Yes |
| Egress | None - in-box (pure Postgres, no outbound call) |
| Render schema | Yes - drives an inline chat artefact and a GUI panel |
Lists the columns (schema) of a data-plane table. RLS-scoped + an explicit workspace predicate. Columns are ordered by display_order, then key. Each column carries its id, key, name, type, displayOrder, required, isUnique, isComputed (INV-6 boundary), and isPii (INV-7 flag). Computed columns (FORMULA/LINK/LOOKUP/ROLLUP) are marked isComputed=true and have no canonical Facts. PII columns are marked isPii=true (values stored encrypted).
Input { tableId } (fields ending in ? are optional)
Call POST /v/column.list with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Reordercolumn.reorder
| Property | Value |
|---|---|
| Capability | data.table.write |
| Risk class | Write - autonomy L1 |
| Min. permission | Member |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
where order is the array of column KEYS in the desired display order. Sets each
named column’s display_order to its index in the array. A key not on the table is an actionable
not-found (fail-closed before any write). Keys omitted from order keep their existing display_order
(a partial reorder is allowed; the array only re-ranks the keys it names).
Input { tableId, order } (fields ending in ? are optional)
Call POST /v/column.reorder with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.
Retypecolumn.retype
| Property | Value |
|---|---|
| Capability | data.table.write |
| Risk class | Destructive - autonomy L3 |
| Min. permission | Admin |
| Availability | Admin only (CLI/HTTP; never advertised over MCP) |
| Idempotent | No |
| Egress | None - in-box (pure Postgres, no outbound call) |
SCHEMA-ALTERING: changes a column’s storage type. Best-effort
coercion is the RENDER layer’s job (it reads the new type); this verb NEVER deletes or rewrites a Fact
(an un-coercible value is kept VERBATIM in data_fact.value - the canonical datum is never dropped).
The one destructive move it WILL refuse: retyping a STORED column to a COMPUTED type (or vice-versa)
while Facts exist - a stored->computed flip would orphan every Fact (a computed column has no Fact,
INV-6 + the 0027 trigger), so it is a hard reject naming the conflict (the data is preserved, not
silently destroyed). A FORMULA target requires config.formula (INV-6, same as column.add).
Input { tableId, key, type, config? } (fields ending in ? are optional)
Call POST /v/column.retype with a JSON body; returns JSON (or { error, code }). Not advertised over MCP (admin-only); reachable via the CLI and HTTP doors.