Filed from the repo:hotcrm seat, measured on the pinned @objectstack/* 17.1.0. Unassigned and ungraded — this repo's triage seat owns domain:* and type.
This is about the diagnostic, not the mechanism. The real defect it half-points at is filed separately (the backfill destroying the autonumber high-water mark).
Problem 1 — the guard conflates "no organizations yet" with "ambiguous owner"
The guard is organizationIds.length !== 1, which treats 0 and 2-or-more as the same condition. They are not:
- With 2 or more, the target organization is genuinely ambiguous and a warning is right.
- With 0, there is nothing to adopt yet, and the state self-heals at the first sign-up through the runtime's own
sys_organization-insert handoff.
Measured on a clean 17.1.0 boot of the HotCRM exemplar: SELECT COUNT(*) FROM sys_organization = 0 at the moment the warning prints (its own payload says organizationCount: 0). Seeds load inline during start(), while the first organization is created by plugin-auth's ensureDefaultOrganization, gated on an admin permission-set grant — so it cannot exist until a sign-up POST reaches the running server. After sign-up, all 9 objects went to 0 untenanted rows and exactly 1 partition, and a restart logged grep -c 8686 = 0.
⚠️ Meanwhile the line asserts that the named objects "run two autonumber counters" and "can mint the same unique identifier twice" — at a moment when exactly one counter exists per object (every row carries tenant_id '__global__'; there is not one organization-scoped counter) and zero tenanted rows exist to collide with. The organizationLastValue 0 in the payload is buildSplitProbeSql's LEFT JOIN missing, not a second live counter.
Suggested: a third state that stays silent, or logs at info, when organizationCount is 0.
Problem 2 — the affected-object list is a race snapshot, not a measurement
The backfill probe runs at kernel:readywhile the inline seed is still writing. The same boot emits:
WARN [Seeder] Inline seed exceeded 8000ms budget for app.objectstack.hotcrm;
continuing in background to avoid blocking kernel start 17:41:14.143Z
[metadata-protocol] seed/API tenancy split detected … 17:41:14.337Z
194 ms apart. So the warning names only the counters allocated so far. Measured: the first clean boot named exactly 3 objects; the settled database holds 9 counters; a restart on that same database names all 9.
Suggested: order the backfill after seed completion, or say in the line that the snapshot is partial.
Why this is worth fixing rather than tolerating
⚠️This warning has now generated three cards in the consumer repo — hotcrm#698, hotcrm#1292, and the sweep that produced #1292 — each spending real measurement effort to discover that the headline hazard does not reproduce. The cost is not the log line; it is that the line reads as an active data-integrity emergency, so every reader who takes it seriously pays for the investigation.
It also buried a real defect: the same handoff genuinely does re-issue a burned autonumber (filed separately), and that one is invisible on the happy path. A diagnostic that cries wolf about a benign state while staying silent about an actual duplicate-minting path is worse than either alone.
⚠️ One thing to correct while here
objectstack-ai/hotcrm#698 is closed with its work split as "organization_id NULL on seed rows: CRM (seed data)". That attribution is factually wrong, which is why the "CRM half" was never done — it was never actionable. Seed-row tenancy is resolved entirely inside the platform's SeedLoaderService: tenantOrg = config.organizationId ?? fallbackOrgId, where fallbackOrgId = resolveSoleOrganizationId() and config.organizationId is supplied only by the runtime's per-organization replayer. None of those inputs is reachable from app metadata, and the documented app-side override (a seed record supplying its own organization_id) is unusable on a first boot because sys_organization is empty — there is no id to author, no natural key that resolves, and the loader drops the whole record on an unresolvable reference.
Worth reopening or superseding #698 rather than leaving a half attributed to a repo that cannot act on it.
Back-link: objectstack-ai/hotcrm#1292.
Filed from the
repo:hotcrmseat, measured on the pinned@objectstack/* 17.1.0. Unassigned and ungraded — this repo's triage seat ownsdomain:*and type.This is about the diagnostic, not the mechanism. The real defect it half-points at is filed separately (the backfill destroying the autonumber high-water mark).
Problem 1 — the guard conflates "no organizations yet" with "ambiguous owner"
The guard is
organizationIds.length !== 1, which treats 0 and 2-or-more as the same condition. They are not:sys_organization-insert handoff.Measured on a clean 17.1.0 boot of the HotCRM exemplar:
SELECT COUNT(*) FROM sys_organization= 0 at the moment the warning prints (its own payload saysorganizationCount: 0). Seeds load inline duringstart(), while the first organization is created byplugin-auth'sensureDefaultOrganization, gated on an admin permission-set grant — so it cannot exist until a sign-up POST reaches the running server. After sign-up, all 9 objects went to 0 untenanted rows and exactly 1 partition, and a restart loggedgrep -c 8686= 0.uniqueidentifier twice" — at a moment when exactly one counter exists per object (every row carriestenant_id '__global__'; there is not one organization-scoped counter) and zero tenanted rows exist to collide with. TheorganizationLastValue 0in the payload isbuildSplitProbeSql'sLEFT JOINmissing, not a second live counter.Suggested: a third state that stays silent, or logs at info, when
organizationCountis 0.Problem 2 — the affected-object list is a race snapshot, not a measurement
The backfill probe runs at
kernel:readywhile the inline seed is still writing. The same boot emits:194 ms apart. So the warning names only the counters allocated so far. Measured: the first clean boot named exactly 3 objects; the settled database holds 9 counters; a restart on that same database names all 9.
Suggested: order the backfill after seed completion, or say in the line that the snapshot is partial.
Why this is worth fixing rather than tolerating
It also buried a real defect: the same handoff genuinely does re-issue a burned autonumber (filed separately), and that one is invisible on the happy path. A diagnostic that cries wolf about a benign state while staying silent about an actual duplicate-minting path is worse than either alone.
objectstack-ai/hotcrm#698 is closed with its work split as "
organization_id NULLon seed rows: CRM (seed data)". That attribution is factually wrong, which is why the "CRM half" was never done — it was never actionable. Seed-row tenancy is resolved entirely inside the platform'sSeedLoaderService:tenantOrg = config.organizationId ?? fallbackOrgId, wherefallbackOrgId = resolveSoleOrganizationId()andconfig.organizationIdis supplied only by the runtime's per-organization replayer. None of those inputs is reachable from app metadata, and the documented app-side override (a seed record supplying its ownorganization_id) is unusable on a first boot becausesys_organizationis empty — there is no id to author, no natural key that resolves, and the loader drops the whole record on an unresolvable reference.Worth reopening or superseding #698 rather than leaving a half attributed to a repo that cannot act on it.
Back-link: objectstack-ai/hotcrm#1292.