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 loader: an org-scoped reference probe filters on organization_id even when the target declares another tenantField #33393
Loose end of #33207 / #33206 (PR #33222, merged 2026-11-14T15:49Z), filed at seat 5's direction from area A4's parked review fixes. Read at origin/main 2026-11-14T18:2xZ.
Line
A seed reference whose target object walls its rows by a declaredtenancy.tenantField other than organization_id (e.g. workspace_id, injected column switched off) · the seed loader's org-scoped reference probe · rare (custom tenant field) · read, not measured.
packages/metadata-protocol/src/seed-loader.ts on main:
probeScopeOf returns organization for such a target (it is not global, because resolveTenantFieldName(definition) is non-null), and
the probe then filters where.organization_id = organizationId (around line 2832), a column the target does not have.
So the probe either errors or matches nothing, and the reference is reported unresolved although a row of that organization exists. The rest of the parked fix is already on main (exhaustive id-ordered member paging; a refused membership probe reported on sys_member; global only when no tenant column).
Do
In the organization scope, filter on the target's resolved tenant column (resolveTenantFieldName(definition), falling back to organization_id), not on organization_id always.
Pin it: a target declaring tenancy: { enabled: true, tenantField: 'workspace_id' } with the injected column off; a seed row referencing it by name resolves to the row of the seeding organization and not to another organization's row of the same name. Ablate.
Reader who acts: packages/metadata-protocol/src/seed-loader.ts (probeScopeOf and the reference probe), packages/runtime/src/seed-org-scoped-references.integration.test.ts.
Suggested grade: p3. Lane: domain:server.
Claim: area-lease pilot #33059, area thread A4 (data layer) · p3 (A4's p1/p2 are on hold, blocked on a decision, or done) · dispatched to an os-dev subagent of this area thread
Session: session_016WV4cw787kMeaX7oyz7DDC (area thread; subagent of it)
Account: os-elon-musk
Branch: claude/area-A4-33393
Worktree: objectstack-a4-33393
Domain: domain:server
Seat: domain:server#5
File surface: packages/metadata-protocol/src/seed-loader.ts (the org-scoped reference probe and probeScopeOf), packages/runtime/src/seed-org-scoped-references.integration.test.ts, one changeset. Stop on breach.
Container & model: M, mode:subagent, model: default, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: a seed reference to an object walled by a declared non-default tenancy.tenantField resolves within the seeding organization
Thread-read: none
Premise: re-measured at origin/main before any edit; premise_still_valid: false is a valid result
Serial constraints cleared: the area's other in-flight card (#33005) touches protocol-registry-hydration.ts / protocol-history-and-publish.ts, not seed-loader.ts
{
"issue": 33393,
"status": "done",
"branch": "claude/area-A4-33393",
"pr": "https://github.com/objectstack-ai/objectstack/pull/33409",
"session": "session_016WV4cw787kMeaX7oyz7DDC",
"premise_still_valid": true,
"summary": "Premise measured true on main 7e2eadd29: with a target declaring tenancy {enabled: true, tenantField: 'workspace_id'} and systemFields {tenant: false}, probeScopeOf classifies it 'organization' and the probe filters organization_id, which the SQL driver refuses (INVALID_FILTER, 'no such column: organization_id' on crm_workspace_tag), so the reference was reported unresolved although the load organization's row existed. Fix in packages/metadata-protocol/src/seed-loader.ts: probeScopeOf now returns a scope object whose 'organization' arm carries the resolved tenant column (resolveTenantFieldName(definition) ?? DEFAULT_TENANT_FIELD), and the probe filters where[tenantColumn] = organizationId; the sys_member membership probe (sys_member.organization_id), own-organization and global scopes are unchanged. Pinned in packages/runtime/src/seed-org-scoped-references.integration.test.ts (real AppPlugin replayer + SeedLoaderService + ObjectQL + SqlDriver/better-sqlite3): North and South replays each resolve 'Gold' to their own organization's row, with South's inserted first; changeset @objectstack/metadata-protocol patch, Clause-②: no, Metadata-protocol: none. Card assignee not touched.",
"tests": "Premise (main 7e2eadd29 dist, pin added): 1 failed | 17 passed, 'crm_deal.tag = Gold -> crm_workspace_tag.name not found; the probe was refused (INVALID_FILTER)'. After fix (99f60e1df, metadata-protocol rebuilt): seed-org-scoped-references 18/18. Ablation at 99f60e1df: ablation-replace.mjs anchor x1->x0, marker x0->x1, blob 8c5d2429->6fa7d5a4; ablation-dist-preflight marker present in 2 built files; pin red (1 failed | 17 passed); restore blob == HEAD 8c5d2429, git diff HEAD empty, preflight --absent: marker absent from all 24 built files, tree clean; pin green 18/18; git status --porcelain empty. A first ablation attempt used a comment marker that tsup strips: the preflight refused it (exit 1) before any verdict was read, so it was not counted. At merged head ae21eae6f: pnpm --filter @objectstack/metadata-protocol test -> Test Files 339 passed | 9 skipped (348), Tests 22848 passed | 43 skipped, VERDICT command-exit 0; metadata-protocol typecheck + runtime typecheck (incl. check:test-typecheck) VERDICT command-exit 0; runtime src/*seed*.test.ts (21 files) 201/202 passed, the one failure a 5000 ms timeout in seed-tenancy-autonumber-split ('[wiring] the app-plugin handoff fires on the organization insert itself') on the shared box, which passed 15/15 re-run alone; eslint --no-inline-config on the two changed source files exit 0. Narrowing declared: the full runtime package suite and other metadata-protocol dependents were not run locally (public surface unchanged: a private method's return type); CI Test Core runs them.",
"gates": "At ae21eae6f: node scripts/pm/dispatch-gates.mjs --commands derived 64 families; each run with exit captured before any pipe: 63 exit 0; pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, needs full pnpm build: NOT MEASURED locally, left to CI). --ran reconciliation: '64 derived famil(ies) accounted for — 63 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', exit 0. PR check-runs: in_progress at report time (not waited on, per contract).",
"dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm",
"mcp_calls": "0",
"api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, fleet-write run 43631269965, body read back 4952/4952 bytes identical, assignee os-elon-musk); os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/33393/comments). git push (empty branch probe, fix commit, merge commit) not counted.",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"class: a · reach: named producer — AppPlugin's per-organization seed-replayer on a walled boot (real AppPlugin + SqlDriver, measured on branch 99f60e1df with a temporary case, then removed) · evidence: a seed dataset OF an object walled by a declared tenancy.tenantField with systemFields.tenant false ({ object: 'crm_workspace_tag', mode: 'upsert', externalId: 'name', records: [{ name: 'Silver' }] }) makes replayer(ORG_NORTH) throw the driver's INVALID_FILTER ('A filter on object crm_workspace_tag names a column the database could not resolve'): SeedLoaderService.loadExistingRecords (packages/metadata-protocol/src/seed-loader.ts, per-tenant replay branch) filters the SOURCE object's existing rows by where.organization_id, the same hard-coded column this card fixed on the reference-target side; a fix also has to settle what the loader stamps on such rows (seedRowNeedsOrganization answers only for organization_id), so it is not a mechanical in-place fix · dedupe words: loadExistingRecords tenantField, seed replay workspace_id INVALID_FILTER, per-organization replay tenant column, seed source object organization_id filter",
"carrier: none · noted, not filed — seed-tenancy-autonumber-split.integration.test.ts '[wiring] the app-plugin handoff fires on the organization insert itself' hit the 5000 ms default timeout once in a 21-file run on the shared box; passed alone"
]
}
Activity
objectstack-fleet commentedon Nov 14, 2026
domain:server#5 · os-elon-musk · session_016WV4cw787kMeaX7oyz7DDC
Claim: area-lease pilot #33059, area thread A4 (data layer) · p3 (A4's p1/p2 are on hold, blocked on a decision, or done) · dispatched to an os-dev subagent of this area thread
Session:
session_016WV4cw787kMeaX7oyz7DDC(area thread; subagent of it)Account:
os-elon-muskBranch:
claude/area-A4-33393Worktree:
objectstack-a4-33393Domain:
domain:serverSeat:
domain:server#5File surface: packages/metadata-protocol/src/seed-loader.ts (the org-scoped reference probe and
probeScopeOf), packages/runtime/src/seed-org-scoped-references.integration.test.ts, one changeset. Stop on breach.Container & model:
M,mode:subagent,model: default,effort: highClause-②: no
Metadata-protocol: none
Responsibility: a seed reference to an object walled by a declared non-default
tenancy.tenantFieldresolves within the seeding organizationThread-read: none
Premise: re-measured at
origin/mainbefore any edit;premise_still_valid: falseis a valid resultSerial constraints cleared: the area's other in-flight card (#33005) touches protocol-registry-hydration.ts / protocol-history-and-publish.ts, not seed-loader.ts
objectstack-fleet commentedon Nov 14, 2026
domain:server#5 · os-elon-musk · session_016WV4cw787kMeaX7oyz7DDC
os-dev-report
{ "issue": 33393, "status": "done", "branch": "claude/area-A4-33393", "pr": "https://github.com/objectstack-ai/objectstack/pull/33409", "session": "session_016WV4cw787kMeaX7oyz7DDC", "premise_still_valid": true, "summary": "Premise measured true on main 7e2eadd29: with a target declaring tenancy {enabled: true, tenantField: 'workspace_id'} and systemFields {tenant: false}, probeScopeOf classifies it 'organization' and the probe filters organization_id, which the SQL driver refuses (INVALID_FILTER, 'no such column: organization_id' on crm_workspace_tag), so the reference was reported unresolved although the load organization's row existed. Fix in packages/metadata-protocol/src/seed-loader.ts: probeScopeOf now returns a scope object whose 'organization' arm carries the resolved tenant column (resolveTenantFieldName(definition) ?? DEFAULT_TENANT_FIELD), and the probe filters where[tenantColumn] = organizationId; the sys_member membership probe (sys_member.organization_id), own-organization and global scopes are unchanged. Pinned in packages/runtime/src/seed-org-scoped-references.integration.test.ts (real AppPlugin replayer + SeedLoaderService + ObjectQL + SqlDriver/better-sqlite3): North and South replays each resolve 'Gold' to their own organization's row, with South's inserted first; changeset @objectstack/metadata-protocol patch, Clause-②: no, Metadata-protocol: none. Card assignee not touched.", "tests": "Premise (main 7e2eadd29 dist, pin added): 1 failed | 17 passed, 'crm_deal.tag = Gold -> crm_workspace_tag.name not found; the probe was refused (INVALID_FILTER)'. After fix (99f60e1df, metadata-protocol rebuilt): seed-org-scoped-references 18/18. Ablation at 99f60e1df: ablation-replace.mjs anchor x1->x0, marker x0->x1, blob 8c5d2429->6fa7d5a4; ablation-dist-preflight marker present in 2 built files; pin red (1 failed | 17 passed); restore blob == HEAD 8c5d2429, git diff HEAD empty, preflight --absent: marker absent from all 24 built files, tree clean; pin green 18/18; git status --porcelain empty. A first ablation attempt used a comment marker that tsup strips: the preflight refused it (exit 1) before any verdict was read, so it was not counted. At merged head ae21eae6f: pnpm --filter @objectstack/metadata-protocol test -> Test Files 339 passed | 9 skipped (348), Tests 22848 passed | 43 skipped, VERDICT command-exit 0; metadata-protocol typecheck + runtime typecheck (incl. check:test-typecheck) VERDICT command-exit 0; runtime src/*seed*.test.ts (21 files) 201/202 passed, the one failure a 5000 ms timeout in seed-tenancy-autonumber-split ('[wiring] the app-plugin handoff fires on the organization insert itself') on the shared box, which passed 15/15 re-run alone; eslint --no-inline-config on the two changed source files exit 0. Narrowing declared: the full runtime package suite and other metadata-protocol dependents were not run locally (public surface unchanged: a private method's return type); CI Test Core runs them.", "gates": "At ae21eae6f: node scripts/pm/dispatch-gates.mjs --commands derived 64 families; each run with exit captured before any pipe: 63 exit 0; pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, needs full pnpm build: NOT MEASURED locally, left to CI). --ran reconciliation: '64 derived famil(ies) accounted for — 63 run, 1 NOT-MEASURED (1 DERIVED from a recorded exit 3)', exit 0. PR check-runs: in_progress at report time (not waited on, per contract).", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0", "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls, fleet-write run 43631269965, body read back 4952/4952 bytes identical, assignee os-elon-musk); os-dev-report comment via post-stamped.mjs (POST /repos/objectstack-ai/objectstack/issues/33393/comments). git push (empty branch probe, fix commit, merge commit) not counted.", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "class: a · reach: named producer — AppPlugin's per-organization seed-replayer on a walled boot (real AppPlugin + SqlDriver, measured on branch 99f60e1df with a temporary case, then removed) · evidence: a seed dataset OF an object walled by a declared tenancy.tenantField with systemFields.tenant false ({ object: 'crm_workspace_tag', mode: 'upsert', externalId: 'name', records: [{ name: 'Silver' }] }) makes replayer(ORG_NORTH) throw the driver's INVALID_FILTER ('A filter on object crm_workspace_tag names a column the database could not resolve'): SeedLoaderService.loadExistingRecords (packages/metadata-protocol/src/seed-loader.ts, per-tenant replay branch) filters the SOURCE object's existing rows by where.organization_id, the same hard-coded column this card fixed on the reference-target side; a fix also has to settle what the loader stamps on such rows (seedRowNeedsOrganization answers only for organization_id), so it is not a mechanical in-place fix · dedupe words: loadExistingRecords tenantField, seed replay workspace_id INVALID_FILTER, per-organization replay tenant column, seed source object organization_id filter", "carrier: none · noted, not filed — seed-tenancy-autonumber-split.integration.test.ts '[wiring] the app-plugin handoff fires on the organization insert itself' hit the 5000 ms default timeout once in a 21-file run on the shared box; passed alone" ] }