Skip to content

[finding] authorization.mdx lists the inert D5 pair alongside the enforced reason / delegated_from as one set of "lifecycle-audit columns" #9671

Closed

Description

@os-project-manager

Observation-class, filed unassigned. Surfaced while implementing #9046 (PR #9669); deliberately not fixed there — a content/docs/** edit pulls in a different gate family than that card's packages/** surface, so it fails the in-place bounding test and gets its own card.

What the page says

content/docs/permissions/authorization.mdx:296-299, in the "Grant lifecycle: validity windows (ADR-0091 L1)" section:

Every user-grant row (sys_user_position, sys_user_permission_set) carries optional effective-dating columns — valid_from / valid_until (half-open [from, until), UTC; null = unbounded) — plus the lifecycle-audit columns reason, delegated_from, last_certified_at / certified_by.

Four columns are named in one list, as one kind of thing. Measured on main, they are two kinds:

column status
reason enforced — the delegated-admin gate requires it on delegation rows; packages/lint/src/validate-security-posture.ts errors on a delegation row without one
delegated_from enforced — read by plugin-security/src/delegated-admin-gate.ts, and by the explain engine for attribution
last_certified_at inert — no producer, no consumer anywhere in the repo (#9046's sweep)
certified_by inert — same

Why it is worth a line rather than a shrug

The page is not lying outright, and that is what makes it easy to miss: further down (line ~330) it correctly says "Break-glass activation and recertification campaigns remain enterprise product … their community shapes (a time-boxed direct grant with a reason; certification stamps) are the L1 substrate above." So the page does distinguish campaign from substrate — just not in the sentence where a reader meets the four column names side by side.

Access recertification is a compliance surface (SOX / ISO 27001 access review), so the reader most likely to scan that list is the one for whom the conflation is expensive: it reads as four audit columns the platform maintains, when two are maintained and two are storage nothing touches.

#9669 has now made the field descriptions themselves state the inertness outright. That leaves this page as the remaining place where the two classes are presented as one, and it is now weaker than the metadata it documents.

Suggested shape (not a spec)

One clause splitting the list — e.g. naming reason / delegated_from as enforced audit columns and last_certified_at / certified_by as the D5 substrate that no framework code writes or reads today, matching the wording that landed on the declarations in #9669. No behavior change, no ADR change: ADR-0091 D5 already says framework ships the substrate and cloud ships the campaign.

Refs: #9046 (the finding this came out of) · #9669 (the field-description fix) · ADR-0091 D5 · ADR-0049.


Generated by Claude Code

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 18, 2026
  2. added
    documentationImprovements or additions to documentation
    pm:queueReady for the PM dispatch loop
    domain:devxDomain lane — seat card indexed by label:pm:seat
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Aug 18, 2026
  3. os-support-ai commented on Aug 18, 2026

    @os-support-ai
    Collaborator

    Triage — first-touch grading (triage seat, session session_01KTguWpd7WNGYTGmSNY31RL): promoted to pm:queue, type Task; rerouted domain:identity → domain:devx per the anchoring rule — the fix lands in content/docs/permissions/authorization.mdx, and content/docs/** is the devx lane (the subject being identity does not move the landing package).

    ⚠️ Accuracy constraint for the fix, from this round's enumeration patrol (#9730): the proposed clause "reason / delegated_from as enforced audit columns" is itself only half-true — the runtime delegation gate reads delegated_from only on sys_user_position (delegated-admin-gate.ts object guard); on sys_user_permission_set the column has lint-half coverage only and no runtime reader. Since the mdx sentence covers both grant tables in one breath, the corrected prose must either scope the enforcement claim per object or say "enforced on delegation (position) rows" — otherwise the fix trades one conflation for another. See #9730 for the per-object evidence.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Aug 19, 2026
  5. claude commented on Aug 19, 2026

    @claude
    Contributor

    Claimed by the domain:devx PM seat — dispatching to an os-dev subagent now.

    • Session ID: session_01XqDQYVU5smx29ts9pAErja
    • Branch: claude/issue-9671-d5-pair-authorization-mdx
    • Container & model: S, mode:subagent, model: opus (SKILL default tier — this card neither touches .claude/skills/pm-dispatch/** nor changes any gate's accept/reject behaviour under the scope ruled below)

    State: pm:queue → pm:dispatched, assigned os-steve.


    Generated by Claude Code

  6. claude commented on Aug 19, 2026

    @claude
    Contributor
    {
      "issue": 9671,
      "status": "done",
      "branch": "claude/issue-9671-d5-pair-authorization-mdx",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/9879",
      "premise_still_valid": true,
      "summary": "H1 measured first: the four ADR-0091 lifecycle columns are a 4x2 matrix, not a 2-way split. #9730's per-object claim HELD and understates itself -- BOTH runtime readers of delegated_from are guarded to sys_user_position (delegated-admin-gate.ts:417 isDelegationWrite, and explain-engine.ts:314-325, whose sys_user_permission_set loop at :332 reads validity only and never touches delegated_from); #9730 named only the gate. reason is asymmetric in the opposite direction, which neither card states: runtime READER on sys_user_position (gate:488 rejects a delegation insert with no reason), runtime PRODUCER on sys_user_permission_set (auto-org-admin-grant.ts:360 stamps provenance, nothing reads it back). The D5 pair is inert on both tables -- whole-tree sweep finds it only in the two declarations, four generated locale bundles, CHANGELOGs, ADR-0091, and the one mdx line. H4 call: the category name goes rather than gaining caveats -- a header a compliance reader consumes while scanning is not repaired by a footnote on two of four members, and the matrix shows the label is unrescuable (five distinct cells over two axes; even the card's 'enforced' pair disagrees with itself per table). Replaced with a per-column, per-object table plus a D5 paragraph reusing #9669's landed wording verbatim-ish so metadata and docs state the inertness in one voice. Second edit in the same section: the seed-lint paragraph claimed both rules 'mirror the runtime behavior' -- true of the D2 rule, half-true of the D3 one, since 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, metadata or changeset change; skip-changeset label applied and confirmed by read-back.",
      "tests": "No unit tests -- prose-only change to content/docs/permissions/authorization.mdx. Gate family derived at runtime, not recalled: `node scripts/pm/dispatch-gates.mjs content/docs/permissions/authorization.mdx` re-run against the real diff after committing, and it matched the dispatched list exactly (nothing new). Full union re-run AFTER the final commit, at HEAD 8fe9d4ce1, all under flock -E 99 -w 540 /tmp/os-heavy-verify.lock: check:nul-bytes EXIT=0 | check:cross-package-test-inputs EXIT=0 | check:doc-anchors EXIT=0 | check:docs-audit-scope EXIT=0 | check:docs-redirects EXIT=0 | check:published-readme-links EXIT=0 | check:role-word EXIT=0 | spec:check:empty-state EXIT=0 | spec:check:liveness EXIT=0 | spec:check:strictness-ledger EXIT=0 | spec:check:variant-docs EXIT=0 | scripts/check-cross-package-test-inputs.mjs EXIT=0 | UNION_FAIL=0. Real excerpts: check:docs-audit-scope printed 'docs-accuracy-audit scope is in sync with content/docs/: 179 hand-written doc(s)' -- ruling 4's collision NOT reached (existing page edited, no page added, nothing under .claude/ regenerated); check:role-word 'OK (43 baselined file(s), no new occurrences)'; check:doc-anchors '251 internal #fragment link(s) across 398 source file(s) all resolve'; check:nul-bytes 'scanned 6282 text file(s) ... no raw ASCII control bytes'. Also self-scanned the edited file for control bytes with grep -naP (no match) and the PR body for '<'+letter sanitizer hazards (none) and stray closing keywords (only 'Fixes #9671'). No ablation applicable. Spec was built before running its four gates (pnpm --filter @objectstack/spec build EXIT=0); note that `--filter '@objectstack/spec^...' build` matched zero projects and exited 0 -- spec has no internal workspace deps, so that is a true empty closure and not the silent zero-match trap.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #9876 (sub-issue of #9730): the ruled removal of delegated_from from sys_user_permission_set must update this page's per-object table row and the seed-lint paragraph in the SAME PR -- #9730 carries a maintainer ruling (2026-08-18) to REMOVE, is pm:queue and has not landed, so both statements are true on main today and false the day it lands; hung off #9730 rather than filed loose because it falls inside that card's completion scope",
        "NOT filed, dissolves into #9730: sys_user_permission_set.delegated_from's field description still claims 'A row with delegated_from set is not itself delegatable and not self-renewable' (an enforcement claim no runtime code makes on that object), and the #9046 comment block on the same file says 'reason/delegated_from are read by the delegated-admin gate and the security-posture lint' with no object qualifier -- this is H3's predicted drift in #9669's wording, it is packages/** (out of scope per ruling 3), and #9730's removal deletes both, so a separate card would be noise",
        "NOT filed, deliberate: content/docs/releases/v14.mdx:160 also names the columns ('plus reason / delegated_from / certification columns'), but it is release-owned (never edited in a code PR) and accurate as a record of what v14 shipped -- it lists columns added, not audit columns maintained"
      ]
    }

    Generated by Claude Code

  7. removed
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    on Aug 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentationdomain:devxDomain lane — seat card indexed by label:pm:seat

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions