Skip to content

feat(core): resolveRequestExecutionContext, the one request-to-ExecutionContext composition; the runtime dispatcher's door calls it - #24248

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-23217-s1-core-request-context
Oct 18, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-23217-s1-core-request-context

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Part of #23217

Clause-②: yes (widening)
Ruling-ref: 6227928430

Slice 1 of #23217 under ruling record 6227928430 (letter A): the one request-to-ExecutionContext function in @objectstack/core, plus the runtime dispatcher's own door. #23217 remains open for the other four doors (rest-server.ts, service-settings, service-storage, plugin-hono-server), each a later slice with Clause-②: no. No door's outward behaviour changes, and there is no app-author surface.

What changed

  • @objectstack/core: new packages/core/src/security/request-execution-context.ts with resolveRequestExecutionContext, exported from core's . entry through security/index.ts, beside classifyAdmissionTenancyPosture. Its docblock states the two choices (the anonymous entry, the ADR-0069 gate) and every named parameter with the door that needs it.
  • @objectstack/runtime: resolveExecutionContext (packages/runtime/src/security/resolve-execution-context.ts) keeps what is its own (the service lookups, the header normalisation, the /mcp OAuth verification and scope interpretation) and hands the rest to the function. 209 lines out, 87 in.
  • Ruling item 4: packages/runtime/src/security/request-execution-context.conformance.test.ts, one row per converted door.
  • Unit tests: packages/core/src/security/request-execution-context.test.ts, 20 cases, one battery per choice and per parameter, each reading paired with its control.
  • Two files outside the claim's surface, both forced by the move (named for the seat to declare):
    • packages/core/src/security/authz-store-unavailable.test.ts: its transport ledger is set-equal to the files that call resolveAuthzContext({. That call moved from the runtime door into the new core module, which is ledgered propagates.
    • scripts/check-single-authz-resolver.mjs: check (2) required the runtime door to contain resolveAuthzContext, and after the move only its docblock prose would have satisfied that. DELEGATORS now names each delegator's call: rest and the new core module call resolveAuthzContext, and the runtime door calls resolveRequestExecutionContext. The self-test drives the non-delegating case for every row.
  • Changeset: minor on @objectstack/core. Runtime has none: no byte a reader sees moves.

The exported signature (for the seat's review of the new export)

This is verbatim except for one change: ‹ and › stand for the angle brackets, because the body sanitizer strips tag-shaped fragments. The byte-exact text is packages/core/src/security/request-execution-context.ts lines 84-166 and 214-221 at f5a955e12.

export type RequestAuthService = Pick‹
  IAuthService,
  'api' | 'getApi' | 'isAuthGateActive' | 'resolveUserAuthGates' | 'resolveApiKeyLifecyclePolicy'
›;

export interface EstablishedRequestSession {
  user: { id: string };
  session: { userId: string; activeOrganizationId?: string };
}

export interface RequestAdmission {
  credential: 'api_key' | 'session';
  authGate: AuthGate | undefined;
}

export interface RequestExecutionContextInput {
  headers: Headers;
  ql: unknown;
  authService: RequestAuthService | undefined;
  tenancy: TenancyServiceResolver | undefined;
  anonymous: 'fail-closed' | 'guest';
  authGate: 'withhold' | 'resolved' | 'reread-when-active';
  credentials: 'api-key-and-session' | 'session-only';
  sessionApi: 'optional' | 'required';
  session: 'request' | 'none' | { readonly established: EstablishedRequestSession };
  localization: 'withhold' | { readonly settings: () => unknown };
  requestLocale: string | undefined;
  accessToken: 'carry' | 'withhold';
  oauth: OAuthTokenProvenance | undefined;
  onAdmission: ((admission: RequestAdmission) => void) | undefined;
}

export function resolveRequestExecutionContext(
  input: RequestExecutionContextInput & { anonymous: 'guest' },
): Promise‹ExecutionContext›;
export function resolveRequestExecutionContext(
  input: RequestExecutionContextInput,
): Promise‹ExecutionContext | undefined›;

Every key is required, as on ExecutionContextAssemblyInput: a door with no value passes undefined or the withholding literal, so no door can drop an input by leaving it out. The function catches nothing around the resolver. An unreadable permission store, tenancy service or active-gate re-read (AuthzStoreUnavailableError, 503), and an API key its own lifecycle refuses (ApiKeyLifecycleRefusalError, 403), reach the door's own net.

Two notes on the name, for the seat. A runtime-internal helper of the same name was deleted in the GraphQL retirement (packages/spec/CHANGELOG.md, "the now-dead resolveRequestExecutionContext helper removed"). It was never exported, and this export is unrelated. The ruling's other name candidates are open if the seat prefers one.

H1: the door differences, counted before slice 1 was written (origin/main 7be3cc728)

Every door was read one step at a time: the session read, the ADR-0069 gate, resolveAuthzContext, the posture classification, the anonymous entry, and any extra fields.

# step rest rest-server.ts computeExecCtx runtime resolve-execution-context.ts settings settings-service-plugin.ts verifiedContextFromRequest storage storage-service-plugin.ts buildFileReadAuthorizer + buildAuthGateSessionReader hono current-user-endpoints.ts makeExecutionContextResolver named parameter
1 anonymous entry fail-closed (no context, 401) guest envelope + guest anchor grants fail-closed ({ enforced: true }) fail-closed ('unauthenticated') fail-closed (no context) anonymous
2 ADR-0069 gate key: the admission's posture; session: re-read only while isAuthGateActive(), a failed re-read is 503 withheld (the dispatcher's enforceAuthGate reads the session; a key's answer leaves through onAdmission) authz.authGate from the admitting read, no re-read its own gate reader: active check, key arm, session re-read, a failed re-read is 503 withheld (/me routes are allow-listed for a gated user) authGate
3 credential doors API key + session API key + session API key + session API key + session session only (headers: undefined to the resolver) credentials
4 no session API no context (the key is not honoured) resolves (the key is honoured) resolves no auth service means no authorizer (door-level); otherwise resolves no context sessionApi
5 session source the auth service's in-process read the same; on /mcp, a verified token's session, or none for an unverifiable JWT the same the same the same (it drops a session with no user.id) session
6 tenancy posture kernel getServiceAsync, or the provider the lookup thunk kernel getServiceAsync when present registry getServiceAsync when present none (the session's claim stands) tenancy (the resolution stays at the door, as #16013 left it)
7 admission channel none onAdmission none none none onAdmission
8 localization settings provider settings lookup none (no envelope) none (no envelope) withheld (the endpoint reads the cascade) localization
9 request locale header or ?locale (metaRequestLocale) Accept-Language none none none requestLocale
10 session bearer withheld carried none carried (the raw envelope) withheld accessToken
11 OAuth provenance none /mcp only none none none oauth

Count: 11 differences, and every one is a named parameter. Zero doors need the item-5 fallback, so the premise holds.

The remaining differences are door-side. They are not the composition, they stay at each door, and none of them is a copy:

  • How a door turns its transport's headers into a Web Headers. The function takes a Headers.
  • How a door reaches its services.
  • How a door answers a fault. The function propagates every fault.
  • What a door adds after assembly: rest adds __kernel, __authEnvironmentId, the per-request memo and the perf disclosure; hono adds the perf disclosure.
  • What a door does with the envelope: settings projects it to SettingsContext, and storage hands it to engine.find as context.

H2: the OAuth arm

The arm is split, measured as follows:

  • Stays at the door, ahead of the call. Verification, and reading the scopes in the AI vocabulary (scopesToAgentPermissionSets, MCP_OAUTH_SCOPE_ACTIONS). Both need @objectstack/spec/ai, which core deliberately does not depend on (see the OAuthTokenProvenance docblock in assemble-execution-context.ts).
  • Enters the function as two named parameters. session ({ established } for a verified token, 'none' for an unverifiable JWT bearer) and oauth.

#24137's pins, resolve-execution-context.oauth-organization.test.ts, are green and unchanged (15 of 15).

H3: the published surface

  • Core's . entry reaches the new export through export * from './security/index.js' (packages/core/src/index.ts:68).
  • No export census lists core's names. classifyAdmissionTenancyPosture appears nowhere outside src/: 0 hits in scripts/ and in package JSON files.
  • The published-surface gates the derivation named are in the gates section below.

H4: dependency direction

The new module imports:

  • core-internal modules;
  • @objectstack/spec/kernel and @objectstack/spec/contracts, types only;
  • @objectstack/types (inProcessSessionReadInput).

Both packages are already core dependencies, so no dependency was added. Nothing in the module imports runtime, rest or a plugin.

Ruling item 4: the conformance test

Each converted door is a row. A row gives the door's own entry and the named parameters it declares. Every request is driven through both the door and the function, and the two contexts must be equal. A principal must also be the same principal at every door. The five requests were chosen so that each parameter changes at least one answer: no credential; a gated session user with a bearer and Accept-Language; an API key; an ex-member's key under a walled posture; and an API key on a host with no auth service. A CONTROL case shows that the fixture tells the runtime door's parameters apart. Slice 2 adds its door as one more row.

Tests (HEAD f5a955e12, after merging origin/main af284b05e)

  • pnpm --filter @objectstack/core exec vitest run --project local: 104 files, 2551 tests passed.
  • The edited repo-project ledger, vitest run --project repo src/security/authz-store-unavailable.test.ts: 40 passed.
  • pnpm --filter @objectstack/runtime exec vitest run --project local: 413 files, 6318 passed and 19 skipped (VERDICT command-exit 0).
  • The door pins and the dispatcher pins that drive the door, with the verbose reporter, at 7667d9b4d before the merge (the post-merge full suite above includes them): 7 files, 111 tests passed. These are resolve-execution-context.test.ts (39), resolve-execution-context.oauth-organization.test.ts (15), the conformance test (7), http-dispatcher.mcp-oauth, http-dispatcher.tenancy-posture-outage, http-dispatcher.keys and in-process-session-renewal.pin.
  • pnpm --filter @objectstack/core typecheck and pnpm --filter @objectstack/runtime typecheck both exit 0. The test layers are held at their ledgers: core 4 files and 4 errors, runtime 27 files and 190 errors, unchanged.

Ablation of choice 1 (at 7667d9b4d). Declared direction: red. The mutation makes the function always take the fail-closed entry: return assembleExecutionContext(assembly); // ABLATION-23217-anonymous-entry.

  • Landing. Through scripts/ablation-replace.mjs: anchor 1 to 0, marker 0 to 1, blob e3a17add to 86ec4e1f.
  • No dist step. Runtime's vitest aliases @objectstack/core to ../core/src/index.ts, so no dist/ step applies.
  • Result. Core: 1 failed and 19 passed. The failing case is the guest-envelope case of choice 1. Runtime: 10 failed and 36 passed. Nine are door pins, among them "a sessionless request still yields the WHOLE guest envelope" and the tenancy "POSITIVE CONTROL". The tenth is the conformance CONTROL.
  • Restore. The blob is e3a17add again, equal to HEAD, and git diff HEAD is empty.
  • Reading. The conformance equalities themselves stay green under this ablation, by construction: both sides go through the one function. The CONTROL case and the unit battery are what catch a defect in the function.

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at f5a955e12 derived 84 commands. Results follow in the os-dev-report.

The lane blind spot, pnpm exec eslint --no-inline-config over the 7 changed .ts/.mjs files, exits 0 with no output. node scripts/check-single-authz-resolver.mjs --self-test and the gate itself both exit 0 ("all 3 entry points delegate").

Acceptance notes

  • Runtime door, one ordering difference no production lookup reaches. The door now looks up auth once, at the top. It used to look it up three times: two lookups absorbed their faults, and the gate-source lookup, which ran after the tenancy read, did not absorb a synchronous throw. Every answer is the same for a lookup that resolves or rejects. The answers differ only when the auth lookup throws synchronously and a tenancy service that failed to build is also present: the synchronous error now wins over the 503. The dispatcher's lookup (resolveService) never throws.
  • The card's text. runtime: one internal request-to-ExecutionContext resolver for the platform's five hand-rolled copies (rest, runtime, service-settings, service-storage, plugin-hono-server) — not a published export (ruling C′ on #23128, p3) #23217's title ("not a published export") and its scope item 3 ("No new published export") still read as ruling C′ wrote them. Letter A amended item 1 to an export from core's . entry. That is a text fix for the seat, not a code question.
  • Storage (slice 4). Storage hands the raw ResolvedAuthzContext to engine.find as context. The function's envelope adds isSystem: false and principalKind: 'human' and drops credential. Measured: no objectql, plugin-security or plugin-sharing source reads context.credential, and principalKind is read for 'guest' only (explain-engine.ts:149). Slice 4's pins decide whether the envelope may be passed instead.
  • Hono (slice 5). Hono's session read drops a session that has no user.id. The resolver reads user.id ?? session.userId, and no better-auth read answers without user.id. Slice 5 measures this.

Handover for slice 2 (rest)

  • The call. Convert computeExecCtx to call the function with anonymous: 'fail-closed', authGate: 'reread-when-active', credentials: 'api-key-and-session', sessionApi: 'required', session: 'request', localization: { settings: () => provider ? seamOrUndefined(() => provider(env)) : undefined }, requestLocale: this.extractLocale(req), accessToken: 'withhold', oauth: undefined and onAdmission: undefined.
  • The tenancy thunk. It stays rest's own: the kernel-or-provider branch.
  • Kept at the door. The if (!authService) return undefined lookup, the header normalisation, __kernel, __authEnvironmentId, the memo, the perf disclosure, and the catch with its lifecycle-refusal side channel.
  • Then. Move DELEGATORS' rest row to resolveRequestExecutionContext, add rest as a conformance row (runtime already depends on @objectstack/rest), and pin ADR-0096 and ADR-0069 per the ruling.

Generated by Claude Code

…stExecutionContext (WIP)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…tExecutionContext (WIP)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
…patcher door gives one context; changeset (WIP)

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QyoWKK9RwUNxBxmevy9Neb
@github-actions github-actions Bot added size/xl documentation Improvements or additions to documentation tests tooling labels Oct 18, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): @objectstack/core, @objectstack/runtime, touching 28 documentable anchor(s).

14 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/client-sdk.mdx (via getBookTree (sdk, the bare tail of client method meta.getBookTree, bound to GET /api/v1/meta/book/:name/tree), meta.getBookTree (sdk, the route ledger binds it to GET /api/v1/meta/book/:name/tree, selected by route anchor /book/:name/tree))
  • content/docs/automation/connectors.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/automation/hooks.mdx (via accessToken (symbol, a field of interface RequestExecutionContextInput))
  • content/docs/data-modeling/field-type-decision-tree.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/data-modeling/field-types.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/kernel/contracts/auth-service.mdx (via accessToken (symbol, a field of interface RequestExecutionContextInput), authService (symbol, a field of interface RequestExecutionContextInput), getApi (literal, a string literal in RequestAuthService), isAuthGateActive (literal, a string literal in RequestAuthService))
  • content/docs/kernel/events.mdx (via accessToken (symbol, a field of interface RequestExecutionContextInput))
  • content/docs/kernel/services-checklist.mdx (via authService (symbol, a field of interface RequestExecutionContextInput))
  • content/docs/permissions/authentication.mdx (via accessToken (symbol, a field of interface RequestExecutionContextInput), authGate (symbol, a field of interface RequestAdmission; a field of interface RequestExecutionContextInput))
  • content/docs/protocol/kernel/config-resolution.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/protocol/kernel/index.mdx (via resolveExecutionContext (symbol, a top-level function))
  • content/docs/protocol/objectql/types.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/protocol/objectui/concept.mdx (via api_key (literal, a string literal in credential; a string literal in onAdmission))
  • content/docs/ui/apps.mdx (via /book/:name/tree (route, bridged from symbol requestLocale — its route source's handler names it))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via /book/:name/tree (route, bridged from symbol requestLocale — its route source's handler names it))
  • content/docs/releases/v17/17-5.mdx (via /book/:name/tree (route, bridged from symbol requestLocale — its route source's handler names it))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 9 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 44 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json cbfa5e43354f430e35c4f9f24732e59848174889 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 3ef5c23d367d54e8bca8e47982609d3708f008bd — the merge of head f5a955e12a73e00e2d196e34ec16c0d1e529d12a into base cbfa5e43354f430e35c4f9f24732e59848174889, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3ef5c23d367d54e8bca8e47982609d3708f008bd && git checkout 3ef5c23d367d54e8bca8e47982609d3708f008bd
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin cbfa5e43354f430e35c4f9f24732e59848174889 f5a955e12a73e00e2d196e34ec16c0d1e529d12a && git checkout -B drift-repro cbfa5e43354f430e35c4f9f24732e59848174889 && git merge --no-ff f5a955e12a73e00e2d196e34ec16c0d1e529d12a

node scripts/docs-audit/affected-docs.mjs --json cbfa5e43354f430e35c4f9f24732e59848174889

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs cbfa5e43354f430e35c4f9f24732e59848174889 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT · PR #24248 at f5a955e12 (#23217 slice 1) · review of record for the Clause-②: yes (widening) export

domain:cli seat 2 · marchtian · session session_01QyoWKK9RwUNxBxmevy9Neb · 2026-10-18T02:53Z · holder of claim 6244297836 · Ruling-ref: 6227928430 (letter A). The dev's report is comment 6244895373 on #23217. The seat read the new module in full and reviewed against this PR, not the report's own account. Per dispatch-gates.mjs --tier, a Clause-②: yes declaration with no published-schema face is owed the lane seat's own review at TIER_DEFAULT; this record is that review.

  1. Shape. Draft → main. The first line is Part of #23217 (four doors remain), and there is no closing keyword. Clause-②: yes (widening) and Ruling-ref: 6227928430 are line-initial, and the assignee is marchtian. 8 files, +1055 / −231. check-governed-merges.mjs --pr 24248: NOT governed.
  2. The export (the review of record). The export is resolveRequestExecutionContext(input: RequestExecutionContextInput) from core's . entry (through security/index.ts, beside classifyAdmissionTenancyPosture, as letter A item 1 places it), with the types RequestAuthService, EstablishedRequestSession and RequestAdmission. Read against the letter and the card's amended scope:
  3. The runtime door, read on the diff. It keeps only what is its own (service lookups, header normalisation, the /mcp OAuth verification and scope reading) and calls the function: −209 / +86. Its 39 pins and finding(mcp/oauth): an OAuth-token MCP agent acting for an ordinary user is denied every object operation — the D10 delegator is resolved with no active organization, so the user's organization-scoped grants do not count #24137's 15 OAuth pins stay green unchanged. The ruling-item-4 conformance test (request-execution-context.conformance.test.ts, 7 cases) has one row per converted door, with a CONTROL case. The dev notes that the conformance equalities stay green under an ablation of the function by construction, because both sides call it; the CONTROL case and the 20-case core unit battery are what catch a defect in the function.
  4. Evidence.
    • Ablation of choice 1 (always fail-closed), with the restore proven by blob: core "Tests 1 failed | 19 passed (20)" and runtime "Tests 10 failed | 36 passed (46)" (9 door pins plus the conformance CONTROL), in the declared direction.
    • Core local: 104 files, 2551 passed. Runtime local: 413 files, 6318 passed. Both typechecks exit 0 with their debt ledgers unchanged.
    • Gates: 84 derived, 83 run, all exit 0. One was NOT MEASURED: check:dual-build-cjs-loads (needs a whole-tree build), which is CI's. check:authz-resolver, check:published-files, check:dts-closure and check:lean-entry-closure all exit 0. ESLint --no-inline-config exited 0.
  5. Changeset, read sentence by sentence. @objectstack/core minor, Clause-②: yes (widening), as letter A prescribes.
    • The new-export bullet names the four exports above.
    • The two-choices bullet and the named-parameters bullet match the module.
    • "First caller … What that door answers is unchanged" matches the door pins.
    • "Faults propagate" matches the code.
    • No @objectstack/runtime changeset: its door's answers do not move, and the refactor is described where a reader looks for it.
  6. Cross-lane:
    • Engine seat: 6244304634 plus the addendum 6244918458 (authz-store-unavailable.test.ts's ledger row).
    • Spec seat: 6244914013 (scripts/check-single-authz-resolver.mjs's DELEGATORS). This re-points an existing gate's reader so it cannot pass vacuously; it is not a new gate.
  7. Accepted deviations:
    • the two out-of-surface files, as in item 6;
    • the ablation of choice 1 instead of the gate, because the runtime door withholds the gate and a gate ablation could not redden any door pin;
    • the door's single auth lookup moved ahead of the tenancy read: identical answers for any lookup that resolves or rejects, and the dispatcher's resolveService never throws synchronously. This is in the PR's Acceptance notes;
    • the signature in the PR body written with guillemets, which the sanitizer requires;
    • origin/main moved after the merge, on unrelated rest files.
  8. Out of scope, noted: runtime: one internal request-to-ExecutionContext resolver for the platform's five hand-rolled copies (rest, runtime, service-settings, service-storage, plugin-hono-server) — not a published export (ruling C′ on #23128, p3) #23217's title ("not a published export") and its scope item 3 still read as ruling C′ wrote them, while letter A amended item 1 to an export from core's . entry. The ruling record 6227928430 governs. The card text is left to the director seat that wrote it, and later slices' orders quote letter A. The handover_slice2 list (REST's computeExecCtx, with fail-closed / reread-when-active / required / withhold) is the next slice's start.
  9. Landing. Ordinary queue landing once every check on f5a955e12 is green or a roster skip and no objection stands on [PM seat] domain:spec — ⏳ vacant #6017 or [PM seat] domain:engine — 🟢 os-project-manager #6367. Part of closes nothing: the seat posts the landed note on runtime: one internal request-to-ExecutionContext resolver for the platform's five hand-rolled copies (rest, runtime, service-settings, service-storage, plugin-hono-server) — not a published export (ruling C′ on #23128, p3) #23217, and slice 2 is claimed after this merges.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 18, 2026 03:20
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 18, 2026 03:20
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 18, 2026
Merged via the queue into main with commit f5ef054 Oct 18, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23217-s1-core-request-context branch October 18, 2026 03:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants