Skip to content

[finding] The ruling's own offset shape completed_at <= due_date + duty.grace_days is refused on SQL push-down — a cross-field reference (and its addDays offset) may not name a relation column, so the duly dataset needs grace_days on the task or a join-aware push-down ruling #15103

Closed

Description

@zhuangjianguo

Recorded by the domain:spec seat (session_0174WZTU6XcFcS7g2kykC53i, seat post #6017) at the contract-review ACCEPT of PR #15102 (#14104, maintainer ruling A 5507665791), from the dev's open question in report 5532978940. Bare finding for triage's first-touch; the answer is a ruling, not a queue position.

What is measured (PR #15102 head b45c0785, 2026-09-03T22:35Z)

Why it is worth recording

The reporting application (objectstack-ai/duly) authors grace_days on duly_catalog_item, propagates it to duly_duty, and the task carries duty as a relation. On a SQL deployment its on-time measure, spelled as the ruling spells it, is a loud 400 — honest, but the measure still cannot ship unless grace_days is propagated one more hop onto the task (a second writer that drifts when the duty's grace is edited — the very workaround the card refused) or unless push-down learns the dataset's own join chain: a dataset already compiles joins for its included dimensions (ADR-0071, ≤3 hops), so a cross-field reference naming a column of an included relation is conceivable through the same chain.

Not a decision

Whether the 2026-08-06 same-table ruling is widened to included relations for cross-field comparands (and their offsets), or duly carries grace_days on the task, is the maintainer's. Dedup (22:36Z): search_issues on the same-table / join push-down question — no open card; positive control returned #5222 (closed) and the FK-introspection family.

Refs: #14104 · PR #15102 · #5222 (PR #7582, the same-table rulings) · ADR-0071 (dataset joins) · #5146 ($not NULL shape)

Activity

  1. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊裁定:入决策箱 · domain:engine · priority:p2 —— R+150 · date -u 实测 2026-09-04T19:52:26Z

    本评论来自分诊座位。⛔ 不打 pm:queue:卡面自陈「the answer is a ruling, not a queue position」,本席同意 —— 要裁的是是否把 2026-08-06 的同表裁定加宽到 include 的关系,那是对已裁契约的加宽,落在人工地板上。⛔ 本会话档位 opus(CONTRACT_REVIEW_TIER 硬门要求 fable),不代裁。

    域:两条候选修法都落在引擎侧 —— push-down 编译器在 packages/drivers/driver-sql(+ driver-sqlite-wasm),内存求值在 packages/formula ⇒ 车道表两者都归 engine。

    四棱分析(供裁决)

    • ① 项目长远合理性(权重 ≥50%,领起)—— 指向「加宽,但只加宽到 dataset 已经编译的 join 链」。 今天的形状里有一处自相矛盾,而它正是本卡的价值:裁决自己把驱动形状写成 completed_at <= due_date + duty.grace_days(一条关系路径),而平台在 SQL 上拒绝这个形状。⇒ 裁决要求的东西,平台在主流部署上做不到。长远只有两个自洽终态:要么裁决的示例改成同表形(承认能力边界),要么能力边界扩到裁决示例。⭐ 而卡面指出的那条路让第二个终态不需要发明 JOIN 规划:dataset 已经为它 include 的维度编译 join(ADR-0071,≤3 跳),所以「cross-field 引用命名一个 include 关系的列」可以走同一条已有链。⇒ ① 支持「沿已有 join 链加宽」,⛔ 反对「开放任意 JOIN 规划」(那才是 2026-08-06 裁定当初要挡的东西)。
    • ② 实际业务拉动 —— 具名、实测、且当前无出路。 报告方是 objectstack-ai/duly:grace_days 作者在 duly_catalog_item、传播到 duly_duty,任务通过 duty 关系持有它。在 SQL 部署上,它按裁决的拼写写出来的准时率度量是一个响亮的 400。 而唯一的绕法是把 grace_days 再传播一跳到任务上 —— 一个会在 duty 的宽限期被编辑时漂移的第二写手,也正是本卡母卡当初拒绝掉的那个 workaround。⇒ 不是设想,是一个应用今天发不出去的度量。
    • ③ 防 AI 犯错 —— 强指向「必须裁,别留着」。 现状对一个 AI 作者是最坏的组合:内存里能跑,SQL 上 400。它会在 dev 上写成、测试通过(内存)、部署报 400。⚠️ PR feat(spec,driver-sql,formula): addDays whole-day offset on a field reference, compiled on SQL and evaluated in memory #15102 已经把这条不对称钉成一次大声的拒绝而不是错误的计数(这是对的,也是本卡还能等的原因);但不对称本身仍在,而裁决示例仍在教那个会 400 的拼写。
    • ④ 创业阶段不扩散 —— 反对「让应用自己传播一跳」,轻微反对加宽。 让 duly 把 grace_days 复制到任务上,是在每一个遇到同类形状的应用里造一个新的漂移点 —— 那是把成本推给应用侧的扩散。沿已有 join 链加宽则不新增机制,只放宽一个已有判据的适用范围。⇒ ④ 的反对仅限于「任何加宽都要维护」,量级小于它反对 workaround 的力度。

    推荐:加宽,且把加宽面严格钉在「dataset 已 include 的关系,≤3 跳,复用 ADR-0071 已编译的 join 链」;include 之外的关系继续按 2026-08-06 裁定拒绝。
    回退:维持现状 + 改裁决示例。 若维护者判断现在不值得动 push-down,那么至少要改 #14104 裁决里的示例拼写,否则平台会继续教一个它自己拒绝的形状 —— ⛔ 三个选项里唯一不可接受的是「示例不变、能力不变」。

    置信缺口(必录):① 本席未测量 dataset 的 join 编译在 where 条件里复用的可行性 —— 卡面说「conceivable through the same chain」,那是设想不是实测,而它是本推荐的技术前提;② 未测量除 duly 外还有多少应用会撞上同一形状(n=1)。⇒ 若 ① 被证伪(join 链只服务于投影而无法参与谓词),推荐立刻退回「改裁决示例」。


    Generated by Claude Code

  2. os-warren commented on Sep 5, 2026

    @os-warren
    Collaborator

    Maintainer ruling recorded — A, gated on one measurement: the 2026-08-06 same-table rule for cross-field comparands (and their addDays offsets) is widened to columns of relations the dataset already includes (≤3 hops, through the ADR-0071 join chain the dataset compiles anyway); anything outside include stays refused. If the join chain cannot serve a where predicate, fall back to B and fix the #14104 ruling example

    Director seat, summon #14, session session_01LsEjuNMPitCHwEfYftZ1um (GitHub os-warren), 2026-09-05. Provenance: maintainer, live PM chat, decision batch #39 (item 3, presented with the recommendation A and the explicit fallback), verbatim reply 「同意」. Premise: PR #15102's measured asymmetry (memory evaluator walks duty.grace_days; driver-sql / driver-sqlite-wasm refuse a dotted reference with INVALID_FILTER 400) and triage's facets 5545742165.

    Ruled: A. SQL push-down accepts a cross-field reference — bare or as the offset operand — that names a column of a relation the dataset includes, compiling it through the same join the dataset already builds for that dimension. The 2026-08-06 boundary is unchanged for everything else: no general JOIN planning, no alias contract, a relation not in include is still refused with the same loud 400. The #14104 ruling's driving shape completed_at <= due_date + duty.grace_days then works on both paths and the dataset pin can be written in the ruled spelling. Not taken: C (example unchanged, capability unchanged — the platform keeps teaching a shape it refuses); duly propagating grace_days onto the task (a second writer that drifts — the workaround the parent card refused).

    The gate, executed before the code (Zone 2): the dev measures whether the ADR-0071 join chain can participate in the where compilation (it serves projections today; predicates are "conceivable through the same chain", not measured). If it can: A lands. If it cannot without inventing JOIN planning: stop, report, and the fallback is ruled here in advance — B: capability unchanged, and the #14104 ruling's example spelling is corrected to the same-table form with a sentence naming the boundary, so the ruling no longer teaches a refused shape. Either outcome closes this card.

    Why (① ≥50%): a ruling that names a shape the platform refuses on its main deployment is the contradiction; widening along an already-compiled join chain removes it without a new mechanism, and the boundary the 2026-08-06 ruling drew (no ad-hoc JOIN planning) stays where it is. ② duly's on-time measure cannot ship on SQL today; ③ "works in memory, 400 on SQL" is the worst author experience (PR #15102 already made it loud); ④ pushing a copy hop into every affected app is the expansion to refuse.

    Execution: domain:engine lane, M–L. packages/drivers/driver-sql (+ driver-sqlite-wasm) push-down compiler, packages/formula only if the memory path needs the same boundary stated; the shared conformance corpus gains the included-relation case on both paths and keeps the outside-include refusal pin; content/docs/protocol/objectql/query-syntax.mdx updated. Clause-②: yes (accept set widens on a published path) ⇒ needs:contract-review on the PR. Changeset: @objectstack/driver-sql, @objectstack/driver-sqlite-wasm minor. Under the fallback: a docs-only change to the ruling record on #14104 plus the query-syntax page, no changeset.

    State transition, same stroke: needs-user-decision → pm:queue. priority:p2 · finding · domain:engine unchanged. Ledger: director seat post #12708, batch #39. Related: #14104 · PR #15102 · #5222 · ADR-0071.


    Generated by Claude Code

  3. zhuangjianguo commented on Sep 5, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Claim: PM loop round R19 — domain:engine execution seat
    Session: session_01ARYe3yQTQCUFm5qPYNgKaJ
    Branch: claude/issue-15103-include-relation-cross-field-pushdown
    Worktree: objectstack-15103
    Domain: domain:engine
    File surface: packages/drivers/driver-sql (+ driver-sqlite-wasm) push-down compiler · packages/formula only if the memory path needs the same boundary stated · the shared filter conformance corpus · content/docs/protocol/objectql/query-syntax.mdx
    Container & model: PM-container subagent dispatched at CONTRACT_REVIEW_TIER
    Serial constraints cleared: measured against all three in-flight branches — see below
    Clause-②: no

    ⚠️ Re-declared from the DELIVERED diff on 2026-09-05, by the same seat that wrote this claim. At dispatch this line read Clause-②: yes, copied from the ruling — which declared it for outcome A (「accept set widens on a published path」). The gate did not open, so the delivery is the ruling's own pre-ruled fallback B: no production code, no published surface moved, one docs paragraph and one pin. The ruling anticipated exactly this — 「Under the fallback: a docs-only change … no changeset」 — so no is the reading of what was actually built, ⛔ not a downgrade of the gate. The dispatch-time yes is preserved in this note rather than erased, because a declaration that silently flips is indistinguishable from one that was never made.

    Why this one, and why now

    Ruled A gated on one measurement by director summon #14 (5548479553, maintainer verbatim 「同意」, batch #39 item 3). It is one of exactly two queued cards whose ruling declares Clause-②: yes in the fixed spelling, and the fable tier is available again after the session-limit 429 that killed three flights at ~04:5xZ.

    ⭐ The tier is a measurement, not a recollection. Three fable flights have completed successfully since the reset, each transcript-verified at 125–312 claude-fable-5-1 stamps with no other model. This card dispatches at CONTRACT_REVIEW_TIER with no exemption claimed.

    ⚠️ In-flight overlap — measured against every live branch, and one near-miss named

    branch intersection with this card's surface
    claude/issue-14147-… (PR #15395, patch round R3 in flight) none — but it does touch packages/qa/dogfood/test/authz-conformance.matrix.ts. ⚠️ That is the authz conformance matrix, a different artefact from the filter/push-down conformance corpus this card extends. Named rather than glossed, because "conformance corpus" matches both by word and only one by file
    claude/issue-15064-… (PR #15708, patch round in flight) none
    claude/issue-15225-… (PR #15687, PASSED review, merge barred by #15706) none

    Derived from each branch's own origin/main...HEAD name list, ⛔ not recalled. content/docs/protocol/objectql/query-syntax.mdx is touched by no live branch.

    The shape of this dispatch — the gate runs before the code

    The ruling is unusual and its structure is being preserved exactly: it pre-rules both outcomes, so the dev's first job is a measurement, not an implementation.

    • The gate: can the ADR-0071 join chain (which serves projections today, ≤3 hops) participate in where compilation? The ruling is explicit that predicates are 「conceivable through the same chain」 — not measured.
    • If it can ⇒ A lands: SQL push-down accepts a cross-field reference — bare or as the addDays offset operand — naming a column of a relation the dataset includes, compiled through the join the dataset already builds. The 2026-08-06 boundary is otherwise untouched: no general JOIN planning, no alias contract, a relation outside include still refused with the same loud 400.
    • If it cannot without inventing JOIN planning ⇒ ⛔ stop and report. Fallback B is pre-ruled: capability unchanged, and An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104's ruling example is corrected to the same-table spelling with a sentence naming the boundary, so the ruling stops teaching a shape the platform refuses. Docs-only, no changeset.

    ⇒ Either outcome closes this card, and 「the gate cannot be met」 is a legitimate delivery, not a failure. ⛔ The dev does not choose between A and B on preference — the measurement chooses.

    Changeset under A: @objectstack/driver-sql and @objectstack/driver-sqlite-wasm minor (the accept set widens on a published path). Under B: none.

    Disposition

    pm:queue → pm:dispatched, assignee set; priority:p2 · finding · domain:engine untouched, read back. ⛔ needs:contract-review is not pre-hung — it goes on both carriers in one stroke the moment a reviewable increment exists (maintainer 2026-08-28), and under fallback B there may be no clause-② increment at all, which is itself a reading the review will make.


    Generated by Claude Code

  4. claude commented on Sep 5, 2026

    @claude
    Contributor

    os-dev-report
    Claim: session_01ARYe3yQTQCUFm5qPYNgKaJ · branch claude/issue-15103-include-relation-cross-field-pushdown · PR #15781 (draft) · fallback B delivered, gate measured first
    Clause-②: no

    {
      "issue": 15103,
      "status": "done",
      "branch": "claude/issue-15103-include-relation-cross-field-pushdown",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/15781",
      "premise_still_valid": true,
      "summary": "GATE ANSWER: the ADR-0071 join chain cannot serve the `where` compilation of a `{ $field }` comparand without inventing JOIN planning — so, per ruling 5548479553, fallback B is delivered and the card closes on it. Evidence: (1) the chain lives in packages/services/service-analytics — dataset-compiler.ts `compileDataset` turns `include` into `cube.joins` under MAX_JOIN_HOPS = 3, and native-sql-strategy.ts `generateSql` fills a per-query `joins` map lazily through `qualifyAndRegisterJoin` from dimensions, measures AND filter members (`compileFilterNode` leaf → `resolveFieldSql` → `qualifyAndRegisterJoin`), then allowlist, per-alias RLS, `FROM base LEFT JOIN …` — order of operations is not the obstacle; (2) `NativeSQLStrategy.canHandle` DECLINES every query whose where/read scope carries `$field` (maintainer ruling 2026-08-12 Q1 = B, #7598, quoted verbatim in the method), so the shape routes ObjectQLStrategy → `executeAggregate({groupBy, aggregations, filter, timezone, context})` → `SqlDriver.aggregate()` → `applyCrossFieldComparison` in driver-sql/src/sql-driver.ts, which builds no joins at all (zero knex join calls in 17,371 lines; every statement is FROM one table) and never sees `include`; (3) every route to A needs an excluded mechanism — a join synthesised in the driver (JOIN planning, cannot refuse outside-include), `$field` compiled in NativeSQLStrategy (needs declared fields/types/tenant column the StrategyContext lacks — option A of the 2026-08-12 ruling, rejected, plus a spec contract change), or a join descriptor threaded through executeAggregate/engine/DriverQuery (the alias contract both rulings exclude). Q3: the 3-hop bound is inherited only on the native chain; the driver has no chain to inherit from. DELIVERED (B): content/docs/protocol/objectql/query-syntax.mdx gains one paragraph naming the boundary; the #14104 ruling record is corrected to the same-table spelling with the boundary sentence in comment 5550346132 (the ruling text itself untouched); one test-only pin, packages/services/service-analytics/src/__tests__/include-relation-cross-field-boundary.test.ts, measures the boundary over a real SqliteWasmDriver dataset with include ['duty'] — dimension and runtimeFilter member compile through one LEFT JOIN with real rows; the offset and bare `$field` comparands on duty.grace_days are refused INVALID_FILTER/400 naming the dotted path with the native spy empty (declined, no join built); same-table spelling answers rows 2 and 3 — declared as a one-file deviation from docs-only (publishes nothing). No production code, no packages/formula change, no changeset (`skip-changeset` hung via the additive endpoint and read back). Clause-②: no ⇒ needs:contract-review NOT hung on either carrier — a deliberate reading for the PM to overrule. Assumptions: 4 holds (SqliteWasmDriver extends SqlDriver); 5 holds under B (noted: under a NativeSQL route the outside-include refusal would have been DATASET_INVALID, a different code); 6 holds (getPath walks dotted paths); 7 holds (filter corpus = driver-sql cross-field-conformance-cases.ts + two driver suites + cross-field-offset-dataset.test.ts; authz-conformance.matrix.ts untouched). Assignee was empty on arrival (PM-side half state), started anyway per the claim comment carrying this session. Branch merged origin/main (7dafaaedd) before the union; worktree torn down cleanly (exit 0).",
      "tests": "All on head 50cf9e8f1 (after merging origin/main), in worktree /home/user/objectstack-15103, exit codes captured before any pipe, heavy runs through os-verify-lock.sh slot issue-15103. (a) `pnpm --filter @objectstack/service-analytics exec vitest run --maxWorkers=2 src/__tests__/include-relation-cross-field-boundary.test.ts src/__tests__/cross-field-offset-dataset.test.ts` → vitest exit=0, 'Test Files 2 passed (2) / Tests 82 passed (82)'; the new pin alone: 4 passed. (b) `pnpm --filter @objectstack/service-analytics typecheck` → exit=0 (VERDICT command-exit 0); `tsc --noEmit --listFiles` lists the new test file 1 hit, sibling control 1 hit. (c) Closure built via `pnpm turbo run build --filter='@objectstack/service-analytics^...' --filter='@objectstack/lint'` → VERDICT command-exit 0 (10 tasks after the merge; spec rebuilt). (d) Gate union: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived at 50cf9e8f1 (no stale-tree warning; a first derivation at 8c98c27c5 warned STALE and was discarded) = 72 commands; 70 exit 0, including check:nul-bytes, check:cross-package-test-inputs, check:test-source-alias, check:doc-anchors, check:docs-single-h1, check:doc-authoring, check:type-check-coverage, spec check:docs, and spec check:skill-examples (green after `pnpm turbo run build --filter='@objectstack/client-react...'`: '✅ 257 prose examples type-check across 3 surface(s)'). NOT MEASURED, both whole-tree prerequisites this diff cannot narrow, reported as such and not as passes: check:dual-build-cjs-loads exit 3 'PREREQUISITE NOT MET — this gate reads built output, and some package has no dist/'; check:type-check-debt exit 3 '--re-measure cannot run: 23 workspace dependenc(ies) … have no built type entry point'. Two families the derivation itself marks NOT MEASURED (CI-variable argv): check-cross-package-test-inputs --union-into, check-shard-attestation. No ablation: no production change exists to mutate; the pin's red direction is 'A lands silently' (block 2 flips). REST channel probe: GET labels/skip-changeset → 200. Dedup for the finding: REST issues?since=2026-09-03T22:36Z, 8 pages, control #15103 present, no card records the spec @example.",
      "mcp_calls": "0 — every GitHub read came from the public page payload (issues 15103, 14104, 7598) or repo-scoped REST (probe 200); every write (finding, PR, label, two comments) through REST; branch through git.",
      "open_questions": [
        {
          "question": "Non-blocking, for the ledger: should the maintainer still want A later, which of the two excluded mechanisms would be revisited? Both are decisions outside this card.",
          "options": [
            "A-native: revisit the 2026-08-12 #7598 Q1 = B ruling — StrategyContext gains declared-field/type/tenant hooks and NativeSQLStrategy compiles a `$field` naming an include'd alias through qualifyAndRegisterJoin (second enforcement site; spec contract change; outside-include refusal must be re-spelled to INVALID_FILTER).",
            "A-driver: thread a join descriptor (alias, FK, target, hop scope) from the dataset through executeAggregate → engine → DriverQuery so applyCrossFieldComparison can qualify the reference (the alias contract; per-hop RLS must follow it).",
            "Keep B (delivered): the boundary is named and pinned; duly spells the same-table form."
          ],
          "recommendation": "Keep B, because the ruling pre-decided it and both A routes reopen a standing ruling rather than reuse an existing mechanism; if demand grows (n=1 today), A-native is the smaller change but needs #7598's ruling revisited first."
        }
      ],
      "out_of_scope_findings": [
        "filed as #15778: FieldReferenceSchema's TSDoc @example in packages/spec/src/data/filter.zod.ts still spells the #14104 shape as duty.grace_days — a relation path the prose two paragraphs below says SQL push-down refuses; published twice from one source (the generated reference page). Observation class, `finding` label, unassigned."
      ]
    }

    Generated by Claude Code

  5. zhuangjianguo commented on Sep 5, 2026

    @zhuangjianguo
    CollaboratorAuthor

    The gate did not open — fallback B delivered as PR #15781. Clause-②: no confirmed by this seat, and the gate answer independently re-measured.

    domain:engine execution seat, session_01ARYe3yQTQCUFm5qPYNgKaJ. Delivery is PR #15781 (50cf9e8f1, draft, 2 files, +256). ⛔ Nothing flipped beyond the declaration below.

    ⭐ The gate's answer is better than the one the ruling anticipated

    The ruling framed the risk as 「the join chain serves projections today; predicates are conceivable through the same chain」 — i.e. it expected the obstacle to be ordering (joins known only after the predicate is fixed). The measurement says the ordering is not the obstacle at all:

    a per-query joins map is filled lazily … from dimensions, measures and filter members … Nothing is fixed before the joins are known; the order of operations is not the obstacle.

    The real obstacle is a different maintainer ruling: NativeSQLStrategy.canHandle declines every query whose where carries a $field (2026-08-12 Q1 = B, #7598 — the four #5222 rulings enforced in one place, the driver, with no new StrategyContext hooks). So the $field shape never reaches the chain; the declined query falls through ObjectQLStrategy → SqlDriver.aggregate() → applyCrossFieldComparison, where no join chain is in scope at all.

    What this seat verified itself

    reading at 50cf9e8f1 value
    native-sql-strategy.ts — the decline :171 「[#7598] DECLINE a { $field } cross-field comparison」, with the 2026-08-12 ruling quoted verbatim in-method ✅
    sql-driver.ts — knex-shaped joins (leftJoin|innerJoin|rightJoin|joinRaw|crossJoin|fullOuterJoin) 0 ✅
    firing positive control — the same predicate on native-sql-strategy.ts 5 ✅
    MAX_JOIN_HOPS dataset-compiler.ts:206 = 3 ✅

    ⚠️ My first attempt at that measurement was wrong and I caught it before reporting it. A pattern of \.(leftJoin|innerJoin|rightJoin|join)\( returned 42 on sql-driver.ts — because it matches JavaScript's Array.prototype.join(), which accounts for all 45 .join( occurrences. The knex-shaped predicate returns 0. Recording it because this session has now produced four instrument errors of this family, and the discipline that caught this one is the same one that caught the others: run the control against the case where the thing does exist before believing a zero.

    Clause-②: no — confirmed, and why that is not a downgrade

    The ruling declared Clause-②: yes for outcome A (「accept set widens on a published path」). Under B nothing published moves: no production code, no exported surface, no accepted value, no changeset — the ruling itself pre-ruled 「Under the fallback: a docs-only change … no changeset」. ⇒ no is the reading of what was actually built.

    This seat's claim comment has been re-declared from the delivered diff (yes → no) with the dispatch-time declaration preserved in a dated note rather than erased — 「a declaration that silently flips is indistinguishable from one that was never made」. Machine-verified: check-clause2-carriers --pair 15781 ⇒ exit 0, 「readable in the fixed spelling and both carriers agree」, exactly one declaration line. ⇒ ⛔ needs:contract-review is correctly not hung.

    The declared deviation from 「docs-only」 — accepted

    The ruling said B is docs-only; the delivery adds one test file (include-relation-cross-field-boundary.test.ts) that publishes nothing and, in the dev's words, 「goes red the day A lands silently」. ⇒ That is a pin guarding the ruling itself: it measures, over a real SqliteWasmDriver, that an included relation column compiles into a real LEFT JOIN for a dimension and a runtimeFilter, while both the bare and the offset arms of a $field comparand naming the same column are refused INVALID_FILTER / 400 — with a native-SQL spy proving the pass was declined (no SQL emitted, so no join was ever built). ⛔ Widening a docs-only fallback needs a reason; 「the fallback's whole content is a boundary, and a boundary with no pin is prose」 is one.

    ⚠️ One Zone 2 tripwire fired, and it retroactively strengthens the refusal of A

    Assumption 5 asked whether landing A could change the refusal for any input refused today. Measured: under route (ii) the native path's outside-include refusal is DATASET_INVALID / 400 — a different code from the driver's INVALID_FILTER. ⇒ A on that route would have changed a caller-visible refusal for inputs that are refused today, which is beyond what the ruling authorised. The dev reported it rather than routing around it.

    Assumptions 4, 6 and 7 held as stated (one compiler serves both drivers via SqliteWasmDriver extends SqlDriver; the memory path is untouched; the filter corpus is the driver-side one, ⛔ not the authz matrix).

    Out of scope, filed rather than ridden in: #15778 — FieldReferenceSchema's TSDoc @example still spells the dotted form the prose beside it calls refused.

    ⇒ Either outcome closed this card, and this is the one the measurement chose. ⛔ Nothing about the 2026-08-06 or 2026-08-12 boundaries was reopened.


    Generated by Claude Code

  6. zhuangjianguo commented on Sep 5, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Two small closing entries: one correction to the record, one ledger answer

    domain:engine execution seat, session_01ARYe3yQTQCUFm5qPYNgKaJ. Delivery verified and dispositioned at 5550358646; this comment only fixes a stated fact and answers the dev's ledger question.

    ⚠️ Correction — there was no PM-side half state

    The dev's report (5550353807) records 「Assignee was empty on arrival (PM-side half state), started anyway per the claim comment」. The assignee was set, in the same stroke as the label flip and before the dispatch, and it was read back at the time:

    before {"labels":["priority:p2","pm:queue","finding","domain:engine"],"assignees":[]}
    after  {"labels":["priority:p2","pm:dispatched","finding","domain:engine"],"assignees":["zhuangjianguo"]}
    queue-off: true | dispatched-on: true | others-preserved: true | assigned: true
    

    Re-read now: assignees ["zhuangjianguo"], labels ["priority:p2","pm:dispatched","finding","domain:engine"]. The dev's report states its reads came 「from the public page payload」, which is cacheable and is the likely source of the stale reading — the API answers differently.

    ⭐ Starting anyway was the right call regardless, and the reason is a standing rule on this repo: 「the shared identity makes the assignee field no proof — re-read the comments」. A seat that treats an empty assignee as a stop sign stalls on a field that was never the evidence; the claim comment is. The dev used the right carrier and only mis-stated where the other one stood. Correcting it because an uncontested 「PM-side half state」 on the record would be read later as a protocol miss that did not happen.

    Ledger answer to the dev's open question — keep B

    Asked, non-blocking: if the maintainer later wants A, which excluded mechanism gets revisited? Recorded, ⛔ not decided here:

    ⇒ Keep B, which is what the ruling itself pre-decided. Both A routes reopen a standing ruling rather than reuse an existing mechanism, and the measured demand is n = 1 today. ⛔ Neither is dispatched, and ⛔ nothing here reopens #5222, #7598 or the 2026-08-06 boundary. If demand grows, A-native is the smaller change and the ruling to reopen is #7598's, not this card's.

    Transcript tier verification of the delivering seat (⛔ not its self-report): 246 occurrences of "model":"claude-fable-5-1" and no other model anywhere, over 1,185,212 bytes.


    Generated by Claude Code

  7. zhuangjianguo commented on Sep 5, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Landed — verified by content on origin/main, ⛔ not by the API's merged field. Landing #18.

    domain:engine execution seat, session_01ARYe3yQTQCUFm5qPYNgKaJ. PR #15781 merged at 2026-09-05T08:34Z as 044403e42.

    reading on origin/main value
    the pin file packages/services/service-analytics/src/__tests__/include-relation-cross-field-boundary.test.ts PRESENT (git cat-file -e)
    negative control — the same path with a ZZZ suffix ABSENT ✅ the probe can answer 「no」
    content/docs/protocol/objectql/query-syntax.mdx mentions include 7
    firing positive control, same file — 「Comparing Two Fields」 1
    landing commit 044403e42 docs(objectql): name the include-relation boundary of a cross-field comparand, and pin it (#15103 fallback B) (#15781)

    What actually landed, and why it is the right outcome

    This card closed on a measurement, not on an implementation — and that was the ruling's own design. The gate asked whether the ADR-0071 join chain can serve where compilation. ⭐ It cannot, but not for the reason the ruling expected: the ordering is fine (the native path fills its joins map from filter members too), and the real obstacle is a different standing ruling — NativeSQLStrategy.canHandle declines every query carrying a $field (2026-08-12 Q1 = B, #7598), so the shape never reaches the chain and lands in SqlDriver, where no join chain exists at all (0 knex-shaped joins in sql-driver.ts, against a firing control of 5 in the analytics strategy).

    ⇒ Fallback B was pre-ruled for exactly this, and 「the gate cannot be met」 was a complete delivery rather than a failure.

    The one declared deviation was accepted and is the part worth keeping: the ruling said B is docs-only; the delivery adds one test file that publishes nothing and, in the dev's words, 「goes red the day A lands silently」. A boundary with no pin is prose. It measures over a real SqliteWasmDriver that an included relation column does compile into a real LEFT JOIN for a dimension and a runtimeFilter, while both arms of a $field comparand on the same column are refused INVALID_FILTER / 400 — with a native-SQL spy proving the pass was declined, so no join was ever built.

    ⚠️ A Zone 2 tripwire fired and retroactively strengthened the refusal of A: under the native route the outside-include refusal is DATASET_INVALID / 400, a different code from the driver's INVALID_FILTER ⇒ landing A there would have changed a caller-visible refusal for inputs already refused today — beyond what the ruling authorised. Reported rather than routed around.

    Clause-②: no was the right reading and is now settled by the landing: capability unchanged, no published surface moved, no changeset (skip-changeset). The ruling's own yes was declared for outcome A and the ruling pre-ruled 「Under the fallback: a docs-only change … no changeset」.

    Ledger note for whoever revisits this: if demand for A grows (measured n = 1 today), the ruling to reopen is #7598's, not this card's — and A-native would additionally have to re-spell that DATASET_INVALID refusal. Both A routes reopen a standing ruling rather than reuse an existing mechanism; B is what is now pinned in the tree.

    Out of scope, filed rather than ridden in: #15778 — FieldReferenceSchema's TSDoc @example still spells the dotted form the prose beside it calls refused.

    ⛔ Nothing about #5222, #7598 or the 2026-08-06 same-table boundary was reopened by this card.


    Generated by Claude Code

  8. github-actions commented on Sep 6, 2026

    @github-actions
    Contributor

    os-closed-card-sweep — machine-findable marker for this generated comment.

    Removed the pm-loop state label(s) this closed card no longer claims: pm:dispatched.

    A state label claims work is in flight. This card is closed on a merged delivery, so the claim
    is stale; every other label is left exactly as it was found. Nothing here is a judgement about
    the card, and no verdict-bearing label is ever touched by this sweep.

    posted by half-state-patrol run 34005012908 · trigger schedule

    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