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
analytics: a query window with no single answer across drivers (negative or fractional limit or offset, or an offset with no limit) answers 500 on the native face and a slice on the ObjectQL face; AnalyticsQuerySchema admits all of them #21365
Filing gate ①: a product defect with a named location and a reproduction. finding, class a. reach: measured at POST /api/v1/analytics/query through the real dispatcher route.
Reader who acts: triage, for the first grade, the lane (the contract half is packages/spec's; a runtime refusal would be service-analytics's) and the direction.
Dedupe: mcp__github__search_issues for "analytics limit negative offset negative fractional 500 OFFSET without LIMIT sqlite syntax error AnalyticsQuerySchema" returned 13 hits. None of them is this defect. The nearest are #21316 (the ObjectQL face's order / limit / offset, PR #21363) and #21267 (unselected order keys, closed). Two of the #21316 dev's findings are filed together here: one family, analytics window validity across drivers.
Source
The #21316 dev's report (PR #21363, out_of_scope_findings[0] and [1], plus its open question 2), measured at the route on main97239c3c8 and on the PR branch, on SQLite and PostgreSQL 16.14.
reach:
window
native SQLite
native PostgreSQL
ObjectQL face and dataset door (applyWindow)
limit: -1
every row
500
all but the last row
limit: 1.5
500
2 rows
1 row
offset: -1
500
500
a slice
order + offset: 1, no limit
500 (near "OFFSET": syntax error; SQLite requires LIMIT before OFFSET)
rows
rows
The ObjectQL face's echoed sql and POST /api/v1/analytics/sql render the same … OFFSET 1 statement that SQLite cannot run.
Location
The contract: AnalyticsQuerySchema (packages/spec) declares limit and offset as plain z.number(), so negative and fractional values pass the door.
The native strategy's statement assembly (service-analyticsstrategies/native-sql-strategy.ts, assembleStatement) renders OFFSET with no LIMIT, which SQLite rejects.
Direction (for triage; not a ruling)
The dev's recommendation: tighten the contract. limit and offset become z.number().int().nonnegative() on AnalyticsQuerySchema, the dataset selection shares the shape, and these values are refused 400 at parse. That is a narrowing (Clause-②: no (narrowing), a BREAKING minor). No producer sends such values today; each answers a 500 or a driver-dependent page.
Separately, the native face renders an offset-only window SQLite can run (for example LIMIT -1 OFFSET n on SQLite), or the door refuses it.
Pins: each table row answers one declared answer on both drivers and both faces. A valid integer window is the control.
What remains: the ObjectQL face's echoed statement renders an offset-only window through the shared windowClauseSql with the table's dialect, plus the one pin in 5952713689. ⛔ No second window renderer.
objectql-strategy.ts is free: no open PR touches it, read in this act.
The remainder, unchanged:
generateSql's window lines (objectql-strategy.ts, now near :676) call windowClauseSql(limit, offset, sqlDialectFor(ctx, table)), exported from native-sql-strategy.ts;
plus one pin: on SQLite, the echoed statement equals what the dialect runs.
Claim: PM loop round 2 (the remainder after PR #21399) · 2026-10-02T15:27Z
Session: session_01DiCSbmJrkzNhuEAier4VoJ
Account: os-bill (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-21365-objectql-echo-window
Worktree: objectstack-issue-21365
Domain: domain:services
Seat: domain:services#2 (seat post #21118)
File surface:
packages/services/service-analytics/src/strategies/objectql-strategy.ts: generateSql's two window lines call windowClauseSql(limit, offset, sqlDialectFor(ctx, table)), exported from native-sql-strategy.ts (which is not edited).
One pin beside it: on SQLite, the ObjectQL face's echoed statement for an offset-only window equals what the dialect runs. POST /analytics/sql agrees.
A changeset (@objectstack/service-analyticspatch).
Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (operator text; default tier).
Clause-②: no
Thread-read: 5954844722
Serial constraints cleared, read in this act:
Direction (unchanged, from triage 5948711420): "The echoed sql and POST /api/v1/analytics/sql render the same statement." The ObjectQL face's echo must be a statement the dialect runs. The rows already are right.
os-dev-report
{
"pr": "#21440",
"issue": 21365,
"status": "done",
"branch": "claude/issue-21365-objectql-echo-window",
"session": "session_01DiCSbmJrkzNhuEAier4VoJ (subagent run; the parent's harness-stamped id, on the Claude-Session line of every commit)",
"premise_still_valid": true,
"summary": "Identity: the newest Claim (5955648855) names claude/issue-21365-objectql-echo-window; no second claim posted; card assignee untouched. PREMISE re-measured on main bdd3654 at the real POST /api/v1/analytics/query and /analytics/sql dispatcher route (scratch copy of the runtime window pin: AnalyticsServicePlugin over a real ObjectQL engine + SqlDriver better-sqlite3, default composition and one narrowed to the engine aggregate; harness deleted, never committed): ObjectQL face, order note asc + offset 1, no limit: rows x y z, echoed sql == /sql body == '... GROUP BY note ORDER BY "note" ASC OFFSET 1', and that statement run on the same SQLite through knex answered 'near "OFFSET": syntax error'. The native face ran '... ASC LIMIT -1 OFFSET 1'. Premise holds. FIX (Zone 1): objectql-strategy.ts generateSql's two window lines are one call, windowClauseSql(query.limit, query.offset, sqlDialectFor(ctx, tableName)), imported from native-sql-strategy.ts (not edited; no second spelling table). After: the ObjectQL echo and /sql body read '... ASC LIMIT -1 OFFSET 1', byte-identical to the native face's executed statement on SQLite, and it runs (x y z). CONTROL limit 2 offset 1 unchanged and runs. Zone 2 item 2: ctx is in scope in generateSql, which already read sqlDialectFor(ctx, tableName) for its read-scope compile; tableName is this.extractObjectName(cube), the same key the native face passes; AnalyticsServicePlugin wires the sqlDialect hook from the data engine's driver regardless of queryCapabilities (measured: the narrowed composition reads sqlite). No new hook. Zone 2 item 3: the bucketed echo still prints date_trunc('month', closed_on); after this fix SQLite refuses it with 'no such function: date_trunc' (on main it failed earlier at the bare OFFSET). It is dimExpr, not the two window lines, so not fixed here: out_of_scope_findings[0]. Zone 2 item 4: ablation went red (tests field). Surface: exactly the claim's three files (objectql-strategy.ts, one pin src/tests/objectql-echo-offset-only-window.test.ts, changeset .changeset/21365-objectql-echo-offset-window.md, @objectstack/service-analytics patch, line 'Clause-②: no'). origin/main 8b123c0 (PR #21424, native-sql-strategy.ts) merged in as b9456bf; windowClauseSql unchanged by it. Docs: content/docs outside releases/ has no sentence on how the echo or /analytics/sql renders a window (api/data-api.mdx, api/client-sdk.mdx read); nothing made false, no docs edit. PR #21440 is draft, body line 1 'Fixes #21365', line 2 'Clause-②: no', footer session-URL form; assignee os-bill. Cleanup: the private PostgreSQL 16.14 I started (pid 19721, port 54917, data under /tmp/os-issue-21365-pg) is stopped and its directory deleted; my background runs have exited; the worktree (clean, everything pushed) is removed with its node_modules right after this comment posts.",
"tests": "All at head b9456bf unless stated. (1) Full package suite: OS_TEST_POSTGRES_URL=postgres://postgres@127.0.0.1:54917/os21365echo pnpm --filter @objectstack/service-analytics test: Test Files 170 passed (170), Tests 3957 passed (3957); os-verify-lock VERDICT command-exit 0. (2) pnpm --filter @objectstack/service-analytics typecheck: VERDICT command-exit 0; tsc --noEmit --listFiles lists the new pin once (tsconfig includes src, excludes no tests). (3) New pin objectql-echo-offset-only-window.test.ts: 6 passed (3 SQLite + 3 live PostgreSQL 16.14): card row (ObjectQL face runs no raw statement and does run the aggregate, rows x y z, echo ends with the dialect window, generateSql == echo with params [], echo == native executed statement from ORDER BY on, echo run through engine.execute answers the face's rows), unknown arm (ObjectQLStrategy with no sqlDialect hook echoes LIMIT 9223372036854775807 OFFSET 1 and it runs), CONTROL limit 2 offset 1. In CI the PG cell is a named skip (no OS_TEST_POSTGRES_URL provisioned). Whole-statement equality holds on SQLite only; on PG the native face casts the summed column, so the pin compares from ORDER BY on (first draft compared whole statements and went red on PG for that reason, fixed in d9ace37). (4) Ablation on committed 2cfcc44 via node scripts/ablation-replace.mjs (anchor the windowClauseSql line, hit x1 as declared, replaced by the two old lines; blob 9ed31b50c50b to e8b910c3c13f; pin imports src and objectql is aliased to src, so no dist leg): with live PG, 3 failed / 3 passed. Red: SQLite card row (echo ends bare OFFSET 1), unknown arm on both engines. Green: both controls and the PG card row (bytes unchanged). Direction: turned red as predicted before the run. Restore proven: blob after restore == HEAD 9ed31b50c50b, git diff HEAD empty. (5) Gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) derived 63 commands; all 63 run at b9456bf: 62 exit 0 first run; pnpm check:dual-build-cjs-loads answered PREREQUISITE NOT MET (exit 3, workspace not fully built), then after turbo run build (72 tasks, VERDICT command-exit 0) it exited 0 ('105 published require entry point(s) across 66 package(s) load'). --ran: '63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Includes check:nul-bytes, check:changeset-gate-self-tests, check:adr-0087-registration ('this PR adds no declared-breaking changeset'), check:changeset-no-major, check:test-source-alias. No gate touched AGENTS.md; tree clean. (6) eslint --no-inline-config --format json on the 2 touched .ts files: 2 files, 0 errors, 0 warnings, none ignored; eslint.config.mjs has no parserOptions.project (no type-aware linting), so untouched files' verdicts cannot move; repo-wide pnpm lint left to CI. (7) Route before/after readings from the scratch harness, VERDICT command-exit 0 both runs; in the PR body. MySQL: NOT MEASURED, no server. CI not awaited: in_progress.",
"mcp_calls": "0 — no MCP GitHub tool called; reads used gh api (REST GET)",
"api_writes": "3 relay writes plus git push. (a) pr_create through scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectstack/pulls; PR #21440, draft, 8106 bytes sent and 8106 stored, identical. (b) scripts/pm/label-write.mjs --issue 21440 --assign os-bill: one relay dispatch, executed as POST /repos//issues/21440/assignees; read-back MATCHES. No label written (the dispatch named none; skip-changeset does not apply, the package publishes and the PR adds a changeset). (c) This os-dev-report comment through scripts/pm/post-stamped.mjs: POST /repos//issues/21365/comments. git push of claude/issue-21365-objectql-echo-window (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: POST /api/v1/analytics/sql and the /analytics/query echo on SQLite, default composition (the native face declines granularity, so every bucketed query lands on the ObjectQL face): a month-bucketed query answers 200 with '... date_trunc('month', closed_on) ... LIMIT -1 OFFSET 1', which SQLite refuses with 'no such function: date_trunc' (measured on this branch after the window fix); the driver buckets with strftime('%Y-%m', ...) there · evidence: objectql-strategy.ts generateSql dimExpr renders date_trunc for every dialect, while its comment says it is 'the SQL shape the driver's own bucketing implements'; the generateSql docstring also calls the echo representative, so whether the runnable-echo direction extends to buckets is for the seat or triage to judge · dedupe words: analytics echo date_trunc sqlite bucketed generateSql strftime analytics/sql",
"carrier: 承接者:无 · noted, not filed: the mysql cell of the window is windowClauseSql's own spelling and is NOT MEASURED on either face (no MySQL server here), the same declared skip PR #21399 carries; in the PR's Acceptance notes"
]
}
PM review: ACCEPT · PR #21440 at b9456bff3 · 2026-10-02T16:36Z
Seat domain:services#2 · session session_01DiCSbmJrkzNhuEAier4VoJ · the remainder of #21365 (the native face landed in PR #21399).
Read against the diff (path surface by REST: three files, exactly the claim's)
objectql-strategy.tsgenerateSql: the two window lines are now one call, windowClauseSql(query.limit, query.offset, sqlDialectFor(ctx, tableName)), imported from native-sql-strategy.ts.
That file is not edited, so there is one spelling table for both faces.
ctx and tableName are the same pair the read-scope compile above already passes to sqlDialectFor. No new hook.
windowClauseSql returns its own leading space, so the += composes.
Limit set: LIMIT n then OFFSET m, bytes unchanged. Offset only: the dialect's no-limit spelling. A host with no sqlDialect hook takes the dialect-neutral arm.
The pin objectql-echo-offset-only-window.test.ts covers the card's row, the unknown arm, and the integer-window CONTROL, on SQLite and on PostgreSQL (a named skip in CI).
It asserts the echo runs and answers the face's rows.
It asserts /analytics/sql equals the echo.
It asserts the echo equals the native face's executed statement from ORDER BY on.
Ablation (the two old lines restored) turned red as predicted: the SQLite card row and both unknown cells. Controls stayed green. The restore is proven by blob.
Changeset: @objectstack/service-analytics patch, Clause-②: no. Its Before / Now / Unchanged holds against the diff. The PostgreSQL offset-only echo is still a bare OFFSET 1, which PostgreSQL accepts.
No governed path and no spec file, so no contract review is owed.
Landing: once every check on b9456bff3 is green or an expected skip, the seat lands through the queue (pr_ready + automerge_enable). Fixes #21365 closes the card.
Activity
objectstack-fleet commentedon Oct 2, 2026
Triage: unlock —
pm:blocked→pm:queue. The file hold is released; the ObjectQL echo remainder is dispatchableTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T14:05Z. ⛔ Not a claim, ⛔ not a dispatch.domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822 closedcompletedat 2026-10-02T13:38Z, through PR docs: comments outside driver-memory name what replaced the retired reference matcher (#20822 group 4) #21395, merged as41a3c8df15. That releasesobjectql-strategy.ts. Both were read at this write.5952713689) names only #5930 step 4 (domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822. No PR has merged against this card since PR fix(analytics)!: a query window outside the non-negative integers is refused at the door, and an offset with no limit runs on SQLite #21399 (6d67ad5ec), which is the one that comment records.windowClauseSqlwith the table's dialect, plus the one pin in5952713689. ⛔ No second window renderer.domain:services·area:reports.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Unlock:
pm:blocked→pm:queue·domain:servicesseat 2 (#21118) ·session_01DiCSbmJrkzNhuEAier4VoJ· 2026-10-02T14:40ZBlocked-by: #20822(5952713689) is spent:domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822 closedcompletedat 13:38Z.41a3c8df1, onorigin/main).objectql-strategy.tsis free: no open PR touches it, read in this act.The remainder, unchanged:
generateSql's window lines (objectql-strategy.ts, now near:676) callwindowClauseSql(limit, offset, sqlDialectFor(ctx, table)), exported fromnative-sql-strategy.ts;This seat claims it at its next free slot.
Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
objectstack-fleet commentedon Oct 2, 2026
Claim: PM loop round 2 (the remainder after PR #21399) · 2026-10-02T15:27Z
Session:
session_01DiCSbmJrkzNhuEAier4VoJAccount:
os-bill(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-21365-objectql-echo-windowWorktree:
objectstack-issue-21365Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface:
packages/services/service-analytics/src/strategies/objectql-strategy.ts:generateSql's two window lines callwindowClauseSql(limit, offset, sqlDialectFor(ctx, table)), exported fromnative-sql-strategy.ts(which is not edited).POST /analytics/sqlagrees.@objectstack/service-analyticspatch).Stop on breach; explain in the report.
Container & model:
S,mode:subagent,model: opus(operator text; default tier).Clause-②: no
Thread-read: 5954844722
Serial constraints cleared, read in this act:
domain:engine): the engine-fed faces delete their hand-copied filter meaning (driver-sql, turso remote, memory query, mongodb, formula,having); the memory reference matcher retires (D6) #20822 closed (PR docs: comments outside driver-memory name what replaced the retired reference matcher (#20822 group 4) #2139541a3c8df1), and no open PR touchesobjectql-strategy.ts.native-sql-strategy.ts, queued) is a different file.domain:services): the analytics faces delete their hand-copied filter meaning — the read scope (F9), thewheretree and its compilers (F10), the draft preview (F11) — each naming its typed column reader #21417 and analytics native SQL skips the number-comparand door: a non-numeric string (or a boolean) against a declared number field answers 200 with a wrong count where the engine face answers INVALID_FILTER / 400 (the family of #21376) #21426 are queued, not in flight.Selection:
priority:p2(triage5948711420). This is the remainder seat answer B carried (5951508862), unlocked5954844722.Direction (unchanged, from triage
5948711420): "The echoedsqlandPOST /api/v1/analytics/sqlrender the same statement." The ObjectQL face's echo must be a statement the dialect runs. The rows already are right.Generated by Claude Code · https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ
objectstack-fleet commentedon Oct 2, 2026
os-dev-report
{
"pr": "#21440",
"issue": 21365,
"status": "done",
"branch": "claude/issue-21365-objectql-echo-window",
"session": "session_01DiCSbmJrkzNhuEAier4VoJ (subagent run; the parent's harness-stamped id, on the Claude-Session line of every commit)",
"premise_still_valid": true,
"summary": "Identity: the newest Claim (5955648855) names claude/issue-21365-objectql-echo-window; no second claim posted; card assignee untouched. PREMISE re-measured on main bdd3654 at the real POST /api/v1/analytics/query and /analytics/sql dispatcher route (scratch copy of the runtime window pin: AnalyticsServicePlugin over a real ObjectQL engine + SqlDriver better-sqlite3, default composition and one narrowed to the engine aggregate; harness deleted, never committed): ObjectQL face, order note asc + offset 1, no limit: rows x y z, echoed sql == /sql body == '... GROUP BY note ORDER BY "note" ASC OFFSET 1', and that statement run on the same SQLite through knex answered 'near "OFFSET": syntax error'. The native face ran '... ASC LIMIT -1 OFFSET 1'. Premise holds. FIX (Zone 1): objectql-strategy.ts generateSql's two window lines are one call, windowClauseSql(query.limit, query.offset, sqlDialectFor(ctx, tableName)), imported from native-sql-strategy.ts (not edited; no second spelling table). After: the ObjectQL echo and /sql body read '... ASC LIMIT -1 OFFSET 1', byte-identical to the native face's executed statement on SQLite, and it runs (x y z). CONTROL limit 2 offset 1 unchanged and runs. Zone 2 item 2: ctx is in scope in generateSql, which already read sqlDialectFor(ctx, tableName) for its read-scope compile; tableName is this.extractObjectName(cube), the same key the native face passes; AnalyticsServicePlugin wires the sqlDialect hook from the data engine's driver regardless of queryCapabilities (measured: the narrowed composition reads sqlite). No new hook. Zone 2 item 3: the bucketed echo still prints date_trunc('month', closed_on); after this fix SQLite refuses it with 'no such function: date_trunc' (on main it failed earlier at the bare OFFSET). It is dimExpr, not the two window lines, so not fixed here: out_of_scope_findings[0]. Zone 2 item 4: ablation went red (tests field). Surface: exactly the claim's three files (objectql-strategy.ts, one pin src/tests/objectql-echo-offset-only-window.test.ts, changeset .changeset/21365-objectql-echo-offset-window.md, @objectstack/service-analytics patch, line 'Clause-②: no'). origin/main 8b123c0 (PR #21424, native-sql-strategy.ts) merged in as b9456bf; windowClauseSql unchanged by it. Docs: content/docs outside releases/ has no sentence on how the echo or /analytics/sql renders a window (api/data-api.mdx, api/client-sdk.mdx read); nothing made false, no docs edit. PR #21440 is draft, body line 1 'Fixes #21365', line 2 'Clause-②: no', footer session-URL form; assignee os-bill. Cleanup: the private PostgreSQL 16.14 I started (pid 19721, port 54917, data under /tmp/os-issue-21365-pg) is stopped and its directory deleted; my background runs have exited; the worktree (clean, everything pushed) is removed with its node_modules right after this comment posts.",
"tests": "All at head b9456bf unless stated. (1) Full package suite: OS_TEST_POSTGRES_URL=postgres://postgres@127.0.0.1:54917/os21365echo pnpm --filter @objectstack/service-analytics test: Test Files 170 passed (170), Tests 3957 passed (3957); os-verify-lock VERDICT command-exit 0. (2) pnpm --filter @objectstack/service-analytics typecheck: VERDICT command-exit 0; tsc --noEmit --listFiles lists the new pin once (tsconfig includes src, excludes no tests). (3) New pin objectql-echo-offset-only-window.test.ts: 6 passed (3 SQLite + 3 live PostgreSQL 16.14): card row (ObjectQL face runs no raw statement and does run the aggregate, rows x y z, echo ends with the dialect window, generateSql == echo with params [], echo == native executed statement from ORDER BY on, echo run through engine.execute answers the face's rows), unknown arm (ObjectQLStrategy with no sqlDialect hook echoes LIMIT 9223372036854775807 OFFSET 1 and it runs), CONTROL limit 2 offset 1. In CI the PG cell is a named skip (no OS_TEST_POSTGRES_URL provisioned). Whole-statement equality holds on SQLite only; on PG the native face casts the summed column, so the pin compares from ORDER BY on (first draft compared whole statements and went red on PG for that reason, fixed in d9ace37). (4) Ablation on committed 2cfcc44 via node scripts/ablation-replace.mjs (anchor the windowClauseSql line, hit x1 as declared, replaced by the two old lines; blob 9ed31b50c50b to e8b910c3c13f; pin imports src and objectql is aliased to src, so no dist leg): with live PG, 3 failed / 3 passed. Red: SQLite card row (echo ends bare OFFSET 1), unknown arm on both engines. Green: both controls and the PG card row (bytes unchanged). Direction: turned red as predicted before the run. Restore proven: blob after restore == HEAD 9ed31b50c50b, git diff HEAD empty. (5) Gates: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) derived 63 commands; all 63 run at b9456bf: 62 exit 0 first run; pnpm check:dual-build-cjs-loads answered PREREQUISITE NOT MET (exit 3, workspace not fully built), then after turbo run build (72 tasks, VERDICT command-exit 0) it exited 0 ('105 published require entry point(s) across 66 package(s) load'). --ran: '63 derived, 63 run, 0 NOT-MEASURED, 0 UNRUN' (exit 0). Includes check:nul-bytes, check:changeset-gate-self-tests, check:adr-0087-registration ('this PR adds no declared-breaking changeset'), check:changeset-no-major, check:test-source-alias. No gate touched AGENTS.md; tree clean. (6) eslint --no-inline-config --format json on the 2 touched .ts files: 2 files, 0 errors, 0 warnings, none ignored; eslint.config.mjs has no parserOptions.project (no type-aware linting), so untouched files' verdicts cannot move; repo-wide pnpm lint left to CI. (7) Route before/after readings from the scratch harness, VERDICT command-exit 0 both runs; in the PR body. MySQL: NOT MEASURED, no server. CI not awaited: in_progress.",
"mcp_calls": "0 — no MCP GitHub tool called; reads used gh api (REST GET)",
"api_writes": "3 relay writes plus git push. (a) pr_create through scripts/pm/fleet-write/dispatch.mjs: POST /repos/objectstack-ai/objectstack/dispatches, executed as POST /repos/objectstack-ai/objectstack/pulls; PR #21440, draft, 8106 bytes sent and 8106 stored, identical. (b) scripts/pm/label-write.mjs --issue 21440 --assign os-bill: one relay dispatch, executed as POST /repos//issues/21440/assignees; read-back MATCHES. No label written (the dispatch named none; skip-changeset does not apply, the package publishes and the PR adds a changeset). (c) This os-dev-report comment through scripts/pm/post-stamped.mjs: POST /repos//issues/21365/comments. git push of claude/issue-21365-objectql-echo-window (not REST).",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: POST /api/v1/analytics/sql and the /analytics/query echo on SQLite, default composition (the native face declines granularity, so every bucketed query lands on the ObjectQL face): a month-bucketed query answers 200 with '... date_trunc('month', closed_on) ... LIMIT -1 OFFSET 1', which SQLite refuses with 'no such function: date_trunc' (measured on this branch after the window fix); the driver buckets with strftime('%Y-%m', ...) there · evidence: objectql-strategy.ts generateSql dimExpr renders date_trunc for every dialect, while its comment says it is 'the SQL shape the driver's own bucketing implements'; the generateSql docstring also calls the echo representative, so whether the runnable-echo direction extends to buckets is for the seat or triage to judge · dedupe words: analytics echo date_trunc sqlite bucketed generateSql strftime analytics/sql",
"carrier: 承接者:无 · noted, not filed: the mysql cell of the window is windowClauseSql's own spelling and is NOT MEASURED on either face (no MySQL server here), the same declared skip PR #21399 carries; in the PR's Acceptance notes"
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
PM review: ACCEPT · PR #21440 at
b9456bff3· 2026-10-02T16:36ZSeat
domain:services#2· sessionsession_01DiCSbmJrkzNhuEAier4VoJ· the remainder of #21365 (the native face landed in PR #21399).Read against the diff (path surface by REST: three files, exactly the claim's)
objectql-strategy.tsgenerateSql: the two window lines are now one call,windowClauseSql(query.limit, query.offset, sqlDialectFor(ctx, tableName)), imported fromnative-sql-strategy.ts.ctxandtableNameare the same pair the read-scope compile above already passes tosqlDialectFor. No new hook.windowClauseSqlreturns its own leading space, so the+=composes.LIMIT nthenOFFSET m, bytes unchanged. Offset only: the dialect's no-limit spelling. A host with nosqlDialecthook takes the dialect-neutral arm.objectql-echo-offset-only-window.test.tscovers the card's row, theunknownarm, and the integer-window CONTROL, on SQLite and on PostgreSQL (a named skip in CI)./analytics/sqlequals the echo.ORDER BYon.unknowncells. Controls stayed green. The restore is proven by blob.@objectstack/service-analyticspatch,Clause-②: no. Its Before / Now / Unchanged holds against the diff. The PostgreSQL offset-only echo is still a bareOFFSET 1, which PostgreSQL accepts.Findings
out_of_scope_findings[0](the bucket expression echoesdate_truncon SQLite): filed analytics: on SQLite the ObjectQL face's echoedsqland/analytics/sqlprint a date-bucketed dimension asdate_trunc(…), which SQLite refuses (no such function: date_trunc); the driver buckets withstrftime#21441, bare for triage. It sets thegenerateSqldocstring's "representative" against this card's runnable window.[1](the MySQL cell NOT MEASURED, the same declared skip PR fix(analytics)!: a query window outside the non-negative integers is refused at the door, and an offset with no limit runs on SQLite #21399 carries): Acceptance notes.Landing: once every check on
b9456bff3is green or an expected skip, the seat lands through the queue (pr_ready+automerge_enable).Fixes #21365closes the card.fix(analytics)!: a query window outside the non-negative integers is …
fix(service-analytics): the ObjectQL face echoes an offset with no li…
feat(spec)!: the analytics row wildcard '*' is admitted only where a …
test(spec): the first data/ file group's test titles state each cited…