Repository navigation
driver-turso remote: generate auto_number values on the RemoteTransport — the #6944 appetite door now has measured demand (a hosted HotCRM environment cannot create an account: 501) #21113
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpriority:p1High: required for production / M2High: required for production / M2area:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchand removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (queue-jump by the maintainer's order in seat 2's chat: 「21113 插队 p1 优先」)
Session:session_01Ujdtvqs7ree7WyQmEDwEnG
Account:os-litant(the seat's linked user asGET /useranswers it; always the card's assignee)
Branch:claude/issue-21113-turso-remote-autonumber
Worktree:objectstack-issue-21113
Domain:domain:engine
Seat:domain:engine#2
File surface: the card body's Where and Acceptance (disposition A of #6944, behind its appetite door, now opened by measured demand: cloud#2531).packages/drivers/driver-turso/src/remote-transport.ts: thecreate,bulkCreateandupsertlegs issueauto_numbervalues for an empty slot.packages/drivers/driver-turso/src/turso-driver.ts: the refusalPR #7089added becomes generation, and the upsert leg's post-write report (#7099) is re-read.packages/drivers/driver-sql/src/sql-driver.ts: only to share, not copy, the format and sequence semantics (resolveAutonumberFormat, the_objectstack_sequenceskey shape and its bootstrap from the data-tableMAX). This means its sequence region near:7528–:8030at53ed3d109, ⛔ nothing else in the file.- Tests in
driver-turso: the fix(driver-turso): refuse auto_number writes on the remote transport (#6944) #7089 refusal test is converted to a generation test, ⛔ not deleted, plus a cross-writer concurrency pin. .changeset/21113-*.md.
Stop on breach and explain in the report.
- ⛔ No in-process counter, and ⛔ no engine in-memory fallback: generation is atomic in the database.
- ⛔ No second copy of the format or the sequence key.
Container & model:M,mode:subagent,model: fable(dispatch-gates --tier: no path-derived mandate, floor sonnet · default opus · ceiling fable; reason: a p1 hosted-product defect whose acceptance is cross-process atomicity on a remote transport)
Clause-②: yes (widening)
Thread-read: none
Serial constraints cleared: read at 2026-10-01T07:50Z againstorigin/main53ed3d109. - No open PR touches
packages/drivers/driver-turso. - Two open PRs touch
sql-driver.tsin other regions: PR fix(objectql)!: a per-aggregation filter refuses a scalar comparison on a declared JSON-stored field, in where's words #21097 ([finding] a per-aggregationfilter$ninon a multi-valued field counts the rows it was asked to exclude, and$incounts none, where the samewhereis refused 400: the aggregation evaluator has no JSON-column equality gate #21007, this seat's) near:150and:3298, and PR fix(service-analytics)!: the analytics read scope and the native where answer $contains on a JSON-stored field by membership (#20987) #21117 ([finding]$contains/$notContainson a declared multi-valued or JSON-stored field still answer SUBSTRING on five faces, the analytics RLS read scope among them (u1admits a row storingu10) #20987,domain:services) near:144,:3731and:16079. The second to land mergesmain. - No in-flight claim of this seat touches either package.
Clause-②: yes (widening): a create that the remote face refuses501today is accepted. No spec key or export moves. The at-tier review is owed.- objectstack-ai/cloud#2531 carries the cloud half and is
Blocked-by:this card.
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21113, "status": "done", "branch": "claude/issue-21113-turso-remote-autonumber", "pr": "https://github.com/objectstack-ai/objectstack/pull/21160", "head_sha": "2c9d2985a067984cfb97997d73757e9f38835315", "session": "session_01Ujdtvqs7ree7WyQmEDwEnG", "premise_still_valid": true, "summary": "Premise held on origin/main 99398542b: the remote face refused every empty auto_number slot (the #7089 suite's 9 refusal pins went red the moment generation landed, through 'expected the remote face to refuse, but it resolved'). Disposition A executed: TursoDriver.getNextSequenceValue routes by transport; the remote face moves the shared _objectstack_sequences counter in one atomic statement (warm: UPDATE ... RETURNING last_value; cold: scan the data table's MAX by the shared reading, then INSERT ... ON CONFLICT (key_hash) DO UPDATE SET last_value = last_value + 1 RETURNING), bootstraps and re-seeds (#5495) through SqlDriver members lifted into the sequence region (resolveSequenceTenantId, defineSequencesTable compiled to text by the connection-less Knex, maxAutonumberCounter, escapeLikePrefix, sequencesTableName, autoNumberCollisionRetries), and fills the slots by calling the inherited fillAutoNumberFields before the transport builds its INSERT; create, bulkCreate (now per row through the driver's own create, which is what RemoteTransport.bulkCreate always was underneath) and upsert all generate, upsert naming the autonumber columns insert-only to the transport so a merge keeps its number (#7011) and the #7099 leg gets one. A pre-key_hash sequences table is refused DATABASE_ERROR/500 with the remedy (the remote face has no Knex connection to migrate it and a raw-SQL rewrite would be a second copy); supports.autonumber stays true and is now honoured. H2 falsified on one point and reported: the seed is not folded into the statement as a SQL MAX because the bootstrap reading strips a declared suffix (#6468) and a dialect expression would be the second copy H3 forbids; no Hrana batch needed. H6 holds: Clause-②: yes (widening); changeset driver-turso minor + driver-sql patch. Merged origin/main twice (last 1bd14c984 at 40dddd069); 14 files changed, 1592 insertions(+), 840 deletions(-) vs the merge base, 14 paths. One incident: turbo 2.11.5 (merged from main) rewrote AGENTS.md during a build and a blanket git add carried it into 20a25d847 (pushed); restored to the merge-base blob e9e211fc9 in 78fea1334, AGENTS.md is absent from the PR diff, staging has been by explicit path since, and the PM's heads-up names #21146 / PR #21151 as the fix.", "tests": "Head 2c9d2985a, every run under scripts/pm/os-verify-lock.sh. Build closure `pnpm --filter '@objectstack/driver-turso^...' build` exit 0 (Tasks 21 successful on the final rebuild). Typecheck: driver-sql exit 0, driver-turso exit 0. driver-turso `vitest run`: `Test Files 82 passed (82)`, `Tests 2218 passed | 33 skipped (2251)`. driver-sql `vitest run` in two halves of 108 files on 52ab7ad76 (the second merge touched neither driver package nor the lockfile): `103 passed | 5 skipped`, `1389 passed | 100 skipped`; `102 passed | 6 skipped`, `1914 passed | 88 skipped`; its nine autonumber-named suites again on the final tree: `66 passed | 2 skipped`. Converted suite turso-remote-autonumber-generation.test.ts 27 cases (generation on every leg incl. the #7099 leg; caller-supplied kept; merge keeps CASE-00042 and the next create is 00044; two-face block on ONE file with a local Knex face and a remote native-libsql face: 1,2,3,4 on one key_hash row equal to the local face's sequenceKeyHash, per-tenant buckets, #6468 suffix seed read as 5 not 52026, the {field} refusal is the same sentence on both faces and writes nothing; #5495 re-seed: CASE-00031 after rows 2..30 land by bypass, batch 11/12; the caller's own 409 untouched; legacy shape DATABASE_ERROR/500 twice, nothing written; local and replica still issue; RemoteTransport.prototype has no autonumber member, create.length 2, supports.autonumber true). Resync and batch-resync REMOTE pins rewritten: surface probe verbatim + generation + re-seed. Cross-process pin turso-remote-autonumber-concurrency.test.ts: two tsx child processes, each its own @libsql/client native file: connection and remote-mode TursoDriver, file barrier, 2 x 200 creates per round: 400 distinct, exactly 1..400, strictly increasing per writer, zero failed writes, table and counter row agree (hard pins every round); overlap evidence (at least two writer changes in the global order) ends the loop, three rounds without it skip with a NOT MEASURED note; final-tree runs 3/3 green standalone measuring 31, 21, 23 writer changes, 9 inside the full-package run; one earlier loaded run (another seat's full driver-turso run on the box) measured 1 change, which is why rounds exist. Said plainly: no sqld in this container; measured on two processes over libSQL's engine under its write lock, not an HTTP server. Ablations (scripts/ablation-replace.mjs, anchor hit 1 to 0, blob hash before/after, restore proven blob == HEAD and git diff HEAD empty; trap-guarded scripts): (1) merge-set exclusion dropped: predicted exactly the two #7011 merge pins red; measured `2 failed | 25 passed`, `expected 'CASE-00043' to be 'CASE-00042'` and `'CASE-00099'`. (2) warm path made read-then-write: predicted distinct count below N; measured direction REVERSED and kept: generation suite stayed green (27 passed, the stub serialises) and the concurrency pin went red on its no-failed-write assertion with `SQLITE_CONSTRAINT: UNIQUE constraint failed: index 'uniq_crm_case_organization_id_case_number'` (2 of 3 runs with no gap, 3 of 3 with a 1 ms gap) because the unique index refuses the duplicated number before duplicate rows appear; recorded in the pin's docblock. (3) shared bootstrap reading zeroed in driver-sql (dist): rebuilt, ablation-dist-preflight marker present in 2 built files exit 0; predicted bootstrap/re-seed pins red on both faces; measured `10 failed | 25 passed` (generation 5, resync LOCAL+REMOTE, batch-resync LOCAL x2 + REMOTE); restore rebuilt, preflight --absent exit 0; a first attempt was a null operation (the tool refused a replacement that restated the anchor; the preflight reported the marker absent and that green is void) and was redone. Gates on the final tree: dispatch-gates --commands --repo objectstack-ai/objectstack at 2c9d2985a derives 73; 72 exit 0 (incl. driver-conformance identical before/after: 50 covered, 0 debt, 0 exempt, no autonumber cell; query-options-erasure holds after retyping one as-any find() query the first head added; lean-entry-closure measured after building objectql; type-check-debt 399.5 s none above record; pm-skill-ratchet 0 with AGENTS.md restored); check:dts-closure exit 1 on its first final-tree run naming plugin-email and plugin-security (outside this diff and its closure, dist mid-rewrite by concurrent turbo activity), 0 after a forced rebuild of the two (71 packages, 167/167 declarations); NOT MEASURED 1: check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (78 packages without dist locally). `dispatch-gates --ran`: 73 derived, 72 run, 1 NOT-MEASURED claimed, 0 UNRUN, exit 0; seven families derived only while the turbo block sat in AGENTS.md were run anyway, all 0. pnpm lint narrowed as a measurement: population = the lint script's eslint . --no-inline-config under one eslint.config.mjs; --format json over the 9 changed source files: 0 errors 0 warnings; invariance: no type-aware linting for any file, so no untouched file's verdict moves. check:nul-bytes 0 before the first push. Not run locally and declared to CI: the artifact-roster, wide-population and path-scheduled CI families the derivation lists.", "mcp_calls": "0 — no MCP tool of any kind. Reads: the public issue-page payload tier (curl) for #21113, #6944, #7099, #19772, PR #7089, #19787; one `gh api` GET of PR 21160 after creation. objectstack-ai/cloud#2531 was not readable from this session (gh: repository not enabled, HTTP 403; with-fleet --read refused a command on the relay route) and is used only as the card quotes it.", "api_writes": "3 REST writes, all through the fleet-write relay (`POST /repos/objectstack-ai/objectstack/dispatches`, executed by the relay run as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls, draft forced → #21160, run 36851919250, body read back 16489 bytes identical; (2) label-write assign → POST /repos/objectstack-ai/objectstack/issues/21160/assignees os-litant, run 36851984589, read back MATCHES (the labeler's documentation/size-xl/dependencies/tests/tooling were on the PR already and were preserved); (3) this report → POST /repos/objectstack-ai/objectstack/issues/21113/comments via post-stamped.mjs. No label writes beyond the assign (the dispatch named none; a real changeset is written so skip-changeset does not apply), no PATCH, no issue creation. git pushes to the branch: 6 (empty-branch probe, feat, docs+changeset, retype+merge, AGENTS.md restore, rounds+merge).", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: public door, create on a local-SQLite datasource (`POST /api/v1/data/OBJECT`), named failing probe only, no end-to-end run · SqlDriver.scanMaxNumericTail escapes backslash, `%` and `_` with a backslash (now `escapeLikePrefix`) and builds `where field like ?` through Knex, which emits NO `ESCAPE` clause on the sqlite and pg dialects (measured: the compiled `like ?` with binding `SO\\_%`); SQLite's LIKE has no escape character unless declared, so on the local faces a format whose rendered prefix contains `_` or `%` matches nothing (measured on better-sqlite3: pattern `SO\\_%` without ESCAPE returns [] against a stored `SO_0007`, with `ESCAPE '\\'` returns it) and the cold counter seeds from 0; the collision re-seed scans the same way and cannot move it. Postgres and MySQL default to backslash escaping, so only the SQLite faces (driver-sql on better-sqlite3, turso local/replica) are affected. The remote statement in this PR declares `ESCAPE '\\'`. Dedupe words: scanMaxNumericTail LIKE ESCAPE sqlite · autonumber prefix underscore seeds zero · escapeLikePrefix no ESCAPE clause · knex like no escape", "carrier: none (承接者:无) · noted, not filed · RemoteTransport.upsert with caller-supplied conflictKeys keeps `id` in its merge set (updateCols = columns minus mergeKeys, and toUpsert.id is always set), i.e. `\"id\" = excluded.\"id\"` — the #8622 re-keying the local face excluded ('id is insert-only'). Read from the code, not executed. Seam: spec:IDataDriver.upsert → runtime:RemoteTransport.upsert (remote-transport.ts, merge set) | renderer: none. Dedupe words: remote upsert conflictKeys id excluded re-key · 8622 remote face", "carrier: none (承接者:无) · noted, not filed · RemoteTransport.bulkCreate is a per-row loop (one INSERT and one read-back per row), never one statement, so a remote batch was never all-or-nothing while SqlDriver.bulkCreate is one statement; the old turso-driver comment claiming 'all-or-nothing on this transport too' was false and is corrected in this PR. Observation of a face divergence, no contract text found promising batch atomicity. Dedupe words: RemoteTransport bulkCreate per-row loop partial batch · remote batch not all-or-nothing", "already tracked (#21146 / PR #21151 per the PM): turbo 2.11.5 from the merged dependabot bump appends a turborepo-agent-rules block to AGENTS.md on agent-detected invocations; it rewrote this worktree's AGENTS.md twice (objectql build, closure rebuild) and reddened check:pm-skill-ratchet (3 lines over 120 bytes, 1119 lines over the 1116 ceiling) until restored. Not filed again.", "noted, not filed · check:dts-closure read plugin-email and plugin-security as missing dist/index.d.ts on one run while another process in the same tree was rebuilding (their dist listing seconds later showed the files); a forced rebuild and rerun exit 0. The #15042 shape the gate's own text names; no capture taken. Dedupe words: dts-closure transient missing d.ts concurrent build" ] }objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPatch round 1 on PR #21160 — the seat's decision on review FAIL 5930100091
domain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T11:10Z. Same claim (5927179360), same branch.- The one blocking item (review ②):
@objectstack/driver-sqlis levelledpatchwhile the diff adds a root type export (AutoNumberReservation). - Decision, conditional on a premise: drop the root re-export, and type
TursoDriver's overrides through the indexedParameters/ReturnTypeof the protected members, as itsautoNumberValueExistsoverride already does.driver-sqlthen adds no public surface, andpatchstays right (17.3.0's "no export is added (the collector isprotected)").- The premise, measured first: the type is reachable from the subclass without the export.
- If it is not, ⛔ no structural copy. The export stays and
driver-sqlgoes tominor(17.5.0's "a purely additive widening takes at leastminor"). The dev names which branch was taken. - Why this order: no consumer outside
driver-tursoasks for the type. A new root export would be public surface with no pull, and the repo's default for that is no.
- Asked of the same round, measure only: the review escalated the dev's finding that
RemoteTransport.upsertwith caller-suppliedconflictKeyskeepsidin its merge set (the drivers(sql): an upsert that merges on a non-PK conflict key silently REWRITES the existing row's primary key — measured on SQLite and MySQL alike #8622 re-key shape on the remote face). It was read from the code, not executed. The dev runs one throwaway probe and reports the reach. This seat files a card if it reproduces. ⛔ No fix in this PR. - Already filed from the same report: [finding] SQLite: an autonumber format whose prefix contains
_or%seeds its counter from 0 — scanMaxNumericTail escapes the prefix but Knex emits no ESCAPE clause, so the bootstrap and #5495 re-seed scans match nothing #21163, the SQLiteLIKEwith noESCAPEinscanMaxNumericTail.
Generated by Claude Code
- The one blocking item (review ②):
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21113, "status": "done", "round": "patch round 1 on PR #21160, review 5930100091 (FAIL on ② Semver only)", "branch": "claude/issue-21113-turso-remote-autonumber", "pr": "https://github.com/objectstack-ai/objectstack/pull/21160", "head_sha": "5d09309a65a19d50631d0a349e0e002b1129d6b8", "session": "session_01Ujdtvqs7ree7WyQmEDwEnG", "premise_still_valid": true, "summary": "Blocking item closed on branch 1 of the seat decision, after measuring its premise: the root `type` re-export of `AutoNumberReservation` is dropped from `packages/drivers/driver-sql/src/index.ts` (now byte-identical to origin/main), and the two re-seed overrides in `turso-driver.ts` take their parameter types through `Parameters` indexed over `SqlDriver`'s protected members (`autoNumberValueExists`'s second parameter, `resyncSequenceToDataMax`'s first), the spelling the `autoNumberValueExists` override's first parameter already used. The premise — that the type is reachable from the subclass without the export — measured true: `driver-sql` typecheck exit 0 and `driver-turso` typecheck exit 0 on the retyped overrides, no structural copy anywhere. So `@objectstack/driver-sql` stays `patch` (no export added, six `protected` members, no behaviour change), and the changeset sentence that named the export now reads 'No export is added and no behaviour changes on any dialect'. Branch 2 (keep the export, bump to minor) was not taken. `origin/main` was merged once more (fde553c50, 10 commits, clean; it also lands the `agentGuidance: false` opt-out #21151, so turbo no longer rewrites AGENTS.md — AGENTS.md blob e9e211fc9 equals origin/main's and is absent from the diff; staged by name throughout). Finding (b) measured as asked: it reproduces on the remote face (details in out_of_scope_findings), no fix in this PR. Pushed fast-forward 2c9d2985a → 5d09309a6; PR diff is now 13 paths; PR body rewritten through post-stamped with --ack-through=5930100091 and read back identical. The worktree is removed after this comment is posted.", "tests": "Head 5d09309a6, fresh worktree on the branch (round 0's was removed), merged origin/main fde553c50 at 605a1d2b5; every run under scripts/pm/os-verify-lock.sh. Closure build `pnpm --filter '@objectstack/driver-turso^...' build` exit 0 (fresh tree, no dist before it; AGENTS.md untouched by turbo after #21151). `driver-sql` typecheck exit 0; `driver-turso` typecheck exit 0 — these two are the premise measurement for branch 1. `driver-turso` `vitest run` on the merged tree with the throwaway #8622 probe present: `Test Files 83 passed (83)`, `Tests 2219 passed | 33 skipped (2252)` (82 / 2218 without the probe, which was deleted before the commit); the concurrency pin inside that run measured `400 writes · 400 distinct · 5 writer change(s)`. `driver-sql` autonumber-named suites: `9 passed (9)` files, `66 passed | 2 skipped (68)`. Gates asked for, on the final tree (exit codes): `node scripts/check-adr-0087-registration.mjs --base origin/main` 0 · `node scripts/check-empty-changeset.mjs --base origin/main` 0 · `node scripts/check-changeset-no-major.mjs --base origin/main` 0 · `node scripts/check-changeset-fixed.mjs` 0 · `pnpm check:changeset-gate-self-tests` 0 · `pnpm check:nul-bytes` 0 · `pnpm check:published-files` 0. Not re-run this round (unchanged by it: no test file, no runtime path moved): the round-0 gate set (73 derived, 72 run, 1 NOT-MEASURED `check:dual-build-cjs-loads`) and `driver-sql`'s full suite; CI's Test Core shards carry them on the new head.", "mcp_calls": "0 — no MCP tool. Reads: `gh api` GET of comment 5930100091 (the review, whole), of PR 21160's comment list (newest id) and state, of the PR body after the write, and of the report comment after posting.", "api_writes": "2 REST writes this round, both through the fleet-write relay (`POST /repos/objectstack-ai/objectstack/dispatches`, executed by the relay run as objectstack-fleet[bot]): (1) post-stamped --body=21160 --ack-through=5930100091 → PATCH /repos/objectstack-ai/objectstack/issues/21160 (issue_patch), run 36854519082, 17984 bytes sent and stored identical, 2 comments newer than the body acknowledged through 5930100091; (2) this round-1 os-dev-report → POST /repos/objectstack-ai/objectstack/issues/21113/comments via post-stamped. No label, assignee or PR-state writes. git pushes: 1 (fast-forward 2c9d2985a → 5d09309a6, carrying the merge 605a1d2b5 and the fix 5d09309a6).", "open_questions": [], "out_of_scope_findings": [ "class: b · reach: measured on the driver door of the remote face (throwaway probe, not committed; `TursoDriver.upsert(object, data, ['email'])` on the libsql-sqlite stub harness), the public producers that hand `conflictKeys` down were not enumerated this round · REPRODUCES: an object with `email: { unique: 'global' }`; rows `row-a` (a@example.com) and `row-b` (b@example.com) created; `upsert({ id: 'row-NEW', email: 'a@example.com', title: 'edited' }, ['email'])` on the remote face answered `merged.id = \"row-NEW\"` and the STORED row read `{ id: 'row-NEW', email: 'a@example.com', title: 'edited' }` — the primary key of an existing row replaced by the payload's; and with NO payload id, `upsert({ email: 'b@example.com', title: 'edited too' }, ['email'])` re-keyed the stored row to the freshly minted nanoid `VC0JOyn9ADHMdv3Y` (one row, new primary key — the #8622 shape verbatim). LOCAL control, same calls: the stored row keeps `row-a`. Contract text: `SqlDriver.upsert`, '[#8622] `id` is insert-only for the SAME argument at its strongest … the moment `conflictKeys` names a business key the merged row's identity is silently replaced … dangles every one of them with no error on any dialect'. Seam: spec:IDataDriver.upsert → runtime:RemoteTransport.upsert (remote-transport.ts: the merge set is every column that is neither a merge key nor an insert-only column, and the driver names only the autonumber columns insert-only, while the local face's `insertOnlyUpsertColumns` also names `id` and `created_at`) | renderer: none. Dedupe words: remote upsert conflictKeys id excluded re-key · 8622 remote face · RemoteTransport upsert merge set id · insertOnlyUpsertColumns remote", "noted, not filed · carrier: the seat's card for the finding above (承接者: that card) · observation from the same probe's LOCAL control: `SqlDriver.upsert` keyed on `['email']` with payload `id: 'row-NEW'` keeps the stored id `row-a` (correct) but the RETURNED row carries `id: 'row-NEW'` — the answer names an id that no stored row has. Read off one probe run on better-sqlite3; not measured on PG/MySQL. Dedupe words: local upsert returned id payload not stored · conflictKeys merge return row id", "class: a (round 0, escalated by the review) · unchanged · `scanMaxNumericTail` on the local SQLite faces compiles `like ?` without `ESCAPE`, so a rendered prefix containing `_` or `%` bootstraps from 0; the remote statement in this PR declares an explicit backslash ESCAPE. Dedupe words: scanMaxNumericTail LIKE ESCAPE sqlite · autonumber prefix underscore seeds zero" ] }objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT — PR #21160 @
5d09309a(the remote transport issuesauto_numbervalues from the shared persistent sequence; #6944's disposition A) ·Fixes #21113domain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T11:42Z. Judged against GitHub, not the reports (5929937746, round 0; 5930264607, round 1). p1, queue-jumped by the maintainer's order (claim 5927179360).- Form: draft PR on
main. The body opensFixes #21113/Clause-②: yes (widening). - Scope: 13 files, +1587 / −840. Not governed.
turso-driver.ts: routesgetNextSequenceValueby transport, fills empty slots before the transport builds itsINSERT, and makes upsert's autonumber columns insert-only.remote-transport.ts: an optional insert-only column list onupsert.sql-driver.ts: sixprotectedmembers lifted, all inside the sequence region.- The suites, the changeset, and
tsxas a devDependency. AGENTS.mdis not in the diff.
- Rulings it executes: TursoDriver remote 面根本不生成自增号:RemoteTransport.create 自建 INSERT,auto_number 只是个 TEXT 列 #6944's triage left disposition A behind the appetite door. The measured demand of objectstack-ai/cloud#2531 opened it. The card's Acceptance has 3 items.
- Contract review, at tier:
- FAIL 5930100091 @
2c9d2985, on one blocking item:driver-sqlwas levelledpatchwith a new root type export. - The seat decided it (5930121675): drop the export if the type is reachable without it.
- PASS 5930403898 @
5d09309a, this head. It found:driver-sql/src/index.tsis byte-identical tomain, anddriver-sqlgains no export;- the overrides take
Parametersindexed over the protected members, with no structural copy; driver-sqlpatchanddriver-tursominorare right.- Every ① judgment of round 0 still holds:
- each counter move is one statement (warm
UPDATE … RETURNING, coldINSERT … ON CONFLICT (key_hash) DO UPDATE … RETURNING), with no in-process counter; - the format, key, tenant and bootstrap are called from
SqlDriver, not copied, and a local face and a remote face count on onekey_hashrow; - the refusal suite's 22 cases are carried into the 27-case generation suite, converted and not deleted;
- a merge keeps its number (upsert 每次「合并到既有行」都烧一个自增号,并覆写该行已有的业务号(健康计数器上实测,与陈旧无关) #7011);
- a legacy sequences table is refused
DATABASE_ERROR/500, consistent with the ledger.
- each counter move is one statement (warm
- FAIL 5930100091 @
- Acceptance, item by item:
- Generation on
create,bulkCreateandupsert, withSqlDriver's format and sequence semantics: pinned. - Two writers in different processes: pinned with two child processes, each with its own libSQL connection to one file. 2 × 200 creates give 400 distinct values, exactly 1..400, with no failed write. Limit, stated on the record: this runs on libSQL's engine under its write lock, not over an HTTP
sqld, and none can run in this container. supports.autonumberis honoured, and the fix(driver-turso): refuse auto_number writes on the remote transport (#6944) #7089 refusal test is converted.
- Generation on
- CI on
5d09309a: 42 check-runs. 38 succeeded and 4 were skipped, all on the roster (check-expected-skips --pr 21160: OK, exit 0). That includes Test Core 1–6, the Dogfood gates, Temporal Conformance (live PostgreSQL and MySQL), and every Type Check lane. Not governed (check-governed-merges --pr 21160). The PR merges cleanly ontomainbafb8c949(git merge-tree). - Findings:
- [0] SQLite
scanMaxNumericTailemits noESCAPE, so a prefix containing_or%seeds from 0 → filed [finding] SQLite: an autonumber format whose prefix contains_or%seeds its counter from 0 — scanMaxNumericTail escapes the prefix but Knex emits no ESCAPE clause, so the bootstrap and #5495 re-seed scans match nothing #21163. - [1] The remote
upsertkeyed on a business column replaces the existing row's primary key, the drivers(sql): an upsert that merges on a non-PK conflict key silently REWRITES the existing row's primary key — measured on SQLite and MySQL alike #8622 re-key; measured in round 1 → filed [finding] driver-turso remote: upsert keyed on a business column replaces the existing row's primary key (the #8622 re-key) — RemoteTransport's merge set keepsid, which the local face declares insert-only #21166, which also carries the local face's returned-id observation. - [2]
RemoteTransport.bulkCreateis a per-row loop, never one statement → Acceptance notes. The comment that claimed all-or-nothing is corrected in this PR. - [3]
turbowrites intoAGENTS.md→ dropped — fixed onmainby PR chore(turbo): opt out of the agent-guidance block in the root turbo.json #21151. - [4]
check:dts-closureread a transient missing.d.tsduring a concurrent build → Acceptance notes.
- [0] SQLite
- Landing: this seat readies and arms the PR through the queue. Its merge closes driver-turso remote: generate auto_number values on the RemoteTransport — the #6944 appetite door now has measured demand (a hosted HotCRM environment cannot create an account: 501) #21113. objectstack-ai/cloud#2531 (the pin move, then the hosted check) is
Blocked-by:this card, on the cloud lane's side.
Generated by Claude Code
- Form: draft PR on
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsLanded — PR #21160 as
e35c40a52· #21113 closed (completed): the hosted remote face issuesauto_numbervaluesdomain:engine#2(seat post #20966) ·session_01Ujdtvqs7ree7WyQmEDwEnG· 2026-10-01T13:24Z. p1, queue-jumped by the maintainer's order.- Merged through the merge queue at 2026-10-01T12:09Z as squash
e35c40a52onmain. It has one parent and is an ancestor oforigin/main. It carries the PR's 13 files (1587 insertions, 840 deletions), as reviewed.driver-sql'sindex.tsis unchanged at the squash, so no export was added. TheFixes #21113keyword closed the card as completed. - Records it landed on: ACCEPT 5930587279; contract review FAIL 5930100091 @
2c9d2985, then the seat's decision 5930121675, then PASS 5930403898 @5d09309a. - What changed for callers: on a
TursoDriverin remote mode, the transport every hosted tenant database uses, acreate,bulkCreateorupsertthat leaves anauto_numberslot empty now gets a generated value. The value hasSqlDriver's format and comes from the shared_objectstack_sequencescounter, moved in one atomic statement. Before, it was refusedNOT_IMPLEMENTED/501(fix(driver-turso): refuse auto_number writes on the remote transport (#6944) #7089).supports.autonumberon the remote face is honoured. - Downstream: objectstack-ai/cloud#2531 (the pin move, then HotCRM's account create on a hosted environment) is
Blocked-by:this card on the cloud side. That repo consumes the framework through a pin, so it unlocks when its pin moves paste35c40a52. That is the cloud lane's act, not this seat's. - Filed from this card's reports: [finding] SQLite: an autonumber format whose prefix contains
_or%seeds its counter from 0 — scanMaxNumericTail escapes the prefix but Knex emits no ESCAPE clause, so the bootstrap and #5495 re-seed scans match nothing #21163 (SQLiteLIKEwith noESCAPEinscanMaxNumericTail) and [finding] driver-turso remote: upsert keyed on a business column replaces the existing row's primary key (the #8622 re-key) — RemoteTransport's merge set keepsid, which the local face declares insert-only #21166 (the remoteupsertre-keysid, the drivers(sql): an upsert that merges on a non-PK conflict key silently REWRITES the existing row's primary key — measured on SQLite and MySQL alike #8622 shape). - Labels:
pm:dispatchedremoved in this act. - Unlock scan: no open
pm:blockedcard in this repo namesBlocked-by: #21113.
Generated by Claude Code
- Merged through the merge queue at 2026-10-01T12:09Z as squash
- added a commit that references this issue
on Oct 7, 2026
Category ① — a product defect with a named landing spot; reach measured on a public door.
Reader: the
domain:enginelane (drivers), which dispatches it. Filed by therepo:cloudseat (repo:cloud#1, sessionsession_01Wxo1xhh2bU66T73q23jzE4). The objectstack triage seat (#6015) is vacant, so this seat applied the routing labels as a stand-in, and says so on #6015. ⛔ Not a claim.What a customer hits
objectstack-ai/cloud#2531 (p1) records it from the 2026-10-01 pre-release staging check (cloud
main97784232, HotCRM 3.1.0 installed into a fresh environment):POST /api/v1/data/crm_account {name}answers501 {"code":"NOT_IMPLEMENTED"}. A hosted tenant cannot create a HotCRM account through the UI or the API.Object "crm_account" declares auto_number field(s) [account_number] left empty for this create, and the Turso REMOTE transport does not generate record numbers … remote writes go through RemoteTransport, which builds its own INSERT and never enters SqlDriver.fillAutoNumberFields.Every hosted tenant database is on the remote transport. So any object that declares an
auto_numberfield cannot get a new record on the hosted product. This is not a regression: production's pin already carries the refusal (objectstack#7089).Why this is not re-litigating #6944
#6944 was triaged on 2026-08-09 to disposition B, explicit refusal (PR #7089). It explicitly left A, "implement autonumber on remote", behind the appetite door "for want of measured demand". Its seat also recorded that
supports.autonumberstays knowinglytrueon the remote face until A ships, "that bit flips with the implementation, not before it". The demand is now measured: a published app (HotCRM'scrm_account.account_number) on the hosted product, in a release check. ⇒ The door's own condition is met. The base principle applies: a declared capability the runtime does not honour is an implementation gap, closed by implementing it, not by narrowing on the consumer side.Where (anchored by symbol)
packages/drivers/driver-turso:RemoteTransport.createbuilds its own INSERT and never calls the sequence path.SqlDriver.fillAutoNumberFieldsand the_objectstack_sequencestable carry the semantics to align with: format and precedence viaresolveAutonumberFormat(see fix(lint): check the autonumber format the runtime resolves, not a local copy #19787 / [finding]lint-autonumber-formatskeeps its own copy of the format precedence and disagrees withresolveAutonumberFormaton an emptyautonumberFormat— the lint goes silent on a pattern the engine then throws on for every create #19772), and the counter row per object and field.Acceptance (from cloud#2531, framework half)
auto_numberfield empty gets a generated value with the same format and sequence semantics asSqlDriver.supports.autonumberon the remote face staystrueand becomes true. The fix(driver-turso): refuse auto_number writes on the remote transport (#6944) #7089 refusal test is converted to a generation test, not deleted.The cloud side (pin move, then HotCRM sample data and account create on a hosted environment) stays on objectstack-ai/cloud#2531,
Blocked-by:this card.Dedup
Turso remote transport auto_number generate record numbers sequences: TursoDriver remote 面根本不生成自增号:RemoteTransport.create 自建 INSERT,auto_number 只是个 TEXT 列 #6944, Turso remote:带 id/conflictKeys 但没匹配上的 upsert 仍会静默写入 NULL 自增号(#6944 拒绝闸门覆盖不到的那条腿) #7099, 无 format 的 autonumber 字段两侧渲染不同:driver-sql 兜底成{0000}发出0001,引擎兜底路径发出裸1—— 同一份元数据换驱动号形不同 #6555, drivers(turso): RemoteTransport 条件层的$-算子键被当列名编译成静默空集 —— SqlDriver 已按 #5348 拒收,remote 是唯一剩余面(cloud#1077 移交) #5769, driver-turso remote: cells the pre-#19844 boot door wrote unconverted (a date as a full timestamp, a scalar json unencoded) are converged by no remote backfill, so a stored true reads back as 1 #19868. All are closed, and none implements generation. Control: TursoDriver remote 面根本不生成自增号:RemoteTransport.create 自建 INSERT,auto_number 只是个 TEXT 列 #6944 hit.auto_?number|autonumber|_objectstack_sequences: fix(lint): check the autonumber format the runtime resolves, not a local copy #19787, [finding]lint-autonumber-formatskeeps its own copy of the format precedence and disagrees withresolveAutonumberFormaton an emptyautonumberFormat— the lint goes silent on a pattern the engine then throws on for every create #19772, [finding]field.formatis onez.string()key carrying THREE value vocabularies — the engine reads it as an autonumber pattern, objectui as a date display style, and itsdescribenames a third that nothing honours #19679, ASELECT ... WHERE 1 = 0column-existence probe against_objectstack_sequencesis logged at ERROR on a normal boot, before the table exists #17175, all closed and about format and lint.Generated by Claude Code