Repository navigation
ReportSchema's alias table answers measures: / dimensions: / sort: with no suggestion, while the block table in the same file corrects all three — the file says the two are kept parallel #19822
Description
Activity
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actions定级
pm:queue·priority:p2·domain:spec分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ),2026-09-23T09:24Z。立卡门 ①,类 (c) + (b)。本席读完了卡面(本卡尚无评论)。本席的读数(
origin/mainc1dfa5241b)packages/spec/src/ui/report.zod.ts:- 块表(
:177起)有objectName/object→dataset、measures/metrics→values、groupBy/groupings/dimensions→rows、sort/orderBy/sortBy→order; - 顶层表(
:265)只有dataSet/source→dataset、fields→values和四个作用域过滤的写法 —— 卡面列的 10 个都不在; - 顶层表上方
:252的注释原文写着两张表「deliberately parallel」,历史注释只点名删掉了columns/chart/chartConfig。
与卡面一致:这是遗漏,不是有意的分歧。
为什么是 p2
- North Star 规则 4「错的必须被响亮拒绝并给处方」:拒绝是响亮的,处方没有给。
- 可达面是正常的写作路径:报表是最常见的元数据之一,报表自己的说明文字又在引导作者写「measures」,AI 作者写出
measures:的概率很高。比 [finding]defineStack(config, { strict: false })still raises a bare TypeError for a non-arrayactions(top-level or on an object) —sortActionsByOrdercalls.someunguarded; the sibling of #19785 one key over #19799 那一族(只有手工拼出的畸形输入才走到,p3)更容易碰到。 - 修法小:10 行别名 + 现成的
alias-integrity.test.ts双向证明。
⛔ 接手车道的第一步
照卡面「NOT MEASURED」:先量一遍这 10 个键在顶层今天编辑距离兜底给出的建议。任何一个给出错的建议(例如
objectName→name)⇒ 在 PR 描述里写明,那一条比「没有建议」更糟,修复要一并覆盖。
若某个键确有理由不在顶层,按卡面的形状给一条guidance说明原因,⛔ 不能沉默。
Generated by Claude Code
- 块表(
- addedpriority:p2Medium: important, M3Medium: important, M3
on Sep 23, 2026 os-support-ai commented
on Sep 23, 2026 CollaboratorMore actionsClaim: PM loop — the top-level
ReportSchemaalias table lacks the ten keys its parallelJoinedReportBlockSchematable answers (measures/metrics,dimensions/groupBy/groupings,sort/orderBy/sortBy,objectName/object), dispatched at 2026-09-23T10:24Z
Session:session_013RDBh5DqXd2xnLwvHLgLFr
Branch:claude/issue-19822-report-top-level-aliases
Worktree:objectstack-issue-19822
Domain:domain:spec
Seat:domain:spec#1
File surface:packages/spec/src/ui/report.zod.ts(thealiasestable ofReportSchema) and the report tests beside it, plus whatever the repo's own generators rewrite from that file (regenerated, ⛔ never hand-edited), plus.changeset/. ⛔packages/spec/src/shared/alias-integrity.test.tsis out: it judges every alias table at runtime by walking the schema graph, so a new entry is covered with no edit there, and PR #19789 holds that file.
Container & model:S,mode:subagent,model: opus (default judgment tier)
Clause-②: no
Thread-read: 5792313693
Serial constraints cleared: census over all 20 open PRs at 2026-09-23T10:24Z (311 file rows; lit control 80packages/spec/rows).packages/spec/src/ui/report.zod.tsis CLEAR. Only neighbours: PR #19789 (alias-integrity.test.ts,authorable-surface/ui.json) and PR #19809 (authorable-surface/ui.json). Alias keys are not listed inauthorable-surface/ui.jsontoday (JoinedReportBlockshows nomeasuresrow), so no generated file is expected to move; if one does, the round reports it. Epic index (8 openpm:epic),pm:on-hold(43) andpm:blocked(58) bodies: no hit onreport.zod. In-flightdomain:speccards: none declares this file.Why this qualifies while product P0/P1s are open
An author (human or AI) who writes
measuresorobjectNameon a top-level report gets a rejection with no prescription, or, per triage, possibly a wrong one. NORTH-STAR rule 4 counts that as a product defect (「错的必须被响亮拒绝并给处方」). Triage graded it p2. It is ⛔ not tooling or contract hygiene, so rule 3 does not park it.Order carried to the dev
Triage's first step comes first: measure what the top-level table suggests today for each of the ten keys, and record the readings in the PR body. Any wrong suggestion (e.g.
objectName→name) is named there, and the fix covers it. A key with a real reason not to be accepted at the top level gets aguidanceentry in the card's shape, ⛔ never silence.
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 19822,
"status": "done",
"branch": "claude/issue-19822-report-top-level-aliases",
"pr": "#19855",
"session": "session_013RDBh5DqXd2xnLwvHLgLFr — this run's harness-stamped id (Claude-Session trailer on both commits)",
"premise_still_valid": true,
"summary": "The premise holds on origin/main 71ef221: ReportSchema's alias table lacked ten of JoinedReportBlockSchema's entries, and every target is a key the report declares. Measured first, as triage required. Nine of the ten were refused with no suggestion at all. orderBy alone got a suggestion,orderby edit distance, which is correct. No key drew a WRONG suggestion (objectName did not suggest name). The measured table is in the PR body. The fix adds the ten entries (objectName/object -> dataset, measures/metrics -> values, groupings/groupBy/dimensions -> rows, sort/orderBy/sortBy -> order) in the block table's order and wording, so the two alias tables are now identical (17 entries each). The 'kept deliberately parallel' comment now defines what parallel means and names the one deliberate difference, the guidance tables. The #5013 history note is unchanged and still true, and a new paragraph records the gap. No guidance entry was needed: none of the ten has a reason not to route at the top level. On a joined report, sort -> order ends at the existing refusal 'a joined report orders per block — move order onto blocks[]', verified by parse. Draft PR #19855 has Fixes #19822 and a line-leading Clause-②: no, plus a patch changeset. Deviations: (1) Before creating the worktree, this run once invoked the SHARED checkout's scripts/pm/with-fleet.sh --read to fetch the card. That checkout is stale, at 1f53b0b from the day before. Its fleet-token mint POST was refused by the egress proxy with 403, and its old classifier wrote a 30-minute write-pace stop marker against the fleet App token key (key f665bb8f6447, not the GITHUB_TOKEN key this run writes with). That marker has since expired. origin/main already classifies a proxy refusal as a prerequisite (isProxyRefusal in write-pace/fleet-token/label-write), so this is not a finding on main. All later reads and writes used curl with GITHUB_TOKEN, or write-pace/post-stamped from the worktree. (2) The harness attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. AGENTS.md takes precedence, so both commits carry the model-free pair, and the PR body ends with the session-URL footer; it was read back byte-identical to what was sent. Cleanup: the worktree's node_modules and the worktree itself are removed right after this comment is posted, because the comment tool runs from the worktree.",
"tests": "All readings are at final head c78242f unless another sha is named. (1) Targeted:pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ui/report.test.ts src/shared/alias-integrity.test.ts src/shared/strict-object.test.ts src/ui/strictness-batch14.test.tsunder os-verify-lock gave VERDICT command-exit 0, 'Test Files 4 passed (4) · Tests 123 passed (123)'. (2) alias-integrity.test.ts was run unedited: 20 passed (20) at 8a68843 and again inside (1). (3) The full spec local project at 8a68843 gave '523 passed (523)' files and '15434 passed | 1 todo' tests.git diff --quiet 8a688435cc c78242fe05 -- packagesexits 0, so the packages tree is byte-identical at head. (4)pnpm --filter @objectstack/spec typecheckexits 0: check:test-typecheck OK, debt ledger unchanged, andtsc -p tsconfig.test.json --listFilesOnlyincludes src/ui/report.test.ts. (5) FIRING CONTROL, run at 8a68843: node scripts/ablation-replace.mjs (WRAP mode) swapped the alias region back to 71ef221's text. The on-disk blob during the mutation was fe9d289f63ba, which equals 71ef221's blob for report.zod.ts. Result: 'Tests 10 failed | 40 passed (50)'. The per-key pin went red for 9 keys. It stayed green for orderBy, because distance already answered order. The parity pin went red listing exactly the ten '(top level: absent)' rows. Restore was proven: the blob after restore equals HEAD's 452f138f054d andgit diff HEADis empty. No dist is involved, since vitest imports ./report.zod from src. (6) Generated artifacts:pnpm --filter @objectstack/spec build(gen:schema included) leftgit status --porcelainempty, andcheck:generatedreported 'All 15 generated artifacts are up to date' against that dist (stamp match). (7) Gates:node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsderived 83 commands for 3 paths. All 83 were re-run at c78242f, and--ranread '83 derived, 83 run, 0 NOT-MEASURED, 0 UNRUN', every exit 0. check:dual-build-cjs-loads, check:type-check-debt and check:doc-formula-expressions first answered exit 3 (unbuilt prerequisites). The closure was then built with declarations under the lock, and each exited 0 on the same head; type-check-debt read '4 ledger entr(ies) re-measured … none above its recorded number'. (8) Lint, as a proven narrowing:eslint --no-inline-config --format jsonon the 2 changed source files gave 2 files, 0 errors, 0 warnings.--print-configshows no parserOptions.project, and eslint.config.mjs states it never enables type-aware linting, so no untouched file's verdict can move. The changeset is not an eslint input.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "5 — git push x3 via write-pace --run (empty-branch probe, 8a68843, c78242f); REST POST /repos/objectstack-ai/objectstack/pulls (draft, #19855) via write-pace --run + curl; REST POST /repos//issues/19822/comments (this report) via post-stamped.mjs. Also one REFUSED attempt outside budget: POST /app/installations/INSTALLATION_ID/access_tokens, a token mint by the stale shared checkout's with-fleet.sh --read, refused 403 by the egress proxy, never reached GitHub (see summary). No labels, no PATCH, no other comments.",
"open_questions": [],
"out_of_scope_findings": [
"class: c (with a b limb) · A type: 'joined' report accepts top-level dataset/rows/columns/values, and the renderer never reads them.ReportSchema.safeParse({ name: 'jj', label: 'J', type: 'joined', blocks: [a valid block], dataset: 'sales', rows: ['stage'], columns: ['region'], values: ['revenue'] })is ACCEPTED at c78242f, and so it is at 71ef221, since the superRefine is untouched. Contract text in report.zod.ts, dataset TSDoc: 'For ajoinedreport, the data lives onblocks.' The superRefine refuses onlyorderon a joined container. Renderer: objectui 0cf2d66 packages/plugin-report/src/DatasetReportRenderer.tsx:1462, where the joined branch returns before the top-level reads of dataset/rows/columns/values at :1529-1580, so the keys are silently dropped. Producer: code/AI-authored report metadata (defineReport / stored report rows). Studio's inspector hides the dataset fields for joined (ReportDefaultInspector.tsx:328 datasetBound = reportType !== 'joined'). This predates this PR: the existing fields->values and dataSet->dataset aliases already route there, and the new measures/dimensions/objectName routes do too on a joined report. The fix narrows acceptance, so it needs its own card and changeset. It is not a sub-issue of #19822 but an independent card in objectstack (packages/spec). Seam: spec:ReportSchema.superRefine (joined arm; dataset/rows/columns/values) → renderer:DatasetReportRenderer (joined branch) · dedupe words: 'joined report top-level values dataset ignored' · 'ReportSchema joined superRefine rows columns values' · 'joined report selection keys move onto blocks' · 'DatasetReportRenderer joined top-level dataset dropped'",
"carrier: none · noted, not filed — a stale shared checkout (/home/user/objectstack at 1f53b0b) still carries the pre-isProxyRefusal fleet-token/write-pace; running its with-fleet.sh from a cloud container writes a 30-minute stop marker on a proxy 403. origin/main has this fixed; no action beyond refreshing that checkout."
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 23, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 19822,
"round": 2,
"status": "done",
"branch": "claude/issue-19822-report-top-level-aliases",
"pr": "#19855",
"head": "926cff321d",
"session": "session_013RDBh5DqXd2xnLwvHLgLFr",
"premise_still_valid": true,
"summary": "Round 2: Test Core (4/6) was red on c78242f in @objectstack/platform-objects. Its reading pin in src/apps/translations/report-form-echo-decisions.test.ts asserted thatmeasureson a top-level report got no rename suggestion, which is the exact gap this PR closes. The pin is reversed in place, not deleted. Itsitis retitled "the alias table answers at BOTH levels — the top-level report renamesmeasuresontovalueslike the block", and it asserts that the top-level parse fails AND the message containsmeasures→values. The block half is unchanged, and the comment names #19822 as where the gap closed. That file's header bullet also claimed the asymmetry ("the alias table … lives on the BLOCK and not on the top-level report"), so it is rewritten to the new truth. No other test in the file changed. This is one commit, 926cff3, with model-free trailers and no card relation, pushed via write-pace. REPO-WIDE SEARCH, with git grep over packages/, content/docs/, examples/ and skills/: phrases "top-level report", "alias table is/lives on the block", "not on the top-level", "only on the block", "block and not", "kept deliberately parallel", "on this report", report+alias in either order, the ten keys within 80 chars of rejected/refused/no suggestion/unrecognized/did you mean, and everynot.toContain('Did you mean')site. Every hit outside the edited file is unrelated: other surfaces, CHANGELOG history aboutfilter, or the drillDown callout in content/docs/ui/reports.mdx. The only other readers of the new behaviour are this PR's own report.zod.ts and report.test.ts, which state the new truth. Every test file referencing ReportSchema/defineReport/JoinedReportBlockSchema was screened for message assertions: lint validate-chart-bindings/react-page-props, objectql sweep/overlay, rest meta, qa downstream-contract, spec chart/dashboard/strictness-batch14. None asserts on a report rejection message. content/docs/data-modeling/analytics.mdx: no sentence goes false. It never mentions aliases or suggestions. Its report section states only thatrows/valuesare dimension/measure names, that a non-selectedorderkey is an authoring error (still true), and that the inlineobjectName/columns/groupingsfields were removed in the single-form cutover (still true: the new aliases prescribe their successors, they do not restore them). WHY THE GATE DERIVATION MISSED platform-objects, as a reading, not a fix: dispatch-gates derivescheck:*gate families by matching path literals in gate sources, and its--ranzero covered those 83 families only. It lists package test suites as the path-scheduled CI jobTest Core, printed as NOT MEASURED outside its derived total, and it did print that row on this card.turbo ls --affected(SCM base 71ef221) names 77 packages, platform-objects included, since it depends on spec. I ran only spec's own suite because I read the change as moving no public surface (d.ts and api-surface identical). But the rejection message is runtime output that a consumer test pinned verbatim. It is the very behaviour the changeset declares, so the "public surface unchanged" reading was wrong for this change. Agit grepof consumer tests for ReportSchema message assertions would have found the pin.",
"tests": "All at head 926cff3. (1) Before the tests, spec and the platform-objects closure were rebuilt under os-verify-lock (VERDICT command-exit 0); dist/ui/index.mjs carriesmeasures: \"values\"twice.pnpm --filter @objectstack/platform-objects testunder the lock gave VERDICT command-exit 0, "Test Files 54 passed (54) · Tests 883 passed (883)". CI had 1 failed / 882 passed on c78242f. (2) The reversed test ran verbose and passed: "✓ … ⭐ the alias table answers at BOTH levels — the top-level report renamesmeasuresontovalues…", file 35 passed (35). (3) spec: report.test.ts + alias-integrity.test.ts + strictness-batch14.test.ts gave "3 passed (3) · 91 passed (91)". (4)pnpm --filter @objectstack/platform-objects typecheckexits 0, with check:test-typecheck OK and the ledger held. (5) Gates: dispatch-gates --commands derived 85 commands for 4 paths; new since round 1 are check:i18n and check:i18n-stale-fill. After a full package build under the lock, all 85 ran at 926cff3, and--ranread "85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN", every exit 0. Verdict lines include "check-i18n-bundles: OK (9 package(s) …)", "check-i18n-stale-fill: OK (10 bundle set(s) …)" and type-check-debt "4 ledger entr(ies) re-measured … none above its recorded number". (6) eslint --no-inline-config --format json on the 3 changed source files: 3 files, 0 errors, 0 warnings; no parserOptions.project, so no type-aware rules. (7) NOT RUN AS AN ABLATION this round: the reversed pin's red direction on the old spec. It is derived instead from the round-1 measurement at 71ef221, where the top-levelmeasuresmessage was exactly "Unrecognized key(s) on this report:measures." followed by the history sentence, with nomeasures→values; and conversely from CI's red on the old assertion at c78242f.",
"mcp_calls": "0",
"api_writes": "2 this round: git push (926cff3) via write-pace --run, and this os-dev-report comment via post-stamped.mjs (relay transport). No PR PATCH, labels, draft flip or other comments.",
"pr_body_replacements": [
{
"find": "- Final headc78242fe05:report.test.ts+alias-integrity.test.ts+strict-object.test.ts+strictness-batch14.test.tsgave4 passed (4),123 passed (123).",
"replace": "- Head926cff321d:@objectstack/specreport.test.ts+alias-integrity.test.ts+strictness-batch14.test.tsgave3 passed (3),91 passed (91). Atc78242fe05the same set plusstrict-object.test.tsgave4 passed (4),123 passed (123).\n- Head926cff321d:pnpm --filter @objectstack/platform-objects test, run after rebuilding spec and its closure, gave54 passed (54)files and883 passed (883)tests.pnpm --filter @objectstack/platform-objects typecheckexits 0."
},
{
"find": "## Generated artifacts",
"replace": "Reading pin reversed in@objectstack/platform-objects.packages/platform-objects/src/apps/translations/report-form-echo-decisions.test.tspinned the gap this PR closes as a reading: it asserted thatmeasureson a top-level report drew no rename suggestion. That turnedTest Core (4/6)red onc78242fe05. The test is reversed in place, not deleted. Under a retitledit, it now asserts that the top-level report refusesmeasuresAND names`measures` → `values`; the block half is unchanged. The file header bullet that described the asymmetry is rewritten to match. No other test in that file changed.\n\n## Generated artifacts"
},
{
"find": "Derived withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsfrom this worktree: 83 commands for the 3-path change set. All 83 were run at final headc78242fe05.dispatch-gates --ranread:83 derived, 83 run, 0 NOT-MEASURED, 0 UNRUN, with every recorded exit code 0.",
"replace": "Derived withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsfrom this worktree: 85 commands for the 4-path change set, the first round's 83 pluscheck:i18nandcheck:i18n-stale-fill. All 85 were run at head926cff321dafter a full package build.dispatch-gates --ranread:85 derived, 85 run, 0 NOT-MEASURED, 0 UNRUN, with every recorded exit code 0. That union coverscheck:*families only. Package test suites run in CI'sTest Core, which the tool lists as a path-scheduled CI job outside its derived total."
},
{
"find": "- The two changed source files were linted witheslint --no-inline-config --format json: 2 files, 0 errors, 0 warnings.",
"replace": "- The three changed source files were linted witheslint --no-inline-config --format jsonat926cff321d: 3 files, 0 errors, 0 warnings."
}
],
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsLanding — PR #19855 flipped ready + auto-merge by
domain:specseat 4, 2026-09-24T05:42ZDone on the maintainer's instruction, provenance:
- who: the maintainer;
- words: 「帮我处理」 for a list of twelve PRs "只差一份 at-tier 复核的 PASS 记录", then the landing route 「我直接落地」 chosen in the same exchange;
- where: the chat of session
session_019c3Hi6ZMU1p6m6aA6Bz45d(domain:spec#4).
This does not take over the claim: the claim, the branch and the card stay with the claiming seat, and this seat only lands the PR.
- Contract review: at-tier PASS, record
5808327465on PR fix(spec): the top-level report alias table routes the ten block spellings it claimed to mirror #19855, on the head the PR carries now. - Landing prechecks, re-read immediately before the flip: head unchanged; every check-run completed
successor skipped by design;check-governed-merges.mjsreads 0 governed paths; the PR is under 5,000 lines;mergeable_stateisclean.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorAuthorMore actionsLanded — PR #19855 →
ecf90b286f, 2026-09-24T06:05Zdomain:specseat 4 (session_019c3Hi6ZMU1p6m6aA6Bz45d), landing record for the landing done on the maintainer's instruction (provenance in this seat's landing comment above).- The card closed
completedthroughFixes #19822. The squashecf90b286fhas one parent and is an ancestor oforigin/main. - Mis-close check: of the cards closed since 2026-09-24T06:00Z, each was closed by its own PR; none by a stray keyword.
pm:dispatchedremoved. The assignee and the claim belong to the claiming seat and are left untouched.
Generated by Claude Code
- The card closed
- added a commit that references this issue
on Sep 28, 2026
Filing gate: ① a defect with a named landing site — the
aliasestable ofReportSchemainpackages/spec/src/ui/report.zod.ts. Finding class (c): an AI trap where metadata is refused with no remedy. It also carries a (b) limb: the file's own stated invariant is broken.Filed by the
domain:engineexecution seat 1 (session_01TEhopqrWQYBycZzyJHpAZr). The predecessor seat (session_01NcPSwnmJHczmTu6FG7NMjE) measured this and could not file it; it is item 2 of seat post #6367's owed list, comment5763392209. Re-measured by this seat onorigin/mainc11852406, ⛔ not adopted from that record. ⛔ Filed bare: routing and grading are triage's. ⛔ Not a claim.What happens
report.zod.tsholds two alias tables.ReportSchema's says of itself:It is not parallel. Every key below is DECLARED on
ReportSchemaas the target (dataset,values,rows,orderare all top-level keys there), yet only the block table routes to it:JoinedReportBlockSchemaReportSchema(top level)measures,metricsvaluesdimensions,groupBy,groupingsrowssort,orderBy,sortByorderobjectName,objectdatasetThe entries the two tables DO share (
dataSet,source,fields, the four scope-filter spellings) are identical, so the gap is the rest of the block table, not a deliberate divergence. The table's own history note names the only three deliberate removals (columns,chart,chartConfig) and none of the ten above.Why it matters
measures:on a plain report gets a bareunrecognized_keysrejection, while the same key one repeater level down gets 「did you meanvalues」. The report's own describe prose tells the author the box wants 「the dataset's measures」, so the surface invites exactly this key.objectName→name) would be worse than silence, and would change the card's weight.Suggested shape (⛔ not a ruling)
Copy the ten block-table entries into
ReportSchema's table, then letalias-integrity.test.ts(which the history note says proves every table in the package) re-prove both halves. If any entry is deliberately absent at top level, the fix is aguidanceline saying why, ⛔ never silence.Filing-gate answers
packages/specafter triage routes it (the domain table putspackages/specindomain:spec). ⛔ This seat writes nodomain:*.closedincluded:ReportSchema aliases→ 5 hits, all closed, none this defect (nearest ReportSchema 的filter别名指向filters—— 一个 ReportSchema 同样拒绝的键(#4001 战役自己的假处方,第 5 例) #5013, thefilter→filtersfalse prescription, a different key);ReportSchema alias table measures values dimensions rows JoinedReportBlockSchema→ 0;report measures unrecognized key no suggestion alias values→ 0.Dedupe words:
ReportSchema aliases measures values·report top-level alias parity block table·dimensions rows alias report·sort orderBy order alias reportGenerated by Claude Code