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 dotted dimension whose join the dataset does not back gets three different answers from native, ObjectQL and the draft preview #28258
Filing gate: ② an out-of-scope finding measured by the #28201 card thread on main @ 86cf7cef (real SQLite). Filed by domain:server seat 3. ⛔ Not a claim. No grade: triage sets it.
Measured
A dimension spelled through a join the dataset does not back (<object>.bogus):
Face
Answer
native strategy
refused DATASET_INVALID / 400 (join not backed)
ObjectQL strategy
refused INVALID_FIELD / 400 (the group-by names the object, not a field)
draft preview (previewDrafts: true)
served one null group
Wanted (route is triage's)
A ruling first: the two live faces already disagree, so it has to be decided which refusal (code, status, message) is the contract before the preview is brought to it. Then all three faces give that one answer, pinned three ways. Expected Clause-②: no (narrowing) for the preview. Whether one live face changes its code is part of the ruling.
Dedupe
Read the open titles for preview, dotted, join, dimension: only #28201 (undeclared plain dimension, in flight), which does not cover a dotted spelling. New.
Read fresh, and measured. A dotted dimension through an unbacked join gets three answers: DATASET_INVALID, INVALID_FIELD, and a null group. It is an edge spelling, so the grade is p3.
Changing the native face's published code is a wire change. The thread proposes it in the PR with Clause-② stated, and the contract review judges it. If the review finds the code has consumers, it comes back here as a decision card.
Claim: PM loop round 1 · card thread · 2026-11-01T04:46Z
Session: session_01R1q2xdtYgU6FKrZ7LgjPCC (the seat); the card thread is session_01FEkQ5pG4c8xoSCyPjcztJc
Account: os-project-manager (the seat's linked user; the card's assignee)
Branch: claude/project-thread-h8f0nq (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 maintainer's 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」 (2026-11-01T03:46Z); triage graded it p3 and left it out of pm:queue under 「低优先级的先不考虑」, which that line now lifts. Seat ruling on triage 6532297703's lean: the contract is INVALID_FIELD / 400 naming the member, on all three faces (the native strategy, the ObjectQL strategy and the draft preview), the shape the preview-parity family (#28172, #28201, #28257) converges on. The native face's code change for this one case is measured for consumers first; a real consumer means needs_decision and only the preview half lands.
File surface: packages/services/service-analytics/src/** (the native strategy's join check in the dataset compiler, the ObjectQL strategy, the draft-preview face), a shared judgement where the code allows, tests on real SQLite, and changesets. ⛔ DATASET_INVALID stays for every other reason it is raised. ⛔ No packages/spec/**, ⛔ no ui/**. Stop on breach and explain in the report.
Container & model: M, mode:thread, model and effort as the thread was opened
Clause-②: no (narrowing) — expected: the preview stops serving a null group, and the native face's code for this case moves to INVALID_FIELD; the dev measures and declares, the contract review is owed at CONTRACT_REVIEW_TIER, and the dogfood battery runs in full on the final head
Metadata-protocol: none (expected)
Responsibility: the code is the analytics dimension resolution through a dataset's joins · the platform path is dataset query and draft preview · reached by an author who spells a dimension through a join the dataset does not back: three faces give three answers, and the preview silently serves a null group
Thread-read: 6532297703
Premise: holds (read 2026-11-01T04:46Z at origin/maineeec88d141: the card's measurement on 86cf7cef stands; no PR since has touched the dotted-dimension path)
Serial constraints cleared: no open seat-1 card touches service-analytics; the dev lists open PRs touching its files before pr_create and runs git merge-tree against each
Resume-from: none — fresh from origin/main
State: pm:dispatched, assigned to os-project-manager, recorded by this claim.
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
os-dev-report
{
"issue": 28258,
"status": "done",
"branch": "claude/project-thread-h8f0nq",
"pr": "#28553",
"session": "session_01FEkQ5pG4c8xoSCyPjcztJc",
"premise_still_valid": true,
"summary": "One gate, assertDimensionJoinsBacked in analytics-service.ts, refuses a selected dimension spelled through a relationship path the compiled dataset does not declare: INVALID_FIELD / 400 via invalidMemberError (member, param dimensions, cube), one message naming the member, the first unbacked path hop by hop (new unbackedRelationshipPath beside joinAlias in dataset-compiler.ts), the declared paths and the valid dimensions. It is asked in callCtx after the object, body, internal-field and field-level verdicts (so #20933's 403 for an unreadable related object holds; a first cut in ensureCube broke that pin and was moved) and at the same point in the draft preview's gate list, so the native, ObjectQL and preview faces answer byte-equal messages. Native moves from DATASET_INVALID to INVALID_FIELD for this case only; the native allowlist itself is untouched and still answers DATASET_INVALID for a runtimeFilter member and a hand-authored cube's hook. Also narrows the ObjectQL face, which SERVED a real lookup (account.name) the dataset did not include. Wire readers of DATASET_INVALID for this case measured: the only runtime reader, @object-ui/data-objectstack's queryDataset adapter, treats every named non-permission code identically (branch 5), so no real consumer; one rest test row that used a dotted dimension as its trigger now triggers the strategy allowlist with a runtimeFilter member instead (audit trail kept). Card's assignee was not touched; the PR has no assignees and no labels (the order named none, and the diff carries a changeset, so no skip-changeset).",
"tests": "Red first on origin/main eeec88d (real SQLite, the #28201 harness): new dotted-dimension-unbacked-join-parity.test.ts Tests 4 failed | 5 passed (9), the four refusal pins red because the preview served, controls green; a probe recorded per face OBJECT.bogus native DATASET_INVALID / objectql INVALID_FIELD (driver) / preview one null group; account.name unbacked native DATASET_INVALID / objectql SERVED / preview null group. Green: 9/9, with the three preview-parity siblings 37/37. Ablation: node scripts/ablation-replace.mjs --delete of the preview's assertDimensionJoinsBacked call, anchor 1 -> 0, blob 9b761f324397 -> 5c8914397b7f, Tests 4 failed | 5 passed (9), restore blob == HEAD and git diff HEAD empty (suite imports src, no build between). pnpm --filter @objectstack/service-analytics exec vitest run: Test Files 201 passed (201), Tests 4823 passed | 282 skipped. pnpm --filter @objectstack/rest exec vitest run src/analytics: Test Files 23 passed (23), Tests 298 passed | 22 skipped. service-analytics typecheck exit 0. eslint --no-inline-config on service-analytics/src and the touched rest test exit 0.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 62 families on 9b93223, each run with stdin from /dev/null, exit captured before any pipe; --ran: 62 derived, 62 run, 0 NOT-MEASURED (derived zero, every line carries an exit code). check:dual-build-cjs-loads first exit 3 (prerequisite) -> full pnpm build --concurrency=2 exit 0 -> exit 0. check:type-check-debt exit 0. Hand gates: check-platform-checklist --changed-from origin/main exit 0, check-commit-card-trailers exit 0. Head 6001927 only respells two changeset placeholders; changeset-reading families re-run there, all exit 0 (adr-0087-registration, changeset-no-major, empty-changeset, changeset-gate-self-tests, nul-bytes, issue-citations, closing-keyword-parity, commit-card-trailers); derived list identical. CI families: in_progress at report time, the seat reads #28553's check-runs.",
"dogfood_battery": "9b932230a (code head; 6001927 changes changeset prose only) · os-verify-lock VERDICT command-exit 0 · Test Files 363 passed | 3 skipped (366) · Tests 2932 passed | 28 skipped (2960) · no failure",
"mcp_calls": "4 — mcp__github__issue_read x3 (card, comments twice), mcp__github__list_pull_requests x1; no MCP write",
"api_writes": "2 — repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches for pr_create (#28553), and this os-dev-report comment via post-stamped; plus 2 git pushes (not REST)",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: queryDataset previewDrafts true (pending seed draft), measured on eeec88d real SQLite · the preview joins nothing, so a BACKED dotted dimension (account.name with include account) and a dotted spelling resolving to a declared dimension (OBJECT.region, CUBE.region) serve one null group with the whole total, where both live strategies serve real groups · dedupe: preview dotted backed null group",
"class: a · reach: queryDataset previewDrafts true · a dotted timeDimensions entry (account.created_at, no include): both live faces INVALID_FIELD / 400, the preview serves one row without the time dimension (total 180) · dedupe: preview timeDimensions dotted dropped",
"class: a · reach: queryDataset runtimeFilter, measured all faces · a runtimeFilter member through an undeclared path (account.name): native DATASET_INVALID / 400, ObjectQL INVALID_FIELD / 400 (cross-object filter), preview [] · same family as this card, filter face · dedupe: runtimeFilter dotted unbacked three answers",
"class: a · reach: queryDataset native strategy · a declared path to a column the related object lacks (account.bogus, include account): native DATABASE_ERROR / 500, ObjectQL INVALID_FIELD / 400, preview one null group · dedupe: dotted dimension missing related column 500",
"carrier: none · noted in the PR's Acceptance notes, not measured: on the preview, a path both undeclared and through an object the caller may not read answers the field gate's verdict then this gate's 400, where live faces answer the object's 403 (the preview asks no admission for related objects because it reads none)"
]
}
PR #28553 merged at 2026-11-01T06:42Z by the merge queue as a2e75562f9 on main (Fixes #28258; contract review 6554203536 carried by Regen-provenance6554267229, ACCEPT 6554404948).
Landed: a dataset dimension through a relationship path the dataset does not declare is refused INVALID_FIELD / 400 on the native, ObjectQL and draft-preview faces, at queryDataset and at /analytics/query and /analytics/sql over a registered dataset cube.
Activity
objectstack-fleet commentedon Oct 30, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: 错的必须被响亮拒绝并给处方 | none | P③
Triage: p3 ·
domain:server·area:reports· 2026-10-30T19:58Z⛔ Not a claim.
DATASET_INVALID,INVALID_FIELD, and anullgroup. It is an edge spelling, so the grade is p3.INVALID_FIELD/ 400, naming the member. That is the shape the whole preview-parity family (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, analytics: the dataset draft preview serves a dimension the dataset does not declare, which the live strategies refuse INVALID_FIELD #28201 and analytics: the dataset draft preview serves a runtimeFilter on a field the dataset object does not have, which the live strategies refuse INVALID_FIELD #28257) converges on.Clause-②stated, and the contract review judges it. If the review finds the code has consumers, it comes back here as a decision card.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Claim: PM loop round 1 · card thread · 2026-11-01T04:46Z
Session:
session_01R1q2xdtYgU6FKrZ7LgjPCC(the seat); the card thread issession_01FEkQ5pG4c8xoSCyPjcztJcAccount:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-h8f0nq(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 maintainer's 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」 (2026-11-01T03:46Z); triage graded it p3 and left it out of
pm:queueunder 「低优先级的先不考虑」, which that line now lifts. Seat ruling on triage6532297703's lean: the contract isINVALID_FIELD/ 400 naming the member, on all three faces (the native strategy, the ObjectQL strategy and the draft preview), the shape the preview-parity family (#28172, #28201, #28257) converges on. The native face's code change for this one case is measured for consumers first; a real consumer meansneeds_decisionand only the preview half lands.File surface:
packages/services/service-analytics/src/**(the native strategy's join check in the dataset compiler, the ObjectQL strategy, the draft-preview face), a shared judgement where the code allows, tests on real SQLite, and changesets. ⛔DATASET_INVALIDstays for every other reason it is raised. ⛔ Nopackages/spec/**, ⛔ noui/**. Stop on breach and explain in the report.Container & model:
M,mode:thread, model and effort as the thread was openedClause-②: no (narrowing) — expected: the preview stops serving a
nullgroup, and the native face's code for this case moves toINVALID_FIELD; the dev measures and declares, the contract review is owed atCONTRACT_REVIEW_TIER, and the dogfood battery runs in full on the final headMetadata-protocol: none (expected)
Responsibility: the code is the analytics dimension resolution through a dataset's joins · the platform path is dataset query and draft preview · reached by an author who spells a dimension through a join the dataset does not back: three faces give three answers, and the preview silently serves a
nullgroupThread-read: 6532297703
Premise: holds (read 2026-11-01T04:46Z at
origin/maineeec88d141: the card's measurement on86cf7cefstands; no PR since has touched the dotted-dimension path)Serial constraints cleared: no open seat-1 card touches
service-analytics; the dev lists open PRs touching its files beforepr_createand runsgit merge-treeagainst eachResume-from: none — fresh from
origin/mainState:
pm:dispatched, assigned toos-project-manager, recorded by this claim.Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
os-dev-report
{
"issue": 28258,
"status": "done",
"branch": "claude/project-thread-h8f0nq",
"pr": "#28553",
"session": "session_01FEkQ5pG4c8xoSCyPjcztJc",
"premise_still_valid": true,
"summary": "One gate, assertDimensionJoinsBacked in analytics-service.ts, refuses a selected dimension spelled through a relationship path the compiled dataset does not declare: INVALID_FIELD / 400 via invalidMemberError (member, param dimensions, cube), one message naming the member, the first unbacked path hop by hop (new unbackedRelationshipPath beside joinAlias in dataset-compiler.ts), the declared paths and the valid dimensions. It is asked in callCtx after the object, body, internal-field and field-level verdicts (so #20933's 403 for an unreadable related object holds; a first cut in ensureCube broke that pin and was moved) and at the same point in the draft preview's gate list, so the native, ObjectQL and preview faces answer byte-equal messages. Native moves from DATASET_INVALID to INVALID_FIELD for this case only; the native allowlist itself is untouched and still answers DATASET_INVALID for a runtimeFilter member and a hand-authored cube's hook. Also narrows the ObjectQL face, which SERVED a real lookup (account.name) the dataset did not include. Wire readers of DATASET_INVALID for this case measured: the only runtime reader, @object-ui/data-objectstack's queryDataset adapter, treats every named non-permission code identically (branch 5), so no real consumer; one rest test row that used a dotted dimension as its trigger now triggers the strategy allowlist with a runtimeFilter member instead (audit trail kept). Card's assignee was not touched; the PR has no assignees and no labels (the order named none, and the diff carries a changeset, so no skip-changeset).",
"tests": "Red first on origin/main eeec88d (real SQLite, the #28201 harness): new dotted-dimension-unbacked-join-parity.test.ts Tests 4 failed | 5 passed (9), the four refusal pins red because the preview served, controls green; a probe recorded per face OBJECT.bogus native DATASET_INVALID / objectql INVALID_FIELD (driver) / preview one null group; account.name unbacked native DATASET_INVALID / objectql SERVED / preview null group. Green: 9/9, with the three preview-parity siblings 37/37. Ablation: node scripts/ablation-replace.mjs --delete of the preview's assertDimensionJoinsBacked call, anchor 1 -> 0, blob 9b761f324397 -> 5c8914397b7f, Tests 4 failed | 5 passed (9), restore blob == HEAD and git diff HEAD empty (suite imports src, no build between). pnpm --filter @objectstack/service-analytics exec vitest run: Test Files 201 passed (201), Tests 4823 passed | 282 skipped. pnpm --filter @objectstack/rest exec vitest run src/analytics: Test Files 23 passed (23), Tests 298 passed | 22 skipped. service-analytics typecheck exit 0. eslint --no-inline-config on service-analytics/src and the touched rest test exit 0.",
"gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 62 families on 9b93223, each run with stdin from /dev/null, exit captured before any pipe; --ran: 62 derived, 62 run, 0 NOT-MEASURED (derived zero, every line carries an exit code). check:dual-build-cjs-loads first exit 3 (prerequisite) -> full pnpm build --concurrency=2 exit 0 -> exit 0. check:type-check-debt exit 0. Hand gates: check-platform-checklist --changed-from origin/main exit 0, check-commit-card-trailers exit 0. Head 6001927 only respells two changeset placeholders; changeset-reading families re-run there, all exit 0 (adr-0087-registration, changeset-no-major, empty-changeset, changeset-gate-self-tests, nul-bytes, issue-citations, closing-keyword-parity, commit-card-trailers); derived list identical. CI families: in_progress at report time, the seat reads #28553's check-runs.",
"dogfood_battery": "9b932230a (code head; 6001927 changes changeset prose only) · os-verify-lock VERDICT command-exit 0 · Test Files 363 passed | 3 skipped (366) · Tests 2932 passed | 28 skipped (2960) · no failure",
"mcp_calls": "4 — mcp__github__issue_read x3 (card, comments twice), mcp__github__list_pull_requests x1; no MCP write",
"api_writes": "2 — repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches for pr_create (#28553), and this os-dev-report comment via post-stamped; plus 2 git pushes (not REST)",
"open_questions": [],
"out_of_scope_findings": [
"class: a · reach: queryDataset previewDrafts true (pending seed draft), measured on eeec88d real SQLite · the preview joins nothing, so a BACKED dotted dimension (account.name with include account) and a dotted spelling resolving to a declared dimension (OBJECT.region, CUBE.region) serve one null group with the whole total, where both live strategies serve real groups · dedupe: preview dotted backed null group",
"class: a · reach: queryDataset previewDrafts true · a dotted timeDimensions entry (account.created_at, no include): both live faces INVALID_FIELD / 400, the preview serves one row without the time dimension (total 180) · dedupe: preview timeDimensions dotted dropped",
"class: a · reach: queryDataset runtimeFilter, measured all faces · a runtimeFilter member through an undeclared path (account.name): native DATASET_INVALID / 400, ObjectQL INVALID_FIELD / 400 (cross-object filter), preview [] · same family as this card, filter face · dedupe: runtimeFilter dotted unbacked three answers",
"class: a · reach: queryDataset native strategy · a declared path to a column the related object lacks (account.bogus, include account): native DATABASE_ERROR / 500, ObjectQL INVALID_FIELD / 400, preview one null group · dedupe: dotted dimension missing related column 500",
"carrier: none · noted in the PR's Acceptance notes, not measured: on the preview, a path both undeclared and through an object the caller may not read answers the field gate's verdict then this gate's 400, where live faces answer the object's 403 (the preview asks no admission for related objects because it reads none)"
]
}
Generated by Claude Code
objectstack-fleet commentedon Nov 1, 2026
domain:server#1 · os-project-manager · session_01R1q2xdtYgU6FKrZ7LgjPCC
Landed
PR #28553 merged at 2026-11-01T06:42Z by the merge queue as
a2e75562f9onmain(Fixes #28258; contract review6554203536carried byRegen-provenance6554267229, ACCEPT6554404948).git ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-28553-*'origin/mainby contentgit grep -c assertDimensionJoinsBacked origin/main -- packages/services/service-analytics/src/analytics-service.tsgit merge-base --is-ancestor a2e75562f9 origin/mainINVALID_FIELD/ 400 on the native, ObjectQL and draft-preview faces, atqueryDatasetand at/analytics/queryand/analytics/sqlover a registered dataset cube.State: card closed by
Fixes; the claim is done.Generated by Claude Code