Skip to content

Studio: the object and field inspectors author what the v3 record page reads — relatedList (show / hide / primary), related-list columns / filter / limit on the block, and a new record page starts empty instead of a copy of the synthesized regions #27054

Closed

Description

@objectstack-fleet

Unblocked 2026-10-28T14:04Z — #27069 merged as PR #27215 (c63ce2ad72): ObjectSchema.relatedLists and the field group's relatedList placement entry are on main. Round 2 of this card (the relatedLists drag-to-order editor) is dispatched on branch claude/issue-27054-related-lists-editor.

(#27069 merged 2026-10-28T13:28Z.) Four of five items landed in PR #27163 (merged 2026-10-28T11:02Z, Part of): the relatedList three-state control with the filter builder, the record:related_list block inspector props, ordered highlightFields, and slotted-by-default record pages. The fifth — the relatedLists drag-to-order editor — waits for #27069 to ship the key; this card stays open for it.

Part of the record-page-v3 epic #26676 (v3 child 6 of 8). Independent of the record-page series (Studio files only, plus a spec form file); the relatedLists editor lands after #27069 ships the key (that item waits, the rest does not).

Source (origin/main 7bb8084590)

  • relatedList: true | false | 'primary' (field.zod.ts:1725) has no Studio control: field.form.ts offers relatedListTitle / Columns / Filter rows only and ObjectFieldInspector.tsx:35 says the inverse-collection control 「is a spec card」. relatedListFilter's widget renders raw JSON.
  • record:related_list's inspector (previews/block-config.ts:513-518) exposes objectName, relationshipField, title, limit — not columns, sort, filter, actions, add.
  • metadata-admin/anchors.ts:207-221: creating a record page seeds it with the synthesized page's regions and template, so every authored page starts as a fork of the default and drifts from it.
  • ObjectSettingsPanel.tsx edits nameField, stageField, highlightFields, enable.* but highlightFields has no ordered picker (order is what the highlight strip draws).

Target

  1. Field inspector: a three-state 「关联列表」 control (显示 / 不显示 / 主要) on lookup and master_detail fields writing relatedList; relatedListFilter gets the filter-condition builder the list view uses, not raw JSON.
  2. record:related_list block inspector: columns (string tags of the child's fields), sort, filter (same builder), limit, showViewAll, add — the props the node already declares (component.zod.ts:1574).
  3. Object settings: relatedLists (spec: an object declares the business order of its related lists — relatedLists: ['task', 'event', …], an ordered list of child relationships like highlightFields is of fields; unlisted ones follow the derived order; a name that matches no inbound relationship is refused #27069) as a drag-to-order list of the object's inbound relationships (child object · field, with 「未列出的按默认顺序」 shown greyed below), and highlightFields as an ordered multi-select (drag to reorder, max 6), with a live preview of the strip.
  4. New record page: starts as kind: slotted with no slots (= the synthesized default) and a one-line explanation; 「转成整页」 is an explicit action that copies the regions on demand. The create form's kind default and anchors.ts seed change accordingly.
  5. The objectstack-ui skill is not touched here (skills(objectstack-ui) + docs: teach the record page as it is now — object roles produce the page, six slotted slots, kind: full as the escape hatch, drawer default with page by declaration, the column tiers and the reader's own toggles — and stop naming tabs, modal / split / popover #27059).

File surface

ui/packages/app-shell/src/views/metadata-admin/{anchors.ts,ObjectSettingsPanel.tsx,inspectors/ObjectFieldInspector.tsx}, ui/packages/app-shell/src/views/studio-design/** (block-config, PageBlockInspector), packages/spec/src/data/field.form.ts and packages/spec/src/ui/page.form.ts (form rows only — no schema change; Clause-②: no, Metadata-protocol: none), i18n, tests beside, changesets.

Acceptance

  1. Tests: setting 「主要」 on the task → lead FK writes relatedList: 'primary' and the synthesized lead page puts tasks first; the block inspector round-trips columns / filter / limit; a new record page is created with kind: slotted and empty slots; highlightFields order is preserved.
  2. Screenshots: the field inspector with the three-state control; the new-page dialog.

Rulings (maintainer, chat in the filing session, 2026-10-28 — verbatim, not re-decidable here)

  1. Record-page model (2026-10-27): 「1. 一个对象一个默认打开方式:默认抽屉(带展开整页),page 只给显式声明的对象,modal 退役,split / popover 变成用户个人偏好;抽屉和整页是同一张页在两个宽度。」
  2. On the resident timeline column: 「时间线是不是太突出了,这个只适合部分对象吧?适合做所有业务对象的默认布局吗」 and then 「时间线是否显示,是否可以切换,比如当前版本的 Claude code,顶部有很多切换图标」 — the column's presence is a per-user toggle with an object-derived default, never metadata.
  3. On the related column's cost: 「关联都放在右侧,记录打开时是不是会很慢,因为需要加载所有的关联子表。」 — the related column loads counts first, rows on demand, and never blocks the details.
  4. On mobile: 「还要考虑手机端的效果。」
  5. On the definition surfaces: the PM's keep / narrow / retire table was put to the maintainer (keep object roles, relationship flags, six slotted slots and kind: full; retire the tabs slot and the dead BuildPageOptions hints; fold the quick-look path into the one record page) and approved with the instruction in the footer.

Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff on 2026-10-28 under epic #26676 (record page v2 → v3), from the record-page definition-surface inventory on objectstack main 7bb8084590 and the maintainer's rulings in that session. The maintainer's instruction to file and dispatch, verbatim: 「同意,以上界面相关的需要我确认的你负责立卡派发,如果有可能可以启动云会话,开发完你来确认。」 Design evidence: the design canvas boards 「记录页 v3」 row (RecordPageV3, RecordPageV3NoTimeline, RecordPageRef, RecordDrawerV3, RecordMobile) — exported to the never-merged branch named in the epic parent once this batch is filed.

Activity

  1. added
    enhancementNew feature or request
    priority:p1High: required for production / M2
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    on Oct 28, 2026
  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 (epic #26676, v3 child 6 of 8) — taken from the ui lane queue for the cloud-session trial on the maintainer's instruction (chat, 2026-10-28, verbatim: 「你可以启动云会话试试」)
    Session: session_cdb7a717-def7-4e71-9988-f089fbaa18ff
    Account: zhuangjianguo
    Branch: claude/issue-27054-studio-related-list-controls
    Worktree: a cloud session's own checkout of objectstack-ai/objectstack (Default environment)
    Domain: domain:ui
    Seat: epic #26676 (the epic PM session, not a domain seat)
    File surface: ui/packages/app-shell/src/views/metadata-admin/{anchors.ts,ObjectSettingsPanel.tsx,inspectors/ObjectFieldInspector.tsx}, ui/packages/app-shell/src/views/studio-design/** (block-config, PageBlockInspector), packages/spec/src/data/field.form.ts and packages/spec/src/ui/page.form.ts (form rows only — no schema change), i18n, tests beside, changesets; ⛔ packages/spec/src/**/*.zod.ts, ⛔ the relatedLists editor until #27069 ships the key (that item is explicitly deferred inside this card) (stop on breach; explain in the report)
    Container & model: M, mode:cloud (a cloud session of this seat's account, Default environment, started from origin/main f708cf224a; writes through the fleet relay with the cloud container's session id), model: TIER_DEFAULT (dispatch-gates --tier 2026-10-28T04:05Z: no path-derived mandate; packages/spec/src/** hit is the two form files, judged from content as Clause-②: no — forms, not schema), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: none
    Premise: holds (filed from the definition-surface inventory on 7bb8084590; the Studio files are unchanged on f708cf224a by the scan below)
    Serial constraints cleared: no open PR touches the Studio inspector / anchors / form-row files (scan 2026-10-28T04:05Z); the relatedLists editor item waits for #27069 and is left out of this dispatch; #27052 / #27055 edit the record page, disjoint

    Dispatched at 2026-10-28T04:05Z to a cloud session of this seat; the dispatch order lives in the session's opening prompt.


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 27054,
      "status": "done",
      "branch": "claude/issue-27054-studio-related-list-controls",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27163",
      "session": "session_01Q4EjjRbbbwV5u1Vx4Ge7TT",
      "premise_still_valid": true,
      "summary": "Four of the card's five items landed in Studio (app-shell + studio i18n namespace), no spec change: (1) field inspector 'Related list on the parent record' section on lookup/master_detail — three-state relatedList control (Show = key absent / Don't show = false / Primary (in body) = 'primary') plus relatedListFilter via the dataset inspector's FilterCondition builder, extracted to a shared FilterConditionField; (2) record:related_list block inspector curates columns, sort, filter (list-toolbar FilterBuilder, ViewFilterRule[]), limit, showViewAll (spec default on), add (JSON); union arms it cannot hold show as JSON; (3) highlightFields as an ordered drag/up-down list capped at 6 with a strip preview; (4) record pages are created kind slotted with no slots (create-form kind derives from type via a new page-kind transform), regions copied only on explicit full at create or the new 'Convert to full page' designer action, which writes the persisted section shape. PR is 'Part of #27054': the relatedLists editor waits for #27069. Live Studio verification caught and fixed a convert-to-full defect (runtime-shape sections, 16 spec errors).",
      "tests": "Head 23a697c2 (after merging origin/main 24351cfe): pnpm --dir ui exec vitest run --maxWorkers=2 over all touched families -> Test Files 198 passed (198), Tests 2268 passed | 13 skipped; pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check -> VERDICT command-exit 0. Pre-merge wide run (metadata-admin + studio-design + i18n, 970 files): 1 failed (this branch's 'Hidden' label collided with the field Hidden checkbox) -> renamed to Show / Don't show, ObjectFieldInspector* 23/23 and StudioDesignSurface*+DataPillar* 127/127 files green. Ablations via scripts/ablation-replace.mjs (src-aliased, no dist leg): anchors.ts slotted-seed line neutralised -> anchors.page-seed.test.ts 2 failed | 6 passed, restored blob == HEAD; PagePreview convertToFull toAuthoredDetailSections -> identity -> 1 failed with 'record:details sections.0.fields.0: Invalid input: expected string, received object', restored blob == HEAD. Live: showcase app (--fresh) + console on private ports, screenshots on never-merged branch claude/issue-27054-evidence (bf202d60).",
      "gates": "Local, head 23a697c2: dispatch-gates --commands derived 34 families; all 34 run, every one exit 0; --ran reconciliation: '34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN' (a DERIVED zero, exit codes recorded). Extra ui gates: check-i18n-designer-table-parity, check-i18n-dead-keys, check-i18n-call-site-keys, check-i18n-en-drift, check-new-cross-file-line-citations all exit 0. eslint --no-inline-config over changed files: 0 new errors (1 pre-existing react-hooks/static-components error in ObjectSettingsPanel.tsx whose disable comment --no-inline-config ignores; present on main). CI families: in_progress at report time, read from PR #27163 check-runs.",
      "gates_ran": [
        "node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0",
        "node scripts/check-adr-0087-registration.mjs --self-test :: exit 0",
        "node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs :: exit 0",
        "node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0",
        "node scripts/check-comment-mask-corpus.mjs :: exit 0",
        "node scripts/check-empty-changeset.mjs --base origin/main :: exit 0",
        "node scripts/check-empty-changeset.mjs --self-test :: exit 0",
        "node scripts/check-sdui-manifest.mjs :: exit 0",
        "node scripts/check-sdui-manifest.mjs --self-test :: exit 0",
        "node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0",
        "node scripts/release-pending-publish.mjs --self-test :: exit 0",
        "pnpm --dir ui exec vitest run scripts/ :: exit 0",
        "pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0",
        "pnpm check:changeset-gate-self-tests :: exit 0",
        "pnpm check:closed-ceremony-text :: exit 0",
        "pnpm check:cross-package-test-inputs :: exit 0",
        "pnpm check:dispatcher-error-vocabulary :: exit 0",
        "pnpm check:driver-memory-census :: exit 0",
        "pnpm check:engine-double-contract :: exit 0",
        "pnpm check:gitlink-declared :: exit 0",
        "pnpm check:nul-bytes :: exit 0",
        "pnpm check:pm-changeset-deadline-census :: exit 0",
        "pnpm check:published-files :: exit 0",
        "pnpm check:query-options-erasure :: exit 0",
        "pnpm check:refd-timer-probe :: exit 0",
        "pnpm check:skill-identifier-liveness :: exit 0",
        "pnpm check:test-source-alias :: exit 0",
        "pnpm check:tier-file-adoption :: exit 0",
        "pnpm check:type-check-coverage :: exit 0",
        "pnpm check:type-check-debt :: exit 0",
        "pnpm check:undeclared-dep-imports :: exit 0",
        "pnpm check:watch-hint-literal :: exit 0",
        "pnpm check:where-matcher :: exit 0"
      ],
      "dogfood_battery": "n/a — no ! in the PR title and Clause-②: no without a (narrowing) arm",
      "mcp_calls": "3 — mcp__github__issue_read (get), mcp__github__issue_read (get_comments), mcp__github__pull_request_read (get); all reads, no write tool",
      "api_writes": "2 — fleet relay repository_dispatch for pr_create (objectstack-ai/objectstack#27163, run 40660600739) and for this os-dev-report comment; labels_add: 0 writes (dispatch named no label; a changeset ships, so no skip-changeset); git pushes are not REST writes",
      "line_budget": "n/a — no skills/** or ratcheted ledger touched",
      "files_changed": [
        ".changeset/27054-studio-related-list-controls.md",
        "ui/packages/app-shell/src/views/metadata-admin/anchors.page-seed.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/anchors.ts",
        "ui/packages/app-shell/src/views/metadata-admin/createDerive.ts",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/DatasetDefaultInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/FilterConditionField.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.relatedList-27054.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/ObjectFieldInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.relatedListProps-27054.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/PageBlockInspector.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/datasetFilterCondition.nullOperators-9363.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/datasetFilterCondition.readHalfHolds-10257.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/inspectors/datasetFilterCondition.unmappedInert-9372.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/previews/PageBlockCanvas.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/PagePreview.slottedDefault-27054.test.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/PagePreview.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/previews/__tests__/block-config-i18n.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/previews/__tests__/block-config-schema-parity-8216.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/previews/__tests__/block-config.test.ts",
        "ui/packages/app-shell/src/views/metadata-admin/previews/block-config.ts",
        "ui/packages/app-shell/src/views/metadata-admin/registry.ts",
        "ui/packages/app-shell/src/views/metadata-admin/widgets.tsx",
        "ui/packages/app-shell/src/views/studio-design/HighlightFieldsEditor.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectSettingsPanel.highlightOrder-27054.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectSettingsPanel.tsx",
        "ui/packages/i18n/src/namespaces/studio/en.ts",
        "ui/packages/i18n/src/namespaces/studio/zh.ts"
      ],
      "deviations": [
        "Help text for Primary carries no tracker number: the dispatch asked it to cite #27069, AGENTS.md says runtime strings carry none (os-dev/AGENTS win); the citation is in the RelatedListFields code comment.",
        "field.form.ts and page.form.ts unchanged: the Setup drawer renders field.form via SchemaForm with no object-field context; adding one would re-bind every *Field row (displayField names the referenced object's field). The builder lives in the Studio field inspector.",
        "Show writes the key absent, not true (spec: true/absent = shown; inspector convention: unset deletes).",
        "File surface beyond the card: PagePreview.tsx, PageBlockCanvas.tsx (empty slotted page opens the slot editor), createDerive.ts + registry.ts (page-kind transform), widgets.tsx (export FilterBuilderField), DatasetDefaultInspector.tsx (shared FilterConditionField extracted), block-config-schema-parity-8216 test (judges the single array arm of a string|array union), new HighlightFieldsEditor.tsx and FilterConditionField.tsx; block-config/PageBlockInspector live under metadata-admin, ObjectSettingsPanel under studio-design.",
        "Commit trailers are model-free per AGENTS.md although the harness reminder proposed a model-named Co-Authored-By; the merge commit 23a697c2 carries the default merge message.",
        "Primary help text describes the v3 body full-width table (ruled wording); until #27069 lands the runtime still gives a primary list its own tab.",
        "PR activity was not subscribed: os-dev's dispatch contract outranks the platform's auto-watch posture; CI convergence is the PM's."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: public door = metadata page editor, Design canvas of a full record page whose record:details names its fields (the existing full-create seed on main produces the same) draws a location field as [Object] and a select value raw · evidence: e4-after-convert-to-full.png on claude/issue-27054-evidence (HEADQUARTERS [Object], SALES REGION amer) · dedupe words: page designer canvas record:details authored field names [Object] location",
        "class: a · reach: public door = Studio page block inspector, any curated json prop (record:related_list.add, sort/columns union fallback): the Label is a sibling with no htmlFor, so the textarea has no accessible name (testing-library: 'Found a label with the text of: Sort, however no form control was found associated') · evidence: InspectorJsonField in PageBlockInspector.tsx · dedupe words: InspectorJsonField label htmlFor textarea accessible name",
        "carrier: none · noted, not filed: the Setup metadata drawer still edits field.relatedListFilter as JSON (no object-field context in EmbeddedItemEditor's SchemaForm)"
      ]
    }

    Generated by Claude Code

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    Round: patch round 1 · head 7731242e37 · reported 2026-10-28T10:42Z

    {
      "issue": 27054,
      "round": "patch round 1",
      "status": "done",
      "branch": "claude/issue-27054-studio-related-list-controls",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27163",
      "head": "7731242e37",
      "session": "session_cdb7a717-def7-4e71-9988-f089fbaa18ff (local os-dev subagent of the epic PM session; the cloud session session_01Q4EjjRbbbwV5u1Vx4Ge7TT authored the earlier rounds)",
      "premise_still_valid": true,
      "summary": "Re-pinned ui/packages/app-shell/src/views/recordDetailSections.agreement-12442.test.ts to the contract after this PR, as the seat review specified. The seed-and-runtime agreement is now asserted on the explicit-full path: one named helper (fullSeedFor) calls createSeed with kind 'full', the seeded record:details still carries the runtime's sections in the persisted shape and parses under RecordDetailsProps, and the fieldGroups CONTROL still seeds group references. One new case pins the default half: with no kind, or with slotted, both fixture objects seed exactly { kind: 'slotted' } and no record:details node at all. The 'one derivation, three call sites' block is unchanged. anchors.ts is untouched (the pin follows the contract, not the other way round), and so are packages/spec and the PR body. First merged origin/main (b8615afaba) into the branch: clean merge, no conflicts, no regen deferral. Head 7731242e37 is the one push of this round, made with the user token, and CI ran on it.",
      "tests": "Head 7731242e37. (1) recordDetailSections.agreement-12442.test.ts: 8 passed (8), all listed with the verbose reporter, the new default-half case included. (2) The whole app-shell views directory, run as pnpm --dir ui exec vitest run --maxWorkers=2 packages/app-shell/src/views/: 'Test Files 1246 passed (1246) / Tests 11776 passed, 1 skipped (11777)', EXIT=0. That covers anchors.page-seed, PagePreview.slottedDefault-27054, PagePreview.test.tsx (its objectui#12442 block included), ObjectFieldInspector*, PageBlockInspector*, ObjectSettingsPanel* and block-config*, so no other old pin conflicts with the new contract. (3) i18n package: 'Test Files 89 passed (89) / Tests 1373 passed, 13 skipped', VERDICT command-exit 0. (4) pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check: VERDICT command-exit 0. Both packages run tsc --noEmit and then tsc -p tsconfig.test.json, and --listFilesOnly shows the 12442 test file in the app-shell test program (count 1). Before any test ran, the dependency closure was built with pnpm --filter '@object-ui/app-shell^...' --workspace-concurrency=2 build: VERDICT command-exit 0, 427s. Every locked command ran in DECLARED UNLOCKED mode, because this host has no flock. REVERSE VERIFICATION, done through scripts/ablation-replace.mjs after the fix was committed: the anchor `const fullSeedFor = (def: unknown) => seedFor(def, 'full');` (hit x1) was replaced with the same line passing undefined. The tool reported anchor 1 -> 0, replacement 0 -> 1, blob 179e2f314378 -> 17b6a7480cae, so the mutation landed on disk. Under the mutant, 'Tests 4 failed, 4 passed (8)': all four explicit-full cases went red with the same signatures as CI job 122723053671 ('the page carries exactly one record:details node: expected [] to have a length of 1' x3, and 'expected undefined to deeply equal [ { name: main, ... } ]'). The default-half case and the three-call-sites block stayed green. Restore: blob after restore == HEAD blob 179e2f314378, `git diff HEAD` empty, grep -c afterwards anchor=1 mutant=0. The test imports anchors.ts by relative path, so no dist leg is involved and the ablation's mutation was in the test file itself.",
      "gates": "Local, head 7731242e37: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 34 families from 28 changed paths against merge base b8615afab, and each was run with its exit code captured before any pipe. 33 exited 0. `pnpm --dir ui exec vitest run scripts/` exited 1: 2 failed, 173 passed, 2 skipped files. Both failures are host-only. (a) Two cases in select-affected-tests-wiring.test.ts fail on 'mapfile: command not found', the known macOS bash 3.2 gap. (b) One case in check-i18n-dead-keys.test.ts ('keeps every key it makes reachable OUT of both tiers') hit its 15000ms timeout after 239s in the batch and 305s when re-run alone. That is a timeout, not an assertion, so it reads NOT MEASURED here. CI's run of the same tree is green. Reconciliation via `--ran` with every line written as `command :: exit code`: 'Run reconciliation — 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN', a DERIVED zero, with all 34 carrying an exit code. The derivation noted origin/main had moved about 3 commits past the merge, none of them an input to the answer, and I did not merge again: AGENTS.md section 10 asks for a merge only on a conflict, a main-caused red or a queue removal. CI on PR #27163 at head 7731242e37 had completed every check-run (54 in total) when this report was written. All nine required contexts passed: Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard, Lint UI, Type Check UI, Test UI (all 16 shards, Test UI (9/16) included, which was the red before), Build & E2E UI, plus Build UI Deps. The rest also passed: Check Changeset, Check PR Size, Dogfood Regression Gate, PR-body guards, the Lint and Type Check sub-jobs. Build Core, Docs, Temporal and the opt-in lanes were skipped by their selectors.",
      "gates_ran": "the 34 lines of the --ran file, in this shape: `node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0` ... `pnpm check:where-matcher :: exit 0` (33 lines at exit 0), plus `pnpm --dir ui exec vitest run scripts/ :: exit 1` (host gap, explained under gates)",
      "dogfood_battery": "n/a: the diff is test-only, the PR title has no !, and Clause-② is no",
      "files_changed": [
        "ui/packages/app-shell/src/views/recordDetailSections.agreement-12442.test.ts"
      ],
      "deviations": [
        "Every verify-lock command ran in DECLARED UNLOCKED mode, because this macOS host has no usable flock. Each VERDICT line says so.",
        "The ui scripts/ vitest family exits 1 on this host for two reasons: the bash 3.2 mapfile gap (2 cases) and a dead-keys case that times out on host speed (1 case, NOT MEASURED locally). CI is green on the same head.",
        "I did not merge origin/main again after the round's one merge: origin/main moved about 3 commits during the run, none of them inputs to the derived families (AGENTS.md section 10).",
        "The worktree was created with git worktree add followed by checkout -B rather than the dispatch's exact --no-track spelling, because git refuses --no-track when no new branch is created. The upstream is origin/claude/issue-27054-studio-related-list-controls, as the dispatch asked.",
        "The PR body was not touched, as the dispatch instructs: the seat patches it at landing. The body still describes the round-0 test list, and this round adds only the re-pinned 12442 file."
      ],
      "mcp_calls": "0",
      "api_writes": "1: this os-dev-report comment on #27054 (post-stamped via with-fleet). The one git push of 7731242e37 used the user's git credentials and is not a REST write. labels: 0 writes (none named).",
      "open_questions": [],
      "out_of_scope_findings": []
    }
  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Review recorded on the PR: #27163 ## 席位核对 — ACCEPT at head 7731242e37 (cloud build + one local patch round under this claim: the objectui#12442 agreement pin re-pinned to the slotted-by-default contract); ready + auto-merge handed to the queue on the maintainer's standing authorization for this epic. The PR is Part of #27054: after MERGED the card stays open for its fifth item (the relatedLists drag-to-order editor) behind #27069 — pm:dispatched → pm:blocked, Blocked-by: #27069. Findings filed from this PR: #27177, #27178. — epic PM session_cdb7a717-def7-4e71-9988-f089fbaa18ff, 2026-10-28T10:46Z


    Generated by Claude Code

  6. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    State: pm:dispatched → pm:blocked, Blocked-by: #27069 (body line added in this stroke). PR #27163 merged on main as fd73f2b391 with four of the card's five items; the fifth (the relatedLists drag-to-order editor) needs the spec key #27069 ships. The claim stays with the epic session; when #27069 closes, the unlock scan puts this card back and the remaining item is dispatched as a patch card on a new branch. — epic PM session_cdb7a717-def7-4e71-9988-f089fbaa18ff, 2026-10-28T11:19Z


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 (epic #26676, v3 child 6 of 8) — round 2 of this card, the fifth item
    Session: session_cdb7a717-def7-4e71-9988-f089fbaa18ff
    Account: zhuangjianguo
    Branch: claude/issue-27054-related-lists-editor
    Worktree: objectstack-issue-27054-r2
    Domain: domain:ui
    Seat: epic #26676 (the epic PM session)
    File surface: ui/packages/app-shell/src/views/studio-design/ObjectSettingsPanel.tsx (+ a new RelatedListsEditor.tsx beside HighlightFieldsEditor.tsx), the field-group editor in metadata-admin/** or studio-design/** if one exists on main (the relatedList placement entry), studio i18n (ui/packages/i18n/src/namespaces/studio/{en,zh}.ts), tests beside, changeset; ⛔ packages/spec/**; ⛔ the record-page renderer (#27055 in flight) (stop on breach; explain in the report)
    Container & model: S, mode:subagent (a local os-dev agent of this seat; user-token pushes), model: TIER_DEFAULT (dispatch-gates --tier 2026-10-28T14:02Z: no path-derived mandate), effort: high
    Clause-②: no
    Metadata-protocol: none
    Responsibility: n/a — not a defect card
    Thread-read: 6467622412
    Premise: holds (re-read on main after PR #27215 c63ce2ad72: ObjectSchema.relatedLists entries are a child object name or object.field under RELATED_LIST_ENTRY_PATTERN, a name matching no inbound relationship is refused by the spec and packages/lint validate-related-list-refs; deriveRelatedLists.ts reads the order; HighlightFieldsEditor.tsx from round 1 is the ordered-list pattern to reuse)
    Serial constraints cleared: round 1 of this card (PR #27163) MERGED; #27069 (PR #27215) MERGED; #27055 (cloud, in flight) edits plugin-detail and RecordDetailView.tsx — disjoint from this round's Studio files; #27177 / #27178 (os-warren, in flight) edit the page designer canvas and PageBlockInspector.tsx — disjoint (scan 2026-10-28T14:02Z)

    Dispatched at 2026-10-28T14:02Z to a local os-dev agent of this seat; the dispatch order lives in the agent's opening prompt.


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
    os-dev-report

    Round: round 2 · head 5c2b715589 · PR #27303 · reported 2026-10-28T15:48Z

    {
      "issue": 27054,
      "round": "round 2",
      "status": "done",
      "branch": "claude/issue-27054-related-lists-editor",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27303",
      "head": "5c2b715589",
      "report_comment": "this comment (os-dev-report on #27054)",
      "session": "session_cdb7a717-def7-4e71-9988-f089fbaa18ff (local os-dev subagent of the epic PM session; subagent = parent's id)",
      "premise_still_valid": true,
      "summary": "The card's fifth item is in Studio. (1) Object settings has a relatedLists editor (RelatedListsEditor) over the object's inbound relationships. Its vocabulary is deriveRelatedLists with a new includeHidden option, the record page's own derivation, so there is no second relationship enumeration. Stored entries are ordered rows: drag, or up/down for keyboard. Unlisted relationships trail greyed in the derived order and are not draggable, and the add picker offers exactly that tail. A hidden FK is listed and badged; the editor only orders. Removing the last entry removes the key, and a preview splits the record page's order into body tables and side cards. Every write passes acceptRelatedListEntries first, so an entry naming no inbound relationship never enters the value; nothing is written while the object list is loading. (2) Studio's field-group editor exists on main (ObjectGroupInspector + ObjectFormDesigner), so the group inspector got a 'Place a related table here' picker. It writes fieldGroups[].relatedList from the same vocabulary through the same check, is withheld while the group holds fields, and skips a relationship another group already places. The designer draws such a group as a related-table card that is no drop target. (3) The drag/up-down rows are extracted into a shared OrderedRowList, which HighlightFieldsEditor now uses too. relatedListEntryNames is the one entry reading, shared by resolveRelatedListOrder and the editors. PR is 'Fixes #27054' (last item).",
      "tests": "Head 5c2b715589. New pins: ObjectSettingsPanel.relatedListsOrder-27054.test.tsx (5) and ObjectGroupInspector.relatedListPlacement-27054.test.tsx (5). Acceptance pin 1: a drag writes ['event.related_to','task'] and deriveRelatedLists over the real fixture registry reorders to ['event.related_to','task.lead_id','note.lead','event.who_id']. Pin 2: stored 'nope'/'event.nope' never reach a written value. Pin 3: the unlisted relationships trail in the derived order with aria-disabled and no draggable. Focused re-run on the final head (ObjectSettingsPanel*, ObjectGroupInspector*, ObjectFormDesigner*, deriveRelatedLists* including relatedListsOrder-27069): 'Test Files 21 passed (21) / Tests 181 passed (181)'. Wide run before the merge of 0d45c0e71f (studio-design/, utils/__tests__/, metadata-admin/inspectors/, i18n/): 'Test Files 567 passed | 1 skipped (568) / Tests 6360 passed | 22 skipped', VERDICT command-exit 0. pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check: VERDICT command-exit 0 on the final head; --listFilesOnly counts both new pins in the app-shell test program. Closure build pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build: VERDICT command-exit 0, before and after the merge. REVERSE VERIFICATION after the fix was committed, via scripts/ablation-replace.mjs: anchor 'const accepted = acceptRelatedListEntries(next, relationships);' (x1) replaced with 'const accepted = [...next];'. The tool reported anchor 1 → 0, replacement 0 → 1, blob eb202671ad73 → 6d9ac4f61fbd. Result 'Tests 1 failed | 4 passed (5)': red was exactly the pin 'a name that matches no inbound relationship never enters a written value' (expected [ Array(4) ] to deeply equal [ 'task', 'note' ]), the expected direction. Restore: blob == HEAD eb202671ad73, git diff HEAD empty, grep -c after: anchor 1, mutant 0. The pin imports src by relative path, so there is no dist leg. LIVE: showcase --fresh backend on :4417 and Console vite on :5197 from this branch, in a scratch writable package (desk_customer with children desk_ticket, desk_contract (master_detail), desk_visit (two FKs), desk_survey (relatedList false)). Authored relatedLists ['desk_visit.referred_by','desk_ticket'] and fieldGroups[0].relatedList 'desk_contract'; the package publish accepted both. A stored 'nope' rendered as unmatched, and one move-up wrote ['desk_ticket']. Both servers were stopped by their recorded listener PIDs. Screenshots on never-merged branch claude/issue-27054-r2-evidence @ 20360a7f0f (r2-1-related-lists-editor.jpg, r2-2-group-related-table.jpg, r2-3-unmatched-entry.jpg).",
      "gates": "Local, head 5c2b715589: dispatch-gates --commands --repo objectstack-ai/objectstack derived 34 families from 14 paths vs merge base 0d45c0e71, not stale. Each was run with its exit code captured before any pipe; 33 exited 0. 'pnpm --dir ui exec vitest run scripts/' exited 1, host-only: 2 select-affected-tests-wiring cases fail on 'mapfile: command not found' (macOS bash 3.2), and the rest were 'Test timed out in 15000ms' under batch load. Re-run alone (check-i18n-call-site-keys, check-vi-mock-inherit, markdown-test-inputs, check-hand-rolled-comment-mask): 'Tests 259 passed (259)'. The check-i18n-dead-keys real-tree case is NOT MEASURED locally (timeout); the gate itself exits 0 and lists none of the new keys. --ran reconciliation: 'Run reconciliation — 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN'. Extra, outside the derived list, all exit 0: pnpm check:changeset-fixed (roster under .changeset), pnpm check:i18n-stale-fill, and pnpm --dir ui run with check:new-line-citations (0 new), check:i18n-keys, check:i18n-drift (18 keys added, 0 en values changed) and check:i18n-dead-keys. node scripts/check-nul-bytes.mjs exit 0 plus a control-byte grep over the changed files: 0 hits. eslint --no-inline-config over the changed files: 0 new findings; the static-components error in ObjectSettingsPanel.tsx and the any / setState warnings are on main. Every locked command ran DECLARED UNLOCKED (no flock on this host). CI on PR #27303 at report time: 33 check-runs, in_progress. Already passed: Governed Surface Queue Guard, PR-body guards, Check PR Size, filter. Running: Lint, Type Check, Test Core, Live E2E UI, Build UI Deps, Check Changeset, Dogfood Regression Gate. CI convergence is the PM's.",
      "gates_ran": "the 34 lines of the --ran file as 'command :: exit N': 33 at exit 0 (node scripts/check-adr-0087-registration.mjs --base origin/main ... pnpm check:where-matcher), plus 'pnpm --dir ui exec vitest run scripts/ :: exit 1' (host gap, explained under gates)",
      "mcp_calls": "0",
      "api_writes": "3 — POST /repos/objectstack-ai/objectstack/pulls (draft PR #27303), POST /repos/objectstack-ai/objectstack/issues/27303/assignees (zhuangjianguo), and this os-dev-report comment on #27054 via post-stamped. All went through scripts/pm/with-fleet.sh, direct transport. Labels: 0 writes (the dispatch named none, and a changeset ships, so no skip-changeset). The git pushes (feature branch, and the evidence branch claude/issue-27054-r2-evidence) used the user's git credentials and are not REST writes.",
      "files_changed": [
        ".changeset/27054-related-lists-editor.md",
        "ui/packages/app-shell/src/utils/deriveRelatedLists.ts",
        "ui/packages/app-shell/src/views/studio-design/HighlightFieldsEditor.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectFormDesigner.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectGroupInspector.relatedListPlacement-27054.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectGroupInspector.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectSettingsPanel.relatedListsOrder-27054.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/ObjectSettingsPanel.tsx",
        "ui/packages/app-shell/src/views/studio-design/OrderedRowList.tsx",
        "ui/packages/app-shell/src/views/studio-design/RelatedListsEditor.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/relatedListVocabulary.ts",
        "ui/packages/i18n/src/namespaces/studio/en.ts",
        "ui/packages/i18n/src/namespaces/studio/zh.ts"
      ],
      "deviations": [
        "Comment shape: post-stamped now refuses a body whose first non-empty line is not a seat signature (landed in #27263 after this dispatch was written). So line 1 is the signature and 'os-dev-report' is line 2; every first-line recogniser skips one leading signature line. The signature's session is spelled without hyphens because the session pattern there admits no hyphen; the full id is in the JSON session field.",
        "PR body sent with gh api -F body=@FILE, not the dispatch's -f: with -f, gh sends the literal string '@FILE'. The body was read back afterwards, and only the trailing newline differs.",
        "File surface beyond the claim's named files: ui/packages/app-shell/src/utils/deriveRelatedLists.ts. The ruling's 'reuse the derivation' needed an additive includeHidden option and one exported entry matcher; the record page passes neither, so its output is unchanged. The new OrderedRowList.tsx and relatedListVocabulary.ts sit beside the editors. ObjectFormDesigner.tsx is part of Studio's field-group editor: related-table groups are no drop target. StudioDesignSurface.tsx gained one prop line (objectName to ObjectGroupInspector). #27055's files (RecordDetailView.tsx, plugin-detail) are untouched, but RecordDetailView imports deriveRelatedLists, so a textual conflict with #27055 is possible there.",
        "origin/main moved 7 commits after the one merge (0d45c0e71f) and before the PR opened. Not merged again: git merge-tree against it is clean, and its only overlap is the studio i18n en/zh files. AGENTS.md section 10 asks for a merge only on a conflict, a main-caused red or a queue removal.",
        "Every verify-lock command ran in DECLARED UNLOCKED mode (no usable flock on this macOS host).",
        "The ui scripts/ vitest family exits 1 on this host: the bash 3.2 mapfile gap (2 cases) plus load timeouts, which pass when re-run alone. One dead-keys real-tree case is NOT MEASURED locally.",
        "The changeset names @object-ui/console as well as app-shell and i18n, mirroring round 1's changeset (the console bundles app-shell).",
        "PR body carries a short 维护者速读 section, although the diff touches no governed surface."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b · reach: public door = Studio package publish (PUT /api/v1/meta/object/desk_customer?mode=draft, then the package publish) accepted and stored relatedLists ['nope','desk_ticket'] with no refusal. Contract: the relatedLists .describe() says 'An entry that matches no inbound lookup / master_detail field of this object is refused.' Seam: spec:ObjectSchema.relatedLists → runtime:packages/lint validate-related-list-refs (an unknown child is judged only under wholeCollection, unset on a package-closure publish). ⚠ The rule's header documents this boundary as deliberate (a downstream package's child may point in), so the seat may rule it by design or a .describe() wording fix. The Studio editor's pre-write check closes the Studio half. · dedupe words: relatedLists unknown child publish accepted package closure wholeCollection validate-related-list-refs",
        "carrier: #27055 · noted, not filed: fieldGroups[].relatedList has no runtime reader on main yet. Studio now authors it, and the record page that draws it is #27055 (in flight).",
        "carrier: none (承接者:无) · noted, not filed: the metadata-admin object designer (ObjectFormCanvas) also edits fieldGroups and has no related-table placement picker. The ruling named Studio's field-group editor, so it is left as it is (recorded in the PR's Acceptance notes)."
      ]
    }
  9. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff
    os-dev-report

    Round: patch round 1 · head 833a9d4ee8 · PR #27303 · reported 2026-10-28T16:44Z

    {
      "issue": 27054,
      "round": "patch round 1",
      "status": "done",
      "branch": "claude/issue-27054-related-lists-editor",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27303",
      "head": "833a9d4ee8",
      "session": "session_cdb7a717-def7-4e71-9988-f089fbaa18ff (local os-dev subagent of the epic PM session)",
      "premise_still_valid": true,
      "summary": "One row added to the objectui#12811 census in ui/packages/app-shell/src/console/__tests__/openPathObjectReaders-12811.test.ts. The new reader is packages/app-shell/src/views/studio-design/relatedListVocabulary.ts (useRelatedListCatalog reads metadata.objects), classified kind 'off' with why: Studio: an object's inbound relationships, read while its Settings relatedLists editor or a field group's related-table picker is mounted. 'off' is correct by the header's categories: the hook is mounted only by RelatedListsEditor (Studio object Settings) and ObjectGroupInspector (Studio form layout), never on an app's object-list open. Nothing else changed: no source file, and not the PR body.",
      "tests": "Head 833a9d4ee8. Control leg on 5c2b715589 before the edit: openPathObjectReaders-12811.test.ts 'Tests 1 failed | 2 passed (3)', the red being 'classifies every reader, and every row names a reader' (the CI failure, reproduced). After the edit, after the closure build (pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build, VERDICT command-exit 0): openPathObjectReaders-12811 + ObjectSettingsPanel* + ObjectGroupInspector* gave 'Test Files 9 passed (9) / Tests 70 passed (70)', EXIT 0. No ablation: the diff is test-only and changes no tested behaviour.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at 833a9d4ee8 derived 34 families from 15 paths vs merge base 0d45c0e71, the same 34 as round 2. It warns the tree is STALE: 18 commits behind origin/main, with 3 derivation inputs changed (package.json, scripts/pm/dispatch-gates.data.mjs, scripts/pm/record-recognisers.mjs). Not merged, per the patch scope. All 34 were run with exit codes captured; 33 exited 0. 'pnpm --dir ui exec vitest run scripts/' exited 1, host-only: 2 mapfile cases (bash 3.2) and 3 batch-load timeouts. check-hand-rolled-comment-mask and check-vi-mock-inherit re-run alone gave 'Tests 105 passed (105)'; the dead-keys real-tree case is NOT MEASURED locally. --ran: 'Run reconciliation — 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN'. CI on 833a9d4ee8 at report time: all 55 check-runs completed, 44 success and 11 skipped, 0 failures. Required contexts all green: Lint & Repo Gates, TypeScript Type Check, Test Core, Governed Surface Queue Guard, Lint UI, Type Check UI, Test UI (all 16 shards, 8/16 included), Build & E2E UI, Live E2E UI. Build Core was skipped by its selector. Locked commands ran DECLARED UNLOCKED.",
      "mcp_calls": "0",
      "api_writes": "1 — this os-dev-report comment on #27054 (post-stamped via with-fleet). One git push of 833a9d4ee8 with the user's credentials (not a REST write). Labels 0, PR body untouched.",
      "files_changed": [
        "ui/packages/app-shell/src/console/__tests__/openPathObjectReaders-12811.test.ts"
      ],
      "deviations": [
        "Worktree re-created with git worktree add --no-track -B (the local branch name still existed), from origin/claude/issue-27054-related-lists-editor at 5c2b715589, with its upstream set to that branch.",
        "Comment line 1 is the seat signature with the session spelled without hyphens, as in round 2: the signature pattern admits no hyphen.",
        "The tree is 18 commits behind origin/main and the gate derivation flags it STALE. Not merged: the patch scope was the census row only, and CI on the head is fully green."
      ],
      "open_questions": [],
      "out_of_scope_findings": []
    }
  10. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    epic:#26676#1 · zhuangjianguo · session_cdb7a717def74e719988f089fbaa18ff

    Review recorded on the PR: #27303 ## 席位核对 — ACCEPT at head 833a9d4ee8 (round 2 + one census-row patch) (round 2: the relatedLists editor and the field-group placement picker, the card's last item); ready + auto-merge handed to the queue on the maintainer's standing authorization for this epic. pm:dispatched stays until MERGED; the card closes by the PR's Fixes line. — epic PM session_cdb7a717-def7-4e71-9988-f089fbaa18ff, 2026-10-28T16:45Z


    Generated by Claude Code

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

Metadata

Metadata

Assignees

Labels

area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uienhancementNew feature or requestpm:epicpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions