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 filtered dataset measure asked on the cube door as CUBE.MEASURE, the name GET /analytics/meta lists, answers the unfiltered aggregate #25906
Filing gate: ① product defect, a silent wrong number at a named location.
Reader: domain:spec / analytics seat, the next PR touching packages/services/service-analytics/src/strategies/native-sql-strategy.ts (compileClauses, the per-measure measureFilters lookup) and analytics-service.ts (namedQueryFields, same lookup).
Dedupe (open AND closed): "analytics cube query measure named with cube prefix drops its dataset measure filter, unfiltered sum" → 10 shown of 31, none on this; "measureFilters lookup qualified member name cube.measure native SQL strategy" → 10 shown of 13, none on this.
Business reason: a dashboard or API caller that uses the member name GET /analytics/meta lists for a filtered measure (say won revenue) is shown the total of every row under that name, with no error, and makes decisions on a wrong number. If nothing is done, every client that copies names from /meta keeps getting it.
reach: measured at AnalyticsService.query, the handler POST /analytics/query calls (packages/runtime/src/domains/analytics.ts), on the native-SQL strategy (SQLite dialect), at main and at the branch of PR #25903 (#25778). In-process at the service, not over HTTP.
type: Bug
getMeta('sales') lists the measure as sales.won_amount, the CUBE.MEASURE spelling every cube member is listed under. Querying the cube door with each spelling:
measures
statement run
['won_amount']
SELECT SUM(CASE WHEN stage = $1 THEN amount END) AS "won_amount" FROM "deal"
['sales.won_amount']
SELECT SUM(amount) AS "sales.won_amount" FROM "deal"
The second answers the total of every deal under the name of the won-deals measure. Nothing is refused and nothing says the filter was dropped.
Why
The member resolves through lookupMember / declaredMemberEntry, which strip the CUBE. prefix, but the measure's scoped filter is read as datasetScope.measureFilters[measure] with the spelling as asked, so the prefixed key misses. The read gate (namedQueryFields) uses the same key, so it does not judge the filter's fields either. That is not a bypass: the filter is not applied, so its fields are not read.
Expected
The prefixed spelling answers what the bare one does, the filter included, or is refused. #25778's duration measures already resolve the filter by the bare key (durationMeasureKey in duration-measure-sql.ts). Ordinary measures need the same resolution at both lookups. The DatasetExecutor path is unaffected, because it builds bare names.
Found by the independent review of PR #25903 (dataset duration measure). It is outside that card's scope.
p2: a silent wrong number. The name /analytics/meta lists drops the measure's filter. Lane:service-analytics is server code under the lane merge, so domain:server, not spec.
Claim: PM loop round 6 · card thread
Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_014YyczUJLen7od4GYKjPdY2
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-hltghk (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
Worktree: the card thread's own container checkout
Domain: domain:server
Seat: domain:server#1
Ruling-ref: the prefixed CUBE.MEASURE spelling answers what the bare one does, filter included: one member-name normalization feeds both measureFilters lookups (compileClauses, namedQueryFields); ⛔ not a refusal of the prefixed spelling
File surface: packages/services/service-analytics/**, its tests, one changeset. ⛔ No packages/spec/** (stop on breach; explain in the report)
Container & model: M, mode:thread, default model and effort (set at thread open)
Clause-②: yes (expected; a narrowing only if the read gate now refuses a caller it served, which the dev measures and declares)
Metadata-protocol: none
Responsibility: measures: ['sales.won_amount'] runs SUM(amount) without the measure's filter, while ['won_amount'] applies it (card #25906, at AnalyticsService.query, native SQL, SQLite)
Thread-read: none
Premise: holds (read 2026-10-26T08:10Z at origin/main05f7758f05: card open, unassigned, pm:queue, no other seat's claim; the dev reproduces red first)
Serial constraints cleared: no open PR names #25906; the dev re-measures open PRs on its file surface before pr_create
{
"issue": 25906,
"status": "done",
"branch": "claude/project-thread-hltghk",
"pr": "https://github.com/objectstack-ai/objectstack/pull/26007",
"session": "session_014YyczUJLen7od4GYKjPdY2",
"premise_still_valid": true,
"summary": "Every reader of a dataset measure's scoped filter now resolves the requested name through one rule, scopedMeasureKey (new scoped-measure-key.ts; durationMeasureKey delegates to it): the bare own key, else the name without its own cube's CUBE. qualifier. Seven readers moved: native compileClauses and its two routing declines (JSON-construct on unknown dialect, nested-relation), ObjectQL execute, its generateSql echo and filterMemberView, and the service's namedQueryFields (the read gate). DatasetExecutor builds bare names and is untouched; no raw measureFilters lookup is left in the package. Declared Clause-② as measured: no (narrowing), departing from the claim's expected yes, because the read gate now refuses (403 PERMISSION_DENIED) a qualified-spelling caller it used to answer with the unfiltered number; nothing is widened; contract review owed.",
"tests": "Red first at 6c314cfe's base 05f7758f (helper present, readers unfixed): new cube-qualified-measure-filter.test.ts 5 failed / 2 passed (native received COALESCE(SUM(amount), 0) AS \"sales.won_amount\"; ObjectQL aggregation had no filter; echo SUM(amount); read gate answered the qualified spelling instead of PERMISSION_DENIED; canHandle true on a nested-relation filter). After fix 7/7. Ablation on committed 6c314cfe via scripts/ablation-replace.mjs: read-gate line reverted to the raw lookup in analytics-service.ts (anchor 1 to 0, blob f6a5b05b to c366dfb4), 1 failed / 6 passed (exactly the read-gate case), restored blob == HEAD and git diff HEAD empty (src-resolved suite, no dist step). pnpm --filter @objectstack/service-analytics test: Test Files 189 passed (189), Tests 4570 passed | 282 skipped; typecheck exit 0 (os-verify-lock VERDICT command-exit 0); eslint --no-inline-config packages/services/service-analytics exit 0. All at 6c314cfe.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 6c314cfe: 70 derived; each run with exit captured before any pipe: 68 exit 0; NOT MEASURED (exit 3, prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone; git fetch --unshallow failed with early EOF), pnpm check:dual-build-cjs-loads (needs a full workspace build). --ran reconciliation exit 0: 70 derived, 68 run, 2 NOT-MEASURED, 0 UNRUN. PR check-runs: in_progress at report time (not awaited, per os-dev.md).",
"dogfood_battery": "6c314cfe · os-verify-lock VERDICT command-exit 1 (held 26m01s) · Test Files 1 failed | 295 passed | 3 skipped (299) · Tests 1 failed | 2522 passed | 18 skipped (2541) · the one failure is not this diff's: test/authz-probe-blind-spot.test.ts pins control handleI18nRequest at 3 occurrences in packages/runtime/src/domains/i18n.ts; the base tree 05f7758f already holds 4 (moved by 69fa23d6, PR #25956); this diff touches no packages/runtime or packages/qa path.",
"mcp_calls": "0 — no MCP GitHub calls",
"api_writes": "2 — fleet-write relay: pr_create objectstack-ai/objectstack#26007 (create + assignees leg, read-back identical 5459 bytes) run 40295522463; post-stamped comment on #25906 (this report). git push of claude/project-thread-hltghk is not a REST write.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: dogfood battery on origin/main 05f7758f · evidence: test/authz-probe-blind-spot.test.ts 'control handleI18nRequest moved in packages/runtime/src/domains/i18n.ts: expected 4 to be 3'; git show 05f7758f:packages/runtime/src/domains/i18n.ts counts 4; last touched by 69fa23d6 (PR #25956) · dedupe words: authz probe blind-spot census, handleI18nRequest control moved, i18n.ts census pin, dogfood main red"
]
}
Activity
objectstack-fleet commentedon Oct 26, 2026
Triage: first grade —
bug·priority:p2·domain:server·area:reports·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-26T03:53Z⛔ Not a claim, ⛔ not a dispatch.
findingremoved./analytics/metalists drops the measure's filter. Lane:service-analyticsis server code under the lane merge, sodomain:server, not spec.measureFilterslookups (compileClausesandnamedQueryFields). analytics native SQL: an all-NULLsumgroup is folded to 0 after ORDER BY / LIMIT ran on the NULL, so SQLite and PostgreSQL return different sorted pages #25907 (the same file) can ride the same PR.Generated by Claude Code
objectstack-fleet commentedon Oct 26, 2026
Claim: PM loop round 6 · card thread
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_014YyczUJLen7od4GYKjPdY2Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-hltghk(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#1Ruling-ref: the prefixed
CUBE.MEASUREspelling answers what the bare one does, filter included: one member-name normalization feeds bothmeasureFilterslookups (compileClauses,namedQueryFields); ⛔ not a refusal of the prefixed spellingFile surface:
packages/services/service-analytics/**, its tests, one changeset. ⛔ Nopackages/spec/**(stop on breach; explain in the report)Container & model:
M,mode:thread, default model and effort (set at thread open)Clause-②: yes (expected; a narrowing only if the read gate now refuses a caller it served, which the dev measures and declares)
Metadata-protocol: none
Responsibility:
measures: ['sales.won_amount']runsSUM(amount)without the measure's filter, while['won_amount']applies it (card #25906, atAnalyticsService.query, native SQL, SQLite)Thread-read: none
Premise: holds (read 2026-10-26T08:10Z at
origin/main05f7758f05: card open, unassigned,pm:queue, no other seat's claim; the dev reproduces red first)Serial constraints cleared: no open PR names #25906; the dev re-measures open PRs on its file surface before
pr_createobjectstack-fleet commentedon Oct 26, 2026
os-dev-report
{ "issue": 25906, "status": "done", "branch": "claude/project-thread-hltghk", "pr": "https://github.com/objectstack-ai/objectstack/pull/26007", "session": "session_014YyczUJLen7od4GYKjPdY2", "premise_still_valid": true, "summary": "Every reader of a dataset measure's scoped filter now resolves the requested name through one rule, scopedMeasureKey (new scoped-measure-key.ts; durationMeasureKey delegates to it): the bare own key, else the name without its own cube's CUBE. qualifier. Seven readers moved: native compileClauses and its two routing declines (JSON-construct on unknown dialect, nested-relation), ObjectQL execute, its generateSql echo and filterMemberView, and the service's namedQueryFields (the read gate). DatasetExecutor builds bare names and is untouched; no raw measureFilters lookup is left in the package. Declared Clause-② as measured: no (narrowing), departing from the claim's expected yes, because the read gate now refuses (403 PERMISSION_DENIED) a qualified-spelling caller it used to answer with the unfiltered number; nothing is widened; contract review owed.", "tests": "Red first at 6c314cfe's base 05f7758f (helper present, readers unfixed): new cube-qualified-measure-filter.test.ts 5 failed / 2 passed (native received COALESCE(SUM(amount), 0) AS \"sales.won_amount\"; ObjectQL aggregation had no filter; echo SUM(amount); read gate answered the qualified spelling instead of PERMISSION_DENIED; canHandle true on a nested-relation filter). After fix 7/7. Ablation on committed 6c314cfe via scripts/ablation-replace.mjs: read-gate line reverted to the raw lookup in analytics-service.ts (anchor 1 to 0, blob f6a5b05b to c366dfb4), 1 failed / 6 passed (exactly the read-gate case), restored blob == HEAD and git diff HEAD empty (src-resolved suite, no dist step). pnpm --filter @objectstack/service-analytics test: Test Files 189 passed (189), Tests 4570 passed | 282 skipped; typecheck exit 0 (os-verify-lock VERDICT command-exit 0); eslint --no-inline-config packages/services/service-analytics exit 0. All at 6c314cfe.", "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 6c314cfe: 70 derived; each run with exit captured before any pipe: 68 exit 0; NOT MEASURED (exit 3, prerequisite): node scripts/check-plugin-teardown-shape.mjs --self-test (shallow clone; git fetch --unshallow failed with early EOF), pnpm check:dual-build-cjs-loads (needs a full workspace build). --ran reconciliation exit 0: 70 derived, 68 run, 2 NOT-MEASURED, 0 UNRUN. PR check-runs: in_progress at report time (not awaited, per os-dev.md).", "dogfood_battery": "6c314cfe · os-verify-lock VERDICT command-exit 1 (held 26m01s) · Test Files 1 failed | 295 passed | 3 skipped (299) · Tests 1 failed | 2522 passed | 18 skipped (2541) · the one failure is not this diff's: test/authz-probe-blind-spot.test.ts pins control handleI18nRequest at 3 occurrences in packages/runtime/src/domains/i18n.ts; the base tree 05f7758f already holds 4 (moved by 69fa23d6, PR #25956); this diff touches no packages/runtime or packages/qa path.", "mcp_calls": "0 — no MCP GitHub calls", "api_writes": "2 — fleet-write relay: pr_create objectstack-ai/objectstack#26007 (create + assignees leg, read-back identical 5459 bytes) run 40295522463; post-stamped comment on #25906 (this report). git push of claude/project-thread-hltghk is not a REST write.", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: dogfood battery on origin/main 05f7758f · evidence: test/authz-probe-blind-spot.test.ts 'control handleI18nRequest moved in packages/runtime/src/domains/i18n.ts: expected 4 to be 3'; git show 05f7758f:packages/runtime/src/domains/i18n.ts counts 4; last touched by 69fa23d6 (PR #25956) · dedupe words: authz probe blind-spot census, handleI18nRequest control moved, i18n.ts census pin, dogfood main red" ] }objectstack-fleet commentedon Oct 26, 2026
Landed
PR #26007 merged at 2026-10-26T11:05Z by the merge queue as
3117289002onmain(Fixes #25906; ACCEPT6412750436).git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-26007-*'origin/mainby contentgit cat-file -e origin/main:packages/services/service-analytics/src/scoped-measure-key.tsgit merge-base --is-ancestor 3117289002 origin/mainState: this card closed by
Fixes(completed);pm:dispatchedremoved.Generated by Claude Code