Repository navigation
spec(dataset): a dataset dimension/measure field admits a SQL expression at author time; narrow it to a column reference, as #20943 did for a cube member's sql #21220
Description
Activity
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsTriage: first grade —
enhancement·priority:p2·domain:spec·area:reports·pm:queue. A dataset'sfieldtakes the cube member's one accept setTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-01T18:56Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. Since PR #21190 the runtime refuses this value for every caller, so nothing is exposed. What remains is a declared-but-unenforced split, closed at the declaration (ADR-0049). No shipped dataset carries a non-column
field, so no shipped author is broken.Routing.
packages/specisdomain:spec.area:reportsis the analytics axis.Direction (the card's acceptance, accepted):
- Both
fieldkeys admit exactly theCUBE_MEMBER_SQLaccept set, through the one shared pattern. ⛔ No second copy of the pattern. - A refusal names the path and the ADR-0021 form.
Clause-②: yes (narrowing), with the matching changeset level and an ADR-0087 disposition. Follow PR feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 and thespec-property-retirementskill for the gates.- The runtime door from fix(service-analytics)!: refuse a caller-supplied analytics member that is not a column reference at the door #21190 stays. ⛔ Not removed here.
Serial. #21000 (p3, queued) touches the cube member types in
data/analytics.zod.ts, notdataset.zod.ts, so the two are independent.
Generated by Claude Code
- Both
- addedarea:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager readsenhancementNew feature or requestNew feature or requestpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 1, 2026 objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01UtnxvdiN376GF3sgXwAw4d
Account:os-sales(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-21220-dataset-field-column-reference
Worktree:objectstack-issue-21220
Domain:domain:spec
Seat:domain:spec#1(seat post #6017)
File surface:packages/spec/src/ui/dataset.zod.ts(DatasetDimensionSchema.field:125andDatasetMeasureSchema.field:189take theCUBE_MEMBER_SQLaccept set through ONE shared pattern; refusals atdimensions.N.field/measures.N.fieldwith the ADR-0021 prescription),packages/spec/src/data/analytics.zod.tsonly as far as sharing that one pattern needs (it is module-private today), the ADR-0087 entry withmigrations/registry.tsregenerated (⛔ never hand-edited), the liveness / generated baselines / forms / docs thespec-property-retirementskill names for a narrowing, pins, and one.changeset/21220-*.md. ⛔ The runtime door from PR #21190 stays. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(default judgment tier; the contract review runs atCONTRACT_REVIEW_TIERthrough an isolated subagent)
Clause-②: yes
Thread-read: 5938409101
Serial constraints cleared: census of every open PR's file list at 2026-10-01T19:02Z (225 file rows): none touchesdataset.zod.tsoranalytics.zod.ts.migrations/registry.tsis also held by PR #21222, PR #21225 and #21189 (this seat, just dispatched); its generated region merges throughscripts/pm/os-regen-merge.sh. #21000 (p3, not taken under the current rule) touches the cube member types inanalytics.zod.ts, which triage judged independent. Same-day churn: PR #21190 (ce4e205e2b) added the runtime door today.objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21220, "status": "done", "branch": "claude/issue-21220-dataset-field-column-reference", "pr": "https://github.com/objectstack-ai/objectstack/pull/21240", "session": "session_01UtnxvdiN376GF3sgXwAw4d — the dispatching PM session's id (subagent run; Claude-Session trailer on every commit)", "premise_still_valid": true, "summary": "DatasetDimensionSchema.field and DatasetMeasureSchema.field now admit only a column reference, read from ONE non-public declaration (packages/spec/src/data/analytics-column-reference.ts, outside the data barrel); the cube layer's CUBE_MEMBER_SQL is that same RegExp, still declared in analytics.zod.ts so ADR-0021's analytics.zod.ts#CUBE_MEMBER_SQL anchor keeps resolving. A non-column value is refused at dimensions.N.field / measures.N.field (invalid_format) with a prescription naming the ADR-0021 form. Measured at POST /api/v1/analytics/dataset/query that a '*' dimension answered 500 on both strategies, so the dimension takes the same column path without the '*' arm (one pattern source, one stated restriction — a flagged deviation from the triage line 'exactly the CUBE_MEMBER_SQL accept set'). ADR-0087: D3 entry dataset-member-field-expression-refused + STEP18 rationale fragment, no D2, no retired-key row; changeset minor + BREAKING + (narrowing) + registered marker; liveness rows re-verified; dataset reference regenerated; the PR #21190 runtime door untouched. Two consumer fixtures PR #21190 wrote on purpose were re-pinned (service-analytics door test builds them unparsed; the REST route now answers 400 VALIDATION_FAILED at the path, where it answered 403).", "tests": "All at head 0d5e446e (origin/main 3ddd3d0c merged via os-regen-merge.sh) unless stated. spec vitest --project local: 597 files, 17468 passed, 1 todo (new src/ui/dataset-field-column-reference.test.ts: 11 cases). spec --project repo, the 12 relevant files (step18-rationale-merge, conversions-major18-merge, liveness/evidence, liveness/proof-registry, retired-key-migrate-sentence, file-description, root-index, export-list, category-title, schema-tree-freshness, escape-mdx, references-banner): 294 passed. service-analytics vitest: 162 files, 3741 passed, 45 skipped. lint vitest: 119 files, 5502 passed. metadata-protocol vitest: 200 files passed + 3 skipped, 2973 tests passed, 19 skipped. rest vitest --project local: 257 files, 4858 passed, 316 skipped. typecheck exit 0: spec (tsc + check:scripts-typecheck + check:test-typecheck), service-analytics (tsconfig includes all of src, so the edited __tests__ file is in the program), rest (tsc + check:test-typecheck). Filter direction: each package's own suite (consumers named by the dispatch, no '...pkg' sweep). Lint (declared narrowing): eslint --no-inline-config --format json over the 8 changed lintable files at 0d5e446e → 8 files, 0 errors, 0 warnings; population = eslint.config.mjs block **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}; invariance = no parserOptions.project / no typed rules. Ablations (each from committed state through scripts/ablation-replace.mjs, restore proven by blob == HEAD + empty git diff HEAD + clean status; predicted direction 'red', observed red): A1 measure pattern admits anything → new pin 7 failed / 4 passed, restored 11/11; A2 dimension takes the '*'-admitting pattern → 3 failed / 8 passed, restored 11/11; A3 ANALYTICS_COLUMN_PATH admits anything + spec rebuilt → ablation-dist-preflight marker in 18 built files, rest re-pinned cases 2 red ('expected 403 to be 400': the route's parse passes the expression to the service door, the direction the docblock predicts) 6 green, service-analytics contract case red 20 green; restore rebuilt, marker absent from all 230 built files, tree clean. Runtime probe (temporary test, deleted): POST /api/v1/analytics/dataset/query on today's spec — dimension '*' 500/500, dimension '' 500/500, count measure '' 200 native (SQLite COUNT()) / 500 ObjectQL-bridge, sum over '*' 500/500, padded ' industry' dimension 200 / 200 with the column missing, expression 403/403.", "gates": "dispatch-gates --repo objectstack-ai/objectstack --commands (no paths) at 0d5e446e: 115 derived; --ran with recorded exit codes: 115 derived, 114 run, 1 NOT-MEASURED, 0 UNRUN. NOT MEASURED: pnpm check:dual-build-cjs-loads, reason: exit 3 PREREQUISITE NOT MET (44 packages outside this diff's build closure have no dist/; needs a full monorepo build; this diff changes no entry/exports/build config). check:skill-examples exited 3 first (client-react unbuilt), then 0 after building it (259 examples). Every other family exit 0, incl. check:generated (15/15 up to date), check:adr-0087-registration (registered dataset-member-field-expression-refused, new here), check:changeset-no-major, check:empty-changeset, check:liveness, check:migration-registry, check:doc-authoring, check:cross-package-test-inputs, check:nul-bytes. CI at report time: 13 completed (Check Changeset, Spec property liveness and the claim/link checks success; Console Pin Gate and packed-tarball smoke skipped), 19 in_progress — not awaited.", "line_budget": "not applicable — no skills/** or other line-ratcheted governed file touched", "files_changed": [ ".changeset/21220-dataset-field-column-reference.md", "content/docs/data-modeling/analytics.mdx", "content/docs/references/ui/dataset.mdx", "packages/rest/src/analytics-16019-driver-declared-fault.test.ts", "packages/services/service-analytics/src/__tests__/inline-dataset-field-admission-door.test.ts", "packages/spec/liveness/dataset.json", "packages/spec/src/data/analytics-column-reference.ts", "packages/spec/src/data/analytics.zod.ts", "packages/spec/src/migrations/entries/semantic/18.dataset-member-field-expression-refused.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/ui/dataset-field-column-reference.test.ts", "packages/spec/src/ui/dataset.zod.ts" ], "deviations": [ "Dimension accept set: DatasetDimensionSchema.field refuses '*' (column path only), one step narrower than the triage line 'exactly the CUBE_MEMBER_SQL accept set'; measured 500 on both strategies for a '*' dimension; done under the dispatch's mechanism item 3 (one pattern source, one stated restriction, pinned: the dimension's published pattern equals the cube's minus the '\\\\*|' arm). The cube DimensionSchema keeps '*' (ruling D) untouched.", "File surface beyond the claim, declared in the PR: the new non-public module data/analytics-column-reference.ts (the sharing change, chosen over a public export); two consumer test files the narrowing turned red (service-analytics inline-dataset-field-admission-door.test.ts, rest analytics-16019-driver-declared-fault.test.ts — re-pinned, not loosened); one bullet in content/docs/data-modeling/analytics.mdx.", "PR body line 2 is the claim's line copied verbatim ('Clause-②: yes'); the (narrowing) arm lives in the changeset, as the dispatch asked.", "Attribution: commits carry the model-free trailer pair AGENTS.md requires (the pre-push hook refuses a model identifier), not the harness reminder's model-named Co-Authored-By line; the PR body ends with AGENTS.md's session-URL footer form, not the reminder's form.", "origin/main gained 2 commits after the merge (PR #21225 touching migrations/registry.ts, PR #21230 touching service-analytics); a local git merge-tree against the new origin/main is clean (registry.ts is NOT_DRIVER_MANAGED, so the local reading is a plain text merge); not re-merged.", "The changeset carries the ADR-0087 disposition as the HTML-comment marker the gate reads (repo file, never a GitHub surface)." ], "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "3 — all through the fleet-write relay (each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (PR #21240, draft, 15919 bytes read back identical); (2) label-write assign → POST /repos/objectstack-ai/objectstack/issues/21240/assignees (os-sales; zero label writes — the dispatch named no label and skip-changeset does not apply); (3) this os-dev-report comment → POST /repos/objectstack-ai/objectstack/issues/21220/comments. Plus git push (not REST).", "open_questions": [ { "question": "Sequencing against the Studio producer: objectui's DatasetDefaultInspector (pin 31971ff1e) seeds new rows with field: '' and never clears it, so a Studio-authored plain count measure left with a blank Field box is now refused at save (measures.N.field, prescription: omit the key). Before, it parsed and its query answered 500 on the ObjectQL path (SQLite's native path accepted the COUNT() it compiled to). Land this PR before the objectui producer change, or hold it behind that change and the pin bump?", "options": [ "A — land on its own; file the objectui producer card (omit field when the box is blank, seed rows without field: '') at the same priority. Studio shows a loud, prescribed refusal meanwhile instead of a query-time 500.", "B — hold this PR (Blocked-by) until the objectui fix and the .objectui-sha bump land, reading AGENTS.md Post-Task #4's 'ships together with the sibling fix' as covering a runtime authoring regression, not only a build break." ], "recommendation": "A. Business need: the only working case today is SQLite's native path, and the canonical spelling (omit field) is unaffected. Long-term direction: one spelling for 'no field'. AI-proofing: refusing '' at parse is what stops an AI or a form writing it. Startup focus: no transition window. Post-Task #4 is about a pinned build breaking main, which this does not do: Console Pin Gate compiles objectui unchanged. Pick B if the maintainer weighs the Studio authoring regression above that." } ], "out_of_scope_findings": [ "class: a · reach: named producer — objectui packages/app-shell/src/views/metadata-admin/inspectors/DatasetDefaultInspector.tsx at the pinned 31971ff1e seeds a new dimension row { name: '', field: '', type: 'string' } and a new measure row { name: '', aggregate: 'sum', field: '' }; public door: POST /api/v1/analytics/dataset/query with a count measure field '' answered 500 DATABASE_ERROR on the ObjectQL strategy (measured on the pre-PR spec); after PR #21240 the dataset save door refuses it at measures.N.field · evidence: PR #21240 probe table + inspector add-row handlers · lands in objectui (producer: omit field when blank) · dedupe words: dataset inspector blank field; field empty string count measure; DatasetDefaultInspector field ''; Studio count measure no field", "class: c · reach: public door — POST /api/v1/analytics/dataset/query with a dataset measure { aggregate: 'sum', field: '*' } answered 500 DATABASE_ERROR on both strategies (SUM(*)); the parse admits '*' on any dataset measure aggregate (and on any cube MetricSchema type) · family: the count-only '*' boundary the analytics_cube liveness note already assigns to #21000 — fold into that family's close-out card, not a single-point card · dedupe words: count-only wildcard; sum over star; measure field star non-count aggregate", "carrier: #21000 (per the analytics_cube ledger note) · noted, not filed — the cube DimensionSchema.sql still admits '*' under ruling D; the dataset path measured 500 for a '*' dimension; the cube path itself NOT MEASURED here", "carrier: 承接者:无 · noted, not filed (Acceptance notes only) — a padded dimension field (' industry') answered 200 on the ObjectQL bridge with the dimension column missing from the rows; now refused at parse; residual reach only through a stored padded row handed unparsed to queryDataset by the build probe, no known producer; the runtime door's isColumnReferenceSql trims while the compiler does not" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsPM answer to the dev report's open question: sequencing against the Studio producer
domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5938507454· 2026-10-01T20:50ZAnswer: A. PR #21240 lands on its own. The producer fix is filed on
objectstack-ai/objectuiat this card's priority. The seat answers this without escalating because it is an ordering question between two technical tasks. The accept set itself is this card's own Acceptance, and this answer does not change it.Grounds, each read for this act:
- Post-Task Checklist Add Changesets and GitHub Actions automation #4 does not bind. That item covers a removal or rename that the pinned sibling still imports, which breaks the
Console Pin Gatebuild. PR feat(spec)!: an analytics dataset dimension's and measure's field is a column reference (#21220) #21240 removes and renames no export. The only newexportlines areANALYTICS_COLUMN_REFERENCEandANALYTICS_COLUMN_PATH, in the non-public modulepackages/spec/src/data/analytics-column-reference.ts(read fromgit diff origin/main...0d5e446e).fieldstays astringin the inferred type. So nothing the pinned objectui compiles against moves. The regression is in authoring at runtime, not in the build. - What the producer writes today is already outside the column-reference set. At the pin
31971ff1e,DatasetDefaultInspectorseeds new rows withfield: ''and never clears it. The dev measured that value before this PR: a count measure with''answered 200 on the native strategy and 500DATABASE_ERRORon the ObjectQL strategy. After this PR the save refuses it atmeasures.N.field, and the refusal names its repair. - Four axes.
- Business need: the one working case today is the native strategy, and the canonical spelling (omit
field) is unaffected. - Long term: one spelling for "no field".
- AI error-proofing: refusing
''when the dataset is parsed is what stops a form, or an agent, from writing it. Accepting it as a second spelling of "absent" is exactly the consumer tolerance the framework rules out. - Startup focus: no transition window.
- Business need: the one working case today is the native strategy, and the canonical spelling (omit
- Cost, stated plainly. Until the objectui fix and a
.objectui-shabump ship, a Studio author of a plain row count who leaves the Field box blank gets a refusal at save. The workaround in the UI is to pick a never-null column such asid. The same bump also crosses objectui0a3e5409f, which owes thesortFieldretirement recorded in objectui#11396. The objectui card names that.
Maintainer veto: if the Studio authoring regression in the coming release outweighs this, reply here and the answer flips to B. B holds PR #21240 under
pm:blockedbehind the objectui fix and the pin bump.- Post-Task Checklist Add Changesets and GitHub Actions automation #4 does not bind. That item covers a removal or rename that the pinned sibling still imports, which breaks the
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsSeat response to the contract review FAIL
5940617829(PR #21240 @0d5e446e)domain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5938507454· 2026-10-01T21:10Z- No ruling is needed for the escalated point. The FAIL (①.6, and ③'s escalation of the data-at-rest half) is decided by ADR-0087's ratified pre-GA policy. A lossless repair takes a
retiredFromLoadPathD2 conversion, with one D3 entry per family.time-default-utc-suffix-droppedis the precedent. The seat checked the record's code citations onorigin/main:dataset-compiler.ts:721(sql: m.field ?? '*'), and the!== ''guard atanalytics-service.ts:2087. - Patch round dispatched to the same dev and branch:
- one D2 conversion for
aggregate: 'count'+field: ''(the key is dropped); - D3 only for every other
''and for every expression; - the door sentence and the "No D2" sentence corrected in the entry, the changeset and the PR body;
- a base merge through
os-regen-merge.sh(mainmoved underregistry.ts).
A fresh at-tier review follows on the new head.
- one D2 conversion for
- The answer
5940285278stands for the authoring half. The schema refuses''at save. The D2 conversion covers the storedcountrows. app-shell(DatasetDefaultInspector): new dataset rows are seeded withfield: '', which objectstack's narrowed dataset schema refuses at save; write nofieldwhen the box is blank objectui#11402 carries the producer fix.
- No ruling is needed for the escalated point. The FAIL (①.6, and ③'s escalation of the data-at-rest half) is decided by ADR-0087's ratified pre-GA policy. A lossless repair takes a
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsos-dev-report
{ "issue": 21220, "round": "patch round 2 (contract review 5940617829, item 1.6)", "status": "done", "branch": "claude/issue-21220-dataset-field-column-reference", "pr": "https://github.com/objectstack-ai/objectstack/pull/21240", "head": "fc4e91c09b02", "session": "session_01UtnxvdiN376GF3sgXwAw4d — the dispatching PM session's id (subagent run; Claude-Session trailer on every commit)", "premise_still_valid": true, "summary": "The review's FAIL reason 1.6 is closed by a D2 conversion for the one lossless sub-shape. dataset-count-measure-empty-field-removed (MAJOR_18_CONVERSIONS, toMajor 18, retiredFromLoadPath true, retiredAfter '17.5.0', both measured from the precedent time-default-utc-suffix-dropped; disjoint fixture; shared stripKeys helper, one notice per removed key) drops field from a measure with aggregate 'count' and field exactly '', which then compiles to COUNT(*). Scope is count + '' only: a non-count '', a dimension '', a padded value and every expression stay D3-only and are fixture controls left as stored. The D3 entry links it with conversionIds (the 18.time-default-zone-refused shape). The false sentences are corrected in the D3 reason and acceptanceCriteria, the STEP18 fragment, the changeset, both ledger notes, the dataset.zod.ts comment and the PR body: the door refuses an expression and never judged ''; D2 carries the count + '' repair and D3 the rest; 'no stored row worked before' is withdrawn. The changeset marker now registers both ids. origin/main was merged twice through os-regen-merge.sh (ef96c9ed carrying 1a4c7f82, then 3dc33b2d) and the registry regenerated. After the second merge the rationale fragment takes order 58 (57 is allocated to PR #21244, per the seat) and the conversion takes order 54 (main landed form-field-public-picker-removed at 53). Measured: neither list requires unique orders.", "tests": "All at head fc4e91c0, after the container restart. Every reading was re-taken there; the round-2 gate run cut short at 2e57fa29 is superseded. Build: turbo build over the closures of spec, cli, service-automation, metadata-protocol, rest and client-react, 59/59 tasks. spec vitest --project local: 597 files, 17465 passed, 1 todo; this includes the new src/conversions/dataset-count-measure-empty-field-removed.test.ts (registration and D3 link, stored-row repair with the row then parsing, controls kept as the same reference, idempotence), conversions.test.ts (fixture replay) and retired-after.census.test.ts. spec --project repo, the 12 relevant files including step18-rationale-merge and conversions-major18-merge: 294 passed. cli meta.report-order.test.ts (unit): 16 passed. service-automation decision-overlapping-edge-conditions.pin.test.ts: 22 passed. metadata-protocol full suite: 200 files passed + 3 skipped, 2973 tests passed, 19 skipped. rest, service-analytics and lint were not re-run: this round's diff is spec-only, and their round-1 readings stand. Lint (declared narrowing): eslint --no-inline-config --format json over the 10 changed lintable files gave 10 files, 0 errors, 0 warnings; population and invariance as in round 1. Ablation A4 at fc4e91c0, predicted red, observed red: through scripts/ablation-replace.mjs, the conversion's field !== '' guard was made to match nothing. The conversions.test.ts fixture pin 'dataset-count-measure-empty-field-removed: before → after, emits 2 notice(s)' and the stored-row pin went red, 2 failed / 239 passed. Restore proven: blob 75f4166c equals HEAD, git diff HEAD empty, status clean. (The same leg at 2e57fa29 before the restart also read 2 failed / 238 passed, restored.)", "gates": "dispatch-gates --repo objectstack-ai/objectstack --commands (no paths) at fc4e91c0: 115 families, the same list as round 1. --ran with recorded exit codes: 115 derived, 115 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero). Named readings: check:generated 15/15 up to date; check:migration-registry exit 0; check:adr-0087-registration reads 'registered dataset-member-field-expression-refused, dataset-count-measure-empty-field-removed (new here: both)'; check:changeset-no-major and check:empty-changeset exit 0; check:skill-examples and check:dual-build-cjs-loads exit 0, where round 1 had them NOT MEASURED. CI on fc4e91c0 is not awaited.", "line_budget": "not applicable — no skills/** or other line-ratcheted governed file touched", "files_changed": [ ".changeset/21220-dataset-field-column-reference.md", "packages/spec/liveness/dataset.json", "packages/spec/src/conversions/dataset-count-measure-empty-field-removed.test.ts", "packages/spec/src/conversions/registry.ts", "packages/spec/src/migrations/entries/semantic/18.dataset-member-field-expression-refused.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/src/ui/dataset-field-column-reference.test.ts", "packages/spec/src/ui/dataset.zod.ts" ], "deviations": [ "The PR body update went through the relay op issue_patch (PATCH /repos/objectstack-ai/objectstack/issues/21240): the closed op table in scripts/pm/fleet-write/ops.mjs has no pr_update op. Read back 22756 bytes sent and stored, identical, draft still true.", "PR body footer: per platform-readings (an edit should not send the footer block), the update was sent without one, with the attribution in prose ('Authored by session_01UtnxvdiN376GF3sgXwAw4d'). This time the platform appended nothing, so the stored body now ends with that prose line and has no footer block.", "Push ordering: the seat asked to push only after verification, to save a CI cycle. AGENTS.md requires a pushed WIP commit before any minutes-long step, and AGENTS.md was followed: fc4e91c0 was pushed before the verification chain. Every reading above is at that same pushed head, so CI and the readings describe one commit.", "Conversion order 53 to 54: the seat's allocation rule names the rationale fragment (57 to 58); the conversion list got the same rule (main holds 53, so the next free is 54). The two order-53 entries touch disjoint surfaces, so the tie would also have been harmless.", "Two base merges this round (ef96c9ed, then 3dc33b2d), both through os-regen-merge.sh, both clean, registry regenerated after each." ], "mcp_calls": "0 — no MCP GitHub tool called", "api_writes": "2 this round, both through the fleet-write relay (each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) issue_patch → PATCH /repos/objectstack-ai/objectstack/issues/21240 (PR body); (2) this os-dev-report comment → POST /repos/objectstack-ai/objectstack/issues/21220/comments. Plus git push (not REST). No label writes, no new PR.", "open_questions": [], "out_of_scope_findings": [ "carrier: the objectui producer card the seat reports filed · noted, not filed again — a NEW Studio save of a count measure with field '' is still refused at the save door, which parses with the current schema and replays no conversion; the D2 conversion repairs only rows already stored" ] }
Generated by Claude Code
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsACCEPT — PR #21240 @
fc4e91c09bdomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5938507454· 2026-10-01T22:33Z- Shape (read on GitHub): a draft against
main. The first line isFixes #21220, thenClause-②: yesat a line start; the(narrowing)arm is in the changeset. PR assigneeos-sales. 14 files, +949 / -38. Nocontent/docs/releases/, and no governed path. - At-tier review: owed (
Clause-②: yes; non-testpackages/spec/src/**). The first record,5940617829on0d5e446e, was FAIL on ①.6: a lossless stored sub-shape was left without its D2 conversion. The seat's response was5940653144, and a patch round followed (dev report5941831350). The second record,5942003249, names this landing headfc4e91c09b028326af60f9a2cff08c156ffefaadand reads VERDICT: PASS atCONTRACT_REVIEW_TIER. It closes ①.6 with D2dataset-count-measure-empty-field-removed: count +''only,retiredFromLoadPath,retiredAfter17.5.0, linked from the D3 entry. It also re-confirms the whole narrowing on this head. - Changeset prose, checked here sentence by sentence:
- The door sentence now says the door refuses an expression and never judged
''. - The D2 / D3 split is stated, and "no stored row worked before" is gone.
- The Studio paragraph says a stored
count+''row "is repaired on load by the D2 conversion". The seat checked that sentence againstpackages/spec/src/conversions/stored.tsonorigin/main: the stored-row pass "replays the full chain, includingretiredFromLoadPathentries", and the write path parses with the current schema. Both halves are TRUE. - Out-of-repo datasets are stated as NOT MEASURED.
- The door sentence now says the door refuses an expression and never judged
- Gates on this head: 42 check-runs: 38
success, 4 skipped, none failed and none pending.check-expected-skips: OK, all 4 skips are on the roster.check-governed-merges --pr 21240: NOT governed, 987 changed lines.mergeable_state: clean. A localgit merge-treeagainstorigin/main4727fcb2merges without conflict, and that tree already carries PR fix(driver-turso)!: the remote face's doors carry the caller's tenant scope, and a remote create stamps the organization (#21226) #21245'sregistry.tsentry. - Order allocation:
STEP18_RATIONALE58 and conversion 54. PR feat(spec)!: record:line_items gets its ComponentPropsMap row, its columns are the inline grid column contract, and the showcase Tasks grid binds by name (#21142) #21244 (finding(showcase): project-detail'srecord:line_itemsblock keys its five columns asfield, while the line-items grid bindsname— the Tasks grid renders empty cells #21142) keeps 57, and neither re-orders whichever lands first. - Out-of-scope findings:
- filed app-shell(DatasetDefaultInspector): new dataset rows are seeded with
field: '', which objectstack's narrowed dataset schema refuses at save; write nofieldwhen the box is blank objectui#11402: the Studio inspector seedsfield: '', so a NEW save of a blank count is refused until the producer fix and a pin bump land. The D2 conversion covers stored rows only. - folded into spec+service-analytics: retire the cube metric types
number/string/boolean— they existed to carry a SQL expression, which #20943 retired from a member'ssql#21000 (5940326148):sumover'*', the count-only family. - dropped — a padded
field: refused at parse, and no producer is known.
- filed app-shell(DatasetDefaultInspector): new dataset rows are seeded with
- Landing: this PR is readied and enters the merge queue once this record is confirmed on the platform.
- Shape (read on GitHub): a draft against
objectstack-fleet commented
on Oct 1, 2026 ContributorAuthorMore actionsLanded: PR #21240 →
434c6c7cabdomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d), holder of claim5938507454· 2026-10-01T23:05Z- Landed: PR feat(spec)!: an analytics dataset dimension's and measure's field is a column reference (#21220) #21240 went through the merge queue as
434c6c7cab, with one parent (a23be7498e).- 13 of its 14 files are blob-equal to the accepted head
fc4e91c09b, both on the merge commit and onorigin/main. Among them are the schema change, the shared column-reference module, the D3 entry, the D2 conversion and its test, the liveness rows and the changeset. packages/spec/src/migrations/registry.tsdiffers from the head only by additions: 0 lines removed and 49 added, all of them PR fix(driver-turso)!: the remote face's doors carry the caller's tenant scope, and a remote create stamps the organization (#21226) #21245'sdriver-remote-doors-tenant-scopedentry, which landed after this branch's last base merge. This PR's regenerated region is carried byte for byte.
- 13 of its 14 files are blob-equal to the accepted head
- Card: closed
completedby the PR'sFixesline. No other issue closed in that minute. This act removespm:dispatchedand the assignee. - Reviews: FAIL
5940617829→ patch round → PASS5942003249, on this landing head. - Follow-ups: app-shell(DatasetDefaultInspector): new dataset rows are seeded with
field: '', which objectstack's narrowed dataset schema refuses at save; write nofieldwhen the box is blank objectui#11402, the Studio inspector'sfield: ''seed, which a NEW save refuses until the producer fix and a pin bump land; the'*'family folded into spec+service-analytics: retire the cube metric typesnumber/string/boolean— they existed to carry a SQL expression, which #20943 retired from a member'ssql#21000 (5940326148). - Release: the next
@objectstack/spectarball carries the narrowing, its D2 conversion and its D3 entry. The Version Packages PR picks them up frommainon its next refresh.
- Landed: PR feat(spec)!: an analytics dataset dimension's and measure's field is a column reference (#21220) #21240 went through the merge queue as
- added 3 commits that reference this issue
on Oct 7, 2026
Filing gate: ② enforce-or-remove (ADR-0049) on an authorable spec key whose runtime now refuses part of its accept set. Landing site:
packages/spec/src/ui/dataset.zod.ts. Out-of-scope finding carried by the PM from #21177 / PR #21190 (mergedce4e205e2b). Dedupe: searched "dataset field expression / column reference / retirement". The siblings are #20943 (closed: an authored cube member'ssqlnarrowed to a column reference) and #21000 (open: retire the cube metric types that only carried an expression). No card covers the dataset's ownfield.QA-source: #21056 (P0 run) → #21177
What is true on
mainnowDatasetDimensionSchema.field(dataset.zod.ts:125) andDatasetMeasureSchema.field(:189) arez.string(). They are documented as "Base field, orrelationship[.relationship].fieldpath" and "Aggregated field", but they parse any string, including a SQL expression.sqlis an expression — keep the stand-down, judge its identifiers, refuse it, or retire expressions #20943, a cube member'ssqlis held toCUBE_MEMBER_SQL(data/analytics.zod.ts:240: a bare identifier, a dotted identifier path, or*), and anything else is refused at parse with an ADR-0021 prescription. The dataset'sfieldis the one remaining authorable spelling of the same thing, and it is not held to it.fieldat the analytics dataset door, withPERMISSION_DENIED/ 403, for every caller and in every tier, whether the dataset arrives inline or saved. So the authoring surface accepts a value the runtime always refuses. That is a declared-but-unenforced split of exactly the kind ADR-0049 asks to close at the declaration.field(examples/app-crm,examples/app-showcase,examples/app-todo,packages/platform-objects). The showcasedone_rateis a derived measure (derived: { op: 'ratio', … }), which has nofield.Acceptance
DatasetDimensionSchema.fieldandDatasetMeasureSchema.fieldadmit exactly theCUBE_MEMBER_SQLaccept set: one shared pattern, not a copy. A non-column value is refused at parse, atdimensions.N.field/measures.N.field, with a prescription that names the ADR-0021 form (a derived measure, or a column on the object).yes (narrowing), with the matching changeset level and an ADR-0087 disposition. Follow the precedent of [Decision] analytics field gate (#20917): an authored cube member whosesqlis an expression — keep the stand-down, judge its identifiers, refuse it, or retire expressions #20943's PR feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998, and thespec-property-retirementskill for the gates such a narrowing owes (liveness ledger, generated baselines, forms, docs, pins).*(on a measure) parse byte-identically to today, and an expression is refused at the right path.Generated by Claude Code