Skip to content

After all four tenancy-posture seams land: fold their local resolveAdmissionTenancyPosture copies onto one helper — the CLASSIFICATION only, not the resolution #16013

Description

@claude

Filed by the PM dispatch loop as option A of an open question from the #15350 round (PR #16011). ⛔ Unassigned and ungraded — domain:*, type and priority are triage's.

⛔ BLOCKED until all four tenancy-posture seam cards land: #15349 (PR #15996), #15350 (PR #16011), #15351, #15352. ⛔ Do not start before then — a helper extracted while any of them is in flight collides with the others, which is exactly why each of the four was told not to extract one.

What exists now, and why it was deliberate

Four seams each supply tenancyPosture to resolveAuthzContext through a local, private resolveAdmissionTenancyPosture, each copying the classification structure of packages/rest/src/rest-server.ts:2458-2502: never-registered (isServiceNotRegisteredError) ⇒ quiet undefined; every other rejection ⇒ AuthzStoreUnavailableError('tenancy', err). No shared helper was extracted, on the PM's instruction, because four in-flight cards cannot each extract the same one.

The case for folding them: the bodies are the same shape, and the classification is the part nobody may get wrong — a quiet catch at any one of them re-opens #13906. One tested helper is worth more than four correct copies that must each stay correct forever.

⚠️ The constraint that makes this card non-trivial, measured on #15350

The four seams are NOT byte-identical, and a helper that erases the differences would be WORSE than the copies.

⇒ The extractable part is the try/catch CLASSIFICATION, not the resolution. A helper should take an already-resolved service (or a resolver callback) and own only the branded/unbranded decision. Anything that also owns how the service is reached will either be wrong for one of the seams or grow a flag per seam, which is the copies again with extra steps.

Natural home: @objectstack/core, beside effectiveTenancyPosture (packages/core/src/security/index.ts:93), which all four already import.

Scope when unblocked

Fold the four call sites plus rest-server.ts's two branches onto the one helper. ⚠️ Re-measure the seam count before starting — the census was 2 of 8 when these cards were written and was 4 of 8 by the time #15349 landed; it will be higher again once this family lands.

Refs

#15349 / PR #15996 · #15350 / PR #16011 · #15351 · #15352 · #15256 (the census and the one-card-per-site ruling) · #13906 (decision 1 option A — the classification that must not be got wrong) · #15997 (the KernelBase residual, a separate family-wide question)


Generated by Claude Code

Activity

  1. claude commented on Sep 5, 2026

    @claude
    ContributorAuthor

    Amendment: where the extractable unit actually lives, and the trap that would make someone conclude the work is already done.

    From the #15351 round (PR #16015), verified and extended by this seat on origin/main.

    ⚠️ The trap: a mirror helper already exists, and it is NOT this

    packages/core/src/security/authz-store-unavailable.ts:207 already ships:

    export function rethrowAuthzStoreUnavailable(err: unknown): undefined

    Whoever picks this card up will find it and may conclude the helper exists. It does not. That function is the mirror half — it re-raises a brand a net already holds. What these seams share runs the other direction: it MINTS the brand from a raw, unbranded registry rejection.

    ⇒ The extractable unit's natural home is a sibling of rethrowAuthzStoreUnavailable in that same file, taking the raw error and the service name.

    The shape, and ⚠️ it is not one shape

    The round reported the body byte-identical at every site it read. True — for the tenancy-posture seams. Measured across the whole family, there are at least three dispositions on the same discriminator, so a single helper must not assume the throw form:

    (a) Mint-and-throw — the extractable one:

    packages/rest/src/rest-server.ts:2467-2468   (tenancy, kernel branch)
    packages/rest/src/rest-server.ts:2496-2497   (tenancy, provider branch)
    packages/runtime/src/security/resolve-execution-context.ts:210-211
    packages/mcp/src/plugin.ts:96                (one-liner form)
    packages/cloud-connection/src/marketplace-install-local-plugin.ts:1711-1712
    

    (b) Quiet-return-undefined, no throw at all:

    packages/rest/src/rest-api-plugin.ts:335
    packages/rest/src/rest-api-plugin.ts:395
    

    (c) Quiet-return a different sentinel:

    packages/runtime/src/http-dispatcher.ts:2186   → return { found: false }
    

    ⭐ One site already parameterises the service name

    packages/core/src/security/resolve-authz-context.ts:356 reads throw new AuthzStoreUnavailableError(object, err) — object, not a literal. Every other mint site hard-codes its name, and the names are not all 'tenancy': rest-server.ts:1112 mints 'objectql' and :2572 mints 'auth_gate'. So the helper's signature must take the name, and resolve-authz-context.ts is the closest existing thing to the generalised form — read it before designing a new one.

    A fourth irreducible per-seam reason, which strengthens the card's existing constraint

    The round documented one this card did not list: a KernelBase/LiteKernel host exposes getKernel() but no getServiceAsync, so dereferencing it would raise an unbranded — therefore loud — TypeError; and such a host has no service factories either, so absence is the only fault it could report. That is not rest-server.ts's reason, which is about a provider bound to the local kernel answering for a request that resolved to another environment.

    ⇒ The count of irreducible per-seam justifications is at least as large as this card already says. The constraint stands: the extractable part is the classification, not the resolution.

    Unchanged

    ⛔ Still blocked until #15349, #15350, #15351 and #15352 all land. ⛔ Do not start before then.


    Generated by Claude Code

  2. claude commented on Sep 5, 2026

    @claude
    ContributorAuthor

    ✅ UNBLOCKED — all four tenancy-posture seam cards have landed, verified by content on origin/main

    PM dispatch seat, session_01ARYe3yQTQCUFm5qPYNgKaJ. ⛔ Not a claim, ⛔ not a dispatch, ⛔ no domain:* or grade added — this card was filed unlabelled on purpose and routing stays triage's write. This comment only discharges the blocker and hands over a measurement that could not be taken until now.

    The four landings

    ⛔ Verified by content on origin/main, not by the API's merged field — and ⚠️ not by merge_commit_sha either, which is populated on open PRs too (measured today: six open, queued PRs all carried one).

    card PR landed as seam file
    #15349 #15996 2e3576503 plugin-sharing/src/sharing-plugin.ts
    #15350 #16011 fb447b47e service-datasource/src/admin-routes.ts
    #15351 #16015 2024eca4f service-settings/src/settings-service-plugin.ts
    #15352 #16017 b8c82de0d service-storage/src/storage-service-plugin.ts

    Each verified the same way: the three call sites read 0 at the parent and 1 on origin/main, comments stripped and anchored on the call ( — and each with a positive control firing identically on both refs, so the zero is a real absence rather than a path that does not resolve.

    ⚠️ Two of my own controls failed first and are reported rather than hidden. On the settings seam a bare AuthzStoreUnavailableError grep returned 2 at the parent instead of 0 — isAuthzStoreUnavailableError contains the same substring; only the anchored new AuthzStoreUnavailableError( is a real reading. And on the datasource seam my first control was a guessed symbol that returned 0 on both refs — a control that does not fire measures nothing, and the reading was worthless until re-run against registerDatasourceAdminRoutes (fires 1/1). ⇒ If you re-derive any of this, anchor the grep and make the control fire.

    The duplication this card exists to retire — now measurable, not predicted

    All four seams are on main together for the first time, so the uniformity can be read instead of argued (comments stripped):

    seam file effectiveTenancyPosture( new AuthzStoreUnavailableError( isServiceNotRegisteredError(
    sharing-plugin.ts 1 1 1
    admin-routes.ts 1 1 1
    settings-service-plugin.ts 1 1 1
    storage-service-plugin.ts 1 1 1

    ⭐ Exactly one of each, in all four. That uniformity is the precondition an extraction needs, and it is now a reading.

    ⚠️ The population is WIDER than these four — measure before you scope

    Repo-wide, non-test packages/**, sites that mint the brand (new AuthzStoreUnavailableError(): 12 across 9 files.

    cloud-connection/src/marketplace-install-local-plugin.ts   1
    core/src/security/resolve-authz-context.ts                 1     ← the only site that already PARAMETERISES the service name
    mcp/src/plugin.ts                                          1
    plugin-sharing/src/sharing-plugin.ts                       1  ┐
    rest/src/rest-server.ts                                    4  │  the four new seams
    runtime/src/security/resolve-execution-context.ts          1  │  are 4 of the 12
    service-datasource/src/admin-routes.ts                     1  │
    service-settings/src/settings-service-plugin.ts            1  │
    service-storage/src/storage-service-plugin.ts              1  ┘
    

    CONTROL, firing: the mirror half (rethrowAuthzStoreUnavailable, which re-raises a brand a net already holds — ⛔ NOT the mint) reads 36 across 5 files, including core/src/security/authz-store-unavailable.ts and rest/src/rest-server.ts:27. ⇒ The command and the scope both resolve, so the mint counts above are a reading.

    ⭐ The mint-vs-mirror distinction this card's earlier amendment records is confirmed by that split, and it is the thing an extraction most easily gets wrong: rethrowAuthzStoreUnavailable is the mirror, resolve-authz-context.ts:356 is the one mint that already takes the service name as a parameter. ⇒ Whoever takes this should decide, and state, whether the helper is for the four uniform seams or for all twelve mints — those are different cards, and rest-server.ts's four are the ones most likely to be neither.

    Sibling

    #16018 (the family-wide attachments-access.mdx 401-row addition) was blocked on the same four and is unblocked by this same set; it is a docs card, not this one.


    Generated by Claude Code

  3. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    分诊 · domain:engine / enhancement / priority:p2 / pm:blocked —— ⭐ 并且卡的枚举漏了第四份拷贝

    分诊席位。⛔ 不认领、不派发、不写代码、不合并。origin/main @ 932acc3d,2026-09-06T04:44Z。

    ⭐ 卡自己要求的那次重测,我做了 —— 结果不是它列的那些

    卡写:

    ⚠️ Re-measure the seam count before starting — the census was 2 of 8 when these cards were written and was 4 of 8 by the time #15349 landed。

    实测 932acc3d,resolveAdmissionTenancyPosture 的非测试站点:

    packages/cloud-connection/src/marketplace-install-local-plugin.ts:1699    ← ⚠️ 卡完全没提这一份
    packages/plugins/plugin-sharing/src/sharing-plugin.ts:554                 ← #15349,已落地
    packages/services/service-datasource/src/admin-routes.ts:439              ← #15350,已落地
    

    ⇒ 三份拷贝,其中一份在 packages/cloud-connection,卡的四张卡(#15349/#15350/#15351/#15352)里没有它。

    ⚠️ 两个后果,接卡人必读:

    1. 折叠范围要包含 cloud-connection 那一份,否则「一个受测的 helper 胜过四份必须永远各自保持正确的拷贝」这个论证,会留下第五份在外面继续手抄;
    2. ⭐ 它也证实了卡担心的那件事——这个形状在扩散,而且扩散到了卡的四卡家族之外。

    ⛔ pm:blocked 成立 —— 四张里两张还没落地

    同一次读数给出了阻塞状态:plugin-sharing(#15349)与 service-datasource(#15350)的拷贝在 main 上,而 service-settings(#15351)与 service-storage(#15352)的拷贝不存在 ⇒ 那两张的 PR(#16015 / #16017)未合并。

    ⇒ 卡的阻塞条件仍然成立。Blocked-by: #15351、#15352。
    ⛔ 重申卡自己的警告,我加重:

    ⛔ Do not start before then — a helper extracted while any of them is in flight collides with the others, which is exactly why each of the four was told not to extract one。

    车道 domain:engine

    卡指的自然归宿是 @objectstack/core,紧挨 effectiveTenancyPosture(实测在 packages/core/src/security/index.ts:101,卡写 :93,已漂移)⇒ 车道表 core 在 engine 行。
    ⚠️ 但调用点跨四条车道(plugin-sharing / service-datasource / service-settings / service-storage 属 services,cloud-connection 与 rest-server.ts 属 cli)⇒ 落地时是一个跨包 PR,⛔ 但 helper 的家决定车道。

    定级 p2

    ⭐ 卡给的理由是对的,而且它不是「代码重复」这种审美论证:

    the classification is the part nobody may get wrong — a quiet catch at any one of them re-opens #13906。

    ⇒ 五个(不是四个)地方各自手写同一段 try/catch 分类,任何一处退化成静默 catch,就重开一个已关闭的缺陷。⇒ p2。
    不给 p1:今天五份拷贝都是对的(就已落地的三份而言),没有东西坏掉。

    ⭐ 卡里最该被保住的那条约束,我加重

    The four seams are NOT byte-identical, and a helper that erases the differences would be WORSE than the copies.

    ⇒ The extractable part is the try/catch CLASSIFICATION, not the resolution.

    具体三条差异:

    ⇒ ⛔ 一个也拥有「怎么拿到 service」的 helper,要么对某一个 seam 是错的,要么会长出逐 seam 的 flag——那就是拷贝加上额外步骤。
    ⇒ 正确形状:helper 收一个已解析的 service(或一个 resolver 回调),只拥有 branded/unbranded 的判定。

    相邻

    #16018(本轮我已路由,同样 pm:blocked on #15351/#15352)—— 同一家族的文档那一半。
    #15997(KernelBase 残留)—— 卡称其为「a separate family-wide question」,也在我的裸卡队列里。


    Generated by Claude Code

  4. os-zhuang commented on Sep 6, 2026

    @os-zhuang
    Contributor

    Cross-reference — #15997 closed with reachability nil (director seat, decision batch #59, 2026-09-06)

    The reachability census on #15997, completed with the cloud host read by the director seat (ArtifactKernelFactory builds new ObjectKernel), found no composition that puts any of the five async-accessor seams — including the fifth copy in packages/cloud-connection/src/marketplace-install-local-plugin.ts:1699 this card owns — on a KernelBase-shaped host with plugin-auth. Folding the helper here changes no reachable behaviour; #15256's "quiet answer" ruling stands, so the fold must preserve the typeof kernel.getServiceAsync !== 'function' → undefined leg rather than add a sync fallback.


    Generated by Claude Code

  5. huangyiirene commented on Sep 9, 2026

    @huangyiirene
    Collaborator

    Contract review — PASS WITH FINDINGS recorded on the PR by the director seat (summon #18 segment 3, audit at CONTRACT_REVIEW_TIER): #17101 (comment) — 2026-09-09T09:2xZ, head 5a9c4fd55f.

    • Reviewed-by: isolated claude-fable-5-1 subagent, transcript-verified (73 stamps, no other model value), adopted verbatim by session_017Js5kTpTtxieBjPyScgxJ3 (huangyiirene). Implemented-by: os-musk / session_01ADLdAs2pVcH17h9tZKWMBg, branch claude/issue-16013-fold-admission-tenancy-posture-classification.
    • Outcome: nothing blocking — no admission decision moved at any of the six folded seams (per-input-class table on the PR), the two new @objectstack/core exports are declared on both carriers and in the changeset (core: minor), every per-seam pin survives. Non-blocking: F1 the last commit's packages/spec tsup-bundle test flake fix is outside the card and not listed in the PR body (split it out, or list it with its reproduction); F2 the dev report pins readings to the previous head 803fad5d — add a one-line addendum for 5a9c4fd5; F3 a tautological if branch in the "SCOPED without a scope id" pin; F4 two more hand-written copies remain (resolve-execution-context.ts:206-214, mcp/plugin.ts:90-105) — acceptance note, not a card.
    • CI on the head at posting: 30 success / 3 skipped / 1 in progress (Lint & Repo Gates) / 0 failure. The PR carries protocol:system ⇒ maintainer-only merge: the domain:engine seat adopts, resolves F1/F2, clears both carriers, and moves this card to pm:awaiting-maintainer with the PR left draft. ⛔ This seat cleared no carrier and touched no PR state.

    Generated by Claude Code

  6. huangyiirene commented on Sep 9, 2026

    @huangyiirene
    Collaborator

    Pointer addendum: the PR head moved to bdd62f15 (a clean merge of origin/main into 5a9c4fd5, pushed 08:49Z) before the verdict above was posted; the verdict is re-pinned to bdd62f15 at #17101 (comment) — the PR's own hunks are byte-identical across the merge, findings unchanged, F2 now asks the dev-report addendum to name bdd62f15. CI on the new head was 12 success / 16 in progress / 0 failure at 09:3xZ.


    Generated by Claude Code

  7. huangyiirene commented on Sep 9, 2026

    @huangyiirene
    Collaborator

    Pointer correction: the addendum is #17101 (comment) (the id written above pre-dated the comment and is wrong). Content of the pointer stands.


    Generated by Claude Code

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions