Skip to content

docs: a cube member's sql is a column reference, in ADR-0021, the dashboards skill and the cube checklist item - #21071

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-20943-adr0021-note
Oct 1, 2026
Merged

os-zhuang merged 2 commits into
mainfrom
claude/issue-20943-adr0021-note

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Part of #20943

Clause-②: no

Ruling D's governed half (Tier H, docs only). The code half landed as #20998 (5d5e679873). Five files, no package source:

  • docs/adr/0021-analytics-dataset-semantic-layer.md: a dated note at the end of D1 naming MetricSchema / DimensionSchema sql in packages/spec/src/data/analytics.zod.ts.
  • skills/objectstack-ui/rules/dashboards.md: the iron rule no longer escalates to a hand-authored Cube with raw SQL, or to a formula field (virtual, no stored column); the Level A table drops "any custom-SQL metric". File: 468 lines and 6211 tokens before and after. Package: every skills/**/SKILL.md together is 4395 lines before and after (none is touched).
  • skills/objectstack-ui/evals/analytics-inline-vs-dataset.json (eval 2) and skills/objectstack-ui/evals/README.md: the same skill's eval fixture and its index no longer name a Cube as an escalation. Eval 2's must_contain drops "Cube", which a correct answer need no longer say. Tokens: the eval file goes from 1102 to 1097 (ceiling 1102), and the README from 287 to 286 (ceiling 289). Lines: 57 and 31, unchanged.
  • docs/qa/platform-checklist/areas/dashboards.json: dashboards.cube-query revision 3 drops the showcase cube's done_rate, which now lives on its dataset.

No changeset: none of the five paths ships in a package's files[].

维护者速读(草稿)

  • 改了什么: 只改五个文档文件,不改代码。
    • ADR-0021 在 D1 末尾加一条带日期的说明:分析 cube 的度量和维度,sql 只能写列引用;写表达式,解析时就会被拒。这是裁决 D,代码已随 feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 落地。
    • 看板技能不再教“dataset 表达不了,就去手写带原始 SQL 的 Cube”,也不再把公式字段当出路。改为:存成对象上的字段(汇总字段,或存好计算值、分桶的字段),或者写应用代码。
    • 同一技能包里的评测文件和它的说明也一并改了:评测的标准答案不再把 Cube 当出路。
    • QA 清单的 cube 条目删掉 done_rate,它已经搬到 dataset 上了。
  • 为什么改: 代码那一半落地后,这几处文字都不对了。看板技能是 AI 写元数据时读的教材:按旧教法写出的 cube,解析时就会被拒。
  • 风险与代价(含回滚): 只改文字,不发版。看板技能规则文件的行数和 token 数改前改后一样,两个评测文件略缩短。合并后是一个提交,回滚就是 revert 它。
  • 席位意见: 建议合并。终稿见 PR 上的维护者速读评论。
  • 你要做的: 看一眼 ADR 那条说明和技能里那一段的措辞,同意就批准。

Generated by Claude Code

… note, dashboards skill, cube checklist item)

The governed half of ruling D. ADR-0021 gets a dated note under D1
naming MetricSchema / DimensionSchema sql in data/analytics.zod.ts. The
objectstack-ui dashboards rules stop escalating past a dataset to a
hand-authored Cube with raw SQL, and stop naming a formula field as a
stored escape. The dashboards.cube-query checklist item drops the
showcase cube's done_rate, which now lives on its dataset.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 68593d46d88dbd3843b1332bd5eb02124fb0a763
Local-runs: none

Inputs read: card #20943 (body and all 10 comments: the seat analysis 5919778230, ruling D 5921156712, claim 5921298734, dev reports 5922668750 / 5922922632 / 5923452954 / 5924841117, seat rulings 5922695050 / 5924874583, landing record 5924625065); PR #20998 (merged 5d5e679873); PR #21071 body and file list; git diff origin/main...68593d46d8 (3 files, +18 / -11; the merge base is 6073bb96b8 and no commit on main since touches the three paths, so the three-dot diff IS the net diff); the tree at the head via git show / git grep; the head's check-runs, read last.

① Derived judgments

Accept set and public surface. The diff touches docs/adr/**, skills/** and docs/qa/** only; no schema, no runtime, no generated artifact. It changes no accept set. Every public-surface change it implies is text, judged sentence by sentence below.

A. ADR-0021 note (D1, after the RLS paragraph) — every sentence tested against the tree at the head, verdict RIGHT.

  • "carries 'zero raw SQL / zero raw expressions' to the members of an analytics cube; it shipped in feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998" — right: ruling D 5921156712 retires raw expressions in a cube member's sql; feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 merged as 5d5e679873. "members" is the precise word: the cube-level CubeSchema.sql is NOT narrowed (the dev's out-of-scope finding on 5922668750), and the note does not claim it is.
  • "the sql of a cube measure (MetricSchema) and of a cube dimension (DimensionSchema) is a column reference: a field of the cube's object, a relationship path ending in one, or '*' (CUBE_MEMBER_SQL)" — right: analytics.zod.ts:240 CUBE_MEMBER_SQL = /^(?:\*|[A-Za-z_][A-Za-z0-9_]*(?:\.[A-Za-z_][A-Za-z0-9_]*)*)$/, applied as .regex() at MetricSchema.sql (:330) and DimensionSchema.sql (:394). '*' is admitted on both members (the dev's Q2, ruled A on 5922695050), and the note says '*' without the ruling's "for a count" qualifier, which is the tree's truth, not an over-statement. The three symbol anchors name symbols that exist at the head (export const MetricSchema :282, export const DimensionSchema :367, const CUBE_MEMBER_SQL :240); Lint & Repo Gates (success) carries check:adr-symbol-anchors and check:adr-links.
  • "A SQL expression there (a CASE, an aggregate, a ratio of aggregates) is refused at parse" — right: the regex admits no space, parenthesis, operator or quote; refusal is invalid_format with the prescription at every parse door (defineCube, StackSchema.parse). True on main now.
  • "A measure's refusal names the D1 form: a measure with its own filter for a conditional count or sum, and derived: { op, of } over named measures for a ratio, sum, difference or product" — right, verbatim against CUBE_METRIC_SQL_EXPRESSION_REFUSED (:248-258); dataset.zod.ts carries measure filter (:196) and DerivedMeasureOp = ['ratio','sum','difference','product'] (:136).
  • "A dimension's refusal says to keep a computed bucket as a field of the object" — right against CUBE_DIMENSION_SQL_EXPRESSION_REFUSED (:260-265).
  • More or less than ruling D: the note says nothing MORE than the ruling. It says LESS in one respect: it omits the ruling's interim and services follow-up (the gate's stand-down branch at analytics-service.ts:411 still exists at the head, unreachable for any parsed cube, deletion owed to service-analytics: delete the analytics field gate's stand-down on an authored cube expression member once #20943 retires raw expressions in a cube member's sql (the branch becomes unreachable) #20965). The "Governed half" bullet of 5921156712 asks only for "a dated note under its decision naming the spec symbol", which this is; the omission is not a defect of the contract record, noted here so the reader of the ADR does not take the runtime branch as gone.
  • ADR-0021 elsewhere: read whole at the head. :16 (dated Correction), :118 (the principle, now enforced one layer lower), :123 ("expressible four ways", decision-time context), :358-364 (Finding 2, dated 2026-05-31, about joins and RLS) — none is made false by this retirement. :140-143 and :381 (D-A: "the author-facing Cube DSL is retired/absorbed") were already at odds with an authored analytics_cube before ruling D (defineCube, defineStack({ analyticsCubes }), the showcase cube) — pre-existing drift, agreed with the dev's flag, not this PR's.

B. skills/objectstack-ui/rules/dashboards.md (AI-facing, published via npx skills add) — the rewritten sentences, verdict RIGHT.

  • :70 the escalate cell drops "or any custom-SQL metric" — right: a cube member admits no expression, so no layer offers a custom-SQL metric; the one in the tree (done_rate) is now a dataset measure-filter count plus a derived ratio (chart-gallery.dataset.ts:34-40).
  • :78-80 "escalate to a stored field on the object (a summary rollup, or a field holding the computed value or bucket) or app code, not to a Cube: a cube member's sql is a column reference" — right on each limb. summary is a live stored field: field.zod.ts:1669-1697 (summaryOperations with function count/sum/min/max/avg and a filter), recomputed by the engine (objectql/src/engine.ts:10431-10774, summary-aggregate.ts, summary-backfill.ts), a numeric column in SQL (sql-driver.ts:19438). Is a Cube still a valid escalation for ANY need in the right column? No: cube joins are declared by target only with the ON clause derived from the FK on the base object (showcase.cube.ts:67-86, analytics.zod.ts:428-502), so a cube reaches the same to-one grain a dataset include reaches — no grain-changing or to-many join; a dimension sql is a column, so no CASE bucket or bin; AggregationMetricType has no list aggregation; the analytics query schema has no having, window or union (grep over api/analytics.zod.ts and data/analytics.zod.ts); scalar math and aggregate-of-aggregate are expressions. "explicit joins" in the old text was already false before this PR (joins derived since [finding] Cube.joins[].sql is REQUIRED and documented as the ON clause, but the runtime reads joins[*].name only — an authored join condition is silently REPLACED by a synthesised FK equality under a 200 #18612). For HAVING / window / union the honest escalation left is "app code", which the sentence names.
  • Dropped "formula field" (declared beyond the retirement) — right, the dev's evidence holds: sql-driver.ts:19561-19562 "Virtual — no column"; aggregate-field-type-compatibility.ts (spec) refuses sum/avg/min/max over formula on the storage ground; dataset-compiler.ts:355-357 and :389 / :713 enforce it; skills/objectstack-formula/SKILL.md:407-411 already says "Denormalise onto a stored field". A formula field can carry no analytics need (it cannot be aggregated or grouped in SQL), so naming it as an escalation was false text and dropping it is a correction, not scope creep.
  • Unchanged sentences in the same Level A section (:59-100): :75-77 (dataset: no raw SQL, no hand-authored joins, no window/having) true; :85-88 (to-many rollup → stored rollup field) true and now consistent with the iron rule; :91-94 (CASE / bins / computed dimensions → not a dataset) true, names no target; :95-100 true. The Analytics Cubes section :433-468 uses identifiers and '*' only; Rule 1 is the cube-level sql. Nothing in the file is left false.
  • File readings in the PR body: 468 lines before and after (verified by wc -l on both blobs); 24842 → 24843 bytes; the ratchet measures ceil(bytes/4) = 6211 both sides against the 6252 ceiling (check-skills-token-ratchet.mjs:523); skills/**/SKILL.md sum 4395 lines both sides (verified). All right.

C. docs/qa/platform-checklist/areas/dashboards.json item dashboards.cube-query — verdict RIGHT.

  • Lifecycle "Change" obeyed: fields edited, revision 2 → 3, a history entry appended with what and why; revision equals the last history entry's, the rule the validator's header names (check-platform-checklist.mjs:28-30).
  • Every clause names only what the showcase declares at the head: measures count / total_estimate_hours / avg_estimate_hours and dimensions status / priority / due_date / assignee (showcase.cube.ts:22-66); registered as analyticsCubes (coverage.ts:198-200). The history entry's "declared on the showcase_task_metrics dataset ... as a filtered count over a count" is right (chart-gallery.dataset.ts:34-40); "steps, reconciliation clauses and the four dimensions unchanged" is right against the diff. No other done_rate remains anywhere under docs/qa/platform-checklist. The unchanged source anchor showcase.cube.ts#showcase_project and the analytics.ts #2852 line citation are pre-existing and untouched.
  • The validator itself is out of per-PR CI; the dev's exit 1 on identity-auth.json twoFactor reproduces on main and is not this diff's (the file list proves this PR touches neither that area file nor plugin-auth).

D. PR body, as text the maintainer acts on. "Part of #20943", "Clause-②: no", three files, no package source, "No changeset: none of the three paths ships in a package's files[]" (verified: no package.json under packages/ / examples/ / apps/ lists a files[] entry naming skills or docs) — all right. The ## 维护者速读(草稿) section: 改了什么 / 为什么改 / 风险与代价 / 你要做的 — each sentence true at the head ("按旧教法写出的 cube,解析时就会被拒" is exactly the parse refusal; "回滚就是 revert 这一个提交" — the branch holds one commit). One sentence of the body is over-broad, and it is the blocking finding:

E. BLOCKING — the same published skill still teaches the retired escalation, outside the three files.

  • skills/objectstack-ui/evals/analytics-inline-vs-dataset.json:18 (eval 2, expected_output): "Escalates to a hand-authored Cube (raw SQL) or a stored/materialized rolling field" — false at the head (a cube member's sql is a column reference; a Cube cannot carry a window or rolling calculation), and the direct opposite of the rule this PR writes at dashboards.md:78-80 ("not to a Cube"). The evals README says these fixtures score whether an AI applies the skill's rules, so this is text a grader acts on: an answer that follows the rewritten rule is now graded against an expected answer that endorses the retired shape.
  • skills/objectstack-ui/evals/README.md:9-10: "when it must escalate to a Cube or a stored rollup field" — the same false escalation, in the fixture index.
  • Both are skills/objectstack-ui/** (governed Tier H, shipped by npx skills add), both were made false by feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998, and this PR is the Tier H carrier the ruling names for skills/**; the dev contract the dev itself cites (5922668750 Q3) says published text the change makes false is fixed in the same round. The PR body's "the iron rule no longer escalates to a hand-authored Cube with raw SQL" is therefore true of rules/dashboards.md and false of the skill as published.
  • The fix is two sentences, and the token ratchet constrains it: analytics-inline-vs-dataset.json sits exactly at its ceiling (4406 bytes = 1102 tokens of 1102, zero headroom) and evals/README.md at 287 of 289. Rewrite eval 2's expected output to escalate to a stored or materialized rolling field or app code and NOT to a Cube (shorter than the current text, so the ceiling holds; must_contain: ["window","Cube"] still passes on an answer that says "not a Cube", or drop "Cube" from it), and re-spell README :9-10 to "when it must escalate to a stored rollup field or app code" (shorter). No other file under skills/ or content/docs/ teaches a cube member expression (grep over custom-SQL, custom SQL, CASE WHEN, raw SQL, SQL expression at the head; the generated references/data/analytics.mdx already carries the new describe text).

② Semver level

Right. The diff publishes nothing from any package: docs/adr/**, skills/** and docs/qa/** are in no released package's files[] (measured above; AGENTS.md rule 3 reserves skip-changeset for exactly this case), skills/ reach consumers from GitHub, not npm. Clause-②: no and the skip-changeset label (present on the PR, applied by the seat in 5924874583) are right; Check Changeset is skipped on the head accordingly. No changeset is owed. The BREAKING changeset for the retirement itself landed with #20998, where it belongs.

③ Boundary flags

  • Dev 5924841117 open question Q1 (speed-read section and package reading): seat answered A; verified both are now in the body (## 维护者速读(草稿) present; "every skills/**/SKILL.md together is 4395 lines" verified).
  • Dev 5924841117 declared beyond-scope item ("formula field" dropped): judged in ①B — right, keep.
  • Dev 5924841117 out-of-scope finding (ADR-0021 D-A row and D1 sketch say the Cube DSL is absorbed while analytics_cube is still authored): agreed — decision-time text, pre-existing, not made false by this retirement; no action in this PR; whether to file an ADR-0021 revision is the seat's call, not blocking.
  • Dev 5924841117 seat actions: skip-changeset — applied, verified; speed-read section — inserted, verified. "Check Changeset is expected to go red" — overtaken: it is skipped on the head.
  • Dev 5924841117 "check:platform-checklist exit 1 on twoFactor": main's, reproduced by the dev on a detached origin/main; this diff touches neither the area file nor plugin-auth. Not this PR's.
  • Carried from 5922668750 / 5922695050 (the code half): the dashboards.md :70 / :78 finding and the checklist done_rate finding — both delivered here (①B, ①C). The CubeSchema.sql description ("Base SQL statement or Table Name") stays outside ruling D (members only) — the ADR note's "members" wording keeps it so; not this PR's. The check:i18n-coverage derivation gap (5922922632) is not reachable from a diff with no examples/ path.
  • Escalated by this review (①E): the skill's eval fixture and evals README still name a raw-SQL Cube as an escalation. Owner: this PR (Tier H skills/** carrier). Not a seat decision — it is a false sentence with a mechanical fix.
  • Observation, not this PR's text and not filed: CUBE_MEMBER_SQL_RETIRED (analytics.zod.ts:242) says the expression "was retired in @objectstack/spec 17" while packages/spec is at 17.5.0 and the retirement is a BREAKING major-18 D3 entry per feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998's own body; the ADR note repeats no version, so nothing in this diff inherits it. For the spec seat.

Check-runs on 68593d46d8, fetched 2026-10-01T05:06Z, 46 runs deduped by name to 31 keeping the newest started_at: 21 success, 10 skipped, 0 failure, 0 in progress. Successes include Governed Surface Queue Guard, Lint & Repo Gates (the ADR link and symbol-anchor gates, the skills ratchets), Check Documentation Links, Part-of PR must not also close its card, Test Core and its six shards, the four Type Check jobs and TypeScript Type Check, Dogfood Regression Gate. Skipped: Auto Label, Build Core, Build Docs, Check Changeset, Check PR Size, Console Pin Gate, the dogfood shard matrix, Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance. None still running. The gates are green; the verdict below is on ①E alone.

Implemented-by: claude/issue-20943-adr0021-note
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: FAIL

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-10-01T05:09Z · rendered by the seat's at-tier review subagent on this head. The seat read its served tier family from the subagent transcript before posting.

  • FAIL adopted, on ①E alone. The same published skill still names a raw-SQL Cube as an escalation, in skills/objectstack-ui/evals/analytics-inline-vs-dataset.json (eval 2's expected output) and skills/objectstack-ui/evals/README.md. This PR is the skills/** carrier, so both sentences are fixed here.
  • Cut round: the dev is resumed for exactly those two sentences. Each is re-spelled shorter, because the eval file sits at its token ceiling. No other file changes. A delta review on the new head follows.
  • The observation on CUBE_MEMBER_SQL_RETIRED ("retired in @objectstack/spec 17"): read, no action. The house spelling for a retirement shipped on the 17.x line is the major alone: "removed in @objectstack/spec 17" appears 112 times in packages/spec/src.
  • needs:contract-review stays on the PR and [Decision] analytics field gate (#20917): an authored cube member whose sql is an expression — keep the stand-down, judge its identifiers, refuse it, or retire expressions #20943 until the new head passes.

Generated by Claude Code

Eval 2's expected output escalates a rolling window to a stored or
materialized rolling field or app code, not a Cube: a cube member's sql
is a column reference. Its must_contain drops "Cube", which a correct
answer no longer needs to name. The evals README names the same
escalation: a stored field or app code. Both files shrink.

Claude-Session: https://claude.ai/code/session_01Sfe5YjBLwB9J3y8fvm2xq1
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5ed7f87157746d717dfc6164bc9ccc0d99facd29
Local-runs: none

Delta re-review of a docs-only Tier H PR, on the one commit appended since the FAIL record 5925160445 (at 68593d46d8). Inputs read: card #20943 (body and all 11 comments: ruling D 5921156712 and its "Governed half" bullet, the seat ruling 5922695050 and its out-of-scope findings, the landing record 5924625065, the dev report 5924841117, the seat ruling 5924874583, the cut-round report 5925234326); the prior FAIL record and its adoption 5925160445; PR #20998 (merged as 5d5e679873: CUBE_MEMBER_SQL and the two refusal messages read at origin/main); PR #21071 body and file list at this head; git diff 68593d46d8 5ed7f87157 (2 files, +3 / -3); the net diff git diff 6073bb96b8 5ed7f87157 (5 files, +21 / -14; 6073bb96b8 is the merge base with origin/main, and none of the 18 commits on main since it touches any of the five paths, so the two-dot and three-dot diffs coincide); the tree at the head and at origin/main (b3d7a70864) via git show / git grep; the head's check-runs, read last.

① Derived judgments

Accept set and public surface. The delta touches two files under skills/objectstack-ui/evals/; the net diff touches docs/adr/**, skills/** and docs/qa/** only. No schema, runtime or generated artifact moves, so no accept set changes. Every public-surface change is text, judged sentence by sentence.

A. The delta — the two re-spelled sentences, verdict RIGHT.

  • skills/objectstack-ui/evals/analytics-inline-vs-dataset.json:18 (eval 2 expected_output): "Escalates to a stored/materialized rolling field or app code, not a Cube — it does not pretend a dataset date-bucket or a plain measure expresses a moving window." True against the tree now on main: a cube member's sql is CUBE_MEMBER_SQL (analytics.zod.ts:240, an identifier, a dotted path or '*'), applied at MetricSchema.sql (:330) and DimensionSchema.sql (:394), so a Cube can carry no window or rolling calculation; the analytics query grammar has no having, window or union (grep over api/analytics.zod.ts and data/analytics.zod.ts at origin/main finds "window" only in the compareTo and date-range prose), and compareTo runs exactly previousPeriod and previousYear (api/analytics.zod.ts:332-334), as the sentence's first half says. The escalation named matches the rewritten iron rule at rules/dashboards.md:78-80 ("a stored field on the object (a summary rollup, or a field holding the computed value or bucket) or app code, not to a Cube") limb for limb. It became true when feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 landed, and is true on main now.
  • skills/objectstack-ui/evals/README.md:10: "when it must escalate to a stored field or app code". True and consistent with the iron rule's own term, "a stored field"; it covers eval 2 (a stored or materialized rolling field) and eval 3 (a stored rollup / summary field), which "a stored rollup field" (the adoption's suggested spelling) would not. The dev's declared departure from the suggested spelling is right, and the file shrank (1148 to 1143 bytes).
  • The sentence pair the FAIL named is gone: the fixture names a Cube only as a non-target, and the index names none.

B. Eval 2's assertions still grade the behaviour its expected output describes, verdict RIGHT.

  • must_contain: ["window"]: a correct answer recognises "a window/rolling calculation is BEYOND the dataset envelope"; "window" is the skill's own term for that right-column row (dashboards.md:74), and the prompt itself never says "window" (it says "rolling", "moving average", "running total"), so the word is a real signal that the rule was applied, not an echo. The expected output uses it twice.
  • must_not_contain: ["dateGranularity"] is unchanged, so the classic wrong answer (date bucketing as the fix) still fails, and eval 2's note about that strictness is still true.
  • Dropping "Cube" is right on three counts the dev gave and the tree confirms: the expected output names a Cube only as a non-target, so a correct answer need not say the word; a substring check cannot tell "not a Cube" from "a Cube"; and putting "Cube" in must_not_contain would flag the correct "not a Cube". Before the delta the assertion REQUIRED the retired answer; now it requires nothing of it. Residual, inherent to substring assertions and not this PR's: an answer that escalates to a raw-SQL Cube and also says "window" passes the string checks; the expected-output text is what a grader reads for the escalation target, and the evals README calls the assertions "string checks used to score the output", not the verdict. No in-tree runner reads these fixtures (git grep must_contain|expected_output outside skills/ hits only packages/console/CHANGELOG.md); check-skills-token-ratchet.mjs is the one script that reads evals/**, and it is green under Lint & Repo Gates.

C. Nothing else under skills/ names a Cube or raw SQL as an escalation target, verdict RIGHT (independently grepped at the head). The word "cube" on a line with escalat / raw sql / custom sql / hand-authored / expression: the new "not a Cube" at evals/analytics-inline-vs-dataset.json:18, and rules/dashboards.md:466 (Cube Rule 1, "do not put raw SQL there", a prohibition). "escalat" across skills/: dashboards.md:62 / :66 / :78 (the envelope, now a stored field or app code), :373 (the drawer escape hatch, not analytics), evals/README.md:10, the eval json :3 / :18, navigation.md:68 (unrelated), and support-case domain examples in objectstack-ai, objectstack-automation, objectstack-data and objectstack-formula. "raw sql" / "custom sql" / "CASE WHEN" / "SQL expression": dashboards.md:76 (no raw SQL), :466 (Rule 1), objectstack-data/rules/indexing.md:158 (a partial index issued as raw SQL from a runtime migration, unrelated). The dev's grep in 5925234326 is confirmed.

D. Nothing else changed, verdict RIGHT. docs/adr/0021-analytics-dataset-semantic-layer.md (14af595562), docs/qa/platform-checklist/areas/dashboards.json (d13a0f6410) and skills/objectstack-ui/rules/dashboards.md (a3afc088af) have the same blob hash at 68593d46d8 and at this head. The delta is +3 / -3 over exactly the two eval files; the net diff is five files +21 / -14, matching the PR's file list. One commit appended, no merge or rebase.

E. The prior record's judgments, re-read at this head against current main (18 commits landed since the merge base; three touched files the prior record cited as evidence, so those citations were re-checked rather than re-derived).

  • ADR-0021 note (:200): every sentence still true. analytics.zod.ts is untouched on main since the merge base: MetricSchema :282, DimensionSchema :367, CUBE_MEMBER_SQL :240 resolve; the refusal texts :248-258 and :260-265 still say what the note says they say; feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 is merged. The note still says nothing about the services interim (the stand-down branch is at analytics-service.ts:411 on main, unreachable for a parsed cube, service-analytics: delete the analytics field gate's stand-down on an authored cube expression member once #20943 retires raw expressions in a cube member's sql (the branch becomes unreachable) #20965's), which the "Governed half" bullet does not ask for.
  • rules/dashboards.md :70 and :78-80: still right. The summary field is live (field.zod.ts:1669 summaryOperations); the showcase done_rate is a dataset measure-filter count plus a derived ratio (chart-gallery.dataset.ts:34, :36-38 at origin/main); dataset.zod.ts on main changed by one comment-only edit (b8191f7fc3) and still carries measure filter :196 and DerivedMeasureOp :136. The "formula field" drop still holds: aggregate-field-type-compatibility.ts changed in a75311dd27 and still refuses formula as virtual (:80-82, :122); sql-driver.ts changed twice and still says "Virtual — no column" (now :19152; the :19561-19562 the prior record and the dev cited was right at the time and moved, and no sentence in this diff carries a line number); skills/objectstack-formula/SKILL.md:407-411 still says to denormalise onto a stored field. 468 lines and 6211 tokens both sides; skills/**/SKILL.md sum 4395 both sides (re-measured).
  • dashboards.json dashboards.cube-query revision 3: still right against showcase.cube.ts at origin/main (three measures count / total_estimate_hours / avg_estimate_hours, four dimensions, the comment at :38-43 recording the move) and coverage.ts:198-200; done_rate survives under docs/qa/platform-checklist only in the revision-3 history entry that records its removal; revision equals the last history entry's, the rule check-platform-checklist.mjs:28-30 names.

F. The PR body at this head, as text the maintainer acts on. "Part of #20943" (the card is reopened for this half; Part-of PR must not also close its card is green), "Clause-②: no", "Tier H, docs only", "The code half landed as #20998 (5d5e679873)", "Five files, no package source", the ADR bullet, the dashboards.md bullet with its 468 / 6211 / 4395 readings, the eval bullet ("no longer name a Cube as an escalation" — a non-target is not an escalation; "Eval 2's must_contain drops "Cube", which a correct answer need no longer say"; tokens 1102 to 1097 of 1102 and 287 to 286 of 289 — re-measured as ceil(bytes / 4) on 4406 / 4388 and 1148 / 1143 bytes against the ceilings at check-skills-token-ratchet.mjs:506-507; dashboards.md 6211 of 6252 at :523), the dashboards.json bullet, and "No changeset: none of the five paths ships in a package's files[]" (re-measured: no non-private package.json at the head lists a files[] entry reaching skills/ or docs/, and the five paths sit outside every package directory) — each true at this head.

  • ## 维护者速读(草稿), five fixed parts present (改了什么 / 为什么改 / 风险与代价(含回滚) / 席位意见 / 你要做的), 席位意见 blank as the contract requires before ACCEPT. Each sentence tested: "只改五个文档文件,不改代码" true; the ADR, skill, eval and QA sentences each restate ①E and ①A truly ("写表达式,解析时就会被拒" is the parse refusal; "存成对象上的字段(汇总字段,或存好计算值、分桶的字段),或者写应用代码" is :78-80; "评测的标准答案不再把 Cube 当出路" is eval 2 at the head); "看板技能规则文件的行数和 token 数改前改后一样,两个评测文件略缩短" true (468 / 6211 both sides; 18 and 5 bytes shorter); "合并后是一个提交,回滚就是 revert 它" true although the branch now holds two commits, because the repository allows squash merges only (allow_merge_commit and allow_rebase_merge are false), so the landing is one commit whoever clicks; "你要做的" names the Tier H act rightly.
  • Two imprecisions, neither false, neither blocking, both the seat's one-line call: (1) 为什么改 says "这三处文字都不对了" under a 改了什么 with four bullets; it is true on the reading that the ADR note is an addition and the three corrected places are the skill rule, the two eval files and the checklist item, but a maintainer counting the bullets may trip — "这些" or "后三处" removes it. (2) The dev contract (.claude/agents/os-dev.md:291-292, 行数为准) asks each changed skills/** file's whole-file before/after in lines, token beside; the body gives the two eval files in tokens only. Measured here: analytics-inline-vs-dataset.json 57 lines before and after, evals/README.md 31 before and after.

② Semver level

Right. The diff publishes nothing from any package: the five paths are docs/adr/**, docs/qa/** and skills/**, outside every package directory and in no released package's files[] (re-measured above); skills/ reaches consumers from GitHub, not npm. AGENTS.md rule 3 reserves skip-changeset for exactly "a diff that publishes nothing from any released package", so the label (on the PR) and Clause-②: no are right, and Check Changeset is skipped on this head. The BREAKING changeset for the retirement itself landed with #20998.

③ Boundary flags

  • Cut-round report 5925234326: open_questions empty, out_of_scope_findings empty. Its one declared departure (README re-spelled "a stored field or app code", not the adoption's "a stored rollup field or app code") is judged right in ①A. Its grep result is confirmed in ①C. Its CI line (2 in progress at report time) is overtaken: every run on the head is complete.
  • Prior FAIL 5925160445 ①E: delivered, ①A–①C. Its adoption's order ("exactly those two sentences, each re-spelled shorter, no other file changes") is met: both files shrank, both stay under their ceilings, and ①D shows no other file moved.
  • Prior record ③ items carried unchanged by the delta and re-read: the "formula field" drop (right, ①E); ADR-0021 D-A and the D1 sketch saying the Cube DSL is absorbed (pre-existing decision-time drift, not this PR's, not filed); CubeSchema.sql's description (outside ruling D, members only); check:platform-checklist exit 1 on the identity-auth.json twoFactor anchor (main's, out of per-PR CI; this diff touches neither that area file nor plugin-auth); the check:i18n-coverage derivation gap (5922922632, unreachable from a diff with no examples/ path); CUBE_MEMBER_SQL_RETIRED's "spec 17" wording (the adoption ruled no action: house spelling). The prior record's "Check Changeset is expected to go red" is overtaken: skipped.
  • Ruling D's "Governed half" bullet (5921156712): a dated note under ADR-0021's decision naming the spec symbol, in a separate Tier H docs PR never mixed with the code PR — delivered (:200, three symbol anchors, docs/adr/** and skills/** only, no packages/** path). The seat's out-of-scope findings on 5922695050 (the skill at :70 / :78, the checklist's four-measure count) are delivered here; the delta extends the skill fix to the same package's eval fixture and index.
  • Dev 5924841117 Q1 (速读 section and the package reading): seat answered A in 5924874583; both are in the body at this head, with the two imprecisions named in ①F for the seat's one-line patch, not blocking.
  • Nothing escalated by this review. Not this PR's and not filed: the line drift of sql-driver.ts "Virtual — no column" (:19561 at the dev's reading, :19152 on main now) touches only the prior record's and the dev report's citations, never a sentence in the diff.

Check-runs on 5ed7f87157746d717dfc6164bc9ccc0d99facd29, fetched 2026-10-01T05:41Z, 38 runs deduped by name to 31 keeping the newest started_at: 21 success, 10 skipped, 0 failure, 0 in progress. Success: Check Documentation Links, Dogfood Regression Gate, Governed Surface Queue Guard, Lint & Repo Gates (the ADR link and symbol-anchor gates, the skills token ratchet), No other open PR may claim the same issue, No other open PR may claim the same single-writer path, Part-of PR must not also close its card, Test Core and its six shards, The card this PR closes must claim this branch, the four Type Check jobs, TypeScript Type Check, filter. Skipped: Auto Label, Build Core, Build Docs, Check Changeset, Check PR Size, Console Pin Gate, the dogfood shard matrix, Dogfood Verify CLI, Packed-tarball smoke (opt-in), Temporal Conformance (live PG + MySQL). None still running.

Implemented-by: claude/issue-20943-adr0021-note
Reviewed-by: session_01Sfe5YjBLwB9J3y8fvm2xq1

VERDICT: PASS

Adopted and posted by domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-10-01T05:44Z · rendered by the seat's at-tier review subagent on this head (a delta re-review after the FAIL 5925160445 at 68593d46d8). The seat read its served tier family from the subagent transcript before posting.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)· 2026-10-01T05:44Z

domain:spec seat 5(session_01Sfe5YjBLwB9J3y8fvm2xq1)。PR #21071 在 5ed7f87157。第一轮审查不通过:同一技能包的评测文件还在教旧做法(5925160445)。补改后复审通过,记录就是上一条评论。CI 全绿。

改了什么:

  • ADR-0021 的 D1 末尾加一条带日期的说明:分析 cube 的度量和维度,sql 只能写列引用,写表达式会在解析时被拒。这是裁决 D,代码已随 feat(spec)!: an analytics cube member's sql is a column reference, and the showcase done rate moves to its dataset (#20943) #20998 落地。
  • 看板技能(skills/objectstack-ui/rules/dashboards.md)不再教“dataset 表达不了,就去手写带原始 SQL 的 Cube”,也不再把公式字段当出路。改为:存成对象上的字段(汇总字段,或存好计算值、分桶的字段),或者写应用代码。
  • 同一技能包的评测题和它的索引说明同步改了:标准答案不再把 Cube 当出路。
  • QA 清单的 cube 条目删掉 done_rate,它已经搬到 dataset 上了(条目修订号 2 → 3)。

为什么改:

  • 裁决 D 规定,ADR 的说明要单独走一个 Tier H 文档 PR。
  • 代码那一半落地后,这几处文字都不对了。技能和评测是 AI 写元数据时读的教材和打分标准:按旧教法写出的 cube,解析时就会被拒;评测还会把正确答案判错。

风险与代价(含回滚):

  • 只改文字,不动任何包,不发版。看板技能规则文件的行数和 token 数不变;两个评测文件略缩短,都在 token 上限内。
  • 合并后是一个提交,回滚就是 revert 它。

席位意见:

  • 建议合并。
  • 有一处超出裁决 D 的字面范围:技能里把“公式字段”从出路里删掉了。理由是公式字段不落库,对它做聚合会被拒。两轮审查都核对过这一点。您若想保留原样,说一声,我把那半句改回去。

你要做的:

  • 合并本 PR。这是 Tier H,需要您亲手合并;或者给出授权的 APPROVED,由席位落地。

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 1, 2026 06:09
@os-zhuang
os-zhuang enabled auto-merge October 1, 2026 06:11
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit d19da5d Oct 1, 2026
57 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-20943-adr0021-note branch October 1, 2026 06:31
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 skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants