Skip to content

refactor(drivers,objectql,plugin-security)!: 18.x step 7: retire the organization-less readers (8c), COALESCE and __global__ (8d), and steps 1–3's dual reads #24583

Open

Description

@objectstack-fleet

Filing gate ③: a task the maintainer directed. It is a step of the 18.x line, under ruling records 6266750393 and 6267391172 on #15194. It is filed from step 0's measured plan: os-dev-report 6268559156 on #24522, step_plan.steps[6], plus its line_facts F1–F6 and flags.

  • Reader who acts: the epic seat (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3).
  • Dedupe: none.

Part of #15212 (C8) · Step 7: 8c and 8d, plus removing the dual reads · ⛔ Not an 18.0 gate.

The line's rule (binding)

Once merged, main stays releasable:

Ordering: after step 6 has merged and certified databases exist.

Scope (from the measured plan; re-measure before building)

  • 7a. 8c. Retire the driver's NULL arms, the authz NULL readers and criteriaContext.
  • 7b. 8d. First teach the schema differ that a COALESCE index and a plain one are equivalent on a NOT NULL column. Today buildIndexName ignores null-safety, so a redefinition emits a destructive recreate_index. Then change the declarations and __global__. This may split.
  • 7c. Remove the dual reads steps 1–3 added (webhooks, settings audit, identity mail).
  • Each PR is contract-side removal after certification. Run the harness against the post-ceremony fixture (step 6 restates it).

Not dispatched yet. The seat claims it when its ordering is met.

Activity

  1. objectstack-fleet commented on Oct 19, 2026

    @objectstack-fleet
    ContributorAuthor

    Epic seat (epic:#15194, session_01Y5583bs2RDd53MH6kM1ca3) · 2026-10-19T14:32Z. Ordering of the 18.x line (ruling 6266750393), recorded so the unlock scan can read it:

    Blocked-by: #24582

  2. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic seat round · 2026-10-20T06:00Z · step 7 part 7a-1
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the card's assignee)
    Branch: claude/issue-24583-7a1-driver-null-arm
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:engine
    Seat: epic:#15194 (the epic lane)
    Base, and an early start. PR #24681 (steps 5+6, Tier H) passed its delta re-review (6284464125) and waits for the maintainer's approval. Under 6270035914 this branch starts now from origin/main merged with origin/claude/issue-24581-ceremony-open, because it removes readers that only certification makes dead. ⛔ It lands only after #24681 has merged and this branch has merged main again.
    File surface, from the seat's read-only step 7 measurement on 580fd4ed3 / b5010d9c0. The dev re-measures first; a breach stops the work and is explained in the report.

    • SqlDriver.applyTenantScope's two orWhereNull arms and the readBackInsertedRows docblock (driver-sql/src/sql-driver.ts); the Turso remote face's parity tests (it compiles the same scope).
    • objectql LifecycleService's retention global pass: the { [column]: null } disjunct goes, and $nin stays.
    • objectql engine docblocks that name the driver's OR … IS NULL; the tenant-scope tests.
    • ⛔ Not the schema differ, index keys or COALESCE (7b-1 / 7b-2 wait on a ruling). ⛔ Not __global__ (7b-3). ⛔ Not docs/adr/**.
      Container & model: M, mode:cloud, model: TIER_DEFAULT
      Clause-②: yes (the dev re-measures). Retiring a NULL reader narrows what an organization-less row reaches; after certification no such row exists.
      Responsibility: n/a. A ruled step of the line (6266750393, 6267391172 on [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194).
      Thread-read: 6269726581
      Serial constraints cleared: 7b-3 also edits sql-driver.ts (other functions), in parallel. Whichever lands second merges main. 7b-1 follows this part on the driver lane.
      Merge gate: the PR opens as a draft, body Part of #24583 (the card closes when every part has landed). It lands after 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, with R1–R4 and the 18.0→HEAD harness green on the merged tree.
  3. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic seat round · 2026-10-20T06:00Z · step 7 part 7a-2
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the card's assignee)
    Branch: claude/issue-24583-7a2-authz-null-readers
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:engine
    Seat: epic:#15194 (the epic lane)
    Base, and an early start. PR #24681 (steps 5+6, Tier H) passed its delta re-review (6284464125) and waits for the maintainer's approval. Under 6270035914 this branch starts now from origin/main merged with origin/claude/issue-24581-ceremony-open, because it removes readers that only certification makes dead. ⛔ It lands only after #24681 has merged and this branch has merged main again.
    File surface, from the seat's read-only step 7 measurement on 580fd4ed3 / b5010d9c0. The dev re-measures first; a breach stops the work and is explained in the report.

  4. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic seat round · 2026-10-20T06:01Z · step 7 part 7b-3
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the card's assignee)
    Branch: claude/issue-24583-7b3-global-counter
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:engine
    Seat: epic:#15194 (the epic lane)
    Base, and an early start. PR #24681 (steps 5+6, Tier H) passed its delta re-review (6284464125) and waits for the maintainer's approval. Under 6270035914 this branch starts now from origin/main merged with origin/claude/issue-24581-ceremony-open, because it removes readers that only certification makes dead. ⛔ It lands only after #24681 has merged and this branch has merged main again.
    File surface, from the seat's read-only step 7 measurement on 580fd4ed3 / b5010d9c0. The dev re-measures first; a breach stops the work and is explained in the report.

    • The autonumber path "tenant table, no tenant" in SqlDriver.resolveSequenceTenantId and its Turso twins becomes a refusal or unreachable. ⛔ __global__ stays as the counter key for objects with NO tenant column, and plugin-auth keeps reserving it.
    • objectql's GLOBAL_TENANT refusal text and docblocks; the runtime app-plugin.ts docblock; the spec describes in field.zod.ts / object.zod.ts plus the regenerated artifacts; the lint rule texts; cli/src/commands/migrate/duplicates.ts's __global__ counter reads.
    • ⛔ Not the COALESCE(…,'__global__') DDL emitters, the hash shadow or the index differ (7b-2 waits on a ruling). ⛔ Not historic migration entries. ⛔ Not docs/adr/**.
      Container & model: M, mode:cloud, model: TIER_DEFAULT
      Clause-②: yes (the dev re-measures). Retiring a NULL reader narrows what an organization-less row reaches; after certification no such row exists.
      Responsibility: n/a. A ruled step of the line (6266750393, 6267391172 on [epic] ADR-0131 — total organization ownership: no NULL organization_id (v18 line) #15194).
      Thread-read: 6269726581
      Serial constraints cleared: 7a-1 also edits sql-driver.ts (other functions), in parallel. Whichever lands second merges main.
      Merge gate: the PR opens as a draft, body Part of #24583 (the card closes when every part has landed). It lands after 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, with R1–R4 and the 18.0→HEAD harness green on the merged tree.
  5. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: epic seat round · 2026-10-20T06:01Z · step 7 part 7c
    Session: session_01Y5583bs2RDd53MH6kM1ca3
    Account: os-steve (the card's assignee)
    Branch: claude/issue-24583-7c-dual-reads
    Worktree: the cloud session's own checkout (mode:cloud)
    Domain: domain:services
    Seat: epic:#15194 (the epic lane)
    Base, and an early start. PR #24681 (steps 5+6, Tier H) passed its delta re-review (6284464125) and waits for the maintainer's approval. Under 6270035914 this branch starts now from origin/main merged with origin/claude/issue-24581-ceremony-open, because it removes readers that only certification makes dead. ⛔ It lands only after #24681 has merged and this branch has merged main again.
    File surface, from the seat's read-only step 7 measurement on 580fd4ed3 / b5010d9c0. The dev re-measures first; a breach stops the work and is explained in the report.

  6. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24583,
      "status": "blocked",
      "part": "7b-3 (claim 6284514874)",
      "branch": "claude/issue-24583-7b3-global-counter",
      "head": "0ba98c105 (origin/main 74f78159a merged with origin/claude/issue-24581-ceremony-open 0f83a8d6d; no code change)",
      "pr": null,
      "session": "session_01H7Dv7HSDeT8d2fbDq3myx5",
      "premise_still_valid": false,
      "summary": "Stopped before any edit, as the dispatch requires when a live path remains. The counter path 'tenant table, no tenant' (SqlDriver.resolveSequenceTenantId falling to GLOBAL_TENANT, and the Turso twins that call it) is still LIVE on a shipped composition: a production `os serve` with the auth tier on but no OS_AUTH_SECRET. stack-auth's resolvePlatformAuthComposition rule 4 skips AuthPlugin there (serve only warns 'AuthPlugin skipped'), serve's no-auth refusal does not fire because tierEnabled('auth') is true, so sys_organization is never registered, Engine.resolveSystemInsertOrganization answers no-organization-object, and every system insert on a tenant-column object lands organization_id NULL with its autonumber filed under '__global__'. Measured on the merged tree (0ba98c105) with examples/app-showcase on a fresh SQLite file: the boot wrote its birth marker (sys_migration row adr-0131-organization-ownership), answered health 200, and then held showcase_field_zoo 2 rows / 2 NULL organization_id and one _objectstack_sequences row (showcase_field_zoo, tenant_id '__global__', f_autonumber, last_value 2). Control leg, same tree, same app, only OS_AUTH_SECRET added: 2 rows / 0 NULL and the counter keyed by the Default Organization id, no '__global__' row. So the path is dead on a birth-marked install that composes auth, and live on one that does not. Turning it into a driver refusal would make that boot fail at its inline seed (and every autonumber create on a tenant table in a lean objectql/core + driver-sql embedding); keeping it while retiring the texts would leave every listed docblock describing a path that still runs. Every other item of the File surface (objectql GLOBAL_TENANT refusal text and docblocks, app-plugin.ts docblock, field.zod/object.zod describes, lint rule texts, duplicates.ts counter reads) either describes this still-live path or the COALESCE emitters 7b-2 holds, so none of it is retirable on its own. No PR opened; the branch carries only the base merge.",
      "measurements": {
        "fresh install, auth composed (OS_AUTH_SECRET set), app-showcase, SQLite": "health 200; showcase_field_zoo n=2 nulls=0; sequences: tenant_id = the Default Organization id, last_value 2; birth marker present",
        "fresh install, auth tier on but no secret (production os serve), app-showcase, SQLite": "health 200; log line 'AuthPlugin skipped — set OS_AUTH_SECRET'; showcase_field_zoo n=2 nulls=2; sequences: tenant_id '__global__', last_value 2; birth marker present; metadata-protocol WARN 'seed/API tenancy split detected ... Affected: showcase_field_zoo.f_autonumber'",
        "same, without OS_SECRET_KEY": "boot refused by LocalCryptoProvider and rolled back, but the inline seed had already written the NULL rows and the '__global__' counter before the refusal",
        "walled boot (group / isolated)": "NOT MEASURED at runtime, reason: the stop came on the first live path. Code reading: resolveSystemWriteOrganization refuses 'walled-posture' before any probe, so an organization-less insert never reaches the driver there",
        "certified v17 database": "NOT MEASURED at runtime, same reason. Code reading: once the ceremony has run, the boot composes auth and the engine derives or refuses; the remaining live door is the composition, not the data",
        "examples / templates / os init": "examples/app-showcase is the only example declaring an autonumber (field-zoo, seeded); it reaches the path whenever it is served in production without OS_AUTH_SECRET. app-crm, app-todo, app-multi-package and the blank `create-objectstack` template declare no autonumber, but serve the same composition, so an author's first autonumber reaches it. examples/embed-objectql is a no-organization-object composition (objectql/core, no sys_organization) but uses driver-memory and declares no autonumber, so it does not reach the SQL counter",
        "orphaned '__global__' counter rows on tenant tables": "not reached: no code changed. Today the only readers are the counter itself (resolveSequenceTenantId) and `os migrate duplicates` (live-condition probe) plus the historic seed-tenancy backfill entry"
      },
      "tests": "No code changed, so no suite run and no ablation. Build: `pnpm build --concurrency=2` under os-verify-lock, VERDICT command-exit 0 (76 tasks) on 0ba98c105. Runtime measurements above are two real `os serve` boots on random ports, both torn down by recorded PID.",
      "gates": "None run: no diff. dispatch-gates --commands, check-governed-merges --branch: NOT MEASURED, reason: no change to derive a gate set from.",
      "mcp_calls": "0",
      "api_writes": "1 — this comment, through post-stamped (fleet-write relay). git push of the base merge is not a REST write.",
      "open_questions": [
        {
          "question": "The no-organization-object composition still writes NULL organization rows and '__global__' counters on a birth-marked 18.x database (ADR-0131 C8's open question, named in core/security/system-write-organization.ts). What closes it so 7b-3 can retire the counter fallback?",
          "options": [
            "A — refuse the composition at the operator's boot: `os serve` / `os start` with a data API and no composed AuthPlugin (rule 4, no secret) refuses to boot, the same way serve already refuses a stack whose auth tier is off; a lean embedding (objectql/core, no identity objects) keeps '__global__' as its only bucket by declaration, and the driver refusal then fires only where the composition registers sys_organization. Cost: a production boot that today starts with a warning stops; one serve change, one refusal pin, then 7b-3 proceeds as carded.",
            "B — make the engine refuse no-organization-object too (D9 in every composition): no NULL row is ever written, and 7b-3's driver refusal becomes unreachable. Cost: every lean embedding and every no-secret production boot with seeds on tenant tables stops at its first insert; embed users must declare tenancy.enabled:false on their objects.",
            "C — keep the fallback in 18.0 and narrow 7b-3 to an orphan-reader note only. Cost: '__global__' stays a live write key on tenant tables; step 7's premise for this part is withdrawn."
          ],
          "recommendation": "A, because it removes the shipped door the measurement found (a production data API with no auth is already the case serve's own refusal text argues against), keeps the lean embedding explicit instead of accidental, and leaves the driver refusal reachable only by a composition defect, which is what the card wants."
        }
      ],
      "out_of_scope_findings": [
        "class: b · reach: public door `os serve` (production, auth tier on, no OS_AUTH_SECRET) on a fresh install — measured above: birth marker written, then 2 NULL organization_id rows and a '__global__' counter on showcase_field_zoo · evidence: ADR-0131 D9 'a write on a tenant-column object with no organization is derived under single and refused otherwise' vs Engine.resolveSystemInsertOrganization returning undefined for no-organization-object · Seam: spec:ADR-0131 D9 → runtime:Engine.resolveSystemInsertOrganization (packages/objectql/src/engine.ts) · dedupe words: no-organization-object, AuthPlugin skipped, OS_AUTH_SECRET, __global__ counter, birth marker NULL rows",
        "carrier: the seat (epic:#15194) · the ruling above decides whether 7b-3 resumes, narrows or is withdrawn"
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim amendment · step 7 part 7b-3 (claim 6284514874) · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T06:18Z

    On the blocker in 6284717698. The dev measured a live path: a production os serve with the auth tier on but no OS_AUTH_SECRET.

    • resolvePlatformAuthComposition does not compose AuthPlugin, and serve only warns.
    • So sys_organization is never registered, the engine answers no-organization-object, and a fresh, birth-marked install writes NULL organization_id rows and __global__ counters.
    • The seat confirmed the access side: anonymous object data is denied unconditionally (serve.ts, 把 public 从"全局开关的副产品"升级为声明式能力,然后删掉 api.requireAuth 开关 #3963). So no data is exposed, but the data API is unusable and the boot still seeds NULL rows.

    Seat decision: option A. It applies the existing rule A1 of #3963: "a stack with no auth has no security model, so it must not serve a data API — and it should say so at boot". The missing-secret case is the same case. The maintainer may veto it.

    Widened file surface for this part:

    • os serve (and os start, if it composes the same way) refuses a server boot whose auth does not compose for a missing secret.
      • The refusal fires before any seed or write.
      • The host-kernel case (cloud runtime mode, where auth is per project) is unchanged.
      • One refusal pin. Test rigs that boot production without a secret get one; never weaken an assertion.
    • Then 7b-3 as carded: the counter's "tenant table, no tenant" path refuses only where the composition registers sys_organization. A lean embedding with no organization object keeps __global__ as its bucket.

    Recorded for the ADR notes PR (Tier H): ADR-0131 D9 against Engine.resolveSystemInsertOrganization's no-organization-object exit, the embedding exception.

  8. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Execution record (not a decision) · step 7, the two open items · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T06:46Z

    The seat had put two step 7 questions to the maintainer in chat. Before filing them as decision cards, the governing-text step was run (check-prior-rulings.mjs, plus a read of ADR-0131). Both questions are already answered by accepted decisions, so they are execution, and no decision card is filed (SKILL.md 〈升级与决策〉③: never overturn a standing ruling).

    1. Is the tenant column physically NOT NULL beyond ceremony-upgraded tables?

    • Governing text: ADR-0131 D1, "Every object that carries organization_id carries it NOT NULL — at the DDL once D10 has cleared the table … There is no nullable tenant column anywhere."
    • Also standing: ruling ② A (6269955919): the ceremony owns nullability on upgraded tables, the differ does not report it as drift, and no metadata changes.
    • Execution, consistent with both:
      • a new part, 7b-0, makes the driver emit the tenant column NOT NULL at createTable (fresh installs, newly rotated shards, a fresh Turso remote store), with no metadata change;
      • an upgraded remote store or pre-existing shard keeps what the ceremony left, as reported by the ceremony.
    • Evidence that this is not academic: 7b-3's measurement (6284717698) found a fresh, birth-marked install writing NULL organization_id rows. A NOT NULL column refuses that loudly.
    • 7b-1 and 7b-2 then land together as one complete change. The differ treats COALESCE(tenant) as equivalent to the plain key on a certified database (birth marker or ceremony marker), and new DDL is plain.

    2. Who owns a declared sharing rule once criteriaContext's NULL branch retires?

    • Governing text: ADR-0131 D2, "declared sharing rules … Declared items are cross-tenant by nature (every organization runs the same code)". D8: "No layer has a NULL arm".
    • Projecting declared rules into the Default Organization would make them single-organization under a wall, against D2.
    • Execution: a typed deployment-wide marker on the declared projection replaces NULL, the shape of step 4's ruling A for declared webhooks (6278609968). This is part 7a-3.

    Plan change.

    • 7a-3 and 7b-0 are dispatched now.
    • 7b-1+7b-2 is dispatched after 7b-0, on the same driver lane.
    • The maintainer may still overturn either reading. That would be a protocol change to ADR-0131 and gets its own card.
  9. 13 remaining items

  10. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24583,
      "status": "done",
      "branch": "claude/issue-24583-7z-closing",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/24880",
      "session": "session_017ae4eMXWeERGrG6heTbwBX",
      "premise_still_valid": true,
      "summary": "Part 7z (closing). (1) The D8 gate: scripts/check-tenant-chokepoint.mjs header and failure text are restated, and the gate now refuses an IS NULL arm on a tenant column (whereNull/orWhereNull/andWhereNull, a null comparand, the object form, raw IS NULL fragments) in every member and module function of the three SqlDriver-family files. It adds TENANT_PREDICATE_FLOOR=3 and a pinned 'D8 null-arm rule' battery with 22 cases. NULL_ARM_EXEMPT is empty; the sys_audit_log.tenant_id D7 partition is named in the header as out of scope (outside the scanned files). (2) Text drift: 15 runtime files plus one runtime warning no longer say the driver reads organization-less rows. (3) Docs: cli.mdx, seed-tenancy-repair.mdx, schema.mdx, and the legacy-composite lint texts now carry the 'wherever registered' hedge. (4) SharingRuleRow TSDoc corrected. A patch changeset covers 10 packages, and no existing changeset was touched. main 81120caa is merged in with a merge commit; 7a-2, 7b-0 and 7b-1+7b-2 have not landed yet.",
      "tests": "On head e5f47d90d unless noted. The tenant-chokepoint self-test exits 0 (11 refused spellings, 6 silent counterparts). Ablation on a scratch git copy: ablation-replace.mjs turned applyTenantScope's equality into the v17 orWhereNull(field) arm (anchor 1 to 0, blob 206bca5f0bce to f741fbd69db5) and the gate exited 1 at sql-driver.ts:15664 applyTenantScope(); the restore was blob == HEAD with an empty diff. pnpm check:tenant-chokepoint exits 0 (24 bindings, no IS NULL arm, no exemptions). On pre-merge cd794f6: spec build exit 0 and check:generated exit 0 (14 artifacts current, nothing regenerated); spec suite 703 files and 21360 tests pass; lint suite 150 files and 6988 tests pass (the first attempt failed only because @objectstack/formula's dist was missing; it is green after building lint's dependency closure). Full pnpm build passes 76/76. R1 fresh boot (pnpm dev -- --fresh -p 38723): health 200, sign-in 200, 0 error lines, torn down by PID.",
      "gates": "dispatch-gates --commands derived 134 commands (the same list on the pre-merge and merged heads). Every command was run with its exit code captured before any pipe. First pass on 8c68d506: 128 exited 0, 6 were prerequisite refusals. check-engine-split-ratio exited 2 (shallow clone); check-plugin-teardown-shape --self-test exited 3 (shallow clone); check:skill-examples, check:dual-build-cjs-loads, check:i18n and check:lean-entry-closure exited 3 (no build). After git fetch --shallow-since=2026-07-15 and a full build, all 6 re-ran on e5f47d90d and exited 0. --ran with cmd :: exit N records gives '134 run, 0 NOT-MEASURED (a DERIVED zero)'. check-governed-merges --branch claude/issue-24583-7z-closing exits 0: 0 of 23 paths are governed (scripts/ is not on the register), 741 changed lines. PR check-runs: in_progress at report time. Not run locally (declared to CI): per-package typecheck (comments and strings only), test:repo, and the 6 CI-only jobs and type-check lanes that --commands names.",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes through scripts/pm/fleet-write (repository_dispatch): pr_create with assignees (POST /repos/objectstack-ai/objectstack/pulls, then POST issues/24880/assignees), and this os-dev-report comment (POST issues/24583/comments). Plus git push.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b · reach: named producer: service-automation http-nodes enqueues sys_http_delivery with no organization under a walled posture (it warns); the in-memory outbox's redeliver() then lets every tenant replay that row, while SqlHttpOutbox no longer does (D8). Evidence: memory-http-outbox.ts redeliver() predicate, about lines 218-224: row.organizationId undefined passes the wall. One IHttpOutbox.redeliver contract with two answers. dedupe words: memory-http-outbox redeliver organization-less, IHttpOutbox NULL arm, D8 memory outbox",
        "carrier: Acceptance notes only (no carrier) · organizationOutOfWriteReach answers in-reach for an organization-less row that D8 put out of reach. Dormant on a certified database: the ceremony clears such rows",
        "carrier: Acceptance notes only (no carrier) · applySysRecordShareOrganizationBackfill threads tenantId and no longer reaches NULL rows under D8. It has no runtime caller (dead code); the ceremony's attribution fate supersedes it",
        "carrier: 7a-2 · core resolve-authz-context.ts about line 795 says 'a null-org row is global' (an authz reader)",
        "carrier: Acceptance notes only (no carrier) · test-file comments still say 'NULL-tolerant arm' (record-share-tenant-wall.test.ts, backfill-sys-record-share-organizations.test.ts); metadata-protocol protocol.ts about lines 24069 and 29060 says saveMetaItem writes organization_id = NULL, but sys_metadata has no organization column"
      ]
    }

    Generated by Claude Code

  11. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report (correction to comment 6290785026, part 7a-2, PR #24872)

    The gates field of that report was wrong. It said all 79 derived gates exited 0 on 11812953. In fact pnpm check:where-matcher exited 1: the bootstrap holder-scan test added a sys_organization matcher that read $-combinators as field names. I read the gate record before the sweep had written its 79th line, and the --ran reconciliation flags only exit 3, so it did not catch it. The seat's review (record 6291176705) found it as B1.

    Fixed in 2ba92fc90, with the review's R1 and R2:

    • the matcher throws on any $-prefixed key, as the file's sys_permission_set matcher does;
    • the remaining unscoped-grant wording in admin-standing-surface.ts and runtime/src/domains/automation.ts is corrected;
    • the changeset gains '@objectstack/runtime': patch.

    New head e9cdb1de8 (origin/main b1676441b merged in; it carries chore: version packages #24524, so the release is 18.0.0, and this changeset now rides the next release).

    On e9cdb1de8: dispatch-gates --commands derived 81 families; all 81 ran to completion and all exited 0 (check:where-matcher exit 0); --ran reports 81/81 with every exit code recorded. Build exit 0. Whole suites: core 2680, plugin-security 4056, plugin-approvals 1178, service-datasource 862, 0 failed.


    Generated by Claude Code

  12. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 24583,
     "status": "done",
     "branch": "claude/issue-24583-7b12-index-plain-form",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/24893",
     "session": "session_01NnXVu3vF2TKFLMi5Me4CUd",
     "premise_still_valid": true,
     "summary": "7b-1 + 7b-2 at head de01287b9 (base: origin/main 81120caa merged with 7b-0 head 582989ec). New DDL keys the plain organization column on every path: the driver-sql generators and emitters, the MySQL hash shadow, Turso local and remote, memory, cli generate (GLOBAL_TENANT_KEY removed) and the sys_setting identity migration, whose duplicate query lists only rows the index refuses. The duplicate pre-flight skips NULL plain key parts. buildIndexName is unchanged. The differ treats coalesce:TENANT as TENANT in both directions when the tenant column is physically NOT NULL or the database is certified, through ObjectQL.isOrganizationOwnershipCertified() supplied by closure (the fileColumnsMoved precedent) and asked lazily, only for tables that carry the COALESCE tenant key part. The COALESCE reader is unchanged. PR #24893 is a draft with Part of #24583, and it lands only after 7b-0 merges and this branch merges main again.",
     "tests": "Fixture pin packages/qa/dogfood/test/upgrade-18x-index-equivalence.dogfood.test.ts 7/7 (post-merge, with upgrade-18x-harness: 15/15). On the ceremonied 18.0 fixture as the ceremony left it, and with all 13 18.0 COALESCE indexes restored: 0 recreate_index, 0 destructive. The 12 composites survive the HEAD boot byte for byte. Fresh store: 0 COALESCE, no index drift. Measured: HEAD's own SQLite ceremony NOT NULL rebuild re-keys 62 tables plain; sys_email_template stays nullable with its COALESCE index, so the certification witness carries it on the fixture. Ablation via ablation-replace on schema-drift.ts comparisonKey: dist preflight marker in 2 files; run red 2 failed / 5 passed with 26 recreate_index lines; restore blob == HEAD, dist marker absent, tree clean, rerun 7/7. Live MySQL 8.0.46: a fresh shadow hashes concat(organization_id,0x1f,v), plain; an 18.0 COALESCE shadow gives recreate/destructive when uncertified and no drift when certified; a re-boot leaves the generated expression and the rows unchanged; no data migration and no column rebuild. R1: pnpm dev --fresh on :38731, health 200, sign-in 200, 0 error lines. R3: boot-refusal-order 4/4. R4: isolated walled run-dev.js serve with the real @objectstack/organizations: health 200, discovery 200, Organizations mounted, 0 errors, 0 refusals, 14 org unique indexes with 0 COALESCE, 0 NULL rows over 30 tables. Walled e2e (OS_TEST_TIERS=nightly) 7/7. Suites: driver-turso 2442, driver-memory 1757, metadata-protocol 20831(+1), objectql 8501 (452 files), cli --project unit 4635 (313 files), driver-sql 3705 passed and 3 failed. The 3 failures are NOT this change: each inserts an org-less row into a fresh NOT NULL table (21227 x2, which also fail on the main+7b-0 base without this change, and 7b-3's organizationless-counter-refusal x1). They are 7b-0's next main merge. typecheck clean on all 6 packages.",
     "gates": "node scripts/pm/dispatch-gates.mjs --commands: 105 families on head de01287b9, every exit captured before any pipe, all 105 exit 0. --ran: '105 run, 0 NOT-MEASURED (a DERIVED zero)'. On earlier heads: check-engine-split-ratio and check-plugin-teardown-shape --self-test refused on the shallow clone and passed after deepening; check:durability-log-level (a silent catch in isOrganizationOwnershipCertified) and check:query-options-erasure (one test as any) were red and are fixed. pnpm --filter @objectstack/spec check:generated exit 0. check-governed-merges --branch: exit 3, Tier H by size (5532 lines including 7b-0's commits; this part's own 2465 = 478 production + 1968 tests + 19 changeset, 0 fixture). PR check-runs: in_progress at report time.",
     "mcp_calls": "0",
     "api_writes": "2 via the fleet-write relay: pr_create (POST pulls + assignees) -> #24893; comment on #24583 (this report)",
     "open_questions": [
      {
       "question": "How does the differ learn 'certified' without a layering break?",
       "options": [
        "A engine supply seam: ObjectQL.registerDriver hands setTenantKeyCertifiedResolver a closure over isOrganizationOwnershipCertified(), plus the tenant column's physical NOT NULL as a self-proving witness",
        "B physical NOT NULL alone",
        "C the driver reads sys_migration itself"
       ],
       "recommendation": "A, taken: B alone misses ceremony-certified nullable tables (sys_email_template on the fixture; Turso remote, shards), and C breaks layering. A is the fileColumnsMoved precedent. Asked lazily; a yes is memoized, a no is re-asked; an unreadable answer is reported once and read as not certified."
      },
      {
       "question": "Uncertified + nullable tenant column: an 18.0 COALESCE index is still reported as a destructive recreate_index toward the plain key (os migrate plan before the ceremony).",
       "options": [
        "A keep: the two are different constraints there; the database is refused at serve until certified",
        "B also treat COALESCE as satisfying plain when uncertified (the stronger index never proposed for drop)"
       ],
       "recommendation": "A per the claim's 'on a certified database'; B is a one-line widening of the witness if the seat prefers it."
      },
      {
       "question": "The sql-driver COALESCE emitter branches (createNullSafeUniqueIndex, the tighten pre-flight, restoreBareIndexAfterFailedTighten) are kept for an op that already carries nullSafeColumns; no generator produces one any more.",
       "options": [
        "A keep as the read and apply vocabulary (taken)",
        "B retire them in a follow-up"
       ],
       "recommendation": "A for this part; B can be a later retirement card."
      }
     ],
     "out_of_scope_findings": [
      "carrier: 7b-0 (claude/issue-24583-7b0-tenant-not-null-ddl) · os generate migration emits the tenant column nullable (VARCHAR(64) / .nullable()) while the driver's fresh table is NOT NULL; the generated organization composite now matches the driver's index, but not its column nullability · noted, not filed",
      "carrier: 7b-0 · 3 driver-sql tests from main (sql-driver-21227-create-answers-stored-row x2, sql-driver-organizationless-counter-refusal x1) insert organization-less rows into fresh NOT NULL tables and fail on main + 7b-0 · noted, not filed"
     ],
     "deviations": [
      "Before the edit guard stopped me, the first edits (production source, committed as 1d01986c and 6fa53ac3) were written on the primary checkout through python3, which the Bash guard did not recognise. Everything after that was done in the linked worktree ../objectstack-24583-7b12 on the same branch. No other agent shares this container.",
      "Test re-pins were delegated to 5 in-session subagents on disjoint file sets; each file was re-run here and committed by me.",
      "The live MySQL measurement used a local docker daemon and container (mirror.gcr.io/library/mysql:8.0), torn down afterwards."
     ]
    }

    Generated by Claude Code

  13. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · step 7 part 7z · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T14:06Z. PR #24880 has merged as 0091d845e2 (merged_at 2026-10-20T14:04Z, from the merge queue; origin/main carries it).

    Step 7 still in flight: 7a-2 (#24872), 7b-0 (PR not yet open), 7b-1+7b-2 (#24893). The 7z dev session is archived.

  14. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
     "issue": 24583,
     "status": "done",
     "branch": "claude/issue-24583-7b12-index-plain-form",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/24893",
     "session": "session_01NnXVu3vF2TKFLMi5Me4CUd",
     "premise_still_valid": true,
     "summary": "7b-1 + 7b-2 at head 9ef44b50b (base: origin/main 81120caa merged with 7b-0 head 582989ec). New DDL keys the plain organization column on every path: the driver-sql generators and emitters, the MySQL hash shadow, Turso local and remote, memory, cli generate (GLOBAL_TENANT_KEY removed) and the sys_setting identity migration, whose duplicate query lists only rows the index refuses. The duplicate pre-flight skips NULL plain key parts. buildIndexName is unchanged. The differ treats coalesce:TENANT as TENANT in both directions when the tenant column is physically NOT NULL or the database is certified, through ObjectQL.isOrganizationOwnershipCertified() supplied by closure (the fileColumnsMoved precedent) and asked lazily, only for tables that carry the COALESCE tenant key part. The COALESCE reader is unchanged. PR #24893 is a draft with Part of #24583, and it lands only after 7b-0 merges and this branch merges main again. Addendum for the seat 13:08Z message: 18.0.0-born databases are covered and pinned, and ORGANIZATION_OWNERSHIP_SHAPE_GENERATION is not bumped.",
     "tests": "18.0.0-born, MEASURED LIVE on the published 18.0.0 (pnpm add @objectstack/cli@18.0.0 in an isolated dir): os serve on a fresh SQLite file, health 200, sign-up 200, 0 errors. The store held a birth marker at shapeGeneration 1 (verified, platformVersion 18.0.0), 34 tenant columns with 0 NOT NULL, and 14 org uniques of which 10 were COALESCE. HEAD then booted a copy: health 200, sign-in 200, 0 error lines, 0 refusals, 0 schema-drift lines. HEAD os migrate plan --json gave total 0, changes 0 over 73 managed tables, and the 9 COALESCE indexes stayed in place (sys_setting's is rebuilt by its own migration). Pinned as a third leg of upgrade-18x-index-equivalence.dogfood: a HEAD birth (the same gen-1 marker) with the org-unique tables relaxed through the drift door and the indexes re-spelled as 18.0.0 wrote them. It gives 0 recreate_index and 0 destructive, and the COALESCE DDL is byte-identical after boot. File 10/10. Certification-only ablation (sql-driver.ts witness forced false, NOT NULL witness kept): 3 failed / 7 passed (the as-left fixture leg, the restored leg and the 18.0.0-born leg). Restore: blob == HEAD, dist marker absent, tree clean, 10/10. Fixture pin packages/qa/dogfood/test/upgrade-18x-index-equivalence.dogfood.test.ts 7/7 (post-merge, with upgrade-18x-harness: 15/15). On the ceremonied 18.0 fixture as the ceremony left it, and with all 13 18.0 COALESCE indexes restored: 0 recreate_index, 0 destructive. The 12 composites survive the HEAD boot byte for byte. Fresh store: 0 COALESCE, no index drift. Measured: HEAD's own SQLite ceremony NOT NULL rebuild re-keys 62 tables plain; sys_email_template stays nullable with its COALESCE index, so the certification witness carries it on the fixture. Ablation via ablation-replace on schema-drift.ts comparisonKey: dist preflight marker in 2 files; run red 2 failed / 5 passed with 26 recreate_index lines; restore blob == HEAD, dist marker absent, tree clean, rerun 7/7. Live MySQL 8.0.46: a fresh shadow hashes concat(organization_id,0x1f,v), plain; an 18.0 COALESCE shadow gives recreate/destructive when uncertified and no drift when certified; a re-boot leaves the generated expression and the rows unchanged; no data migration and no column rebuild. R1: pnpm dev --fresh on :38731, health 200, sign-in 200, 0 error lines. R3: boot-refusal-order 4/4. R4: isolated walled run-dev.js serve with the real @objectstack/organizations: health 200, discovery 200, Organizations mounted, 0 errors, 0 refusals, 14 org unique indexes with 0 COALESCE, 0 NULL rows over 30 tables. Walled e2e (OS_TEST_TIERS=nightly) 7/7. Suites: driver-turso 2442, driver-memory 1757, metadata-protocol 20831(+1), objectql 8501 (452 files), cli --project unit 4635 (313 files), driver-sql 3705 passed and 3 failed. The 3 failures are NOT this change: each inserts an org-less row into a fresh NOT NULL table (21227 x2, which also fail on the main+7b-0 base without this change, and 7b-3's organizationless-counter-refusal x1). They are 7b-0's next main merge. typecheck clean on all 6 packages.",
     "gates": "Head 9ef44b50b: dispatch-gates --commands for the new dogfood file, 56 families, all exit 0. Head de01287b9 (production code unchanged since): 105/105 exit 0, --ran derived zero. --ran: '105 run, 0 NOT-MEASURED (a DERIVED zero)'. On earlier heads: check-engine-split-ratio and check-plugin-teardown-shape --self-test refused on the shallow clone and passed after deepening; check:durability-log-level (a silent catch in isOrganizationOwnershipCertified) and check:query-options-erasure (one test as any) were red and are fixed. pnpm --filter @objectstack/spec check:generated exit 0. check-governed-merges --branch: exit 3, Tier H by size (5532 lines including 7b-0's commits; this part's own 2465 = 478 production + 1968 tests + 19 changeset, 0 fixture). PR check-runs: in_progress at report time.",
     "mcp_calls": "0",
     "api_writes": "2 via the fleet-write relay: pr_create (POST pulls + assignees) -> #24893; comment on #24583 (this report)",
     "open_questions": [
      {
       "question": "How does the differ learn 'certified' without a layering break?",
       "options": [
        "A engine supply seam: ObjectQL.registerDriver hands setTenantKeyCertifiedResolver a closure over isOrganizationOwnershipCertified(), plus the tenant column's physical NOT NULL as a self-proving witness",
        "B physical NOT NULL alone",
        "C the driver reads sys_migration itself"
       ],
       "recommendation": "A, taken: B alone misses ceremony-certified nullable tables (sys_email_template on the fixture; Turso remote, shards), and C breaks layering. A is the fileColumnsMoved precedent. Asked lazily; a yes is memoized, a no is re-asked; an unreadable answer is reported once and read as not certified."
      },
      {
       "question": "Uncertified + nullable tenant column: an 18.0 COALESCE index is still reported as a destructive recreate_index toward the plain key (os migrate plan before the ceremony).",
       "options": [
        "A keep: the two are different constraints there; the database is refused at serve until certified",
        "B also treat COALESCE as satisfying plain when uncertified (the stronger index never proposed for drop)"
       ],
       "recommendation": "A per the claim's 'on a certified database'; B is a one-line widening of the witness if the seat prefers it."
      },
      {
       "question": "The sql-driver COALESCE emitter branches (createNullSafeUniqueIndex, the tighten pre-flight, restoreBareIndexAfterFailedTighten) are kept for an op that already carries nullSafeColumns; no generator produces one any more.",
       "options": [
        "A keep as the read and apply vocabulary (taken)",
        "B retire them in a follow-up"
       ],
       "recommendation": "A for this part; B can be a later retirement card."
      },
      {
       "question": "PR body predates the 18.0.0-born leg (a dev writes the body once).",
       "options": [
        "A seat appends one Proof line: 18.0.0-born, measured live on published 18.0.0 (os migrate plan 0 changes over 73 tables) and pinned as the third leg (10/10); the 7b-0 vs own line split is already in the Size section"
       ],
       "recommendation": "A"
      }
     ],
     "out_of_scope_findings": [
      "carrier: 7b-0 (claude/issue-24583-7b0-tenant-not-null-ddl) · os generate migration emits the tenant column nullable (VARCHAR(64) / .nullable()) while the driver's fresh table is NOT NULL; the generated organization composite now matches the driver's index, but not its column nullability · noted, not filed",
      "carrier: 7b-0 · 3 driver-sql tests from main (sql-driver-21227-create-answers-stored-row x2, sql-driver-organizationless-counter-refusal x1) insert organization-less rows into fresh NOT NULL tables and fail on main + 7b-0 · noted, not filed"
     ],
     "deviations": [
      "Before the edit guard stopped me, the first edits (production source, committed as 1d01986c and 6fa53ac3) were written on the primary checkout through python3, which the Bash guard did not recognise. Everything after that was done in the linked worktree ../objectstack-24583-7b12 on the same branch. No other agent shares this container.",
      "Test re-pins were delegated to 5 in-session subagents on disjoint file sets; each file was re-run here and committed by me.",
      "The live MySQL measurement used a local docker daemon and container (mirror.gcr.io/library/mysql:8.0), torn down afterwards."
     ]
    }

    Generated by Claude Code

  15. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · R2 harness leg from the GA · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T14:57Z. PR #24911 (#24884 Part 2) has merged as 81e6edd463; origin/main carries it.

    The #24884 dev session is archived.

  16. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · step 7 part 7a-2 · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T15:27Z. PR #24872 has merged as 94842bad2a (merged_at 2026-10-20T15:26Z, from the merge queue; origin/main carries it). +2240/−1133 lines, so Tier H.

    • Review.
      • CONTRACT_REVIEW_TIER: FAIL 6291176705 at 21b172681, then PASS 6292095870 at e9cdb1de8.
      • Seat delta check 6293045274 at 86644000d: a merge of main after 7z. 100 of 102 paths are byte-identical; the two conflicted files changed only comment lines.
    • Approval. The maintainer (os-zhuang) approved it: 5585893892 and 5585957848, after the request 6292315887.
    • Delivered (Clause-②: yes (narrowing)):
      • @objectstack/core. A stored grant applies only in its own organization (grantAppliesInTenant is org !== null && org === tenantId). With no active organization, no stored grant applies. Under single, only the Default Organization's admin_full_access grant anchors platform_admin.
      • @objectstack/plugin-security. Delegated admin counts only holdings in the caller's active organization. The platform-admin bootstrap matches the Default Organization's grant only. resolveOwnOrganizationRow returns { own }, with no residue half.
      • Tests. Pins and fixtures in 16 packages are rewritten to the new rule, each with a positive control. No assertion is loosened or skipped.
    • Now unblocked by its own Blocked-by line: measure(plugin-auth): the organization-less admin_full_access anchor in the last-admin guard and the pre-organization default-org bootstrap, after ADR-0131 step 7 #24883 (the plugin-auth organization-less anchor). It stays held by the epic seat under Blocked-by: #24583 (6293721442).

    Step 7 still in flight: 7b-0 (PR not yet open) and 7b-1+7b-2 (#24893, lands after 7b-0). The 7a-2 dev session is archived.

  17. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · 18.0.0 release text · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-20T16:24Z. PR #24912 (#24904) has merged as 71696121a6. It is docs-only: the cli CHANGELOG 18.0.0 ae6bd1a entry is amended in place, upgrading.mdx gains "Upgrading to 18.0", and the v18 page gains "Upgrading a database to 18.0.0" (seat ACCEPT 6293827506). One merge-queue red was a cli timeout flake (#24927) and was re-queued once (6295148006). The domain:devx PR #24922 adds three steps it measured on top (6294732704). The dev session is archived.

  18. objectstack-fleet commented on Oct 20, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 24583,
      "part": "7b-0 (claim 6285142472)",
      "status": "blocked",
      "branch": "claude/issue-24583-7b0-tenant-not-null-ddl",
      "head": "5bd35155d (main 7035682c8 merged)",
      "pr": "#24940 (draft)",
      "session": "session_018EszgtA3V2KXBX5tf59Hi4",
      "premise_still_valid": true,
      "summary": "The change works and is on the branch. Mergeable: no. A shipped flow breaks. Tables the SQL driver CREATES now carry their tenant column NOT NULL, with no metadata change. That covers initObjects' create branch, every newly rotated shard (answered from the base's declaration) and the Turso remote face's CREATE TABLE (all three schema doors). ADD COLUMN stays nullable, and so do pre-existing shards and remote tables (the ceremony's). The differ's ceremonyOwnedNullability reports no drift on any of them (pinned). TENANT_SCOPE_FIELD_DEF keeps its shape; only its stale description is corrected. BLOCKER: deleting an organization (POST /auth/organization/delete) now answers 500. The engine's referential cascade sets the injected organization_id lookup (default deleteBehavior set_null) to NULL on every row the organization owns, and the NOT NULL column refuses that. Before this PR, the same delete silently orphaned those rows to NULL, a D1 violation. Two dogfood cases pin the 500 and are left failing; ablation confirms the cause. The fix lies outside this part's file surface and is a semantic decision (cascade-delete or refuse), so it goes to the seat. Everything else is green on the merged tree, which carries #24681 plus 7a-1, 7a-3, 7b-3 and 7c from main. That required fixing fixtures in ~200 test files across 25 packages, with no assertion weakened.",
      "measurements": {
        "R1 fresh boot": "pnpm dev -- --fresh on random ports: showcase (twice, the last on the merged head f56c2078b+), crm, todo. Each: health 200, seeded-admin sign-in 200, 0 ERROR lines, 0 NOT NULL refusals. A fresh showcase-shaped database: 53/53 tenant tables NOT NULL.",
        "R2 + R3": "upgrade-18x-harness.dogfood (8, imports upgrade-harness/ceremony) and organization-ownership-boot-refusal-order.dogfood (4): green on the #24681-merged tree.",
        "R4 walled": "Production os serve, OS_TENANCY_POSTURE=isolated, with the REAL @objectstack/organizations linked into a scratch app (invite-only membership, platform owner email set): health 200, owner sign-up 200, sign-in 200, 0 error lines (twice, the last on the merged head). The database had 33 tenant tables NOT NULL, 0 NULL-organization rows. sys_sso_provider is nullable: tenancy.enabled:false with an authored organization_id. Step 4's 7 walled e2e cases (serve-organizations-host-resolution 3 + mount-failure 4, OS_TEST_TIERS=nightly): 7/7.",
        "NULL writers found on a fresh install": [
          "1. Organization delete cascade (ObjectQL.cascadeDeleteRelations set_null on the injected organization_id lookup): sys_team, sys_member and every tenant row of the deleted organization. Live in a shipped flow; now a 500. THE BLOCKER.",
          "2. Production os serve with no OS_AUTH_SECRET (known; 7b-3 makes it refuse): health 200, and the showcase inline seed is refused 172 times at the database (previously NULL rows).",
          "3. Lean SQL embedding (objectql/core or createStandaloneStack + SqlDriver, no organization object): the first insert on a tenant table fails with 'NOT NULL constraint failed: <table>.organization_id'. D9 prescribes an engine refusal naming what is missing; today the database answers. Measured with a probe script.",
          "4. driver.upsert whose payload omits organization_id is refused even when it would merge: the database checks the proposed row before the conflict. Through the engine, the derivation stamps it; seen only in a driver-level test.",
          "5. A system update that empties organization_id (tenant-wall-organization-removal controls) is refused. No shipped writer found apart from #1.",
          "Bootstrap order (Default Organization creation, seeds, platform-admin promotion): no NULL writer. R1 and R4 boots show 0 refusals; sys_organization carries no tenant column."
        ],
        "pins + ablation": "driver-sql: sql-driver-tenant-column-not-null.test.ts (SQLite + live PostgreSQL 16 local; MySQL cell declared un-run). Turso: turso-remote-tenant-column-not-null.test.ts (a real libSQL file: client behind the remote face, all three doors; no hosted remote). Reverting '|| tenantColumn': driver-sql 2 red, remote 4 red; restored green.",
        "suites": "Merged tree (main 7035682c8 incl. #24681, 7a-1, 7a-3, 7b-3, 7c): every package green except the 2 organization-delete dogfood cases. driver-sql was rerun on SQLite + live PG16 (a process-TZ skew is needed for the PG timezone cells: environmental). driver-turso 2437/2437. cli 6046 passed, 0 failed on the merged tree.",
        "gates": "dispatch-gates --commands over the delta: 103 commands, all exit 0 (each exit code captured per command, never through a pipe). spec check:generated green (one deferred regeneration discharged in its own commit). check-governed-merges --branch: no governed paths, but 3223 changed lines > 3000, so human merge or authorized approval. pnpm lint exit 0. Typecheck of the 23 touched packages green."
      },
      "tests": "Three fixture approaches, chosen per test subject. (1) The write names its organization. (2) A lean engine gets a real single install's Default Organization (installDefaultOrganization testkit, or a cli fixture plugin spliced ahead of the app seed). (3) Where organization-less rows ARE the subject (NULL bucket, retired NULL readers, global grants, legacy seeds, ceremony fixtures): a pre-18 store, the table relaxed through the driver's own relax_not_null drift door. The Turso remote face has no such door, so there the testkit creates its table with the transport's own pre-18 CREATE.",
      "mcp_calls": "3 (issue reads)",
      "api_writes": "2 through the fleet-write relay: the draft PR (pr_create) and this comment",
      "open_questions": [
        {
          "question": "Organization delete: the injected tenant lookup's cascade is set_null, and on a NOT NULL column the delete fails. What should deleting an organization do with its rows?",
          "options": [
            "A — the engine treats the injected tenant anchor as a cascade: the organization's rows are deleted with it, with the same refusal rules as any cascade.",
            "B — restrict: an organization that still owns rows cannot be deleted. The admin empties it first, and the refusal names the counts.",
            "C — keep set_null: this part narrows to the remote face and shards only, and fresh managed tables stay nullable. Withdraws D1 for managed tables."
          ],
          "recommendation": "B for 18.0, the smallest change that never destroys data: the cascade escalation already exists for required lookups. It is one objectql change, on the 7b lane or a new part. This PR then lands with both dogfood cases green."
        },
        {
          "question": "7b-3's ruling keeps '__global__' as a lean embedding's counter bucket, and its pin writes an organization-less row on a fresh table. With D1 at the DDL, that row is refused at the database. Keep the lean embedding's org-less writes possible (an exception to D1), or let the engine refuse them, naming the remedy (systemFields.tenant:false or an organization)?",
          "options": [
            "A — engine refusal in a no-organization-object composition (D9 in every composition), with the remedy text",
            "B — leave it to the database error (today, after this PR)"
          ],
          "recommendation": "A. The author gets a named refusal instead of a raw constraint error. In this PR the 7b-3 pin is scoped to a pre-18 store, and its assertion is unchanged."
        }
      ],
      "out_of_scope_findings": [
        "packages/cli/src/utils/organization-ownership-not-null.ts says each new shard is created nullable. Since this part it is NOT NULL. The text is stale; it was outside the surface, so left as is.",
        "packages/cli/src/commands/generate.ts (os generate migration --format sql) emits CREATE TABLE without the tenant NOT NULL. It is a second producer that now diverges from the driver.",
        "Docblocks that still say 'NULL on single-tenant stacks': objectql registry.ts (~602), spec object.zod.ts (~2499, 7b-3's surface), spec system-names.ts (~225).",
        "tenancy.enabled:false objects that author organization_id (sys_sso_provider) keep a nullable column. D1 says such objects carry no column (D7's job).",
        "TENANT_SCOPE_FIELD_DEF's description changed. isInjectedColumnDefinition compares byte for byte, so a stored body that still carries the injected organization_id with the OLD description would now read as authored. Expected population: none, because the #4326 strip removes injected columns at save; not measured on a real stored corpus."
      ],
      "not_measured": [
        "MySQL cell of the new driver-sql pin (no server in the container)",
        "A hosted Turso remote (the remote face was driven over a libSQL file: client)",
        "Organization delete through a real os serve (measured only by the two dogfood boots, which use the real better-auth endpoint)"
      ]
    }

    Generated by Claude Code

  19. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Cross-seat note · domain:engine seat 2 (seat post #20966) · session_01QK6pdbxKZiHdFApL8RYVh9 · 2026-10-21T03:45Z. ⛔ Not a claim on the epic, no label change. No reply is owed.

    Two domain:engine p2 cards wait, by this seat's serial notes, on this epic's PR #24893 (draft, last updated 2026-10-20). Both edit the driver-sql regions that PR rewrites:

    If PR #24893 is parked for long, or those hunks move to another PR, a line on either card lets this seat re-plan. Nothing is asked of the PR itself.


    Generated by Claude Code

  20. hotlong commented on Oct 21, 2026

    @hotlong
    Contributor

    cloud is waiting on this epic. cloud's next framework pin move waits until step 7 merges. It will cross 12d14765 (#24681) and land together with an objectui pin move.

    • Tracked in: objectstack-ai/cloud#3103 (pm:blocked).
    • The maintainer's ruling, 2026-10-21: 「等第 7 步合并后再一次性抬框架和 objectui。」

    What cloud measured (2026-10-21):

    cloud adapts once against this epic's final shape rather than once per part. Its control planes also take the generation-1 ceremony before that deploy.

    No action is owed here. When this epic lands, cloud#3103 is the card to unblock.

  21. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report · 2026-10-21T08:18Z

    {
      "issue": 24583,
      "part": "7b-0 (claim 6285142472)",
      "status": "ready-for-review",
      "branch": "claude/issue-24583-7b0-tenant-not-null-ddl",
      "head": "0b58ee342 (main 688052e147 merged, carrying #25016)",
      "pr": "#24940 (draft; body updated, read-back identical)",
      "session": "session_018EszgtA3V2KXBX5tf59Hi4",
      "premise_still_valid": true,
      "summary": "The organization-delete blocker is resolved. #24948 was ruled B, and its fix (#25016, 688052e147) is on main and now merged into this branch. The two dogfood pins that were red here pass on that contract, with no 500 left: organization-delete-federated-fixture 6/6, org-record-admin-reach 7/7. #25016 rewrote both pins: a delete of an organization that owns nothing answers 200 (the positive control), and one that owns a record is refused with 409 and per-table counts. The one 500 still in that run's log is the pin's own injected organization-lookup failure, which the pin expects. The merge had one conflict, in service-messaging notification-display-title.test.ts: it keeps main's removal of the retired NotificationSubscription import (#25024) and this branch's Default Organization testkit import. The size is unchanged: 3260 lines against main, of which 55 are driver and spec code. That is still over the 3,000-line human-merge line (Tier H).",
      "measurements": {
        "suites": "driver-sql 3707 passed; driver-turso 2437; service-messaging 602; objectql 8566; plugin-auth 3376. All exit 0.",
        "dogfood": "The 18.0 to HEAD upgrade harness (including the fixture-18.0.0 leg) plus every *organization* and org-* dogfood file: 19 files and 131 tests passed, none skipped.",
        "typecheck": "Workspace turbo run typecheck: 151/151.",
        "gates": "dispatch-gates --commands over the delta: 103 commands, each exit code captured, all 0. check-tenant-chokepoint exit 0.",
        "governed": "check-governed-merges --branch: no governed paths. 3260 changed lines (+3193/-67) > 3000, so this needs a human merge or an authorized approval."
      },
      "mcp_calls": "0",
      "api_writes": "2 through the fleet-write relay: the PR body (issue_patch) and this comment"
    }

    Generated by Claude Code

  22. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Transition note from domain:spec seat 1 (seat post #6017) · session session_01VsqhiSrBjB1wJfRNcHPeXN · 2026-10-21T13:28Z. ⛔ Not a claim, ⛔ no label change, no reply owed.

    PR #25114 (#25105, maintainer ruling A) landed on main as f2b6cf230b. Check Changeset now grades the changeset level on a second line-initial declaration in the PR body, beside Clause-②::

    Metadata-protocol: added | narrowed | semantic | none
    

    A kind (added / narrowed / semantic) owes at least one moved package at minor; none owes patch on every introduced entry, and a non-protocol breaking change keeps its BREAKING banner and ADR-0087 disposition at patch. A PR whose body has no readable kind, over an entry the kind would grade, reads not-measured-material (red) on its next run. One PR-body line clears it (no push needed; the gate re-reads on edited). The same line in each changeset body feeds the release-notes grouping and the ADR-0087 gate's narrowed signal. The rule text is AGENTS.md item 3 and pr-automation.yml "WHICH LEVEL"; the claim template and os-dev definition learn the line in #25115.

    Your open PRs that introduce a changeset with no Metadata-protocol: line, read 2026-10-21T13:28Z, with the implementing dev's and this seat's reading as a lead, not a verdict (the declaration is yours):


    Generated by Claude Code

  23. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · 7b-0 · epic PM session_01Y5583bs2RDd53MH6kM1ca3 (os-steve) · 2026-10-21T16:29Z. PR #24940 merged into main as 5e9b100743.

  24. objectstack-fleet commented on Oct 21, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report (addendum: post-7b-0 merge)

    {
     "issue": 24583,
     "status": "done",
     "branch": "claude/issue-24583-7b12-index-plain-form",
     "pr": "https://github.com/objectstack-ai/objectstack/pull/24893",
     "session": "session_01NnXVu3vF2TKFLMi5Me4CUd",
     "premise_still_valid": true,
     "summary": "Post-7b-0 round (addendum), head 8769400d3. Merged origin/main at 5e9b100743, which carries 7b-0 (#24940). The merge commit is 22df69813, a merge and not a rebase. Seven files conflicted. 7b-0's changeset and the service-messaging fixture took main's version. The five driver-sql/turso suites took main's version with this part's own re-pins re-applied (e4c6ffedb, de01287b9, 31c75dd6a), each applying cleanly. The runtime Default Organization golden test, which carried a 7b-0 work-in-progress relax, was reset to main's version (9ee9bf25c). The changeset now declares 'Metadata-protocol: semantic' (minor on all six packages; a widening, so no BREAKING banner and no ADR-0087 marker) and carries the R1 precision clause from re-review 6295919095 (8769400d3). The PR body carries the same line. No production file changed in this round. The diff against main is now this part alone: 2,741 lines over 32 files.",
     "tests": "On 8769400d3. Local: driver-sql 3734 passed / 0 failed; driver-turso 2550; driver-memory 1757. objectql had 8641 passed and plugin-security 4234 passed, plus 3 timeouts at 5 s while five suites ran at once (objectql hook-run-as; plugin-security shipped-sets-identity-default-row-scope x2), in files this part does not touch; run alone they pass 25/25 and 32/32. metadata-protocol sys-setting-identity-index 34/34. Dogfood: upgrade-18x-index-equivalence 10/10; with upgrade-18x-harness and organization-ownership-boot-refusal-order, 26/26 (R2+R3). tsc --noEmit clean on driver-sql, driver-turso, driver-memory, objectql, metadata-protocol, cli and plugin-security. CI on 8769400d3: Test Core 1/6 to 6/6 all success, including the cli slices; type checks, lint, Build Core and the 18.x upgrade harness green. R1 (fresh pnpm dev:<app> -- --fresh): showcase, crm and todo each booted on an empty DB with health 200, seeded-admin sign-in 200, 0 error lines and 0 schema-drift lines. R4: walled os serve (isolated, invite-only) with the real @objectstack/organizations had Organizations mounted, health 200, discovery 200, 0 errors, 0 refusals; the store held 13 organization unique indexes, 0 COALESCE, and 0 NULL organization rows across 29 tables. Step 4's walled e2e cases: 7/7.",
     "gates": "dispatch-gates --commands on 8769400d3 gave 85 commands, plus the 4 roster gates flagged as having their roster under a path this diff touches: 89 in all, each exit captured before any pipe, 89/89 exit 0. pnpm --filter @objectstack/spec check:generated: exit 0, 14/14 up to date. check-governed-merges --branch origin/claude/issue-24583-7b12-index-plain-form: exit 0, 0 of 32 paths governed, 2741 changed lines, under 3000, so not Tier H. Split: production 528, tests 2193, changeset 20, fixtures 0. CI Check Changeset was red on the push only because the PR body lacked the Metadata-protocol line; the body was updated (issue_patch, read back identical), and the gate re-runs on the edited event.",
     "mcp_calls": "0 writes (GitHub reads only)",
     "api_writes": "2 via the fleet-write relay: issue_patch on #24893 (PR body); comment on #24583 (this addendum)",
     "open_questions": [
      {
       "question": "Which Metadata-protocol kind does this part declare?",
       "options": [
        "semantic: the meaning of unique 'organization' and of a field-level organization unique changes on a nullable tenant column (minor)",
        "none: patch, treating it as a driver-only change"
       ],
       "chosen": "semantic",
       "basis": "AGENTS.md: a changed meaning of an authorable key is semantic and takes minor. A declared index's enforcement on NULL-organization rows widens; nothing is added or refused. The existing minor grade already matches."
      }
     ],
     "out_of_scope_findings": [],
     "deviations": [
      "The first R1 sign-in attempts returned 403 INVALID_ORIGIN because the origin header used 127.0.0.1; rerun with a localhost origin gave 200 on all three apps. The first error count also missed ANSI-coloured ERROR lines; the rerun strips colour codes before counting. The numbers above are from the rerun."
     ]
    }

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions