Repository navigation
Commit df8bb40
feat(cli,driver-sql)!: a tenant table's organization-less row no longer reaches the
Part of #24583 — step 7, part 7b-3 (claim 6284514874, amended
6284744613). The card stays open until every step-7 part has landed.
Clause-②: yes (narrowing) — measured: two accept sets narrow (the
driver's autonumber counter and `os serve`'s production server boot),
and four exports are added and one is removed.
#24681 (steps 5+6) has merged, and this branch merged `main` after it,
at `112ea871` over `main` `307ce4c4`. The diff against `main` is this
part alone: 24 files, +637 / −88. `check-governed-merges --branch` puts
0 of the 24 paths on the register, and the size is under the human-merge
line.
## Why
Step 7 rests on the premise that the readers it retires are dead after
certification. The first measurement on this part found one still live.
A production `os serve` with the `auth` tier on and no `OS_AUTH_SECRET`
skipped AuthPlugin with a warning and served anyway. With no AuthPlugin,
`sys_organization` is never registered, so the engine answers
`no-organization-object`, and the inline seed wrote rows with
`organization_id` NULL and drew their autonumbers from the `__global__`
counter.
Measured on `examples/app-showcase`, on a fresh SQLite database, on the
merged tree before this change:
- the boot wrote its birth marker and answered health 200;
- `showcase_field_zoo` then held 2 rows, both with NULL
`organization_id`;
- `_objectstack_sequences` held one row with `tenant_id` `__global__`.
The seat ruled option A (amendment 6284744613, under #3963's rule A1).
Close the door at the boot, then retire the counter path where the
organization object exists.
## What changed
1. **`os serve` refuses a production server boot whose auth did not
compose for a missing secret.** `packages/cli/src/commands/serve.ts`.
- Placement: beside A1's existing no-auth refusal, in the same
prescriptive shape. It fires while the kernel is only composed: no
plugin has started and the database is never opened.
- The message names `OS_AUTH_SECRET` and `--no-server`.
- Unchanged: `--no-server` (kernel only) still boots with the warning,
`--dev` still uses its fallback secret, and the `host-kernel` reason
(the cloud runtime) still only warns.
- `os start` spawns `serve`, so it gets the same refusal.
2. **The counter refuses an organization-less row on a tenant table,
wherever the driver registered the organization object.**
`SqlDriver.resolveSequenceTenantId` is the one rule both faces call;
`TursoDriver`'s remote face passes the table and field through.
- The refusal is the new `OrganizationlessCounterRefusal`
(`packages/drivers/driver-sql/src/organizationless-counter-refusal.ts`),
with code `ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED` and status 500. That
is the engine twin's code, registered for `@objectstack/driver-sql` in
the error-code ledger.
- It is resolved before the lazy sequences DDL and before any counter
read, so nothing is minted and nothing is written.
- `__global__` stays the counter key for an object with no tenant
column. It also stays for a composition with no organization object,
i.e. a lean `@objectstack/objectql/core` embedding.
- The composition fact is `organizationObjectRegistered`, set in
`computeAndRecordTenantField`, the one funnel every registration door
passes through.
3. **Texts:**
- objectql's refusal message and module docblock, with `GLOBAL_TENANT`
no longer exported from `@objectstack/objectql`;
- the `app-plugin.ts` seed-handoff docblock;
- the `UniqueScopeSchema` TSDoc in `field.zod.ts`;
- the lint rule texts for `unique/legacy-organization-composite`;
- `duplicates.ts`'s live-condition docblocks.
Each now says that a NULL-organization row is residue an earlier release
wrote, not something a new write produces.
4. **Pins:**
- New `sql-driver-organizationless-counter-refusal.test.ts`
(driver-sql): refusal on create and bulkCreate, carried organization, no
tenant column, lean embedding.
- New `serve-no-auth-secret-refusal.integration.test.ts` (cli): the
refused boot's database file never exists; the control boot with the
secret creates it and reaches ready.
- Rewritten to the new rule, not weakened:
- objectql `system-write-organization.test.ts` and runtime
`system-write-tenancy-autonumber-split.integration.test.ts` assert
`ADR-0131 D9` and `organization_id = NULL`, and assert that `__global__`
is absent. The runtime one also asserts it is the engine's refusal, not
the driver's.
- lint `data-model-rules.unique-verdicts.test.ts`.
- runtime `seed-tenancy-autonumber-split.integration.test.ts` now lands
its 17.x residue through the same driver call with the refusal's
composition fact withheld (`asA17xDriver`), so the residue is the
driver's own 17.x path.
- Test rigs that boot a production server for an unrelated property now
get a secret (`E2E_AUTH_SECRET` in `test/helpers/serve-process.ts`).
`serve-ready-degraded-boot.e2e.test.ts` reaches its degraded banner
kernel-only (`--no-server`).
What reads the orphaned `__global__` counter rows a 17.x install left on
tenant tables, after this change: the counter does not. `os migrate
duplicates` still reports them in `liveConditions`, now documented as
residue. The historic seed-tenancy backfill (`kernel:ready`, and the
handoff on organization create) still merges them into the
organization's counter. Its migration entry is not touched here.
## Measured
On HEAD `112ea871` (after `main` was merged), with a fresh `pnpm
install` and `pnpm build` (exit 0):
- **Premise, again.**
- Production `os serve` of app-showcase with no secret: refused, with
"no auth secret is set" and `OS_AUTH_SECRET` named, and the database
file never created.
- The same boot with the secret: health 200. 65 tables carry
`organization_id`, with 0 NULL rows, and the counter is keyed by the
Default Organization.
- `examples/embed-objectql`: 2/2 tests passed.
- **R1.** `pnpm dev -- --fresh` on a random port: health 200,
seeded-admin sign-in 200, 0 error lines.
- **R2 + R3.** `upgrade-18x-harness.dogfood.test.ts` (over the
ceremonied fixture) and
`organization-ownership-boot-refusal-order.dogfood.test.ts`: 2 files, 12
passed.
- **R4.** An `isolated` production `os serve` with the real
`@objectstack/organizations` runtime composed (`Organizations` in the
plugin list): health 200, 33 tables carry `organization_id` with 0 NULL
rows, and 0 failure lines. The walled dogfood cases (the six `wall-*`
files and the three `*-walled*` files): 9 files, 133 passed. Step 4's
walled e2e cases (`serve-organizations-host-resolution`,
`serve-organizations-mount-failure`) ran in the CLI nightly tier below,
green.
- **Suites:**
- driver-sql: 224 files passed, 3683 tests passed;
- driver-turso: 92 files, 2430 tests;
- runtime: 431 files, 6782 tests;
- cli unit: 313 files, 4635 tests;
- objectql: 450 files, 8490 tests;
- lint: 149 files, 6958 tests;
- spec: 703 files, 21360 tests;
- cli integration: 163 files passed, 1 skipped; 1411 tests passed, 9
skipped;
- cli nightly tier (`OS_TEST_TIERS=nightly`, every `*.e2e.test.ts`): 83
files, 879 tests;
- `turbo run typecheck` over the seven touched packages and their
closure: 68 of 68 tasks.
- **Ablation**, on `b2fec9a9` (before the `main` merge; the ablated line
is unchanged by it). `node scripts/ablation-replace.mjs` replaced the
refusal line in `resolveSequenceTenantId`. The landing was proven on
disk: anchor count 1 to 0, blob `0af77d32fad3` to `0828ff0cfadb`. Then
`sql-driver-organizationless-counter-refusal.test.ts` went 2 failed, 3
passed: both refusal cases red, the three `__global__`/carried cases
still green. Restored: blob equals HEAD and `git diff HEAD` is empty.
- **Gates.** `node scripts/pm/dispatch-gates.mjs --commands` on the
final head derives 124 commands over the 24 paths. All 124 ran, each
exit code captured to a file before any pipe. `--ran` reconciles 124 of
124.
- 122 exited 0, among them `check:adr-0087-registration`,
`check-changeset-no-major`, `check:error-code-provenance`,
`check:cli-test-child-env`, `check:type-check-debt` and the spec
`check:generated` (14 artifacts up to date).
- `check-engine-split-ratio --days 90` (exit 2) and
`check-plugin-teardown-shape --self-test` (exit 3) refuse in this
shallow clone: they need history older than the clone. NOT MEASURED
here; this is checker health, not this diff.
## Acceptance notes
- **Finding, batched by the seat into the Tier H ADR notes PR.**
ADR-0131 D9 reads "a write on a tenant-column object with no
organization is derived under single and refused otherwise".
`Engine.resolveSystemInsertOrganization`'s `no-organization-object` exit
still writes NULL. After this PR no served composition reaches that exit
(`os serve` refuses it); a lean embedding still does, by design.
- **`liveConditions` in `os migrate duplicates`.** The JSON shape is
unchanged; only its meaning narrowed from forecast to residue.
`content/docs/deployment/cli.mdx` still calls it "about to mint more
duplicates". That page is outside this part's file surface, so the
correction is left to the seat.
- **The spec `.describe()` on `DeclaredIndexSchema.unique` and the
`object.zod.ts` comment** describe the `COALESCE` DDL emitters, which
are 7b-2's (held for a ruling). They are untouched.
- **Turso remote face.** The refusal reaches it through the shared
`resolveSequenceTenantId`. No new remote-face test was added; the
driver-turso suite is green.
- **Files outside the claimed surface, each forced by the widened one:**
- `scripts/check-cli-test-child-env.mjs`: the new refusal pin joins its
pinned census of files that spawn `bin/run.js` (13 to 14). The gate and
its self-test pass.
- `packages/cli/test/helpers/serve-process.ts`: gains `E2E_AUTH_SECRET`.
- `uncreatable-directory-doors.integration.test.ts` and
`serve-ready-degraded-boot.e2e.test.ts`: production server rigs whose
property is not auth. The first now hands in the secret; the second
boots `--no-server`. No assertion was weakened.
- **Not touched:** `docs/adr/**`, `content/docs/releases/`, the
`COALESCE(…,'__global__')` DDL emitters, the hash shadow and the index
differ (7b-2), and historic migration entries.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01H7Dv7HSDeT8d2fbDq3myx5
---
_Generated by [Claude
Code](https://claude.ai/code/session_01H7Dv7HSDeT8d2fbDq3myx5)_
---------
Co-authored-by: Claude <noreply@anthropic.com>__global__ counter, and a production server boot with no auth secret is refused (18.x step 7, part 7b-3) (#24845)1 parent de9cd1f commit df8bb40
24 files changed
Lines changed: 644 additions & 88 deletions
File tree
- .changeset
- packages
- cli
- src/commands
- migrate
- test
- helpers
- drivers
- driver-sql/src
- driver-turso/src
- lint/src
- objectql/src
- tenancy
- runtime/src
- spec/src
- api
- data
- scripts
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
48 | 54 | | |
49 | 55 | | |
50 | 56 | | |
| |||
154 | 160 | | |
155 | 161 | | |
156 | 162 | | |
157 | | - | |
| 163 | + | |
158 | 164 | | |
159 | | - | |
160 | | - | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
161 | 169 | | |
162 | 170 | | |
163 | 171 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4674 | 4674 | | |
4675 | 4675 | | |
4676 | 4676 | | |
| 4677 | + | |
| 4678 | + | |
| 4679 | + | |
| 4680 | + | |
| 4681 | + | |
| 4682 | + | |
| 4683 | + | |
| 4684 | + | |
| 4685 | + | |
| 4686 | + | |
| 4687 | + | |
| 4688 | + | |
| 4689 | + | |
| 4690 | + | |
| 4691 | + | |
| 4692 | + | |
| 4693 | + | |
| 4694 | + | |
| 4695 | + | |
| 4696 | + | |
| 4697 | + | |
| 4698 | + | |
| 4699 | + | |
4677 | 4700 | | |
4678 | 4701 | | |
4679 | 4702 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
792 | 792 | | |
793 | 793 | | |
794 | 794 | | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
795 | 805 | | |
796 | 806 | | |
797 | 807 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
48 | 48 | | |
49 | 49 | | |
50 | 50 | | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
51 | 57 | | |
52 | 58 | | |
53 | 59 | | |
| |||
92 | 98 | | |
93 | 99 | | |
94 | 100 | | |
95 | | - | |
| 101 | + | |
96 | 102 | | |
97 | 103 | | |
98 | 104 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
74 | 74 | | |
75 | 75 | | |
76 | 76 | | |
77 | | - | |
| 77 | + | |
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
| |||
171 | 171 | | |
172 | 172 | | |
173 | 173 | | |
174 | | - | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
175 | 183 | | |
176 | 184 | | |
177 | 185 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
32 | 40 | | |
33 | 41 | | |
34 | 42 | | |
| |||
0 commit comments