feat(security): ADR-0091 L2 — delegation of duty (职务代理) - #2810
Merged
Conversation
A holder of a delegatable position may hand off their OWN authority, time-boxed, WITHOUT being an administrator — the one self-service branch of the D12 gate. - spec: PositionSchema.delegatable (default false) + sys_position.delegatable - D12 gate: self-service branch on sys_user_position insert — approves iff the row is a well-formed delegation: delegated_from = writer, mandatory valid_until (future, within the 30-day ceiling), mandatory reason, and the writer holds the position DIRECTLY (validity-filtered; a delegated holding is not re-delegatable — chains cut). Insert-only → not self-renewable. A delegatable position distributing an adminScope set is rejected fail-closed (administration is never self-delegated — D12 containment). Dual audit: granted_by (writer) + delegated_from (authority source) - explain: buildContextForUser surfaces delegation provenance; the principal layer attributes a delegated position 'via delegation from X, until Y' - proof (ADR-0054): position.delegatable is a bound high-risk class with an end-to-end dogfood proof — a gated delegation write over the real HTTP API, then the delegate's grant resolving in-window and dying at valid_until via the real resolver - docs: authorization.mdx delegation section; ADR-0091 phasing L2 landed Tests: gate 38 (16 new delegation cases), explain 14, spec 6688, core 94, plugin-security 286 — all green; dogfood delegation-of-duty 5/5; liveness gate (bound proof resolves); role-word + authz-resolver guards pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
…gistry conflicts Both this branch and the merged ADR-0057 lifecycle work (#2791) appended a HIGH_RISK_CLASSES entry and extended the BOUND_PROOF_PATHS expectation: kept BOTH ('delegation' → position/delegatable, 'data-lifecycle' → object/lifecycle). proof-registry tests and the spec-liveness gate pass on the merged tree. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BNBzMWmSECrbiEDdVzwBt3
Contributor
📓 Docs Drift CheckThis PR changes 3 package(s): 98 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Jul 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
ADR-0091 L2 (delegation half). Builds on the L1 validity substrate: a holder of a
delegatableposition may self-service hand off their own authority, time-boxed — the one branch of the D12 gate open to a non-administrator.D3 — the self-service delegation branch
A non-admin (no
adminScope, no tenant-superuser wildcard) hitting the D12 gate is normally denied. L2 adds a branch: asys_user_positioninsert that stampsdelegated_fromis judged as a delegation and approved iff every invariant holds:delegated_from= the writervalid_until, future, ≤ 30-day ceilingreasondelegatable: truePositionSchema.delegatable, default false)adminScope-carrying setDual audit:
granted_by(writer) +delegated_from(authority source), both stamped/recorded.Explain (D3 rule 5)
buildContextForUsersurfaces delegation provenance; the principal layer attributes a delegated position "via delegation from X, until Y".Proof (ADR-0054)
position.delegatableis registered as a bound high-risk class with an end-to-end dogfood proof (delegation-of-duty): a gated delegation write over the real HTTP API, then the delegate's grant resolving in-window and dying atvalid_untilvia the realresolveAuthzContext— the L1 filter on the real persisted row. Access dies with no cleanup job.Tests
delegation-of-dutyLiveness gate: bound proof resolves.
check:role-word,check:authz-resolver,check-changeset-fixedall pass.Not in this PR
Break-glass activation workflow and recertification campaigns stay enterprise (D7); their community shapes are the L1 substrate. Early revocation of a delegation is an admin/expiry concern, not a self-service affordance in L2.
🤖 Generated with Claude Code
https://claude.ai/code/session_012oLzaP8n7A3YKFmgaHWC8H
Generated by Claude Code