feat(spec): C2-α — explain 契约扩展到 record 粒度 (#2920)#2928
Merged
Conversation
Extend the ADR-0090 D6 access-explanation contract to record granularity (schema only; no engine or UI changes). Adds optional recordId on the request; per-record attribution (ExplainMatchedRule, ExplainRecordAttribution, the optional ExplainLayer.record, and top-level ExplainDecision.record) on the response; and reserves the ADR-0095 kernel-chain vocabulary for the β engine: the tenant_isolation Layer 0 id, ExplainLayer.kernelTier, and the AuthzPosture ladder on principal. Every new field is optional/additive — object-level requests parse unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 97 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.
C2-α — explain 契约扩展到 record 粒度
Contract-first slice of C2「记录级共享 debugger」 (tracking #2920). This PR extends the ADR-0090 D6 access-explanation contract (
@objectstack/spec) from object+operation+FLS granularity down to record granularity — schema/types only. No engine (explain-engine.tsuntouched) and no UI changes. That is C2-β, which this contract unblocks so framework and objectui can build in parallel.Contract design
Request side
ExplainRequest.recordId(optional) — explain one concrete record at row granularity ("why can user X do OP on record Y?"). Omitted = the pre-C2 object-level question, answered identically. Backward compatible.Response side — row-level attribution (populated only for record-grained requests)
ExplainMatchedRule(new) — one concrete rule that governed the record at a layer:kind∈ {tenant_filter,owd_baseline,ownership,record_share,sharing_rule,team,territory,rls_policy}, plusgrants(SharingLevel),via,predicate, andeffect(admits/excludes/neutral).ExplainRecordAttribution(new) — a layer's per-record determination:outcome(admitted/excluded/not_evaluated), effectiverowFilter,matchesRecord, matchedrules[],detail. Attached as the optionalExplainLayer.record.ExplainDecision.record(new, optional) — the row-level bottom line:recordId,visible,decidedBy(the decisive layer).Together these carry the β-phase "逐层展开" trace: permission set → 岗位 → 共享 → 行规则 → 有效行过滤 (contributors express set/position;
record.rules+rowFilterexpress share → row rule → effective filter).Reserved for the ADR-0095 kernel chain (optional/additive; β engine fills)
tenant_isolationlayer id — Layer 0, the always-first tenant wall (D1). The layer enum grows to ten ids.ExplainLayer.kernelTier(layer_0_tenant|layer_1_business) — lets a consumer answer "Layer 0 挡的还是业务 RLS 挡的" without hard-coding ids.AuthzPostureenum (PLATFORM_ADMIN>TENANT_ADMIN>MEMBER>EXTERNAL, D2) exposed as optionalExplainDecision.principal.posture.What β (engine + UI) gets to fill
explain-engine.ts: threadrecordIdthrough, emittenant_isolationlayer +kernelTierper layer, resolveprincipal.posture, and populate each layer'srecord(from the sharing service + RLS compiler outputs already used by enforcement — "explained by construction") and the top-levelrecordverdict.ExplainLayer.record.rules/rowFilterand therecord.decidedBysummary; posture/kernelTier drive the "tenant wall vs business RLS" and posture-tier framing.Verification
pnpm --filter @objectstack/spec build(typecheck + dts) ✓pnpm --filter @objectstack/spec check:api-surface✓ (new exports registered)@objectstack/specminor).Backward compatibility: every new field is optional or additive; existing object-level requests and reports parse unchanged.
Relates to #2920.
🤖 Generated with Claude Code
https://claude.ai/code/session_019QRUvVfpvSycAHMMF2xTxs
Generated by Claude Code