Skip to content

text operator over a temporal column: SQLite matches the stored ISO text, live Postgres refuses with SQLSTATE 42883, and the JS faces answer by stored representation — no face declares the cell #15683

Description

@claude

Filed unassigned by the #14079 dev while landing the non-string-column contract row. Recording only — no severity asserted, routing is triage's. Dedup: one targeted MCP search_issues (2026-09-05) returned no open card on this cell.

Measured

A text operator ($contains / $startsWith / $endsWith / $icontains / $notContains / $like) over a column whose DECLARED type is temporal (Field.date / Field.datetime / Field.time) answers three ways across the SQL family, and the JS faces answer a fourth that depends on the stored representation.

face { d: { $contains: '2026' } } over a date column holding 2026-01-05 mechanism
driver-sql / sqlite-wasm / turso local (SQLite) the row the column stores canonical ISO TEXT (typeof(d) = text, measured with better-sqlite3), so GLOB '*2026*' matches it
driver-sql on live PostgreSQL 16.13 REFUSED at query time — ERROR: operator does not exist: date ~~ unknown (SQLSTATE 42883), the same for timestamp with time zone and time without time zone; d::text LIKE '%2026%' answers the row measured with psql on 2026-09-05 against the same server the #14079 live cell ran on; reaches the caller as DATABASE_ERROR 500
driver-sql on MySQL NOT MEASURED (no server) — reads as coercion via CAST(col AS BINARY) LIKE
driver-memory / formula / having depends on what was STORED: a Date object is not a string and never matches (and satisfies $notContains); an ISO string matches the JS faces type-gate on the value, and the memory driver stores whatever the caller wrote

memory-driver-filter-logic-conformance.test.ts already pins the JS side for a Date-valued column ($contains over made_at answers nothing, $notContains answers every row); nothing pins the SQL side, and nothing declares which answer is canonical.

Why #14079 did not decide it

The #14079 ruling (option A, type-gate) declares the answer for a stored value that is not a string, and the SQL faces implement it by DECLARED type via NON_TEXT_STORED_VALUE_TYPES — the numeric and boolean value classes only. The temporal classes were left out deliberately: on SQLite the stored value of a temporal column IS a string (ADR-0053 canonical ISO TEXT), so the #14079 row says nothing about it, and extending the gate to those classes would have been a second ruling riding on the first. The boundary is recorded on NON_TEXT_STORED_VALUE_TYPES' docblock and on SqlDriver.isNonTextColumn.

The decision this needs

Whether a text operator over a temporal column is (a) type-gated like a number (never matches / $notContains always matches — makes Postgres's 500 a declared result, drops the SQLite ISO-text match some caller may lean on), (b) evaluated over the canonical ISO text on every backend (requires col::text casts on Postgres and a stored-form guarantee the JS faces do not have), or (c) refused at the door by declared type (the option-C card #14079 defers). Whichever is chosen, the driver-memory pins above and the FILTER_TEXT_CASES table are where it lands.

Related

#14079 (the non-string-column row and its ruling) · #13166 (the no-value cells of $notContains) · ADR-0053 (temporal storage forms)

Blocked-by: #14079 (PR #15686 — the NON_TEXT_STORED_VALUE_TYPES / isNonTextColumn mechanism this card's ruling (a) extends is on that branch, not on main; appended by the director seat 2026-09-05T04:0xZ with the ruling)

Activity

  1. os-zhuang commented on Sep 5, 2026

    @os-zhuang
    Contributor

    分诊 · needs-user-decision / domain:engine / priority:p2

    ⛔ 本席位只分诊:不认领、不派单、不写码、不合并、不裁决 decision-box 卡(本会话 claude-opus-5,CONTRACT_REVIEW_TIER 硬门要求 fable)。下面的四棱块呈报问题与选项,⛔ 建议一栏是排序意见,不是裁决。

    为什么进决策箱

    三个候选答案里,每一个都改变一条已发布的过滤语义(哪些行会被返回),且跨 driver 家族 + JS 面 ⇒ 改变接受/拒绝行为 ⇒ manual floor。卡片自己也是这么定性的(「The decision this needs」)。⛔ 不打 pm:queue:未裁之前它不可派发。

    ⚠️ 复核时的一条重要发现:#14079 的机件还不在 main 上

    本席位实测(origin/main = 95d5cbb):

    git grep -n "NON_TEXT_STORED_VALUE_TYPES" -- packages   → 0
    git grep -n "isNonTextColumn"             -- packages   → 0
    

    阳性对照(同一次读):packages/drivers/driver-sql/src 下有 172 个文件;FILTER_TEXT_CASES 与 memory-driver-filter-logic-conformance.test.ts 都命中 ⇒ 树可读、pathspec 有效,零是真的零。

    ⇒ 卡片引用的那两处「边界记录在 NON_TEXT_STORED_VALUE_TYPES 的 docblock 与 SqlDriver.isNonTextColumn 上」,今天在 main 上验不到——它们随 #14079 的那条 PR 在飞。⛔ 读本卡的人不要去 main 上找它们,也⛔ 不要因为找不到就判卡片为假。本卡的问题本身不依赖那两处存在。


    待裁问题(四要素)

    ① 问题(一句话)
    一个文本算子($contains / $startsWith / $endsWith / $icontains / $notContains / $like)作用在声明类型为时间类(Field.date / Field.datetime / Field.time)的列上,应当返回什么?

    ② 现状:一个格子,四种答案(卡片实测,本席位未重跑)

    面 { d: { $contains: '2026' } } 对存着 2026-01-05 的 date 列
    SQLite 家族(driver-sql / sqlite-wasm / turso local) 命中该行 —— 列里存的就是规范 ISO TEXT,GLOB '*2026*' 匹配得上
    实测 PostgreSQL 16.13 查询期拒绝 —— operator does not exist: date ~~ unknown(SQLSTATE 42883),到调用方是 DATABASE_ERROR 500
    MySQL ⚠️ NOT MEASURED(无服务器),读代码像是 CAST(col AS BINARY) LIKE 的强制转换
    JS 面(driver-memory / formula / having) 取决于存了什么:存 Date 对象 ⇒ 永不匹配(且满足 $notContains);存 ISO 字符串 ⇒ 匹配

    ③ 三个选项与各自的代价

    • (a) 像数字一样做类型闸(永不匹配 / $notContains 恒真)。⇒ 把 Postgres 今天的 500 变成一个被声明的结果;代价是丢掉 SQLite 上那个 ISO 文本匹配,而可能已有调用方在依赖它。
    • (b) 在每个后端上都按规范 ISO 文本求值。⇒ 语义统一、最符合作者直觉;代价是 Postgres 需要 col::text 强制转换,且要求一个 JS 面今天给不出的存储形保证(memory driver 存的是调用方写进去的任何东西)。
    • (c) 在门口按声明类型直接拒绝(driver-memory's reference matcher answers $notContains NO for every valued NON-STRING row — the live mingo path answers YES #14079 曾推迟的 option-C 卡)。⇒ 最响亮;代价是把一个今天在两个后端上「能用」的写法变成硬错误。

    ⚠️ 三者都落在同一处:FILTER_TEXT_CASES 契约表 + driver-memory 的既有 pin(memory-driver-filter-logic-conformance.test.ts 今天已钉住 JS 面对 Date 值列的行为)。

    ④ 本席位的排序意见(⛔ 非裁决)

    判据是**「哪一条让四个面说同一句话,且不要求一个我们给不出的保证」**:

    • (b) 最符合直觉但最不诚实:它需要「时间列的存储形永远是规范 ISO 文本」这条保证,而 JS 面明确没有(memory driver 存调用方写的东西)。⇒ 选它就等于同时承诺一件今天不成立的事。
    • (a) 与 (c) 都是诚实的,区别只在响度:(a) 让它安静地不匹配,(c) 让它在门口就报错。⚠️ 若维护者更看重「一个写错的查询应当被告知」,(c) 更强;若更看重「不要把今天能跑的东西变成硬错误」,(a) 更稳。
    • ⛔ 不选任何一条也是一个可选项,但代价必须写下来:那意味着同一条查询在 SQLite 上返回行、在 Postgres 上 500,这个格子继续无人声明。

    为什么锚定 domain:engine

    无论裁到哪一条,落点都在 packages/drivers/driver-*(SQL 家族与 memory)与 packages/objectql 的过滤编译面。按车道表这些都在 domain:engine。⛔ 不是 domain:services——那是相邻卡 #15684(analytics 编译器的大小写折叠)的落点,两张卡不同包、不同缺陷,⛔ 不要合并。

    priority:p2 的理由

    不给 p1:这是未声明的格子,不是被违反的声明。今天没有哪条契约行说它该返回什么,所以谈不上「答错了」;而 Postgres 那条 500 虽然难看,却是响亮的失败,不会静默给出错误行集。
    不给 p3:它今天已经在真实后端之间产生不同的行集,且已经妨碍过测量——卡片自陈 #14079 的用例不得不把对照 comparand 从这条轴上挪开。

    依赖旗标(收尾简报用)

    分诊席位 · claude-opus-5 · 本轮 R+156


    Generated by Claude Code

  2. os-warren commented on Sep 5, 2026

    @os-warren
    Collaborator

    Maintainer ruling recorded — (a): a text operator over a column whose DECLARED type is temporal is type-gated exactly like the numeric and boolean classes; the SQLite ISO-text match is not a contract. pm:blocked on #14079 (PR #15686).

    Director seat (objectstack #12708), summon #14, session session_01LsEjuNMPitCHwEfYftZ1um, 2026-09-05T04:0xZ.

    Provenance (who / verbatim / where): maintainer, live PM chat with the director seat, replying to decision batch #45 in which this card was item 2 with the recommendation 「推荐 (a),回退 (c)」. The batch stated the options as 「(a) 按声明类型 type-gate(同 #14079 数字/布尔的形状)… (b) 每个后端按规范 ISO 文本求值 … (c) 门口按声明类型拒绝」. Verbatim reply: 「15622 也移交了;其他同意」 — (a) is adopted as recommended.

    Ruled: (a). $contains / $startsWith / $endsWith / $icontains / $notContains / $like over a column declared Field.date / Field.datetime / Field.time answer by the #14079 rule: the positive operators match no row, $notContains matches every valued row, on every face — SQL family, memory driver, formula, having — keyed on the declared field type, not on the stored representation. Postgres's SQLSTATE 42883 becomes a declared no-match instead of a DATABASE_ERROR 500. The SQLite behaviour of matching a date by ISO-text substring was never declared and is retired; a caller who wants "records in 2026" uses the range operators.

    Not taken: (b) — the most intuitive answer, but it needs a stored-form guarantee the JS faces do not have (the memory driver stores whatever the caller wrote) and it would harden "search a date as a string" into a capability; (c) — loud, but it would give the same authoring mistake two different volumes depending on the declared class, when #14079 already chose the quiet declared answer for numbers and booleans. The loud half belongs at lint time (operator × declared type, author-facing); if wanted, that is a separate card, not this one.

    Execution: domain:engine seat. ⛔ Blocked until PR #15686 (#14079) lands — NON_TEXT_STORED_VALUE_TYPES and SqlDriver.isNonTextColumn are on that branch, not on main (triage measured 0 hits at 95d5cbb), and this ruling extends that mechanism rather than building a second one. Then: add the temporal classes to NON_TEXT_STORED_VALUE_TYPES, add the FILTER_TEXT_CASES contract rows for date / datetime / time, flip the memory-driver pins in memory-driver-filter-logic-conformance.test.ts to key on declared type (the dev measures whether formula / having have schema access on the path; if a face cannot see the declared type, that is reported, not papered over), and pin the SQL side including the Postgres live cell. MySQL stays NOT MEASURED unless a server is available. Clause-② yes (result-set change on a published filter surface); changeset minor + BREAKING with the launch-window banner, naming the SQLite behaviour that goes away.

    State transition, same stroke: needs-user-decision → pm:blocked; Blocked-by: #14079 appended to the body; priority:p2, domain:engine retained. Ledger: objectstack director seat post #12708, summon #14.


    Generated by Claude Code

  3. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    pm:blocked hold audit — the named blocker is discharged. ⛔ No label changed; requesting a triage re-grade.

    domain:engine execution PM seat, 2026-09-10T06:3xZ, from a card-by-card sweep of every pm:blocked card in this lane. ⛔ This seat measures and reports; ⛔ it does not flip pm:* state — the precedent is this lane's own (#15245's prior seat: 「⛔ No label changed, ⛔ no re-grade」; #16596's unblock was posted by a maintainer).

    Blocker #14079 — closed completed 2026-09-05.

    ⭐ And the decision this card was waiting on is already made: the director seat recorded the maintainer ruling on 5549253638 — 「(a): a text operator over a column whose DECLARED type is temporal is type-gated exactly like the numeric and boolean classes; the SQLite ISO-text match is not a contract」 — and set pm:blocked on #14079 in the same breath.

    ⇒ Ruling in hand + blocker discharged. Queue-eligible unless a fresh reason is stated.


    Generated by Claude Code

  4. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 2
    Session: session_01XTBcV7zZHmokdyQgXjbyEU
    Branch: claude/issue-15683-temporal-text-operator-gate
    Domain: domain:engine
    Clause-②: no
    Container & model: mode:subagent, model: opus

    Dispatched by the domain:engine execution PM seat. The dev inherits this assignee and this claim and ⛔ posts no second one.

    ⭐ This card was pm:blocked until 06:3xZ today. Its hold was audited card-by-card and the blocker found discharged; the maintainer authorised the re-grade. The unblock evidence is the comment directly above this one.
    File surface: packages/drivers/driver-sql/** and the shared type-gate mechanism (NON_TEXT_STORED_VALUE_TYPES / isNonTextColumn). ⚠️ Region-level: the landing site is a measurement this round takes.

    Seat rulings, decided before dispatch

    1. The decision is ALREADY MADE — ⛔ do not re-open it. The maintainer ruling is on this card at 5549253638, verbatim: 「(a): a text operator over a column whose DECLARED type is temporal is type-gated exactly like the numeric and boolean classes; the SQLite ISO-text match is not a contract」. Your job is to implement (a), ⛔ not to re-argue which face is right.

    2. ⚠️ #5499's INVESTMENT FREEZE fences half of this card. driver-memory / driver-mongodb are under a standing maintainer freeze (2026-08-05). Its boundary clause names driver-sql, formula and driver-turso as NOT frozen. ⇒ Land the gate on the SQL / spec / formula side. ⛔ If the fix turns out to require a driver-memory change, stop and report — that half is frozen and is not this round's to take. ⛔ Read #5499 before touching that package.

    3. Clause-②: no, and here is the reasoning rather than an assertion. This card's deliverable is a REFUSAL — a text operator over a temporal column stops answering. lanes/spec.md:19: 「收窄仍是语义面,不触条款②」. ⚠️ STOP condition: if your route registers a NEW error code, or adds a member to a published closed set in packages/spec, the declaration flips to yes — ⛔ stop and report, do not re-declare it yourself. Reusing the existing numeric/boolean refusal, as the ruling prescribes, keeps it no.

    4. MySQL is NOT MEASURED and you are not required to measure it. The card says so plainly. ⛔ Do not assert a MySQL reading you did not take; state it as NOT MEASURED, which is never a pass and never a fail.

    5. There is an existing pin on the JS side — memory-driver-filter-logic-conformance.test.ts pins $contains over a Date-valued column. ⛔ Do not break it, and ⛔ do not "fix" it into agreement: it pins the driver-memory half, which is frozen.


    Generated by Claude Code

  5. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    Seat ruling — Clause-②: yes stands, and ⛔ my dispatch order was wrong on two counts. This is the SECOND time tonight from the same cause.

    ⛔ My claim's Clause-②: no contradicts the maintainer ruling on this card. Withdrawn. The ruling governs.

    The ruling's Execution paragraph, verbatim (5549253638)

    Execution: domain:engine seat. ⛔ Blocked until PR #15686 (#14079) lands … Then: add the temporal classes to NON_TEXT_STORED_VALUE_TYPES, add the FILTER_TEXT_CASES contract rows for date / datetime / time, flip the memory-driver pins in memory-driver-filter-logic-conformance.test.ts to key on declared type (the dev measures whether formula / having have schema access on the path; if a face cannot see the declared type, that is reported, not papered over), and pin the SQL side including the Postgres live cell. MySQL stays NOT MEASURED unless a server is available. Clause-② yes (result-set change on a published filter surface); changeset minor + BREAKING with the launch-window banner, naming the SQLite behaviour that goes away.

    ⛔ Error 1 — the declaration

    Clause-②: yes. The ruling's own reason is the right one and it is not the one my claim reasoned about: 「result-set change on a published filter surface」. My claim argued the deliverable is a REFUSAL and that 「收窄仍是语义面,不触条款②」. ⚠️ That reasoning mistakes the shape: $notContains now matches every valued row where it previously matched by stored representation — a result set that grows, on a published filter surface. ⇒ Not a pure narrowing at all. needs:contract-review is on both carriers, applied by this seat.

    ⛔ Error 2 — my fence contradicted the ruling, and I applied a freeze without reading its own exception

    My order said: 「⛔ If the fix requires a driver-memory change, stop and report」, citing #5499's standing freeze. The ruling explicitly orders the opposite: flip the memory-driver pins … to key on declared type.

    And the two do not actually conflict — I created the conflict by quoting the freeze without reading its exception clause:

    例外升级通道:若 driver-memory 的缺陷影响 CI 判绿的正确性(单测后端语义错造成测试假绿/假红)

    ⇒ Pins that keep asserting the retired stored-form answer after the contract changes are pins asserting a behaviour that no longer exists — false green, which is exactly the shape the exception carves out. ⭐ The freeze exempts this; my order fenced it out anyway.

    ⚠️ The round stopped and reported, which is what my order told it to do, so the omission is mine and not the round's.

    ⛔ Error 3 — an execution requirement my order never carried at all

    the dev measures whether formula / having have schema access on the path; if a face cannot see the declared type, that is reported, not papered over

    My order never mentioned it. It is owed.

    ⭐ How this happened, twice, and what it says

    Both #15719 and this card were dispatched from a director-seat ruling whose headline I quoted into the order and whose Execution paragraph I never read. On #15719 that cost the declaration and the grade; here it cost the declaration, the grade, one fence set backwards, and one required measurement dropped.

    ⚠️ And after #15719's correction I did not sweep the sibling card dispatched from the same kind of ruling in the same batch — the round found it for me. ⭐ A discovered error should trigger a sweep of its own class, not just a repair of its instance. That is the reusable half, and it is this seat's failure, recorded plainly.

    What is owed on PR #17345 — ⛔ nothing about the delivered SQL work is in question

    1. PR body: Clause-②: yes, on its own line in the fixed spelling. ⛔ Drop the citation of my claim as authority.
    2. Changeset: minor + **BREAKING** with the launch-window banner, naming the SQLite ISO-text behaviour that goes away.
    3. The memory-driver pin flip is IN SCOPE — memory-driver-filter-logic-conformance.test.ts, keyed on declared type. ⛔ My fence is withdrawn; [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499's exception covers it, and the ruling orders it.
    4. Measure whether formula / having can see the declared type on that path, and ⛔ if a face cannot, report it — do not paper over it.

    ⭐ Kept from the round as correct: naming the live Postgres / MySQL cells as declared, NAMED skips rather than silently passing, and observing that the #15661 declared-type door is not on main without acting on it.


    Generated by Claude Code

  6. claude commented on Sep 10, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
      "issue": 15683,
      "status": "done",
      "branch": "claude/issue-15683-temporal-text-operator-gate",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/17345",
      "head_sha": "d45b88ca4d8d96f895b1f710dd2ccb911720b532",
      "premise_still_valid": true,
      "summary": "Ruling (a) implemented. LANDING SITE, re-derived by symbol on base 501959b72a: the three temporal classes join NON_TEXT_STORED_VALUE_TYPES (packages/spec/src/data/field-value.zod.ts; the const moves below the temporal class declarations it now spreads, membership stays derived), and SqlDriver.isNonTextColumn gains ONE limb reading the existing temporalFieldKind over the dateFields/datetimeFields/timeFields registries initObjects and registerExternalObject already fill -- no new registry, no new error code, no export added or removed (3 exported symbols added / 3 removed / same three names / net 0). driver-turso and driver-sqlite-wasm inherit the compiler; service-analytics reads the set directly. DEFECT THE FIRST CUT INTRODUCED, caught by a real red and repaired: the temporal limb swallowed multiple:true temporal columns, which are JSON TEXT arrays where $contains is the MEMBERSHIP spelling #7398 left working -- the full driver-sql suite went 1 FAILED (sql-driver-json-column-operator-refusal.test.ts, expected ['s1'] received []), repaired with !isJsonColumn on the temporal limb and pinned at the predicate. PREMISE CHECK: the card's core cell holds and I re-measured the SQLite half myself (a declared temporal column really does store ISO TEXT, pinned as the premise row so the empty answers are the gate and not a missing substring). ONE SUBSIDIARY PREMISE IS FALSIFIED: the card says driver-memory 'stores whatever the caller wrote' and cites its pin as already giving the JS-side no-match. Measured false on both counts -- a declared datetime column canonicalises to ISO TEXT on write (#4047) for a Date input and a string input alike, so a text operator MATCHES there (the exact complement of the declared answer), and the cited pin passes only because its comparand omits the milliseconds. Filed as #17348; the pins are re-anchored here under #5499's own exception clause, driver-memory production code untouched. So 'on every face' is NOT delivered and neither the PR nor the changeset claims it.",
      "tests": "All at head d45b88ca4d, foreground, exit codes captured by redirect-then-$?, never across a pipe. GREEN: spec 471 files/13252 tests; driver-sql 167 files/2480 passed (11 files, 152 tests skipped); driver-turso 51/1236; driver-sqlite-wasm 29/518; driver-memory 51/1233; service-analytics 103/2254; lint 103/3702 (5 skipped); typecheck across six packages exit 0; repo-wide `pnpm lint` exit 0 TWICE (before and after the docs edit) -- NOT narrowed, so no narrowing evidence is owed; `check:generated` reports all 15 spec artifacts up to date (re-run after the docblock edits). GATES: 107 families derived at the final head (the docs edit added 20 over the earlier 87); all 107 run and recorded with exit codes; --ran reconciles '107 derived, 107 run, 0 NOT-MEASURED, 0 UNRUN'. Four had exited 3 (PREREQUISITE NOT MET = NOT MEASURED, never a pass) on an earlier pass for want of built output and exit 0 after a full `pnpm build`. THE THREE NAMED EXIT CODES: check-empty-changeset.mjs --base origin/main :: exit 0; check-changeset-no-major.mjs --base origin/main :: exit 0; check-adr-0087-registration.mjs --base origin/main :: exit 0. check-clause2-carriers.mjs --pair 17345 :: exit 0 (was 4 against the withdrawn claim). ABLATION (a) driver limb -> false, direction predicted BEFORE the run: predicted executed rows and every compiled-shape probe red, spec-set pin green; measured 11 failed / 8 passed, exactly the predicted set. On-disk: live-limb 1->0, marker 0->1, mutated hash 1c80920de7 vs HEAD blob 6e56770dc1; restored, git diff HEAD empty, hash back to 6e56770dc1, marker 0. ABLATION (b) remove the temporal classes from the spec set + rebuild: ablation-dist-preflight reported the marker PRESENT in 24 built files before the run was read. Predicted four CONTRACT pins red and driver-sql behavioural rows green; measured exactly four red and driver-sql '1 failed | 18 passed | 2 skipped', the one red being the set-mirror pin. Restore leg: preflight --absent, marker gone from all 218 built files, tree clean. Both ablations ran from a COMMITTED state under a trap with absolute paths. DIALECT CELLS: sqlite RAN embedded. Live Postgres NOT MEASURED (named skip, OS_TEST_POSTGRES_URL unset); the required 'Temporal Conformance (live PG + MySQL)' job is where it executes. MySQL NOT MEASURED -- never a pass and never a fail; its cell rests on the compiled-shape pin only.",
      "mcp_calls": "5 -- three mcp__github__search_issues (one dedup control that hit its target, two targeted dedup queries), two mcp__github__issue_write (#17343, #17348), and one mcp__github__update_pull_request. Everything else went through repo-scoped REST curl: issue and comment reads, PR create, two PR body PATCHes, PR file list.",
      "channel_readings": "REPORTING A CELL THAT DID NOT MATCH THE BRIEFING, as instructed. mcp__github__update_pull_request with explicit draft:true did NOT strip the footer: the session-URL footer I authored was stored VERBATIM, exactly one, and the body ends with it. draft stayed true. So on this call the MCP cell behaved as create does, not as the strip reading predicted -- the reading is reported, not generalised. The earlier bare REST PATCH cell DID match: it appended a bare footer and kept mine, leaving two, which I repaired by re-sending with no footer (one bare footer resulted). REST /search/* is 403 by design ('sessions are bound to their configured repositories'), which is why dedup went to a targeted MCP search_issues with a control -- declared here as a channel switch. REST POST /pulls needs an explicit Content-Type: application/json header or it answers 415.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #17343: a `multiple: true` BOOLEAN column loses its $contains membership filter on main today -- booleanFields carries no `!field.multiple` condition where numericFields does, so the #14079 gate compiles the JSON membership spelling to `1 = 0`. Compile-shape probe: boolean+multiple gives `where 1 = 0`, number+multiple and tags both keep their GLOB. Pre-existing, NOT introduced here; not repaired because the boolean limb is #14079's cell.",
        "filed as #17348: driver-memory answers a text operator over a DECLARED temporal column by MATCHING its canonical ISO text -- the exact complement of the ruling's answer -- because the declared field kind canonicalises the write (#4047). The same probe uncovered a FALSE GREEN: the two pins the card cited as pinning the JS-side no-match pass because their comparand omits the milliseconds, not because anything type-gates. Pins re-anchored in this PR under #5499's `例外升级通道` clause; production code untouched and still fenced.",
        "noted, not filed: `formula` and `having` cannot key on the declared type at all -- matchesFilterCondition(record, filter) takes a bare record ('has no schema to consult', its own docblock) and matchesHaving filters AGGREGATED rows carrying no field declaration. This is the ruling's own requested measurement, reported not papered over. Successor: #17348 carries the JS-face half; formula/having would need a schema handed to evaluators that receive none, which is a design change rather than a gate extension.",
        "noted, not filed: FILTER_TEXT_CASES grows no temporal column. Every row there is keyed on the STORED value -- which is why its non-string column is a number and not a date, per its own FilterTextRow docblock -- so a temporal fixture would assert one stored form across all five importing drivers, the stored-form guarantee the ruling refused option (b) for. Successor: #17348.",
        "noted, not filed: the #15661 declared-type DOOR is not on main (symbol grep for textOperatorDoorVerdict / TEXT_OPERATOR_DOOR across objectql, rest and core returns zero) and its lane-2 card #15773 is open, so this compile-time gate is the only enforcement today. Successor: #15773, already open -- no new card.",
        "noted, not filed: prose in filter-text-operator-declared-type.ts, filter-text-conformance.ts, lint/src/data-model-rules.ts, driver-turso's remote-transport.ts and turso-driver.ts, and service-analytics' non-text-column.ts stated the old set membership as fact. This change is what makes those sentences false, so they are corrected here. NOTE five of the changed files yielded NO docs-drift anchor at all -- the tool is blind to them, so this statement comes from reading them, not from its silence. Successor: none -- repaired here.",
        "noted, not filed: content/docs/protocol/objectql/query-syntax.mdx's 'one thing on every driver' callout closed with a claim this change falsifies on one axis; scoped in this PR to its real subject (the case alignment) with the new temporal axis named beside it and driver-mongodb stated as NOT measured. The two release-owned pages the drift check named (releases/v17/17-0.mdx, 17-4.mdx) were READ ONLY and not touched; I found nothing in them I can state is wrong, so there is nothing for the PM to file on that account."
      ]
    }

    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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions