You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
driver-sql (mssql): substring operators on a uniqueidentifier column compare SQL Server's upper-case text form while reads answer lower-case, so $contains / $startsWith with a lower-case GUID fragment misses #31395
epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ out-of-scope finding of the #31327 card thread, escalated by the CONTRACT_REVIEW_TIER record on PR #31381: "noted, not filed" is not a carrier.
Ruling-ref: none
Dedupe (open AND closed): "uniqueidentifier contains", "GUID substring case" → none.
Blocked-by: #31327
Reader: the epic PM dispatches it after #31327 lands.
type: Bug
For a column whose declared or introspected type is uniqueidentifier, the substring arms compare LOWER(CONVERT(nvarchar(36), col)), or refuse substring operators on it with a typed refusal. Pick one and state it in the docs.
Live pins in the mssql cell, covering both cases of the fragment.
Claim: PM loop round 1 · #31395 · 2026-11-08T16:10Z · fix (p3 bug; unblocked by the seat 6723757275)
Session: session_01ADHkmPF1skfu2kx89BEXZi
Account: os-steve
Branch: claude/project-thread-2ilf2k (the card thread's harness-pinned branch; platform exception to claude/issue-<n>-<slug>)
Worktree: objectstack-issue-31395
Domain: domain:server
Seat: domain:server#1
File surface: packages/drivers/driver-sql/src/ (the substring-operator emitters for the mssql dialect: sql-driver-filter-helpers.ts and/or sql-driver-predicate-emitters.ts, sql-driver-mssql-*), their tests, the SQL Server dialect docs page, a changeset (stop on breach; explain in the report)
Container & model: S/M, mode:thread, model: default tier, effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: driver-sql (objectstack) compiles the substring arms against SQL Server's upper-case uniqueidentifier text while #31327 made reads answer lower-case · no platform path covers it today · reached by any filter with a lower-case GUID fragment on a federated SQL Server object; federation-only dialect landed 2026-11-08 (#31381), no measured user yet
Thread-read: 6723757275
Premise: holds (read at 645ed3d1: the [#31327] lower-case read comment is in sql-driver-class-head.ts; no LOWER(CONVERT(nvarchar(36), …)) arm and no typed refusal for substring operators on uniqueidentifier)
Serial constraints cleared: no open PR touches packages/drivers/driver-sql/src/ (open PR list read at claim time); sibling #31390 (oracle HEXTORAW, same package, different dialect file) is unclaimed; #31394 (service-analytics) is outside this surface.
Order: standard order + server-seat1/31395-delta.md (project files). If the dev picks the refusal route instead, Clause-② becomes yes (narrowing) and the dev writes it as measured.
{
"issue": 31395,
"status": "done",
"branch": "claude/project-thread-2ilf2k",
"pr": "https://github.com/objectstack-ai/objectstack/pull/31623",
"session": "session_01ADHkmSddVo92aurMz8Z5Vf",
"premise_still_valid": true,
"summary": "Route: compare LOWER(CONVERT(nvarchar(36), col)), no refusal. Which columns are uniqueidentifier is read from the remote catalog (introspectColumns / INFORMATION_SCHEMA.COLUMNS) once per object by the new SqlDriver#primeMssqlGuidColumns (FieldMappingFacet), only when the where holds a text operator, for the object and every composed ReadableIds leaf; every read door primes behind isMssql (findRows, count, aggregate, distinct, updateMany, deleteMany, analyzeQuery, findWithWindowFunctions). The compiler passes a per-field guidColumn flag; applyLike / applyLikePattern route a GUID column on mssql to two new sibling emitters (mssqlGuidTextMatch, mssqlGuidLikePattern in sql-driver-predicate-emitters.ts); textMatchPredicate and likePatternPredicate are byte-identical to main per the seat's serial note. Docs (external-datasources.mdx section 8, GUIDs bullet) state the choice; changeset @objectstack/driver-sql patch.",
"tests": "Head 2de7d355. Compiled: src/sql-driver-mssql-dialect.test.ts 34/34 (+19 new pins: every text operator on a GUID column in both fragment cases, key column, nvarchar control, equality control, five-dialect no-flag control, catalog gating, ReadableIds leaf, failed catalog read). Live SQL Server 2022 cell stood up in the container (docker mcr.microsoft.com/mssql/server:2022-latest): src/sql-driver-mssql-federation-conformance.test.ts 229/229 with OS_EXPECT_MSSQL_FEDERATION=1 (+17 cases, find AND count). Base vs head measured live (table in PR body): lower-case fragment 0 -> 1 row on $contains/$startsWith/$endsWith/$like; upper-case 1 -> 0; $icontains/$ilike 1 -> 1; nvarchar column identical. driver-sql full with the live cell: 246 files passed, 12 skipped; 4361 tests passed. service-datasource 74/74 (1291), objectql 570/570 (11571). Ablations via ablation-replace.mjs, each restored blob == HEAD and git diff HEAD empty: A1 applyLike routing off -> 13 red; A2 catalog gate off -> 1 red; A3 flag on every column -> nvarchar control red; A4 likePattern routing on every dialect -> five-dialect control red; A5 catalog type filter off -> nvarchar control red (first attempt refused by the tool as a no-op, redone); L1 live flag never true -> 12 live red. No dist leg: the suites import driver-sql src.",
"gates": "Head 2de7d355. dispatch-gates --commands --repo objectstack-ai/objectstack derived 99; all run; --ran reconciliation: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). First pass exit 3 (prerequisite) on check-plugin-teardown-shape --self-test (fixture commit fetched), check:dual-build-cjs-loads and spec check:skill-examples (closure build) -> re-run exit 0. eslint --no-inline-config packages/drivers/driver-sql/src exit 0. driver-sql typecheck exit 0. check:driver-conformance before 66 covered / 6 DEBT / 0 exempt, after identical. PR check-runs: in_progress at report time (not awaited).",
"dogfood_battery": "2de7d355 · os-verify-lock VERDICT command-exit 0 (1675s) · Test Files 440 passed, 3 skipped (443) · Tests 3633 passed, 41 skipped (3674) · no failures. Run because the delta lists it; the title has no ! and the claim no (narrowing) arm.",
"mcp_calls": "4 — reads only: issue_read get, issue_read get_comments x2, pull_request_read get. No MCP write.",
"api_writes": "2 — relay pr_create (PR #31623, read-back 11178 bytes sent == stored), this os-dev-report comment via post-stamped.mjs. git push x2 to the branch (not REST).",
"open_questions": [
{
"question": "Answer change for an UPPER-case GUID fragment: the case-exact operators now follow the lower-case read form, so `$startsWith: '3F2504E0'` (and `$contains` / `$endsWith` / `$like`) which matched SQL Server's upper-case rendering on main now matches nothing, and `$notContains` flips with it. No accepted request is refused, so Clause-② stays `no` as claimed, but the answer set moves. Keep?",
"options": [
"A keep: case-exact against the read form, the same answer Postgres / SQLite / memory give for a GUID stored as lower-case text; $icontains / $ilike match either case",
"B fold both sides for a GUID column only: any case matches, but $contains on a GUID then folds while the #6518 contract says it is case-exact"
],
"recommendation": "A, because it is what a read shows the caller and what every other dialect answers; B makes the case-exact operators fold on one column type of one dialect"
},
{
"question": "A failed catalog read is silent (records nothing, asks again next read), as primeMssqlPagingKey is. Warn once instead?",
"options": [
"A keep silent: the data read on the same connection fails loudly anyway, and the precedent is the paging key",
"B warn once per object naming that GUID columns are matched as SQL Server's upper-case text until the catalog answers"
],
"recommendation": "A, matching the #31327 precedent; B is a small follow-up if the seat wants it"
},
{
"question": "Scope widening inside the same defect class: $like / $ilike (pattern operators, not in the card's substring list) also read the GUID column as text and are fixed the same way, through one flag. Accept in this PR?",
"options": [
"A accept: same column-reading defect, same emitter, same gates; leaving it would keep $like and $contains disagreeing on one column",
"B split $like / $ilike to their own card"
],
"recommendation": "A"
}
],
"security_findings": "none",
"out_of_scope_findings": [],
"files_changed": [
"packages/drivers/driver-sql/src/sql-driver-aggregation.ts",
"packages/drivers/driver-sql/src/sql-driver-bulk-operations.ts",
"packages/drivers/driver-sql/src/sql-driver-crud.ts",
"packages/drivers/driver-sql/src/sql-driver-distinct.ts",
"packages/drivers/driver-sql/src/sql-driver-field-mapping.ts",
"packages/drivers/driver-sql/src/sql-driver-filter-helpers.ts",
"packages/drivers/driver-sql/src/sql-driver-mssql-dialect.test.ts",
"packages/drivers/driver-sql/src/sql-driver-mssql-federation-conformance.test.ts",
"packages/drivers/driver-sql/src/sql-driver-predicate-emitters.ts",
"packages/drivers/driver-sql/src/sql-driver-query-plan.ts",
"packages/drivers/driver-sql/src/sql-driver-window-functions.ts",
"packages/drivers/driver-sql/src/sql-driver.ts",
"content/docs/data-modeling/external-datasources.mdx",
".changeset/31395-mssql-guid-text-match.md"
],
"line_budget": "479 changed lines (+447 / -32) across 14 files; source 210-ish lines incl. docblocks, tests the rest; under the 3000 human-merge threshold",
"deviations": [
"PR opened with no assignee: pr_create read-back lists none; the standard order forbids MCP writes, so the harness's MCP assignee/reviewer step was not taken",
"The live SQL Server cell was stood up locally with docker; CI provisions none, so CI runs the new live cases as the file's named skip",
"Ablation A5 first attempt was refused by ablation-replace (replacement text already present, nothing ran); redone with a distinct replacement",
"Three derived gates answered exit 3 on the first pass (prerequisites) and were re-run green before reconciliation",
"test:repo (vitest repo project) not run locally; CI's Test Core runs it",
"Per the seat's serial note the GUID construct was moved out of textMatchPredicate / likePatternPredicate into sibling emitters before the PR (commit 2de7d355)"
]
}
Activity
objectstack-fleet commentedon Nov 8, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p3 ·
domain:server· blocked · 2026-11-08T06:53Z⛔ Not a claim.
lowerCaseGuids, driver-sql: SQL Server as a federation dialect — an external datasource over Microsoft SQL Server reads (and, double opt-in, writes) live, with type-compat and the predicate emitters modelling mssql #31327), but a substring filter compares the stored uppercase text, so a lowercase fragment matches nothing.area:data.pm:blocked: the body carriesBlocked-by: #31327.findingremoved.Generated by Claude Code
objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi
Unblocked · re-verified · 2026-11-08T16:04Z (idle chore ①,
execution-duties.md)Blocked-by:driver-sql: SQL Server as a federation dialect — an external datasource over Microsoft SQL Server reads (and, double opt-in, writes) live, with type-compat and the predicate emitters modelling mssql #31327 (2026-11-08T15:18Z) — read live now: closedcompleted. Epic [Decision] Ontology over external systems: virtual objects on ADR-0015 federation — two driver tiers (plugin code · declarative OpenAPI), pushdown/identity/catalog/cache rules, Studio and AI drafts (2026 ontology research + deep design) #27261, whose PM was this card's reader, is closed, so the card returns to the lane queue.origin/main645ed3d1:sql-driver-class-head.tsturnsuniqueidentifierreads lower-case (driver-sql: SQL Server as a federation dialect — an external datasource over Microsoft SQL Server reads (and, double opt-in, writes) live, with type-compat and the predicate emitters modelling mssql #31327); the substring operators still compile against the column's own text, with noLOWER(CONVERT(nvarchar(36), …))arm and no typed refusal.pm:blocked→pm:queue. ⛔ Not a claim.objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi
Claim: PM loop round 1 · #31395 · 2026-11-08T16:10Z · fix (p3 bug; unblocked by the seat 6723757275)
Session:
session_01ADHkmPF1skfu2kx89BEXZiAccount:
os-steveBranch:
claude/project-thread-2ilf2k(the card thread's harness-pinned branch; platform exception toclaude/issue-<n>-<slug>)Worktree:
objectstack-issue-31395Domain:
domain:serverSeat:
domain:server#1File surface:
packages/drivers/driver-sql/src/(the substring-operator emitters for the mssql dialect:sql-driver-filter-helpers.tsand/orsql-driver-predicate-emitters.ts,sql-driver-mssql-*), their tests, the SQL Server dialect docs page, a changeset (stop on breach; explain in the report)Container & model:
S/M,mode:thread,model: default tier,effort: highClause-②: no
Metadata-protocol: none
Responsibility: driver-sql (objectstack) compiles the substring arms against SQL Server's upper-case
uniqueidentifiertext while #31327 made reads answer lower-case · no platform path covers it today · reached by any filter with a lower-case GUID fragment on a federated SQL Server object; federation-only dialect landed 2026-11-08 (#31381), no measured user yetThread-read: 6723757275
Premise: holds (read at
645ed3d1: the[#31327]lower-case read comment is insql-driver-class-head.ts; noLOWER(CONVERT(nvarchar(36), …))arm and no typed refusal for substring operators onuniqueidentifier)Serial constraints cleared: no open PR touches
packages/drivers/driver-sql/src/(open PR list read at claim time); sibling #31390 (oracleHEXTORAW, same package, different dialect file) is unclaimed; #31394 (service-analytics) is outside this surface.Order: standard order +
server-seat1/31395-delta.md(project files). If the dev picks the refusal route instead,Clause-②becomesyes (narrowing)and the dev writes it as measured.objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmSddVo92aurMz8Z5Vf
os-dev-report
{ "issue": 31395, "status": "done", "branch": "claude/project-thread-2ilf2k", "pr": "https://github.com/objectstack-ai/objectstack/pull/31623", "session": "session_01ADHkmSddVo92aurMz8Z5Vf", "premise_still_valid": true, "summary": "Route: compare LOWER(CONVERT(nvarchar(36), col)), no refusal. Which columns are uniqueidentifier is read from the remote catalog (introspectColumns / INFORMATION_SCHEMA.COLUMNS) once per object by the new SqlDriver#primeMssqlGuidColumns (FieldMappingFacet), only when the where holds a text operator, for the object and every composed ReadableIds leaf; every read door primes behind isMssql (findRows, count, aggregate, distinct, updateMany, deleteMany, analyzeQuery, findWithWindowFunctions). The compiler passes a per-field guidColumn flag; applyLike / applyLikePattern route a GUID column on mssql to two new sibling emitters (mssqlGuidTextMatch, mssqlGuidLikePattern in sql-driver-predicate-emitters.ts); textMatchPredicate and likePatternPredicate are byte-identical to main per the seat's serial note. Docs (external-datasources.mdx section 8, GUIDs bullet) state the choice; changeset @objectstack/driver-sql patch.", "tests": "Head 2de7d355. Compiled: src/sql-driver-mssql-dialect.test.ts 34/34 (+19 new pins: every text operator on a GUID column in both fragment cases, key column, nvarchar control, equality control, five-dialect no-flag control, catalog gating, ReadableIds leaf, failed catalog read). Live SQL Server 2022 cell stood up in the container (docker mcr.microsoft.com/mssql/server:2022-latest): src/sql-driver-mssql-federation-conformance.test.ts 229/229 with OS_EXPECT_MSSQL_FEDERATION=1 (+17 cases, find AND count). Base vs head measured live (table in PR body): lower-case fragment 0 -> 1 row on $contains/$startsWith/$endsWith/$like; upper-case 1 -> 0; $icontains/$ilike 1 -> 1; nvarchar column identical. driver-sql full with the live cell: 246 files passed, 12 skipped; 4361 tests passed. service-datasource 74/74 (1291), objectql 570/570 (11571). Ablations via ablation-replace.mjs, each restored blob == HEAD and git diff HEAD empty: A1 applyLike routing off -> 13 red; A2 catalog gate off -> 1 red; A3 flag on every column -> nvarchar control red; A4 likePattern routing on every dialect -> five-dialect control red; A5 catalog type filter off -> nvarchar control red (first attempt refused by the tool as a no-op, redone); L1 live flag never true -> 12 live red. No dist leg: the suites import driver-sql src.", "gates": "Head 2de7d355. dispatch-gates --commands --repo objectstack-ai/objectstack derived 99; all run; --ran reconciliation: 99 derived, 99 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). First pass exit 3 (prerequisite) on check-plugin-teardown-shape --self-test (fixture commit fetched), check:dual-build-cjs-loads and spec check:skill-examples (closure build) -> re-run exit 0. eslint --no-inline-config packages/drivers/driver-sql/src exit 0. driver-sql typecheck exit 0. check:driver-conformance before 66 covered / 6 DEBT / 0 exempt, after identical. PR check-runs: in_progress at report time (not awaited).", "dogfood_battery": "2de7d355 · os-verify-lock VERDICT command-exit 0 (1675s) · Test Files 440 passed, 3 skipped (443) · Tests 3633 passed, 41 skipped (3674) · no failures. Run because the delta lists it; the title has no ! and the claim no (narrowing) arm.", "mcp_calls": "4 — reads only: issue_read get, issue_read get_comments x2, pull_request_read get. No MCP write.", "api_writes": "2 — relay pr_create (PR #31623, read-back 11178 bytes sent == stored), this os-dev-report comment via post-stamped.mjs. git push x2 to the branch (not REST).", "open_questions": [ { "question": "Answer change for an UPPER-case GUID fragment: the case-exact operators now follow the lower-case read form, so `$startsWith: '3F2504E0'` (and `$contains` / `$endsWith` / `$like`) which matched SQL Server's upper-case rendering on main now matches nothing, and `$notContains` flips with it. No accepted request is refused, so Clause-② stays `no` as claimed, but the answer set moves. Keep?", "options": [ "A keep: case-exact against the read form, the same answer Postgres / SQLite / memory give for a GUID stored as lower-case text; $icontains / $ilike match either case", "B fold both sides for a GUID column only: any case matches, but $contains on a GUID then folds while the #6518 contract says it is case-exact" ], "recommendation": "A, because it is what a read shows the caller and what every other dialect answers; B makes the case-exact operators fold on one column type of one dialect" }, { "question": "A failed catalog read is silent (records nothing, asks again next read), as primeMssqlPagingKey is. Warn once instead?", "options": [ "A keep silent: the data read on the same connection fails loudly anyway, and the precedent is the paging key", "B warn once per object naming that GUID columns are matched as SQL Server's upper-case text until the catalog answers" ], "recommendation": "A, matching the #31327 precedent; B is a small follow-up if the seat wants it" }, { "question": "Scope widening inside the same defect class: $like / $ilike (pattern operators, not in the card's substring list) also read the GUID column as text and are fixed the same way, through one flag. Accept in this PR?", "options": [ "A accept: same column-reading defect, same emitter, same gates; leaving it would keep $like and $contains disagreeing on one column", "B split $like / $ilike to their own card" ], "recommendation": "A" } ], "security_findings": "none", "out_of_scope_findings": [], "files_changed": [ "packages/drivers/driver-sql/src/sql-driver-aggregation.ts", "packages/drivers/driver-sql/src/sql-driver-bulk-operations.ts", "packages/drivers/driver-sql/src/sql-driver-crud.ts", "packages/drivers/driver-sql/src/sql-driver-distinct.ts", "packages/drivers/driver-sql/src/sql-driver-field-mapping.ts", "packages/drivers/driver-sql/src/sql-driver-filter-helpers.ts", "packages/drivers/driver-sql/src/sql-driver-mssql-dialect.test.ts", "packages/drivers/driver-sql/src/sql-driver-mssql-federation-conformance.test.ts", "packages/drivers/driver-sql/src/sql-driver-predicate-emitters.ts", "packages/drivers/driver-sql/src/sql-driver-query-plan.ts", "packages/drivers/driver-sql/src/sql-driver-window-functions.ts", "packages/drivers/driver-sql/src/sql-driver.ts", "content/docs/data-modeling/external-datasources.mdx", ".changeset/31395-mssql-guid-text-match.md" ], "line_budget": "479 changed lines (+447 / -32) across 14 files; source 210-ish lines incl. docblocks, tests the rest; under the 3000 human-merge threshold", "deviations": [ "PR opened with no assignee: pr_create read-back lists none; the standard order forbids MCP writes, so the harness's MCP assignee/reviewer step was not taken", "The live SQL Server cell was stood up locally with docker; CI provisions none, so CI runs the new live cases as the file's named skip", "Ablation A5 first attempt was refused by ablation-replace (replacement text already present, nothing ran); redone with a distinct replacement", "Three derived gates answered exit 3 on the first pass (prerequisites) and were re-run green before reconciliation", "test:repo (vitest repo project) not run locally; CI's Test Core runs it", "Per the seat's serial note the GUID construct was moved out of textMatchPredicate / likePatternPredicate into sibling emitters before the PR (commit 2de7d355)" ] }Stamped 2026-11-08T17:21Z.
objectstack-fleet commentedon Nov 8, 2026
domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi
ACCEPT → PR #31623 comment 6724635636 · head
2de7d355· 2026-11-08T17:24Z · open questions 1–3 ruled A there · landing waits for every check green.