You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
seed: on an organization-scoped load a reference to sys_user (owner_id: NAME) never resolves — the reference probe adds organization_id to a tenant-less object and swallows the refusal #33207
Found while converting a downstream app's demo book to standard seeds on the published 18.4.0 packages (downstream card: steedos-labs/os-project-blackcat-crm#55). Present on main at 64551047.
Symptom
On a per-organization replay or a sample-data reseed, a seed record that names a person (owner_id: 'Zhang Wei (Rep)') never resolves, even when that user exists with exactly that name. The load reports Deferred reference unresolved after pass 2 for every such reference, and the rows land owned by the installer / platform owner instead of the named person. On the single-tenant boot seed (no organization on the load) the same reference resolves (control).
Producer
packages/metadata-protocol/src/seed-loader.ts, resolveFromDatabase (the external-key probe loop, ~line 1805 onward):
constwhere: Record<string,unknown>={[probeField]: value};if(organizationId)where.organization_id=organizationId;// applied to EVERY target objectconstrecords=awaitthis.engine.find(targetObject,{ where, … });…}catch{// Target object (or this probe's column) may not exist — try the next probe}
sys_user is tenant-less (no organization_id column; identity lives above the organization, membership in sys_member). The SQL driver refuses the unknown column (Find operation failed object sys_user … names a column the database could not resolve), the bare catch swallows it as "try the next probe", every probe fails the same way, and the reference stays unresolved.
Why it matters
Hosted sample data can never be dealt to named people: even if the people are provisioned first (by an admin import, or once #33113 is ruled), every owner reference in an org-scoped load resolves to nobody. It also hides the cause — the warning says "unresolved", not "the probe was refused".
Suggested fix shape (for the lane to judge)
Apply the organization filter only to targets that carry tenancy (the platform's tenancy table already adjudicates each platform object, cf. #14570); for a tenant-less identity target such as sys_user, scope instead through membership in the load's organization (sys_member), so a cross-org person is still never borrowed. And narrow the catch so a refused probe is reported as a refusal, not silently treated as "no match" (same family as #9071, closed, where the probe queried a column the target did not have).
Downstream evidence (steedos-labs/os-project-blackcat-crm PR #59, test/carbon-demo-data.test.ts): after signing up the demo people under the seeded names, seed-replayer(org, { reseed: true }) reports 14 Deferred reference unresolved after pass 2 for the owner references; the boot seed resolves the same names.
Dedupe: is:issue resolveFromDatabase organization (4 hits: #14570, #9071, #12753, #9165 — none on a tenant-less target under an org-scoped load). New. Dedupe words: seed reference sys_user organization_id · resolveFromDatabase owner_id · deferred reference unresolved replay.
Row: on an organization-scoped seed load, a reference to sys_user by name never resolves, even when the user exists. The probe adds organization_id to a tenant-less object and swallows the refusal, so the rows silently land owned by the installer. That is wrong-running (p2).
Raise (+1 → p1), a real user can hit it: a downstream app on published 18.4.0 hit it.
Claim: PM loop round 17, card thread · 2026-11-14T04:03Z · p1 per triage 6870470576 (a downstream app on published 18.4.0 hit it); one card thread with #33206, same seed loader
Session: session_01L9qibzSAkofrHDU1U8yFeA
Account: os-tesla
Branch: claude/project-thread-e226dr
Worktree: objectstack-issue-33207
Domain: domain:server
Seat: domain:server#7
File surface: packages/metadata-protocol/src/seed-loader.ts and the replayer or install path that builds the seed write context (named in the shape note before code); packages/spec/src/kernel/execution-context.zod.ts only if the shape note names it and the seat accepts it; their tests; the seed docs page that states the reference rule or the seed context; one root changeset. Stop on breach.
Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; PM default), effort: high
Clause-②: yes (widening) — references that never resolved on an org-scoped load resolve, and system hooks see the load's own organization; any narrowing is named in the shape note before code
Metadata-protocol: none — unless the change lands in an authorable spec schema, stated before code
Responsibility: the seed loader's reference probe and seed write context (metadata-protocol, seed-loader.ts), objectstack | a downstream app shipping sample data as standard seeds | every org-scoped seed load (per-organization replay, install, reseed, boot under single)
Thread-read: 6870470576
Premise: re-measure at origin/main by a run first — the unresolved sys_user reference with its swallowed refusal, and the seed write context a system hook sees, each against its control
Serial constraints cleared: this seat's #31699 works in metadata-protocol migrations and plugin.ts, not the seed loader; no open PR names seed-loader.ts at claim time
Claim amendment to this seat's claim 6871410059 on #33207 · 2026-11-14T04:13Z: the seat accepts the shape note out/33207-33206-proposed-shape.md (measured at origin/main6e1ab9b), one PR for #33207 and #33206, with these choices.
The seed write context carries the load's organization as tenantId, composed at the loader's call site: { ...SEED_WRITE_EXECUTION_CONTEXT, tenantId: org }. ExecutionContext has no organizationId key, and hooks read the organization from tenantId. SEED_WRITE_EXECUTION_CONTEXT is not changed, so packages/spec is untouched and Metadata-protocol: none stands.
The organization is the one the load stamps rows with, config.organizationId ?? fallbackOrgId, so the boot under single is covered. A load with neither a pinned nor a derived organization keeps today's context byte for byte.
Reference probes: a target that carries organization_id keeps today's filter. sys_user resolves only to a member of the load's organization, through sys_member. sys_organization resolves only to the load's own organization. An app-owned global object is probed without the filter. A platform-managed tenant-less table stays refused. A refused probe is reported once per target, field and code; it never refuses the load.
Clause-②: yes (narrowing). Under isolated / group, a hook's read made from a seed write on an org-scoped load returns the load organization's rows instead of every organization's. The widened members are named in prose: references that now resolve, and hooks that now see the load's organization. The contract review runs at CONTRACT_REVIEW_TIER, and the final head gets a full dogfood run.
The file surface becomes:
packages/metadata-protocol/src/seed-loader.ts;
a new packages/runtime/src/seed-org-scoped-references.integration.test.ts, and existing seed-loader unit pins only where they break;
content/docs/data-modeling/seed-data.mdx;
.changeset/33207-seed-org-scoped-references.md.
packages/spec, app-plugin.ts and cloud-connection are not touched.
{
"issue": 33207,
"status": "done",
"branch": "claude/project-thread-e226dr",
"pr": "https://github.com/objectstack-ai/objectstack/pull/33222",
"session": "session_01L9qibzSAkofrHDU1U8yFeA",
"premise_still_valid": true,
"summary": "One draft PR closes #33207 and #33206 (Fixes both). Reference probes are judged per target on an organization's load: tenant-carrying or undescribable targets keep the organization filter; sys_user resolves only through a sys_member row in the load organization (a non-member is never used); sys_organization answers only for the load's own; author-declared global objects are unscoped; other platform-managed tenant-less tables keep the old probe. A refused probe is recorded, said once per target/field/code and named in the pass-2 error; the load is never refused for it. Every seed write of a load carries tenantId = config.organizationId ?? fallbackOrgId (the card's organizationId key reaches no hook: ExecutionContext has none); SEED_WRITE_EXECUTION_CONTEXT unchanged; a load with no organization uses the frozen SEED_OPTIONS bag. Clause-② yes (narrowing): under isolated/group a hook's ctx.api read on a seeded row is now scoped to the load organization. Owes a contract review at CONTRACT_REVIEW_TIER from the new review agent.",
"tests": "New packages/runtime/src/seed-org-scoped-references.integration.test.ts (real AppPlugin replayer and single inline seed, SeedLoaderService, ObjectQL, SqlDriver): 12/12 at 3c36f9c6e. Six ablations via ablation-replace.mjs + OS_SKIP_DTS=1 build + ablation-dist-preflight (marker present in 2 built files): A membership ignored -> non-member pin red; B tenant filter dropped -> CONTROL tenant + refused-probe pins red; C bare catch -> refused-probe pin red; D own-org filter dropped -> org-table pin red; E write context without org -> 4 hook pins red; F sys_user probed with org filter (main) -> member + non-member pins red. Each restored (blob == HEAD 337629ad067a, git diff HEAD empty); restore leg rebuilt, all markers --absent, 12/12 green. metadata-protocol vitest: 338 files passed/7 skipped, 22810 tests passed/36 skipped (pre-merge). After merging origin/main: runtime seed files (4) 29/29.",
"gates": "dispatch-gates --commands (94 lines, stdin closed, exit codes on disk): 90 exit 0 first run; check:query-options-erasure 1 -> two new probe bags typed EngineQueryOptions -> 0; check:test-source-alias 1 -> module-top import of @objectstack/metadata in the pins -> 0; check:skill-examples 3 (spec declarations missing) -> 0 after dts build; check-plugin-teardown-shape --self-test 3 (fixture commit outside the shallow clone) -> 0 after fetching it; check:dual-build-cjs-loads 3 PREREQUISITE NOT MET (needs every dist/, a full pnpm build): NOT MEASURED, CI Lint & Repo Gates runs it. pnpm lint: exit 0. metadata-protocol typecheck + runtime typecheck (incl. check:test-typecheck): exit 0 after a declaration build. CI check-runs: in_progress at report time.",
"dogfood_battery": "3c36f9c6e · VERDICT command-exit 1 · Test Files 1 failed | 463 passed | 3 skipped (467) · Tests 6 failed | 3921 passed | 42 skipped (3969) · the red file test/rest-route-generation-tombstones.test.ts failed on TS7016 (my post-merge rebuild skipped spec declarations); rerun after a spec declaration build: 1/1 files, 10/10 tests passed. No failure reaches the diff.",
"mcp_calls": "8 — reads only: issue_read x4 (both cards and comments), list_pull_requests x1, pull_request_read get_files x3; 0 writes",
"api_writes": "3 — relay pr_create (#33222, assignee os-tesla); post-stamped comment on #33207; post-stamped comment on #33206",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed: AppPlugin's no-metadata basic-insert fallback writes seeds with SEED_WRITE_OPTIONS and no organization; it stamps nothing either, so it was left as is (Acceptance notes).",
"carrier: none · noted, not filed: platform-managed tables with no organization column other than sys_user/sys_organization keep a refused probe on an organization's load; now reported once, no scope rule invented."
]
}
One PR for both cards (#33207 and #33206): #33222, head 3c36f9c6e. It owes a contract review at CONTRACT_REVIEW_TIER (narrowing) from the new review agent before it can land.
Release: session_01L9qibzuZJBQY4wUQFJbkfx · seat domain:server#7 signs off on the maintainer's word 「所有座位当前任务处理完,合并后就下班」 (2026-11-14T04:35Z, project chat) and 「各座位自己的契约复审也暂停,等新的 review agent 上线后复审」 (2026-11-14T04:37Z) · back to pm:queue for the next claimant · 2026-11-14T06:11Z
Resume-from: claude/project-thread-e226dr@3c36f9c6edfb7f70ef9143b0e65bc1fb036d49d8
Claim: resume after the 04:35Z sign-off, card thread · 2026-11-14T15:22Z · the seat is back on duty for review and landing only (「你现在可以自己契约复审了。」 2026-11-14T14:17Z; 「车道座位应该使用pm-dispatch 技能审核和落地pr」 2026-11-14T14:32Z); one card thread with #33206, PR #33222
Session: session_01L9qibzSAkofrHDU1U8yFeA
Account: os-tesla
Branch: claude/project-thread-e226dr
Worktree: objectstack-issue-33207
Domain: domain:server
Seat: domain:server#7
File surface: unchanged — claim 6871410059 with amendment 6871524022; PR #33222 touches seed-loader.ts, its two tests, the seed docs page and one changeset. Stop on breach.
Container & model: L, mode:thread, model: default (dispatch-gates --tier: no path-derived mandate; PM default), effort: high
Clause-②: yes (narrowing), as declared on PR #33222 and reviewed at CONTRACT_REVIEW_TIER
Metadata-protocol: none
Responsibility: the seed loader's reference probe and seed write context (metadata-protocol, seed-loader.ts), objectstack | a downstream app shipping sample data as standard seeds | every org-scoped seed load
Thread-read: 6879156542
Premise: re-measured by the card thread on 41de6f5 (records 6878779800, 6879099931, 6879156542)
Serial constraints cleared: no other open PR of this seat names seed-loader.ts
Activity
objectstack-fleet commentedon Nov 14, 2026
triage#1 · hotlong · session_01Q4EjjMnyLqJdzuktEmRPXH
Path: none
Triage: first touch →
domain:server· p1 ·pm:queue· 2026-11-14T02:48Z⛔ Not a claim.
sys_userby name never resolves, even when the user exists. The probe addsorganization_idto a tenant-less object and swallows the refusal, so the rows silently land owned by the installer. That is wrong-running (p2).objectstack-fleet commentedon Nov 14, 2026
domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx
Claim: PM loop round 17, card thread · 2026-11-14T04:03Z · p1 per triage 6870470576 (a downstream app on published 18.4.0 hit it); one card thread with #33206, same seed loader
Session:
session_01L9qibzSAkofrHDU1U8yFeAAccount:
os-teslaBranch:
claude/project-thread-e226drWorktree:
objectstack-issue-33207Domain:
domain:serverSeat:
domain:server#7File surface:
packages/metadata-protocol/src/seed-loader.tsand the replayer or install path that builds the seed write context (named in the shape note before code);packages/spec/src/kernel/execution-context.zod.tsonly if the shape note names it and the seat accepts it; their tests; the seed docs page that states the reference rule or the seed context; one root changeset. Stop on breach.Container & model:
L,mode:thread,model: default (dispatch-gates --tier: no path-derived mandate; PM default),effort: highClause-②: yes (widening) — references that never resolved on an org-scoped load resolve, and system hooks see the load's own organization; any narrowing is named in the shape note before code
Metadata-protocol: none — unless the change lands in an authorable spec schema, stated before code
Responsibility: the seed loader's reference probe and seed write context (
metadata-protocol,seed-loader.ts), objectstack | a downstream app shipping sample data as standard seeds | every org-scoped seed load (per-organization replay, install, reseed, boot undersingle)Thread-read: 6870470576
Premise: re-measure at
origin/mainby a run first — the unresolvedsys_userreference with its swallowed refusal, and the seed write context a system hook sees, each against its controlSerial constraints cleared: this seat's #31699 works in
metadata-protocolmigrations andplugin.ts, not the seed loader; no open PR namesseed-loader.tsat claim timeobjectstack-fleet commentedon Nov 14, 2026
domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx
Claim amendment to this seat's claim 6871410059 on #33207 · 2026-11-14T04:13Z: the seat accepts the shape note
out/33207-33206-proposed-shape.md(measured atorigin/main6e1ab9b), one PR for #33207 and #33206, with these choices.tenantId, composed at the loader's call site:{ ...SEED_WRITE_EXECUTION_CONTEXT, tenantId: org }.ExecutionContexthas noorganizationIdkey, and hooks read the organization fromtenantId.SEED_WRITE_EXECUTION_CONTEXTis not changed, sopackages/specis untouched andMetadata-protocol: nonestands.config.organizationId ?? fallbackOrgId, so the boot undersingleis covered. A load with neither a pinned nor a derived organization keeps today's context byte for byte.organization_idkeeps today's filter.sys_userresolves only to a member of the load's organization, throughsys_member.sys_organizationresolves only to the load's own organization. An app-owned global object is probed without the filter. A platform-managed tenant-less table stays refused. A refused probe is reported once per target, field and code; it never refuses the load.Clause-②: yes (narrowing). Underisolated/group, a hook's read made from a seed write on an org-scoped load returns the load organization's rows instead of every organization's. The widened members are named in prose: references that now resolve, and hooks that now see the load's organization. The contract review runs atCONTRACT_REVIEW_TIER, and the final head gets a full dogfood run.The file surface becomes:
packages/metadata-protocol/src/seed-loader.ts;packages/runtime/src/seed-org-scoped-references.integration.test.ts, and existingseed-loaderunit pins only where they break;content/docs/data-modeling/seed-data.mdx;.changeset/33207-seed-org-scoped-references.md.packages/spec,app-plugin.tsand cloud-connection are not touched.objectstack-fleet commentedon Nov 14, 2026
domain:server#7 · os-tesla · session_01L9qibzSAkofrHDU1U8yFeA
os-dev-report
{ "issue": 33207, "status": "done", "branch": "claude/project-thread-e226dr", "pr": "https://github.com/objectstack-ai/objectstack/pull/33222", "session": "session_01L9qibzSAkofrHDU1U8yFeA", "premise_still_valid": true, "summary": "One draft PR closes #33207 and #33206 (Fixes both). Reference probes are judged per target on an organization's load: tenant-carrying or undescribable targets keep the organization filter; sys_user resolves only through a sys_member row in the load organization (a non-member is never used); sys_organization answers only for the load's own; author-declared global objects are unscoped; other platform-managed tenant-less tables keep the old probe. A refused probe is recorded, said once per target/field/code and named in the pass-2 error; the load is never refused for it. Every seed write of a load carries tenantId = config.organizationId ?? fallbackOrgId (the card's organizationId key reaches no hook: ExecutionContext has none); SEED_WRITE_EXECUTION_CONTEXT unchanged; a load with no organization uses the frozen SEED_OPTIONS bag. Clause-② yes (narrowing): under isolated/group a hook's ctx.api read on a seeded row is now scoped to the load organization. Owes a contract review at CONTRACT_REVIEW_TIER from the new review agent.", "tests": "New packages/runtime/src/seed-org-scoped-references.integration.test.ts (real AppPlugin replayer and single inline seed, SeedLoaderService, ObjectQL, SqlDriver): 12/12 at 3c36f9c6e. Six ablations via ablation-replace.mjs + OS_SKIP_DTS=1 build + ablation-dist-preflight (marker present in 2 built files): A membership ignored -> non-member pin red; B tenant filter dropped -> CONTROL tenant + refused-probe pins red; C bare catch -> refused-probe pin red; D own-org filter dropped -> org-table pin red; E write context without org -> 4 hook pins red; F sys_user probed with org filter (main) -> member + non-member pins red. Each restored (blob == HEAD 337629ad067a, git diff HEAD empty); restore leg rebuilt, all markers --absent, 12/12 green. metadata-protocol vitest: 338 files passed/7 skipped, 22810 tests passed/36 skipped (pre-merge). After merging origin/main: runtime seed files (4) 29/29.", "gates": "dispatch-gates --commands (94 lines, stdin closed, exit codes on disk): 90 exit 0 first run; check:query-options-erasure 1 -> two new probe bags typed EngineQueryOptions -> 0; check:test-source-alias 1 -> module-top import of @objectstack/metadata in the pins -> 0; check:skill-examples 3 (spec declarations missing) -> 0 after dts build; check-plugin-teardown-shape --self-test 3 (fixture commit outside the shallow clone) -> 0 after fetching it; check:dual-build-cjs-loads 3 PREREQUISITE NOT MET (needs every dist/, a full pnpm build): NOT MEASURED, CI Lint & Repo Gates runs it. pnpm lint: exit 0. metadata-protocol typecheck + runtime typecheck (incl. check:test-typecheck): exit 0 after a declaration build. CI check-runs: in_progress at report time.", "dogfood_battery": "3c36f9c6e · VERDICT command-exit 1 · Test Files 1 failed | 463 passed | 3 skipped (467) · Tests 6 failed | 3921 passed | 42 skipped (3969) · the red file test/rest-route-generation-tombstones.test.ts failed on TS7016 (my post-merge rebuild skipped spec declarations); rerun after a spec declaration build: 1/1 files, 10/10 tests passed. No failure reaches the diff.", "mcp_calls": "8 — reads only: issue_read x4 (both cards and comments), list_pull_requests x1, pull_request_read get_files x3; 0 writes", "api_writes": "3 — relay pr_create (#33222, assignee os-tesla); post-stamped comment on #33207; post-stamped comment on #33206", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed: AppPlugin's no-metadata basic-insert fallback writes seeds with SEED_WRITE_OPTIONS and no organization; it stamps nothing either, so it was left as is (Acceptance notes).", "carrier: none · noted, not filed: platform-managed tables with no organization column other than sys_user/sys_organization keep a refused probe on an organization's load; now reported once, no scope rule invented." ] }One PR for both cards (#33207 and #33206): #33222, head
3c36f9c6e. It owes a contract review at CONTRACT_REVIEW_TIER (narrowing) from the new review agent before it can land.objectstack-fleet commentedon Nov 14, 2026
domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx
Release: session_01L9qibzuZJBQY4wUQFJbkfx · seat domain:server#7 signs off on the maintainer's word 「所有座位当前任务处理完,合并后就下班」 (2026-11-14T04:35Z, project chat) and 「各座位自己的契约复审也暂停,等新的 review agent 上线后复审」 (2026-11-14T04:37Z) · back to pm:queue for the next claimant · 2026-11-14T06:11Z
Resume-from: claude/project-thread-e226dr@3c36f9c6edfb7f70ef9143b0e65bc1fb036d49d8
CONTRACT_REVIEW_TIER(Clause-②: yes (narrowing): under isolated/group, a hook's read from a seed write on an org-scoped load is scoped to that organization) from the new review agent, then ACCEPT and land.out/33207-33206-*.objectstack-fleet commentedon Nov 14, 2026
domain:server#7 · os-tesla · session_01L9qibzuZJBQY4wUQFJbkfx
Claim: resume after the 04:35Z sign-off, card thread · 2026-11-14T15:22Z · the seat is back on duty for review and landing only (「你现在可以自己契约复审了。」 2026-11-14T14:17Z; 「车道座位应该使用pm-dispatch 技能审核和落地pr」 2026-11-14T14:32Z); one card thread with #33206, PR #33222
Session:
session_01L9qibzSAkofrHDU1U8yFeAAccount:
os-teslaBranch:
claude/project-thread-e226drWorktree:
objectstack-issue-33207Domain:
domain:serverSeat:
domain:server#7File surface: unchanged — claim 6871410059 with amendment 6871524022; PR #33222 touches
seed-loader.ts, its two tests, the seed docs page and one changeset. Stop on breach.Container & model:
L,mode:thread,model: default (dispatch-gates --tier: no path-derived mandate; PM default),effort: highClause-②: yes (narrowing), as declared on PR #33222 and reviewed at
CONTRACT_REVIEW_TIERMetadata-protocol: none
Responsibility: the seed loader's reference probe and seed write context (
metadata-protocol,seed-loader.ts), objectstack | a downstream app shipping sample data as standard seeds | every org-scoped seed loadThread-read: 6879156542
Premise: re-measured by the card thread on 41de6f5 (records 6878779800, 6879099931, 6879156542)
Serial constraints cleared: no other open PR of this seat names
seed-loader.ts