Skip to content

Commit 4b99386

Browse files
claude[bot]claude
andauthored
docs(permissions): state the four ADR-0091 lifecycle columns per column and per grant table (#9671) (#9879)
`authorization.mdx` introduced `reason`, `delegated_from`, `last_certified_at` and `certified_by` as one set of "lifecycle-audit columns". Measured on main they are not one kind of thing, and the axes are two, not one — the column AND the grant table it sits on: - `reason` — runtime-enforced on `sys_user_position` (the D3 delegation gate rejects a delegation insert without one); on `sys_user_permission_set` it is written by the platform (the auto-derived org-admin grant stamps provenance) and read by nothing. - `delegated_from` — load-bearing on `sys_user_position`: it is what makes a write a delegation, and both of its runtime readers, the gate's delegation branch and the explain engine's delegation attribution, are guarded on that object. On `sys_user_permission_set` it has the seed-authoring lint only and no runtime reader. - `last_certified_at` / `certified_by` — inert on both tables: no producer, no consumer, the ADR-0091 D5 substrate and nothing more. The category name goes rather than gaining a caveat: a header that already tells a SOX / ISO 27001 reader these are four audit columns the platform maintains is not repaired by a per-member footnote. The list becomes a small per-object table, and the D5 pair gets the wording that landed on the field declarations themselves so metadata and docs say the inertness in one voice. Also corrected in the same section: the two seed lint rules were described as mirroring runtime behaviour, which is true of the D2 rule but only half true of the D3 one — on `sys_user_permission_set` that lint has no runtime counterpart, and is the only enforcement `delegated_from` has there. Docs-prose only. No behaviour, ADR, or metadata change. Co-authored-by: Claude <noreply@anthropic.com>
1 parent 460d7aa commit 4b99386

1 file changed

Lines changed: 32 additions & 6 deletions

File tree

content/docs/permissions/authorization.mdx

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,32 @@ details: **[Explain Engine](/docs/permissions/explain)**.
295295

296296
Every user-grant row (`sys_user_position`, `sys_user_permission_set`) carries
297297
optional **effective-dating columns**`valid_from` / `valid_until` (half-open
298-
`[from, until)`, UTC; null = unbounded) — plus the lifecycle-audit columns
299-
`reason`, `delegated_from`, `last_certified_at` / `certified_by`.
298+
`[from, until)`, UTC; null = unbounded).
299+
300+
ADR-0091 D1 declares four further nullable columns on both grant tables:
301+
`reason`, `delegated_from`, `last_certified_at`, `certified_by`. They are
302+
declared together and **enforced separately**, so they are listed here one by
303+
one rather than as one set of audit columns — what a value in any of them is
304+
worth depends on the column *and* on which grant table it sits on. Access
305+
recertification is a compliance surface (SOX / ISO 27001 access review), where
306+
"the platform maintains this column" and "the platform stores what you write
307+
here" are very different statements:
308+
309+
| Column | On `sys_user_position` | On `sys_user_permission_set` |
310+
|---|---|---|
311+
| `reason` | **Enforced at runtime.** The D3 delegation gate rejects a delegation insert whose row carries no non-empty `reason` — the dual-audit half described below. | **Written by the platform, read by nothing.** The org-admin grant auto-derived from a membership grade stamps its own provenance here; no gate, resolver or lint reads the value back. |
312+
| `delegated_from` | **Enforced at runtime, and load-bearing.** Stamping it is what *makes* a write a delegation: the gate requires it to name the writer and refuses to re-delegate a row that itself arrived by delegation, and the explain engine attributes the position "via delegation from X, until Y". | **Authoring lint only — no runtime reader.** Both readers opposite are guarded on `sys_user_position` (the gate's delegation branch, and explain's delegation attribution alike). A seed row that sets it must still carry a `reason` (below), but at runtime the column is provenance you record, not provenance the platform checks or acts on. |
313+
| `last_certified_at` / `certified_by` | **Inert** — the ADR-0091 D5 recertification *substrate*, storage and nothing more. | **Inert** — identically. |
314+
315+
The D5 pair is worth spelling out, because it is the pair a compliance reader
316+
is likeliest to over-read: no framework code writes either column and none
317+
reads either one, on either table — no resolution path, gate or lint consults
318+
them, and nothing derives "never certified" or "certification stale" from
319+
them. A null therefore means *the recertification workflow does not exist
320+
here*, not that the grant went unreviewed; a value means some client wrote one,
321+
and the platform checked nothing about it. Their field descriptions on both
322+
objects say the same in the same words, and ADR-0091 D5 is where that split
323+
was decided: framework ships the substrate, cloud ships the campaign.
300324

301325
Correctness lives in **resolution-time filtering, fail-closed** (ADR-0091 D2):
302326
a row outside its window simply stops resolving — in `resolveAuthzContext`,
@@ -307,10 +331,12 @@ unscoped `admin_full_access` grant no longer derives `platform_admin`.
307331

308332
The explain engine reports an expired-but-present row as a dedicated
309333
contributor state ("held until 2026-08-01 — expired"), so "why did access
310-
disappear" is self-answering. Two authoring lint rules mirror the runtime
311-
behavior: a seed grant whose `valid_until` is already past (or unparseable)
312-
is dead on arrival (error), and a delegation row (`delegated_from`) without
313-
`reason` breaks the dual audit (error).
334+
disappear" is self-answering. Two authoring lint rules cover seed grants: one
335+
mirrors D2 — a seed grant whose `valid_until` is already past (or unparseable)
336+
is dead on arrival (error) — and one mirrors the D3 dual audit: a delegation
337+
row (`delegated_from`) without `reason` is an error. The second runs on **both**
338+
grant tables, so on `sys_user_permission_set`, where no runtime gate reads
339+
`delegated_from`, it is the only enforcement that column has.
314340

315341
**Delegation of duty (职务代理, ADR-0091 D3)** builds on this substrate and is
316342
enforced today. A position opts in with `delegatable: true`; a holder may then

0 commit comments

Comments
 (0)