Skip to content

analytics: '*' runs only under count, but a cube measure's or dimension's sql and a dataset measure's field admit it under any aggregate — a summed '*' answers 500 at the dataset door (split from #21000) #21409

Closed

Description

@objectstack-fleet

Split from #21000 by the triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U, answering the pm:retriage question 5952467081 with B. ⛔ Not a claim.

Grade: bug · priority:p2 · domain:spec · area:reports · pm:queue.

Why a card of its own

#21000 retires three meaningless enum values. It is p3, with the full retirement kit. This is a measured defect of class (a): an authoring shape the contract admits answers a server fault at a public door.

The positions (read at main)

  1. A cube measure's sql, under any aggregate other than count.
  2. A cube dimension's sql.
  3. A dataset measure's field (PR feat(spec)!: an analytics dataset dimension's and measure's field is a column reference (#21220) #21240 narrowed it to a column reference, '*' included, under any aggregate), under any aggregate other than count.

A dataset dimension's field already refuses '*' (PR #21240), so it is the control.

Direction

  • '*' is admitted only where a count consumes it. Elsewhere it is refused at the authoring parse, naming the field and prescribing either count or a column. That is one cross-field rule per position, sharing one predicate. ⛔ No second spelling of the rule per position.
  • The refinement is not expressible in the published JSON Schema, so it is ledgered as a dropped-refinement gap, the way the gate requires.
  • packages/spec/liveness/analytics_cube.json's note, which points the count-only boundary at spec+service-analytics: retire the cube metric types number / string / boolean — they existed to carry a SQL expression, which #20943 retired from a member's sql #21000, is re-pointed here.
  • It is a narrowing of a published accept set: a BREAKING minor, Clause-②: no (narrowing). No producer is known to send such a shape. Census the repository's examples and objectui's mirror first.

Pins

  • Each position refuses its non-count '*' at parse.
  • count over '*' is still admitted and still runs, on both strategies.
  • The measured door cell answers 400, not 500.

Serial

#21000 (the enum retirement) and #21365 (in flight, AnalyticsQuerySchema) edit the same analytics.zod.ts. Whichever lands later merges main.

Activity

  1. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_01UtnxvdiN376GF3sgXwAw4d
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-21409-star-count-only
    Worktree: objectstack-issue-21409
    Domain: domain:spec
    Seat: domain:spec#1 (seat post #6017)
    File surface: on origin/main ceb4a939b4. packages/spec: one shared predicate (beside src/data/analytics-column-reference.ts) and the three cross-field refinements it backs. They are MetricSchema's sql under a non-count aggregate and DimensionSchema's sql (src/data/analytics.zod.ts), and DatasetMeasureSchema's field under a non-count aggregate (src/ui/dataset.zod.ts), with their tests. Also the dropped-refinements.baseline.json gap entries the gate requires, the liveness/analytics_cube.json note re-pointed here (and liveness/dataset.json if it names the boundary), one ADR-0087 semantic entry with its generated migrations/registry.ts region, the regenerated reference pages, and one @objectstack/spec changeset. Declared cross-lane test-only surface: one door cell pinning the measured 500 → 400 (service-analytics or rest tests). ⛔ No strategy code edit. Stop on breach; explain in the report.
    Container & model: M, mode:subagent, model: opus (default build tier; packages/spec/src/** non-test, so the contract review runs at CONTRACT_REVIEW_TIER through an isolated subagent)
    Clause-②: no (narrowing) per triage's card; the dev confirms the arm with check-widening-tells and switches to yes (narrowing) only if an export-listing row is added
    Thread-read: none
    Serial constraints cleared: at 2026-10-02T13:55Z, PR #21413 (#20274, spec seat 2) edits dropped-refinements.baseline.json and migrations/registry.ts, and this seat's #21293 (in flight) adds a registry.ts entry. Each generated region merges through scripts/pm/os-regen-merge.sh by whichever lands later. #21000 (pm:queue) and #21365 (domain:services) also edit analytics.zod.ts in other regions; whichever lands later merges main. This seat's PR #21416 (#21082) deliberately leaves '*' unrefused in lint.

  2. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21409,
      "status": "done",
      "branch": "claude/issue-21409-star-count-only",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21431",
      "session": "session_01UtnxvdiN376GF3sgXwAw4d — the dispatching PM session's id (subagent run; Claude-Session trailer on every non-merge commit)",
      "premise_still_valid": true,
      "summary": "The analytics row wildcard '*' is now admitted only where a count consumes it. Cube measure MetricSchema.sql and dataset measure DatasetMeasureSchema.field refuse '*' under any aggregate but count (code custom, at sql / field) through ONE shared predicate, rowWildcardOutsideCount, plus one shared refusal builder, both in the non-public packages/spec/src/data/analytics-column-reference.ts and called from a superRefine on each measure schema. Cube dimension DimensionSchema.sql takes ANALYTICS_COLUMN_PATH (code invalid_format), the dataset dimension's own pattern. That is a flagged reading: a dimension has no aggregate, so its rule cannot be cross-field, and Zone 2 item 2 said to follow the control's spelling, which is a pattern. The measured door cell (dataset sum over '*' at POST /api/v1/analytics/dataset/query) answered 500 DATABASE_ERROR on both strategies at the base build and answers 400 VALIDATION_FAILED at measures.N.field after the fix. count over '*' still answers the row count on both strategies. Kit: dropped-refinement ledger rows (data/Metric and ui/DatasetMeasure roots plus 10 embedded sites, measured 217 / 652); one D3 entry, analytics-row-wildcard-outside-count-refused (registry regenerated, no D2, no retired-key row, no STEP18 fragment); liveness analytics_cube measures.sql / dimensions.sql re-pointed from #21000 to here, and dataset measures.field noted; ui/dataset reference regenerated; changeset minor + BREAKING + (narrowing) + registered marker. Runtime and service-analytics strategy code untouched. Census: zero producers in this repo or objectui at the pin.",
      "tests": "Final union at HEAD 60644d73d9 (after the main merge): pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 gave 'Test Files 601 passed (601) / Tests 17647 passed | 1 todo'; pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/analytics-dataset-row-wildcard-door.test.ts src/analytics-16019-driver-declared-fault.test.ts gave 'Tests 42 passed (42)'. Before the merge, at 934b70a2db: rest --project local 'Test Files 256 passed (256) / Tests 4848 passed | 322 skipped'; service-analytics 'Test Files 168 passed (168) / Tests 3794 passed | 89 skipped'; spec repo-project subset of 5 files '131 passed'; spec + rest typecheck exit 0. The whole spec repo project is NOT MEASURED (one run exceeded the 570 s cap; declared to CI). Reverse reading: the door test against the BASE spec dist (ceb4a939b4, dist grep-verified free of the predicate) gave 'Tests 20 failed | 4 passed (24)'. The inline cells answered '{\"error\":\"Internal server error\",\"code\":\"DATABASE_ERROR\"}: expected 500 to be 400' for sum/avg/min/max/count_distinct over '*' on native and ObjectQL; the saved dataset with only its count selected answered 200; the 4 count controls were green. The saved-wildcard-selected cells were added later, so their base answer is NOT MEASURED. Ablation, from the committed fix through scripts/ablation-replace.mjs: rowWildcardOutsideCount made to return false (anchor x1 to x0, marker x0 to x1, blob 2bb692602dc8 to 4bdfba658269). The new spec file went '19 failed | 12 passed (31)': predicate, positions 1 and 3, and door cases red; position 2 pattern, controls, JSON-Schema and D3 pins green, as predicted. Restored via git checkout HEAD: blob == HEAD blob and git diff HEAD empty, under trap EXIT INT TERM. The spec suite imports source by relative path, so there is no dist on its resolution path. Read-path probe: computeMetadataDiagnostics on the built spec reads a stored dataset back as valid:false at measures.1.field/custom, and a stored cube at measures.total.sql/custom plus dimensions.everything.sql/invalid_format.",
      "gates": "dispatch-gates --repo objectstack-ai/objectstack --commands (no paths, merge base 39a912ea7, tree 60644d73d9) derived 115 families, all run. --ran reconciliation (each line recorded with its exit code): 115 derived, 113 run green, 2 NOT MEASURED (exit 3 prerequisite), 0 UNRUN. NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: needs every package's dist (whole-repo build). NOT MEASURED: pnpm check:type-check-debt, reason: its --re-measure builds the ledgered closure itself, unlocked, and that build passed the 300 s per-gate cap (killed, exit 124, gate exit 3). check:skill-examples first exited 3 (client-react unbuilt); after building client + client-react it exited 0. pnpm --filter @objectstack/spec check:generated: all 15 artifacts green after gen:docs (only content/docs/references was stale). check-widening-tells --declaration no --diff (merge-base diff): exit 0, no tell; 2 stated silences on the imported refusal factory. ESLint, narrowed and measured: the config population **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} minus NEVER_LINTED covers all 8 changed .ts files; --format json gave 8 files, 0 errors, 0 warnings; the config never enables type-aware linting, so untouched files' verdicts cannot move. Repo-wide pnpm lint is CI's. CI on PR #21431: in_progress (not awaited).",
      "line_budget": "n/a — no skills/** or line-ratcheted ledger touched",
      "files_changed": [
        ".changeset/21409-analytics-row-wildcard-count-only.md",
        "content/docs/references/ui/dataset.mdx",
        "packages/rest/src/analytics-dataset-row-wildcard-door.test.ts",
        "packages/spec/dropped-refinements.baseline.json",
        "packages/spec/liveness/analytics_cube.json",
        "packages/spec/liveness/dataset.json",
        "packages/spec/src/data/analytics-column-reference.ts",
        "packages/spec/src/data/analytics-row-wildcard-count-only.test.ts",
        "packages/spec/src/data/analytics.zod.ts",
        "packages/spec/src/data/cube-member-sql-column-reference.test.ts",
        "packages/spec/src/migrations/entries/semantic/18.analytics-row-wildcard-outside-count-refused.ts",
        "packages/spec/src/migrations/registry.ts",
        "packages/spec/src/ui/dataset.zod.ts"
      ],
      "deviations": [
        "Position 2 (cube dimension sql) is spelled as the ANALYTICS_COLUMN_PATH pattern, not a cross-field refinement. Zone 1's 'one cross-field rule per position' cannot hold for a slot with no aggregate. Zone 2 item 2 (follow the control's spelling) points to the pattern, which the published JSON Schema carries, so no ledger row is needed for it. The measure positions share the one predicate.",
        "Zone 2 item 2 partly falsified: the control (PR #21240) is a pattern, not a refinement.",
        "No STEP18_RATIONALE fragment: optional, and it would be a hand edit to registry.ts outside the claimed generated region.",
        "verifiedAt bumped to 2026-10-02 on the three liveness rows whose notes changed (evidence anchors re-read).",
        "The main merge commit 60644d73d9 carries git's default merge message without the Claude-Session / Co-authored-by pair; not amended after push.",
        "dropped-refinements.baseline.json conflicted on its measured counts at the main merge. Resolved by taking main's file (MERGE_HEAD blob) and re-declaring this branch's 12 sites with counts recomputed from the entries, never a text merge; gen:schema then validated the ledger against the tree.",
        "A throwaway read-path probe file was placed briefly in packages/metadata-protocol so its imports resolved, then deleted; git status was verified clean before the next commit.",
        "Two gates NOT MEASURED (whole-repo build prerequisites), and the whole spec repo project NOT MEASURED (subset run), all declared to CI."
      ],
      "mcp_calls": "0",
      "api_writes": "3 — all through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft, #21431, body read back identical, 14670 bytes); (2) label-write --assign os-sales → POST /repos/objectstack-ai/objectstack/issues/21431/assignees (read back: assignee os-sales, label size/xl from another actor left untouched); (3) this os-dev-report comment → POST /repos/objectstack-ai/objectstack/issues/21409/comments. git push is not a REST write.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: domain:services lane (no carrier named) · noted, not filed — service-analytics inferMeasure turns a caller-named measure with an empty prefix (_sum, _avg, _min, _max, _count_distinct) into type sum etc. with sql '*' (key.slice(0, -suffix.length) || '*'), and the caller-measure gate admits inferredSql === '*'; read from code only, NOT MEASURED at a door (read-only inference), outside this card's no-strategy-edit surface · dedupe words: inferMeasure, caller-named measure, empty prefix, row wildcard, SUM(*)",
        "carrier: none named · noted, not filed — a derived dataset measure's field is read by nothing (the compiler skips it), so any column value there parses inert; this card refuses only '*' there; no producer found (observation) · dedupe words: derived measure, field ignored, dataset compiler, inert key"
      ]
    }

    Generated by Claude Code

  3. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21409,
      "status": "done",
      "round": "patch round 2 (contract review 5956317829 ②, plus the escalated door probe)",
      "branch": "claude/issue-21409-star-count-only",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21431",
      "head": "b79d1be80d2a37297f27f7c84716ca4b7001fcfc (was 60644d73d9; one commit, .changeset/21409-analytics-row-wildcard-count-only.md only, 8 insertions / 4 deletions)",
      "session": "session_01UtnxvdiN376GF3sgXwAw4d — the dispatching PM session id (subagent run)",
      "premise_still_valid": true,
      "summary": "The changeset's dataset FROM/TO block attributed 'parsed' and the ZodError to defineDataset, which is an identity function. It now names the doors that parse: DatasetSchema.parse (custom at measures.0.field), defineStack (422 STACK_SCHEMA_INVALID at datasets.N.measures.0.field) and the dataset query route (400 VALIDATION_FAILED). FROM is a plain dataset literal. Both door outcomes for this exact literal were re-measured from source before the commit: DatasetSchema issues [[custom,[measures,0,field]]]; defineStack STACK_SCHEMA_INVALID 422 at [datasets,0,measures,0,field]. The cube half (defineCube parses) is unchanged. Every other changeset sentence was re-read, and no other defineDataset attribution exists there, in the PR body (live body grep: 0 hits) or in the D3 entry, so the PR body was not touched (no pr_update). The escalated finding A is now measured at a public door: a caller-named '_sum' / '_avg' measure answers 500 DATABASE_ERROR at POST /api/v1/analytics/query on both strategies, on an ad-hoc and on an authored cube, while the named-column controls answer 200 (table below).",
      "corrected_sentences": [
        {
          "file": ".changeset/21409-analytics-row-wildcard-count-only.md (FROM → TO block, dataset half)",
          "before": "FROM  defineDataset({ name: 'deal_metrics', label: 'Deal Metrics', object: 'deal',\n        dimensions: [{ name: 'stage', field: 'stage' }],\n        measures: [{ name: 'deals', aggregate: 'sum', field: '*' }] })\n      -> parsed; every query selecting `deals` answered 500 DATABASE_ERROR\nTO    -> ZodError at measures.0.field (custom):\n         `measures[].field` is the row wildcard `'*'` under `aggregate: 'sum'`. …",
          "after": "FROM  { name: 'deal_metrics', label: 'Deal Metrics', object: 'deal',\n        dimensions: [{ name: 'stage', field: 'stage' }],\n        measures: [{ name: 'deals', aggregate: 'sum', field: '*' }] }\n      -> DatasetSchema.parse accepted it; a dataset query selecting `deals`\n         answered 500 DATABASE_ERROR\nTO    -> DatasetSchema.parse throws a ZodError at measures.0.field (custom):\n         `measures[].field` is the row wildcard `'*'` under `aggregate: 'sum'`. …\n         defineStack({ datasets }) refuses it at datasets.N.measures.0.field (422\n         STACK_SCHEMA_INVALID), and POST /api/v1/analytics/dataset/query answers\n         400 VALIDATION_FAILED for an inline or a saved copy"
        }
      ],
      "merge": "No main merge: git merge-tree --write-tree HEAD origin/main (origin/main 3bddd4a6b5) exited 0, tree 8d09b677bf, no conflict.",
      "gates": [
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
        "node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
        "node scripts/check-empty-changeset.mjs --self-test :: exit 0",
        "pnpm check:changeset-gate-self-tests :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectui-changeset :: exit 0",
        "pnpm check:pm-changeset-deadline-census :: exit 0",
        "Verdict lines: check-adr-0087-registration --base origin/main: 1 declared-breaking changeset, [BREAKING+bang+clause-②-narrowing] registered analytics-row-wildcard-outside-count-refused; check-changeset-no-major: no major bump; check-empty-changeset: no empty-frontmatter changeset introduced, no merge-base changeset modified or deleted.",
        "Derived by dispatch-gates --repo objectstack-ai/objectstack --commands at b79d1be80d (no paths); the 12 lines above are every changeset-driven family it named (the ADR-0087 registration, no-major, empty-changeset, closing-keyword parity, changeset-gate self-tests, nul-bytes, objectui-changeset, changeset-deadline census), each exit captured before any pipe."
      ],
      "probe": {
        "route": "POST /api/v1/analytics/query, served by the runtime dispatcher (createDispatcherPlugin), not by @objectstack/rest. The rest package mounts only /analytics/dataset/query, so the probe sat beside the existing runtime door test packages/runtime/src/analytics-json-dimension-door.test.ts and copied its harness: a real ObjectQL engine, a better-sqlite3 SqlDriver, AnalyticsServicePlugin composition, read counters. No new harness.",
        "file": "packages/runtime/src/zz-probe-21409-inferred-wildcard.test.ts (throwaway; deleted, never staged or committed)",
        "fixture": "object probe_wildcard_ledger {category text, amount number}, 3 rows, amounts 100/400/900; authored cube probe_wildcard_cube {measures: count over '*', amount_total sum over amount; dimension category}",
        "table_columns": "strategy | cell | request body | status | reads | response body (verbatim)",
        "rows": [
          "native | adhoc _sum | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"_sum\"]} | status 500 | rawSql+1 aggregate+0 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "native | adhoc _avg | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"_avg\"]} | status 500 | rawSql+1 aggregate+0 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "native | adhoc amount_sum (control) | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"amount_sum\"]} | status 200 | rawSql+1 aggregate+0 | {\"success\":true,\"data\":{\"rows\":[{\"amount_sum\":1400}],\"fields\":[{\"name\":\"amount_sum\",\"type\":\"number\"}]}}",
          "native | authored _sum | {\"cube\":\"probe_wildcard_cube\",\"measures\":[\"_sum\"]} | status 500 | rawSql+1 aggregate+0 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "native | authored amount_total (control) | {\"cube\":\"probe_wildcard_cube\",\"measures\":[\"amount_total\"]} | status 200 | rawSql+1 aggregate+0 | {\"success\":true,\"data\":{\"rows\":[{\"amount_total\":1400}],\"fields\":[{\"name\":\"amount_total\",\"type\":\"number\"}]}}",
          "objectql | adhoc _sum | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"_sum\"]} | status 500 | rawSql+0 aggregate+1 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "objectql | adhoc _avg | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"_avg\"]} | status 500 | rawSql+0 aggregate+1 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "objectql | adhoc amount_sum (control) | {\"cube\":\"probe_wildcard_ledger\",\"measures\":[\"amount_sum\"]} | status 200 | rawSql+0 aggregate+1 | {\"success\":true,\"data\":{\"rows\":[{\"amount_sum\":1400}],\"fields\":[{\"name\":\"amount_sum\",\"type\":\"number\"}]}}",
          "objectql | authored _sum | {\"cube\":\"probe_wildcard_cube\",\"measures\":[\"_sum\"]} | status 500 | rawSql+0 aggregate+1 | {\"success\":false,\"error\":{\"code\":\"DATABASE_ERROR\",\"message\":\"Internal server error\",\"httpStatus\":500}}",
          "objectql | authored amount_total (control) | {\"cube\":\"probe_wildcard_cube\",\"measures\":[\"amount_total\"]} | status 200 | rawSql+0 aggregate+1 | {\"success\":true,\"data\":{\"rows\":[{\"amount_total\":1400}],\"fields\":[{\"name\":\"amount_total\",\"type\":\"number\"}]}}"
        ],
        "reading": "Every caller-named '_sum' / '_avg' cell answered 500 DATABASE_ERROR after a statement reached the engine (rawSql+1 on native SQL, aggregate+1 on ObjectQL): the minted SUM(*) / AVG(*) reached the database. That holds on an ad-hoc cube (cube = the object name) and on an authored cube that does not declare the member. Both named-column controls answered 200 with the true sum, 1400. This is the one measured wrong answer at a public door the seat asked for.",
        "clean_status_proof": "After deleting the probe: git status --porcelain printed 0 lines; HEAD b79d1be80d2a37297f27f7c84716ca4b7001fcfc equals origin/claude/issue-21409-star-count-only (git ls-remote), and HEAD's only file is the changeset. Order, stated: the changeset commit was made and pushed BEFORE the probe was written, so the probe never existed at any commit; the clean status was proven after its deletion, with no commit since."
      },
      "tests": "Changeset-only round: no code or test touched. Probe run: pnpm --filter @objectstack/runtime exec vitest run --maxWorkers=2 src/zz-probe-21409-inferred-wildcard.test.ts gave Tests 10 passed (10). These are observation cells with no assertions on the status; the readings are the PROBE lines. The runtime dependency closure was rebuilt by turbo (29/29 cached); the spec dist was grep-verified to carry the predicate.",
      "deviations": [
        "The probe lives beside the runtime door test, not the rest one: POST /api/v1/analytics/query is the runtime dispatcher route, and @objectstack/rest does not mount it.",
        "The changeset commit came before the probe rather than after; the probe was never committed, and the clean status is proven after its deletion."
      ],
      "mcp_calls": "0",
      "api_writes": "1 this round — this os-dev-report comment, through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos/objectstack-ai/objectstack/issues/21409/comments as objectstack-fleet[bot]). No pr_update: the PR body had no defineDataset attribution. git push is not a REST write. Reads only otherwise: the review comment and the PR body, by REST GET.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: POST /api/v1/analytics/query answers 500 DATABASE_ERROR to a caller-named measure '_sum' / '_avg' on both strategies, on an ad-hoc and on an authored cube; the named-column controls answer 200 (probe table above) · evidence: service-analytics inferMeasure mints { type: 'sum', sql: '*' } via key.slice(0, -suffix.length) || '*'; assertCallerMembersResolvable admits inferredSql === '*'; the strategies emit the operand verbatim · dedupe words: inferMeasure, caller-named measure, empty prefix, row wildcard, SUM(*) 500",
        "carrier: seat's filing decision (the review escalated it as a candidate spec card) · noted, not filed — a derived dataset measure's field (and aggregate) is read by nothing (dataset-compiler continues before m.field), so any value parses inert · dedupe words: derived measure, field ignored, aggregate ignored, dataset compiler"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 21409,
      "status": "done",
      "round": "patch round 3 (re-review 5956873511 ②: one changeset sentence; plus the owed regen merge of main)",
      "branch": "claude/issue-21409-star-count-only",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/21431",
      "head": "30437505b8b1cbcff045b29fcc3d6eaccdc9e64c",
      "commits_since_b79d1be80d": [
        "fce0c9a042 chore(changeset): os migrate meta lists the entry as a manual change; it rewrites nothing — .changeset/21409-analytics-row-wildcard-count-only.md only, +3 / -1",
        "30437505b8 Merge remote-tracking branch 'origin/main' into claude/issue-21409-star-count-only — origin/main 3a6d92f78b through scripts/pm/os-regen-merge.sh; pure merge, no regeneration commit needed (step 4 regenerated byte-identically)"
      ],
      "session": "session_01UtnxvdiN376GF3sgXwAw4d — the dispatching PM session id (subagent run)",
      "premise_still_valid": true,
      "summary": "The one-line-fix paragraph's last sentence claimed os migrate meta lists nothing for this narrowing. It lists the D3 entry as a manual change (chain.ts maps every step.semantic entry to a MigrationTodo; meta.ts prints them under 'manual change(s) require your judgment'), and only rewrites nothing. Respelled in its own commit. The D3 entry ('so the sweep is mechanical — parse each document') and the PR body ('has no mechanical rewrite') make no such tool claim and are true, so neither was changed and there was no pr_update. Then origin/main was merged through os-regen-merge.sh: no conflict; registry.ts auto-merged and gen:migration-registry reproduced it byte-identically; check:generated passed 15/15 on the rebuilt spec; every changeset family exited 0.",
      "corrected_sentences": [
        {
          "file": ".changeset/21409-analytics-row-wildcard-count-only.md (the one-line-fix paragraph, last sentence)",
          "before": "There is no mechanical rewrite, so `os migrate meta` lists nothing for it.",
          "after": "There is no mechanical rewrite: `os migrate meta` rewrites nothing for it, and\nlists the entry `analytics-row-wildcard-outside-count-refused` as a manual\nchange that requires your judgment."
        }
      ],
      "merge_readings": {
        "tool": "bash scripts/pm/os-regen-merge.sh: exit 0. Step 1 merged origin/main 3a6d92f78b at merge base 39a912ea73, 45 files, with no conflict; packages/spec/src/migrations/registry.ts auto-merged. Step 2 took main's side of the generated artifacts main moved, and kept the branch's content/docs/references/ui/dataset.mdx (branch-only change). Step 3: the merge left nothing further to commit.",
        "registry_byte_identity": "merged blob a49da25ca28fe75334d02ee7243cd82f6e8b31b4 == HEAD blob a49da25ca28f…; after pnpm --filter @objectstack/spec gen:migration-registry the blob is still a49da25ca28f… and git diff HEAD on registry.ts is 0 lines. Byte-identical, so no regeneration commit.",
        "semantic_entry_counts": "before the merge (fce0c9a042, gen:migration-registry): 347 semantic, 244 retired-key, 212 retired-def, entries/semantic 347 files. After the merge (30437505b8): 348 semantic, 244 retired-key, 212 retired-def, entries/semantic 348 files. The +1 is main's dashboard-widget-single-series-multi-measure-refused (#21293 via 32d5769080 / #21425): present once at HEAD and once on origin/main. This branch's analytics-row-wildcard-outside-count-refused is present once at HEAD; #21413's agent-memory-store-retired-and-limits-required is still present.",
        "rebuild": "pnpm --filter @objectstack/spec build (gen:schema validated dropped-refinements.baseline.json against the merged tree; main did not touch the ledger) then check:generated, both under the verify lock: VERDICT command-exit 0, all 15 artifacts green (check:migration-registry, check:spec-changes, check:upgrade-guide, check:authorable-surface, check:api-surface, check:docs, check:liveness among them)."
      },
      "gates": [
        "pnpm --filter @objectstack/spec check:migration-registry :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
        "node scripts/check-changeset-no-major.mjs --self-test :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
        "node scripts/check-empty-changeset.mjs --self-test :: exit 0",
        "pnpm check:changeset-gate-self-tests :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:objectui-changeset :: exit 0",
        "pnpm check:pm-changeset-deadline-census :: exit 0",
        "pnpm --filter @objectstack/spec build && pnpm --filter @objectstack/spec check:generated :: exit 0 (15/15 artifacts green)",
        "Verdict lines: check:migration-registry: 'src/migrations/registry.ts is current (348 semantic, 244 retired-key, 212 retired-def)'; check-adr-0087-registration --base origin/main: '1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.'",
        "The changeset families are every one dispatch-gates --repo objectstack-ai/objectstack --commands named at 30437505b8 (no paths); each exit was captured before any pipe."
      ],
      "tests": "Spec pins on the merged tree: vitest --project local on analytics-row-wildcard-count-only, cube-member-sql-column-reference and dataset-field-column-reference gave Test Files 3 passed (3), Tests 57 passed (57). The rest door test was not re-run locally on the merged tree; main's merge brought native-sql-strategy changes (#21376), so CI Test Core measures it. The Test Core (4/6) red on b79d1be80d is the known #21428 flake (import-template-route.test.ts timing out at 5000 ms), recorded by the coordinator in 5956763546; not re-run or touched here.",
      "deviations": [
        "The merge commit 30437505b8 again carries git's default merge message without the trailer pair. It was not amended, to avoid re-entering the pre-commit os-regen deferral on a merge commit; the repository squash-merges with the PR body's message."
      ],
      "mcp_calls": "0",
      "api_writes": "1 this round — this os-dev-report comment through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches executing POST /repos/objectstack-ai/objectstack/issues/21409/comments as objectstack-fleet[bot]). No pr_update: the PR body carries no os-migrate-meta claim. git push (once) is not a REST write. Reads: the re-review comment and the PR body, by REST GET.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT — PR #21431 @ 30437505b8

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5953981594 · 2026-10-02T17:19Z

    Landing: ready, then auto-merge through the merge queue.

  6. objectstack-fleet commented on Oct 2, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21431 → b79301000c

    domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5953981594 · 2026-10-02T17:48Z

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsbugSomething isn't workingdomain:specpriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions