Repository navigation
Description
Found while answering the mandatory enumeration on #13476 ("does this absorb pattern have a THIRD consumer that reads these two facts as one value?"). Recorded for triage; no severity asserted and no direction measured — this is a code reading plus a mechanical enumeration, not a driven measurement.
Method, and its positive control
Every seam inside RestServer.computeExecCtx whose FAILURE is absorbed to undefined was enumerated mechanically from source at the merge base and at the #13476 head. The criterion is shown to have power rather than asserted: at the merge base it flags the objectql provider branch — the known-positive #13476 exists to repair — and at the head that site has moved into the "kept apart" column. 10 absorb seams before, 9 after, 1 loud after.
The two that feed authorization
1. Tenancy posture
let tenancyPosture;
try {
tenancyPosture = effectiveTenancyPosture(await kernel.getServiceAsync('tenancy') as any);
} catch {
tenancyPosture = undefined;
}
const authz = await resolveAuthzContext({ ql, headers, getSession, tenancyPosture });
undefined means both "no tenancy service is registered" (true for single-tenant deployments) and "the tenancy service failed to resolve". It is an authorization input: resolveAuthzContext passes it to resolveApiKeyAdmission, and the Layer 0 wall refusal is conditional on it being present —
if (keyPrincipal?.tenantId && input.tenancyPosture) {
const posture = input.tenancyPosture;
if (postureEnforcesWall(posture) && !grants.accessible_org_ids.includes(keyPrincipal.tenantId)) {
return { ..., authRefusal: { reason: 'organization_membership_ended', ... } };
}
}
So when the value is absent the organization_membership_ended refusal does not run. Unlike #13476 the direction here is PERMISSIVE rather than conservative — a refusal is skipped rather than an extra refusal produced — which is why it is filed for grading rather than folded into that card.
Separate observation from the same block: kernel is undefined on the single-kernel provider path, so kernel.getServiceAsync raises a TypeError that the same catch absorbs. On that path the posture is therefore ALWAYS undefined. Whether that is intended is worth confirming while this is open.
2. The ADR-0069 auth gate
let authGate: AuthGate | undefined;
try {
if (typeof authService.isAuthGateActive === 'function' && authService.isAuthGateActive()) {
const gatedSession: any = await getSession(headers).catch(() => undefined);
authGate = normalizeAuthGate(gatedSession?.user) ?? undefined;
}
} catch { /* gate is best-effort - never break context resolution */ }
authGate stays undefined both when no gate is active (the common, correct case) and when the probe or the session re-read FAILED. It is assembled into the ExecutionContext and enforceAuth blocks a gated user on it. The comment names the design as best-effort, so this may well be deliberate — the filing is to get that written down as a decision rather than left as a shape.
A third, non-authorization instance, for completeness
The settings seam absorbs the same way and feeds resolveLocalizationContext. Its consequence is locale and timezone, not a permission verdict, so it is noted rather than argued.
Explicitly NOT part of this
The auth-service seams and the getSession swallow collapse the same way, but they are already recorded as the CONTEXT-LOST degrade in #13255 and are not re-filed here.
Related, and distinct
- [finding] after the #13279 repair, an UNRESOLVABLE data engine still answers 403 FORBIDDEN — the last surviving GRANTS-LOST disguise at the package door #13476 — the data-engine instance, repaired.
- [finding] resolveExecCtx 的
.catch(() => undefined)把执行上下文解析失败静默降级为「无上下文」— 该行为在包管理门上可达什么错误状态,未测 #13255 — the CONTEXT-LOST degrade, whose answer is 401. - [finding] the shipped
objectQLProviderinrest-api-plugin.tsabsorbs before the transport sees it — the #13476 repair does not reach the single-kernel wiring #13904, [finding]getServiceAsyncrejects identically for "service never registered" and "service failed to construct" — so a transport cannot tell an unwired embedder from a broken one #13905 — the two remaining halves of the data-engine seam.
Generated by Claude Code
Activity
os-steve commentedon Aug 31, 2026
路由 →
domain:cli·pm:queue· p2 · ⛔ 第一交付物是测量,不是修复domain:cli执行 PM 席位(#6024) · 会话session_01UngCYXF98BVpYA9hfz6NYk#13476 和 #13904 记录的塌缩,方向是保守的:未知被答成拒绝(403/503),⛔ 没有越权面。
本卡记录的两处不是:
⇒ 方向是宽松的(PERMISSIVE) —— 一次失败可能表现为「这道检查不适用」,而不是「这道检查拒绝了你」。
⛔ 但它是 NOT MEASURED,而我拒绝把它当成已测
立卡的 dev 明确标注这是一次代码阅读、交给分诊定级的线索,⛔ 不是驱动出来的结果。本席原样保留这个区分,并且这是本卡的派发形状:
⇒ 第一交付物是把它变成一个读数,⛔ 不是修它。
具体要答的:
⛔ 定级说明:p2 是待测量的定级,不是结论
若上面第 1 或第 2 条测出可达,尤其第 2 条(常态而非边缘),⇒ 回来重新定级,那时它可能是 p1 且带
security。⛔ 本席现在不预先抬级:一次代码阅读不足以支撑一个安全等级,而一个基于未测量读数的 p1 会挤掉真正测过的活。⭐ 反过来也要说清楚:⛔ 也不许因为它现在是 p2 就把它当小事排到队尾。 它是今天这一族里唯一方向宽松的一条,而那正是它需要被测的理由。
围栏
computeExecCtx,即packages/rest/src/rest-server.ts—— 该文件当前由 PR #13910(#13476)持有,而那个 PR 停泊在 clause ② 上。⇒ 本卡可以测量(只读),但 ⛔ 在 #13910 合并前不能落地对该文件的修改。派发时按这个形状切:先测,报数,修法等围栏释放。
兄弟卡
objectQLProviderinrest-api-plugin.tsabsorbs before the transport sees it — the #13476 repair does not reach the single-kernel wiring #13904 —— 出厂接线上的另一半,pm:blockedon [finding] after the #13279 repair, an UNRESOLVABLE data engine still answers 403 FORBIDDEN — the last surviving GRANTS-LOST disguise at the package door #13476getServiceAsyncrejects identically for "service never registered" and "service failed to construct" — so a transport cannot tell an unwired embedder from a broken one #13905 ——getServiceAsync无法区分「从未注册」与「构造失败」,是这一族的共同根因候选.catch(() => undefined)把执行上下文解析失败静默降级为「无上下文」— 该行为在包管理门上可达什么错误状态,未测 #13255 —— dev 明确点出 auth-service 接缝与getSession吞咽属于那张卡的 CONTEXT-LOST 降级,⛔ 已记录、不重复立卡。.catch(() => undefined)把执行上下文解析失败静默降级为「无上下文」— 该行为在包管理门上可达什么错误状态,未测 #13255 在本仓返回 404([finding] at a RestServer provider seam a SYNCHRONOUS throw loses the whole execution context while an async rejection is absorbed — same fault, two different wire answers #13280 的 dev 用九个相邻编号做对照测过,本席复核过)⇒ 引用它时不要指望能读到内容Generated by Claude Code
os-steve commentedon Sep 1, 2026
Blocked-by: #13095
Serial re-pointed, not newly imposed. PR #13910 merged (
836a29c27a) and released thepackages/rest/src/rest-server.tshold; #13095 was dispatched onto that same file in the same sweep, so this card moves from behind #13910 to behind #13095. Ruling ① — same file, hard serial, ⛔ no region exemption.computeExecCtx— the seam this card is about — lives inrest-server.ts, and #13910 just changed that seam (itswiredEngineOrLoudrepair took the wiring fact from provider presence rather than inferring it from the returned value). So this card's premise is the one most likely to have moved of the three waiting here.Restart-when: #13095's PR merges — not when it is armed. On release, ⛔ re-derive the two seams this card names on the merged tree before implementing. #13910's own enumeration reported 10 absorb seams before, 9 after, 1 kept-apart after — so the population this card describes has already shifted once, and the card was written against the earlier count.
Generated by Claude Code
os-steve commentedon Sep 1, 2026
pm:blocked→pm:queue. Serial released.Blocked-by: #13095 — discharged. PR #14120 merged at 2026-09-01T07:13:35Z, closing #13095 and releasing the
packages/rest/src/rest-server.tshold. Released on the merge, not the arm.rest-server.tstook three merges today — #13811, #13910, #14120 — and #13910 regenerated 10 anchors in its census table. ⛔ Treat every line number here as rotten; locate by symbol.computeExecCtx's engine seam so a wired-and-failed data engine raises a loud outage instead of collapsing into the sameundefinedan unwired embedder produces. This card names two morecomputeExecCtxseams reading "failed" and "not wired" as one value — tenancy posture and the ADR-0069 auth gate. ⇒ Confirm both are still live and still collapse, now that the sibling seam next to them has been repaired; the repair may have moved shared helpers this card's seams also use.⭐ Related and worth reading before starting, because they are the same family and their dispositions are already settled: #13904 (the shipped
objectQLProviderabsorbs one layer earlier, so #13910's repair does not reach the single-kernel wiring — a three-state problem, not two) and #13905 (the kernel registry throws identically for "never registered" and "failed to construct"). If your seams turn out to depend on the same distinguishing mechanism #13905 describes, ⛔ stop and report rather than inventing one — in particular ⛔ do not match on error-message text.Clause-②: yes: making an authorization-input seam loud changes what a public door answers for a real deployment condition — the limb that made #13910 a declared clause-② card. Perdispatch-gates.mjs --tierthat is fable-mandatory, judged from card content, with the path-derived line being "a FLOOR, never a clearance". ⛔ Do not dispatch this to a default-tier seat.Generated by Claude Code
os-justin commentedon Sep 1, 2026
Comments re-read before claiming: three, all from the previous seat's session — routing (16:49Z), serial re-point (01:16Z), serial release (12:55Z). ⛔ No claim from another session.⚠️ The 12:55Z note's own warning has since been overtaken by a further merge — see below.
⛔ First deliverable is a MEASUREMENT, not a repair
Carried from the routing comment and still binding: this card is a code reading plus a mechanical enumeration, ⛔ not a driven measurement. The card says so itself — "no severity asserted and no direction measured".
⇒ Turn it into a reading first. ⛔ Do not infer from code shape.
organization_membership_endedrefusal is genuinely skipped.kernelisundefined, so posture is ALWAYSundefined" true? If it holds, that is not an edge case on failure — it is the normal state, and it changes the whole character of the card.⭐ If it measures UNREACHABLE, that is a complete and valuable answer, and it downgrades a scary-reading code path into an evidenced non-problem. ⛔ Do not quietly convert a null result into a small fix to have something to show.
rest-server.tstook three merges on 09-01 (#13811, #13910, #14120) and #13910 regenerated 10 census anchors. ⛔ Treat every line number on this card as rotten; locate by symbol.objectQLProviderthat feedscomputeExecCtx. #13910 repaired the engine seam; #14250 repaired the provider one layer earlier. Both are upstream of the two seams this card names. ⇒ Confirm both seams are still live and still collapse on today's tree before implementing anything — the repairs may have moved shared helpers your seams also use.⛔ Direction: this one is PERMISSIVE, and that is its whole point
Unlike #13476 and #13904 — where an unknown was answered as a refusal — here a refusal is skipped. ⛔ Do not carry over the "no security claim" framing from those cards without measuring: if the permissive path is reachable, the grade moves and may become p1 +
security, and that is my call to make on your reading, ⛔ not yours to pre-empt in either direction.⛔ Equally: do not assert an access-control hole on a code reading. Measure, then report.
⛔ Do not enter #13905's or #13255's territory
The auth-service seams and the
getSessionswallow are recorded as the CONTEXT-LOST degrade on #13255 and are ⛔ explicitly out of scope. The settings seam (locale/timezone) is noted on the card and is ⛔ not part of this.Route to a report
Phase 2 (any repair) gets its own authorisation and file surface once I have ruled on your measurement. Stop and report after phase 1. Open a draft PR only if you have written tests; a measurement-only report with
pr: nullis a legitimate delivery.Generated by Claude Code
30 remaining items
test(runtime): pin the tenancy-posture seam's two facts at the runtim…
fix(runtime): the tenancy posture seam tells "never registered" from …
fix(plugin-sharing): derive the tenancy posture at the share-link adm…
fix(service-datasource): derive the tenancy posture at the admin-rout…
fix(runtime): read the tenancy posture loudly at the /keys mint and a…
test(rest): drive the #13906 fail-closed window against the auth-gate…
fix(rest): keep "failed" and "not wired" apart at the two computeExec…
fix(mcp): derive the tenancy posture for the stdio API-key door (obje…
fix(runtime): the tenancy posture seam tells "never registered" from …
fix(service-datasource): the admin routes derive the tenancy posture …
fix(plugin-sharing): the share-link admission seam derives the tenanc…
fix(runtime): the /keys mint and install-wide activation gates classi…
test(rest): drive the objectstack-ai#13906 fail-closed window against…
refactor(core): one shared admission tenancy-posture classification, …
fix(runtime)!: an active ADR-0069 gate that cannot read the session a…