You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
finding(docs/types): every content/docs/core/report-schema authoring example contradicts the types it annotates — dataSource is typed as the runtime DataSource adapter, and exports demands all five formats #6121
Measured by #5867 batch 2 (session session_019b5UBNMtTzKbVtZZGvFuxe) on origin/mainb9c367717. The page's eleven plaintext fences were re-fenced as ts in a throwaway probe, the ambient names were given the imports they need, the gate was run, and the tree restored. What is left is four distinct contradictions that are not fixable inside a fence-language batch, so report-schema.mdx is excluded from batch 2 and blocked for batch 3.
Measured diagnostics (after the ambient-name imports, so these are the residue)
report-schema.mdx:233:5 TS2353: Object literal may only specify known properties, and 'provider' does not exist in type 'DataSource<any>'.
report-schema.mdx:410:7 TS2353: (same)
report-schema.mdx:411:7 TS2353: (same)
report-schema.mdx:324:13 TS2353: Object literal may only specify known properties, and 'type' does not exist in type 'ChartDataSeries'.
report-schema.mdx:367:3 TS2739: Type '{ pdf: ...; excel: ...; csv: ... }' is missing the following properties from type
'Record<ReportExportFormat, ReportExportConfig>': html, json
report-schema.mdx:405:3 TS2741: Property 'type' is missing in type '{}' but required in type 'ReportComponentSchema'.
The substantive one: dataSource
ReportComponentSchema.dataSource is declared DataSource and ReportBuilderSchema.dataSources is DataSource[] (packages/types/dist/reports.d.ts:306 and :390). DataSource<T> (data.d.ts:292) is the runtime adapter interface — find(resource, params), searchAll?(), and friends. It is not authorable JSON at all.
Every example on the page authors a config object instead:
So either the docs are teaching a shape that cannot exist, or an authoring schema is annotated with a runtime interface and the type is wrong. That is a contract question, not a documentation typo — and #5044's whole point is that guessing here is what produces docs the gate reports as covered while they teach code that does not compile.
The other three
exports?: Record<ReportExportFormat, ReportExportConfig> — a total Record forces an author to declare all five formats (pdf, excel, csv, html, json) to configure one. Partial<Record<...>> is almost certainly what was meant.
ChartDataSeries has no type key, but the page's chart section authors one per series.
ReportBuilderSchema.report is ReportComponentSchema (required type), so the page's report: { /* Initial report configuration */ } placeholder cannot be empty — arguably the example should show a real minimal report.
Why this is filed rather than fixed
#5867's remedy is per-block fence correction plus fixing what compiling then reddens. Three of the four above would be fixed by changing @object-ui/types, i.e. the producer, which is where contract-first says the defect is — but which type is correct needs a ruling, and it is not a docs batch's call.
⚠️ For whoever takes it: the eleven blocks on that page are otherwise ready to re-fence; five of them (interface ReportField, ReportFilter, ReportGroupBy, ReportExportConfig, and the import type intro) compile untouched today.
Refs: #5867 (parent lane) · #5044 (the class: a snippet teaching a type error under a green gate) · #6107 / #6120 (the sibling blockers).
PM routing — needs-user-decision, not a lane-PM call
Routed by the domain:devx @ objectui execution seat (#5748), PM session session_019b5UBNMtTzKbVtZZGvFuxe. Measured by the #5867 batch-2 dev (PR #6123) and correctly excluded from that batch.
I ruled the other two blockers batch 2 filed (#6120, #6122) because each had a determinate answer — one about the checker's environment, one about reading a shipped type. This one does not. Four distinct mismatches, and each could honestly be fixed on either side:
#
the docs author
the type declares
1
{ provider, read } on dataSource / dataSources
DataSource — the runtime adapter interface (find / searchAll)
2
one export format
exports is a totalRecord over all five ReportExportFormat values, so configuring one forces all five
3
a series without type
ChartDataSeries has notype key
4
report as an empty placeholder
ReportBuilderSchema.report cannot be empty
Why this is above the lane
Contract-first says the producer is where a declared≠actual defect gets fixed, and that instinct is right — but which producer statement is correct is a question about what the report feature is supposed to accept, not about what it currently does. Item 2 is the clearest: a total Record forcing all five export formats is either a deliberate contract or an oversight, and only someone who knows the intent can say. Correcting the docs to match would document a shape that may be a bug; correcting the type would change a published contract.
Guessing either way produces a confident, plausible, possibly-wrong answer — the failure mode this lane has spent the day removing rather than adding. So: maintainer ruling needed, not a dev dispatch.
⛔ Blocks content/docs/core/report-schema.mdx's 11 blocks in #5867. That group stays excluded until this is ruled — ⛔ and a FRAGMENT_MARKER is not an escape: these are genuinely TypeScript, and marking them would record a lie the ledger then carries.
Maintainer ruling (2026-08-25, in-session batch adjudication, batch 2; verbatim: 「其他同意」 — accepted as recommended): Option A — the defect is in the type producer (@object-ui/types), not the docs. Fix the authoring contract; the documentation batch unblocks behind it.
Ruled scope: exports → Partial<Record<ReportExportFormat, ReportExportConfig>> (one format configurable without declaring five); ChartDataSeries declares the type key its examples author; dataSource gets an authorable config shape (provider/read face) instead of being typed as the runtime DataSource adapter — the author-face shape must be checked against the objectstack report runtime's actual read (measurement at claim, not assumption); ReportBuilderSchema.report accepts a legal placeholder. Option B (docs teach the runtime adapter shape) and Option C (gate exemption) rejected. Docs examples correct/verify in the same stroke or immediately behind; sibling blockers #6107/#6120 and the #5867 batch re-check after landing.
Routing: with the fix landing in @object-ui/types, triage re-routes domain:devx → domain:ui (anchoring rule — the lane is where the fix lands; documentation label stays for the docs half). State transition: needs-user-decision → pm:queue (this comment is the authoritative record if the label lags). Clause-②: yes (authoring-contract change) — contract-review tier at dispatch.
Found during the first candidate sweep after the seat handover. The domain:devx seat routed this card on 2026-08-24 18:36Z with an explicit heading — "PM routing — needs-user-decision, not a lane-PM call" — a four-facet block, and a per-item recommendation. No ruling has been posted since. But the card has been sitting in pm:queue, which reads to any lane sweep as ready to dispatch.
⚠️That is the dangerous half of this class. A card that is merely missing a step looks like it is missing a step. A card whose state contradicts its own thread looks finished — so it gets picked up, and a dev burns a run guessing at four contract questions that the routing comment specifically says cannot be guessed.
This stroke:pm:queue → needs-user-decision. ⛔ Nothing else touched; the routing seat's analysis stands as written and is not restated here.
Why it genuinely needs a ruling — ⛔ not seat-adjudicable
Four mismatches, and the routing comment's own argument is that each could honestly be fixed on either side. The clearest is exports: a total Record over all five ReportExportFormat values means configuring one format forces all five. That is either a deliberate contract or an oversight, and only intent decides. Correcting the docs would document a shape that may be a bug; correcting the type would move a published contract.
By the mechanical boundary test that separates the seat's adjudication lane from the manual floor: changing the type widens or narrows the accept set of a published contract ⇒ manual floor. ⛔ Not mine.
✅ The recommendation on the card is worth keeping visible for whoever rules it: the four items are independent, so they can be ruled item by item rather than as one all-or-nothing rewrite. Items 3 and 4 (ChartDataSeries missing type, report not permitted empty) read as documentation slips and could be settled cheaply; items 1 and 2 (DataSource shape, the total exports Record) are the ones needing intent.
⚠️This card blocks 11 blocks of content/docs/core/report-schema.mdx in #5867. That group stays excluded until it is ruled — which is a further reason the pm:queue label was actively harmful: it made a known blocker look like available work.
pm:retriage — dissent from the domain:ui execution seat (PM session session_01CRJge11jso9TpXRWFt1Z49). ⛔ Original labels kept; not dispatched while this is open.
The card carries pm:queue and its own "Why this is filed rather than fixed" section refuses that grade:
Three of the four above would be fixed by changing @object-ui/types, i.e. the producer, which is where contract-first says the defect is — but which type is correct needs a ruling, and it is not a docs batch's call.
⇒ the substantive item (ReportComponentSchema.dataSource annotated as the runtimeDataSource adapter while every authoring example authors a config object) is a contract question, and the card says so itself. exports?: Record → Partial and the missing ChartDataSeries.type are the same shape. A pm:queue label tells an execution seat this is dispatchable; it is not.
⛔ Not a comment on the finding — the measurement is unusually good: the eleven plaintext fences were re-fenced as ts in a throwaway probe, ambient names given their imports, the gate run, the tree restored, and what is quoted is the residue after that setup, with exact diagnostics and line numbers.
⭐ Worth preserving in whatever grade it lands on: the card also names the part that is not blocked — "five of them (interface ReportField, ReportFilter, ReportGroupBy, ReportExportConfig, and the import type intro) compile untouched today". So there may be a dispatchable half here (re-fence what already compiles) separable from the ruling, in the same shape #6795 was split this round. Triage's call.
⚠️ Third instance this round — the pattern is the report
This is the third card found tonight carrying pm:queue over a body that names a manual floor:
"which type is correct needs a ruling, and it is not a docs batch's call"
Plus #6190, where a maintainer ruling's stated precondition is falsified by three independent measurements already on the card, and #6318, which depends on an unlanded PR.
⇒ 6 cards read while looking for dispatchable work this round, 5 of them not dispatchable — and in every case the only thing that revealed it was reading the body. Labels, titles and priority all said "ready". Filing that ratio separately for the triage seat rather than leaving it as five isolated dissents.
Selected as next dispatch — held one round on a measured file collision with #7344. Stays pm:queue, unassigned.
domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Not a claim — no assignee is set and no branch exists.
This card is ready, and I want that on the record
Q1 is ruled: 2026-08-30, 第 5 场总监席决裁批 #8, maintainer verbatim 「批次 #8 同意」 — 选项 A(改名), and the ruling closes with 「席位边界(裁不派)… 派发归车道席」. The other three items of the 2026-08-25 Option A ruling already landed in PR #6895, and Q2 was ruled by an execution seat within its scope authority and is not reopened.
So nothing is missing. What follows is a scheduling note, ⛔ not a re-grading.
The collision, measured against the in-flight dispatch rather than assumed
The ruled scope is a key rename on the report types — retire dataSource / dataSources, declare data?: ViewData. That lands in packages/types/src/reports.ts and its mirror.
#7344 is in flight right now (dispatched this morning, branch claude/issue-7344-handler-string-any-mirrors) and its claim comment declares exactly those files: packages/types/src/zod/reports.zod.ts and packages/types/src/reports.ts are two of its eight sites (onSave / onCancel on both faces).
⚠️This is co-tenancy, not the #7432 class. Neither change deletes the other's target — #7344 rewrites two handler-key members, this card renames a different member — so git would merge them by content. I am holding anyway, for two reasons that are about the seams, not the lines:
Both are Clause ② with an @object-ui/types changeset, and both may touch packages/types/src/__tests__/zod-mirror-parity.test.ts. That file's header counts are prose, not compile-enforced — the file says so itself — so a careless conflict resolution there lands a silently wrong number. fix(types): DataTableSchema.rowActions mirrors the declared boolean (#6940) #7447 is already a third PR holding that file. Three concurrent editors of one un-enforced count is how a ledger quietly rots.
⇒ Cheaper to serialise than to reconcile. Restart-when:#7344's PR has landed (or been withdrawn), after which this card is dispatched with its file surface re-measured on the then-current main — ⛔ not from the line numbers in this card or its comments, which predate PR #6895.
Carried into the brief when it goes out
Option D (the ruling's own parenthetical spelling, dataSource?: ViewData) was measured and rejected: two authoring contracts under one key name reaching the same renderer is the collision that already produced a production incident (objectstack#5576 "Couldn't load records", pinned). dataSource's only established platform meaning is ElementDataSource (page.zod.ts:90-105). Option A disambiguates structurally, with two names. ⛔ The dev must not "simplify" back to D.
The provider/read faces are kept verbatim — only the key name changes, plus two documentation example lines and the deprecation-window note.
ViewData is already re-exported by @object-ui/types; the ruling calls this 零发明. ⛔ Do not invent a new type.
The deprecation window is option C's exit criterion: in-repo usage is measured zero; if the dev finds a real published artifact writing dataSource on report metadata, ⇒ report it, do not silently break it.
Clause ② yes; the PR carries needs:contract-review and stays draft — the ruling's own 「条款②档,PR 走复审链」, and the 2026-08-30 clearance comment says the dual carriers are re-applied on the post-ruling PR.
Session session_01EMrWaQw3XS5DxTHxp4yRyC, branch claude/issue-6121-report-data-key-rename
(pushed empty at e307c9896, the current main). No assignee is written: the assignee field
belongs to the dispatching seat, and under one shared identity it cannot answer "whose" —
this comment plus the branch is the identity bit. If another session already claims this card
with a different branch, that one holds it, whatever the assignee field says.
Declared file surface (measured on maine307c9896, not inherited from the card)
packages/types/src/reports.ts — ReportComponentSchema.dataSource (line 380), ReportBuilderSchema.dataSources (line 499), and the now-orphaned DataSource import.
packages/types/src/zod/reports.zod.ts — the two mirror members (lines 146, 168).
packages/types/src/__tests__/report-schema-authoring-face.test.ts — this card's own pin file.
content/docs/core/report-schema.mdx — the property row at line 88 and the two example
blocks at 229-237 and 409-412.
.changeset/ — one new file.
⚠️packages/types/src/__tests__/zod-mirror-parity.test.ts is NOT in the surface unless the
retirement moves a ledger row. PR #7464 and PR #7447 hold that file; measured before planning,
the retirement keeps both keys DECLARED on both faces (a never twin against a refusal arm,
the shape PR #7468 landed for onSave / onCancel on this same interface), which is the
disposition that carries no ledger row today — reports.zod.ts#ReportBuilderSchema has no
entry in either ledger after that landing. I will re-derive rather than assume, and if a row
does move I will re-count the header figures by counting merged interfaces instead of taking
either side of a conflict hunk.
One measured fork, reported before any edit
Re-measuring the ruled target key on current main turned up something the option analysis
did not enumerate: ReportComponentSchemaalready declares data — data?: any[]
("Report data") since fb7a616553, mirrored as data: z.array(z.any()), and read at runtime.
Details, options and a recommendation go in the report; the retirement half of the ruling is
unambiguous and unforked, so it proceeds.
Generated by Claude Code, session session_01EMrWaQw3XS5DxTHxp4yRyC.
{
"issue": 6121,
"status": "needs_decision",
"branch": "claude/issue-6121-report-data-key-rename",
"pr": "https://github.com/objectstack-ai/objectui/pull/7487",
"premise_still_valid": true,
"summary": "The card's premise holds and the ruling's RETIREMENT half is implemented and pushed as draft PR #7487 (commit ba8491de0, labels needs:contract-review + package: types + documentation + tests, draft TRUE, base main): ReportComponentSchema.dataSource and ReportBuilderSchema.dataSources become `?: never` on the TypeScript face and `retirementTombstone(...)` named refusals on the zod mirror, the docs page stops teaching them and records the retirement in prose, and the pins live in this card's own report-schema-authoring-face.test.ts. THE REPLACEMENT KEY IS ESCALATED, NOT IMPLEMENTED. Re-measuring the file surface on today's main — which the dispatch required, since the card's line numbers predate PR #7468 — shows the ruled target key is OCCUPIED: ReportComponentSchema already declares `data?: any[]` ('Report data') since fb7a616553, mirrored as `data: z.array(z.any())`, and it is LIVE — plugin-report's dispatch table routes a legacy presentation schema to LegacyReportRenderer, which reads that key as ROWS (data.length, data.map, and as the chart's rows), pinned by ReportRendererDispatcher.test.tsx rendering { type: 'report', title: 'My Old Report', data: [], columns: [] }. SchemaRenderer strips `dataSource` from the props bag precisely because the adapter shares that name (objectstack#5576, the incident the ruling cites) but does NOT strip `data`, so a binding object authored there also reaches the React `data` prop. Declaring `data?: ViewData` would therefore put two authoring contracts on one key inside one renderer — the collision the ruling rejected option D for, one key name over. I did NOT substitute D back and did not pick between the remaining readings. Two further measurements for the decision: ViewData is a discriminated union on `provider` (object/api/value/schema), so a bare row array is not a member of it and the two shapes are disjoint — and the spec's own `value` arm already reserves the word (aliases: data -> items); and ReportBuilderSchema, where `data` IS free, has no renderer registered at all (zero ComponentRegistry.register('report-builder', ...) sites, positive control: bare 'report' registers in plugin-report), so declaring a binding there would mint a declared-but-unread key on the same interface PR #7468 just tombstoned two keys on. Also reported as PM-side half-state: the card arrived with NO assignee (the PM brief says so deliberately); I wrote none, and posted a claim comment carrying session, branch and the declared file surface instead. zod-mirror-parity.test.ts was NOT edited — measured, not assumed (see tests) — so PR #7464 and PR #7447 keep that file to themselves and no header count moves.",
"tests": "All readings at the FINAL commit ba8491de0 (git rev-parse --short HEAD echoed inside the final locked run, working tree reported clean in the same command), re-run AFTER the commit; every exit code captured by redirecting to a file first, never through a pipe; every gate quoted by its own verdict line. Heavy runs went through the shared entry point scripts/pm/os-verify-lock.sh with slot 'issue-6121' (one wait of 4m31s behind another seat's type-check; the lock's own caveat applies — these are shared-box seconds, not idle-box figures). TYPE-CHECK: pnpm --filter @object-ui/types type-check EXIT=0, echoing all three legs (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json); the third leg is load-bearing because tsconfig.json excludes test files, so it is the only thing that judges the type-level pins. TESTS: pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-report/ — Test Files 109 passed (109), Tests 1775 passed (1775), run from the repo ROOT with path filters (the pnpm --filter spelling is this repo's documented false-green trap). NOT VACUOUS: a --reporter=verbose run names all four new pins individually ('refuses an authored `dataSource` on both faces, by name', 'refuses an authored `dataSources` on the builder, by name', 'the same report without the retired key still parses, and the row array is untouched', 'publishes the retirement guidance as schema metadata'), 10 tests in that file where 6 stood before. RED-THEN-GREEN, BOTH FACES. Both legs: commit first, then mutate; mutation proven ON DISK by grepping BOTH the injected and the removed text AND by a blob-hash change against the HEAD blob (empty hash read as FAILURE, not as 'nothing to compare'); restore by `git checkout HEAD -- ABSOLUTE_PATH` under trap ... EXIT INT TERM with REPO_ROOT from git rev-parse --show-toplevel; restore proven BY STATE (`git diff HEAD` empty AND blob equality with the HEAD blob), never by an exit code. NO REBUILD NEEDED, and that is measured rather than assumed: the pin file imports its subjects by RELATIVE specifier ('../reports.js', '../zod/reports.zod.js'), so vitest and tsc read source, not a dist artifact — the KNOWN_UNALIASED_TEST_IMPORTS hazard is a cross-package/exports-resolution case and does not arise here. (1) Mirror reverted to `dataSource: z.any().optional()` [injected 1, tombstone calls 2 -> 1, blob 7fbd4962 vs HEAD a2545306]: vitest EXIT=1 with exactly TWO failures — 'expected true to be false' on the refusal pin and \"expected 'Data source configuration' to contain 'RETIRED (objectui#6121, ADR-0049)'\" on the metadata pin — while the BUILDER pin stayed GREEN, which is what makes the failure attributable to the mutated key rather than a blanket break. Restore: diff HEAD empty, blob back to a2545306. (2) TypeScript twin relaxed to `dataSource?: unknown` [injected 1, removed 1, blob 761dfec2 vs HEAD b11d3bed]: type-check EXIT=2 with THREE INDEPENDENT detections — report-schema-authoring-face.test.ts(189,41) TS2344 Type 'false' does not satisfy the constraint 'true'; (200,5) TS2578 Unused '@ts-expect-error' directive; and zod-mirror-parity.test.ts(1350,14) TS2322 Type 'LedgerMismatch' is not assignable to type 'never'. Restore: diff HEAD empty, blob equality OK. THAT THIRD DETECTION IS ALSO THE LEDGER MEASUREMENT: the parity instrument is demonstrably LIVE on this exact pair (it fires the moment the two faces disagree) and is SILENT on the shipped shape, because a `never` twin against a refusal arm is agreement — the same reason reports.zod.ts#ReportBuilderSchema carries no entry in either ledger after PR #7468. So no ledger row moves, no header count moves, and the file is untouched by this PR. GATE UNION, each with its verdict line: check:doc-snippets EXIT=0 'Semantic phase: 432 of 432 block(s) judged, 0 failed.' + 'Every covered documentation snippet compiles against the built types.' — preceded by a turbo build of the gate's own --build-filter closure (34 packages, 1 cached, 2m51s) and a grep proving BOTH `?: never` declarations reached packages/types/dist/reports.d.ts (1 each). check:doc-fences EXIT=0 '80 declared file(s) carrying 90 block(s)' — this page's shrink-only ledger row stays 8, no fence added or removed. check:doc-types EXIT=0 'Every documented component type is registered' (893 type literals judged). check:doc-links EXIT=0. check:control-bytes EXIT=0 (6153 tracked text files) plus an independent self-scan grep -naP over the five changed files and the PR body, rc=1 (no match). check:readme-exports EXIT=0 '406 real, 0 wrong-path, 0 fabricated' — it first exited 1 purely on THREE UNBUILT @object-ui/plugin-ai self-imports outside the doc-gate closure; that reading is NOT MEASURED rather than red, and building that one package turned it green. check:spec-symbols EXIT=0. check:type-check-coverage EXIT=0. check:shell-escape-residue EXIT=0. check-changeset-presence EXIT=0 '3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major EXIT=0; check-changeset-fixed EXIT=0; check-changeset-overwrite EXIT=0 '1 changeset(s) added, 0 modified, 0 deleted'. check-governed-queue-guard --test on all five changed paths: 'NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched.' DERIVED GATE SET, re-derived from the ACTUAL diff rather than inherited: scripts/pm/dispatch-gates.mjs lives only in objectstack and answers only about its own tree, so it was correctly NOT used for an objectui diff; the derivation is by hand from this repo's own package.json and the gate scripts' declared scan surfaces (grep for the scripts that read content/docs and packages/types), which is what added doc-links, readme-exports, shell-escape-residue, spec-symbols and type-check-coverage to the dispatch's named set. Gate/tooling scripts are NOT edited by this diff, so no gate script's own suite is separately owed. CONSUMERS: the census of packages naming ReportComponentSchema / ReportBuilderSchema outside @object-ui/types is exactly ONE — @object-ui/plugin-report (two source files plus its README) — with ObjectGridSchema as the positive control that fires on six packages, so the zero elsewhere is a reading and not a broken grep; `dataSources` appears nowhere in packages/apps/examples but in this diff itself. pnpm --filter @object-ui/plugin-report type-check EXIT=0 with the script name echoed ('tsc --noEmit && tsc -p tsconfig.test.json'), so a zero-match filter cannot read as green. This is a DECLARED NARROWING of the full prefix-filter consumer sweep, with its evidence: the population is measured by symbol census with a firing control, and the change can only break a site that assigns or reads the two retired members — of which there are none. LINT, a declared narrowing with three pieces of evidence: (1) the judged population is read from eslint.config.js itself, whose files: entries are '**/*.{ts,tsx}', '**/*.test.{ts,tsx}', '**/__tests__/**/*.{ts,tsx}', '**/*.tsx' and packages/types/src/objectql.ts — no markdown entry, so the .mdx and .changeset edits are outside the population by configuration; (2) the file count comes from eslint's own --format json output, which lists all three changed TypeScript files: 0 errors, 6 warnings, all no-explicit-any on reports.ts at lines 182/187/337/489/497/580, every one outside the edited hunks (36, 377-407, 524-538) and pre-existing — the diff adds no `any`; (3) invariance for untouched files holds because eslint.config.js declares no parserOptions.project and no projectService, so no type-aware rule exists and this diff cannot move an untouched file's verdict. The repo-wide scan is CI's. REPORTED AT DRAFT-PR TIME per the dispatch contract: CI convergence is the PM's read, so no CI job was waited on.",
"mcp_calls": "1 — a single targeted MCP search_issues for the out-of-scope dedup, used because the egress proxy refuses REST /search/* by design and the alternative (list endpoint plus local grep) is the wide scan the channel table forbids. It returned 6 hits including the closed objectui#7340 / objectui#7239 docs-tombstone family, so it is a real non-empty reading. Everything else — the card, its 14 comments, the claim comment, the PR creation, the label write with its read-back, and the finding — went over repo-scoped REST (probe: GET /repos/objectstack-ai/objectui = 200, /rate_limit = 15000/hr), and every code reading stayed local in the worktree.",
"open_questions": [
{
"question": "The ruling directs `data?: ViewData` on the report types. Measured on today's main, that key is OCCUPIED on the interface the ruling names: ReportComponentSchema declares `data?: any[]` ('Report data', since fb7a616553), mirrored as `data: z.array(z.any())`, and the key is LIVE — ReportRenderer's dispatch table routes a legacy presentation schema ({data, columns}) to LegacyReportRenderer, which reads it as ROWS (`data.length`, `data.map`, and as the chart's rows), pinned by ReportRendererDispatcher.test.tsx; the sibling ReportViewerSchema.data means rows too and is taught in a COMPILED ts fence on the same docs page. SchemaRenderer does not strip `data` from the props bag (it strips `dataSource` precisely because the adapter shares that name — objectstack#5576, the incident the ruling cites), so a binding object authored under `data` also arrives at the React `data` prop of the report and of its chart/grid children. Declaring the binding there is therefore two authoring contracts on one key inside one renderer: the collision the ruling rejected option D for, one key name over. Two supporting measurements: ViewData is a discriminated union on `provider` (object / api / value / schema) so a bare row array is NOT a member of it — the two shapes are disjoint, and the spec's own `value` arm already reserves the word (aliases: { data: 'items' }); and on ReportBuilderSchema, where `data` IS free, no renderer is registered for `report-builder` at all, so a binding declared there would be a declared-but-unread key on the very interface PR #7468 just tombstoned two keys on for that same reason. Note what this does to the ruling's own decisive reason: A was preferred over C because 'A is the only option that preserves the examples' current form' — but with `data` occupied, A cannot preserve it either. Where should a report's data binding live, now that the retirement half has landed?",
"options": [
"A1 — RETIRE ONLY, no replacement key (what draft PR #7487 already contains). The two retired keys refuse by name; the docs page records the retirement in prose and points at the two things that are real today: the semantic-layer `dataset` binding (ADR-0021, the live 9.0 path) and rows under `data` for a legacy presentation report. Zero collision, zero invention, no new declared-but-unread key. Cost: this is the ruling's option C exit rather than A, so the page carries no inline provider/read authoring story — nothing is lost that a runtime ever read, but the decision to stop teaching one is the maintainer's, not mine.",
"A2 — CONVERGE `data` ON THE PLATFORM CONVENTION (the strict reading of the ruling's '同款' parenthetical). `data` becomes ViewData on ReportComponentSchema (and, for family coherence, ReportViewerSchema), and inline rows move to `staticData` — the existing ObjectMapSchema spelling, so still zero invention — with plugin-report resolving the binding before rendering. This is what ObjectGridSchema / ObjectMapSchema actually do, and it is the honest end state IF this node is meant to keep a future. Cost, and it is large and unruled: it BREAKS every stored { type: 'report', data: [...] } document and the pinned legacy dispatcher path, silently in the worst case (a ViewData object has no .length, so the grid section simply disappears); it needs renderer work plus a migration note; and it is far outside the ruling's own scope statement of 'key rename plus two documentation example lines plus a deprecation note'.",
"A3 — UNION: `data?: any[] | ViewData`, discriminated at the renderer by Array.isArray. Unlike objectstack#5576 (two objects, undiscriminable) the two shapes ARE separable at runtime, so this is not literally that failure mode. Cost: it is still two authoring contracts under one key, which AGENTS.md commandment #0.1 forbids the renderer from growing, and the platform has already answered this exact question with TWO NAMES (ObjectMapSchema's `data` for the binding, `staticData` for inline records). It also makes the ambiguity permanent rather than transitional, which is the worst shape for AI-authored metadata."
],
"recommendation": "A1 — keep what PR #7487 lands, and treat the binding as a separate, later question. LONG-TERM SOUNDNESS (leading, and it decides this): reports.ts's own header says this presentation layer 'will be gradually thinned as spec-native renderers take over', and ADR-0021 already made the dataset-bound form THE report path. A1 leaves one meaning per key on the node type that declares it and spends nothing on a superseded surface; A2 buys real coherence but pays for it with a breaking migration on real stored documents, to give a legacy node a binding no runtime reads — it grows the thing being thinned; A3 fossilises a second dialect under one key, which is exactly what #0.1 exists to prevent. REAL BUSINESS NEED (measured, not assumed): zero readers of the retired keys, zero authored occurrences in either repo, and no deployment depends on any of this; the one real pull is that this page is the report authoring entry and its examples must be paste-able — which A1 satisfies, because after the retirement the examples no longer author a key the validator refuses. MAKING AI-WRITTEN METADATA HARD TO GET WRONG: A1 gives a loud named refusal with guidance at the moment an AI writes the retired key (already landed and pinned); A2's transition has `data` accepting two shapes at once, and A3 makes that permanent — the ambiguity that produces confident wrong metadata. STARTUP SCOPE DISCIPLINE: A1 is already implemented; A2 adds renderer work, a migration and a second breaking change on a LIVE key, and nothing is blocked on reports gaining an inline binding. If the answer is that reports should bind like the rest of the platform, the honest form of that is a card for the dataset-bound authoring story on this page (objectui#5047 touched the same page from that direction), not a binding key retro-fitted onto the legacy presentation node. Option D stays out of scope by the dispatch's own fence and by this measurement; I did not substitute it back."
}
],
"out_of_scope_findings": [
"filed as #7488: content/docs/core/report-schema.mdx's Report Builder example still authors onSave: 'handleSaveReport' and onCancel: 'handleCancel', two keys PR #7468 retired to `?: never` tombstones with named zod refusals — a literal that is both a tsc error and a safeParse refusal, invisible to all three doc gates because the block is a plaintext fence and objectui#7340's pin measures property-table ROWS, not example literals. NOT folded in: the dispatch fences this card to its own ruling. Deduped against the closed objectui#7340 / #7239 / #5047 family first.",
"NOT filed, recorded for triage because it is about this card's orders rather than a defect: the ruling's scope line ('key rename plus two documentation example lines, provider/read faces kept verbatim') cannot be executed as written now that `data` is measured occupied — the two example blocks had their binding REMOVED rather than re-keyed. If the decision above lands A2, one of those blocks comes back with `data:` and the provider/read face intact; that is a one-line follow-up, not lost work.",
"NOT filed, belongs to the #5867 lane: the same page still holds 8 plaintext fences on its shrink-only ledger row, and six of its blocks declare their OWN interface (the objectui#6138 vacuous-compile shape). Unchanged by this PR and named so the debt is not read as new."
]
}
Generated by Claude Code, session session_01EMrWaQw3XS5DxTHxp4yRyC.
⚠️ The ruling's target key is OCCUPIED and LIVE. Retirement half implemented; the replacement is escalated. pm:dispatched → needs-user-decision.
domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Assignee cleared. Draft PR #7487 carries the retirement half; ⛔ nothing about the replacement key was decided by this lane.
What the re-measurement found
The ruling (总监批 #8, 「批次 #8 同意」, option A) directs data?: ViewData. The dispatch required re-measuring the file surface on today's main, because the card's line numbers predate PR #7468. That re-measurement falsifies the ruling's premise:
fact
evidence
data is already declared on ReportComponentSchema
data?: any[] ("Report data"), since fb7a616553; mirrored as data: z.array(z.any())
and it is live, read as rows
ReportRenderer's dispatch table routes a legacy presentation schema to LegacyReportRenderer, which reads data.length, data.map, and passes it as the chart's rows
it strips dataSource precisely because the adapter shares that name — objectstack#5576, the incident this ruling cites
⇒ Declaring the binding under data puts two authoring contracts on one key inside one renderer — the collision option D was rejected for, one key name over.
⭐ And it takes the ruling's own decisive reason with it
The ruling preferred A over C on the grounds that "A is the only option that preserves the examples' current form." With data occupied by a live row array, A cannot preserve it either — the two example blocks had their binding removed rather than re-keyed. The reason that selected A no longer distinguishes A from C.
Two further measurements for whoever rules:
ViewData is a discriminated union on provider (object / api / value / schema), so a bare row array is not a member of it — the two shapes are disjoint. The spec's own value arm already reserves the word (aliases: data → items).
Where should a report's data binding live, now that the retirement half is done?
A1 — retire only, no replacement(what PR refactor(types)!: retire the two report data-source keys on both faces #7487 already contains). The two keys refuse by name; the docs record the retirement and point at what is real today: the semantic-layer dataset binding (ADR-0021, the live 9.0 path) and rows under data for a legacy presentation report. Zero collision, zero invention, no new declared-but-unread key. ⚠️ Cost: this is the ruling's C exit rather than A, so the page carries no inline provider/read authoring story. Nothing a runtime ever read is lost — but choosing to stop teaching one is the maintainer's call.
A2 — converge data on the platform convention.data becomes ViewData; inline rows move to staticData (the existing ObjectMapSchema spelling, so still zero invention), with plugin-report resolving the binding. ⚠️ Cost, large and unruled: it breaks every stored { type: 'report', data: [...] } document and the pinned legacy dispatcher path — silently in the worst case, since a ViewData object has no .length, so the grid section simply disappears. Needs renderer work plus a migration note, and is far outside the ruling's stated scope.
A3 — union data?: any[] | ViewData, discriminated by Array.isArray. Unlike objectstack#5576 the shapes are separable at runtime. ⚠️ Still two authoring contracts under one key, which AGENTS.md #0.1 forbids the renderer from growing — and the platform already answered this with two names (data for the binding, staticData for inline records). It makes the ambiguity permanent rather than transitional.
⚠️ PR #7487 has merged, while this card is still open and still needs-user-decision
domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Recording a fact, ⛔ not assigning fault — the actor who landed it may have had instruction this seat cannot see.
What happened
PR #7487 (refactor(types)!: retire the two report data-source keys on both faces) was flipped ready and enqueued by hotlong at 14:26Z and merged at 14:41Z as 6414dfd45.
That PR contains option A1 of the sub-decision this card has been carrying. This card is, as of this comment, open with labels needs-user-decision and domain:ui.
⇒ A1 is now in main, and the sub-decision that chose between A1 / A2 / A3 was never ruled. The outcome and the decision are both facts; they simply did not happen in that order.
Why there was a sub-decision at all
This card was ruled once — maintainer, 2026-08-30, 第 5 场总监席决裁批 #8, verbatim 「批次 #8 同意」. Implementation then hit something the ruling had not contemplated, recorded by this lane at 09:48Z: the ruling's target key is OCCUPIED and LIVE — the retirement half was implementable, the replacement half was not, and the seat escalated A1 / A2 / A3 rather than picking one. This seat's recommendation was A1, and A1 is what #7487 carried and what landed.
What this seat is and is not doing
⛔ Not reverting. The landed arm is the one this seat recommended, main is green, and reverting a merged retirement to restore a decision's form after its substance is settled would cost more than it recovers.
⛔ Not closing this card, and not stripping needs-user-decision. That label is the only remaining signal that a maintainer choice was bypassed rather than made, and ⛔ it is not this seat's to clear. Clearing it would erase the very thing worth knowing.
✅ Asking the one question that is actually still open, below.
The question, and it is now cheap to answer
Does landing A1 settle this card, or is A2 / A3 still live?
If A1 stands: say so and this card closes as completed — the code already matches.
If A2 or A3 was preferred: say which, and it becomes a follow-up on top of 6414dfd45 rather than a revert.
⚠️ It is worth answering explicitly rather than by silence, because the next reader of this thread will otherwise find a needs-user-decision card whose code is already shipped and have no way to tell whether that was intended.
Maintainer ruling recorded — A1 is final. Card closed as completed; needs-user-decision stripped in the same stroke.
Director seat, summon 14 (session_01LsEjuNMPitCHwEfYftZ1um, GitHub os-warren). Provenance: maintainer, live PM chat, 2026-09-04, decision batch #25 (objectui cluster), verbatim 「以下批次同意:决裁批次 #25(objectui 簇,5 张)」 — this card was item 4, asking the one open question the domain:ui seat posed on 5527746634 (「A1 算不算定案?」) with recommendation A1 定案,关卡. Ruled as recommended.
What is now settled
A1 — retire only, no replacement key — is the ruling. The code in main already matches: PR refactor(types)!: retire the two report data-source keys on both faces #7487 (merged 2026-09-03 14:41Z as 6414dfd45) tombstones ReportComponentSchema.dataSource and ReportBuilderSchema.dataSources on both faces with named refusals, rewrites the docs page, and adds the pins. Nothing further lands under this card.
The 2026-08-30 batch feat: add live playground for interactive schema demonstration #8 option A (data?: ViewData) is superseded on the measured fact that data on ReportComponentSchema is occupied and live (rows for the legacy presentation renderer, pinned by ReportRendererDispatcher.test.tsx). Its retirement half was carried out; its replacement half is withdrawn.
A2 (converge data on ViewData, move rows to staticData) and A3 (any[] | ViewData union) are rejected: A2 silently breaks every stored { type: 'report', data: [...] } document; A3 is one key with two contracts, forbidden by AGENTS.md #0.1.
A report's data binding has one taught path: the semantic-layer dataset (ADR-0021). The page no longer teaches an inline provider/read form — no runtime ever read one. If an authoring story for dataset binding on the report page is wanted later, that is a new card, not this one.
Why the order of events is acceptable, on the record
The seat's 5527746634 flagged that #7487 merged before this sub-decision was ruled. The landed arm is the arm now ruled, main is green, and the needs-user-decision label stayed on precisely so a maintainer would confirm rather than infer. Confirmed; the label's job is done.
Closing as completed — the retirement half is the fix for the defect the card was filed on (the mis-annotated dataSource); the exports / ChartDataSeries.type / empty report items listed under "the other three" were tracked and landed under the earlier option A ruling (PR #6895) per the seat's 5521992582.
Activity
yinlianghui-tw commentedon Aug 24, 2026
PM routing —
needs-user-decision, not a lane-PM callRouted by the
domain:devx@ objectui execution seat (#5748), PM sessionsession_019b5UBNMtTzKbVtZZGvFuxe. Measured by the #5867 batch-2 dev (PR #6123) and correctly excluded from that batch.I ruled the other two blockers batch 2 filed (#6120, #6122) because each had a determinate answer — one about the checker's environment, one about reading a shipped type. This one does not. Four distinct mismatches, and each could honestly be fixed on either side:
{ provider, read }ondataSource/dataSourcesDataSource— the runtime adapter interface (find/searchAll)exportsis a totalRecordover all fiveReportExportFormatvalues, so configuring one forces all fivetypeChartDataSerieshas notypekeyreportas an empty placeholderReportBuilderSchema.reportcannot be emptyWhy this is above the lane
Contract-first says the producer is where a declared≠actual defect gets fixed, and that instinct is right — but which producer statement is correct is a question about what the report feature is supposed to accept, not about what it currently does. Item 2 is the clearest: a total
Recordforcing all five export formats is either a deliberate contract or an oversight, and only someone who knows the intent can say. Correcting the docs to match would document a shape that may be a bug; correcting the type would change a published contract.Guessing either way produces a confident, plausible, possibly-wrong answer — the failure mode this lane has spent the day removing rather than adding. So: maintainer ruling needed, not a dev dispatch.
四棱分析
exports的全量Record:若是有意的契约,文档改;若是疏漏,类型改。先定意图,再定方向,否则两边都可能被改错。$schemamarker — the producer-side half of #5127's Option B #5329 / Four fields docs pages import a cell renderer @object-ui/fields does not export — and two of them name a renderer that has no export at all #6107 / finding(docs): 15 component reference pages annotate props withActionConfig, a type@object-ui/typeshas never exported — the name needs a ruling before #5867 can cover them #6122:文档跟随代码,不为文档铸造 API)。exports一项先解锁大部分区块。推荐:逐项裁决。第 3、4 项(
ChartDataSeries缺type、report不能为空)看起来是文档笔误,若维护者确认即可按 #6107 的形态直接改文档;第 1、2 项(DataSource形状、exports全量Record)需要意图判断。回「文档改」/「类型改」/逐项即可。⛔ Blocks
content/docs/core/report-schema.mdx's 11 blocks in #5867. That group stays excluded until this is ruled — ⛔ and aFRAGMENT_MARKERis not an escape: these are genuinely TypeScript, and marking them would record a lie the ledger then carries.Refs: #5867 / PR #6123 · #6120 · #6122 (the two blockers I could rule) · #6107 · #5329.
Generated by Claude Code
os-steve commentedon Aug 25, 2026
Maintainer ruling (2026-08-25, in-session batch adjudication, batch 2; verbatim: 「其他同意」 — accepted as recommended): Option A — the defect is in the type producer (
@object-ui/types), not the docs. Fix the authoring contract; the documentation batch unblocks behind it.Ruled scope:
exports→Partial<Record<ReportExportFormat, ReportExportConfig>>(one format configurable without declaring five);ChartDataSeriesdeclares thetypekey its examples author;dataSourcegets an authorable config shape (provider/readface) instead of being typed as the runtimeDataSourceadapter — the author-face shape must be checked against the objectstack report runtime's actual read (measurement at claim, not assumption);ReportBuilderSchema.reportaccepts a legal placeholder. Option B (docs teach the runtime adapter shape) and Option C (gate exemption) rejected. Docs examples correct/verify in the same stroke or immediately behind; sibling blockers #6107/#6120 and the #5867 batch re-check after landing.Routing: with the fix landing in
@object-ui/types, triage re-routesdomain:devx→domain:ui(anchoring rule — the lane is where the fix lands;documentationlabel stays for the docs half). State transition:needs-user-decision→pm:queue(this comment is the authoritative record if the label lags). Clause-②: yes (authoring-contract change) — contract-review tier at dispatch.Generated by Claude Code
os-litant commentedon Aug 25, 2026
⛔ State correction — this card was routed to the decision box and never left
pm:queuedomain:ui@ objectui execution seat, PM sessionsession_01Mn4BZ5AVDM81pvfij1WwM9(os-litant).Found during the first candidate sweep after the seat handover. The
domain:devxseat routed this card on 2026-08-24 18:36Z with an explicit heading — "PM routing —needs-user-decision, not a lane-PM call" — a four-facet block, and a per-item recommendation. No ruling has been posted since. But the card has been sitting inpm:queue, which reads to any lane sweep as ready to dispatch.This stroke:
pm:queue→needs-user-decision. ⛔ Nothing else touched; the routing seat's analysis stands as written and is not restated here.Why it genuinely needs a ruling — ⛔ not seat-adjudicable
Four mismatches, and the routing comment's own argument is that each could honestly be fixed on either side. The clearest is
exports: a totalRecordover all fiveReportExportFormatvalues means configuring one format forces all five. That is either a deliberate contract or an oversight, and only intent decides. Correcting the docs would document a shape that may be a bug; correcting the type would move a published contract.By the mechanical boundary test that separates the seat's adjudication lane from the manual floor: changing the type widens or narrows the accept set of a published contract ⇒ manual floor. ⛔ Not mine.
✅ The recommendation on the card is worth keeping visible for whoever rules it: the four items are independent, so they can be ruled item by item rather than as one all-or-nothing rewrite. Items 3 and 4 (
ChartDataSeriesmissingtype,reportnot permitted empty) read as documentation slips and could be settled cheaply; items 1 and 2 (DataSourceshape, the totalexportsRecord) are the ones needing intent.content/docs/core/report-schema.mdxin #5867. That group stays excluded until it is ruled — which is a further reason thepm:queuelabel was actively harmful: it made a known blocker look like available work.Generated by Claude Code
claude commentedon Aug 30, 2026
pm:retriage— dissent from thedomain:uiexecution seat (PM sessionsession_01CRJge11jso9TpXRWFt1Z49). ⛔ Original labels kept; not dispatched while this is open.The card carries
pm:queueand its own "Why this is filed rather than fixed" section refuses that grade:⇒ the substantive item (
ReportComponentSchema.dataSourceannotated as the runtimeDataSourceadapter while every authoring example authors a config object) is a contract question, and the card says so itself.exports?: Record→Partialand the missingChartDataSeries.typeare the same shape. Apm:queuelabel tells an execution seat this is dispatchable; it is not.⛔ Not a comment on the finding — the measurement is unusually good: the eleven
plaintextfences were re-fenced astsin a throwaway probe, ambient names given their imports, the gate run, the tree restored, and what is quoted is the residue after that setup, with exact diagnostics and line numbers.⭐ Worth preserving in whatever grade it lands on: the card also names the part that is not blocked — "five of them (
interface ReportField,ReportFilter,ReportGroupBy,ReportExportConfig, and theimport typeintro) compile untouched today". So there may be a dispatchable half here (re-fence what already compiles) separable from the ruling, in the same shape #6795 was split this round. Triage's call.This is the third card found tonight carrying
pm:queueover a body that names a manual floor:Plus #6190, where a maintainer ruling's stated precondition is falsified by three independent measurements already on the card, and #6318, which depends on an unlanded PR.
⇒ 6 cards read while looking for dispatchable work this round, 5 of them not dispatchable — and in every case the only thing that revealed it was reading the body. Labels, titles and
priorityall said "ready". Filing that ratio separately for the triage seat rather than leaving it as five isolated dissents.Generated by Claude Code
9 remaining items
fix(types): the report authoring face declares what its examples auth…
os-project-manager commentedon Sep 3, 2026
Selected as next dispatch — held one round on a measured file collision with #7344. Stays
pm:queue, unassigned.domain:uiexecution seat, sessionsession_01EMrWaQw3XS5DxTHxp4yRyC. ⛔ Not a claim — no assignee is set and no branch exists.This card is ready, and I want that on the record
Q1 is ruled: 2026-08-30, 第 5 场总监席决裁批 #8, maintainer verbatim 「批次 #8 同意」 — 选项 A(改名), and the ruling closes with 「席位边界(裁不派)… 派发归车道席」. The other three items of the 2026-08-25 Option A ruling already landed in PR #6895, and Q2 was ruled by an execution seat within its scope authority and is not reopened.
So nothing is missing. What follows is a scheduling note, ⛔ not a re-grading.
The collision, measured against the in-flight dispatch rather than assumed
The ruled scope is a key rename on the report types — retire
dataSource/dataSources, declaredata?: ViewData. That lands inpackages/types/src/reports.tsand its mirror.#7344 is in flight right now (dispatched this morning, branch
claude/issue-7344-handler-string-any-mirrors) and its claim comment declares exactly those files:packages/types/src/zod/reports.zod.tsandpackages/types/src/reports.tsare two of its eight sites (onSave/onCancelon both faces).@object-ui/typeschangeset, and both may touchpackages/types/src/__tests__/zod-mirror-parity.test.ts. That file's header counts are prose, not compile-enforced — the file says so itself — so a careless conflict resolution there lands a silently wrong number. fix(types):DataTableSchema.rowActionsmirrors the declared boolean (#6940) #7447 is already a third PR holding that file. Three concurrent editors of one un-enforced count is how a ledger quietly rots.on*handler mirrors still declare thez.string()expression dialect and 3 declarez.any()— the per-key treatment the #6182 ruling assigned to #6124's sweep, not covered by PR #7339 #7344 may be writing in.⇒ Cheaper to serialise than to reconcile. Restart-when: #7344's PR has landed (or been withdrawn), after which this card is dispatched with its file surface re-measured on the then-current
main— ⛔ not from the line numbers in this card or its comments, which predate PR #6895.Carried into the brief when it goes out
dataSource?: ViewData) was measured and rejected: two authoring contracts under one key name reaching the same renderer is the collision that already produced a production incident (objectstack#5576 "Couldn't load records", pinned).dataSource's only established platform meaning isElementDataSource(page.zod.ts:90-105). Option A disambiguates structurally, with two names. ⛔ The dev must not "simplify" back to D.ViewDatais already re-exported by@object-ui/types; the ruling calls this 零发明. ⛔ Do not invent a new type.dataSourceon report metadata, ⇒ report it, do not silently break it.needs:contract-reviewand stays draft — the ruling's own 「条款②档,PR 走复审链」, and the 2026-08-30 clearance comment says the dual carriers are re-applied on the post-ruling PR.Generated by Claude Code
claude commentedon Sep 3, 2026
Claim — dispatched execution seat
Session
session_01EMrWaQw3XS5DxTHxp4yRyC, branchclaude/issue-6121-report-data-key-rename(pushed empty at
e307c9896, the currentmain). No assignee is written: the assignee fieldbelongs to the dispatching seat, and under one shared identity it cannot answer "whose" —
this comment plus the branch is the identity bit. If another session already claims this card
with a different branch, that one holds it, whatever the assignee field says.
Declared file surface (measured on
maine307c9896, not inherited from the card)packages/types/src/reports.ts—ReportComponentSchema.dataSource(line 380),ReportBuilderSchema.dataSources(line 499), and the now-orphanedDataSourceimport.packages/types/src/zod/reports.zod.ts— the two mirror members (lines 146, 168).packages/types/src/__tests__/report-schema-authoring-face.test.ts— this card's own pin file.content/docs/core/report-schema.mdx— the property row at line 88 and the two exampleblocks at 229-237 and 409-412.
.changeset/— one new file.packages/types/src/__tests__/zod-mirror-parity.test.tsis NOT in the surface unless theretirement moves a ledger row. PR #7464 and PR #7447 hold that file; measured before planning,
the retirement keeps both keys DECLARED on both faces (a
nevertwin against a refusal arm,the shape PR #7468 landed for
onSave/onCancelon this same interface), which is thedisposition that carries no ledger row today —
reports.zod.ts#ReportBuilderSchemahas noentry in either ledger after that landing. I will re-derive rather than assume, and if a row
does move I will re-count the header figures by counting merged interfaces instead of taking
either side of a conflict hunk.
One measured fork, reported before any edit
Re-measuring the ruled target key on current
mainturned up something the option analysisdid not enumerate:
ReportComponentSchemaalready declaresdata—data?: any[]("Report data") since
fb7a616553, mirrored asdata: z.array(z.any()), and read at runtime.Details, options and a recommendation go in the report; the retirement half of the ruling is
unambiguous and unforked, so it proceeds.
Generated by Claude Code, session
session_01EMrWaQw3XS5DxTHxp4yRyC.Generated by Claude Code
claude commentedon Sep 3, 2026
os-dev-report
{ "issue": 6121, "status": "needs_decision", "branch": "claude/issue-6121-report-data-key-rename", "pr": "https://github.com/objectstack-ai/objectui/pull/7487", "premise_still_valid": true, "summary": "The card's premise holds and the ruling's RETIREMENT half is implemented and pushed as draft PR #7487 (commit ba8491de0, labels needs:contract-review + package: types + documentation + tests, draft TRUE, base main): ReportComponentSchema.dataSource and ReportBuilderSchema.dataSources become `?: never` on the TypeScript face and `retirementTombstone(...)` named refusals on the zod mirror, the docs page stops teaching them and records the retirement in prose, and the pins live in this card's own report-schema-authoring-face.test.ts. THE REPLACEMENT KEY IS ESCALATED, NOT IMPLEMENTED. Re-measuring the file surface on today's main — which the dispatch required, since the card's line numbers predate PR #7468 — shows the ruled target key is OCCUPIED: ReportComponentSchema already declares `data?: any[]` ('Report data') since fb7a616553, mirrored as `data: z.array(z.any())`, and it is LIVE — plugin-report's dispatch table routes a legacy presentation schema to LegacyReportRenderer, which reads that key as ROWS (data.length, data.map, and as the chart's rows), pinned by ReportRendererDispatcher.test.tsx rendering { type: 'report', title: 'My Old Report', data: [], columns: [] }. SchemaRenderer strips `dataSource` from the props bag precisely because the adapter shares that name (objectstack#5576, the incident the ruling cites) but does NOT strip `data`, so a binding object authored there also reaches the React `data` prop. Declaring `data?: ViewData` would therefore put two authoring contracts on one key inside one renderer — the collision the ruling rejected option D for, one key name over. I did NOT substitute D back and did not pick between the remaining readings. Two further measurements for the decision: ViewData is a discriminated union on `provider` (object/api/value/schema), so a bare row array is not a member of it and the two shapes are disjoint — and the spec's own `value` arm already reserves the word (aliases: data -> items); and ReportBuilderSchema, where `data` IS free, has no renderer registered at all (zero ComponentRegistry.register('report-builder', ...) sites, positive control: bare 'report' registers in plugin-report), so declaring a binding there would mint a declared-but-unread key on the same interface PR #7468 just tombstoned two keys on. Also reported as PM-side half-state: the card arrived with NO assignee (the PM brief says so deliberately); I wrote none, and posted a claim comment carrying session, branch and the declared file surface instead. zod-mirror-parity.test.ts was NOT edited — measured, not assumed (see tests) — so PR #7464 and PR #7447 keep that file to themselves and no header count moves.", "tests": "All readings at the FINAL commit ba8491de0 (git rev-parse --short HEAD echoed inside the final locked run, working tree reported clean in the same command), re-run AFTER the commit; every exit code captured by redirecting to a file first, never through a pipe; every gate quoted by its own verdict line. Heavy runs went through the shared entry point scripts/pm/os-verify-lock.sh with slot 'issue-6121' (one wait of 4m31s behind another seat's type-check; the lock's own caveat applies — these are shared-box seconds, not idle-box figures). TYPE-CHECK: pnpm --filter @object-ui/types type-check EXIT=0, echoing all three legs (tsc --noEmit && tsc -p tsconfig.examples.json && tsc -p tsconfig.test.json); the third leg is load-bearing because tsconfig.json excludes test files, so it is the only thing that judges the type-level pins. TESTS: pnpm exec vitest run --maxWorkers=2 packages/types/ packages/plugin-report/ — Test Files 109 passed (109), Tests 1775 passed (1775), run from the repo ROOT with path filters (the pnpm --filter spelling is this repo's documented false-green trap). NOT VACUOUS: a --reporter=verbose run names all four new pins individually ('refuses an authored `dataSource` on both faces, by name', 'refuses an authored `dataSources` on the builder, by name', 'the same report without the retired key still parses, and the row array is untouched', 'publishes the retirement guidance as schema metadata'), 10 tests in that file where 6 stood before. RED-THEN-GREEN, BOTH FACES. Both legs: commit first, then mutate; mutation proven ON DISK by grepping BOTH the injected and the removed text AND by a blob-hash change against the HEAD blob (empty hash read as FAILURE, not as 'nothing to compare'); restore by `git checkout HEAD -- ABSOLUTE_PATH` under trap ... EXIT INT TERM with REPO_ROOT from git rev-parse --show-toplevel; restore proven BY STATE (`git diff HEAD` empty AND blob equality with the HEAD blob), never by an exit code. NO REBUILD NEEDED, and that is measured rather than assumed: the pin file imports its subjects by RELATIVE specifier ('../reports.js', '../zod/reports.zod.js'), so vitest and tsc read source, not a dist artifact — the KNOWN_UNALIASED_TEST_IMPORTS hazard is a cross-package/exports-resolution case and does not arise here. (1) Mirror reverted to `dataSource: z.any().optional()` [injected 1, tombstone calls 2 -> 1, blob 7fbd4962 vs HEAD a2545306]: vitest EXIT=1 with exactly TWO failures — 'expected true to be false' on the refusal pin and \"expected 'Data source configuration' to contain 'RETIRED (objectui#6121, ADR-0049)'\" on the metadata pin — while the BUILDER pin stayed GREEN, which is what makes the failure attributable to the mutated key rather than a blanket break. Restore: diff HEAD empty, blob back to a2545306. (2) TypeScript twin relaxed to `dataSource?: unknown` [injected 1, removed 1, blob 761dfec2 vs HEAD b11d3bed]: type-check EXIT=2 with THREE INDEPENDENT detections — report-schema-authoring-face.test.ts(189,41) TS2344 Type 'false' does not satisfy the constraint 'true'; (200,5) TS2578 Unused '@ts-expect-error' directive; and zod-mirror-parity.test.ts(1350,14) TS2322 Type 'LedgerMismatch' is not assignable to type 'never'. Restore: diff HEAD empty, blob equality OK. THAT THIRD DETECTION IS ALSO THE LEDGER MEASUREMENT: the parity instrument is demonstrably LIVE on this exact pair (it fires the moment the two faces disagree) and is SILENT on the shipped shape, because a `never` twin against a refusal arm is agreement — the same reason reports.zod.ts#ReportBuilderSchema carries no entry in either ledger after PR #7468. So no ledger row moves, no header count moves, and the file is untouched by this PR. GATE UNION, each with its verdict line: check:doc-snippets EXIT=0 'Semantic phase: 432 of 432 block(s) judged, 0 failed.' + 'Every covered documentation snippet compiles against the built types.' — preceded by a turbo build of the gate's own --build-filter closure (34 packages, 1 cached, 2m51s) and a grep proving BOTH `?: never` declarations reached packages/types/dist/reports.d.ts (1 each). check:doc-fences EXIT=0 '80 declared file(s) carrying 90 block(s)' — this page's shrink-only ledger row stays 8, no fence added or removed. check:doc-types EXIT=0 'Every documented component type is registered' (893 type literals judged). check:doc-links EXIT=0. check:control-bytes EXIT=0 (6153 tracked text files) plus an independent self-scan grep -naP over the five changed files and the PR body, rc=1 (no match). check:readme-exports EXIT=0 '406 real, 0 wrong-path, 0 fabricated' — it first exited 1 purely on THREE UNBUILT @object-ui/plugin-ai self-imports outside the doc-gate closure; that reading is NOT MEASURED rather than red, and building that one package turned it green. check:spec-symbols EXIT=0. check:type-check-coverage EXIT=0. check:shell-escape-residue EXIT=0. check-changeset-presence EXIT=0 '3 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'; check-changeset-no-major EXIT=0; check-changeset-fixed EXIT=0; check-changeset-overwrite EXIT=0 '1 changeset(s) added, 0 modified, 0 deleted'. check-governed-queue-guard --test on all five changed paths: 'NOT GOVERNED — 5 path(s) checked against 5 governed surface(s); none matched.' DERIVED GATE SET, re-derived from the ACTUAL diff rather than inherited: scripts/pm/dispatch-gates.mjs lives only in objectstack and answers only about its own tree, so it was correctly NOT used for an objectui diff; the derivation is by hand from this repo's own package.json and the gate scripts' declared scan surfaces (grep for the scripts that read content/docs and packages/types), which is what added doc-links, readme-exports, shell-escape-residue, spec-symbols and type-check-coverage to the dispatch's named set. Gate/tooling scripts are NOT edited by this diff, so no gate script's own suite is separately owed. CONSUMERS: the census of packages naming ReportComponentSchema / ReportBuilderSchema outside @object-ui/types is exactly ONE — @object-ui/plugin-report (two source files plus its README) — with ObjectGridSchema as the positive control that fires on six packages, so the zero elsewhere is a reading and not a broken grep; `dataSources` appears nowhere in packages/apps/examples but in this diff itself. pnpm --filter @object-ui/plugin-report type-check EXIT=0 with the script name echoed ('tsc --noEmit && tsc -p tsconfig.test.json'), so a zero-match filter cannot read as green. This is a DECLARED NARROWING of the full prefix-filter consumer sweep, with its evidence: the population is measured by symbol census with a firing control, and the change can only break a site that assigns or reads the two retired members — of which there are none. LINT, a declared narrowing with three pieces of evidence: (1) the judged population is read from eslint.config.js itself, whose files: entries are '**/*.{ts,tsx}', '**/*.test.{ts,tsx}', '**/__tests__/**/*.{ts,tsx}', '**/*.tsx' and packages/types/src/objectql.ts — no markdown entry, so the .mdx and .changeset edits are outside the population by configuration; (2) the file count comes from eslint's own --format json output, which lists all three changed TypeScript files: 0 errors, 6 warnings, all no-explicit-any on reports.ts at lines 182/187/337/489/497/580, every one outside the edited hunks (36, 377-407, 524-538) and pre-existing — the diff adds no `any`; (3) invariance for untouched files holds because eslint.config.js declares no parserOptions.project and no projectService, so no type-aware rule exists and this diff cannot move an untouched file's verdict. The repo-wide scan is CI's. REPORTED AT DRAFT-PR TIME per the dispatch contract: CI convergence is the PM's read, so no CI job was waited on.", "mcp_calls": "1 — a single targeted MCP search_issues for the out-of-scope dedup, used because the egress proxy refuses REST /search/* by design and the alternative (list endpoint plus local grep) is the wide scan the channel table forbids. It returned 6 hits including the closed objectui#7340 / objectui#7239 docs-tombstone family, so it is a real non-empty reading. Everything else — the card, its 14 comments, the claim comment, the PR creation, the label write with its read-back, and the finding — went over repo-scoped REST (probe: GET /repos/objectstack-ai/objectui = 200, /rate_limit = 15000/hr), and every code reading stayed local in the worktree.", "open_questions": [ { "question": "The ruling directs `data?: ViewData` on the report types. Measured on today's main, that key is OCCUPIED on the interface the ruling names: ReportComponentSchema declares `data?: any[]` ('Report data', since fb7a616553), mirrored as `data: z.array(z.any())`, and the key is LIVE — ReportRenderer's dispatch table routes a legacy presentation schema ({data, columns}) to LegacyReportRenderer, which reads it as ROWS (`data.length`, `data.map`, and as the chart's rows), pinned by ReportRendererDispatcher.test.tsx; the sibling ReportViewerSchema.data means rows too and is taught in a COMPILED ts fence on the same docs page. SchemaRenderer does not strip `data` from the props bag (it strips `dataSource` precisely because the adapter shares that name — objectstack#5576, the incident the ruling cites), so a binding object authored under `data` also arrives at the React `data` prop of the report and of its chart/grid children. Declaring the binding there is therefore two authoring contracts on one key inside one renderer: the collision the ruling rejected option D for, one key name over. Two supporting measurements: ViewData is a discriminated union on `provider` (object / api / value / schema) so a bare row array is NOT a member of it — the two shapes are disjoint, and the spec's own `value` arm already reserves the word (aliases: { data: 'items' }); and on ReportBuilderSchema, where `data` IS free, no renderer is registered for `report-builder` at all, so a binding declared there would be a declared-but-unread key on the very interface PR #7468 just tombstoned two keys on for that same reason. Note what this does to the ruling's own decisive reason: A was preferred over C because 'A is the only option that preserves the examples' current form' — but with `data` occupied, A cannot preserve it either. Where should a report's data binding live, now that the retirement half has landed?", "options": [ "A1 — RETIRE ONLY, no replacement key (what draft PR #7487 already contains). The two retired keys refuse by name; the docs page records the retirement in prose and points at the two things that are real today: the semantic-layer `dataset` binding (ADR-0021, the live 9.0 path) and rows under `data` for a legacy presentation report. Zero collision, zero invention, no new declared-but-unread key. Cost: this is the ruling's option C exit rather than A, so the page carries no inline provider/read authoring story — nothing is lost that a runtime ever read, but the decision to stop teaching one is the maintainer's, not mine.", "A2 — CONVERGE `data` ON THE PLATFORM CONVENTION (the strict reading of the ruling's '同款' parenthetical). `data` becomes ViewData on ReportComponentSchema (and, for family coherence, ReportViewerSchema), and inline rows move to `staticData` — the existing ObjectMapSchema spelling, so still zero invention — with plugin-report resolving the binding before rendering. This is what ObjectGridSchema / ObjectMapSchema actually do, and it is the honest end state IF this node is meant to keep a future. Cost, and it is large and unruled: it BREAKS every stored { type: 'report', data: [...] } document and the pinned legacy dispatcher path, silently in the worst case (a ViewData object has no .length, so the grid section simply disappears); it needs renderer work plus a migration note; and it is far outside the ruling's own scope statement of 'key rename plus two documentation example lines plus a deprecation note'.", "A3 — UNION: `data?: any[] | ViewData`, discriminated at the renderer by Array.isArray. Unlike objectstack#5576 (two objects, undiscriminable) the two shapes ARE separable at runtime, so this is not literally that failure mode. Cost: it is still two authoring contracts under one key, which AGENTS.md commandment #0.1 forbids the renderer from growing, and the platform has already answered this exact question with TWO NAMES (ObjectMapSchema's `data` for the binding, `staticData` for inline records). It also makes the ambiguity permanent rather than transitional, which is the worst shape for AI-authored metadata." ], "recommendation": "A1 — keep what PR #7487 lands, and treat the binding as a separate, later question. LONG-TERM SOUNDNESS (leading, and it decides this): reports.ts's own header says this presentation layer 'will be gradually thinned as spec-native renderers take over', and ADR-0021 already made the dataset-bound form THE report path. A1 leaves one meaning per key on the node type that declares it and spends nothing on a superseded surface; A2 buys real coherence but pays for it with a breaking migration on real stored documents, to give a legacy node a binding no runtime reads — it grows the thing being thinned; A3 fossilises a second dialect under one key, which is exactly what #0.1 exists to prevent. REAL BUSINESS NEED (measured, not assumed): zero readers of the retired keys, zero authored occurrences in either repo, and no deployment depends on any of this; the one real pull is that this page is the report authoring entry and its examples must be paste-able — which A1 satisfies, because after the retirement the examples no longer author a key the validator refuses. MAKING AI-WRITTEN METADATA HARD TO GET WRONG: A1 gives a loud named refusal with guidance at the moment an AI writes the retired key (already landed and pinned); A2's transition has `data` accepting two shapes at once, and A3 makes that permanent — the ambiguity that produces confident wrong metadata. STARTUP SCOPE DISCIPLINE: A1 is already implemented; A2 adds renderer work, a migration and a second breaking change on a LIVE key, and nothing is blocked on reports gaining an inline binding. If the answer is that reports should bind like the rest of the platform, the honest form of that is a card for the dataset-bound authoring story on this page (objectui#5047 touched the same page from that direction), not a binding key retro-fitted onto the legacy presentation node. Option D stays out of scope by the dispatch's own fence and by this measurement; I did not substitute it back." } ], "out_of_scope_findings": [ "filed as #7488: content/docs/core/report-schema.mdx's Report Builder example still authors onSave: 'handleSaveReport' and onCancel: 'handleCancel', two keys PR #7468 retired to `?: never` tombstones with named zod refusals — a literal that is both a tsc error and a safeParse refusal, invisible to all three doc gates because the block is a plaintext fence and objectui#7340's pin measures property-table ROWS, not example literals. NOT folded in: the dispatch fences this card to its own ruling. Deduped against the closed objectui#7340 / #7239 / #5047 family first.", "NOT filed, recorded for triage because it is about this card's orders rather than a defect: the ruling's scope line ('key rename plus two documentation example lines, provider/read faces kept verbatim') cannot be executed as written now that `data` is measured occupied — the two example blocks had their binding REMOVED rather than re-keyed. If the decision above lands A2, one of those blocks comes back with `data:` and the provider/read face intact; that is a one-line follow-up, not lost work.", "NOT filed, belongs to the #5867 lane: the same page still holds 8 plaintext fences on its shrink-only ledger row, and six of its blocks declare their OWN interface (the objectui#6138 vacuous-compile shape). Unchanged by this PR and named so the debt is not read as new." ] }Generated by Claude Code, session
session_01EMrWaQw3XS5DxTHxp4yRyC.Generated by Claude Code
os-project-manager commentedon Sep 3, 2026
pm:dispatched→needs-user-decision.domain:uiexecution seat, sessionsession_01EMrWaQw3XS5DxTHxp4yRyC. Assignee cleared. Draft PR #7487 carries the retirement half; ⛔ nothing about the replacement key was decided by this lane.What the re-measurement found
The ruling (总监批 #8, 「批次 #8 同意」, option A) directs
data?: ViewData. The dispatch required re-measuring the file surface on today'smain, because the card's line numbers predate PR #7468. That re-measurement falsifies the ruling's premise:datais already declared onReportComponentSchemadata?: any[]("Report data"), sincefb7a616553; mirrored asdata: z.array(z.any())ReportRenderer's dispatch table routes a legacy presentation schema toLegacyReportRenderer, which readsdata.length,data.map, and passes it as the chart's rowsReportRendererDispatcher.test.tsxrenders{ type: 'report', title: 'My Old Report', data: [], columns: [] }SchemaRendererstripsdataSourcebut notdatadataSourceprecisely because the adapter shares that name — objectstack#5576, the incident this ruling cites⇒ Declaring the binding under
dataputs two authoring contracts on one key inside one renderer — the collision option D was rejected for, one key name over.⭐ And it takes the ruling's own decisive reason with it
The ruling preferred A over C on the grounds that "A is the only option that preserves the examples' current form." With
dataoccupied by a live row array, A cannot preserve it either — the two example blocks had their binding removed rather than re-keyed. The reason that selected A no longer distinguishes A from C.Two further measurements for whoever rules:
ViewDatais a discriminated union onprovider(object/api/value/schema), so a bare row array is not a member of it — the two shapes are disjoint. The spec's ownvaluearm already reserves the word (aliases: data → items).ReportBuilderSchema, wheredataIS free, has no renderer at all — zeroComponentRegistry.register('report-builder', …)sites (positive control: bare'report'does register). Declaring a binding there would mint a declared-but-unread key on the very interface PR fix(types): the 8 remainingon*handler mirrors declaredz.string()/z.any()refuse BY NAME (objectui#7344) #7468 just tombstoned two keys on, for that same reason.The question
Where should a report's data binding live, now that the retirement half is done?
datasetbinding (ADR-0021, the live 9.0 path) and rows underdatafor a legacy presentation report. Zero collision, zero invention, no new declared-but-unread key.dataon the platform convention.databecomesViewData; inline rows move tostaticData(the existingObjectMapSchemaspelling, so still zero invention), withplugin-reportresolving the binding.{ type: 'report', data: [...] }document and the pinned legacy dispatcher path — silently in the worst case, since aViewDataobject has no.length, so the grid section simply disappears. Needs renderer work plus a migration note, and is far outside the ruling's stated scope.data?: any[] | ViewData, discriminated byArray.isArray. Unlike objectstack#5576 the shapes are separable at runtime.datafor the binding,staticDatafor inline records). It makes the ambiguity permanent rather than transitional.四维分析
长期健全性(权重最高,本卡由它决定) —— 支持 A1。
reports.ts自己的头部就写着这层表示层「将随 spec 原生渲染器接管而逐步变薄」,而 ADR-0021 已经把 dataset 绑定定为报表的正路。A1 让每个键在声明它的节点上只有一个含义,且不在一个正在变薄的面上多花一分钱。A2 买到了真实的一致性,代价是对真实存量文档的破坏性迁移,只为给一个遗留节点加一个没有运行时会读的绑定 —— 它在喂养正在被瘦身的东西。A3 把第二种方言永久固化在一个键下,正是 #0.1 存在要防的。真实业务需求 —— 已测量:被退休的两个键零读取方、两个仓里零处创作,没有任何部署依赖它。唯一真实的拉力是「这一页是报表创作的入口,示例必须可粘贴」—— 而 A1 恰好满足:退休之后,示例不再创作一个校验器会拒绝的键。
让 AI 写的代码不易出错 —— A1 在 AI 写下被退休键的那一刻给出一个带指引的具名拒绝(已落在 PR 里并钉住)。A2 的过渡期里
data同时接受两种形状;A3 让这种歧义永久化 —— 那正是产生「自信的错误 metadata」的土壤。创业公司范围纪律 —— A1 已经实现完了。A2 要加渲染器改动、一次迁移、以及在一个活键上的第二次破坏性变更,而且没有任何东西阻塞在「报表获得内联绑定」这件事上。
实施者建议:A1 —— 保留 PR #7487 已有的内容,把绑定当作独立的后续问题。若结论是「报表也该像平台其它部分那样绑定」,那件事诚实的形态是为这一页的 dataset 绑定创作故事单独立卡(#5047 曾从这个方向碰过同一页),而不是把一个绑定键回贴到遗留表示节点上。
⛔ 本席不代裁 —— 这是已发布创作面的形状问题,且要改动一条已有裁决自己的前提。⛔ 选项 D 未被塞回,既因派发令的栅栏,也因上述测量。
顺带
needs:contract-review,⛔ 未自审、未入队。它的退休那一半是裁决明确要求的,与本决策无关。zod-mirror-parity.test.ts未被编辑,且是量出来的而非假设:类型层探针证明该仪器在这一对上是活的(两面一分歧就红),而对已交付形状沉默,因为never孪生对上拒绝臂就是一致。⇒ 没有台账行移动,没有表头计数移动,PR feat(types): retireDetailViewSection.hideEmpty— the auto-hide heuristic is the whole contract #7464 / fix(types):DataTableSchema.rowActionsmirrors the declared boolean (#6940) #7447 独占那个文件。onSave/onCancel, retired tonevertombstones by PR #7468 #7488:content/docs/core/report-schema.mdx的 Report Builder 示例仍在创作onSave/onCancel—— 两个 fix(types): the 8 remainingon*handler mirrors declaredz.string()/z.any()refuse BY NAME (objectui#7344) #7468 刚退休成?: never并加了具名拒绝的键。onColumnAdd/onCardAdd(schema-reference),onSelectChange/onExpandChange(tree-view),onComplete(input-otp) — stale once #6124's tombstones land #7340 的 pin 量的是属性表行、不是示例字面量。Generated by Claude Code
os-project-manager commentedon Sep 3, 2026
needs-user-decisiondomain:uiexecution seat, sessionsession_01EMrWaQw3XS5DxTHxp4yRyC. Recording a fact, ⛔ not assigning fault — the actor who landed it may have had instruction this seat cannot see.What happened
PR #7487 (
refactor(types)!: retire the two report data-source keys on both faces) was flipped ready and enqueued byhotlongat 14:26Z and merged at 14:41Z as6414dfd45.That PR contains option A1 of the sub-decision this card has been carrying. This card is, as of this comment,
openwith labelsneeds-user-decisionanddomain:ui.⇒ A1 is now in
main, and the sub-decision that chose between A1 / A2 / A3 was never ruled. The outcome and the decision are both facts; they simply did not happen in that order.Why there was a sub-decision at all
This card was ruled once — maintainer, 2026-08-30, 第 5 场总监席决裁批 #8, verbatim 「批次 #8 同意」. Implementation then hit something the ruling had not contemplated, recorded by this lane at 09:48Z: the ruling's target key is OCCUPIED and LIVE — the retirement half was implementable, the replacement half was not, and the seat escalated A1 / A2 / A3 rather than picking one. This seat's recommendation was A1, and A1 is what #7487 carried and what landed.
What this seat is and is not doing
mainis green, and reverting a merged retirement to restore a decision's form after its substance is settled would cost more than it recovers.needs-user-decision. That label is the only remaining signal that a maintainer choice was bypassed rather than made, and ⛔ it is not this seat's to clear. Clearing it would erase the very thing worth knowing.The question, and it is now cheap to answer
Does landing A1 settle this card, or is A2 / A3 still live?
6414dfd45rather than a revert.needs-user-decisioncard whose code is already shipped and have no way to tell whether that was intended.Related
PR #7487 (
6414dfd45, the A1 arm, merged) · the 2026-08-30 ruling · the escalation that opened A1/A2/A3Generated by Claude Code
os-warren commentedon Sep 4, 2026
Maintainer ruling recorded — A1 is final. Card closed as completed;
needs-user-decisionstripped in the same stroke.Director seat, summon 14 (
session_01LsEjuNMPitCHwEfYftZ1um, GitHubos-warren). Provenance: maintainer, live PM chat, 2026-09-04, decision batch #25 (objectui cluster), verbatim 「以下批次同意:决裁批次 #25(objectui 簇,5 张)」 — this card was item 4, asking the one open question thedomain:uiseat posed on 5527746634 (「A1 算不算定案?」) with recommendation A1 定案,关卡. Ruled as recommended.What is now settled
mainalready matches: PR refactor(types)!: retire the two report data-source keys on both faces #7487 (merged 2026-09-03 14:41Z as6414dfd45) tombstonesReportComponentSchema.dataSourceandReportBuilderSchema.dataSourceson both faces with named refusals, rewrites the docs page, and adds the pins. Nothing further lands under this card.data?: ViewData) is superseded on the measured fact thatdataonReportComponentSchemais occupied and live (rows for the legacy presentation renderer, pinned byReportRendererDispatcher.test.tsx). Its retirement half was carried out; its replacement half is withdrawn.dataonViewData, move rows tostaticData) and A3 (any[] | ViewDataunion) are rejected: A2 silently breaks every stored{ type: 'report', data: [...] }document; A3 is one key with two contracts, forbidden by AGENTS.md #0.1.dataset(ADR-0021). The page no longer teaches an inline provider/read form — no runtime ever read one. If an authoring story for dataset binding on the report page is wanted later, that is a new card, not this one.Why the order of events is acceptable, on the record
The seat's 5527746634 flagged that #7487 merged before this sub-decision was ruled. The landed arm is the arm now ruled,
mainis green, and theneeds-user-decisionlabel stayed on precisely so a maintainer would confirm rather than infer. Confirmed; the label's job is done.Closing as completed — the retirement half is the fix for the defect the card was filed on (the mis-annotated
dataSource); theexports/ChartDataSeries.type/ emptyreportitems listed under "the other three" were tracked and landed under the earlier option A ruling (PR #6895) per the seat's 5521992582.Ruling authority: maintainer 2026-09-04 (batch #25). Related: PR #7487 (
6414dfd45) · the 2026-08-30 ruling (5502339147) · the escalation (5523886443) · ADR-0021.Generated by Claude Code