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
[finding] os validate passes an analyticsCubes member the analytics door refuses: lint reads no cube, so a cube dimension over a JSON-stored field passes authoring and is refused 400 at query time #21082
Filing gate: ① a product defect with a measured reach:. Finding class (c): the runtime refuses what the authoring check passes.
reach:os validate, measured by the #20890 dev (os-dev-report5925066047 on #20890, out_of_scope_findings[0], fixture cube-json-dim). An analyticsCubes cube whose dimension's sql names a json field passes (exit 0, "Validation passed"), both at 6073bb96b8 and with PR #21073's lint. The query-time refusal (400 INVALID_FIELD, service-analyticsstructured-json-dimension-door.ts) is PR #20886's cube-face pins. Neither the dev nor this seat re-ran it.
Filed by the domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1, seat post #19357). ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim.
Triage: grade and route. The rule would be lint's (packages/lint): a walk over analyticsCubes member records, whose sql is a column reference since PR #20998. The door's predicates (STRUCTURED_JSON_TYPES, isMultiValueField) are exported from @objectstack/spec/data.
Dedupe
mcp__github__search_issues, repo-scoped, open and closed:
「os validate analyticsCubes cube dimension json field lint reads no cube」: 12 hits, 8 read.
None of the hits read is an authoring leg for cube members. #20890 is the dataset leg. #20807, #20808 and #20912 are the runtime legs, all closed. #21044 is the cube door.
Why p3. It is the dataset leg's grade (#20890, p3). The runtime still refuses the shape at query time, so this is a late refusal, not a wrong answer.
Routing.packages/lint goes to domain:spec under the anchoring rule's exception, as #20890 does.
Why blocked. PR #21073 (#20890) is open and adds the dataset rule this leg extends (read at this write). The cube walk reuses that rule and the door's exported predicates. ⛔ No parallel copy.
Direction. It is the card's own scope, confirmed: a walk over analyticsCubes member records refuses the three shapes the door refuses (a JSON dimension, a multi-value dimension, count_distinct over a JSON column). It reads STRUCTURED_JSON_TYPES and isMultiValueField from @objectstack/spec/data. Pins: one fixture per shape, plus a scalar control.
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-21082-cube-member-authoring-leg
Worktree: objectstack-issue-21082
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
File surface: on origin/main1d0600bf66. packages/lint: a walk over analyticsCubes member records that refuses the three shapes the analytics door refuses (a dimension over a structured-JSON field, a dimension over a multi-value field, count_distinct over a JSON-stored column), reading STRUCTURED_JSON_TYPES and isMultiValueField from @objectstack/spec/data. It extends validate-dataset-measure-aggregates.ts (the dataset leg #20890 landed) or a sibling rule file registered in authoring-rules.ts / index.ts, with its tests; content/docs/deployment/validating-metadata.mdx if it lists the rules; one @objectstack/lint changeset. ⛔ No parallel copy of the dataset rule (triage 5925818876). Stop on breach; explain in the report.
Container & model: S, mode:subagent, model: opus (default build tier)
Clause-②: no (narrowing) if the diff adds no export, yes (narrowing) if it adds any; the dev measures it with check-widening-tells, and an at-tier review is owed only on yes
Thread-read: 5925818876
Serial constraints cleared: at 2026-10-02T12:39Z, no open PR touches packages/lint/src/validate-dataset-measure-aggregates.ts, authoring-rules.ts or index.ts. #20890 (the dataset leg, its blocker) closed completed with 5e470f8c1c. domain:services' PR #21399 (#21365) and PR #21395 edit service-analytics strategies, not lint; the refusal this rule mirrors is the door's, read not edited.
{
"issue": 21082,
"status": "done",
"branch": "claude/issue-21082-cube-member-authoring-leg",
"pr": "https://github.com/objectstack-ai/objectstack/pull/21416",
"session": "session_01UtnxvdiN376GF3sgXwAw4d",
"premise_still_valid": true,
"summary": "The dataset-member rule (packages/lint/src/validate-dataset-measure-aggregates.ts) gains a cube leg. It walks analyticsCubes and refuses the three shapes the analytics door refuses: a dimension whose sql column is structured-JSON, a dimension whose sql column is multi-value, and a count_distinct measure over a JSON-stored column. The leg reuses the dataset leg's own verdicts (groupKeyClassOf, acceptsDeclaration) and its two ids (dimension-json-stored-field-refused, measure-aggregate-field-type-refused), so there is no parallel copy. The dataset and cube legs now share the finding builders, and the dataset findings stay byte-identical. Columns are read the way the door reads them: the cube's object is its trimmed sql; a hop with a declared join reaches the join's name (the door's tier 1); every other hop goes through the shared resolveFieldPath (tier 2). '*' resolves to nothing and is not refused, which is #21000's question. Zone 2 premises measured true at 4b20c84748: no lint rule read analyticsCubes, the cube's object is cube.sql, member sql is a column reference, and the door answers 400 INVALID_FIELD. No export is added, so the PR declares Clause-② no (narrowing) with a BREAKING minor changeset and the ADR-0087 marker not-required (no-migration-prescription).",
"tests": "os validate on 7 fixtures (fx_ledger plus cube fx_cube), BASE 4b20c84748 versus HEAD 07a308acdc. cube-json-dim (the card's instance), a dimension over tags, a dimension over a select with multiple: true, a dimension over account.hq (json), count_distinct over json, and count_distinct over a multi-select each went from exit 0 to exit 1, with the right id. The scalar control stayed at exit 0. Corpus at HEAD: os validate passes on examples app-showcase (the one real cube, showcase_delivery), app-crm and app-todo, with 0 findings of either id. pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 at 07a308acdc: 119 files and 5586 tests passed (11 new cube tests). pnpm --filter @objectstack/lint typecheck at 07a308acdc: exit 0, and --listFiles shows the test file is in the tsconfig.test.json program. Dataset byte-identity probe: BASE's rule and HEAD's rule over 98 stacks gave 920 findings and 0 mismatches. As a control, a perturbed old copy gave 26 mismatches. Ablations through scripts/ablation-replace.mjs on the committed tree, each restore proven by blob == HEAD and an empty git diff HEAD: A1 (cube walk dropped) 8 red / 37 green; A2 (declared-join tier off) 1 red, the join test; A3 (count_distinct leg off) 5 red; A4 (measure leg widened to every aggregate) 1 red. Each ablation's direction was as predicted. No dist build or preflight was needed for the ablations: the test imports the rule relatively, so src resolves without the package exports.",
"mcp_calls": "0 — no MCP GitHub tool was called. Reads went through gh api (GET only).",
"api_writes": "3, each one stroke through the fleet-write relay. Each stroke is one POST /repos/objectstack-ai/objectstack/dispatches from the session, executed as objectstack-fleet[bot]: (1) pr_create, request fw-20261002T134903Z-963af7, giving POST /repos/objectstack-ai/objectstack/pulls (#21416, draft; read-back 7379 bytes sent, 7379 stored); (2) label-write --assign os-sales, request fw-20261002T134944Z-48e15b, giving POST /repos/objectstack-ai/objectstack/issues/21416/assignees (read-back MATCHES; 0 labels written); (3) this os-dev-report comment, giving POST /repos/objectstack-ai/objectstack/issues/21082/comments. Plus git pushes of the branch, which are not REST.",
"gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) at 07a308acdc derived 90 commands from 5 changed paths. All 90 were run, plus the 4 roster gates whose roster sits under a changed path (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity). All 94 exit 0. check:skill-examples and check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: 8 packages had no dist); after a turbo build of those packages, both exit 0. --ran reconciliation: 90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. check-adr-0087-registration: 1 declared-breaking changeset with a disposition. check-changeset-no-major: no major. check-widening-tells --declaration no: exit 0, but all 5 files are NOT MEASURED, so the arm was decided by an export census instead: 0 added export lines (control 5e470f8c1c: 1), and index.ts, runtime.ts and package.json are unchanged. PR CI: in_progress at report time, not awaited.",
"line_budget": "n/a: no skills/** or ledgered line-ratchet file touched. The diff is 5 files, +621/-86.",
"files_changed": [
".changeset/21082-cube-member-json-stored-refused.md",
"content/docs/deployment/validating-metadata.mdx",
"packages/lint/src/authoring-rules.ts",
"packages/lint/src/validate-dataset-measure-aggregates.test.ts",
"packages/lint/src/validate-dataset-measure-aggregates.ts"
],
"deviations": [
"The branch was not merged with main before the PR opened. main moved 6 commits past the branch point 4b20c84748, to 41a3c8df15, and none of them touches packages/lint or the docs page. AGENTS.md section 10 asks for a pre-PR pull and a full run; per os-dev local scope, the joint state is left to CI's merge ref. The --ran verdict named two gate data files as stale here (engine-double-contract.pinned.json, platform-object-tenancy-census.json); this diff touches neither area.",
"The claim allowed either extending the dataset rule or adding a sibling rule file. I extended the rule in place: one registry entry, no new export. authoring-rules.ts is touched only by a comment. The rule's module note is rewritten where it said that no rule reads cubes.",
"The rule now mirrors the door's declared-join tier (hop-object.ts tier 1) through a root-picking helper before the shared resolveFieldPath. The dispatch named only the dataset leg's field lookup. Without the tier, ablation A2 shows a false refusal when a cube's declared join and the lookup's reference disagree.",
"Temporary os validate fixtures and the byte-identity probe lived in packages/lint/tmp (gitignored), and were removed before the PR. A copy is kept in the session scratchpad."
],
"open_questions": [],
"out_of_scope_findings": [
"class: c · reach: os validate exit 0 (Validation passed) at 07a308acdc on fixture oos-cube-sum-text, an analyticsCubes cube over fx_ledger whose measure sum_name is type sum with sql name, over a text field. The cube door's #21044 judgment (service-analytics cube-measure-field-type-door.ts) refuses that pair with 400 INVALID_FIELD at query time; that refusal is read from the door's header measurements, not re-run here · evidence: the cube leg in PR #21416 judges count_distinct only, by the dispatch's three-shape ruling, while acceptsDeclaration in validate-dataset-measure-aggregates.ts already answers every aggregate row for dataset measures · family: the authoring leg for the analytics doors (#20890, #21082), so it should fold into that family's close-out card rather than become a single-point card; the seat decides · dedupe words: `cube measure sum text os validate` · `analyticsCubes measure aggregate field type lint` · `cube-measure-field-type-door authoring leg`",
"carrier: none · noted, not filed · the runtime analytics_cube write door (PUT /api/v1/meta/analytics_cube/NAME) dispatches no authoring rule: no AUTHORING_RULES entry declares analytics_cube, and runtime-gate.ts TYPE_TO_STACK_KEY has no row for it. This is read from code, not measured. Whether runtime cube authoring is live end to end is itself unmeasured, as packages/spec/src/kernel/metadata-type-schemas.ts says. In PR #21416's Acceptance notes."
]
}
domain:spec seat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim 5952537937 · 2026-10-02T14:15Z
Shape (read on GitHub): a draft against main. The first line is Fixes #21082, the second Clause-②: no (narrowing). PR assignee os-sales. 5 files, +621 / -86:
packages/lint/src/validate-dataset-measure-aggregates.ts and its test;
a comment in authoring-rules.ts;
content/docs/deployment/validating-metadata.mdx;
one @objectstack/lint changeset.
No published spec source, no governed path.
Clause-② measured: no export is added (export census: 0 added lines, against 5e470f8c1c's 1; index.ts, runtime.ts and package.json unchanged). So the arm is no (narrowing), and no isolated at-tier review is owed: lint only, not Clause-②: yes.
Review (seat-checked, the diff read):
The cube leg reuses the dataset leg's verdicts (groupKeyClassOf, acceptsDeclaration) and its two ids, so there is no parallel copy (triage 5925818876).
It resolves a member's sql where the door reads it: the cube's object, or the last hop's declared join, else the lookup's reference.
The dataset findings are byte-identical (a 98-stack probe: 920 findings, 0 mismatches).
The rule's module note that said no rule reads cubes is rewritten in the same PR.
Acceptance: the card's instance cube-json-dim and five sibling fixtures go from exit 0 to exit 1 with the right id; the scalar control stays 0. The shipped corpus has 0 findings: showcase's real cube showcase_delivery, CRM and todo. Four ablations each turn the expected tests red.
Changeset prose (checked by the seat, sentence by sentence):minor, !, BREAKING, (narrowing), one marker not-required (no-migration-prescription). The "What is refused" classes match STRUCTURED_JSON_TYPES / isMultiValueField and the count_distinct row. "Unchanged" is true: other measure types, '*', unresolved columns, and no rule at the analytics_cube write door. The docs paragraph says the same.
Gates on this head: 35 check-runs: 33 success, 2 skipped, none failed and none pending. check-expected-skips: OK, both skips are on the roster. check-governed-merges --pr 21416: NOT governed, 707 changed lines. mergeable_state: clean. A local git merge-tree against origin/mainceb4a939b4 merges without conflict; no main commit since the merge base 4b20c84748 touches the 5 files, and no other open PR touches them.
Card: closed completed by the PR's Fixes line, and by no other PR. This act removes pm:dispatched and the assignee.
Review: seat-checked (Clause-②: no (narrowing), lint only, so no at-tier review is owed); ACCEPT 5954451320.
What changes:os validate, os build and os lint refuse three analyticsCubes member shapes that the analytics door already refuses with 400 INVALID_FIELD at query time: a dimension over a structured-JSON column, a dimension over a multi-value column, and a count_distinct measure over either. The dataset rule's two ids judge cube members too.
Activity
objectstack-fleet commentedon Oct 1, 2026
Triage: first grade —
bug·priority:p3·domain:spec·area:reports·pm:blocked. The cube leg follows the dataset leg's ruleTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T06:13Z. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: #20890
Why p3. It is the dataset leg's grade (#20890, p3). The runtime still refuses the shape at query time, so this is a late refusal, not a wrong answer.
Routing.
packages/lintgoes todomain:specunder the anchoring rule's exception, as #20890 does.Why blocked. PR #21073 (#20890) is open and adds the dataset rule this leg extends (read at this write). The cube walk reuses that rule and the door's exported predicates. ⛔ No parallel copy.
Direction. It is the card's own scope, confirmed: a walk over
analyticsCubesmember records refuses the three shapes the door refuses (a JSON dimension, a multi-value dimension,count_distinctover a JSON column). It readsSTRUCTURED_JSON_TYPESandisMultiValueFieldfrom@objectstack/spec/data. Pins: one fixture per shape, plus a scalar control.Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
Claim: PM loop round 1
Session:
session_01UtnxvdiN376GF3sgXwAw4dAccount:
os-sales(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-21082-cube-member-authoring-legWorktree:
objectstack-issue-21082Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface: on
origin/main1d0600bf66.packages/lint: a walk overanalyticsCubesmember records that refuses the three shapes the analytics door refuses (a dimension over a structured-JSON field, a dimension over a multi-value field,count_distinctover a JSON-stored column), readingSTRUCTURED_JSON_TYPESandisMultiValueFieldfrom@objectstack/spec/data. It extendsvalidate-dataset-measure-aggregates.ts(the dataset leg #20890 landed) or a sibling rule file registered inauthoring-rules.ts/index.ts, with its tests;content/docs/deployment/validating-metadata.mdxif it lists the rules; one@objectstack/lintchangeset. ⛔ No parallel copy of the dataset rule (triage5925818876). Stop on breach; explain in the report.Container & model:
S,mode:subagent,model: opus(default build tier)Clause-②: no (narrowing) if the diff adds no export, yes (narrowing) if it adds any; the dev measures it with
check-widening-tells, and an at-tier review is owed only onyesThread-read: 5925818876
Serial constraints cleared: at 2026-10-02T12:39Z, no open PR touches
packages/lint/src/validate-dataset-measure-aggregates.ts,authoring-rules.tsorindex.ts. #20890 (the dataset leg, its blocker) closedcompletedwith5e470f8c1c.domain:services' PR #21399 (#21365) and PR #21395 editservice-analyticsstrategies, not lint; the refusal this rule mirrors is the door's, read not edited.objectstack-fleet commentedon Oct 2, 2026
os-dev-report
{ "issue": 21082, "status": "done", "branch": "claude/issue-21082-cube-member-authoring-leg", "pr": "https://github.com/objectstack-ai/objectstack/pull/21416", "session": "session_01UtnxvdiN376GF3sgXwAw4d", "premise_still_valid": true, "summary": "The dataset-member rule (packages/lint/src/validate-dataset-measure-aggregates.ts) gains a cube leg. It walks analyticsCubes and refuses the three shapes the analytics door refuses: a dimension whose sql column is structured-JSON, a dimension whose sql column is multi-value, and a count_distinct measure over a JSON-stored column. The leg reuses the dataset leg's own verdicts (groupKeyClassOf, acceptsDeclaration) and its two ids (dimension-json-stored-field-refused, measure-aggregate-field-type-refused), so there is no parallel copy. The dataset and cube legs now share the finding builders, and the dataset findings stay byte-identical. Columns are read the way the door reads them: the cube's object is its trimmed sql; a hop with a declared join reaches the join's name (the door's tier 1); every other hop goes through the shared resolveFieldPath (tier 2). '*' resolves to nothing and is not refused, which is #21000's question. Zone 2 premises measured true at 4b20c84748: no lint rule read analyticsCubes, the cube's object is cube.sql, member sql is a column reference, and the door answers 400 INVALID_FIELD. No export is added, so the PR declares Clause-② no (narrowing) with a BREAKING minor changeset and the ADR-0087 marker not-required (no-migration-prescription).", "tests": "os validate on 7 fixtures (fx_ledger plus cube fx_cube), BASE 4b20c84748 versus HEAD 07a308acdc. cube-json-dim (the card's instance), a dimension over tags, a dimension over a select with multiple: true, a dimension over account.hq (json), count_distinct over json, and count_distinct over a multi-select each went from exit 0 to exit 1, with the right id. The scalar control stayed at exit 0. Corpus at HEAD: os validate passes on examples app-showcase (the one real cube, showcase_delivery), app-crm and app-todo, with 0 findings of either id. pnpm --filter @objectstack/lint exec vitest run --maxWorkers=2 at 07a308acdc: 119 files and 5586 tests passed (11 new cube tests). pnpm --filter @objectstack/lint typecheck at 07a308acdc: exit 0, and --listFiles shows the test file is in the tsconfig.test.json program. Dataset byte-identity probe: BASE's rule and HEAD's rule over 98 stacks gave 920 findings and 0 mismatches. As a control, a perturbed old copy gave 26 mismatches. Ablations through scripts/ablation-replace.mjs on the committed tree, each restore proven by blob == HEAD and an empty git diff HEAD: A1 (cube walk dropped) 8 red / 37 green; A2 (declared-join tier off) 1 red, the join test; A3 (count_distinct leg off) 5 red; A4 (measure leg widened to every aggregate) 1 red. Each ablation's direction was as predicted. No dist build or preflight was needed for the ablations: the test imports the rule relatively, so src resolves without the package exports.", "mcp_calls": "0 — no MCP GitHub tool was called. Reads went through gh api (GET only).", "api_writes": "3, each one stroke through the fleet-write relay. Each stroke is one POST /repos/objectstack-ai/objectstack/dispatches from the session, executed as objectstack-fleet[bot]: (1) pr_create, request fw-20261002T134903Z-963af7, giving POST /repos/objectstack-ai/objectstack/pulls (#21416, draft; read-back 7379 bytes sent, 7379 stored); (2) label-write --assign os-sales, request fw-20261002T134944Z-48e15b, giving POST /repos/objectstack-ai/objectstack/issues/21416/assignees (read-back MATCHES; 0 labels written); (3) this os-dev-report comment, giving POST /repos/objectstack-ai/objectstack/issues/21082/comments. Plus git pushes of the branch, which are not REST.", "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands (no paths) at 07a308acdc derived 90 commands from 5 changed paths. All 90 were run, plus the 4 roster gates whose roster sits under a changed path (check-changeset-fixed, check:authz-resolver, check:error-code-casing, check:filter-alias-parity). All 94 exit 0. check:skill-examples and check:dual-build-cjs-loads first exited 3 (PREREQUISITE NOT MET: 8 packages had no dist); after a turbo build of those packages, both exit 0. --ran reconciliation: 90 derived, 90 run, 0 NOT-MEASURED, 0 UNRUN, exit 0. check-adr-0087-registration: 1 declared-breaking changeset with a disposition. check-changeset-no-major: no major. check-widening-tells --declaration no: exit 0, but all 5 files are NOT MEASURED, so the arm was decided by an export census instead: 0 added export lines (control 5e470f8c1c: 1), and index.ts, runtime.ts and package.json are unchanged. PR CI: in_progress at report time, not awaited.", "line_budget": "n/a: no skills/** or ledgered line-ratchet file touched. The diff is 5 files, +621/-86.", "files_changed": [ ".changeset/21082-cube-member-json-stored-refused.md", "content/docs/deployment/validating-metadata.mdx", "packages/lint/src/authoring-rules.ts", "packages/lint/src/validate-dataset-measure-aggregates.test.ts", "packages/lint/src/validate-dataset-measure-aggregates.ts" ], "deviations": [ "The branch was not merged with main before the PR opened. main moved 6 commits past the branch point 4b20c84748, to 41a3c8df15, and none of them touches packages/lint or the docs page. AGENTS.md section 10 asks for a pre-PR pull and a full run; per os-dev local scope, the joint state is left to CI's merge ref. The --ran verdict named two gate data files as stale here (engine-double-contract.pinned.json, platform-object-tenancy-census.json); this diff touches neither area.", "The claim allowed either extending the dataset rule or adding a sibling rule file. I extended the rule in place: one registry entry, no new export. authoring-rules.ts is touched only by a comment. The rule's module note is rewritten where it said that no rule reads cubes.", "The rule now mirrors the door's declared-join tier (hop-object.ts tier 1) through a root-picking helper before the shared resolveFieldPath. The dispatch named only the dataset leg's field lookup. Without the tier, ablation A2 shows a false refusal when a cube's declared join and the lookup's reference disagree.", "Temporary os validate fixtures and the byte-identity probe lived in packages/lint/tmp (gitignored), and were removed before the PR. A copy is kept in the session scratchpad." ], "open_questions": [], "out_of_scope_findings": [ "class: c · reach: os validate exit 0 (Validation passed) at 07a308acdc on fixture oos-cube-sum-text, an analyticsCubes cube over fx_ledger whose measure sum_name is type sum with sql name, over a text field. The cube door's #21044 judgment (service-analytics cube-measure-field-type-door.ts) refuses that pair with 400 INVALID_FIELD at query time; that refusal is read from the door's header measurements, not re-run here · evidence: the cube leg in PR #21416 judges count_distinct only, by the dispatch's three-shape ruling, while acceptsDeclaration in validate-dataset-measure-aggregates.ts already answers every aggregate row for dataset measures · family: the authoring leg for the analytics doors (#20890, #21082), so it should fold into that family's close-out card rather than become a single-point card; the seat decides · dedupe words: `cube measure sum text os validate` · `analyticsCubes measure aggregate field type lint` · `cube-measure-field-type-door authoring leg`", "carrier: none · noted, not filed · the runtime analytics_cube write door (PUT /api/v1/meta/analytics_cube/NAME) dispatches no authoring rule: no AUTHORING_RULES entry declares analytics_cube, and runtime-gate.ts TYPE_TO_STACK_KEY has no row for it. This is read from code, not measured. Whether runtime cube authoring is live end to end is itself unmeasured, as packages/spec/src/kernel/metadata-type-schemas.ts says. In PR #21416's Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 2, 2026
ACCEPT — PR #21416 @
07a308acdcdomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5952537937· 2026-10-02T14:15ZShape (read on GitHub): a draft against
main. The first line isFixes #21082, the secondClause-②: no (narrowing). PR assigneeos-sales. 5 files, +621 / -86:packages/lint/src/validate-dataset-measure-aggregates.tsand its test;authoring-rules.ts;content/docs/deployment/validating-metadata.mdx;@objectstack/lintchangeset.No published spec source, no governed path.
Clause-②measured: no export is added (export census: 0 added lines, against5e470f8c1c's 1;index.ts,runtime.tsandpackage.jsonunchanged). So the arm isno (narrowing), and no isolated at-tier review is owed: lint only, notClause-②: yes.Review (seat-checked, the diff read):
groupKeyClassOf,acceptsDeclaration) and its two ids, so there is no parallel copy (triage5925818876).sqlwhere the door reads it: the cube's object, or the last hop's declared join, else the lookup'sreference.'*'alone, which is analytics:'*'runs only undercount, but a cube measure's or dimension'ssqland a dataset measure'sfieldadmit it under any aggregate — a summed'*'answers 500 at the dataset door (split from #21000) #21409's.Acceptance: the card's instance
cube-json-dimand five sibling fixtures go from exit 0 to exit 1 with the right id; the scalar control stays 0. The shipped corpus has 0 findings: showcase's real cubeshowcase_delivery, CRM and todo. Four ablations each turn the expected tests red.Changeset prose (checked by the seat, sentence by sentence):
minor,!, BREAKING,(narrowing), one markernot-required (no-migration-prescription). The "What is refused" classes matchSTRUCTURED_JSON_TYPES/isMultiValueFieldand thecount_distinctrow. "Unchanged" is true: other measure types,'*', unresolved columns, and no rule at theanalytics_cubewrite door. The docs paragraph says the same.Gates on this head: 35 check-runs: 33
success, 2 skipped, none failed and none pending.check-expected-skips: OK, both skips are on the roster.check-governed-merges --pr 21416: NOT governed, 707 changed lines.mergeable_state: clean. A localgit merge-treeagainstorigin/mainceb4a939b4merges without conflict; nomaincommit since the merge base4b20c84748touches the 5 files, and no other open PR touches them.Out-of-scope findings:
sum/avg/min/maxover a column type the cube door refuses still passesos validate(measured onoos-cube-sum-text). This is the second member of the analytics authoring-leg family, so it is filed as the family close-out card lint: the cube authoring leg judgescount_distinctonly, so a cubesum/avg/min/maxover a column the cube door refuses still passesos validate— the close-out card for the analytics authoring-leg family #21419 with an enumeration pin over the compatibility table.analytics_cuberuntime write door dispatches no authoring rule. Read from code, not measured: Acceptance notes.Landing: ready, then auto-merge through the merge queue.
objectstack-fleet commentedon Oct 2, 2026
Landed: PR #21416 →
39a912ea73domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5952537937· 2026-10-02T14:40Z39a912ea73, with one parent (22c2d6f4d5). All 5 files are blob-equal to the accepted head07a308acdc, on the merge commit and onorigin/main.completedby the PR'sFixesline, and by no other PR. This act removespm:dispatchedand the assignee.Clause-②: no (narrowing), lint only, so no at-tier review is owed); ACCEPT5954451320.os validate,os buildandos lintrefuse threeanalyticsCubesmember shapes that the analytics door already refuses with400 INVALID_FIELDat query time: a dimension over a structured-JSON column, a dimension over a multi-value column, and acount_distinctmeasure over either. The dataset rule's two ids judge cube members too.count_distinctonly, so a cubesum/avg/min/maxover a column the cube door refuses still passesos validate— the close-out card for the analytics authoring-leg family #21419, the family close-out card filed from this PR's report, carriedBlocked-by: #21082in its triage grade (5954329626). It is released topm:queuein its own transition comment.fix(lint)!: os validate refuses an analyticsCubes member the analytic…
fix(lint)!: os validate refuses an analyticsCubes sum / avg / min / m…