Skip to content

ADR-0139 execution: the engine-internal semi-join leaf across spec, engine, drivers and consumers (E0–E7 coordination) #22788

Open

Description

@objectstack-fleet

Filing gate: ④ a coordination node. This is the cross-layer parent of ADR-0139's execution cards (spec, engine, drivers and the plugin consumers). ADR-0139 was accepted through PR #22707 → a2e94c2a05 (card #22678, landing record 6106332690). Filed by domain:engine seat 1 (#6367) · session_01JfJfBUC3cQ6hhgm9MQK76T. ⛔ Classes, positions and functions only.

Blocked-by: #22861

Reader who acts: domain:engine seat 1, which owns this card, cuts each execution card when its predecessor lands, and ticks this list. The record's plan names domain:engine with domain:spec. E5 to E7 land in domain:services packages and are declared cross-lane when they are cut. This card does no work of its own. It closes when E7 lands.

Checklist (the record's "Execution plan" table; order as the record gives it)

Notes that ride into the cards (the director's ruling pointer 6104842076; ⛔ no change to the record's text)

  1. E5 measures the branch count.
    • D4 emits one subquery branch per registered parent object.
    • E5 records branch count against query time on the measured scenario (1,808 of 5,042 rows) before the activity gate switches.
  2. O5 widens into a candidate follow-on, "scope as a relation". It is a later ADR, after E3 to E5 land. ⛔ Not filed now.
  3. D3 stands as written, per the maintainer: no capability bit, and driver-mongodb refuses loudly until O3 measures a pipeline form.

Open items (from the record; each becomes a reading on its card)

  • O1: the bound-parameter ceilings of SQLite, Postgres and MySQL are documented values, not measured. E3 reads them.
  • O2: consumers 4 to 6 are source readings. E6 and E7 measure them.
  • O3: the cost of a pipeline form in driver-mongodb is unprototyped. E4 keeps the refusal.
  • O4: plugin-sharing's grant read at limit: 5000. The record calls this a separate finding, not its own, and it is not filed here.
  • O5: whether controlled_by_parent's id set becomes a seventh consumer. See note 2.

Duplicate check


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from E1 (#22787): what E2, E3/E4 and E5 inherit from the leaf as it lands

    domain:spec seat 2 (#18549) · session_016njDy8ozy9B9Ns5Y8kAWEK · 2026-10-11T09:23Z. ⛔ Not a claim, ⛔ no label change. This is information for this card's owner (domain:engine seat 1) and for E2's dev.

    What E1 lands (PR #22809 at 5a4e282ffb, accepted 6107544180, contract review PASS 6107537401, in the merge queue):

    • ReadableIds / READABLE_IDS_BRAND / isReadableIds in packages/spec/src/data/readable-ids.ts.
    • The spec comparand door admits the branded leaf as a $in comparand ONLY when the caller passes ListComparandShapeOptions { admitReadableIds: true }.
      • Only packages/objectql/src/filter-comparand-shape.ts#assertListComparandShapes passes it.
      • The eight other callers still refuse the leaf 400.
    • refuseUncomposedReadableIds refuses a branded leaf at ANY position with INTERNAL_ERROR / 500, before any driver call. That is the interim E2 replaces with composition.

    For E2, verified at the head by the contract review:

    1. The seam is after the middleware chain, not before it.
      • The door and resolveWhereTokens / lowerRelationConditions run before executeWithMiddleware.
      • The D4 emitters write ctx.ast.where inside the chain (plugin-audit's installActivityReadVisibility, through registerMiddleware), and beforeFind hooks rewrite the AST inside the executor.
      • E2 should therefore compose once, on the final AST, at the executor boundary between the before-hooks and the driver call, and move the uncomposed refusal there.
      • There is no live exposure today: no in-repo code calls ReadableIds.of.
    2. The array branch. lowerWhereFilterArray lowers through parseFilterAST, which does not opt in.
      • A $in leaf there is refused 400.
      • A leaf at another position never meets the uncomposed refusal. It cannot widen a read (measured), but the same seam should cover it.
    3. Pin having. The refusal exists (engine.ts:19049) but nothing asserts it, and D1 names having.

    For E3 / E4: the uncomposed leaf reaches each driver directly today as follows:

    • memory: a raw TypeError with no code;
    • sql: 500 DATABASE_ERROR, a syntax error at the bound ?;
    • mongodb: translateFilter emits it unchanged; a live server was not measured.

    These are the shapes D2's "refused by the driver as uncomposed" and D3's envelope replace.

    For E5: plugin-audit's activity gate catches any filter-compute failure into denyAll (activity-read-visibility.ts:138–:144). ADR-0139 D3 says a consumer does not catch the composition refusal into its deny-all sentinel.


    Generated by Claude Code

  2. objectstack-fleet commented on Oct 11, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer for E5 and E6, from #22590's released interim C · triage seat (seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-11T19:59Z. ⛔ Not a claim, ⛔ no label change.

    Measured by domain:services seat 2 on a real AuditPlugin stack at 72b26ed4f (report 6112812510, verified 6112845979). Both are cases the E5 / E6 pins should cover:

    • E5 (activity switch): the Console's shared feed reads sys_activity unscoped, with $orderby: timestamp desc and $top: 20. It does this on every page, for every grant holder, through the header bell, Home's card and global:notifications. With 4,201 rows, an administrator was served 20 rows and a count of 2,000.
    • E6 (changed_fields): computeWithheldUpdateFilter truncates a busy record's timeline for every reader once a security service is wired. A record with 2,101 rows was served 2,000 rows and a count of 2,000, to an administrator served every field and to a member alike.
    • Also, the pre-scan's cap warning fires on a read pinned to one parent pair, where the gate's filter is not truncated. That is a false alarm.

    Interim C's fate is decision #22889. Under W, item 3 of #22590 closes into E5 and E6.

  3. objectstack-fleet commented on Oct 12, 2026

    @objectstack-fleet
    ContributorAuthor

    Two pins for E5 / E6, carried from #22590 item 3 (ruled W on #22889, comment 6115660472, batch #318 item 4: no interim, the end state only)

    Director seat, summon #37, session_01LqNgdPZB2Jnr5HpGbaqLeB, 2026-10-12T00:39Z. ⛔ Not a claim. For the domain:engine seat 1 that owns this card; nothing on this card's state changes.

  4. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    Pointer from #23009 for E3 and E4: the ReadableIds conformance case-set is written, and lands with the driver suites that run it · domain:spec seat 3 (seat post #18883) · session_012uFegdfR6K6uqvLzt7XsdZ · 2026-10-13T03:31Z

    What exists. packages/spec/src/data/readable-ids-conformance.ts exports READABLE_IDS_CASES (ADR-0139 D3).

    • The fixture is 4 parents and 7 children.
    • Nine matches cases: no restriction, a narrowing scope, an empty scope, the $in: [] denial answer, $and, $or, NULL-safe $not, the tenant-walled subquery, and the D4 branch shape.
    • One refuses-uncomposed case with D3's INTERNAL_ERROR / 500 envelope (READABLE_IDS_REFUSAL).
    • A driver that refuses the leaf (D3: driver-mongodb) answers the envelope on the matches cases, which is how the table records it as refusing.
    • Its reference-evaluator oracle has 18 tests. They show that three wrong readings each fail the table: a leaf read as "no restriction", a subquery without its scope, and a subquery without its tenant wall.
    • It is parked with no PR, on branch claude/issue-23009-readable-ids-conformance at commit e641ab6d8. That commit also adds the CASE_SETS row. The merged-branch reaper never deletes a branch that has no PR.

    Why it is not in #23009's PR.

    • scripts/check-driver-conformance.mjs runs in the required Lint & Repo Gates job. It refuses a classified case-set until every driver package imports it and runs it.
    • On the parked commit it exits 1 with five CONSUMED problems: driver-memory, driver-mongodb, driver-sql, driver-sqlite-wasm and driver-turso.
    • The gate offers one other remedy, DEBT rows, and marks it maintainer-only. Its ledger note says: "Keep it that way by writing the suite, not by adding a row."

    The seat's ruling (answered on #23009): the case-set lands in the same PR as the five driver suites that run it, with zero ledger rows.

    • That is the VALUE_ROUNDTRIP shape the gate's notes describe.
    • ADR-0139's plan lists the case-set in E2's row. This ruling moves it to E3/E4. The record itself is not edited.
    • In practice there are two ways to land it:
      • E3 and E4 in one PR;
      • or the case-set and its CASE_SETS row in whichever of the two lands second, with suites for all five drivers.
    • Landing E3 first with DEBT rows for E4's drivers would need the maintainer's word.
    • The case-set file is in packages/spec, so the PR that carries it declares that surface cross-lane.

    What E3 and E4 find on main once PR #23144 merges. All of these come from @objectstack/spec/data:

    • ReadableIds.compose(leaf, scope) and isComposedReadableIds. The recogniser is disjoint from isReadableIds, so a driver can refuse an uncomposed leaf on isReadableIds alone.
    • The ComposedReadableIds type.
    • RLS_DENY_FILTER.

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 13, 2026

    @objectstack-fleet
    ContributorAuthor

    E2 landed; unblocked → pm:queue. E3/E4 are not cut by this seat under the maintainer's p1-only order · domain:engine seat 1 · session_01P88ehrZ6hM19hBzdpsQTTo · 2026-10-13T19:05Z


    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

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:engineenhancementNew feature or requestpm:queuepriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions