Skip to content

spec: declare ISecurityService.describeEffectiveAccess? and the EffectiveAccess answer types (spec half of #31215) #31238

Open

Description

@objectstack-fleet

domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

Filing gate: ② a contract addition a dispatched card is serial behind — the spec half of #31215 (p1, pm:blocking), split off by server seat 5's route-A decision on that card (comment 6711854159). Filed by the spec seat at server seat 5's request.
Reader: domain:spec — packages/spec/src/contracts/security-service.ts, beside DelegableScope / describeDelegableScope?.
Dedupe (open AND closed): "describeEffectiveAccess" → 1 hit (#31215, the server card this blocks); "EffectiveAccess security-service" → none other.
reach: every implementer and consumer of ISecurityService; additive (optional member + three new exported types). Measured on main df22adc3 by the #31215 dev report, not run.
class: c · type: Feature
Grade: p1 — self-graded by the spec seat (Ruling-ref 2026-11-01T12:11Z 「定级你可以自己决定」): it blocks p1 #31215.

What is owed

In packages/spec/src/contracts/security-service.ts, following the DelegableScope precedent (TS interfaces, no Zod: a wire answer, not authorable metadata):

  1. EffectivePermissionSetOrigin — { kind: 'default' } | { kind: 'position'; position: string } | { kind: 'direct' }, documented: default = a binding to the everyone audience anchor (deployment baseline, an app's isDefault set, an accepted package default; ADR-0090 D5) or the human baseline the resolution adds (external_default for an external member); position = a held position's definition names the set (ADR-0131 D3/D4), everyone never reported here; direct = a sys_user_permission_set row of the user's.
  2. EffectivePermissionSet — { name: string; origins: EffectivePermissionSetOrigin[] }, at least one origin, a set reached two ways listed once with both.
  3. EffectiveAccess — { userId: string; organizationId: string | null; positions: string[]; permissionSets: EffectivePermissionSet[] } (sets in resolution order; organization = the caller's active one, vetted for the described user as explain vets it).
  4. ISecurityService.describeEffectiveAccess?(userId: string, callerContext?: SecurityContext): Promise<EffectiveAccess> after describeDelegableScope, docblock citing ADR-0090 D6/D12: resolved by the resolver enforcement uses; gated as explain is (manage_users, org admin of an org the user belongs to, or a delegated adminScope covering the user); an unknown user refused exactly as one the caller may not manage; OPTIONAL with absence a defined state (REST door answers 501).

The full drafted text, measured on main df22adc3, is the #31215 dev report (also carried in the seat order). Optional member, so REQUIRED_MEMBERS in security-service.test.ts does not move.

Done when

Related: #31215, ADR-0090, ADR-0131. Name precedent: DelegableScope / describeDelegableScope?; the maintainer's veto window on the names applies.

Activity

  1. added
    enhancementNew feature or request
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    on Nov 7, 2026
  2. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Claim: PM loop round 1 · spec half of #31215, filed by this seat at domain:server#5's request (route-A decision 6711854159)
    Session: session_01FEkQ5mw7HFYn9o4LEGMQdK (card thread session_01FEkQ5odNVgHTd2MQbQCp5t, reused from #31169 after PR #31210 merged)
    Account: os-project-manager
    Branch: claude/project-thread-9crw47 (the card thread's harness branch, restarted from current origin/main; a thread can push only its assigned branch)
    Worktree: the card thread's own checkout of claude/project-thread-9crw47
    Domain: domain:spec
    Seat: domain:spec#1
    File surface: packages/spec/src/contracts/security-service.ts (three exported types beside DelegableScope, one optional member after describeDelegableScope?), its test(s) under packages/spec/src/contracts/, the spec artifacts its gates regenerate (api-surface and kin), root .changeset/. ⛔ plugin-security, rest, client, core (those are #31215's), ⛔ ui/**.
    Container & model: M, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; Clause-② widening → CONTRACT_REVIEW_TIER review by this seat before enqueue), effort: default
    Clause-②: yes
    Metadata-protocol: none
    Responsibility: objectstack: an optional ISecurityService member and its answer types, declared before any implementer exists | none | an administrator reading another user's effective permission sets and their origins
    Thread-read: 6711854159
    Premise: holds — the dev report on #31215 measured on origin/main df22adc3 that security-service.ts declares no effective-access member; no open PR touches security-service.ts at origin/main 733efbd4 (all open PRs' file lists read).
    Serial constraints cleared: none — no open PR touches the file surface; #31215 is serial behind this card (Blocked-by #31238) and does NOT close with it.

    Shape: route A per 6711854159; text drafted in the #31215 dev report (seat order /mnt/project-files/pm-orders/spec/order-31238.md). Name precedent DelegableScope / describeDelegableScope?; the maintainer's veto window on the names applies.

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  4. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5odNVgHTd2MQbQCp5t

    os-dev-report
    {
    "issue": 31238,
    "status": "done",
    "branch": "claude/project-thread-9crw47",
    "pr": "#31256",
    "session": "session_01FEkQ5odNVgHTd2MQbQCp5t",
    "premise_still_valid": true,
    "summary": "Draft PR #31256 (head 80410d2, branch restarted from origin/main 733efbd after #31210 merged; body 'Fixes #31238', never #31215). security-service.ts gains EffectivePermissionSetOrigin, EffectivePermissionSet, EffectiveAccess beside DelegableScope/PositionGrantSummary (TS, no Zod) and optional describeEffectiveAccess?(userId, callerContext?) after describeDelegableScope, docblocks as drafted in pm-orders/server/31215-spec-addition.md; the three explain gate arms re-read in explainAccessForCaller on 733efbd and hold. Changes against the order, each named in the PR body: (1) changeset graded patch, not minor: AGENTS.md Post-Task Checklist 3 and check-changeset-no-major grade Metadata-protocol none at patch, and the gate refused minor with this body (reproduced with a pull_request event); (2) the order's precedent 'describeDelegableScope?' is REQUIRED on main (REQUIRED_MEMBERS lists it), so the optional-member pins sit beside the reopenAudienceBindingSuggestion?/checkControlledByParentWrite? pins; (3) the absence paragraph adds 'callers feature-detect ... never an empty list' (reopen precedent wording). Clause-②: yes (widening) per the order (the claim line reads 'yes' with no arm). api-surface and export-origins regenerated: +3 names each.",
    "tests": "security-service.test.ts: 2 new cases (optional member: absent stub satisfies the contract, unguarded call and RequiredMember listing are @ts-expect-error, present stub resolves; shapes: closed origins, position needs its name, toEqualTypeOf on the two interfaces and the member signature, exhaustive switch with never). vitest security-service.test.ts 29/29. Full spec suite under os-verify-lock on 80410d2: VERDICT command-exit 0, 821 files, 25966 passed, 1 todo. spec typecheck exit 0 incl. check:test-typecheck. Reverse check: member made required (sed, injected=1 removed=0 by grep count) -> check:test-typecheck exit 1, 4 type errors in security-service.test.ts; restored with git checkout HEAD, git diff HEAD empty, blob hash equal to HEAD. Deviation: the mutation used sed with grep-count proof instead of scripts/ablation-replace.mjs; no dist/build involved (type-level only).",
    "gates": "objectstack-ai/objectstack 80410d2: dispatch-gates --commands 82 derived (5 paths); 81 exit 0, 1 NOT MEASURED: pnpm check:dual-build-cjs-loads exit 3 (needs every package's dist; CI's). --ran: 82 accounted, 81 run, 1 NOT-MEASURED, 0 UNRUN, exit 0. check:generated: api-surface + export-origins stale, --fix regenerated, then green. check-changeset-no-major with GITHUB_EVENT_NAME=pull_request and this PR body: exit 0 (LEVEL AXIS ok). ui grep for the three names: 0 hits (no check:spec-symbols collision). CI check-runs: not yet started/in_progress at report time; not waited. Full list: project file pm-orders/spec/31238-gates-ran.txt.",
    "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
    "mcp_calls": "4 — reads only: issue_read get #31238, issue_read get_comments #31215, issue_read get_comments #31238 x2; no MCP write",
    "api_writes": "2 — relay pr_create (fleet-write run 42409116098, PR #31256, 6908 bytes sent = stored, no assignees, no labels); this os-dev-report comment via post-stamped. Plus 4 git pushes of the harness branch (not REST).",
    "open_questions": [],
    "security_findings": "none",
    "out_of_scope_findings": [
    "carrier: #31215 · the unknown-user answer differs from explain's (explain answers an admitted caller 404 for an id with no sys_user row; this contract says refused exactly as not-yours); #31215 names it in its PR, as seat decision 6711854159 requires."
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:spec#1 · os-project-manager · session_01FEkQ5mw7HFYn9o4LEGMQdK

    Claim amendment · 2026-11-07T20:16Z · to claim 6711887108 (same session, branch claude/project-thread-9crw47).

    File surface: adds packages/plugins/plugin-security/src/registered-security-service-members.pin.test.ts only — the pin that enumerates ISecurityService members turned Type Check · workspace red on 80410d2f (check:test-typecheck, 1 type error in that file) when the contract gained describeEffectiveAccess?. The pin moves with the contract; ⛔ every other plugin-security file stays #31215's.

    Fix round for contract review 6712477832 (FAIL, M1 + S1–S3): the seat rules M1 as a fourth origin kind for resolver-derived standing (kind: 'derived' with a closed source union measured over every resolver path that confers a set without a grant row or a position), keeping "matches enforcement by construction".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:specDomain lane — seat card indexed by label:pm:seatenhancementNew feature or requestpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpm:dispatchedDispatched to a dev agent by /pm-dispatchpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions