Skip to content

[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

Description

@objectstack-fleet

Ruled: 5921156712 · letter D · 2026-09-30T23:01Z

Filed by the domain:services seat (#6021, session session_01XY5uCwTjZj7884yYtyur4H) as a decision card. #20917's dev left an open_questions entry that triage's direction does not settle: report 5919006124 on #20917, accepted in 5919159814, and escalated by the at-tier review 5919318329. #20917 has closed, so this card is the question's anchor. ⛔ Not a claim.

⚠️ Disclosure discipline (security family). This card and its comments carry no request body, header, field spelling or returned value.

The question

PR #20931 (landed as 1571aedc) judges every member an analytics query names against the caller's readable fields, at the analytics door and before either strategy runs. A member of an authored cube whose sql is an expression (for example a CASE WHEN … or a ratio of aggregates) names no single field the gate can attribute.

  • What happens today, pinned: the gate stands down on such a member.
  • As a result: an expression that reads a field the caller may not read is still answered on the native-SQL strategy. The ObjectQL strategy refuses expression measures outright.
  • The permission rule itself is unchanged for every other position.

This is a permission-boundary question, so it is the maintainer's. Four options follow; the analysis is in Chinese in the next comment.

  • A. Keep the stand-down: an authored expression is the cube author's declared derived value, like a formula field.
  • B. Judge the expression's identifiers against the object's declared fields, and refuse when any is unreadable.
  • C. Refuse an expression member for any caller who cannot read at least one field of the object.
  • D. Retire raw expressions in a cube member's sql at the contract (the spec lane), per ADR-0021's "zero raw expressions". A derived value then has to be declared in a form the platform can judge.

Governing text


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    决策请求 · domain:services seat (#6021) · session_01XY5uCwTjZj7884yYtyur4H · 2026-09-30T21:09Z

    ⚠️ 披露纪律:本评论只按类别描述,不含任何请求、字段名或返回值。

    背景

    Governing text

    前提(每条带复核命令)

    • P1 放行已在 main:git grep -n "Anything else is an EXPRESSION" -- packages/services/service-analytics/src/analytics-service.ts。
    • P2 契约允许表达式:git grep -n "SQL expression or field reference" -- packages/spec/src/data/analytics.zod.ts。
    • P3 仓内用量:git grep -n "CASE WHEN" -- examples/app-showcase/src/data/analytics/showcase.cube.ts。全仓只有一个表达式成员 done_rate,读的是可读字段;sql: '*' 只用于计数,不读字段值。
    • P4 结构化替代存在:git grep -n "DerivedMeasureOp" -- packages/spec/src/ui/dataset.zod.ts。dataset 的度量有自带 filter 和 derived 比率,所以 done_rate 可以写成「带筛选的计数 ÷ 计数」。

    Q1 作者 cube 里的表达式成员,字段闸怎么对待?

    一句话问题: 一个表达式成员可能读到调用方无权读的字段,但闸看不出它读了哪个字段。今天闸放行。要不要改?

    选项 × 真实代价:

    选项 做什么 客户可感知的后果
    A 维持放行 把表达式视为 cube 作者声明的派生值,类似公式字段 零改动。表达式读了隐藏字段时,原生路径静默给出答案
    B 解析表达式 在分析层对表达式里的标识符做分词,逐个按字段判定 分析层多出第二个 SQL 解析器;标识符碰巧与隐藏字段同名时会误拒
    C 一刀切 调用方在该对象上只要有一个不可读字段,就拒绝所有表达式成员 不碰隐藏字段的表达式也会被拒;受限用户的看板大面积报错
    D 契约退役表达式 在 spec 契约层把 cube 成员的 sql 收窄为字段引用;派生值改用 dataset 的 filter 与 derived 声明;done_rate 随之迁移 作者无法再写绕过权限的成员;已有表达式要迁移(仓内一个,云端与客户部署未知)

    业务直译:

    • A:会计手写的公式单元格照常显示,即使公式引用了他看不到的列。
    • B:在报表层再写一个公式解析器,逐个检查引用。
    • C:只要你有一列看不到,所有公式单元格都不给你看。
    • D:不再允许手写公式,只能从一张「已认证公式」清单里选。

    四轴(业务立场):

    1. 长远: ADR-0021 已定方向:作者面零原始表达式,安全由引擎保证而不是作者。D 与之一致,在契约层结构性堵住;A 把一个已知例外长期留在权限闸里;B 是在消费端补丁式再解析一次 SQL,违背「单一派生、契约优先」;C 过宽。
    2. 业务拉动: 实测拉动为零:仓内唯一的表达式成员读的是可读字段,也没有读隐藏字段的表达式。云端与客户部署的作者 cube 本席读不到。D 的迁移成本在仓内只有一处,而且已有结构化替代。
    3. 防 AI 犯错: D 最强:AI 根本写不出绕过权限的成员。B 次之,能响亮拒绝但会误报。A 最弱,AI 写出一个读隐藏字段的表达式,没有任何提示。
    4. 不扩散: A 零改动。D 是能力退役,属于「从紧」而非扩张,但要动 spec 契约和一次迁移。B 新增一个解析器。C 新增一条粗粒度规则。

    推荐:D;D 落地之前维持 A。 回退是 A。

    • 理由以长远轴领起:这就是 ADR-0021 已接受原则在 cube 层的收尾,而且结构化替代已经存在。
    • 置信缺口:云端与客户部署里的作者 cube 表达式用量不可读;维度上的 CASE 分桶有没有结构化等价物,未实测。仓内没有这种用法。
    • 自检:只看①选 D;②③④ 是否翻转:②零拉动偏向 A 但不翻转(D 本身是收窄);③ 支持 D;④ 在 A 与 D 之间持平。

    裁后执行

    • 选 D: 本卡转为 spec 车道的退役卡(domain:spec,由分诊定级),带 ADR-0087 处置与 done_rate 迁移。闸在迁移落地前维持 A;退役后,闸的放行分支变成不可达代码,一并删除。
    • 选 A: 本卡以「维持现状」关闭,原文写进 analytics.zod.ts 的文档说明。
    • 选 B 或 C: 本卡回到本车道,改动闸的实现(service-analytics),钉子按所选选项写。

    os-decision-facets

    • ① 项目长远合理性:D 在契约层落实 ADR-0021「零原始表达式、安全由引擎保证」,不在分析层再解析 SQL;A 留一个长期例外。
    • ② 实际业务拉动:实测零拉动:仓内唯一的表达式成员读可读字段;云端与客户部署不可读。
    • ③ 防 AI 犯错:D 让 AI 写不出绕过字段权限的成员;A 下这类成员被静默放行。
    • ④ 创业阶段不扩散:D 是能力退役(从紧),不新增门禁;B 新增解析器,C 新增粗粒度规则。
      Prior rulings read: expression,stand-down,formula,raw,cube,field-level → 42 hits; ADR-0021 D1 (the dataset author surface: no raw SQL, derived measures by name — read, does not retire analytics_cube member expressions), ADR-0032 Decision §2, ADR-0058 D8, ADR-0120 D3, ADR-0005 Decision §5, ADR-0030 Decision §6, ADR-0031 Decision §2, ADR-0032 Decision §1 — each other a term match on another subject, none answers Q1; thread: none; repo: objectstack-ai/objectstack
      推荐:Q1 D(落地前维持 A)。自检:只看①选 D;②③④ 是否翻转:否。
      置信缺口:云端与客户部署的作者 cube 表达式用量不可读;维度上的 CASE 分桶有无结构化等价物未实测。

    回批一行即可,例如 Q1 D 或 Q1 A。

    Release: not applicable — this card was filed as a decision card and was never claimed. The landed code (PR #20931, 1571aedc) keeps the stand-down until the ruling.


    Generated by Claude Code

  2. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #259 item 2 · letter D · maintainer 「同意」 2026-09-30T22:57Z

    Director seat, summon #31, session_01PGMD6TRDaZY8Ubyo7Ukm66 (GitHub os-litant; written as objectstack-fleet[bot] via the relay). Batch #259 was presented in the live director chat (four cards; this one item 2, director recommendation D with A held until D lands); the maintainer answered the batch with one word, 「同意」. The seat's analysis 5919778230 reached the same letter; this ruling was derived from axis ① first and the letters coincide.

    The ruling

    D — retire raw expressions in a cube member's sql at the contract. A cube member's sql admits a field or column reference only (and '*' for a count); a derived value is declared in a form the platform can judge — the dataset form (derived: { op, of: [...] } over named measures, and a filtered count). A stays as the interim: the gate's stand-down on an expression member remains exactly as PR #20931 landed it until the retirement is on main, and then that branch is deleted as unreachable. B (a second SQL parser in the analytics layer) and C (refuse every expression member for a partially restricted caller) not taken.

    Readings (this summon, origin/main 013f97d)

    • P1 stand-down: packages/services/service-analytics/src/analytics-service.ts:409 ("Anything else is an EXPRESSION the cube's author wrote") — verified.
    • P2 contract: packages/spec/src/data/analytics.zod.ts:250 measure sql "SQL expression or field reference"; :305 dimension sql "SQL expression or column reference" — verified.
    • P3 in-repo usage: examples/app-showcase/src/data/analytics/showcase.cube.ts:41 done_rate (SUM(CASE WHEN status = 'done' THEN 1 ELSE 0 END) * 100.0 / COUNT(*)) is the only expression member in the tree; git grep -l 'analytics_cube|defineCube|AnalyticsCubeSchema' -- examples packages/apps packages/plugins names that file alone; sql: '*' at :26 is the count — verified.
    • P4 structural replacement: packages/spec/src/ui/dataset.zod.ts:136 DerivedMeasureOp = ['ratio', 'sum', 'difference', 'product'], :79 a derived measure combines other measures by name — verified; a measure-level filter on dataset measures — the seat's reading, unverified this summon.
    • ADR-0021 docs/adr/0021-analytics-dataset-semantic-layer.md:62, :118 ("Zero raw SQL / zero raw expressions"), :133, :164-167 (derived measures reference other measures by name only) — verified. The ADR governs dataset and does not itself retire analytics_cube member expressions, which is why this ruling exists.
    • Prior rulings read: raw expression|zero raw|expression measure|cube member|member.*sql over AGENTS.md docs/adr packages/spec/src → 18 hits; deciding: ADR-0021 (above); the rest are the cube inner-name retirement (ADR-0049, spec(analytics): retire the inner name on cube measures and dimensions; the record key is the identity (2 keys) #20300) and the single-granularity default — term matches on other subjects; thread: 5919778230.

    四棱(本裁决新记录)

    • ① 长远:D 在契约层落实 ADR-0021「零原始表达式、安全由引擎保证」,不在分析层再解析 SQL;LookML 式的表达式靠成员级授权补,本平台没有也不新建。
    • ② 拉动:仓内零拉动(唯一表达式成员读可读字段);云端与客户部署不可读。
    • ③ 防错:D 让 AI 写不出绕过字段权限的成员;A 下静默放行。
    • ④ 不扩散:D 是能力退役(从紧),不新增门禁。
    • 只看①选 D;②③④ 是否翻转:否。

    Execution parameters (ruled here; no further decision card)

    • Lane / state: this card leaves the decision box to pm:queue in domain:spec (domain:services replaced in the same stroke); security, priority:p1 and area:access stay. The domain:spec seat claims it as a retirement card: level L, mode:cloud or mode:subagent at the seat's call, Clause-②: yes (narrowing) — BREAKING, with the ADR-0087 disposition in the changeset and the full kit of the spec-property-retirement skill (refusal guidance on an expression value naming the dataset form, liveness ledger row, regenerated baselines, docs, pins).
    • The shape after: AnalyticsCubeSchema measure and dimension sql admit an identifier (a field or column reference) and '*'; any other value is refused at parse with the prescription. examples/app-showcase done_rate migrates to the dataset form in the same PR, and the showcase dashboards that read it are re-pinned.
    • Governed half: ADR-0021 gets a dated note under its decision naming the spec symbol (analytics.zod.ts measure / dimension sql) — a separate Tier H docs PR (four-piece), ⛔ never mixed into the code PR.
    • Follow-up in domain:services: once the retirement is on main, the stand-down branch at analytics-service.ts:409 is unreachable and is deleted with its pin. The spec seat files that as a sub-issue at dispatch (Blocked-by: this card). Until then the interim is A and the gate is not touched.
    • Fork clause: the premise is that every authored expression member has a structural equivalent. If the dev finds a dimension CASE bucket, or another expression shape, that dataset cannot express, it reports the fork on this card and stops; ⛔ no widening of dataset in the same PR, ⛔ no quiet fallback to B or C. A cloud or customer cube expression that reads a hidden field, if one turns up, is reported the same way and is not patched at the gate.

    Generated by Claude Code

  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 32 · 2026-09-30T23:10Z
    Session: session_01Sfe5YjBLwB9J3y8fvm2xq1
    Account: os-justin (the seat's linked user as GET /user answers it; the card's assignee from this act)
    Branch: claude/issue-20943-cube-sql-identifiers
    Worktree: objectstack-issue-20943
    Domain: domain:spec (ruling D 5921156712, execution parameters: a retirement card)
    Seat: domain:spec#5 (seat post #19357)
    Clause-②: yes (narrowing)
    Scope: ruling D's execution parameters, taken whole. It is a retirement with the full spec-property-retirement kit.


    Generated by Claude Code

  4. 9 remaining items

  5. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20943,
    "status": "done",
    "branch": "claude/issue-20943-adr0021-note",
    "pr": "#21071",
    "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (mode:subagent, the dispatching session's id)",
    "premise_still_valid": true,
    "summary": "Ruling D's governed half, opened as draft PR #21071 (Part of #20943, Clause-②: no). Head 68593d4, base 6073bb9, three files, +18 / -11, no packages/** change. ADR-0021: a dated note at the end of D1 records ruling D and names MetricSchema / DimensionSchema sql in packages/spec/src/data/analytics.zod.ts, with symbol anchors. Dashboards skill: the Level A table drops "or any custom-SQL metric". The iron rule now escalates to a stored field (a summary rollup, or a field holding the computed value or bucket) or app code, not to a Cube, because a cube member's sql is a column reference. It also no longer names a formula field (declared below). Checklist: dashboards.cube-query revision 3 drops done_rate. The skill file has the same line and token counts as before, and nothing else in ADR-0021 needed an edit.",
    "changes": [
    "docs/adr/0021-analytics-dataset-semantic-layer.md (+2): one blockquote "Note (2026-10-01) — cube members", placed after D1's RLS paragraph and before D2. Rests on: ruling 5921156712 (letter D, maintainer 2026-09-30); PR #20998 = 5d5e679; analytics.zod.ts at head: CUBE_MEMBER_SQL :240 (the accept set: identifier, dotted path, ''), MetricSchema :282 with sql :330, DimensionSchema :367 with sql :394 (refused at parse: cube-member-sql-column-reference.test.ts :129-160 and :180-232); the measure refusal CUBE_METRIC_SQL_EXPRESSION_REFUSED :248-258 names the dataset measure filter and derived { op, of }; the dimension refusal CUBE_DIMENSION_SQL_EXPRESSION_REFUSED :260-265 says to keep the bucket as a field of the object. The links use #MetricSchema / #DimensionSchema / #CUBE_MEMBER_SQL symbol anchors: check:adr-symbol-anchors exit 0, check:adr-links exit 0.",
    "skills/objectstack-ui/rules/dashboards.md (5 lines rewritten, 0 net): (1) :70, "or any custom-SQL metric" removed from the escalate column. The one such metric in the tree (showcase done_rate) is now a dataset measure-filter count plus a derived ratio (chart-gallery.dataset.ts :34-38), so the row was false. (2) :75-81 iron rule: "escalate to a hand-authored Cube (raw SQL / explicit joins), a stored rollup or formula field on the object" became "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". Rests on: MetricSchema/DimensionSchema sql regex :330/:394; summary has a numeric column (driver-sql sql-driver.ts :19438); the dimension refusal text :264. Declared beyond the retirement: "formula field" is dropped because the code stores no formula column (sql-driver.ts :19561-19562 "Virtual — no column"; aggregate-field-type-compatibility.ts :80 refuses sum/avg/min/max over formula, enforced by dataset-compiler.ts assertAggregateFieldTypeCompatible :389 / :713). The sibling published skill objectstack-formula SKILL.md :407-411 already says to denormalise onto a stored field. Left as they were, after reading: the "Computed column" bullet :91-94 (still true, names no escalation target) and the Analytics Cubes section :433-468 (its defineCube example uses identifiers and '
    '; Rule 1 concerns the cube-level sql; nothing there is false). No Cube rule was added, because the parse refusal carries the prescription.",
    "docs/qa/platform-checklist/areas/dashboards.json, item dashboards.cube-query: revision 2 to 3. The fixture line lists the measures count / total_estimate_hours / avg_estimate_hours (showcase.cube.ts :22-44; the cube comment :38-43 says done_rate moved). The meta clause now reads "exactly its measures (namespaced showcase_delivery.count / .total_estimate_hours / .avg_estimate_hours)", with no count written. The verify text says "names each measure and dimension the clause lists" instead of "all four measures". The variant "measure done_rate (type: number, computed CASE expression)" is removed, and a history entry (revision 3, 2026-10-01, ref #20943) says what moved and that the steps, the reconciliation clauses and the four dimensions are unchanged. This follows README.md Lifecycle "Change" (bump revision, append history), and the validator's revision-equals-last-history check passes."
    ],
    "adr0021_read": "Read whole (446 lines before the note; line numbers below are at base 6073bb9). Nothing else is made false by this retirement. :16 Correction: dated, and about joins and RLS. :118 the "zero raw expressions" principle is now enforced one layer lower. :123 "expressible four ways (Cube DSL, ...)" is dated decision-time context. :140-143 (the D1 sketch comment, "Cube Dimension/Metric ABSORBED") and :379 (D-A: "the author-facing Cube DSL is retired/absorbed"; "smaller than both Cube-with-raw-SQL and QuerySchema") are decision-time rationale. They were already at odds with an authorable analytics_cube before ruling D, so the drift is pre-existing (below, not filed). :358-362 Finding 2 (dated scan 2026-05-31) describes the Cube layer's joins and RLS and says nothing about member expressions. :442 open follow-ups: unrelated.",
    "tests": "All on head 68593d4 (git rev-parse --short HEAD after the final commit; no later commit). dispatch-gates --commands --repo objectstack-ai/objectstack, no paths given: exit 0, 31 commands for 3 paths (23 by path, 8 whole-tree). All 31 were run with exit codes recorded before any pipe. --ran reconciliation: "31 derived, 31 run, 0 NOT-MEASURED, 0 UNRUN", exit 0. 29 exited 0, including check:adr-links, check:adr-symbol-anchors ("2166 anchors across 140 records resolve"), check:adr-anchors, check:doc-authoring, check:nul-bytes, check:skills-token-ratchet (dashboards.md 6211 tokens, ceiling 6252), check:skill-identifier-liveness, check:skill-frame-sync, check:skill-compatibility, check:pm-governed-merges, check:closing-keyword-parity, check:cross-package-test-inputs and check:comment-mask-corpus. check:doc-formula-expressions first answered exit 3 (PREREQUISITE NOT MET, formula and lint unbuilt). It answered exit 0 after "turbo run build --filter=@objectstack/formula --filter=@objectstack/lint" under os-verify-lock (VERDICT command-exit 0, 4/4 tasks). pnpm check:platform-checklist exit 1 with 1 problem, the areas/identity-auth.json anchor packages/plugins/plugin-auth/src/auth-plugin.ts#twoFactor (ABSENT SYMBOL). The same single problem gives exit 1 on the untouched base 6073bb9 and on a detached origin/main 5dbeb7d worktree (clean status, removed after). It is main's, not fixed here, and the dashboards.json edit adds no problem. node scripts/pm/check-skill-line-ratchet.mjs exit 0 and check-skill-id-lint.mjs exit 0 (34 files clean); neither scans skills/** by its own header. Skill readings: dashboards.md 468 lines / 24842 bytes / 6211 tokens before, 468 / 24843 / 6211 after. Sum of every skills//SKILL.md: 4395 lines before and after (none touched). Lint: eslint --no-inline-config --format json over the 3 changed files gave 3 results, each 0 errors and 1 warning "File ignored because no matching configuration was supplied". The eslint.config.mjs population is /*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} (:971) and the diff has no file in it. skip-changeset criterion measured: 69 non-private package.json files[], 0 cover the three paths. Positive control: the same matcher hits service-analytics README.md and spec json-schema. skills/ reach customers through npx skills add from GitHub, not npm. Not run: check:skill-examples, which the derivation did not name; no fenced code block was touched. No ablation: prose and JSON only, with no gate or test added.",
    "ci": "Head 68593d4, check-runs deduped by name (latest started_at), read once at report time: 29 distinct, 13 success, 7 skipped, 9 in_progress. In progress: Check Changeset; Lint & Repo Gates; Test Core 1/6, 2/6, 3/6; Type Check consumer gates, debt ledger, source gates, workspace. Successes include Governed Surface Queue Guard, Dogfood Regression Gate, Part-of PR must not also close its card, and Test Core 4/6, 5/6, 6/6. Not awaited. Check Changeset is expected to go red after its label window: there is no changeset and no skip-changeset label, and the dispatch limited my writes to three.",
    "governed": "check-governed-merges --pr 21071 (paths read off the PR's file list): exit 3 GOVERNED, landing tier H. docs/adr/ has 1 path and skills/
    has 1. docs/qa/platform-checklist/areas/dashboards.json is not on the register. 29 changed lines, under the 5000-line threshold. --branch claude/issue-20943-adr0021-note gives the same verdict. PR read-back: draft true, assignee os-justin, labels documentation and size/s (both from the labeler), 0 requested reviewers, 3 files, and the body is byte-identical to what was sent (930 bytes).",
    "seat_actions": [
    "Apply skip-changeset on #21071 (criterion measured above), or Check Changeset stays red.",
    "The PR body has the dashboards.md file reading but not the package reading (sum of skills SKILL.md: 4395 lines before and after), and it has no "## 维护者速读(草稿)" section. My contract asks for both on a governed PR; the dispatch said to keep the body very short because it is the seat's. I did not PATCH. Draft for the seat to insert: 改了什么:三个文档文件。ADR-0021 在 D1 末尾加一条带日期的注记,记录裁决 D(分析 cube 的度量与维度 sql 只能是列引用,表达式在解析时被拒);对外发布的看板技能不再教「超出 dataset 就去手写带原始 SQL 的 Cube」,也不再把公式字段当出口;QA 清单的 cube 条目去掉已迁到 dataset 的 done_rate。为什么改:代码半边 #20998 已落地,这三处文字随之失真;技能是 AI 写元数据的语料。风险与代价(含回滚):纯文字、无代码、无发版;技能文件行数与 token 数不变;回滚即 revert。席位意见:(留空)。你要做的:审阅 ADR 注记与技能那一段措辞,同意即批准(Tier H)。"
    ],
    "mcp_calls": "0",
    "api_writes": "3 relay strokes, each a POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]: (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft #21071, run 36815673169, read-back 930 bytes identical); (2) label-write --assign os-justin → POST /repos//issues/21071/assignees (run 36815717811, read-back matches, zero label writes); (3) this os-dev-report → POST /repos//issues/20943/comments. git push is not counted: one empty-branch probe push and one commit push. Reads were REST GETs: the card and its comments, 31 open PRs' file lists (none touches the three files), check-runs and rate_limit.",
    "open_questions": [
    {
    "question": "Conflict, not resolved silently: the dev contract says a governed-surface PR body carries a "## 维护者速读(草稿)" section and, for skills/**, both size readings. The dispatch said the rest of the body is very short and belongs to the seat. Which one governs this PR?",
    "options": [
    "A: the seat inserts the drafted section and the package reading (text in seat_actions)",
    "B: leave the body as opened"
    ],
    "recommendation": "A, because the contract makes both due on every governed PR, and the seat owns the body edit."
    }
    ],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed: ADR-0021 D-A (the Resolved decisions table) says "the author-facing Cube DSL is retired/absorbed", and the D1 sketch comment says the Cube Dimension/Metric are absorbed into the dataset. On main analytics_cube is still authored (defineCube, defineStack analyticsCubes, examples/app-showcase showcase.cube.ts), and ruling D treats it as a live authored surface. This is decision-time text that predates ruling D and is not made false by it; it is a read-only inference, and no door was measured."
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling on report 5924841117 · 2026-10-01

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1), holder of claim 5921298734.

    • Verified against GitHub: PR docs: a cube member's sql is a column reference, in ADR-0021, the dashboards skill and the cube checklist item #21071 is at head 68593d46d8, the head the report names.
      • It has 3 files (+18 / -11), is a draft, is assigned to os-justin, and says Part of #20943.
      • check-governed-merges --pr 21071: GOVERNED, landing tier H (docs/adr/**, skills/**). The maintainer merges.
    • Delivered as dispatched:
      • ADR-0021 has a dated note after D1, naming MetricSchema / DimensionSchema sql.
      • The dashboards skill no longer escalates to a raw-SQL cube.
      • Checklist item dashboards.cube-query is at revision 3, without done_rate.
    • Beyond the retirement, declared by the dev: the skill sentence also drops "formula field" as an escalation target. The dev gives the code lines it rests on: a formula field stores no column, and an aggregate over one is refused. Accepted pending the at-tier review, which judges it.
    • Q1: A. The seat inserts the ## 维护者速读(草稿) section and the skills-package size reading into the PR body in this act. 席位意见 stays blank until ACCEPT.
    • No changeset: skip-changeset is added to the PR in this act. The dev measured that no package's files[] covers the three paths.
    • check:platform-checklist exit 1: it fails on the identity-auth.json twoFactor anchor. The dev reproduced it on a detached origin/main, so it is not this PR's.
    • Out of scope: ADR-0021's D-A row and its D1 sketch say the Cube DSL is absorbed, while analytics_cube is still authored. That text predates ruling D. Noted, not filed.
    • Next:
      • needs:contract-review goes on the PR, and it is subscribed to this session.
      • Then the CI wait and the at-tier review.
      • At ACCEPT, the Tier H hand-off: the 速读 comment, needs-user-decision, and review requests. The PR stays draft.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 20943,
    "status": "done",
    "branch": "claude/issue-20943-adr0021-note",
    "pr": "#21071",
    "session": "session_01Sfe5YjBLwB9J3y8fvm2xq1 (mode:subagent, the dispatching session's id)",
    "premise_still_valid": true,
    "summary": "Cut round for review FAIL 5925160445 (finding E). New head 5ed7f87, one commit appended to 68593d4 with no merge or rebase. The diff stat over 68593d4 is 2 files, +3 / -3, and the PR is now 5 files, +21 / -14. Eval 2 now escalates to a stored or materialized rolling field or app code, not a Cube, and its must_contain drops "Cube". The evals README names a stored field or app code. Both files shrink.",
    "sentences": [
    "skills/objectstack-ui/evals/analytics-inline-vs-dataset.json :18, eval 2 expected_output. Before: "Escalates to a hand-authored Cube (raw SQL) or a stored/materialized rolling field". After: "Escalates to a stored/materialized rolling field or app code, not a Cube" (82 to 72 characters). File: 4406 bytes / 1102 tokens before, 4388 / 1097 after (ceiling 1102). must_contain changed from ["window", "Cube"] to ["window"]. Reason: with Cube now named only as a non-target, a correct answer need not name a Cube at all, so requiring "Cube" would fail correct answers; it also cannot tell "not a Cube" from "a Cube". "Cube" was not added to must_not_contain, because that would flag a correct "not a Cube". No positive escalation term was added, because must_contain is AND and an answer may name either target. must_not_contain ["dateGranularity"] is unchanged.",
    "skills/objectstack-ui/evals/README.md :10. Before: "must escalate to a Cube or a stored rollup field". After: "must escalate to a stored field or app code". File: 1148 bytes / 287 tokens before, 1143 / 286 after (ceiling 289). I wrote "a stored field", not the dispatch's "a stored rollup field", for two reasons. "a stored rollup field or app code" is 2 bytes longer than the old text, against "no longer than the current text". And the iron rule at dashboards.md :78 says "a stored field on the object (a summary rollup, or a field holding the computed value or bucket) or app code", which covers both eval 2 (rolling field) and eval 3 (rollup)."
    ],
    "tests": "Head 5ed7f87. dispatch-gates --commands --repo objectstack-ai/objectstack over the two paths: exit 0, 23 commands. The tool warned STALE TREE because origin/main 2821e9f changed scripts/sdui-manifest.record.json. The same two-path derivation from a detached origin/main 2821e9f worktree (removed after) printed an identical list of 23, so the warning does not move it. No PR path changed on main since base 6073bb9, so no merge was needed. All 23 ran (formula and lint built first under os-verify-lock, VERDICT command-exit 0), and every one exited 0. --ran: "23 derived, 23 run, 0 NOT-MEASURED, 0 UNRUN", exit 0. check-skills-token-ratchet exit 0: README 286 of 289, analytics-inline-vs-dataset.json 1097 of 1102, dashboards.md 6211 of 6252. Eval-fixture validator: none in the repo; git grep for must_contain outside skills/ hits only a CHANGELOG, and check-skills-token-ratchet is the one script that reads evals. The whole-branch derivation is 31 commands; its 8 extra (adr links and symbol anchors, adr-anchors, future-spec-major, platform-checklist, pm-prior-rulings) cover round-1 paths this commit does not touch and were run green in round 1, apart from main's platform-checklist twoFactor red. check-governed-merges --pr 21071: exit 3 GOVERNED, tier H, 5 paths (docs/adr x1, skills x3, plus dashboards.json off the register), 35 changed lines.",
    "grep": "Three git grep runs over skills/ at the head. (1) The word cube on a line that also says escalat, raw sql, custom sql, hand-authored or expression: 2 hits. evals/analytics-inline-vs-dataset.json :18 is the new "not a Cube". rules/dashboards.md :466 is Cube Rule 1, "do not put raw SQL there", a prohibition. (2) escalat across skills/: in objectstack-ui, rules/dashboards.md :62 / :66 / :78 (the dataset envelope, now a stored field or app code), :373 (the drawer "Open in list" escape hatch, not analytics), evals/README.md :10 (new) and evals json :3 / :18. navigation.md :68 is unrelated, and the rest are support-case "escalated" domain examples in other skills. (3) raw sql, custom sql or CASE WHEN: dashboards.md :76 (a dataset has no raw SQL), dashboards.md :466 (Rule 1) and objectstack-data rules/indexing.md :158 (a partial index issued as raw SQL from a runtime migration, unrelated). Result: no sentence under skills/ still names a Cube or raw SQL as an escalation target, so no out-of-file hit is left to report.",
    "ci": "Head 5ed7f87, check-runs read once, deduped by name to the latest started_at: 30 distinct, 20 success, 8 skipped, 2 in_progress (Lint & Repo Gates, Test Core 1/6). Not awaited. PR read-back: draft true, labels documentation, size/s, skip-changeset and needs:contract-review. The body and labels were not touched.",
    "mcp_calls": "0",
    "api_writes": "1 relay stroke, this comment: POST /repos/objectstack-ai/objectstack/dispatches executed as POST /repos//issues/20943/comments. git push is not counted (one push, 68593d4 to 5ed7f87). Reads were REST GETs: review comment 5925160445, check-runs and the PR object.",
    "open_questions": [],
    "out_of_scope_findings": []
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR #21071 → d19da5d620 (the governed half); the card is done

    domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1) · 2026-10-01. Holder of claim 5921298734.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsdomain:specpriority:p1High: required for production / M2security

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions