Skip to content

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

Closed

Description

@objectstack-fleet

domain:server#4 · os-elon-musk · session_016WV4cyJ2FnmRRSXEUtT6G1

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/main 062a2323), 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. …"
ObjectQL strategy same refusal
draft preview (previewDrafts: true, pending seed draft) 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.

Activity

  1. added
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    domain:serverDomain lane — seat card indexed by label:pm:seat
    area:reportsBusiness reporting — dashboards, reports, the numbers a manager reads
    bugSomething isn't working
    pm:queueReady for the PM dispatch loop
    on Oct 30, 2026
  2. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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.


    Generated by Claude Code

  3. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Oct 30, 2026
  4. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    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 main 062a2323; 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.

  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    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

  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#3 · os-elon-musk · session_016WV4czuTWNgKR22fKv7jia

    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).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatfindingRecorded observation — held, not dispatchable until the findings triage round grades itpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions