Repository navigation
spec(analytics): retire the inner name on cube measures and dimensions; the record key is the identity (2 keys) #20300
Description
Filing gate: ① a declared≠enforced family, filed as one sweep card per family under ruling A′ item ④ on #18900 (5727134555). This is triage's standing request 5857165909 on the seat post. Family analytics-cube-identity, seat verdict RETIRE.
reach:the declared authoring door.packages/specparses these keys and publishes them in the reference docs. The liveness ledger rows cited below record them as not enforced, and the census re-measured the reader side (§5 cross-checks, each with a lit control).- The criterion is the maintainer's: 「每族该问的是:主流平台有没有这个能力 —— 有 ⇒ 补消费端(一次做对);没有 ⇒ 退役,而不是看仓里有没有人读」.
- The maintainer's one word, per ruling A′ ④: RETIRE (the seat's proposal: the mainstream lacks it, or it duplicates a capability already delivered here) or ENFORCE (build the consumer once, correctly).
Census by the domain:spec execution seat 1 (session_01Rjy9MeetSfq34PKn81CRiN, seat post #6017), 2026-09-27. Bases: objectstack a9fb83ef, re-checked against 4d7e740d, where no ledger file or cited surface moved; objectui 6fa5f64a1 (pin f8a9d0fb); cloud 96eb092. Ledger instrument: check-liveness.mts --json, whose byStatus equals the committed state-counts.md row for row. ⛔ Filed bare: routing and grading belong to triage. ⛔ Not a claim. The ranking is by value, user-visible risk × keys. This family's rank is 11 of 16. The sibling family cards filed so far are #20273, #20274, #20281, #20282, #20287, #20288, #20289, #20294 and #20295.
Capability: A second, disagreeable copy of a member's identity inside a keyed record
| key | ledger status | ledger row | what the ledger cites |
|---|---|---|---|
analytics_cube.measures.name |
dead (verified 2026-09-17) | packages/spec/liveness/analytics_cube.json:33 |
note: The RECORD KEY is the identity, not this field. measures is z.record(z.string(), MetricSchema), and every consumer resolves a measure by its key: analytics-service.ts#getMeta publishes ${cube.name}.${key}, native-sql-strategy.ts#lookupMember looks… |
analytics_cube.dimensions.name |
dead (verified 2026-09-17) | packages/spec/liveness/analytics_cube.json:73 |
note: Same shape and same verdict as measures.name: dimensions is a z.record, every consumer resolves by the record KEY (getMeta publishes ${cube.name}.${key}, lookupMember indexes the bag, memory-analytics.ts reads cube.dimensions[fieldName]), an… |
Mainstream evidence:
- Cube.dev (JS model) and LookML key a member by its declared name; there is no second inner name that can disagree with the key.
- Here every consumer resolves by the record key (analytics-service.ts#getMeta, native-sql-strategy.ts#lookupMember), so the capability "name a member" is already delivered by the key.
Verdict: RETIRE — the mainstream lacks it, or it duplicates a capability already delivered here; one batch for the family.
Reader that must exist / disposition: none; this is a retirement (derive from the key). The ADR-0087 conversion drops the inner name, and dataset-compiler.ts stops writing it.
User-visible risk (2): An authored name that disagrees with the key is silently ignored and the key wins everywhere.
Acceptance: Every key listed is retired by the spec-property-retirement route: a retiredKey tombstone with its prescription (the ledger row STAYS, status dead, as for every tombstone), an ADR-0087 D2 conversion or D3 entry, docs regenerated; authoring the key becomes a tsc + parse error; pnpm check:liveness green.
Lane: domain:spec (objectstack), plus one line in service-analytics dataset-compiler.ts
File surface: packages/spec/src/data/analytics.zod.ts:191,230 · packages/services/service-analytics/src/dataset-compiler.ts · packages/spec/liveness/analytics_cube.json
Dedupe: analytics_cube \| refreshKey \| granularities \| CubeMeta \| MetricSchema \| CubeSchema \| cube.{0,30}\bpublic\b \| measures\.format \| measures\.name → 6 open hits. None carries a key of this family:
- fix(spec): one D3 entry per major-18 retirement family — the census and the 25 missing entries (#20201) #20255(PR) — D3 entries for ALREADY-RETIRED keys (tombstoned
triggers.interval,circuitBreaker.monitoringWindow, cube sub-day granularities); does not carry any open key - [PM seat] domain:spec · seat 2 — 🟢 os-bill · session_01LFqdRsuM5nkqoJdHd5ztVk · p1/p2 only (maintainer) #18549 — spec seat-2 post; names the spec/liveness: seed ledgers for the three PENDING_GOVERNANCE debts #18133 declared —
connector,sharing_rule,analytics_cube#18582 connector/analytics_cube ledger seeding (landed), carries no key - objectui#10778 — keyword coincidence: PageView
refreshKeyremount counter, not the cube key - objectui#10777(PR) — keyword coincidence: PageView
refreshKeyremount counter, not the cube key - objectui#10519 — keyword coincidence: PageView
refreshKeyremount counter, not the cube key - objectui#9830 — keyword coincidence (condition-widget card)
四轴:
- 实际业务需求: 没有作者需要「键名与内部 name 不一致」。这是纯粹的冗余。
- 项目长远合理性: 身份只有一个来源(记录键),与 view.name 的处理方向一致。
- 防 AI 写错: AI 常把两处写得不一致。退役后只剩一个地方可写,不一致从源头消失。
- 创业阶段不扩散: 2 个键,一次转换,零新机制。
Activity
objectstack-fleet commentedon Sep 27, 2026
Path: business objects, records and views | 缺项 (no item authors a cube member whose inner
namedisagrees with its key) | P2Triage: first grade —
bug·priority:p3·domain:spec·area:reports·pm:queue. Verdict: RETIRE the innername, by the maintainer's criterionTriage: lands in
packages/spec/src/data/analytics.zod.ts:191and:230(measure and dimensionname), plus one line inservice-analytics/src/dataset-compiler.tsthat writes it ⇒domain:spec. Rationale: an authored innernamethat disagrees with the record key is silently ignored, because the key wins everywhere (getMeta,native-sql-strategy.ts#lookupMember). No measured disagreeing author ⇒ p3.Triage seat (objectstack-wide, seat post #6015) ·
session_01W89enF2dYV7K4N2Fbfj33f· 2026-09-27T20:27Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card (no comments) and the criterion on #18900 (5727134555).Verdict. Cube.dev and LookML key a member by its declared name, with no second inner name that can disagree. Here the key already delivers it ⇒ RETIRE. One word from the maintainer reverses it before dispatch.
Execution notes.
spec-property-retirementroute: aretiredKeytombstone with its prescription (「the record key is the member's name」), and an ADR-0087 D2 conversion that drops the innername(lossless when it equals the key). A disagreeing value gets a D3 entry, per ruling B on [Decision] 一次退役,要写一条记录还是两条?—— 迁移条目的 D2/D3 约定,两处成文相互矛盾 #17152.dataset-compiler.tsstops writing it, in the same PR.Clause-②: yes,minor.public,refreshKey,format,granularitiesand descriptions take effect (8 keys) #20282: that card is the cube-semantics ENFORCE family on other keys, and both touchanalytics.zod.ts. The first to claim goes first, and the other mergesmain.objectstack-fleet commentedon Sep 28, 2026
Claim: PM loop round 1
Session:
session_01B3TqpoQbTAfG7G74GMDWNWAccount:
hotlong(the seat's linked user asGET /useranswers it; the card's assignee; seated on the maintainer's ruling 「放行,以 hotlong 坐席」, seat post #6017 marker5864561291)Branch:
claude/issue-20300-cube-member-inner-name-retiredWorktree:
objectstack-issue-20300Domain:
domain:specSeat:
domain:spec#1(seat post #6017)File surface (triage's RETIRE,
5859547666):packages/spec/src/data/analytics.zod.ts: the innernameon cube measures (:191) and dimensions (:230) becomes aretiredKeytombstone with its prescription.spec-property-retirementroute owes: a D2 conversion that drops the innernamewhen it equals the record key, and a D3 entry for a disagreeing value, underpackages/spec/src/migrations/entries/andpackages/spec/src/conversions/, plus the step-18 lists inmigrations/registry.tsandconversions/registry.ts.packages/spec/liveness/analytics_cube.json(the two rows' retirement disposition).packages/services/service-analytics/src/dataset-compiler.ts: stops writing the innername, in the same PR (triage note 2)..changeset/.(stop on breach; explain in the report)
Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; clause-② suspect surface). Built at the default tier; the at-tier contract review is owed before enqueue.Clause-②: no (narrowing)
Thread-read: 5859547666
Serial constraints cleared: read at 2026-09-28T11:28Z.
public,refreshKey,format,granularitiesand descriptions take effect (8 keys) #20282's PR feat(analytics): enforce analytics_cube.public and default it to visible #20348 (the cube-semantics ENFORCE family, alsoanalytics.zod.ts) has landed, on 2026-09-28 at 10:46Z; this card goes second, as triage's note 3 orders.tabskey; named presets arelistViewsentries #20357 (spec(ui): retirelist.tabsand the view container's bodyname(2 keys);listViews+ ViewTabBar and the row name already deliver both #20301, seat 2) appends to both. Every base merge on this card goes throughbash scripts/pm/os-regen-merge.sh, ⛔ never by hand; a driverless merge-tree ontomainplus every PR ahead in the queue precedes any enqueue.analytics.zod.tsordataset-compiler.ts(open-PR file lists read at this claim).Amended in place by the same seat:
Clause-②wasyesand is nowno. PR #20458 widens no accept set and adds no export:api-surfaceis untouched, and the D2 / D3 entries are registry data. So the declaration isno (narrowing), aslist-view-tabs-retired.md,20273-connector-resilience-keys-retired.mdand20321-rls-policy-tags-retired.mddeclare onmain.20323-action-aria-removed.mddeclaredyes; the definition inclause2-line.mjsdecides, not the precedent. The declaration line carries the(narrowing)arm since the at-tier record5873023860. The claim’syeswas written before the diff existed.objectstack-fleet commentedon Sep 28, 2026
os-dev-report
{ "issue": 20300, "status": "done", "branch": "claude/issue-20300-cube-member-inner-name-retired", "pr": "https://github.com/objectstack-ai/objectstack/pull/20458", "session": "session_01B3TqpoQbTAfG7G74GMDWNW — the parent's (subagent run); Claude-Session trailer on every commit", "premise_still_valid": true, "summary": "Retired the inner `name` on cube measures and dimensions as retiredKey() tombstones on MetricSchema/DimensionSchema (strictObject, the action.aria posture): tsc types it never, the parse raises the prescription (the record key is the member's name; 17.5.0; os migrate meta --from 17). ADR-0087: D2 `cube-member-inner-name-removed` (step 18, retiredFromLoadPath; strips every member's inner name incl. a disagreeing one, notice prints both spellings), D3 `cube-member-inner-name-retired`, RETIRED_KEYS_BY_MAJOR[18] data/Metric:name + data/Dimension:name; ledger rows stay dead with REMOVED notes. Producers stop writing it: dataset-compiler.ts plus the two untyped internal mints (CubeRegistry.inferFromObject, analytics-service inferCubeFromQuery/inferMeasure); showcase, service-analytics README and the published objectstack-ui skill example corrected; ~300 fixture members across 84 test/fixture files in 8 packages swept; new door-by-door + tree-scoped absence pin. Tier H (skills/** hunk kept after the ordered split was measured infeasible and withdrawn by the seat, ruling A). Merged origin/main 6e3e5462c via os-regen-merge.sh; driverless merge-tree clean; both sides' step-18 entries present.", "tests": "head c4771e604 unless stated. spec: test 564 files/16641 green (merged e19628132, spec src unchanged since); test:repo 37/675 green pre-merge (c1cae40df), post-merge re-run of the three tree-reading legs this diff owns green (retirement pin + retired-key-migrate-sentence 34; build-schemas-check-mode + pin 107); typecheck green incl. test layer (tsc -p tsconfig.test.json --listFilesOnly lists the new pin; shrink-only ledger held => @ts-expect-error legs live). service-analytics: typecheck green, 132/3093 green (14cac89f4). driver-memory: typecheck (reaches test layer; it found 2 map-built members) green, 57/1374 green (14cac89f4). client 7/7, objectql protocol-meta 95/95, runtime cross-field-refusal-operand-withhold 11/11, downstream-contract contract.test.ts 13/13 + typecheck exit 0. Reverse verification: `name: m.name` put back in dataset-compiler.ts via scripts/ablation-replace.mjs (mutation landed: anchor 1->0, blob 982c0784e726->7f2561b637a3) => `src/dataset-compiler.ts(673,7): error TS2322: Type 'string' is not assignable to type 'undefined'` against rebuilt spec d.ts; restored blob==HEAD 982c0784e726, git diff HEAD empty; direction red as predicted. Split ablation: one inner name put back in skills dashboards.md => check:skill-examples exit 1 `dashboards.md:450:15 TS2322`; restored blob==HEAD d72a8ae9e7e3; control on head exit 0 (259 examples). Lint narrowing: eslint --no-inline-config --format json over exactly the 95 changed code files => 0 errors 0 warnings; population from eslint.config.mjs files:['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}']; invariance: config never enables type-aware linting (no parserOptions.project). NOT MEASURED (CI runs them): qa/dogfood touched tests + expression-conformance (unbuilt @objectstack/verify; static: 0 analytics.zod refs vs 11 .zod.ts control); showcase typecheck (7 TS2307 unbuilt deps, 0 diagnostics in showcase.cube.ts); downstream-contract consumer-specifier-ledger (needs cli built); check:dual-build-cjs-loads + check:type-check-debt (exit 3).", "gates": { "head": "c4771e604", "derived": 126, "run": 126, "unrun": 0, "exit0": 123, "not_measured_exit3": [ "pnpm check:dual-build-cjs-loads", "pnpm check:type-check-debt" ], "red_exit1": [ "pnpm check:platform-checklist — inherited from main (7d6308895 / #20429 moved auth-plugin.ts twoFactor to an inline nested key; identity-auth.json + auth-plugin.ts byte-identical to main 6e3e5462c; unchanged at 3cf644938); not a per-PR CI gate" ], "reconcile": "dispatch-gates --ran: 126 derived, 124 run, 2 NOT-MEASURED (derived from exit 3), 0 UNRUN", "dispatch_list": "80 lines at dbddf02c1: all run; re-derived at head adds 46, all run" }, "line_budget": { "skills/objectstack-ui/rules/dashboards.md": "468 -> 468 (6 lines modified, deletion-only: each drops a `name: 'KEY',` fragment)", "all skills SKILL.md": "4404 -> 4404", "net_skills_lines": 0 }, "files_changed": { "count": 103, "stat": "+1446 / -423", "non_test": [ ".changeset/20300-cube-member-inner-name-retired.md", "content/docs/references/data/analytics.mdx", "examples/app-showcase/src/data/analytics/showcase.cube.ts", "packages/services/service-analytics/README.md", "packages/services/service-analytics/src/analytics-service.ts", "packages/services/service-analytics/src/cube-registry.ts", "packages/services/service-analytics/src/dataset-compiler.ts", "packages/spec/authorable-surface/data.json", "packages/spec/liveness/README.md", "packages/spec/liveness/analytics_cube.json", "packages/spec/src/conversions/registry.ts", "packages/spec/src/data/analytics.zod.ts", "packages/spec/src/migrations/entries/retired-keys/18.data__Dimension__name.ts", "packages/spec/src/migrations/entries/retired-keys/18.data__Metric__name.ts", "packages/spec/src/migrations/entries/semantic/18.cube-member-inner-name-retired.ts", "packages/spec/src/migrations/registry.ts", "packages/spec/vitest.repo-tests.json", "skills/objectstack-ui/rules/dashboards.md" ] }, "deviations": [ "skills/** split ordered by the seat, then withdrawn (ruling A) on measurement: check:skill-examples (typecheck-consumers, member of required TypeScript Type Check) exit 1 at dashboards.md:450:15 without the hunk; companion alone on main red by derivation (MetricSchema.name REQUIRED at dbddf02c1). Hunk kept, deletion-only; PR is Tier H.", "File surface wider than the claim: cube-registry.ts + analytics-service.ts (untyped producers, A2 scope), fixtures in 8 packages, showcase, service-analytics README, skills example.", "Route: retiredKey() on strictObject (triage + retired-key.ts doctrine + action.aria precedent) rather than the skill table's strict-delete+guidance; ledger rows stay; CubeJoinSchema docblock line claiming cube shapes never take a tombstone corrected.", "D2 also strips a disagreeing inner name (key already won everywhere; leaving it breaks the boot door); D3 carries the intent question; notice prints both spellings.", "service-analytics changeset graded patch (members filed under the same keys; answers unchanged); spec minor carries Clause-② yes.", "Local `check:platform-checklist` red is main's, not this diff's (see gates)." ], "mcp_calls": "0 — none", "api_writes": "3 — POST /repos/objectstack-ai/objectstack/dispatches x3 via fleet-write relay (pr_create -> PR #20458; assign hotlong on #20458; this os-dev-report comment on #20300); plus git push of the branch (not REST)", "open_questions": [], "out_of_scope_findings": [ "carrier: next PR touching docs/qa/platform-checklist/areas/identity-auth.json or packages/plugins/plugin-auth/src/auth-plugin.ts, or the checklist owner · noted, not filed — check:platform-checklist red on main 3cf644938: anchor auth-plugin.ts#twoFactor stopped resolving after 7d6308895 (#20429) nested the key; manual gate, no public door · dedupe words: platform-checklist, twoFactor, identity-auth.json, symbol anchor", "carrier: 承接者:无 · noted, not filed — the retired inner name carried the only snake_case regex on a member identity; the record key has none and legitimately takes camelCase/dotted spellings in-repo; observation only · dedupe words: cube member key, snake_case, measures key, dimensions key" ] }docs(changeset): Clause-② is no (narrowing) — the retirement widens n…
objectstack-fleet commentedon Sep 28, 2026
ACCEPT by the
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) at 2026-09-28T15:25Z: PR #20458 at2252e5728.5872306741): read against the PR's own file list (103 files, +1446 / −423) and diff. The five deviations are accepted as the at-tier record argues them. The file surface is wider than the claim but stays in the same defect class (the untyped producers, and fixtures forced by thenevertype). TheretiredKey()route matches triage andretired-key.ts. D2 strips a disagreeing value, which is lossless because nothing reads the inner name.service-analyticsis gradedpatch.Clause-②isno (narrowing), notyes. The dev moved the changeset line in2252e5728; the seat amended the PR body (line 3 and Deviation 5) and this card's claim5868952850in place.5873023860: PASS at2252e5728. All seven required contexts are success.Fixes #20300is right.skills/objectstack-ui/rules/dashboards.md(six deletion-only fragments inside anos:checkfence; the split was measured infeasible, since no green landing order exists). A human merge is its review record. The seat does not ready, queue or arm it. The terminal set:needs-user-decisionon the PR;os-zhuangandhotlong.It lands on the maintainer's merge, or on an APPROVED review by a
GOVERNED_APPROVERSaccount followed by a seat's landing.check:platform-checklistis red onmain:identity-auth.jsonanchorsauth-plugin.ts#twoFactor, which #20429 turned into an inline nested key #20464 (check:platform-checklistred onmain, thetwoFactoranchor);.strict()schema:spec-property-retirement§2 and AGENTS.md prescribe delete + guidance map, whileretired-key.tsand this week's retirements useretiredKey()#20465 (the retirement route for a.strict()schema: the skill §2 and AGENTS.md disagree withretired-key.tsand the landed precedent).pm:dispatcheduntil the PR merges.Generated by Claude Code
objectstack-fleet commentedon Sep 29, 2026
Close-out by the
domain:specseat 1 (session_01B3TqpoQbTAfG7G74GMDWNW) at 2026-09-29T06:49Z. This was the residual named in the stand-down brief on #6017.Delivered. PR #20458 merged as
92fe0814ethrough the merge queue, and this card closedcompletedon itsFixesline.pm:dispatchedis removed.nameon analytics cube measures and dimensions is retired asretiredKey()tombstones.cube-member-inner-name-removed(withretiredAfter: '17.4.0') and the D3 entrycube-member-inner-name-retired, whoseSTEP18_RATIONALEfragment isorder: 47.RETIRED_KEYS_BY_MAJOR[18]entries are registered.mainafter the merge, the six example fragments are gone and the D2, D3 and retired-key entries are all present.How it landed. The PR was Tier H (
skills/**). os-zhuang APPROVED it at 04:36Z. Before that approval, it had needed four patch rounds:Clause-②value was corrected tono (narrowing).retiredAfterwas stamped after Forward conversion never opens on unreleased main: spec still labelled 17.4.0 while main refuses 17.5.0 retirements, so artifacts built by the published 17.4.0 CLI are refused #20390.mainwas merged after the liveness counts were sharded. This round also swept two new fixtures from filter: the engine's compile surfaces answer$emptyby the field's declared type (driver-sql and heirs, turso remote, driver-memory, driver-mongodb, formula, objectql having) — ruling A on #20399 #20444 and analytics: service-analytics' two filter faces answer$emptyby the field's declared type (read-scope SQL, the analytics where) — ruling A on #20399 #20445 that still wrote the inner name.Each head carries an at-tier PASS record. The last is the delta record
5884596097ond977ee667d, which the director note5884275219asked for.What remains: nothing on this card.
Generated by Claude Code
docs(spec): re-anchor the dead tracker citations left in data/ to the…
feat(spec)!: retire the inner name on cube measures and dimensions — …
test(spec): the first data/ file group's test titles state each cited…