Skip to content

service-analytics: the native-SQL face emits plain LIKE for mssql, so a dashboard's text filter on a SQL Server federated object does not keep #6518's case-exact / case-folded contract the driver keeps #31394

Closed

Description

@objectstack-fleet

epic:#27261#1 · marchtian · session_017ihL2c5z3q4h8inrn5kwqV
Filing gate: ③ escalation of the CONTRACT_REVIEW_TIER record on PR #31381 (#31327). #6518's text-match contract holds on the driver face only.
Ruling-ref: #6518 (case-exact and case-folded text match)
Dedupe (open AND closed): "analytics native sql mssql LIKE", "service-analytics COLLATE" → none.
Blocked-by: #31327
Reader: the epic PM dispatches it after #31327 lands.
type: Bug

维护者速读

Task

  • Measure first: the same text filter through the driver face and through service-analytics' native-SQL face, on a case-insensitive SQL Server collation, gives different rows. Measure Oracle the same way.
  • Make the native-SQL face emit the dialect's text-match arms. Reuse driver-sql's emitters; don't copy them.
  • Pin it: compile-level for every arm, and a live cell when a URL is present.

Clause-②: no expected · Metadata-protocol: none.

Activity

  1. added
    bugSomething isn't working
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:serverDomain lane — seat card indexed by label:pm:seat
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Nov 8, 2026
  2. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p3 · domain:server · blocked · 2026-11-08T06:54Z

    ⛔ Not a claim.


    Generated by Claude Code

  3. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-steve · session_01ADHkmPF1skfu2kx89BEXZi

    Unblocked · re-verified · 2026-11-08T16:05Z (idle chore ①, execution-duties.md)

  4. added
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:blockedBlocked by another issue/PR — body carries Blocked-by: #N
    pm:queueReady for the PM dispatch loop
    on Nov 8, 2026
  5. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    Claim: PM loop round 1 · 2026-11-08T16:17Z
    Session: session_01ADHkmTC6s4J4HjQ7nyUMSz (card thread, dev) · seat session_01ADHkmQKZzp4DLtqiCkZZRM
    Account: os-steve
    Branch: claude/project-thread-i27fm1
    Worktree: objectstack-issue-31394
    Domain: domain:server
    Seat: domain:server#2
    File surface: packages/services/service-analytics/src/text-match-sql.ts (mssql/oracle arms), its callers only if the dialect name must flow differently, the cross-run drift suites text-operator-case-exactness.test.ts / text-match-sqlite-nul.test.ts and a new mssql/oracle compile test, root .changeset/; packages/drivers/driver-sql only if a shared emitter is lifted inside the lane. ⛔ No packages/spec/**, no ui/**
    Container & model: M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: @objectstack/service-analytics native-SQL face (objectstack) maps mssql/oracle to the unknown arm instead of #6518's case contract | driver-sql's own text-match arms already keep the contract on the driver face | every dashboard text filter over a federated SQL Server (and possibly Oracle) object
    Thread-read: 6723770780
    Premise: holds per seat 1's re-verification 6723770780 on 645ed3d1 (no mssql arm in text-match-sql.ts); the dev measures red first
    Serial constraints cleared: no open PR touches packages/services/service-analytics/** (read at claim); epic siblings #31390 (driver-sql oracle bind) and #31389 (service-datasource catalog) are disjoint

    Scope: Fixes #31394, per #6518 (Ruling-ref). Order: /mnt/project-files/server-seat2/standard-order.md + /mnt/project-files/server-seat2/31394-delta.md (the card's "reuse, don't copy" against the file's no-driver-dependency rule is the dev's four-axis call; a new package or spec export stops at needs_decision). Summons: the maintainer's 「并发都是5」 (project chat, 2026-11-08T16:13Z).

  6. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    Claim amendment · 2026-11-08T16:21Z · #31394 · File surface and Clause-② (Branch claude/project-thread-i27fm1, Metadata-protocol unchanged)

    The dev's route report chose option (b) of the delta: lift driver-sql's text-match construct table into @objectstack/core with caller-supplied emitters, the shape packages/core/src/utils/json-membership-sql.ts already has (re-exported from core's index at origin/main; imported by driver-sql/src/sql-driver-predicate-emitters.ts). No new package, no spec export. Seat accepts the route.

    File surface: packages/core/src/utils/text-match-sql.ts (new) and its re-export in packages/core/src/index.ts; driver-sql's textMatchPredicate and service-analytics' textMatchPredicateSql become thin adapters over it (each keeps its own unknown residue); packages/services/service-analytics/src/text-match-sql.ts and its dialect vocabulary; the cross-run drift suites and new compile tests; root .changeset/. ⛔ No packages/spec/**, no ui/**.
    Clause-②: yes (widening) — new @objectstack/core exports, and the analytics dialect vocabulary widens to mssql/oracle (reachable through the exported AnalyticsServiceConfig); the dev confirms from the real diff; the new public names go in open_questions for the seat's ruling at ACCEPT
    Serial constraints cleared: no open PR touches packages/core/src/index.ts, packages/core/src/utils/** or driver-sql/src/sql-driver.ts (read now). In-flight driver-sql siblings: #31395 (seat 1, mssql substring operators) and #31390 (seat 4, oracle bind); the dev keeps the driver-sql edit to the adapter, merges main after either lands, and conflicts go to the merge queue.

  7. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmTC6s4J4HjQ7nyUMSz
    os-dev-report

    {
      "issue": 31394,
      "status": "done",
      "branch": "claude/project-thread-i27fm1",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/31625",
      "session": "session_01ADHkmTC6s4J4HjQ7nyUMSz",
      "premise_still_valid": true,
      "summary": "Measured on main (5ee59f5e), compile-level: on SQL Server the analytics bridge read dialectName 'mssql' as no answer, so the native-SQL where, read scope and echo emitted a plain LIKE (CI default collation, '[' unescaped) where the driver emits the BIN2/TRANSLATE arms: 17/17 cells red. Oracle measured consistent (16/16 green: same LIKE for the case-exact four; translate vs REPLACE fold row-equivalent), so its arm is left as is. Route (b): the construct table moves to @objectstack/core (text-match-sql.ts, mirroring #20987 json-membership-sql); driver-sql's textMatchPredicate becomes a thin emitter adapter with identical SQL; analytics emits through it and accepts 'mssql' (every other per-dialect table treats mssql as it treated unknown).",
      "tests": "Red first on main: 17 mssql cells red, 16 oracle green. Head 833400e0 via os-verify-lock VERDICT command-exit 0: core text-match-sql.test.ts 3/3; analytics text-match-mssql-oracle + sql-dialect-vocabulary + text-operator-case-exactness 59 passed / 30 skipped; driver-sql full 245 files, 4113 passed / 222 skipped, identical with main's emitter file put back (4113/222). Pre-merge: analytics full 5098 passed / 330 skipped; core 3246 passed; closure typecheck clean. Ablation 1 (ACCEPTED_SQL_DIALECTS without 'mssql', ablation-replace): 17 failed, first '× the bridge hands the driver's mssql on'; restored blob == HEAD. Ablation 2 (core's mssql comparand escape without '[', core rebuilt, ablation-dist-preflight @objectstack/core found the marker in dist): analytics '[ is escaped on this dialect alone' failed (params '%a[b%' where '%a\\[b%' expected) and driver-sql sql-driver-mssql-dialect '$contains: both sides case-exact' failed (binding '%a\\%\\_[b%' where '%a\\%\\_\\[b%' expected); restored (blob == HEAD), rebuilt, preflight --absent ok, suite green 33 passed / 30 skipped. Live cells (OS_TEST_MSSQL_URL / OS_TEST_ORACLE_URL) NOT run: no URL in this container.",
      "gates": "dispatch-gates --commands at 833400e0: 74 commands run; --ran reconciliation 74 derived / 74 run / 0 NOT-MEASURED / 0 UNRUN, all exit 0. Three first exited on container prerequisites and were rerun after closing them: check-plugin-teardown-shape --self-test (exit 3 shallow clone -> fixture commit fetched -> 0), check:dts-closure (exit 1, analytics built without d.ts -> rebuilt -> 0), check:dual-build-cjs-loads (exit 3 no dist -> full pnpm build 119/119 -> 0). eslint on the 12 changed .ts files exit 0. check-platform-checklist --changed-from origin/main exit 0 (0 anchored). check-driver-conformance before/after: 66 covered, 6 DEBT, 0 exempt. CI check-runs not yet read (not waited on).",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm; the change is Clause-②: yes (widening)",
      "files_changed": [
        ".changeset/31394-text-match-mssql-native-sql.md",
        "packages/core/src/index.ts",
        "packages/core/src/utils/text-match-sql.ts",
        "packages/core/src/utils/text-match-sql.test.ts",
        "packages/drivers/driver-sql/src/sql-driver-predicate-emitters.ts",
        "packages/services/service-analytics/src/text-match-sql.ts",
        "packages/services/service-analytics/src/like-pattern.ts",
        "packages/services/service-analytics/src/duration-measure-sql.ts",
        "packages/services/service-analytics/src/contains-membership-sql.ts",
        "packages/services/service-analytics/src/strategies/native-sql-strategy.ts",
        "packages/services/service-analytics/src/__tests__/text-match-mssql-oracle.test.ts",
        "packages/services/service-analytics/src/__tests__/text-operator-case-exactness.test.ts",
        "packages/services/service-analytics/src/__tests__/sql-dialect-vocabulary.test.ts"
      ],
      "line_budget": "src +575 / -851 (net -276); tests +470 / -20; changeset +14. 13 files.",
      "deviations": [
        "core added to the file surface (claim amendment, route (b)).",
        "Forced test edits: sql-dialect-vocabulary.test.ts (accept set now 4 names; 'oracle' replaces 'mssql' as the not-accepted example); text-operator-case-exactness.test.ts (normalise pin names 'mssql'; GLOB-escape pin reads the bound param of compileScopedFilterToSql on sqlite instead of the removed private escapeGlobPattern/globPattern, expected values unchanged).",
        "Live cells not run (no URL)."
      ],
      "mcp_calls": "4 — mcp__github__issue_read x4 (reads only; no write tool)",
      "api_writes": "4 — POST /repos/objectstack-ai/objectstack/dispatches (relay fw-20261108T173011Z-c0723a, executed as POST /pulls -> #31625 and POST /issues/31625/assignees [os-steve]); POST /issues/31394/comments (this report, post-stamped). Branch pushes are git transport, not REST.",
      "open_questions": [
        {
          "question": "New @objectstack/core export names (seat rules at ACCEPT): dialectTextMatchSql, TextMatchDialect, TextMatchShape, TextMatchEmitters, TextMatchRequest, textMatchLikePattern, TEXT_MATCH_ESCAPE_CHARACTER, ASCII_UPPER_LETTERS, ASCII_LOWER_LETTERS, mssqlCaseExactSql, mssqlAsciiLowerSql, mysqlAsciiLowerBinarySql.",
          "options": [
            "A keep as named (dialectTextMatchSql parallels jsonMembershipPredicate's caller-emitter shape)",
            "B rename before ACCEPT"
          ],
          "recommendation": "A, because each name says the construct it emits and the set mirrors #20987's precedent"
        },
        {
          "question": "#16206 declared ACCEPTED_SQL_DIALECTS as canonical SqlDialectName values with no knex aliases; this PR adds 'mssql' (a canonical SqlDialectName, not an alias) and keeps 'oracle' out because it measured consistent. Is that the right reading of #16206?",
          "options": [
            "A yes: widening by a canonical name follows the SqlDialectName anchor",
            "B no: the accept set needs a fresh ruling"
          ],
          "recommendation": "A, because the ruling's constraint was 'canonical, no aliases', which 'mssql' meets"
        }
      ],
      "security_findings": "none",
      "out_of_scope_findings": [
        "class: b · reach: analytics native-SQL face on SQL Server emits 'LIMIT 9223372036854775807' / LIMIT n (as it did for unknown), which SQL Server does not parse · evidence: OFFSET_ONLY_LIMIT_SQL / limit clause in native-sql-strategy.ts · dedupe words: analytics mssql LIMIT OFFSET FETCH",
        "class: b · reach: SUM over a boolean (bit) column on SQL Server: analytics aggregand policy treats mssql as unknown, driver has mssqlAggregandOperandSql · evidence: aggregandDialectOf in native-sql-strategy.ts · dedupe words: analytics mssql bit SUM aggregand",
        "class: c · reach: analytics read-scope membership/empty refusal says 'dialect … is not known' for mssql, which is now a known dialect · dedupe words: analytics refusal message mssql not known",
        "carrier: PR #31625 body · noted, not filed"
      ]
    }

    Generated by Claude Code

  8. added
    pm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-set
    on Nov 8, 2026
  9. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    ACCEPT → PR #31625 comment 6725121433 · head 759110f4 · 2026-11-08T18:08Z · landing: pr_ready + automerge_enable (in the merge queue)

  10. objectstack-fleet commented on Nov 8, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-steve · session_01ADHkmQKZzp4DLtqiCkZZRM

    落地 · 2026-11-08T18:37Z

    reading value
    PR #31625 MERGED 2026-11-08T18:36Z, merge commit 90457e74 (ACCEPT 6725121433; contract review PASS 6725117216 at TIER_DEFAULT)
    #31394 closed by the merge (Fixes #31394)

    Landed:

    • On SQL Server, a dashboard's text filter now matches the same rows as the list view: the analytics native SQL compiles $contains / $notContains / $startsWith / $endsWith / $icontains exactly as driver-sql does (binary collation, ASCII-only fold, [ matched literally).
    • The per-dialect text-match construct table lives once in @objectstack/core (dialectTextMatchSql and its helpers); driver-sql's SQL and bindings are unchanged. AnalyticsServiceConfig.sqlDialect accepts 'mssql'. Oracle measured consistent and left as is.
    • Patch changesets for core, driver-sql and service-analytics. Three adjacent SQL Server gaps filed as service-analytics: the native-SQL face still treats mssql as an unknown dialect for its window, boolean aggregand and refusal text #31626.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:databugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpm:blockingDerived cache from the Blocked-by reverse index: open card with open dependents — never hand-setpriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions