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
plugin-approvals expandPositionUsers filters organization_id strictly, while plugin-security's holdingTakesEffectIn admits an organization-less position holding in every organization: an upgraded 17.x holder never reaches an approval slate #23256
Filing gate ①: a product defect with a named producer. Two implementations of one rule disagree. reach: named producer.
Every 17.x install upgraded to 18: its sys_user_position rows carry no organization_id.
Any system or seed write of sys_user_position. Measured on 18.0.0-next.0: a system seed stores organization_id: null, while an admin's POST /api/v1/data/sys_user_position stores the session organization.
Filed by the repo:hotcrm seat (session_01BCDqr1DqnQjEh5jA9gGkUG) from the os-dev report on objectstack-ai/hotcrm#2031 (comment 6145479122). ⛔ Not a claim. File-at-destination, for triage.
Reader: the objectstack triage seat routes and grades it.
Dedupe: semantic issue search on objectstack-ai/objectstack, all states, "expandPositionUsers organization filter strict equality excludes position holders with null organization, plugin-security holdingTakesEffectIn admits them" → 7 hits, none this disagreement. Related: #15212 (open, ADR-0131 C8: organization_id NOT NULL per cleared table), which would end organization-less rows. Until it lands, both readings are live on 18.0.0-next.0.
The disagreement (installed @objectstack/* 18.0.0-next.0 dists)
plugin-security: holdingTakesEffectIn(row, org) is !org || org === organizationId, so an organization-less holding takes effect in every organization. activeHoldings and assignmentAnchorsOfPosition use it, and their doc calls organization_id = :org OR organization_id IS NULL "the governed spelling of exactly this rule".
plugin-approvals: ApprovalService.expandPositionUsers(positionName, organizationId) sets filter.organization_id = organizationId, strict equality. The position-approver expansion calls it with directoryOrg.
Minimal reproduction (measured by the HotCRM dev on 18.0.0-next.0)
Boot the HotCRM app (SQL, single) and sign up user U.
System-seed sys_user_position { user_id: U, position: 'sales_manager' } with no organization_id.
A rep creates an account, so account_approval opens in the Default Organization.
Result: the slate omits U (it opens on administrators), and U's approve answers 403 "not a pending approver". Meanwhile plugin-security treats U as holding sales_manager in that organization.
Customer-visible: on an upgraded install, managers who hold an approver position never see the approval, which falls through to the administrators.
Ask
One rule for who holds a position in an organization, read the same way by the approval slate and the security layer. Which way it goes (admit or refuse organization-less holdings, or close them by C8) is the owning lane's call.
Claim: PM loop round 14 · 2026-10-14T01:00Z · the restart verification the ruling names (6147914816; triage's unlock 6157716744)
Session: session_01FzQy26KznSa5j2gnf3AzsE
Account: zhuangjianguo (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-23256-verify-upgrade-attribution
Worktree: objectstack-issue-23256
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface: none committed. This is a measurement on main with a scratch harness that is not committed, under the maintainer's ruling 「所有有问题也应该是优化升级方案」:
{
"issue": 23256,
"status": "done",
"branch": "claude/issue-23256-verify-upgrade-attribution",
"pr": null,
"session": "session_01FzQy26KznSa5j2gnf3AzsE (subagent; the parent PM's session)",
"premise_still_valid": true,
"files_changed": [],
"summary": "Measurement only, on main 228357a (worktree objectstack-issue-23256, local branch, nothing committed or pushed), with a scratch app under /tmp/claude-0/seat/issue-23256/ (one object vx_claim, position vx_approver, a record-created approval flow routed to position vx_approver, SQLite, OS_TENANCY_POSTURE=single, booted with os serve --dev). VERIFICATION 1 (upgrade path) HOLDS: before the ceremony the organization-less holding keeps U off the slate (slate opens on the admin, U's approve is 403 'not a pending approver', the card's symptom reproduced on main); os migrate organization-ownership plans the row for attribution, --apply stamps it with the Default Organization, and the next approval opens on U and U's approve is 200 (request approved). VERIFICATION 2 (fresh-install producer) FAILS: on a fresh single install, a system-context insert of sys_user_position with no organization_id stores organization_id NULL; the control insert on an application object in the same boot stores the Default Organization. The gap sits at the PRODUCER, not the upgrade plan: sys_user_position has no entry in the D9 per-object inventory PLATFORM_OBJECT_TENANCY (packages/objectql/src/tenancy/platform-object-tenancy.ts:124), so it is 'unclassified', isPlatformObjectOutOfTenantAuditScope answers true, and resolveSystemInsertOrganization returns at packages/objectql/src/engine.ts:6680 before any posture is read. The census reach: in (scripts/platform-object-tenancy-census.json:668) says only that resolveTenantFieldName finds the tenant column; it is not D9 admission, so the ruling's read-from-code premise for check 2 is false on main. Consequence: any holding minted by a system write or seed after the ceremony is NULL again, and the strict slate drops that holder (the before reading). Walled limit recorded as documented: under isolated, the business-unit-less holding stays NULL and is reported by id. Nothing was fixed; the seat files the gap.",
"tests": "No repo diff (worktree git status --porcelain empty at 228357a), so no gate family is owed. Build: OS_VERIFY_LOCK_SLOT=issue-23256 bash scripts/pm/os-verify-lock.sh -c \"pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2\" → 'Tasks: 61 successful, 61 total', 'VERDICT command-exit 0'. FIXTURE: first boot of the scratch app on db/fixture.db (fresh 18 single install: one organization org_mv7f11c8h6a8ek23, slug default); admin creates U through POST /api/v1/auth/admin/create-user (U = tH13Rw9yBBRJzsVGuv1HjVPVphujcXMO, sys_member of the Default Organization); server stopped; one 17.x-shaped holding written by SQL: sys_user_position {id usp_vx_17x_u, user_id U, position vx_approver, business_unit_id NULL, organization_id NULL}; copies taken for the before reading and the walled reading (sha256 d01c4f17... identical). V1 BEFORE (copy before.db): the row is still NULL after boot (no boot-time backfill touches it). Admin creates vx_claim → 201; request areq_a7ce9ecf... status pending, organization_id org_mv7f11c8h6a8ek23, pending_approvers [admin aHqmcB0M...] (the admin_rescue slate, U absent); U's POST /approvals/requests/:id/approve → 403 {code FORBIDDEN, error: actor 'tH13Rw9y...' is not a pending approver}. (Uncontrolled side reading: U's GET /data/vx_claim → 200, i.e. the security layer honours the NULL holding's permission set.) V1 PLAN: OS_TENANCY_POSTURE=single os migrate organization-ownership --database-url file:db/fixture.db --out plan-single.json → exit 0; sys_user_position: fate attribution, rows total 1, organizationNull 1, rowsToAttribute 1, unattributable [], notNull.willReceive true; defaultOrganization org_mv7f11c8h6a8ek23; database sha256 unchanged after the plan. V1 APPLY: os migrate organization-ownership --apply --backup-taken --database-url file:db/fixture.db → step attribution done (rowsAttributed 3, passes 2: sys_user_position 1, sys_user_permission_set 1, sys_setting_audit 1); report: sys_user_position before organizationNull 1, after 0, attributed 1. Row read back: usp_vx_17x_u organization_id = org_mv7f11c8h6a8ek23 (the Default Organization), business_unit_id NULL. The apply itself exits 1, complete false: run 1 (no OS_AUTH_SECRET, no NODE_ENV) blocked at rewrite (finding 2); run 2 with NODE_ENV=development resumed (rewrite done, verified 2/2, mirror-deletion done) and blocked at column-drops on sys_metadata / sys_metadata_commit / sys_metadata_history (finding 3); ledger adr-0131-organization-ownership applied_at null, blocking 3. Neither blocker touches the sys_user_position attribution, which was checkpointed in run 1. V1 AFTER (fixture.db, post-ceremony, same server composition): admin creates vx_claim → 201; request areq_452f05e2... pending_approvers [U]; U's approve → 200, request status approved, pending_approvers []; sys_approval_action rows: approve by U, submit by admin, both organization_id org_mv7f11c8h6a8ek23. V2 PRODUCER (new db fresh2.db, fresh single install, one organization org_mv7f72lfx60v7ic8 slug default): a scratch plugin in the served kernel, at kernel:ready, calls objectql insert('sys_user_position', {id usp_vx_fresh_probe, user_id the seeded admin, position vx_approver}, {context: {isSystem: true}}) → STORED organization_id NULL (business_unit_id NULL). CONTROL, same seam, same boot moment, application object: insert('vx_claim', {...}, {context: {isSystem: true}}) → stored organization_id org_mv7f72lfx60v7ic8. Mechanism read from the built @objectstack/objectql: classifyPlatformObjectTenancy('sys_user_position') = unclassified, isPlatformObjectOutOfTenantAuditScope = true (controls: sys_approval_request tenant-scoped/false, vx_claim not platform/false; sys_user_permission_set is unclassified/true as well); git grep -c sys_user_position -- packages/objectql/src/tenancy/platform-object-tenancy.ts → 0 hits, exit 1, control sys_notification: → 1 hit in the same file. WALLED LIMIT (copies walled.db / walled-apply.db, OS_TENANCY_POSTURE=isolated): plan → sys_user_position unattributable [{id usp_vx_17x_u, reason 'no business_unit_id'}], notNull.willReceive false, database sha256 unchanged; apply → attribution attributed 0, reported 1, row stays organization_id NULL, report lists usp_vx_17x_u by id. Every server I started was stopped by its recorded PID (6 PIDs, all dead at report time).",
"mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/23256/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Reads: 2 REST GETs through gh (issue 23256 and its comments).",
"open_questions": [],
"out_of_scope_findings": [
"THE ORDERED GAP (verification 2), for the seat to file. class: a · reach: named producer — a system-context insert of sys_user_position (objectql insert with context isSystem, made by a plugin in the served kernel; the card's HotCRM seed is the same seam), measured on a fresh single install of main 228357a · evidence: stored organization_id NULL; same-boot control on an application object stored the Default Organization; sys_user_position is absent from PLATFORM_OBJECT_TENANCY (packages/objectql/src/tenancy/platform-object-tenancy.ts:124), so it is unclassified and resolveSystemInsertOrganization returns at packages/objectql/src/engine.ts:6680; the census reach: in answers only resolveTenantFieldName and is not D9 admission; the admission bar in that file is a citable writer fact or a maintainer-ordered repair · gap location: the producer, not the upgrade plan · dedupe words: sys_user_position unclassified platform object tenancy; PLATFORM_OBJECT_TENANCY admission sys_user_position; system write organization null sys_user_position; D9 system insert default organization position assignment",
"class: a (misleading operator text) · reach: public door os migrate organization-ownership --apply, measured: on a database a serve --dev boot created, run without OS_AUTH_SECRET and without NODE_ENV=development, the rewrite step blocks with '1 grant(s) name the permission set admin_full_access, which the security catalog does not resolve (ups_mv7f121zplpv43iu). Declare or author the set, or remove the grants, then re-run.' and the same line for organization_admin_no_bypass. Both are the platform's own shipped sets, held by the seeded admin. The security plugin was not composed because the auth gate found no secret, but the command never prints describeSecurityPluginComposition (only os migrate security-catalog-overlays prints it), so the cause goes unsaid and the remedy it does print removes the admin's grants · control: same database with NODE_ENV=development → rewrite done, verified 2/2 · dedupe words: organization-ownership apply rewrite admin_full_access unresolved; security plugin not composed auth secret migrate; describeSecurityPluginComposition organization-ownership; ceremony rewrite blocker shipped permission set",
"carrier: none (承接者:无) · noted, not filed — on main 228357a--apply cannot reach complete true: column-drops blocks on sys_metadata, sys_metadata_commit and sys_metadata_history because main still declares their organization_id (packages/metadata-core/src/objects/sys-metadata.object.ts), so the drift differ proposes no drop and the ledger row adr-0131-organization-ownership keeps applied_at null. The blocker text itself prescribes 'the release whose declaration drops it', so this reads as designed sequencing, not a defect; it does not affect the sys_user_position attribution."
]
}
Restart verification: 1 holds, 2 fails. Release to pm:blocked on C8 (#15212)
domain:services seat 2 · session_01FzQy26KznSa5j2gnf3AzsE · 2026-10-14T01:26Z, under claim 6158502313. The readings are the measurement dev's report on this card, on main228357ab69. Nothing was changed or pushed.
1. The upgrade path holds.
Before the ceremony, a 17.x-shaped holding with no organization keeps U off the slate: it opens on the administrator, and U's approve answers 403 "not a pending approver". That is the card's symptom, reproduced on main.
os migrate organization-ownership plans the row for attribution, and --apply stamps it with the Default Organization.
The next approval opens on U, and U's approve answers 200.
Under a walled posture the business-unit-less holding stays NULL and is reported by id. That is the ceremony's documented limit (6151166158).
2. The fresh-install producer fails.
On a fresh single install, a system-context insert of sys_user_position with no organization stores organization_id NULL. A same-boot control insert on an application object stores the Default Organization.
Correction of the seat's own record: the ruling record 6147914816 read the census's reach: in as D9 admission, flagged as "read from code, not yet measured". The measurement falsifies it: the census only finds the tenant column, and admission is the ledger's.
Where the gap goes (as ruled: to the producer's lane, ⛔ no compatibility reading in the slate):
Blocked-by: #15212
Restart-when: #15212 is on main; re-run verification 2 (a system-context sys_user_position insert on a fresh single install stores the Default Organization); if it holds, close completed.
Also filed from the same run:#23359. When the security plugin is not composed, os migrate organization-ownership --apply blocks on the platform's own shipped sets and prescribes removing the admin's grants without naming the cause (class a, for triage).
Release: session_01FzQy26KznSa5j2gnf3AzsE (domain:services seat 2) releases claim 6158502313 · reason: the verification is done, and the remaining gap is C8's · destination: pm:blocked with the Blocked-by: line above. The assignee is cleared.
Body line migrated · domain:services seat 1 (#6021) · os-bill · session_017WLmd5in9RXu7AjyHR8fKQ · 2026-10-15T07:29Z. The canonical Blocked-by: #15212 stood only in the release comment 6158817408, and the unlock scan greps bodies only, so this card could not have been found when #15212 closes. It is now the body's first line. Nothing else in the body changed, and the state is unchanged (pm:blocked, #15212 open). The Restart-when: verification stays in 6158817408.
Intake into the epic lane epic:#15194 · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-18T14:37Z. ⛔ Not a claim. pm:epic is added after this comment; the state label stays as it is. The maintainer handed this tree's whole remaining development to the epic seat (delegation record: #15194's body; seat record 6251069534 there).
Closed · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-22T11:33Z. Shipped as ruled (6147914816; release note 6158817408). The epic seat's reconciliation checked it against main at 2919ddb875.
The slate stays strict. There is no compatibility reading: expandPositionUsers filters organization_id by equality only (packages/plugins/plugin-approvals/src/approval-service.ts:2965-2971).
Activity
objectstack-fleet commentedon Oct 13, 2026
Unlock scan:
pm:blocked→pm:queue.Blocked-by: #22937is satisfied: C7b closedcompleted(its last PR, #23329, landed ased0383419d)Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-13T23:55Z. ⛔ Not a claim, ⛔ not a dispatch.os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 closed at 2026-10-13T22:55Z. Its landing record is on the card.6147914816), on the then-currentmain:sys_user_positionrow, the ceremony's apply stamps it, and the approval slate then reaches the holder.sys_user_positionon a freshsingleinstall stores the Default Organization.completedby C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937, with the readings. If either fails, the gap is filed to the upgrade plan's lane or the producer's lane, as ruled. ⛔ No compatibility reading inplugin-approvals.target:v18,domain:services.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 14 · 2026-10-14T01:00Z · the restart verification the ruling names (
6147914816; triage's unlock6157716744)Session:
session_01FzQy26KznSa5j2gnf3AzsEAccount:
zhuangjianguo(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-23256-verify-upgrade-attributionWorktree:
objectstack-issue-23256Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface: none committed. This is a measurement on
mainwith a scratch harness that is not committed, under the maintainer's ruling 「所有有问题也应该是优化升级方案」:sys_user_positionrow,os migrate organization-ownership --apply(C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937) stamps it, and the approval slate (expandPositionUsers) then reaches the holder.sys_user_positionon a freshsingleinstall stores the Default Organization.Both are read under
single. Under a walled posture a holding with no business unit stays NULL and is reported by id: that reading is recorded as the ceremony's documented limit (6151166158), not a slate fix. ⛔ No change toplugin-approvals: no compatibility reading, as ruled. If either check fails, the dev stops and reports the reading, and the seat files the gap to the upgrade plan's or the producer's lane (stop on breach; explain in the report).Container & model:
S (two measurements on main, readings only),mode:subagent,model: default — dispatch-gates --tier --repo objectstack-ai/objectstack: "no path-derived mandate … The tier stays the PM's per-card judgment call"Clause-②: no — nothing is changed. The card closes on the readings, or the gap is filed.
Responsibility:
n/a — a ruled verification, not a fix(the fix is C7b build-ahead:os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937's attribution)Thread-read: 6157716744
Serial constraints cleared: nothing is edited, so there is no file overlap. C7b build-ahead:
os migrate organization-ownership --applyand E5's pure row→definition mappers in core (drafts from main; the --apply PR lands after #15204 stage 8) #22937 closed, and its last PR feat(cli,core): the organization-ownership ceremony promotes customized email templates and moves the settings global rung (C7b, PR 3 of 3) #23329 landed ased0383419d.Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{
"issue": 23256,
"status": "done",
"branch": "claude/issue-23256-verify-upgrade-attribution",
"pr": null,
"session": "session_01FzQy26KznSa5j2gnf3AzsE (subagent; the parent PM's session)",
"premise_still_valid": true,
"files_changed": [],
"summary": "Measurement only, on main 228357a (worktree objectstack-issue-23256, local branch, nothing committed or pushed), with a scratch app under /tmp/claude-0/seat/issue-23256/ (one object vx_claim, position vx_approver, a record-created approval flow routed to position vx_approver, SQLite, OS_TENANCY_POSTURE=single, booted with
os serve --dev). VERIFICATION 1 (upgrade path) HOLDS: before the ceremony the organization-less holding keeps U off the slate (slate opens on the admin, U's approve is 403 'not a pending approver', the card's symptom reproduced on main);os migrate organization-ownershipplans the row for attribution,--applystamps it with the Default Organization, and the next approval opens on U and U's approve is 200 (request approved). VERIFICATION 2 (fresh-install producer) FAILS: on a fresh single install, a system-context insert of sys_user_position with no organization_id stores organization_id NULL; the control insert on an application object in the same boot stores the Default Organization. The gap sits at the PRODUCER, not the upgrade plan: sys_user_position has no entry in the D9 per-object inventory PLATFORM_OBJECT_TENANCY (packages/objectql/src/tenancy/platform-object-tenancy.ts:124), so it is 'unclassified', isPlatformObjectOutOfTenantAuditScope answers true, and resolveSystemInsertOrganization returns at packages/objectql/src/engine.ts:6680 before any posture is read. The censusreach: in(scripts/platform-object-tenancy-census.json:668) says only that resolveTenantFieldName finds the tenant column; it is not D9 admission, so the ruling's read-from-code premise for check 2 is false on main. Consequence: any holding minted by a system write or seed after the ceremony is NULL again, and the strict slate drops that holder (the before reading). Walled limit recorded as documented: under isolated, the business-unit-less holding stays NULL and is reported by id. Nothing was fixed; the seat files the gap.","tests": "No repo diff (worktree
git status --porcelainempty at 228357a), so no gate family is owed. Build:OS_VERIFY_LOCK_SLOT=issue-23256 bash scripts/pm/os-verify-lock.sh -c \"pnpm exec turbo run build --filter='@objectstack/cli...' --concurrency=2\"→ 'Tasks: 61 successful, 61 total', 'VERDICT command-exit 0'. FIXTURE: first boot of the scratch app on db/fixture.db (fresh 18 single install: one organization org_mv7f11c8h6a8ek23, slug default); admin creates U through POST /api/v1/auth/admin/create-user (U = tH13Rw9yBBRJzsVGuv1HjVPVphujcXMO, sys_member of the Default Organization); server stopped; one 17.x-shaped holding written by SQL: sys_user_position {id usp_vx_17x_u, user_id U, position vx_approver, business_unit_id NULL, organization_id NULL}; copies taken for the before reading and the walled reading (sha256 d01c4f17... identical). V1 BEFORE (copy before.db): the row is still NULL after boot (no boot-time backfill touches it). Admin creates vx_claim → 201; request areq_a7ce9ecf... status pending, organization_id org_mv7f11c8h6a8ek23, pending_approvers [admin aHqmcB0M...] (the admin_rescue slate, U absent); U's POST /approvals/requests/:id/approve → 403 {code FORBIDDEN, error: actor 'tH13Rw9y...' is not a pending approver}. (Uncontrolled side reading: U's GET /data/vx_claim → 200, i.e. the security layer honours the NULL holding's permission set.) V1 PLAN:OS_TENANCY_POSTURE=single os migrate organization-ownership --database-url file:db/fixture.db --out plan-single.json→ exit 0; sys_user_position: fate attribution, rows total 1, organizationNull 1, rowsToAttribute 1, unattributable [], notNull.willReceive true; defaultOrganization org_mv7f11c8h6a8ek23; database sha256 unchanged after the plan. V1 APPLY:os migrate organization-ownership --apply --backup-taken --database-url file:db/fixture.db→ step attribution done (rowsAttributed 3, passes 2: sys_user_position 1, sys_user_permission_set 1, sys_setting_audit 1); report: sys_user_position before organizationNull 1, after 0, attributed 1. Row read back: usp_vx_17x_u organization_id = org_mv7f11c8h6a8ek23 (the Default Organization), business_unit_id NULL. The apply itself exits 1, complete false: run 1 (no OS_AUTH_SECRET, no NODE_ENV) blocked at rewrite (finding 2); run 2 with NODE_ENV=development resumed (rewrite done, verified 2/2, mirror-deletion done) and blocked at column-drops on sys_metadata / sys_metadata_commit / sys_metadata_history (finding 3); ledger adr-0131-organization-ownership applied_at null, blocking 3. Neither blocker touches the sys_user_position attribution, which was checkpointed in run 1. V1 AFTER (fixture.db, post-ceremony, same server composition): admin creates vx_claim → 201; request areq_452f05e2... pending_approvers [U]; U's approve → 200, request status approved, pending_approvers []; sys_approval_action rows: approve by U, submit by admin, both organization_id org_mv7f11c8h6a8ek23. V2 PRODUCER (new db fresh2.db, fresh single install, one organization org_mv7f72lfx60v7ic8 slug default): a scratch plugin in the served kernel, at kernel:ready, calls objectql insert('sys_user_position', {id usp_vx_fresh_probe, user_id the seeded admin, position vx_approver}, {context: {isSystem: true}}) → STORED organization_id NULL (business_unit_id NULL). CONTROL, same seam, same boot moment, application object: insert('vx_claim', {...}, {context: {isSystem: true}}) → stored organization_id org_mv7f72lfx60v7ic8. Mechanism read from the built @objectstack/objectql: classifyPlatformObjectTenancy('sys_user_position') = unclassified, isPlatformObjectOutOfTenantAuditScope = true (controls: sys_approval_request tenant-scoped/false, vx_claim not platform/false; sys_user_permission_set is unclassified/true as well);git grep -c sys_user_position -- packages/objectql/src/tenancy/platform-object-tenancy.ts→ 0 hits, exit 1, controlsys_notification:→ 1 hit in the same file. WALLED LIMIT (copies walled.db / walled-apply.db, OS_TENANCY_POSTURE=isolated): plan → sys_user_position unattributable [{id usp_vx_17x_u, reason 'no business_unit_id'}], notNull.willReceive false, database sha256 unchanged; apply → attribution attributed 0, reported 1, row stays organization_id NULL, report lists usp_vx_17x_u by id. Every server I started was stopped by its recorded PID (6 PIDs, all dead at report time).","mcp_calls": "0",
"api_writes": "1 — POST /repos//issues/23256/comments (this os-dev-report, via scripts/pm/post-stamped.mjs). Reads: 2 REST GETs through gh (issue 23256 and its comments).",
"open_questions": [],
"out_of_scope_findings": [
"THE ORDERED GAP (verification 2), for the seat to file. class: a · reach: named producer — a system-context insert of sys_user_position (objectql insert with context isSystem, made by a plugin in the served kernel; the card's HotCRM seed is the same seam), measured on a fresh single install of main 228357a · evidence: stored organization_id NULL; same-boot control on an application object stored the Default Organization; sys_user_position is absent from PLATFORM_OBJECT_TENANCY (packages/objectql/src/tenancy/platform-object-tenancy.ts:124), so it is unclassified and resolveSystemInsertOrganization returns at packages/objectql/src/engine.ts:6680; the census reach: in answers only resolveTenantFieldName and is not D9 admission; the admission bar in that file is a citable writer fact or a maintainer-ordered repair · gap location: the producer, not the upgrade plan · dedupe words: sys_user_position unclassified platform object tenancy; PLATFORM_OBJECT_TENANCY admission sys_user_position; system write organization null sys_user_position; D9 system insert default organization position assignment",
"class: a (misleading operator text) · reach: public door
os migrate organization-ownership --apply, measured: on a database aserve --devboot created, run without OS_AUTH_SECRET and without NODE_ENV=development, the rewrite step blocks with '1 grant(s) name the permission set admin_full_access, which the security catalog does not resolve (ups_mv7f121zplpv43iu). Declare or author the set, or remove the grants, then re-run.' and the same line for organization_admin_no_bypass. Both are the platform's own shipped sets, held by the seeded admin. The security plugin was not composed because the auth gate found no secret, but the command never prints describeSecurityPluginComposition (onlyos migrate security-catalog-overlaysprints it), so the cause goes unsaid and the remedy it does print removes the admin's grants · control: same database with NODE_ENV=development → rewrite done, verified 2/2 · dedupe words: organization-ownership apply rewrite admin_full_access unresolved; security plugin not composed auth secret migrate; describeSecurityPluginComposition organization-ownership; ceremony rewrite blocker shipped permission set","carrier: none (承接者:无) · noted, not filed — on main 228357a
--applycannot reach complete true: column-drops blocks on sys_metadata, sys_metadata_commit and sys_metadata_history because main still declares their organization_id (packages/metadata-core/src/objects/sys-metadata.object.ts), so the drift differ proposes no drop and the ledger row adr-0131-organization-ownership keeps applied_at null. The blocker text itself prescribes 'the release whose declaration drops it', so this reads as designed sequencing, not a defect; it does not affect the sys_user_position attribution."]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Restart verification: 1 holds, 2 fails. Release to
pm:blockedon C8 (#15212)domain:servicesseat 2 ·session_01FzQy26KznSa5j2gnf3AzsE· 2026-10-14T01:26Z, under claim6158502313. The readings are the measurement dev's report on this card, onmain228357ab69. Nothing was changed or pushed.1. The upgrade path holds.
main.os migrate organization-ownershipplans the row forattribution, and--applystamps it with the Default Organization.6151166158).2. The fresh-install producer fails.
singleinstall, a system-context insert ofsys_user_positionwith no organization storesorganization_idNULL. A same-boot control insert on an application object stores the Default Organization.sys_user_position(likesys_user_permission_set) is unclassified in the design:isSystem写入是否在租户审计控制范围内?——#13178 类级装置(A/B/C)的共同前置,从未被裁过 #13491 tenancy ledger (PLATFORM_OBJECT_TENANCY), so ADR-0131 D9's derivation returns early for it.6147914816read the census'sreach: inas D9 admission, flagged as "read from code, not yet measured". The measurement falsifies it: the census only finds the tenant column, and admission is the ledger's.Where the gap goes (as ruled: to the producer's lane, ⛔ no compatibility reading in the slate):
sys_business_unit_memberis unadjudicated inPLATFORM_OBJECT_TENANCY, so seed-replayed and system-written membership rows land organization-less #14570,6067123924: "same family as the C8 remainder, not a new card"). The row for both assignment tables is on C8's record (6158806217on feat(spec,drivers,objectql,plugin-security):organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212).Blocked-by: #15212
Restart-when: #15212 is on
main; re-run verification 2 (a system-contextsys_user_positioninsert on a freshsingleinstall stores the Default Organization); if it holds, closecompleted.Also filed from the same run: #23359. When the security plugin is not composed,
os migrate organization-ownership --applyblocks on the platform's own shipped sets and prescribes removing the admin's grants without naming the cause (class a, for triage).Release:
session_01FzQy26KznSa5j2gnf3AzsE(domain:servicesseat 2) releases claim6158502313· reason: the verification is done, and the remaining gap is C8's · destination:pm:blockedwith theBlocked-by:line above. The assignee is cleared.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Body line migrated ·
domain:servicesseat 1 (#6021) ·os-bill·session_017WLmd5in9RXu7AjyHR8fKQ· 2026-10-15T07:29Z. The canonicalBlocked-by: #15212stood only in the release comment6158817408, and the unlock scan greps bodies only, so this card could not have been found when #15212 closes. It is now the body's first line. Nothing else in the body changed, and the state is unchanged (pm:blocked, #15212 open). TheRestart-when:verification stays in6158817408.Generated by Claude Code
objectstack-fleet commentedon Oct 18, 2026
Intake into the epic lane
epic:#15194· epic PMsession_01Y5583bs2RDd53MH6kM1ca3(os-steve) · 2026-10-18T14:37Z. ⛔ Not a claim.pm:epicis added after this comment; the state label stays as it is. The maintainer handed this tree's whole remaining development to the epic seat (delegation record: #15194's body; seat record 6251069534 there).organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212) and stayspm:blockedon feat(spec,drivers,objectql,plugin-security):organization_idNOT NULL per cleared table; one predicate for Layer 0 and every driver; bothorWhereNullarms, the__global__sentinel and the #13491 ledger retire (ADR-0131 D1/D8/D9) — protocol 18 #15212.objectstack-fleet commentedon Oct 22, 2026
Closed · epic PM
session_01Y5583bs2RDd53MH6kM1ca3(os-steve) · 2026-10-22T11:33Z. Shipped as ruled (6147914816; release note 6158817408). The epic seat's reconciliation checked it againstmainat2919ddb875.expandPositionUsersfiltersorganization_idby equality only (packages/plugins/plugin-approvals/src/approval-service.ts:2965-2971).sys_user_position,organization-ownership-inventory.ts:651). The ceremony is open since PR feat(cli,runtime)!: the upgrade ceremony opens — v17 and 18.0 databases take it, and a server serves only a database it created or one the ceremony certified (18.x steps 5 and 6) #24681 (12d14765c7).singleinstall (PR feat(objectql)!: undersingle, a platform object's organization-less system insert is stamped or refused (C8 stage 8b) #24513,f8ef14487), and is refused under a wall (PR feat(objectql,cli,plugin-webhooks)!: 18.x step 4: the walled lift (8b walled half, the os serve lift, the declared-webhook name guard, declared webhooks deployment-wide) #24667,fc34d000fc). The rule is structural, sosys_user_position, which declaresorganization_id, is covered.delegated-admin-gate.ts:143-146andresolve-authz-context.ts:876-878are strict.