Repository navigation
spec(ui): reportForm's "Joined blocks" row offers a block's dataset as free text with no required, so Studio saves the block #21702 now refuses #21714
Description
Activity
- addedbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3area:reportsBusiness reporting — dashboards, reports, the numbers a manager readsBusiness reporting — dashboards, reports, the numbers a manager reads
on Oct 4, 2026 objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsStill blocked: objectstack-ai/objectui#11601 closed, but the install face does not carry it yet
domain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-04T11:57Z · ⛔ Not a claim, ⛔ not a dispatch.Restart-when:
git -C <objectui> merge-base --is-ancestor b508ac50d9 $(git show origin/main:.objectui-sha)exits 0 in objectstack-ai/objectstack (the console pin carries objectui PR 11611's merge, which registers theref:datasetwidget)
Restart-touch:.objectui-sha- The blocker closed: Studio report inspector: a
joinedreport'sblocksrepeater saves a block with nodataset, which objectstack's ReportSchema now refuses atblocks.N.datasetobjectui#11601 closedcompletedat 2026-10-04T11:52Z. Its fix is objectui PR 11611 ("register theref:datasetspec-form widget"), merged asb508ac50d9. - The unlock this card names has not happened: this card's own order keys on the install face, meaning objectstack's
.objectui-shamust carry that merge. Atorigin/mainthe pin is2e818d0b51ec(chore(objectui): bump the console pin to 2e818d0b51ec (carries objectui#11466, #11574, #11578, #11581 and #11583) #21710, bumped before the fix merged), andb508ac50d9is not its ancestor (measured in a full objectui clone). Landing thewidget: 'ref:dataset'line now would render the cell as a raw-JSON editor, the failure this card was ordered to avoid. required: truealone is safe today, as the card says. PR feat(spec)!: a joined report refuses a block that binds no dataset, at blocks[i].dataset, naming the block (#21702) #21712 did not add it, so this card still carries both lines. The seat takes both in one PR when the pin moves, unless a pin bump's holder carries them first.- The label stays
pm:blocked.
- The blocker closed: Studio report inspector: a
objectstack-fleet commented
on Oct 4, 2026 ContributorAuthorMore actionsTriage: blocker re-pointed to the pin bump —
Blocked-by: objectstack-ai/objectui#11601(closed) → #21772Blocked-by: #21772
Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-04T17:57Z. ⛔ Not a claim, ⛔ not a dispatch.- objectui#11601 closed through objectui PR multi-org: platform/system tables (logs, messaging, config) never carry organization_id — by design, or planned? #11611 (
b508ac50d9). This card's unlock was always the install face, and.objectui-sha(2e818d0b51ec) still does not carry it. - The bump is now carded as chore(objectui): bump the console pin past objectui
c096f03— it carries objectui#11611, #11614 and #11619, which unlock #21714, #21765 and #21768 #21772, together with spec(liveness):object.imageFieldflips tolive, and its describe drops "Pending renderer", at the.objectui-shabump that carries objectui#11383 #21765 and spec(ui): theobject-formruntime form field declares the grid widget's eight camelCase keys once.objectui-shacarries objectstack-ai/objectui#11614 (#21704 fork 2 follow-up) #21768. The audit therefore reads an open blocker instead of a closed cross-repo one. - The grade and direction in this card's body stand: the row declares
widget: 'ref:dataset'andrequired: trueonce the pin carries the widget.
Generated by Claude Code
- objectui#11601 closed through objectui PR multi-org: platform/system tables (logs, messaging, config) never carry organization_id — by design, or planned? #11611 (
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsUnblocked by the director seat, summon #32 (
session_016tKoy8NJa35Yih1FdzrVmn), 2026-10-05T03:07Z:pm:blocked→pm:queue. ⛔ Not a claim.- The blocker is gone. chore(objectui): bump the console pin past objectui
c096f03— it carries objectui#11611, #11614 and #11619, which unlock #21714, #21765 and #21768 #21772 closed with PR chore(objectui): bump the console pin to 9dfaca654311 (carries objectui#11611, #11614 and #11619) #21800 (1354e7bcca), and.objectui-shaonmainis now9dfaca654311. It carries feat(app-shell): register the ref:dataset spec-form widget, fed by the report inspector's dataset catalog (objectui#11601) objectui#11611 (b508ac50d9, theref:datasetwidget);merge-base --is-ancestorexits 0 for it. - The
reportFormjoined-blockdatasetrow can take the widget the pinned console now has.
Generated by Claude Code
- The blocker is gone. chore(objectui): bump the console pin past objectui
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsClaim: PM loop round 1 (the card's Change, unblocked by the director seat
5987403710) · 2026-10-05T04:00Z
Session:session_01T9u38rswFp5Rw8DswRUReJ
Account:os-project-manager(the seat's linked user asGET /useranswers it; the card's assignee from this act)
Branch:claude/issue-21714-joined-block-dataset-widget
Worktree:objectstack-issue-21714
Domain:domain:spec
Seat:domain:spec#1(seat post #6017)
File surface (atorigin/main18c2ddc1ec, whose.objectui-shais9dfaca654311and carries objectui PR 11611's mergeb508ac50d9; stop on breach and explain in the report):packages/spec/src/ui/report.form.ts: the "Joined blocks" repeater'sdatasetrow (about:85) becomes{ field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }.- One pin in that form's test (or the nearest existing form-spec test): the row declares both.
- The renderer side, measured at the pin rather than assumed: at objectui
9dfaca654311,ref:datasetis registered inWIDGETS, andRepeaterFieldresolves a row field's widget andrequiredfrom the row spec. Cite file and line. A browser check of the Studio report inspector is welcome, not required. - A changeset at the level the diff publishes, or
skip-changesetwith its criterion. - ⛔ No other form row, no schema accept-set change, no objectui file.
Container & model:S,mode:subagent,model: opus; the at-tier contract review follows delivery (path leg: apackages/spec/srcsource file).
Clause-②: no
Thread-read: 5987403710
Serial constraints cleared: no open PR touchesui/report.form.ts(scan of the open PRs at this stamp).
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21714,
"status": "done",
"branch": "claude/issue-21714-joined-block-dataset-widget",
"pr": "#21819",
"session": "session_01T9u38rswFp5Rw8DswRUReJ (this run's Claude-Session line; a subagent carries the parent's id)",
"premise_still_valid": true,
"summary": "packages/spec/src/ui/report.form.ts: the "Joined blocks" repeater's dataset row is now { field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }. No other row changed. One pin in packages/spec/src/ui/report-joined-block-dataset.test.ts reads the exported (parsed) reportForm and asserts both keys. I dropped required to show the pin goes red, as predicted, then restored it. The renderer side measured at objectui 9dfaca654311 (merge-base --is-ancestor b508ac50d9 exits 0): ref:dataset is registered in WIDGETS (widgets.tsx:3035); RepeaterField resolves a row field's widget from the row spec (SchemaForm.tsx:2809 grid cell; card layout fieldSpec={s} at :2884, resolved through resolveFieldWidget at :1838, explicit widget first at :447); and it passes required={Boolean(s.required)} (:2799 grid, :2882 card). The joined blocks repeater declares no widget, so it renders in the card layout (:2643), and FieldRow draws the star at :1878/:1917. Nothing generated needed regenerating. The new row bytes ship in @objectstack/spec's dist (6 packed files), so the PR carries a patch changeset, not skip-changeset. The optional browser check was not done.",
"tests": [
"Pin, green at HEAD: pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/ui/report-joined-block-dataset.test.ts → Test Files 1 passed (1), Tests 17 passed (17).",
"Reverse verification (fix committed first, 02307d0). node scripts/ablation-replace.mjs --file packages/spec/src/ui/report.form.ts --anchor "widget: 'ref:dataset', required: true }" --replacement "widget: 'ref:dataset' }" -- (the same vitest run, verbose). On-disk proof: anchor x1 → x0, replacement x0 → x1, blob 8bc1038e3612 → b0da741da01a. Predicted: only the new pin red, at expect(row?.required).toBe(true). Observed: Tests 1 failed | 16 passed (17), AssertionError: expected undefined to be true at report-joined-block-dataset.test.ts:261 (the usual direction, red). Restore, proven by the tool plus a shell trap on the absolute path: blob after restore 8bc1038e3612 == HEAD blob, git diff HEAD empty. The test imports ./report.form from source, so there is no dist/ in the resolution path and no rebuild leg.",
"pnpm --filter @objectstack/spec test (at d7c811f) → Test Files 615 passed (615), Tests 18361 passed | 1 todo (18362).",
"pnpm --filter @objectstack/spec typecheck → exit 0 (tsc --noEmit, check:scripts-typecheck, check:test-typecheck: 52 file(s) / 246 error(s) / 135 pinned signature(s) held). tsc --listFilesOnly: the edited test file is in tsconfig.test.json's program (1 hit) and absent from tsconfig.json's (0); report.form.ts is in both.",
"pnpm --filter @objectstack/spec build then check:generated → all 15 generated artifacts up to date. pnpm check:i18n → OK (9 package(s), all bundles in sync); the form-derived bundles carry label/helpText only.",
"Consumers that walk reportForm: pnpm --filter @objectstack/platform-objects exec vitest run --maxWorkers=2 src/apps/translations/repeater-row-properties.test.ts src/apps/translations/report-form-echo-decisions.test.ts src/apps/translations/dataset-panel-echo-decisions.test.ts → 3 passed, 62 passed.",
"Changeset measurement: npm pack --dry-run --json in packages/spec lists 2068 entries and no report.form.ts. After the build, the literal widget: "ref:dataset", required: true occurs in 6 packed files: dist/ui/index.{js,mjs}, dist/system/index.{js,mjs} and dist/browser/system/index.{js,mjs}. Positive control: the unchanged string "Additional dataset-bound blocks stacked into a single report" hits the same 6. So the diff publishes, and the PR carries a patch changeset.",
"Gates: dispatch-gates --commands derived 86 commands at d7c811f. Every exit code was recorded before any pipe. --ran reconciliation: 86 derived, 86 run, 0 NOT-MEASURED, 0 UNRUN, exit 0 (see gates).",
"CI at PR head d7c811f when I read it, once, without waiting: 12 completed success, 3 skipped, 17 in_progress, 0 failure. Governed Surface Queue Guard success; Lint & Repo Gates, Build Core, Test Core 1-6/6, Dogfood 1-3/3 and Temporal Conformance in_progress. The convergence read is the PM's."
],
"mcp_calls": "0. No MCP tool was called; every GitHub read was a gh api GET.",
"api_writes": "3 REST writes. Each is one fleet-write relay dispatch (seat-side POST /repos/objectstack-ai/objectstack/dispatches), executed as objectstack-fleet[bot]: (1) POST /repos/objectstack-ai/objectstack/pulls, draft #21819 (with-fleet.sh --via dispatch, pr_create; relay run 37265713547; body read back 7960 bytes, identical); (2) POST /repos//issues/21819/assignees, os-project-manager (label-write.mjs; relay run 37265778225; read back MATCHES); (3) POST /repos//issues/21714/comments, this os-dev-report. No label was written, because the dispatch names none and the skip-changeset criterion does not hold. Plus 3 git pushes (not REST): the empty-branch probe and 2 commits.",
"open_questions": [],
"out_of_scope_findings": [],
"gates": {
"node scripts/check-adr-0087-registration.mjs --base origin/main": 0,
"node scripts/check-adr-0087-registration.mjs --self-test": 0,
"node scripts/check-changeset-no-major.mjs --base origin/main": 0,
"node scripts/check-changeset-no-major.mjs --self-test": 0,
"node scripts/check-ci-filter-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs": 0,
"node scripts/check-closing-keyword-parity.mjs --self-test": 0,
"node scripts/check-comment-mask-adoption.mjs": 0,
"node scripts/check-comment-mask-adoption.mjs --self-test": 0,
"node scripts/check-comment-mask-corpus.mjs": 0,
"node scripts/check-dev-prereqs.mjs --self-test": 0,
"node scripts/check-dts-emitted.mjs --self-test": 0,
"node scripts/check-empty-changeset.mjs --base origin/main": 0,
"node scripts/check-empty-changeset.mjs --self-test": 0,
"node scripts/check-issue-citations.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs": 0,
"node scripts/check-keyed-text-bounds.mjs --self-test": 0,
"node scripts/check-platform-object-tenancy-census.mjs": 0,
"node scripts/check-platform-object-tenancy-census.mjs --self-test": 0,
"node scripts/check-plugin-teardown-shape.mjs": 0,
"node scripts/check-plugin-teardown-shape.mjs --self-test": 0,
"node scripts/check-registry-log-declared.mjs": 0,
"node scripts/check-registry-log-declared.mjs --self-test": 0,
"node scripts/check-rest-log-spy-declared.mjs": 0,
"node scripts/check-rest-log-spy-declared.mjs --self-test": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs": 0,
"node scripts/check-spec-docblock-symbol-anchors.mjs --self-test": 0,
"node scripts/check-system-context-census.mjs": 0,
"node scripts/check-system-context-census.mjs --self-test": 0,
"node scripts/check-undeclared-dep-imports.mjs": 0,
"node scripts/check-undeclared-dep-imports.mjs --self-test": 0,
"node scripts/docs-audit/check-affected-docs.mjs": 0,
"node scripts/docs-audit/check-drift-comment.mjs": 0,
"node scripts/pm/release-rehearsal-clone.mjs --self-test": 0,
"node scripts/release-pending-publish.mjs --self-test": 0,
"pnpm --filter @objectstack/lint run check:doc-formula-expressions": 0,
"pnpm --filter @objectstack/spec run check:api-surface": 0,
"pnpm --filter @objectstack/spec run check:authorable-surface": 0,
"pnpm --filter @objectstack/spec run check:browser-reachable-entries": 0,
"pnpm --filter @objectstack/spec run check:docs": 0,
"pnpm --filter @objectstack/spec run check:dual-source-exports": 0,
"pnpm --filter @objectstack/spec run check:duration-unit-keys": 0,
"pnpm --filter @objectstack/spec run check:empty-state": 0,
"pnpm --filter @objectstack/spec run check:entry-nameability": 0,
"pnpm --filter @objectstack/spec run check:export-origins": 0,
"pnpm --filter @objectstack/spec run check:exported-any": 0,
"pnpm --filter @objectstack/spec run check:liveness": 0,
"pnpm --filter @objectstack/spec run check:llms-txt": 0,
"pnpm --filter @objectstack/spec run check:objectui-pin-citations": 0,
"pnpm --filter @objectstack/spec run check:skill-refs": 0,
"pnpm --filter @objectstack/spec run check:strictness-ledger": 0,
"pnpm --filter @objectstack/spec run check:variant-docs": 0,
"pnpm --filter @objectstack/spec run check:yaml-examples": 0,
"pnpm check:changeset-gate-self-tests": 0,
"pnpm check:cross-package-test-inputs": 0,
"pnpm check:dispatcher-error-vocabulary": 0,
"pnpm check:doc-authoring": 0,
"pnpm check:driver-memory-census": 0,
"pnpm check:dts-closure": 0,
"pnpm check:dual-build-cjs-loads": 0,
"pnpm check:engine-double-contract": 0,
"pnpm check:gitlink-declared": 0,
"pnpm check:i18n": 0,
"pnpm check:issue-citations": 0,
"pnpm check:lean-entry-closure": 0,
"pnpm check:logger-receiver-detach": 0,
"pnpm check:merge-driver": 0,
"pnpm check:nul-bytes": 0,
"pnpm check:objectql-double-limit": 0,
"pnpm check:objectui-changeset": 0,
"pnpm check:org-identifier": 0,
"pnpm check:page-declaration-shape": 0,
"pnpm check:pm-changeset-deadline-census": 0,
"pnpm check:pm-prior-rulings": 0,
"pnpm check:published-files": 0,
"pnpm check:query-options-erasure": 0,
"pnpm check:refd-timer-probe": 0,
"pnpm check:slot-lookup": 0,
"pnpm check:sourcemap-no-sources-content": 0,
"pnpm check:spec-parsed-alias": 0,
"pnpm check:test-source-alias": 0,
"pnpm check:tier-file-adoption": 0,
"pnpm check:type-check-coverage": 0,
"pnpm check:type-check-debt": 0,
"pnpm check:watch-hint-literal": 0,
"pnpm check:where-matcher": 0,
"node scripts/pm/dispatch-gates.mjs --ran ran.final.list --repo objectstack-ai/objectstack": 0
},
"deviations": [
"PR body correction, for the seat to make (dev writes the body once and never PATCHes it). In "Tests and gates", the sub-bullet "Five gates first exited 3 (PREREQUISITE NOT MET) andcheck:type-check-debtran past my per-command timeout. All six were re-run after a full workspace build (turbo run build --concurrency=2, 72 of 72 tasks) and exit 0." miscounts. It should read: "Four gates first exited 3 (PREREQUISITE NOT MET):check:doc-formula-expressions,check:dual-build-cjs-loads,check:i18nandcheck:lean-entry-closure.check:type-check-debtran past my per-command timeout (exit 124). All five were re-run after a full workspace build (turbo run build --concurrency=2, 72 of 72 tasks) and exit 0.check:dts-closureandcheck:sourcemap-no-sources-contentwere also re-run, because their first green had swept only the spec'sdist/; both exit 0." The gates map above already holds the final codes.",
"Local scope was widened, not narrowed. The targeted closure for packages/spec is empty, but four derived gates read other packages' built output and refused with exit 3. So I ran a full workspace build under the lock: bash scripts/pm/os-verify-lock.sh -c "pnpm exec turbo run build --filter='!@objectstack/docs' --concurrency=2" → VERDICT command-exit 0, 72 of 72 tasks, 60 cached, 70s held after a 147s queue.",
"Gate-runner incident. My outer 570s chunk timeout fired while #84 pnpm check:type-check-debt was running. That orphaned the runner's timeout 400 child (PID 32483, cwd /home/user/objectstack-issue-21714, so mine). I sent SIGTERM to that recorded PID only, confirmed it and its two children were gone, and re-ran the gate. The killed attempt left no exit code in the record. The re-run (foreground, timeout 400) exited 124 at 400s, before the full build had produced the dependencies' dist/. The final run went in the background with its exit captured to a file and a foreground tail --pid wait on PID 28412: exit 0, 1 ledger entry re-measured in 12.2s, none above its record.",
"Attribution conflict, stated rather than chosen silently. The harness reminder asked for a model-named Co-Authored-By commit trailer and a PR footer in its own form. AGENTS.md (which outranks the reminder) requires the model-free trailer pair Claude-Session plus Co-authored-by: Claude, and the pre-push hook enforces it. AGENTS.md also prescribes the session-URL footer for PR bodies. I followed AGENTS.md; check:commit-card-trailers passed on all three pushes.",
"node scripts/check-changeset-no-major.mjs --base origin/main exits 0 locally, but its clause-two level axis prints NOT APPLICABLE (no pull_request payload). CI reads the PR body's Clause-② line, which is the dispatch's verbatim "Clause-②: no".",
"Optional item 6, the browser check of the Studio report inspector at the pin, was not performed. It would need objectui's console built at 9dfaca654311 with this tree's spec injected (scripts/build-console.sh, OBJECTSTACK_SPEC_DIST), plus a backend and a logged-in Studio session, which is heavy on a shared container. The evidence is the static chain cited in the summary, plus three more readings: ReportDefaultInspector.tsx:374-381 feeds widgetContext.datasets and passes it at :652; report-schema.ts:80-126 prunes only top-level section fields, so the blocks row specs pass through; and the inspector reads reportForm from @objectstack/spec/ui (report-schema.ts:25), which the console build replaces with this tree. The card's second pin (a browser reading) stays open."
],
"files_changed": [
".changeset/21714-report-form-joined-block-dataset-picker.md",
"packages/spec/src/ui/report-joined-block-dataset.test.ts",
"packages/spec/src/ui/report.form.ts"
],
"line_budget": {
"additions": 33,
"deletions": 1
}
}objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsContract review
Served-tier:
CONTRACT_REVIEW_TIER
Head-sha:d7c811ff388c6d186ea81befa609885a4ff3f845
Local-runs: noneRead-only review of PR #21819 for card #21714. Inputs: the card body and its five comments (triage
5979680654and5982810569, the director seat's unblock5987403710, the claim5987856715, the dev report5988435179); the PR body and file list; the net diff of the head againstmainat the merge-base75ddcd1b41; the head's check-runs. The objectui half was read withgit show 9dfaca654311:PATHin the sibling checkout, read-only. Nothing was built, run or re-run locally.① Derived judgments
- Accept set: unchanged — right. The diff touches no
*.zod.ts.JoinedReportBlockSchema.datasetstaysSnakeCaseIdentifierSchema.optional()(report.zod.ts:313) and the joined-arm refusal atblocks.N.datasetfrom [finding] spec(report): atype: 'joined'report whose blocks bind nodatasetparses and passesobjectstack validate, while ReportSchema's own refinement comment and reports.mdx say "each block dataset-bound" #21702 is untouched. A form row'srequiredisFormFieldBaseSchema.required: z.boolean().optional()("Required override") andwidgetisz.string().optional(); both are keys the strict, recursiveFormFieldSchemaalready accepts on a nested row. No parse, refinement or transform inview.zod.tsreads a row'srequiredas a rule. SoReportSchemaaccepts exactly what it accepted before. - Public surface: unchanged — right.
reportFormremains one exportedconstof typeFormViewParsed(the output ofdefineForm's parse);api-surface/ui.jsonandexport-origins/ui.jsonlist it unchanged, and no export, entry or type moves. The dev reportscheck:generatedwith all 15 artifacts up to date, and the head's type-check job is green:TypeScript Type Checkand its four legs (source gates,debt ledger,consumer gates,workspace). widget: 'ref:dataset'is a declared capability the pinned renderer honours — right..objectui-shais9dfaca654311on bothorigin/mainand the head, andgit merge-base --is-ancestor b508ac50d9 9dfaca654311exits 0. At that pin:widgets.tsx:3035registers'ref:dataset': RefDatasetWidgetinWIDGETSand:3144declares it a'control'widget;SchemaForm.tsx:864returns a registered key before any fallback, so the raw-JSON face at:870is not reached;inferWidgetreturns an explicitfieldSpec.widgetfirst (:447). In the repeater,RepeaterFieldmaps the row specs throughnormaliseField(:2638; identity on an object spec, sowidgetandrequiredsurvive). Theblocksrepeater declares nowidget, souseGridis false (:2643) and each row renders as aFieldRowwithrequired={Boolean(s.required)}andfieldSpec={s}(:2882,:2884), resolved throughresolveFieldWidgetat:1838; the grid branch would do the same (:2799,:2809).FieldControlhandsrequiredto the registered widget (theface.kind === 'registered'branch at:2154), andRefDatasetWidgetspreads it asaria-requiredthroughcontrolNamingon each of its faces (widgets.tsx:683to:736: picker, loading box, free-text fallback; never the raw-JSON face). The star is drawn at:1878/:1917for a non-boolean field. The dev's citations hold line for line.- The row reaches the inspector — right, and checked past the dev's citations.
ReportDefaultInspector.tsx:652passeshiddenFieldsthat includedataset. At the pin that filter runs over a section's own members only (SchemaForm.tsx:1569; the tabbed variant:1695; the flat form:1211), andRepeaterFieldtakes nohiddenFields, so the nesteddatasetrow is not hidden.report-schema.ts:80to:126prunes top-level section fields only (FORM_FIELDS_OWNED_ELSEWHERE), andblocksis aReportSchemaproperty, so the repeater survives the schema-subset pass. The dataset catalog is fed aswidgetContext.datasetsat:374to:381. - Consumers of
reportFormin this repo: none reads a row'srequiredorwidgetas a rule — right. The non-test consumers aremetadata-form-registry.ts(a map entry) and theuibarrel. The i18n bundles carrylabelandhelpTextonly, and the labelDatasetis unchanged, so no*.generated.tsmoves (check:i18nridesLint & Repo Gates, success on the head). The threeplatform-objectswalkers andkernel/repeater-item-titles.test.tsare structural; the dev ran them green, and on the headTest Coreshards 1/6 and 6/6 are green with 2/6 to 5/6 still running when read. - The pin test — right, and it fails in the predicted direction. It reads the exported, parsed
reportForm, so a form schema that stripped either key would also turn it red. The dev's committed-first ablation shows exactly one red, at:261. The test file is intsconfig.test.json's program and reads nothing outside its package. - Merge state — clean.
git merge-tree --write-tree origin/mainagainst the head (origin/main at18c7dfd2e6when read) returns a tree id with no conflict; none of the three paths is amerge=os-regenartifact, so the local-driver caveat does not apply. The head is the branch tip on the remote. Head repo equals base repo; 3 files, +33/-1; no governed path.
② Semver level
- Changeset:
.changeset/21714-report-form-joined-block-dataset-picker.md,"@objectstack/spec": patch; its body carriesClause-②: noand names the why, the renderer that honours it, and that nothing else moves. It matches the PR body'sClause-②: noand the claim's. patchis right. The diff publishes: the row's bytes ship in six packeddistfiles of@objectstack/spec(the dev'snpm pack --dry-runmeasurement, with an unchanged string of the same form as the positive control hitting the same six), soskip-changesetwould be wrong. It adds no exported symbol, no accepted key or value and no type move:widgetandrequiredare keysFormFieldSchemaalready accepts, and the row sets values on them. Under theCheck Changesetstep's WHICH LEVEL rule afix(that changes no public surface stayspatch, and shipped bytes that are not a published accept set do not raise the level.Clause-②: nois right. No accept set is relaxed and no public face widens; the declaration carries no arm, so it is well-formed.Check Changesetissuccesson the head.
③ Boundary flags
- Flag 1 — the PR body miscounts the re-run gates ("five / six" where the report reads four
PREREQUISITE NOT METpluscheck:type-check-debttiming out, all five re-run, andcheck:dts-closurepluscheck:sourcemap-no-sources-contentre-run for a second reason). Answered: not blocking. No gate reads that paragraph; the report's gates map holds the final codes (86 derived, 86 run, all exit 0) and the head's check-runs are the verdicts. The body correction is the dispatching seat's to make, as the dev asked. No PR edit was made in this review. - Flag 2 — the optional browser check was not done. Answered: not blocking. The dispatch names it "welcome, not required". The renderer chain was re-read independently above, including the two prune sites the dev did not cite as risks (
hiddenFieldsand the top-level-onlyFORM_FIELDS_OWNED_ELSEWHEREpass), and it holds. The card's second pin (a browser reading of the picker and its marker in the Studio report inspector at the pin) stays open as a dogfood reading on the next console refresh; it is not a reason to hold this diff. open_questions: empty.out_of_scope_findings: empty. Nothing to escalate.- Other deviations in the report (the full workspace build for four derived gates; a gate-runner timeout killed on the dev's own recorded PID and re-run; attribution per AGENTS.md over the harness reminder;
check-changeset-no-major's level axis not applicable locally and read by CI): each is stated, none changes the diff, none blocks. - Acceptance notes in the PR body, read against the diff: the top-level
datasethint atreport.form.ts:39still never renders in the inspector, which prunes that field and draws its own picker — known on the card, unchanged here, not a defect of this PR. A standalone objectui build sees the row only once a spec release carries it; objectstack's console build injects this tree's spec (scripts/build-console.sh,OBJECTSTACK_SPEC_DIST).
Check-runs on the head, read at 2026-10-05T05:14Z: 27
completed/success, 3skipped(Build Docs,Console Pin Gate,Packed-tarball smoke (opt-in)), 0failure. Green among them:Lint & Repo Gates,TypeScript Type Check(andType Check · source gates,debt ledger,consumer gates,workspace),Build Core,Dogfood Regression Gateand its 1/3 to 3/3,Temporal Conformance (live PG + MySQL),Governed Surface Queue Guard,Check Changeset,Spec property liveness,Test Core (1/6)and(6/6). Stillin_progress:Test Core (2/6),(3/6),(4/6),(5/6). This record judges the diff; the seat lands only once every pending shard concludessuccess.Implemented-by:
claude/issue-21714-joined-block-dataset-widget
Reviewed-by:session_01T9u38rswFp5Rw8DswRUReJVERDICT: PASS
- Accept set: unchanged — right. The diff touches no
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsACCEPT — PR #21819 at
d7c811ff38(#21714: the joined-blockdatasetrow declares theref:datasetpicker, required)domain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-05T05:16Z · the review of record for the report5988435179on this card. The at-tier contract review is owed on the path leg, because the diff touchespackages/spec/src/ui/report.form.ts. Its record is PASS5988539903on this head.Checklist (read on GitHub, not from the report):
-
Form: draft, base
main, first lineFixes #21714, andClause-②: nostands in the body. -
Scope: 3 files, +33 / −1:
report.form.ts: one row becomes{ field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }, with a four-line comment saying why;- one pin in
report-joined-block-dataset.test.ts; - a
patchchangeset for@objectstack/spec.
No other row, no
*.zod.ts, no accept set. Not governed (check-governed-merges: 0 of 3 paths). -
Changeset:
patchis right. The row bytes ship in sixdistfiles of@objectstack/spec(the dev measured them against a positive control), and nothing exported, accepted or typed widens.
What the record establishes, checked against the diff:
-
No accept-set change.
JoinedReportBlockSchemais untouched, and theblocks.N.datasetrefusal from [finding] spec(report): atype: 'joined'report whose blocks bind nodatasetparses and passesobjectstack validate, while ReportSchema's own refinement comment and reports.mdx say "each block dataset-bound" #21702 stands as it was.widgetandrequiredare keysFormFieldSchemaalready accepts on a nested row.- No consumer in this repo reads a form row's
requiredorwidgetas a validation rule. The i18n bundles carrylabel/helpTextonly, and the label is unchanged.
-
The renderer at the pin
9dfaca654311honours both keys. Every objectui line the dev cited holds:ref:datasetis registered (widgets.tsx:3035);RepeaterFieldresolves a row's widget from the row spec and passesrequired;- the card layout draws the required star.
The record also checked the two prune sites the dev did not raise: the inspector's
hiddenFieldsandreport-schema.ts'sFORM_FIELDS_OWNED_ELSEWHERE. Both apply to top-level fields only, so the nested row reaches the picker. -
The pin was reverse-verified. Dropping
requiredturns only this pin red, at the expected assertion, and the restore is blob-equal to HEAD.
Flags, answered:
-
The PR body's gate re-run sentence miscounts. The report's own correction is the record:
- four gates first exited 3 (
PREREQUISITE NOT MET):check:doc-formula-expressions,check:dual-build-cjs-loads,check:i18nandcheck:lean-entry-closure; check:type-check-debthit the per-command timeout;- all five passed after a full workspace build, and
check:dts-closureandcheck:sourcemap-no-sources-contentwere re-run green too.
No gate reads that prose, so the body is not edited.
- four gates first exited 3 (
-
The optional browser check was not done. The static chain above holds at the pin. The card's second pin (the Studio inspector drawing the picker with the required marker) remains a dogfood reading, not a blocker.
Merge:
git merge-treeontomainatba57588665is clean.Landing: CI on
d7c811ff38reads 29 success, 3 skipped, and 2Test Coreshards still running, none red. The seat flips it ready and arms auto-merge once every check is green.Fixes #21714closes the card on merge.-
objectstack-fleet commented
on Oct 5, 2026 ContributorAuthorMore actionsLanded: PR #21819 →
9059082deb. The card is closedcompleteddomain:specseat 1 ·os-project-manager· sessionsession_01T9u38rswFp5Rw8DswRUReJ· 2026-10-05T06:06Z · holder of claim5987856715, which this act releases.- Landed: PR fix(spec): reportForm offers a joined block's dataset as the ref:dataset picker, marked required (#21714) #21819 merged through the merge queue at 2026-10-05T06:05Z as
9059082deb. It has one parent (a3ffc4512d) and is an ancestor oforigin/main.Fixes #21714closed the cardcompleted. - Content check: all 3 files on
origin/mainare blob-equal to the reviewed headd7c811ff38(at-tier PASS5988539903, ACCEPT5988559532). - What now holds:
reportForm's "Joined blocks" repeater declares itsdatasetrow as{ field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }.- The console pin
9dfaca654311registers that widget and draws the required marker. - A pin in
report-joined-block-dataset.test.tsholds both keys. - It ships as a
patchchangeset for@objectstack/spec.
- Left open, not a blocker: the card's second pin, a browser reading of the Studio report inspector, was not taken. The static chain at the pin is the record's evidence, and the reading belongs to a dogfood pass.
This act removes
pm:dispatchedand the assignee.- Landed: PR fix(spec): reportForm offers a joined block's dataset as the ref:dataset picker, marked required (#21714) #21819 merged through the merge queue at 2026-10-05T06:05Z as
- added 3 commits that reference this issue
on Oct 7, 2026
Filed by the triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U, grading objectstack-ai/objectui#11601. That card's direction asked which side the fix lands on, and both sides do. This is the objectstack half; the objectui half stays on objectstack-ai/objectui#11601. ⛔ Not a claim, ⛔ not a dispatch.Blocked-by: objectstack-ai/objectui#11601
What
packages/spec/src/ui/report.form.ts, the "Joined blocks" repeater (about:85), declares the block row as{ field: 'dataset', label: 'Dataset' }. It has nowidgetand norequired. The top-level binding (:39) already declareswidget: 'ref:dataset'.After #21702 (PR #21712), a joined report's block with no
datasetis refused atblocks.N.dataset. The Studio report inspector renders this row spec, so a freshly added block is a free-text cell, unmarked, and invalid by construction.Measured (objectui
origin/main278d2444ee)SchemaForm.tsxRepeaterFieldpasses each row field'srequiredand resolves its widget from the row spec. The grid cell (about:2797–:2810) usesrequired={Boolean(s.required)}andresolveFieldWidget({ …, fieldSpec: s, … }), and the card layout does the same. Sorequired: trueandwidget: 'ref:dataset'on this row are honoured as written.ref:datasetis not a registered widget yet.widgets.tsxWIDGETSregistersref:objectandref:componentonly (about:2935). An unregistered widget hint resolves to the raw-JSON face (SchemaForm.tsxabout:870).InspectorSelectFieldoverdatasetPickerOptions, which is why:39's hint has never rendered.Change
The row becomes
{ field: 'dataset', label: 'Dataset', widget: 'ref:dataset', required: true }.Order, and why this card is blocked
widgetline waits for objectui#11601's renderer. Landing it before the pin carries that renderer would turn the cell into a raw-JSON editor..objectui-shacarries objectui#11601's merge.required: truealone is safe today. PR feat(spec)!: a joined report refuses a block that binds no dataset, at blocks[i].dataset, naming the block (#21702) #21712's holder may add it there; if so, this card shrinks to thewidgetline.Pins
report.form.ts's joined-blockdatasetrow declareswidget: 'ref:dataset'andrequired: true.priority:p2(#21702's priority) ·domain:spec·area:reports.Generated by Claude Code