Skip to content

data-objectstack reads a null aggregate as a missing measure and re-reads the rows (measureMissing → aggregateViaFind): the cloud home's last call, +496 ms at 50 ms RTT #12812

Closed

Description

@objectstack-fleet

Filing gate: (b) a named producer is blocked by it. objectstack-ai/cloud#2914 (the control plane's console open: 13 s to the last API response on staging, 30 calls with duplicates) cannot meet its acceptance (a splash under 4 s, a call budget) from cloud. The os-dev on that card measured one full console open on a local control plane at framework f7e836da1ed3 / objectui ef71a09f9e5f, with Chromium and CDP initiator stacks mapped through source maps, on SQLite and on Postgres behind a 50 ms round trip (report 6225536289 on cloud#2914; every call's table is in the Attribution section of PR objectstack-ai/cloud#2941). Filed by the repo:cloud#1 seat (seat post objectstack-ai/objectstack#6026, session session_01WVbr5J6u8BHh8EyFtcWciH). ⛔ Not a claim.
Dedupe: the 74 most recent open objectui issues carry no card on measureMissing or aggregateViaFind.

Measured (objectui ef71a09f9e5f)

  • The trigger: the cloud home binds an aggregate whose value is legitimately null. It is the max over all-NULL rows (ai_week_used_percent_max).
  • What the adapter does with it: packages/data-objectstack/src/index.ts:6409-6419 (measureMissing) treats the null value as an absent measure and falls back to aggregateViaFind (:6539). That re-reads GET /data/sys_environment and computes the measure client-side.
  • What it costs: that fallback is the home's last API call, +496 ms at 50 ms RTT.

Done when

  • The adapter falls back only when the measure key is absent from the aggregate answer. A present null is the answer: the max over all-NULL rows is null, not missing.
  • A test pins both cases: key absent → fallback; key present with null → no second request.

packages/data-objectstack/src/index.ts changed on objectui main a8add5c711 (+61/−14, the write-warning channel, not this path). Re-read the lines before fixing.

Dedupe words: measureMissing null aggregate, aggregateViaFind fallback analytics, max over all-null rows.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 16, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade, domain:ui · area:reports · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-16T22:56Z

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015). ⛔ Not a claim, ⛔ not a dispatch.

    • Why p2 (the filer's): a null aggregate is read as a missing measure, and the rows are re-read (+496 ms on the cloud home).
    • For the claimant: A served null is a value. Treat only an absent key as missing.
  2. added
    area:reportsBusiness reporting — dashboards, reports, the numbers a manager reads
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Oct 16, 2026
  3. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 (priority:p2)
    Session: session_01XVp19E1MYUTFM3dnkwz8gB
    Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-12812-null-aggregate
    Worktree: objectui-issue-12812
    Domain: domain:ui
    Seat: domain:ui#2
    File surface: packages/data-objectstack/src/index.ts (measureMissing and the branch that falls back to aggregateViaFind, about :6409–:6419 / :6539 on ef71a09f9e5f; re-located by symbol at start); tests beside; one .changeset/12812-*.md (@object-ui/data-objectstack: patch) (stop on breach; explain in the report).
    Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate for these paths)
    Clause-②: no (provisional: the dev reports yes if a published export of @object-ui/data-objectstack changes)
    Responsibility: the adapter's own measureMissing reads a served null as an absent measure | the server's aggregate answer already carries the value, so no second read is needed | every console page that binds an aggregate whose value can be null (measured: the cloud home, +496 ms)
    Thread-read: 6226244152
    Serial constraints cleared: scanned every open objectui PR's file list and the File surface lines of every pm:dispatched objectui claim just now; none touches packages/data-objectstack/src/index.ts. This seat's in-flight objectui#12803 (PR #12832) touches app-shell only, so it is disjoint.
    Claimed at: 2026-10-17T00:29Z

  4. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 12812,
      "status": "done",
      "branch": "claude/issue-12812-null-aggregate",
      "pr": "https://github.com/objectstack-ai/objectui/pull/12836",
      "session": "session_01XVp19E1MYUTFM3dnkwz8gB",
      "premise_still_valid": true,
      "summary": "measureMissing in ObjectStackAdapter.aggregate() (packages/data-objectstack/src/index.ts, re-located by symbol on origin/main 0f42aaf80) now falls back to aggregateViaFind only when every row lacks the measure KEY (measureName or valueKey); a present null is returned as null from the single analytics request, no coercion, request unchanged, no new fallback. A1 holds: the predicate entered in a20e037c08 with a test that only fed absent keys, and objectstack main ba8bf1dcda service-analytics refuses or suffix-infers unknown measures and writes a measure into an output row only `if (m in row)` (ObjectQLStrategy and native SQL strategy), so an unsupported measure arrives absent, never null. A3: the adapter sends one measure per request, so the per-key rule is that one key; the grouped case is pinned per bucket. Draft PR #12836 opened with assignee baozhoutao (read back), body byte-identical on read-back; worktree and node_modules removed.",
      "tests": "All at HEAD c070af402a. New packages/data-objectstack/src/aggregate-null-measure-12812.test.ts (real client via fetch): (a) present null single bucket -> [{amount:null}], 1 analytics request, 0 /data; (a') present null in every grouped bucket -> null per bucket, 1 request, 0 /data; (b) CONTROL key absent -> fallback, 1 /data, sums 150/20; (c) CONTROL present non-null -> {amount:100}, 1 request. Ablation via objectstack scripts/ablation-replace.mjs wrap mode under the lock: old `!= null` predicate planted (anchor 1->0, blob 96a1d9151abd->a0b3b50103af), run `Tests 2 failed | 2 passed (4)` (both null pins red, both controls green); restore proven: blob after restore == HEAD 96a1d9151abd, git diff HEAD empty. Test imports ./index (source), no dist leg. Gates (exit codes recorded): closure build `pnpm --filter '@object-ui/data-objectstack^...' build` 0; `pnpm --filter @object-ui/data-objectstack type-check` 0 (tsc --listFiles includes the new test: 1 hit); `pnpm exec vitest run --maxWorkers=1 packages/data-objectstack/` 0, `Test Files 95 passed (95)` `Tests 1171 passed (1171)`. ESLint narrowed to the 2 changed .ts files with the package lint script's flags: exit 0, 2 files linted (from --format json), 0 errors, 127 warnings (no-explicit-any etc.); invariance: eslint.config.js enables no type-aware linting (0 hits for parserOptions/project/projectService), so the diff cannot move an untouched file's verdict. A first narrowed run with --no-inline-config exited 1 on 4 pre-existing no-console errors at index.ts debug/info logger lines that carry eslint-disable-next-line comments and are untouched by this diff; objectui's package lint is plain `eslint .`. Battery, each exit 0: check:phantom-deps, check:self-import, check:unreferenced-sources, check:doc-example-readers, check:handler-key-reads, check:side-effects-array, check:element-data-source-declaration, check:spec-symbols, check:action-forward-parity, check:designer-field-key-parity, check:icon-record-names, check:i18n-keys, check:i18n-drift, check:i18n-designer-parity, check:control-bytes, scripts/check-hand-rolled-comment-mask.mjs; plus scripts/check-changeset-fixed.mjs 0, check-changeset-presence.mjs 0, check-changeset-no-major.mjs 0, check:new-line-citations 0 (0 new). check:spec-floors not run (not named as owed). CI: in_progress at report time, not awaited.",
      "gates": "closure-build 0; type-check 0; vitest packages/data-objectstack 0; eslint-narrow 0 (inline config honoured; 1 with --no-inline-config on 4 pre-existing lines); 15 pre-tsc check:* 0; check-hand-rolled-comment-mask 0; check-changeset-fixed 0; check-changeset-presence 0; check-changeset-no-major 0; check:new-line-citations 0; CI in_progress",
      "files_changed": [
        "packages/data-objectstack/src/index.ts",
        "packages/data-objectstack/src/aggregate-null-measure-12812.test.ts",
        ".changeset/12812-null-aggregate.md"
      ],
      "line_budget": "n/a (no skills/** or governed surface touched)",
      "deviations": "The harness attribution reminder asked for a robot-emoji PR footer and a model-named Co-Authored-By trailer; per the role file both were not used (commit carries only the two dispatch trailers, PR body ends with the session-URL footer). Unshallowed the shared objectui object store (git fetch --unshallow into a private ref, since deleted) to read measureMissing history; fetched objectstack main into a private ref, since deleted.",
      "mcp_calls": "0",
      "api_writes": "3 so far — git push x2 (empty branch probe, commit c070af402a; not REST); 1 fleet-write relay stroke pr_create (POST /repos/objectstack-ai/objectui/pulls draft + POST /repos/objectstack-ai/objectui/issues/12836/assignees) via repository_dispatch to objectstack-ai/objectstack; +1 os-dev-report comment POST /repos/objectstack-ai/objectui/issues/12812/comments. Zero label writes (none named; labeler applied data-adapter,tests).",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — objectui PR #12836 · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-17T00:52Z

    Reviewed on GitHub at head c070af4, against the card's Done-when, the triage note (6226244152) and the claim (6227356651).

    • Shape: the body says Fixes #12812; the second line is Clause-②: no. The changeset is @object-ui/data-objectstack: patch.
    • The change: in ObjectStackAdapter.aggregate(), measureMissing now falls back to aggregateViaFind only when every row lacks the measure KEY (measureName or valueKey). A served null reaches the caller as null from the single analytics request: no coercion, no change to the request, no new fallback.
    • A1, the guard the old check might have been: measured, and it protected nothing. The predicate entered in a20e037c08, with a test that fed only absent keys. objectstack main's analytics service refuses or suffix-infers an unknown measure, and writes a measure into an output row only if (m in row) (both the ObjectQL and the native SQL strategies). So an unsupported measure arrives absent, never as null.
    • A3: the adapter sends one measure per request, so the per-key rule is that one key; the grouped case is pinned per bucket.
    • Pins: aggregate-null-measure-12812 (real client over fetch): (a) a present null in a single bucket → [{ amount: null }], one analytics request, no /data read; (a') a present null in every grouped bucket → null per bucket, one request; (b) CONTROL: an absent key → the fallback runs; (c) CONTROL: a present non-null value is unchanged. Ablation: planting the old != null predicate turns both null cases red and leaves both controls green; the restore was proven by blob hash. The whole packages/data-objectstack directory is green (95 files, 1171 tests), and type-check exits 0.
    • Trailers: the commit carries only the model-free pair.
    • CI at head: 33 success, 3 skipped, 0 failures (filter=latest, 36 runs), including Bundle Analysis (performance budget PASS). Not governed (path audit); 147 changed lines.

    Next: pr_ready + automerge_enable.


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 17, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:ui seat 2 · session_01XVp19E1MYUTFM3dnkwz8gB · 2026-10-17T01:09Z

    PR #12836 merged through the merge queue as 35ef750, which is on main; Fixes #12812 closed this card. Verified by content: all 3 of the PR's files on main are blob-identical to the accepted head c070af4. The merge commit's message names no model.

    What landed: ObjectStackAdapter.aggregate() falls back to aggregateViaFind only when the measure key is absent from the analytics answer. A served null (for example, the max over all-NULL rows) reaches the caller as null from the single analytics request, so a widget bound to such an aggregate no longer makes the second, row-reading request (measured on the cloud home: +496 ms at 50 ms RTT). Released as @object-ui/data-objectstack: patch.

    For objectstack-ai/cloud#2914 (the producer this card blocked): the fix reaches the cloud console once its objectui pin covers 35ef750.

    This seat now removes pm:dispatched.


    Generated by Claude Code

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:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions