Skip to content

docs(skills): the objectstack-data field-types formula row says a formula is no dataset or cube dimension (group key) either - #30921

Merged
os-zhuang merged 8 commits into
mainfrom
claude/project-thread-70v39y
Nov 6, 2026
Merged

os-zhuang merged 8 commits into
mainfrom
claude/project-thread-70v39y

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Tesla · project thread

Fixes #30824

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:skills/objectstack-data/rules/field-types.md 的 formula 行多了一个短句:formula 既不能做聚合,也不能做 dataset / cube 的维度(分组键),两者都需要一个存储字段。新增以删掉同文件 Formula 示例里的一条站内指路注释付账。
  • 为什么改:PR fix(service-analytics, lint)!: a dataset or cube dimension over a formula field is refused where it is authored, instead of on the first query that groups by it #30815 之后,对 formula 的 dataset 或 analyticsCubes 维度在作者侧就被拒(lint dimension-virtual-field-refused、dataset 编译 400 DATASET_INVALID),查询侧按它分组一律 400 INVALID_FIELD;但技能文本只说了不能聚合,AI 作者读完仍可能写出这样的维度。
  • 风险与回滚:只改技能文本,不改 schema、键或运行时行为;文件 token 3198 → 3199(上限 3207 不动)。回滚即 revert 本 PR。
  • 先看:skills/objectstack-data/rules/field-types.md 第 131 行(formula 行);第 204 行删掉的注释;.changeset/30824-formula-no-group-key.md。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs | grep field-types,预期 3199 tokens (ceiling 3207; headroom 8) 且退出 0。
  • 需要你做的:本 PR 触及 skills/**(Tier H),席位做契约复审后,请你批准,席位落地。

What changes

  • skills/objectstack-data/rules/field-types.md, the formula row: virtual: not aggregatable, a dataset measure needs a stored field becomes virtual: neither aggregatable nor a dataset or cube dimension (group key); both need a stored field. The pointer to objectstack-ui → Dashboards → Dataset-Bound Widgets stays.
  • The same file, the ### Formula example: the trailing comment // see the Calculated table on the returnType line is deleted — it pointed at a table in the same file, 70 lines up, linked from the file's own contents list, so it carried no rule. This deletion pays for the clause.
  • .changeset/30824-formula-no-group-key.md: '@objectstack/skills': patch.

Nothing else: no ratchet row moves (the count stays under its ceiling), no other file.

Why

Since PR #30815 (card #30773) a dataset or analyticsCubes dimension over a formula field is refused where it is authored: the authoring rule dimension-virtual-field-refused (packages/lint/src/validate-dataset-measure-aggregates.ts) on os validate / os build / os lint and the runtime dataset save, and the dataset compile with 400 DATASET_INVALID (assertDimensionFieldStored in packages/services/service-analytics/src/dataset-compiler.ts). Since #29304 every analytics query grouping by one answers 400 INVALID_FIELD at groupBy.N. The contract review of #30815 (should-fix 3, comment 6677341880) kept the skill line out of that PR because skills/** is Tier H; this is that follow-up. The row teaches only what main refuses today, in the words the rule explanation uses (os explain dimension-virtual-field-refused: "A dimension is a GROUP KEY").

Tests

No test changes: a skill text change, with no executable example added. The touched skill's two eval fixtures (skills/objectstack-data/evals/*.json) are unchanged and still parse.

Acceptance notes

  • The row keeps "not aggregatable" (the pre-existing shorthand). Strictly, a count over a formula is accepted by the measure table (count reads no value); the sibling rule objectstack-ui/rules/dashboards.md already says so under "Total over a computed value", which the row's pointer reaches. Not widened here; noted, not filed (class: none of a/b/c — a reading, no wrong answer from a public door).
  • Wording: "dataset or cube dimension" follows dashboards.md's own phrase ("refused as a dataset or cube dimension and as groupBy"), where the cube is the analyticsCubes entry.

Verification

Readings per the skills PR rules (file before/after, bundle before/after), measured on origin/main 1697d5ca vs this head f89a2740:

Reading Before After
rules/field-types.md lines 252 252
rules/field-types.md bytes / tokens (ceil(bytes/4)) 12790 / 3198 12794 / 3199 (ceiling 3207, headroom 9 → 8)
Package (sum of every skills/**/SKILL.md) lines / bytes 4525 / 226885 4525 / 226885
Bundle total (whole shipped skills/ tree, the ratchet's own line) 227012 227013

Growth paid by deletion: +34 bytes for the clause (56 → 90 bytes), −30 bytes for the deleted comment; net +4 bytes = +1 token. No line was re-wrapped; no ceiling moved.

  • node scripts/check-skills-token-ratchet.mjs on f89a2740: exit 0 — skills/objectstack-data/rules/field-types.md is 3199 tokens (ceiling 3207; headroom 8); 57 authored bundle file(s) within their ceilings.
  • node scripts/pm/dispatch-gates.mjs --tier skills/objectstack-data/rules/field-types.md .changeset/30824-formula-no-group-key.md: claude-fable-5-1 MANDATORY (skills/**, no one-line exit) — the tier this thread ran on.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on f89a2740 derived 43 families; the sweep (</dev/null per line, exit codes landed in gates-ran-f89a2740.list, reconciled with --ran) is reported with every exit code in the os-dev-report comment on skills: the formula row of objectstack-data field-types should say a formula is no group key either (follow-up to #30773) #30824, since this body is written once at creation.
  • CI: the PR's own check-runs are the record for every family CI runs.

Tier H by path (skills/**): draft until a governed approver's APPROVED review; the seat's contract review precedes it; the seat lands.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No schema touched.)
  • 2. Every name I added uses the casing for its kind. (No name added.)
  • 3. No formatter ran; pnpm lint is CI's run.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: n/a.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English.
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

os-tesla and others added 2 commits November 6, 2026 11:42
…ataset or cube dimension either

The `formula` row of `skills/objectstack-data/rules/field-types.md` now says
a formula is neither aggregatable nor a dataset or cube dimension (group
key), and that both need a stored field — the refusals the platform ships:
`dimension-virtual-field-refused` at authoring, `400 DATASET_INVALID` at the
dataset compile, `400 INVALID_FIELD` on every query grouping by one. The
growth is paid by deleting the in-file pointer comment on the Formula
example (the Calculated table sits in the same file).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzWJ43paU98P99Cmu9
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 6, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-tesla · session_01L9qibz5RLVEYDFco93jZ7u

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: f89a2740f964ee9c6a7d4e737fcc360c588e1066
Local-runs: none
Scope: skills/objectstack-data/rules/field-types.md + cited on main: packages/lint/src/validate-dataset-measure-aggregates.ts (the virtual-dimension leg on dataset and cube members, DIMENSION_VIRTUAL_FIELD_REFUSED, virtualDimensionFinding); packages/lint/src/rule-explanations.ts (dimension-virtual-field-refused, "A dimension is a GROUP KEY"); packages/services/service-analytics/src/dataset-compiler.ts (assertDimensionFieldStored; the measure gate's count acceptance); packages/spec/src/data/search-fields.ts (SEARCH_VIRTUAL_TYPES, isVirtualSearchField); skills/objectstack-ui/rules/dashboards.md § Dataset-Bound Widgets (the row's pointer); skills/objectstack-data/SKILL.md and rules/relationships.md (the sibling formula claims); skills/objectstack-data/evals/*.json; scripts/check-skills-token-ratchet.mjs (the discipline header, the field-types.md row at 3207 and its history); .github/workflows/lint.yml (the ratchet step); .claude/agents/os-dev.md (the skills PR readings rule); .changeset/30824-formula-no-group-key.md as the ② input.

Inputs read: card #30824 body and both comments (triage 6678070733, claim 6678864865; no os-dev-report on the card at the time of this record); PR #30921 body, its 2-file list, both commits, the compare against main (2 ahead; 1 behind after #30831 merged at 08:18Z); the check-runs on this head, read four times; record 6677341880 on PR #30815 (the should-fix 3 this card follows up); PR #30831's file list for the serial constraint.

① Derived judgments

② Semver level

'@objectstack/skills': patch is the right carrier and level: packages/skills is the published package whose CHANGELOG already carries skill-text entries, the diff publishes prose only inside the shipped bundle, and no other package moves. Clause-②: no with no arm, and Metadata-protocol: none, on both carriers (PR body and changeset): right, because no accept set widens or narrows; clause2-line.mjs reads a bare no (the arm is optional by design). The changeset's Before / Now reads as the diff does and names the three refusals the row now teaches. No ADR-0087 marker is owed (no migration, no key). Check Changeset and PR-body guards are success on this head. Tier: skills/** is a governed path, so the rules face at CONTRACT_REVIEW_TIER, as --paths printed; the remainder of the diff (the changeset) is the lane seat's ACCEPT at TIER_DEFAULT.

③ Boundary flags

  • Dev flag: the derived gate sweep is still running; no os-dev-report on the card yet. Per the brief the check-runs on the head are the gate verdicts, and this record reads them; the dev's local sweep is the dev's own evidence, and its completion belongs to the seat's ACCEPT, not to this record. At the fourth read (about 08:40Z): 25 completed/success, among them Check Changeset, Governed Surface Queue Guard, PR-body guards, Type Check · source gates (the ratchet step inside it success), Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · ESLint, the three Lint · scoped, Lint · PM dispatch-gates, Build Core, Dogfood Regression Gate, Test UI, Live E2E UI; none failed; Test Core (1/8) and (2/8) still in_progress. The all-green landing precondition is the seat's to read on this head.
  • Dev flag: 3198 → 3199 under 3207, no ceiling row moved. Judged in ① as acceptable; the one thing the rules suggest, re-locking the row at the landed count, is should-fix 1 below.
  • Acceptance note (count over a formula). Answered: a reading of pre-existing shorthand, no wrong answer from a public door; agree with "not filed".
  • Wording "dataset or cube dimension". Matches dashboards.md's multi-value bullet word for word, where the cube is the analyticsCubes entry.
  • Head is 1 behind main. PR docs(skills): teach one naming rule, every package-authored metadata name starts with its namespace #30831 (seat 1's draft, the serial constraint) merged at 08:18Z with 28 files, among them six other skills/objectstack-data/** files; it touches neither rules/field-types.md nor the ratchet row (still 3207 on main), so no conflict; the squash land is the seat's. The PR body's package and bundle totals (227012 / 227013) are therefore stale by docs(skills): teach one naming rule, every package-authored metadata name starts with its namespace #30831's delta — the body's account, not the tree; the per-file reading is unaffected.
  • The branch carries a merge of origin/main (f89a274), which the claim ordered; the repository squash-merges, so no branch commit message reaches main.
  • open_questions: none in the PR body; the os-dev-report is not yet on the card, so none to escalate from it.
  • Draft PR, author objectstack-fleet[bot], Tier H by path: a governed approver's APPROVED review precedes landing; no auto-merge to disarm.
  • Independence: rendered by an isolated contract-review subagent fed the card, the PR, the net diff, the cited code on main read through the API, the ratchet source and the check-runs, not the dispatching seat's conclusions; adopted by the session named in Reviewed-by:.

④ Design judgment

skills/objectstack-data/rules/field-types.md, the formula row, against the spec it teaches on main and against the card (the card carries no eval prompt; the two eval fixtures are unchanged and do not exercise this row; eval 1's must_not_contain: "type: 'formula'" for a search mirror is consistent with "virtual").

  • Against the code. Every clause in the new cell is a refusal that ships: lint on os validate / os build / os lint and the runtime dataset write, compile 400 DATASET_INVALID, query 400 INVALID_FIELD. The remedy is the doors' own. Holds.
  • Trying to break it. (a) An author reads "both need a stored field" and reaches for a summary rollup as the group key: summary is not in SEARCH_VIRTUAL_TYPES, so it is stored and served. (b) A dotted dimension over a parent's formula (account.margin): the lint judges the leaf's declaration, the query door refuses, the compile stands down; the row's generic "a formula" covers it. (c) An author avoids count over a formula because of "neither aggregatable": a capability under-taught, not a wrong refusal, and the pointer's section says "title the widget the count it is". (d) The deleted comment: returnType's values stand in the Calculated table the TOC links; nothing an author needs leaves the file. No break found.
  • Readability and cost. One table cell, +34 bytes, no re-wrap, no second account of the rule elsewhere in the file; the sibling claims (SKILL.md nameField and search-mirror rows, relationships.md:304) say "virtual"/"stored" consistently with it.

Findings (none must-fix):

Implemented-by: claude/project-thread-70v39y
Reviewed-by: session_01L9qibz5RLVEYDFco93jZ7u

VERDICT: PASS

claude and others added 2 commits November 6, 2026 11:57
…ointer; re-lock both ratchet rows at the landed counts

Contract review should-fixes on the field-types formula row: the
dashboards Dataset-Bound Widgets bullet that lists the refused dataset /
cube dimensions and `groupBy` keys now names a `formula` beside the
multi-value types, paid by the `compareTo` paragraph's restated "no second
widget-side vocabulary" sentence. The token-ratchet rows for
`rules/field-types.md` (3207 -> 3199) and `rules/dashboards.md`
(6262 -> 6245) move down to the measured counts, zero headroom.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzWJ43paU98P99Cmu9
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-tesla · session_01L9qibz5RLVEYDFco93jZ7u

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bd2b01548f5eb46bd7a3bbcec7debd2c863d845d
Local-runs: none
Scope: skills/objectstack-data/rules/field-types.md, skills/objectstack-ui/rules/dashboards.md + cited on main: scripts/check-skills-token-ratchet.mjs (the discipline header, the field-types.md row at 3207 and the dashboards.md row at 6262 with their histories, the ceil(bytes/4) convention and the stamp-line exclusion); .github/workflows/lint.yml (the ratchet step inside Type Check · source gates); packages/objectql/src/aggregate-query-shape-door.ts (assertGroupByTargetsAreStored, INVALID_FIELD at groupBy.N); packages/services/service-analytics/src/structured-json-dimension-door.ts (the multi-value group refusal, INVALID_FIELD / 400); packages/services/service-analytics/src/dataset-compiler.ts (assertDimensionFieldStored, DATASET_INVALID / 400; no multi-value dimension gate); packages/lint/src/validate-dataset-measure-aggregates.ts (the virtual-dimension leg on dataset and cube members); .changeset/29304-formula-plain-group-refused-alike.md; packages/skills/package.json and scripts/sync-catalog.mjs (the package ships the whole skills/** tree); .changeset/30824-formula-no-group-key.md as the ② input.

Delta record on the head after f89a2740 (record of record on that head: PASS, two should-fixes). What moved: a second merge of origin/main (fb89c79d) and one commit bd2b0154 touching the ratchet ledger (two rows re-locked), dashboards.md (one bullet, one deleted sentence) and the changeset (one bullet). field-types.md is byte-identical to the reviewed head (12794 bytes) and is not re-judged. Inputs read: the PR's 4-file list and 4 commits, the net diff against main (4 ahead, 1 behind), the delta diff f89a2740...bd2b0154, the file sizes on main and on this head through the API, the check-runs on this head read three times, and the Type Check · source gates job's steps.

① Derived judgments

  • Accept set and public surface: nothing moves. Prose in two shipped skill files, two ledger constants lowered, one changeset bullet. No schema, export, error code, rule id or gate semantics change; a lowered ceiling narrows only what an author may add to a file. TRUE.
  • dashboards.md's bullet now asserts INVALID_FIELD for a formula too; on main that holds at two of the three sites it names and not at the third. As groupBy: the engine door assertGroupByTargetsAreStored refuses a groupBy entry naming a virtual field with INVALID_FIELD / 400 at groupBy.N (analytics: a plain groupBy over a formula field answers DATABASE_ERROR 500 on the native composition, INVALID_FIELD 400 on ObjectQL, and a null group on the preview and in-memory engine #29304), and the service's copy gives the same on the dry run, the preview and the delegated face. As a cube dimension: cubes are not compiled, so a query selecting one reaches the same engine door, INVALID_FIELD. As a dataset dimension: since fix(service-analytics, lint)!: a dataset or cube dimension over a formula field is refused where it is authored, instead of on the first query that groups by it #30815 the dataset compile meets it first, assertDimensionFieldStored throws DATASET_INVALID / 400 (and the authoring lint dimension-virtual-field-refused before that), so the query never reaches INVALID_FIELD. For the multi-value types the parenthetical is exact at all three sites, because the compile carries no multi-value dimension gate (its only dimension gate is the virtual one) and the structured-JSON door answers INVALID_FIELD for a dataset or cube dimension alike. So one of the six site-by-type combinations the sentence now covers answers a different code. The teaching — refused, use a stored field — is right at every site, and the author meets the real code in the lint finding and the compile message when it happens; a should-fix in wording, below, not a wrong refusal taught.
  • The deleted sentence has its home in the clause before it. "compareTo is { kind, dimension? } — the same shape the analytics executor reads (DatasetSelection.compareTo), so what a widget declares is exactly what runs." says that no second widget-side vocabulary exists; "There is no second widget-side vocabulary." restated it. Genuine deletion of restatement, not a re-wrap; nothing an author needs leaves the file. TRUE.
  • The two ledger rows match the measurements. field-types.md on this head is 12794 bytes, ceil(12794 / 4) = 3199, row 3207 → 3199, headroom 0 (2 bytes short of the next unit). dashboards.md on this head is 24979 bytes (main 25005; the bullet's wording and its re-wrap +18, the deleted sentence −44, net −26), ceil(24979 / 4) = 6245, row 6262 → 6245, headroom 0 (1 byte). Neither file carries a release-stamp line, so no exclusion applies. Both are lowerings to the measurement, the ratchet's ordinary direction, which needs no ruling; each comment names the card, the payment and the measured bytes, in the shape of the rows above it. The gate's verdict: step "Published skills token ratchet ([finding] the published skills bundle has no length budget — a maintainer ruling now establishes the principle, but nothing enforces it #10473) + its self-test" inside Type Check · source gates (job 127885945069) completed success on this head, zero non-success steps in the job. TRUE.
  • The bullet's re-wrap moves the line breaks of a 4-line bullet to 5 lines and costs 2 bytes; it is not offered as payment. Fine.

② Semver level

'@objectstack/skills': patch, unchanged: @objectstack/skills ships the repository's whole skills/** tree (sync-catalog.mjs), so the objectstack-ui file rides the same package and level; the changeset's new bullet names the dashboards.md change as the row's pointer landing on the dimension refusal, which is what the diff does. The ledger constants in scripts/ publish nothing and owe no changeset. Clause-②: no, Metadata-protocol: none on both carriers, still right: no accept set widens or narrows. Check Changeset and PR-body guards are success on this head. Tier unchanged: two governed paths, the rules face at CONTRACT_REVIEW_TIER.

③ Boundary flags

  • The diff now exceeds the claim's file surface. Claim 6678864865 named field-types.md (the formula row only), one changeset and a ledger row "only if a measured ceiling moves DOWN", "⛔ every other file". dashboards.md is another skill's file, taken up on the earlier record's should-fix 2, which named a skills seat as carrier and said "not this PR (other file)". The ledger rows are inside the surface (both move down). The seat decides whether its ACCEPT covers the objectstack-ui file in this PR or asks for it to be split; Tier H approval is whole-PR either way, and Governed Surface Queue Guard and the single-writer guard are success on this head. Escalated to the seat, not a defect of the tree.
  • Head is 1 behind main after the second merge; the compare touches none of the four files. The squash land is the seat's.
  • Check-runs on this head at the fourth read (about 09:05Z): 22 completed/success — Check Changeset, Governed Surface Queue Guard, PR-body guards, Type Check · source gates (ratchet step success), Type Check · workspace, Type Check · debt ledger, Type Check · consumer gates, TypeScript Type Check, Lint · CI, release & engine gates, Lint · docs & code guards, Lint · PM dispatch-gates, Lint · ESLint, Lint · scoped · tooling self-tests, Build Core, Plan Test Core shards, Test Core (3/8), Dogfood Regression Gate, Test UI, Live E2E UI, Check PR Size, Auto Label, filter; 5 in_progress — Lint · repo & tooling gates, Lint · scoped · query-options erasure, Lint · scoped · slot lookup & corpus, Test Core (1/8), (2/8); 13 skipped; none failed. The all-green landing precondition is the seat's to read on this head.
  • The earlier record's two should-fixes are both taken up here; its nit ("neither aggregatable" over-states by count) stands as a nit.
  • Independence: rendered by an isolated contract-review subagent fed the delta diff, the cited code on main read through the API, the ledger source and the check-runs, not the dispatching seat's conclusions or the dev's account; adopted by the session named in Reviewed-by:.

④ Design judgment

skills/objectstack-ui/rules/dashboards.md, the multi-value breakdown bullet and the compareTo paragraph, against the spec it teaches on main and against the scenario: an author sent here by the field-types.md formula row's pointer.

  • The reader now lands on the dimension refusal. The bullet names formula beside the multi-value types as refused for a dataset or cube dimension and groupBy, in the same sentence the earlier record asked for. The remedy half of the bullet ("One widget per member, filtered ... or a single-value axis") is written for multi-value fields; a formula's remedy (store the value) is two bullets up under "Total over a computed value" and in the iron rule, so the section as a whole carries it. Holds.
  • Trying to break it. (a) An author declares a dataset dimension over a formula and expects INVALID_FIELD: os validate names dimension-virtual-field-refused first, and the compile answers DATASET_INVALID — a different code, the same verdict and remedy; the author's authored output does not change. (b) "no per-member buckets" is a multi-value fact that now sits after formula in the same parenthetical; it reads as a clause about the multi-value types and misleads no one into expecting buckets from a formula. (c) The compareTo paragraph without its last sentence still states the one-vocabulary rule through "the same shape the analytics executor reads ... exactly what runs". No break.
  • Cost. Net −26 bytes in dashboards.md, both rows re-locked at zero headroom: the steady state the ledger header describes.

skills/objectstack-data/rules/field-types.md: byte-identical to the head judged PASS; the ledger re-lock at 3199 is should-fix 1 of that record, done.

Findings (none must-fix):

  • should-fix — dev, this PR or a later pass at the same cost: the parenthetical (INVALID_FIELD; no per-member buckets) is now claimed for formula too, and a dataset dimension over a formula answers the compile's DATASET_INVALID (after the lint's dimension-virtual-field-refused), not INVALID_FIELD. Either qualify it (for example "(INVALID_FIELD, a dataset compile meeting a formula says DATASET_INVALID; no per-member buckets)") paid by a deletion in the file, or move formula out of the code's reach ("...; a formula likewise, see its field-types row"). Not a wrong refusal taught; the remedy and the verdict are right at every site.
  • nit (carried) — field-types.md "neither aggregatable" over-states by count.

Implemented-by: claude/project-thread-70v39y
Reviewed-by: session_01L9qibz5RLVEYDFco93jZ7u

VERDICT: PASS

os-tesla and others added 2 commits November 6, 2026 13:55
…le, not INVALID_FIELD

The dashboards multi-value breakdown bullet kept `INVALID_FIELD` for the
multi-value types and `groupBy`, and says a `formula` is refused too, as a
dataset dimension at compile with `DATASET_INVALID`. Paid by deleting the
`DatasetSchema` source pointer that `references/_index.md` already carries;
the ratchet row follows the measurement down (6245 -> 6234).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzWJ43paU98P99Cmu9
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-tesla · session_01L9qibz5RLVEYDFco93jZ7u

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: ff46fbe18bb3a4b5b56ed157aff9665bbc279bea
Local-runs: none
Scope: skills/objectstack-data/rules/field-types.md, skills/objectstack-ui/rules/dashboards.md + cited on main: scripts/check-skills-token-ratchet.mjs (the dashboards.md row and its history, the ceil(bytes/4) convention, the generator-owned references/_index.md exclusion); .github/workflows/lint.yml (the ratchet step inside Type Check · source gates); skills/objectstack-ui/references/_index.md line 17 and skills/objectstack-ui/SKILL.md line 284 (the deleted pointer's home and the path to it); packages/services/service-analytics/src/dataset-compiler.ts (assertDimensionFieldStored, DATASET_INVALID); packages/objectql/src/aggregate-query-shape-door.ts (assertGroupByTargetsAreStored, INVALID_FIELD); packages/lint/src/validate-dataset-measure-aggregates.ts (the virtual-dimension leg on dataset and cube members); .changeset/30824-formula-no-group-key.md as the ② input.

Second delta record, on the head after bd2b0154 (record of record there: PASS, one should-fix on the INVALID_FIELD parenthetical). What moved: commit e62de446 (the dashboards.md bullet re-worded, a source pointer deleted from the top of § Dataset-Bound Widgets, the dashboards.md ledger row 6245 → 6234) and a third merge of origin/main (ff46fbe1). field-types.md (12794 bytes), its row (3199) and the changeset are byte-identical to the judged head and are not re-judged. Inputs read: the PR's 4-file list and 6 commits; the net diff against main (6 ahead, 0 behind); the compare bd2b0154...ff46fbe1 and the compare fb89c79d...616311c1 (what main added between the two merges), both filtered to the PR's four paths; the file sizes on main and this head through the API; the check-runs on this head and the Type Check · source gates job's steps.

① Derived judgments

  • Accept set and public surface: nothing moves. Prose in one shipped skill file and one ledger constant lowered. TRUE.
  • The new sentence is accurate. "multiselect/checkboxes/tags and any multiple: true field is refused as a dataset or cube dimension and as groupBy (INVALID_FIELD; no per-member buckets)" is main's text, exact at all three sites for the multi-value types (the compile has no multi-value dimension gate; the structured-JSON door and the engine door answer INVALID_FIELD). "; a formula too, as a dataset dimension at compile (DATASET_INVALID)" states what assertDimensionFieldStored does, and too is true: a formula is refused at every site the predicate names. Nothing in the sentence is false against the cited code.
  • The new sentence is ambiguous on one reading. "a formula too, as a dataset dimension at compile" admits two readings. Inclusive: a formula is refused everywhere the predicate says, and at the dataset compile the code is DATASET_INVALID. Restrictive: a formula is refused as a dataset dimension at compile, full stop, with too attaching to that one site. English carries both ("dogs too, in the house"), and the restrictive one is at least as natural because the qualifier follows too with no bracket. Under it this file no longer tells the reader that a formula is refused as a cube dimension or as groupBy with INVALID_FIELD, and nowhere else in dashboards.md says so (its only other formula sentences are the measure-side ones under "Total over a computed value"). It also does not say the opposite, and the reader arrives from the field-types.md row that states all three sites ("neither aggregatable nor a dataset or cube dimension (group key)"), while the authoring lint dimension-virtual-field-refused refuses a formula in analyticsCubes[].dimensions.KEY.sql and in datasets[].dimensions[].field alike before any query. A should-fix in wording at equal cost, below; not a wrong refusal taught and not a lost capability.
  • The deleted pointer has its home. "The dataset shape is DatasetSchema — see node_modules/@objectstack/spec/src/ui/dataset.zod.ts." is carried by skills/objectstack-ui/references/_index.md line 17 ("node_modules/@objectstack/spec/src/ui/dataset.zod.ts — Analytics Dataset — the one semantic layer (ADR-0021)"), a generator-owned file (gen:skill-refs, excluded from ratcheting precisely because it tracks the spec) that SKILL.md line 284 sends the reader to for the Zod sources. The sentence carried a pointer and no rule; its deletion is genuine, not a re-wrap. TRUE.
  • The ledger row matches. dashboards.md on this head is 24935 bytes (main 25005; the prior head 24979; this commit −44 net), ceil(24935 / 4) = 6234, row 6262 → 6234, headroom 0 (1 byte short of the next unit); no stamp line applies to a rules/ file. A lowering to the measurement, the ratchet's ordinary direction, no ruling needed; the comment names the card, both payments and the measured bytes. field-types.md is unchanged at 12794 bytes, row 3199, headroom 0. The gate's verdict is the ratchet step inside Type Check · source gates, read in ③.
  • The merge brought nothing into the PR's files. fb89c79d...616311c1 (the seven main commits the third merge carries: fix(scenarios): the loader refuses an operation its persona cannot reach, and two cards stop writing hidden fields #30846, test(formula): judge each distinct sweep refusal once, and give the validateExpression sweep a measured budget #30919, docs(qa): drop the retired currency prompt from the studio-authoring app step #30929, i18n(service-settings): ship the built-in settings pages in ru, de and ko #30931, feat(spec): a field declares the key(s) it was renamed from — renamedFrom on FieldSchema #30934, docs(automation): the notify click-through default says a before-create run links to its record only when the caller wrote the id #30926, fix(service-settings): a settings change's audit row records the value before and after, with secrets as digests #30937) touches none of the four paths; the scripts/check-skills-token-ratchet.mjs and dashboards.md entries in bd2b0154...ff46fbe1 are e62de446's own. The net diff against main on this head shows exactly the PR's four files, 0 behind. TRUE.

② Semver level

Unchanged from the prior record: '@objectstack/skills': patch, Clause-②: no, Metadata-protocol: none on both carriers; the changeset's dashboards.md bullet ("names a formula beside the multi-value types as refused for a dataset or cube dimension and groupBy") still describes the sentence on its inclusive reading, which is the one the fix below makes the only reading. The ledger constant publishes nothing. Check Changeset is read in ③.

③ Boundary flags

  • Check-runs on this head at the last read (14:00Z): 18 completed/success — Check Changeset, Governed Surface Queue Guard, PR-body guards, Type Check · source gates (its step "Published skills token ratchet ([finding] the published skills bundle has no length budget — a maintainer ruling now establishes the principle, but nothing enforces it #10473) + its self-test" completed/success in job 127931241618, zero non-success steps), Type Check · workspace, Type Check · debt ledger, Lint · PM dispatch-gates, Lint · ESLint, Lint · docs & code guards, Build Core, Plan Test Core shards, Test Core (3/8), Dogfood Regression Gate, Test UI, Live E2E UI, Check PR Size, Auto Label, filter; 8 in_progress — Lint · repo & tooling gates, Lint · CI, release & engine gates, Lint · scoped · query-options erasure, Lint · scoped · tooling self-tests, Lint · scoped · slot lookup & corpus, Type Check · consumer gates, Test Core (1/8), (2/8); 13 skipped; none failed. The all-green landing precondition is the seat's to read on this head.
  • The prior record's should-fix is taken up: the INVALID_FIELD parenthetical no longer claims a formula; the compile's DATASET_INVALID is named. Its carried nit ("neither aggregatable" over-states by count) stands.
  • File surface: unchanged from the prior record's flag — dashboards.md lies outside claim 6678864865's surface; the seat decides whether its ACCEPT covers it; Governed Surface Queue Guard and the single-writer guard are success on this head.
  • Head is 0 behind main at this read; the squash land is the seat's.
  • Independence: rendered by an isolated contract-review subagent fed the delta diffs, the cited code and skill files on main, the ledger source and the check-runs, not the dispatching seat's conclusions or the dev's account; adopted by the session named in Reviewed-by:.

④ Design judgment

skills/objectstack-ui/rules/dashboards.md, the multi-value breakdown bullet and the opening paragraph of § Dataset-Bound Widgets, against the spec on main and the scenario: an author sent here by the field-types.md formula row.

  • Against the code. Every clause is a refusal that ships, with the right code at the site it names. Holds.
  • Against the scenario. The reader who arrives from the formula row already holds "neither aggregatable nor a dataset or cube dimension (group key)"; this bullet now names the dataset-dimension refusal and its code where they land. On the restrictive reading the cube and groupBy sites for a formula are unstated in this file, never contradicted; os validate refuses either declaration by name. Holds, with the wording should-fix.
  • Trying to break it. (a) An author takes "a formula too, as a dataset dimension at compile" to mean a formula is fine as a cube dimension: the field-types row and the lint say otherwise, and nothing here says it is fine. (b) An author looks for the dataset schema source: SKILL.md → references/_index.md line 17, kept current by the generator. (c) The bullet's remedy half is still the multi-value one; a formula's remedy stays two bullets up and in the iron rule. No break.
  • Cost. Net −70 bytes against main in dashboards.md; both rows at zero headroom.

Findings (none must-fix):

  • should-fix — dev, this PR or a later pass, inside the row's zero headroom or paid in the file: make too unambiguously attach to the whole predicate, for example "...(INVALID_FIELD; no per-member buckets); so is a formula (at a dataset's compile, DATASET_INVALID)." — "so is a formula" restates the full predicate (dataset or cube dimension, groupBy) and the bracket carries the one differing code; it is 11 bytes shorter than the current clause (61 against 72), so it needs no payment.
  • nit (carried) — field-types.md "neither aggregatable" over-states by count.

Implemented-by: claude/project-thread-70v39y
Reviewed-by: session_01L9qibz5RLVEYDFco93jZ7u

VERDICT: PASS

os-tesla and others added 2 commits November 6, 2026 14:07
…eads as a general refusal

"so is a `formula` (at a dataset's compile, `DATASET_INVALID`)" replaces
the clause that read as if a formula were refused only as a dataset
dimension; the ratchet row follows the measurement down (6234 -> 6232).

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01L9qibzWJ43paU98P99Cmu9
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:skills#2 · os-tesla · session_01L9qibz5RLVEYDFco93jZ7u

维护者速读

席位意见:契约复审 PASS,三轮共四条建议修改都已补上(上限下调、UI 技能补公式、错误码分开、措辞不再歧义),无必改。可以批准。

席位核对

ACCEPT · seat domain:skills#2 · head 2311bb30 · 2026-11-06T14:26Z

  1. Shape: Fixes #30824, draft, base main, 4 files (skills/objectstack-data/rules/field-types.md formula row, skills/objectstack-ui/rules/dashboards.md one bullet, scripts/check-skills-token-ratchet.mjs two rows down, .changeset/30824-formula-no-group-key.md '@objectstack/skills': patch), 8 commits ahead, 0 behind.
  2. Ruling: skills: the formula row of objectstack-data field-types should say a formula is no group key either (follow-up to #30773) #30824 is the follow-up of PR fix(service-analytics, lint)!: a dataset or cube dimension over a formula field is refused where it is authored, instead of on the first query that groups by it #30815 (ruling on contract review 6677341880, should-fix 3); triage 6678070733 (p3); Claim 6678864865, amended 6684688329 (seat-directed widening to dashboards.md and the ratchet script after review 6684262819).
  3. Contract review: PASS 6684262819 at f89a2740 (two should-fixes); delta PASS 6684677925 at bd2b0154 (one should-fix: DATASET_INVALID for a formula dataset dimension); second delta PASS 6686624863 at ff46fbe1 (one wording should-fix, with the reviewer's own replacement text). The head moved to 2311bb30 by 5c5d2de7, which applies that text verbatim (dashboards.md L103–105 re-flowed, seat-checked against compare ff46fbe1...2311bb30), plus a merge of main; the ratchet row moved down 6234 → 6232 at the measurement. Clause-②: no, Metadata-protocol: none.
  4. Gates / check-runs: dev reports 43 families at f89a2740 (41 exit 0, dual-build-cjs-loads and lean-entry-closure NOT MEASURED, CI runs them; report 6684239925), 59/61 exit 0 at bd2b0154 (same two NOT MEASURED), and at 2311bb30 the ratchet (+ --self-test 97) and the fast doc families exit 0. Ratchet rows at headroom 0: field-types.md 3199 (12794 bytes), dashboards.md 6232 (24926 bytes). On head 2311bb30: 42 check-runs, 29 success, 13 skipped, 0 failed; mergeable_state clean.
  5. Accepted deviations: the PR body is not patched; its package and bundle totals are stale and its file list predates the widening. This ACCEPT carries the head-2311bb30 facts. Nit carried from the review: "neither aggregatable" over-states by count, which is pre-existing shorthand.
  6. Landing: GOVERNED, Tier H (skills/**). The seat readies the PR and the relay's governed-review leg requests the approvers; no auto-merge is armed by the seat.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 6, 2026 14:27
@os-zhuang
os-zhuang added this pull request to the merge queue Nov 6, 2026
Merged via the queue into main with commit 95ba410 Nov 6, 2026
43 checks passed
@os-zhuang
os-zhuang deleted the claude/project-thread-70v39y branch November 6, 2026 16:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills: the formula row of objectstack-data field-types should say a formula is no group key either (follow-up to #30773)

2 participants