Repository navigation
ADR-0139 E3 + E4: the five drivers compile the composed ReadableIds leaf or refuse it with D3's envelope, and the conformance case-set lands with their suites (D3) #23639
Description
Filing gate: ④ a coordination node's execution card, rows E3 and E4 of ADR-0139's execution plan (part of #22788). It is derived and in scope, so it inherits #22788's lane, grade and area. Filed by domain:engine seat 1 (#6367) · sunhaolin · session_01GP3comALHRcRBixS2TEnLF, as the record's plan orders ("cut after acceptance", each row when its predecessor lands). ⛔ Classes, positions and functions only.
Predecessors landed:
- E2 (ADR-0139 E2: the engine composes the
ReadableIdsleaf once per parent object, fail closed, refuses it on write verbs and for D5's three classes, and marks the scope'policy'(D2, D5) #22861), PR feat(objectql,core): the engine composes the ReadableIds leaf at its executor boundary, fail closed, and refuses it by name where it cannot (ADR-0139 E2) #23301 →87e902dcbb. The engine composes everyReadableIdsleaf at its executor boundary, immediately before each read verb's driver call, and fails closed. - spec: declare the RLS deny sentinel once in
@objectstack/spec, imported byplugin-securityandobjectql(ADR-0139 D2 step 2; #22861 sub-question 2 ruled (i)) #23009's spec half, PR feat(spec): the RLS deny sentinel and the composed ReadableIds leaf, declared once in the data entry; getReadFilter answers the one object (ADR-0139 D2) #23144:ReadableIds.compose,isComposedReadableIds,ComposedReadableIdsandRLS_DENY_FILTERfrom@objectstack/spec/data.
Why E3 and E4 are one card. domain:spec seat 3's ruling on #23009 (pointer 6137585321 on #22788): "the case-set lands in the same PR as the five driver suites that run it, with zero ledger rows."
scripts/check-driver-conformance.mjs, in the requiredLint & Repo Gatesjob, refuses a classified case-set until every driver package imports and runs it. The plan row reads E4 "with E3".- This card is the "E3 and E4 in one PR" form. Landing E3 first with DEBT rows for E4's drivers would need the maintainer's word, and is not this card.
The work (ADR-0139 D3; the plan's rows E3 and E4)
driver-sqlcompiles the composed leaf in its filter compiler tofield IN (SELECT id FROM P_TABLE WHERE SCOPE AND TENANT). The subquery is scoped bySqlDriver#applyTenantScopewith the outer read's options, in one statement, sofind,countandaggregateagree by construction.driver-sqlite-wasmand the local transport ofdriver-tursoinherit it.- The Turso remote transport (
RemoteTransport, an independent compiler) implements the same compilation. driver-memoryimplements it in process: it reads P under the composed scope, then member-tests. No tenant wall is owed, because memory refuses multi-tenant boot.driver-mongodbrefuses loudly (open item O3: a pipeline form is unmeasured).- The refusal is one envelope on every driver (ADR-0112): status 500,
INTERNAL_ERROR, a message naming the driver, the leaf and the remedy, logged aterroronce per driver.- ⛔ Not
INVALID_FILTER. - ⛔ No
DriverCapabilitiesbit. - A driver refuses an uncomposed leaf on
isReadableIdsalone.
- ⛔ Not
- The conformance case-set (
READABLE_IDS_CASES, D3's last bullet) lands with the five suites.- It is parked, with no PR, on branch
claude/issue-23009-readable-ids-conformanceate641ab6d8:packages/spec/src/data/readable-ids-conformance.tsplus itsCASE_SETSrow. - That branch's other commits are feat(spec): the RLS deny sentinel and the composed ReadableIds leaf, declared once in the data entry; getReadFilter answers the one object (ADR-0139 D2) #23144's, already squash-merged to
main, so a claimant carries the two artefacts over, not the branch. packages/specand the gate script aredomain:specsurfaces. The claim declares them cross-lane.
- It is parked, with no PR, on branch
Pins (the plan's rows; red before, green after)
- E3:
- a reader with more than 2,000 readable candidates gets every visible row;
- an unreadable row stays hidden;
- a deleted parent is excluded;
countequals the rows served.
- E4: the conformance table records memory as compiling and mongodb as refusing.
- Every driver: the case-set's
matchescases and itsrefuses-uncomposedcase, through the shared conformance suite (no per-driver hand copy).
Readings this card owes (ADR-0139 open items)
- O1: the bound-parameter ceilings of SQLite, Postgres and MySQL. The record cites documented values only. E3 measures what the subquery form binds.
- O3: mongodb keeps the refusal. No pipeline prototype here.
- The driver conformance ledger only goes down. The claimant reads
coveredand DEBT before and after. Zero new DEBT rows.
Governing text
- ADR-0139 D3 and the execution plan's rows E3 and E4.
- ADR-0112: the refusal envelope.
- ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 note 3 (the director's ruling pointer 6104842076): "D3 stands as written, per the maintainer: no capability bit, and
driver-mongodbrefuses loudly until O3 measures a pipeline form." - [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499 (the 2026-08-05 driver-memory / driver-mongodb freeze) freezes defect and semantic-completion investment in those two drivers.
- ADR-0139 was accepted later (PR docs(adr): ADR-0139 — one engine-internal semi-join leaf, "the ids of object X this caller may read" (ruling A on #22590 item 3) #22707 →
a2e94c2a05, Tier H). Its D3 names memory's implementation and mongodb's refusal, so this card's two rows are the record's, not a thaw. - Nothing else in those two drivers rides along.
- ADR-0139 was accepted later (PR docs(adr): ADR-0139 — one engine-internal semi-join leaf, "the ids of object X this caller may read" (ruling A on #22590 item 3) #22707 →
State at filing (origin/main 23e689dc7d)
git grep -l "ReadableIds" origin/main -- packages/driversreturns 0 files. The controlTEMPORAL_CASESover the same pathspec returns 10.readable-ids-conformance.tsis not onmain.git cat-file -efails, whilepackages/spec/src/data/readable-ids.tsis present.
Dedupe
MCP search_issues on this repo, closed included: "ADR-0139 E3 driver-sql compiles the ReadableIds leaf applyTenantScope memory mongodb E4" returns 43 hits.
- The nearest are ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788 (the parent), ADR-0139 E2: the engine composes the
ReadableIdsleaf once per parent object, fail closed, refuses it on write verbs and for D5's three classes, and marks the scope'policy'(D2, D5) #22861 (E2), feat(spec): ADR-0139 E1 — theReadableIdsbranded comparand leaf, declared in spec and unspellable by a request #22787 (E1) and ADR (ruling A on #22590 item 3): one engine-internal semi-join leaf, "the ids of object X this caller may read"; ADR-0055 alternatives row (a) revisited for this one leaf #22678 (the ADR card). - None is E3 or E4.
Dedupe words: ReadableIds driver compile; READABLE_IDS_CASES conformance suites; applyTenantScope semi-join subquery; driver-mongodb refuses composed leaf; ADR-0139 E3 E4.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1 · 2026-10-15T08:29Z
Session:
session_01GP3comALHRcRBixS2TEnLF(the dispatching seat; the dev runs as its own cloud session, named in the dispatch comment)Account:
sunhaolin(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-23639-readable-ids-driversWorktree:
objectstack-issue-23639Domain:
domain:engineSeat:
domain:engine#1(seat post #6367)File surface:
packages/drivers/driver-sql/src/sql-driver.ts(the filter compiler andapplyTenantScope);packages/drivers/driver-turso/src/remote-transport.tsand its compiler;packages/drivers/driver-memory/src/memory-driver.ts(the query path);packages/drivers/driver-mongodb/src/mongodb-filter.ts(the refusal); each of the five drivers' conformance suite; changesets.domain:specseat 3 ([PM seat] domain:spec · seat 3 — 🟢 baozhoutao · session_01D6F9axfoPzR7t8G1J7Ytzz #18883) under its ruling on spec: declare the RLS deny sentinel once in@objectstack/spec, imported byplugin-securityandobjectql(ADR-0139 D2 step 2; #22861 sub-question 2 ruled (i)) #23009 (6137585321):packages/spec/src/data/readable-ids-conformance.tsand itsdata/index.tsexport, with the regeneratedapi-surface/andexport-origins/shards. Also theCASE_SETSrow inscripts/check-driver-conformance.mjs. Both are carried frome641ab6d8.Stop on any other breach and explain it in the report.
Container & model:
L,mode:cloud,model: TIER_DEFAULT(this run'sdispatch-gates.mjs --tier: "no path-derived mandate"; five drivers plus a spec case-set)Clause-②: yes
Responsibility: n/a — not a defect card (an ADR-0139 execution row)
Thread-read: none
Serial constraints cleared: no open PR or claim touches
packages/drivers/**(open-PR file lists read at 2026-10-15T08:0xZ; seat 2's #23635 is indriver-sqlite-wasm/src/wasm-connection.ts, the background flush, not the filter compiler). The ADR-0131 epic territory (#15194) does not name the drivers.Clause-②: yesis for the spec half only: the case-set's exports widen@objectstack/spec/data's public surface. The driver halves implement an already-declared leaf. Theyesowes the seat's own review of record of the declaration before the queue.File surface amended 2026-10-15T11:0xZ by this seat (same session), on the dev's report 6190258381 (
deviations,open_questions[0], option A accepted):packages/core/src/utils/readable-ids-driver-refusal.ts, its test and thepackages/core/src/index.tsexport line (one shared walk, rewrite and refusal for the four compilers, followingjson-column-operator-refusal.ts;domain:engine, a new@objectstack/coreexport inside this claim'sClause-②: yes); a two-linereadableIdsDriverNameoverride inpackages/drivers/driver-sqlite-wasm/src/sqlite-wasm-driver.ts; andpackages/drivers/driver-turso/src/turso-driver.ts(toRemoteWhere).Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
Dispatch ·
domain:engineseat 1 ·session_01GP3comALHRcRBixS2TEnLF· 2026-10-15T08:32Z:mode:cloud, the dev sessionsession_01B1r5eJEPgvn7bUsfBvK2tS, titled "⚡ engine #23639 ReadableIds drivers E3+E4". It runs at the default tier on branchclaude/issue-23639-readable-ids-drivers, the claim 6187884851.@objectstack/spec, imported byplugin-securityandobjectql(ADR-0139 D2 step 2; #22861 sub-question 2 ruled (i)) #23009's ruling 6137585321.DriverCapabilitiesbit.INTERNAL_ERROR/ 500, notINVALID_FILTER.driver-memoryanddriver-mongodb([裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499's freeze).e641ab6d8withgit restore, and regenerates the spec shards. It never copies them.Fixes #23639, then anos-dev-reportcomment here.Clause-②: yesdeclaration. Over 3,000 changed lines, the PR lands only by a human merge.Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{
"issue": 23639,
"status": "done",
"branch": "claude/issue-23639-readable-ids-drivers",
"pr": "#23656",
"session": "session_01B1r5eJEPgvn7bUsfBvK2tS — this run's own cloud session (dispatched by session_01GP3comALHRcRBixS2TEnLF); the commits' Claude-Session lines name it",
"premise_still_valid": true,
"summary": "E3 and E4 land in one draft PR (#23656, head 52dc068), with zero ledger rows. driver-sql compiles the composed leaf in the $in arm of SqlDriver#applyFilterCondition to field IN (SELECT id FROM P WHERE SCOPE AND TENANT). The subquery is walled by applyTenantScope with the outer read's options, which are threaded as a new optional last parameter of applyFilters and applyFilterCondition. sqlite-wasm and Turso local inherit it. Turso remote resolves each leaf in TursoDriver#toRemoteWhere (the parent table, the scope in storage form, and the tenant wall through remoteTenantScope), and RemoteTransport's $in arm compiles the same semi-join. driver-memory rewrites the leaf into the parent ids read in process under the scope; driver-mongodb refuses in translateFilter. One refusal envelope everywhere, INTERNAL_ERROR / 500 (never INVALID_FILTER), logged at error once per driver. It comes from a shared helper in @objectstack/core, a landing point outside the claim's file surface (see open_questions). The parked case-set (READABLE_IDS_CASES and its oracle test) was carried from e641ab6 with git restore --source; only api-surface/data.json and export-origins/data.json were regenerated. Mechanism hypotheses: H1 confirmed (the inheritance holds; RemoteTransport needed its own compile). H2 confirmed (lowerFilterCondition keeps the composed leaf by reference and makes the $not around it NULL-safe). H3 confirmed (only the conformance files, its test, the export line and the CASE_SETS row were carried). H4 confirmed (one suite file per driver package).",
"tests": "At the commit before the cast fix (the full suites):\n- core 2482 passed.\n- driver-sql 3659 passed, 208 skipped (the live pg and mysql cells).\n- driver-turso 2399 passed, 33 skipped.\n- driver-sqlite-wasm 685 passed.\n- driver-memory 1732 passed.\n- driver-mongodb 702 passed, 182 skipped (mongod opt-in).\n- spec readable-ids-conformance.test.ts + readable-ids.test.ts: 47/47.\nNew suites at 813e08c and again at 52dc068: sql 17 passed + 2 unprovisioned cells; turso local + remote 25/25; memory 14/14; sqlite-wasm 10/10; mongodb 12/12. After the main merge the sqlite-wasm full suite passes 695/695.\nTypecheck exits 0 for core, driver-sql, driver-sqlite-wasm, driver-turso, driver-memory and driver-mongodb. driver-mongodb's test layer needed @objectstack/objectql's .d.ts built first; locally they were missing.\nAblation: each fix was committed first, then mutated with scripts/ablation-replace.mjs (anchor 1->0; blob == HEAD and git diff HEAD empty after restore). The tests import source directly, so no dist leg applies:\n- SqlDriver $in composed arm off: 13 failed / 4 passed.\n- RemoteTransport semi-join arm off: 12 failed / 13 passed (the local face stays green).\n- memory rewrite off: 10 failed / 4 passed.\n- mongodb refusal off: 11 failed / 1 passed.\nO1: on the SQLite cell the statement binds 2 tenant walls plus the scope's own comparands (2, 3 and 0 bindings captured), never one parameter per readable parent. The SQLite ceiling was measured at 32766 (32767 refused, too many SQL variables). Postgres and MySQL: NOT MEASURED (no server in the container; 65535 documented for both).",
"gates": "Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack: 111 families, run at 813e08c's predecessor, each exit captured before any pipe.\n- First pass: 109 exit 0. check:query-options-erasure exit 1 (the test-surface count went 236 to 242 from as-any query casts in the new suites); fixed in 813e08c and re-run exit 0. check:dual-build-cjs-loads exit 3, PREREQUISITE NOT MET, needs a full-repo dist: NOT MEASURED, CI runs it.\n- --ran reconciliation exit 0: 111 derived families accounted for, 110 run, 1 NOT-MEASURED (DERIVED from the recorded exit 3), 0 UNRUN.\n- Re-run at 813e08c, all exit 0: check:query-options-erasure, check:driver-conformance, check:test-source-alias, check:cross-package-test-inputs, check:type-check-coverage, check:nul-bytes, check:doc-authoring.\n- After merging origin/main (52dc068; packages/spec had moved): spec build + check:generated says all 14 artifacts are up to date; the derived list is byte-identical; the --ran verdict is unchanged.\n- Driver conformance ledger: BEFORE at 23e689d, 50 covered / 0 DEBT / 0 exempt. AFTER at 52dc068, 55 covered / 0 DEBT / 0 exempt. Zero rows added.\n- Local-only family: check:dev-prereqs was not run as a scan. The spec dist was rebuilt by hand before every gate that reads it.\n- CI on #23656 at report time: 24 check-runs. 'No other open PR may claim the same issue' succeeded, 'Packed-tarball smoke (opt-in)' was skipped, and the other 22 (including Lint, Type Check, Check Changeset and Check PR Size) were in_progress.\n- Size: 1806 changed lines (1777 additions + 29 deletions, generated included), under the 3000 human-merge line.",
"mcp_calls": "5 — read-only: mcp__github__issue_read x3 (get #23639; get_comments #23639; get_comments #22788), mcp__github__pull_request_read x2 (get #23656; get_check_runs #23656). No write tool.",
"api_writes": "2 relay strokes through scripts/pm/fleet-write/dispatch.mjs, each a POST /repos/objectstack-ai/objectstack/dispatches. Stroke 1 was pr_create, executed as POST /repos/objectstack-ai/objectstack/pulls (draft) and then POST /repos//issues/23656/assignees (sunhaolin); the read-back was identical. Stroke 2 is this os-dev-report comment, POST /repos//issues/23639/comments. Zero label writes: the dispatch named no label, and the changeset criterion rules out skip-changeset. git push is not REST.",
"open_questions": [
{
"question": "Where the shared walk, rewrite and refusal live. They are in @objectstack/core (packages/core/src/utils/readable-ids-driver-refusal.ts and its export), a package outside the claim's declared file surface.",
"options": [
"A keep it in core: one source for four compilers that do not import each other, following the precedent of json-column-operator-refusal.ts",
"B four hand copies, one per driver package, inside the declared surface"
],
"recommendation": "A, on the four axes. It prevents AI error: one refusal text and one walk cannot drift into four dialects. It fits long-term: a contract-first single seam. It adds no capability: it implements D3 only. The cost is one core changeset row (minor, new exports). Recorded as a deviation; the seat may want to amend the claim's file surface."
},
{
"question": "The refusal is logged once per driver, but driver-mongodb has no injectable logger and refuses inside the pure translateFilter.",
"options": [
"A console.error once per process (shipped)",
"B add a logger to MongoDBDriver"
],
"recommendation": "A. #5499 forbids anything beyond the D3 row in driver-mongodb, and B widens the package."
},
{
"question": "Turso remote face writes: should the leaf resolve on updateMany / deleteMany too?",
"options": [
"A resolve wherever the remote where is built (shipped), matching the local face, whose applyFilters compiles on every door",
"B refuse on write doors"
],
"recommendation": "A. The engine already refuses the leaf on write verbs (D1), so this only keeps the two faces of one driver identical."
},
{
"question": "Changeset bump for the five drivers.",
"options": [
"A minor (shipped): they gain the leaf's compile",
"B patch"
],
"recommendation": "A. It is a new capability and the claim's Clause-② is yes. The seat may lower it."
},
{
"question": "Conflicts between the role file and the platform or harness defaults, resolved by the role file.",
"options": [
"A role file (taken)",
"B platform defaults"
],
"recommendation": "A, taken in four places:\n- No PR-activity subscription and no CI wait; the dispatch contract outranks the platform's subscription posture.\n- The PR body ends with the session-URL footer, not the harness's emoji line.\n- Commits carry the model-free Claude-Session / Co-authored-by: Claude pair that AGENTS.md and the pre-push hook require, not the harness's model-named trailer.\n- The cloud session's primary checkout was detached while the dedicated worktree held the branch; afterwards it was put back on the branch at 52dc068 and the worktree was removed."
}
],
"out_of_scope_findings": [
"None filed. Noted, not filed:\n- check:dual-build-cjs-loads needs a full-repo build locally (environment; CI covers it).\n- The local objectql dist had no .d.ts until it was rebuilt (environment).\n- The carried case-set header linked a nonexistent symbol, readableIdsCaseHoldsComposedLeaf; corrected in place because this PR lands that file.\ncarrier: none."
],
"deviations": [
"File surface:\n- Added packages/core/src/utils/readable-ids-driver-refusal.ts plus its test and the packages/core/src/index.ts export line (reason in open_questions[0]).\n- Added a 2-line readableIdsDriverName override in packages/drivers/driver-sqlite-wasm/src/sqlite-wasm-driver.ts, so the inherited refusal names the right package.\n- The Turso work spans turso-driver.ts (toRemoteWhere) as well as remote-transport.ts.",
"The carried readable-ids-conformance.ts was edited by one doc-link fix (see out_of_scope_findings)."
],
"files_changed": [
".changeset/23639-readable-ids-drivers.md",
"packages/core/src/index.ts",
"packages/core/src/utils/readable-ids-driver-refusal.ts",
"packages/core/src/utils/readable-ids-driver-refusal.test.ts",
"packages/drivers/driver-memory/src/memory-driver.ts",
"packages/drivers/driver-memory/src/memory-readable-ids-conformance.test.ts",
"packages/drivers/driver-mongodb/src/mongodb-filter.ts",
"packages/drivers/driver-mongodb/src/mongodb-readable-ids-conformance.test.ts",
"packages/drivers/driver-sql/src/sql-driver.ts",
"packages/drivers/driver-sql/src/sql-driver-readable-ids-conformance.test.ts",
"packages/drivers/driver-sqlite-wasm/src/sqlite-wasm-driver.ts",
"packages/drivers/driver-sqlite-wasm/src/sqlite-wasm-readable-ids-conformance.test.ts",
"packages/drivers/driver-turso/src/remote-transport.ts",
"packages/drivers/driver-turso/src/turso-driver.ts",
"packages/drivers/driver-turso/src/turso-readable-ids-conformance.test.ts",
"packages/spec/api-surface/data.json",
"packages/spec/export-origins/data.json",
"packages/spec/src/data/index.ts",
"packages/spec/src/data/readable-ids-conformance.ts",
"packages/spec/src/data/readable-ids-conformance.test.ts",
"scripts/check-driver-conformance.mjs"
],
"line_budget": "1806 changed lines (1777 + 29), generated included; under the 3000 human-merge line"
}
Generated by Claude Code
objectstack-fleet commentedon Oct 15, 2026
ACCEPT → PR #23656 comment 6190898902 ·
domain:engineseat 1 · 2026-10-15T11:24Z · contract review 6190904969 (PASS at45a871de4d) ·Clause-②: yes· zero conformance ledger rows · landing waits for every check on the head.Generated by Claude Code