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(flow designer): the entry-condition builder reads the object's published metadata, so for a draft-only object it offers no fields — only previous #11895

Closed

Description

@objectstack-fleet

Filing gate ① — product defect with a named location and a reproduction. reach: measured in the Studio task benchmark baseline (objectstack-ai/objectstack#22180, step 5): with the object repair_ticket still an unpublished draft, Studio → Automations → Start node Record updated on it → Entry condition builder lists only previous as a subject; the network log shows GET /api/v1/meta/object/repair_ticket → 404 while the draft exists. The author had to switch to Expression mode and type CEL (baseline code-shaped entry 8).

Who acts on it: objectui triage → the Studio owner. ⛔ Not a claim. Found by the cloud executor of objectstack-ai/objectstack#22137 and filed by the dispatching seat from its out_of_scope_findings after an objectui dedupe the executor could not run.

What happens

Building an app in Studio naturally means authoring the automation before the first publish. At that point every field the author just created is invisible to the flow's condition builder, because the builder asks for the published object.

Where it comes from (read in source)

useObjectFields (app-shell/src/views/metadata-admin/previews/useObjectFields.ts) loads fields with client.get('object', objectName) — no draft state, no package — and both ConditionBuilder and FlowNodeConfigField read their field lists through it. A draft-only object answers 404, so the list is empty (objectui main 82500a7).

Expected

Authoring surfaces read the draft overlay of the package being edited (the state=draft + package read the Data pillar already uses), so a draft object's fields are offered before its first publish.

Related

Environment

Studio task benchmark baseline run, 2026-10-08: objectstack 8fc50b76, objectui a58626c8 (console built at the .objectui-sha pin), fresh showcase boot, Chromium 141 at 1440×900; source re-read on objectui main 82500a7.

Duplicate check

Dedupe words: flow entry condition builder draft object fields · condition subject list only previous · flow designer reads published object not draft

Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from the objectstack-ai/objectstack#22137 report.


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    area:workflowApprovals and automation — the work that runs without a person driving it
    on Oct 8, 2026
  2. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Evidence moved here from objectstack-ai/objectstack#22191. The maintainer asked for this defect to be filed (useObjectFields reads only the published object). The session that filed it could not search this repository. objectstack-ai/objectstack#22191 is closed as a duplicate of this card. What it adds:

    • The gap is sharper on main than at the pin. objectui#11783 (57dbe34) made the object picker read GET /api/v1/meta/object?preview=draft. An author can now choose the draft object and still gets no fields for it.

    • The path. useFlowScope calls useObjectFields(scope.trigger?.objectName). On the 404 the hook answers fields: [] with notFound: true. That leaves only previous and the whole-record record ref (objectui#11789) in scope.

    • The same hook serves other Studio consumers on main:

      • FlowReferenceField and FlowNodeConfigField's record-node field pickers
      • ObjectFieldInspector, AppNavInspector
      • ViewColumnInspector, ViewVariantInspector, ObjectListViewInspector
      • PageBlockInspector, ActionDefaultInspector

      Each is presumably field-blind for a draft-only object too. That was measured only on the flow Start node, so the fix belongs in the hook, not in one caller.

    • The read to reuse. The validations panel on the same screen already reads the draft: GET /api/v1/meta/object/repair_ticket?state=draft&package=PKG answers 200 with the fields.

    • Still on main: unchanged on objectui main b6f8fc87 (2026-10-08).


    Generated by Claude Code

  3. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Grade (triage): pm:queue added. p2 stands: a draft-only trigger object offers no fields, so an author has to type CEL (measured).

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-08T05:57Z. ⛔ Not a claim, ⛔ not a dispatch.

  4. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 9
    Session: session_01DrKzdPdyLLBW3qpZ4vtk7z
    Account: huangyiirene
    Branch: claude/issue-11895-object-fields-draft
    Worktree: objectui-issue-11895
    Domain: domain:ui
    Seat: domain:ui#1
    File surface (line numbers on main 3683591, read 2026-10-08T06:31Z):

    • packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts: its object read (useObjectFields, about :49–:125). The read becomes the draft-overlaid object, falling back to the published one, per triage's direction 6053408714. The hook's signature and its result shape are unchanged.
    • The tests beside it, and .changeset/11895-*.md.

    ⛔ Not on it:

    • ConditionBuilder's operators and compile path (seat 3's objectui#11894, p1). Its claim declares this hook an adjacent region, and whoever lands second merges main.
    • The hook's consumers: useFlowScope, FlowNodeConfigField, FlowReferenceField, ObjectFieldInspector, AppNavInspector, ViewColumnInspector, ViewVariantInspector, ObjectListViewInspector (this seat's objectui#11799 remainder), PageBlockInspector and ActionDefaultInspector. They change nothing. If one needs a change, the dev reports it.
    • metadata-client.ts, packages/i18n/** and packages/components/src/ui/**.

    Any file outside this list: the dev reports it before opening the PR (stop on breach; explain in the report).
    Container & model: M, mode:subagent, model: opus (dispatch-gates --tier --repo objectstack-ai/objectui over this path: no path-derived mandate; default tier)
    Clause-②: no
    Responsibility: objectui app-shell Studio: the flow entry-condition builder and every field picker reading useObjectFields offer no fields for a draft-only object, so an author must type CEL | the platform path is right: the draft-overlaid read (?preview=draft, objectui#11783's picker) answers the draft's fields; the published read 404s by design | every author building automations before the first publish
    Thread-read: 6053408714
    Serial constraints cleared: none blocking.

    • area:workflow in flight: none on the board. objectui#11905 (p2, area:workflow) is queued, not in flight.
    • Adjacent in-flight claim: seat 3's objectui#11894 fences useObjectFields out of its surface and names this card as its owner (its claim's ⛔ Not on it line).
    • Open objectui PRs: none touches useObjectFields.ts. Read 2026-10-08T06:31Z.
    • This seat: dispatch is parallel, batch 3 working devs, on the maintainer's word in this session's chat (「改为并行派发,batch 3」, 「继续派发」), paced by the shared hourly write budget.
      • objectui#11862's dev has finished (PR objectui#11906), which freed this slot.
      • Working with this card: objectui#11872's dev and objectui#11799's (remainder).

    Why Clause-②: no: a client read inside one hook. Its exports, signature and result shape stay as they are, and the server's answers are unchanged.

  5. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11895,
    "status": "blocked",
    "branch": "claude/issue-11895-object-fields-draft",
    "pr": null,
    "session": "session_01DrKzdPdyLLBW3qpZ4vtk7z (subagent mode: the parent's id, as the claim names it)",
    "premise_still_valid": true,
    "summary": "The in-fence fix is done and pushed (branch tip 3f01d03, no PR): useObjectFields now reads through client.withPreviewDrafts(true).get('object', name), one request, with the signature, the override short-circuit, the result shape and the not-found sentence unchanged. There is a new pin file beside the hook (7 tests) and the changeset .changeset/11895-object-fields-draft.md (@object-ui/app-shell patch). BLOCKED on a fence breach, which the claim says to report before opening the PR. 39 existing test files outside the fence go red with the fix: their metadata-client test doubles define only get (37 files) or a withPreviewDrafts that returns only a list (2 files), so the hook throws 'client.withPreviewDrafts is not a function' inside its effect and the mounted tree unmounts. All 39 are green with the fix ablated and red with it in place, so the doubles are the only cause. 9 of them are ConditionBuilder..test.tsx, which sit inside objectui#11894's claimed surface ('the tests beside them'). The one thing only the PM can resolve: extend this claim's fence to those 39 test doubles (one mechanical change each, below) and sequence it with objectui#11894. Nothing outside the fence was edited.",
    "tests": {
    "head": "Gate union on local HEAD 3f01d03 (git rev-parse --short HEAD after merging origin/main; the remote tip is 3f01d03). The family sweep before the merge ran on a9ac12d, which holds the same hook and pin blobs.",
    "gates": [
    "os-verify-lock -c 'pnpm --workspace-concurrency=2 --filter @object-ui/app-shell^... build' (pre-merge tree) -> exit 0 -> 'VERDICT command-exit 0'",
    "pnpm --filter @object-ui/app-shell type-check (echoes 'tsc --noEmit && tsc -p tsconfig.test.json') @3f01d03 -> exit 0 -> 'VERDICT command-exit 0'; tsc -p tsconfig.test.json --listFilesOnly counts the new pin file (1 hit)",
    "pin: vitest run previews/useObjectFields.draftOverlay-11895.test.tsx -> exit 0 -> 'Test Files 1 passed (1)', 'Tests 7 passed (7)'",
    "family sweep: every test file that statically imports useObjectFields.ts, transitively (489 files on a9ac12d; a reverse import-graph walk over packages/ and apps/, scratchpad tool, with app aliases resolved to src) in 4 lock holds -> exit 0 / 1 / 1 / 1 -> 'Test Files 148 passed (148)'; 'Test Files 3 failed | 117 passed (120)'; 'Test Files 25 failed | 95 passed (120)'; 'Test Files 9 failed | 92 passed (101)' = 37 red files, every one a test double (see the reverse check)",
    "merge delta @3f01d03: the 26 related test files main added or changed since the base, plus viewCacheInvalidation.guard.test.tsx and the pin -> exit 1 -> 'Test Files 2 failed | 26 passed (28)': HookDefaultInspector.reach-11820 and ObjectHooksPanel.newHookTarget-11820, the same double shape ('client.withPreviewDrafts is not a function')",
    "guards: the order's glob ('
    .guard.test.*' under metadata-admin and studio-design) matches 0 files; control: git ls-files over app-shell lists 1156 test files, and the view-cache guard lives at packages/app-shell/src/views/viewCacheInvalidation.guard.test.tsx. It ran in the merge-delta hold and passed, as did StudioDesignSurface.accessGuard and pillarNavGuard",
    "pnpm check:control-bytes -> exit 0 -> 'check-control-bytes: OK (scanned 7980 tracked text file(s); skipped 85 binary)'",
    "pnpm check:new-line-citations -> exit 0 -> 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'",
    "pnpm check:changeset-claims -> exit 0 -> 'No pending changeset names a file this change touches.'",
    "pnpm check:pending-changeset-literals -> exit 0 -> 'No test source names a pending changeset.'",
    "re-derived from the actual diff (a new vi.mock test file, a src change and a changeset): pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape / check:test-path-roots / check:metadata-write-doors / check:unreferenced-sources -> all exit 0 -> each prints its OK line; node scripts/check-changeset-presence.mjs -> exit 0 -> '2 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'",
    "lint, narrowed: eslint --format json over the 2 touched ts/tsx files from packages/app-shell, as its lint script runs (eslint .) -> exit 0 -> 2 files, 0 errors, 2 warnings (react-hooks/set-state-in-effect and no-explicit-any). Linting the base blob through stdin gives the same 2 rule ids, so the change adds no lint findings. Population: the 2 files, both reported in the JSON. Invariance: eslint.config.js declares no parserOptions.project or projectService, so the linting is not type-aware and this diff cannot move the verdict on any untouched file"
    ],
    "reverse_check": "Run with the fix committed (a9ac12d), through objectstack scripts/ablation-replace.mjs in wrap mode. The anchor 'client / .withPreviewDrafts(true) / .get' was replaced by the published-only 'client / .get'. Anchor x1 -> x0, replacement x0 -> x1, blob f6b903e3 -> 72d5be5f, on-disk grep count 0. Expected beforehand: the 3 draft pins red, the 4 controls green, all 37 double files green. Observed: 'Test Files 1 failed | 37 passed (38)', 'Tests 3 failed | 294 passed (297)'. The red ones were the draft-only hook pin, the overlay pin and the flow entry-condition pin; the published control, the not-found pin, the override pin and the flow published control stayed green. Restore proven: blob after restore f6b903e3 == HEAD blob, git diff HEAD empty. A second leg @3f01d03 over the 2 merge-delta files ran under the same mutation: 'Test Files 2 passed (2)', restored to the same blob, git diff HEAD empty. ablation-dist-preflight does not apply, because the pins and consumers import the hook by relative path from src.",
    "deviations": [
    "No draft PR was opened. The fence breach (39 test files) stops it, per the claim's 'Any file outside this list: the dev reports it before opening the PR'. git push ran (the branch is the hand-off); pr_create was not spent.",
    "The order's gate lead 'git grep -l useObjectFields -- .test.' (96 files) misses most of the breakage. The red files mount a consumer with a client double and never name the hook. So the family was re-derived as transitive importers of the hook (489 files), not that grep.",
    "The closure build ran on the pre-merge tree. The merge moved plugin-grid sources (ImportWizard, import coercion) inside the closure, and they were not rebuilt. type-check @3f01d03 read their pre-merge dist. Declared narrowing; CI rebuilds.",
    "The 37-file sweep ran before the merge (a9ac12d and its pre-commit equivalent). After the merge, only the files main added or changed were re-run (the merge delta). The 37 broken files are untouched by main (intersection 0)."
    ],
    "files_changed": [
    "packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts (the object read only: withPreviewDrafts(true) plus a comment naming objectui#11895; header doc paragraph)",
    "packages/app-shell/src/views/metadata-admin/previews/useObjectFields.draftOverlay-11895.test.tsx (new; 7 pins over the REAL MetadataClient on a stub fetch that answers like the framework's item read)",
    ".changeset/11895-object-fields-draft.md (@object-ui/app-shell patch)"
    ],
    "hypotheses": {
    "H1": "HOLDS. At @objectstack 17.7.0 the runtime dispatcher's object item read sets protocolFirst for an admitted ?preview=draft. It calls protocol.getMetaItem({ previewDrafts: true }), and when that has no item it falls back to the registry, which is the published object. RestServer's uncached arm passes previewDrafts to getMetaItem as well. Its previewDrafts arm serves the draft row when one exists and otherwise falls through to the active read ('the render path must degrade to the published value, not error'), and a name with nothing behind it is a 404. Pinned in objectstack packages/objectql/src/protocol-meta.test.ts: 'returns the draft when previewDrafts and a draft exists' and 'falls back to active when previewDrafts but no draft exists (no no_draft 404)'. So there is no separate fallback read. The control pin counts 1 request for a published object, 1 for a draft-only object and 1 for a name with neither.",
    "H2": "HOLDS. Callers without override: ActionDefaultInspector, AppNavInspector, ConditionBuilder, FlowNodeConfigField (FieldValueMapField), FlowReferenceField, ObjectFieldInspector (2 calls), PageBlockInspector, ViewColumnInspector, useFlowScope, ObjectListViewInspector, plus ViewVariantInspector and FieldsListEditor, where override is optional. All of them live under views/metadata-admin or views/studio-design. The one runtime host is ViewConfigPanel. It always passes objectFieldsOverride (mapObjectFields(objectDef)) to ViewVariantInspector, which hands it to the hook and to FieldsListEditor. Its ConditionWidget mounts ConditionBuilder with no objectName, so the hook stays idle. A second layer on the server: mayReadPendingDrafts admits only a system caller or a holder of studio.access, setup.access or manage_metadata. Anyone else gets the published answer as if the switch had not been sent, so an end user cannot see draft fields through this read.",
    "H3": "HOLDS. Before the fix a draft-only object reached notFound, because the 404 maps to null; the ablation shows this as red draft pins. After the fix, the pin 'a name with neither a draft nor a published object is still not found' asserts fields [], the error equal to t('engine.form.objectNotFound', locale) and 1 request. The draft-only pin asserts error null and the draft's fields.",
    "H4": "FALSIFIED, and in the easy direction: preview=draft needs no package. In getMetaItem the previewDrafts arm calls findServedOverlayRow. With no packageId, servedOverlayRowCandidates yields one candidate with no package filter ('with none, any row'), so a package-bound draft is found. The validations panel's ?state=draft&package=PKG is the strict draft read, a different door. The hook has no package in hand and needs none."
    }
    },
    "mcp_calls": "0",
    "api_writes": "1 — POST /repos//issues/11895/comments (this report, through scripts/pm/post-stamped.mjs and the fleet-write relay). Not a REST write: git push of claude/issue-11895-object-fields-draft (the empty probe, then 3683591..a9ac12d, then a9ac12d..3f01d03). Reads: REST GETs of the issue and comments of objectui#11895, objectui#11894 (its claim) and objectui#11862 (report format only).",
    "open_questions": [
    {
    "question": "The fix needs 39 test files outside the fence: their metadata-client doubles predate a client read other than get('object', name). Extend the fence to them?",
    "options": [
    "A. Extend this claim's surface to the 39 doubles. Shape 1 (37 files): add withPreviewDrafts returning the same double, so the test's own get stub keeps answering. Shape 2 (FlowReferenceField.objectPicker-11783 and ObjectFieldInspector.objectPicker-11783): the object their withPreviewDrafts returns also carries get. No assertion changes. 9 of the files (ConditionBuilder.test.tsx, .celGate, .clientMountRoots, .contextSubjects, .mountRoots, .mountScope, .placeholderRoots, .referenceValue, .subjectVocabulary) sit inside objectui#11894's 'tests beside them', so whoever lands second merges main, or this waits for objectui#11894.",
    "B. Make the hook tolerate a client without the method (call withPreviewDrafts only when it exists, otherwise read the published object). No test files change.",
    "C. Hold this card until objectui#11894 lands, then do A without the overlap."
    ],
    "recommendation": "A (or C if the seat prefers no overlap with objectui#11894). B is a consumer-side tolerance that exists only for incomplete doubles. MetadataClient declares withPreviewDrafts, and a silent published fallback would hide exactly this defect from any real client that lost the method (AGENTS.md #0.1, contract-first). The full list of 39 files is in this report's out_of_scope_findings carrier line; the per-file doubles are each test's own vi.mock of ../useMetadata."
    }
    ],
    "out_of_scope_findings": [
    "carrier: this claim, if the fence is extended (open_questions A). The 39 files whose doubles need the change: metadata-admin ResourceEditPage.defaultGate, ResourceEditPage.requiredInGatedSection-6900, capabilityGateChannel-7234; inspectors ConditionBuilder (.test, .celGate, .clientMountRoots, .contextSubjects, .mountRoots, .mountScope, .placeholderRoots, .referenceValue, .subjectVocabulary), FlowReferenceField.objectPicker-11783, HookDefaultInspector.flag.i18n-10448, HookDefaultInspector.rosterFailure, HookDefaultInspector.reach-11820, ObjectFieldInspector.objectPicker-11783, PageBlockInspector (.colorLabelling, .definitionListItems-8279, .i18n, .pageHeaderBreadcrumb-11173, .retiredBlockProps, .sectionName), ViewColumnInspector.rosterFailure, ViewColumnInspector.rosterPending, ViewVariantInspector (.celGate, .homeGate, .requiredInGatedSection-6900), unknownValueFlag.i18n-9652; previews flowFieldChrome.i18n-10862-s4; studio-design DataPillar.designerRegistryPopulated, ObjectHooksPanel.celGate, ObjectHooksPanel.newHookTarget-11820, StudioDesignSurface (.autosaveSwitch-11232, .listViewInspector-11823, .navEntryIdentity-11774, .selectionLeafScope, .studioCanvasLeaf, .zhPillarNames-11801). Noted, not filed: it is this card's own collateral, not a finding.",
    "carrier: 承接者:无 · noted, not filed. ConditionBuilder calls useObjectFields(objectName) even when a fields prop is supplied (fields = fieldsProp ?? hookFields), so a mount that passes both sends a field read whose answer is discarded. This is an observation (a wasted read, no wrong answer), outside classes a/b/c; objectui#11894's fence excludes the field source."
    ]
    }

  6. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Seat ruling on the dev's open question (report 6055355527) · claim amendment

    domain:ui seat 1 · session_01DrKzdPdyLLBW3qpZ4vtk7z · read on main d52d908, 2026-10-08T07:54Z.

    The question: the fix turns 39 existing test files red, and they are outside the fence. Ruled A

    • Measured: each of the 39 files mocks ../useMetadata with a metadata-client double that has no withPreviewDrafts (37 files), or one whose withPreviewDrafts returns only list (2 files). The hook then throws client.withPreviewDrafts is not a function. All 39 are green with the fix ablated and red with it in place, so the doubles are the only cause.
    • Why A:
      • The doubles predate the client's declared withPreviewDrafts. Completing them is the honest fix, one mechanical change per file with no assertion changes.
      • B (the hook calls the method only if it exists, else reads the published object) is a consumer-side tolerance that exists only for incomplete doubles. It would silently hide this card's defect from any real client that lost the method.
      • C (hold the card until objectui#11894 lands) idles a finished fix for an unknown time, for an overlap that is additive.

    Claim amendment

    The file surface of claim 6053914459 gains the 39 test files the report lists (its carrier line), with only their metadata-client doubles changed:

    • Shape 1 (37 files): add withPreviewDrafts returning the same double, so the test's own get stub keeps answering.
    • Shape 2 (FlowReferenceField.objectPicker-11783, ObjectFieldInspector.objectPicker-11783): the object their withPreviewDrafts returns also carries get.

    ⛔ No assertion, fixture or test name changes. The rest of the claim stands, Clause-②: no included.

    The overlap with seat 3's objectui#11894 (p1, in flight, no PR yet)

    • Nine of the files (ConditionBuilder.test.tsx and its .celGate, .clientMountRoots, .contextSubjects, .mountRoots, .mountScope, .placeholderRoots, .referenceValue and .subjectVocabulary siblings) are among objectui#11894's "tests beside them".
    • This card's edit there is additive, one property in each file's vi.mock double. Whoever lands second merges main.
    • A note goes on objectui#11894 in this pass, so its dev expects it.

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11895,
    "status": "done",
    "branch": "claude/issue-11895-object-fields-draft",
    "pr": "#11952",
    "session": "session_01DrKzdPdyLLBW3qpZ4vtk7z (subagent mode: the parent's id, as the claim names it)",
    "premise_still_valid": true,
    "summary": "Draft PR objectui#11952 (first line Fixes #11895, then the Clause-② line; head 811fef2) lands seat ruling A on claim amendment 6055387753. useObjectFields reads the draft-overlaid object through client.withPreviewDrafts(true).get('object', name), one request, with the signature, the override short-circuit, the result shape and the not-found sentence unchanged, so a draft-only object's fields reach every picker, the flow entry-condition builder included. The 39 metadata-client test doubles the report listed each gain the one member they lacked. 37 have withPreviewDrafts returning the same double; in StudioDesignSurface.autosaveSwitch-11232 it is a vi.fn, because its beforeEach calls mockClear on every member. The 2 objectPicker-11783 doubles carry get on the object withPreviewDrafts returns. No assertion, fixture or test name changed; two double comments that named the read were updated. objectui#11894 had not landed at the last merge, and no new ConditionBuilder test appeared. The 9 ConditionBuilder.*.test.tsx edits are one additive property each, which the PR body names.",
    "tests": {
    "head": "Gate union on local HEAD 811fef2 (git rev-parse --short HEAD after merging origin/main f1781be; the pushed tip and the PR head are the same sha).",
    "gates": [
    "os-verify-lock -c "pnpm --workspace-concurrency=2 --filter '@object-ui/app-shell^...' build" @811fef2 -> exit 0 -> 'VERDICT command-exit 0'",
    "pnpm --filter @object-ui/app-shell type-check (echoes 'tsc --noEmit && tsc -p tsconfig.test.json') @811fef2, after that rebuild -> exit 0 -> 'VERDICT command-exit 0'; --listFilesOnly counts the pin file in the test program",
    "family sweep @811fef2: every test file importing useObjectFields.ts directly or transitively, re-derived on this HEAD by a reverse import-graph walk (504 files), in 6 lock holds -> exit 0 x6 -> 'Test Files 84 passed (84)' x6; Tests 713 + 712 + 615 + 1017 + 983 + 482 = 4522 passed. It includes the 39 doubles, the pin, FlowNodeConfigField.entryCondition, FlowNodeInspector.entryCondition, StudioDesignSurface.accessGuard and pillarNavGuard",
    "view-cache guard + pin @811fef2: vitest run viewCacheInvalidation.guard.test.tsx useObjectFields.draftOverlay-11895.test.tsx -> exit 0 -> 'Test Files 2 passed (2)', 'Tests 16 passed (16)'",
    "pnpm check:control-bytes -> exit 0 -> 'check-control-bytes: OK (scanned 7993 tracked text file(s); skipped 85 binary)'",
    "pnpm check:new-line-citations -> exit 0 -> 'VERDICT new-cross-file-line-citations: 0 new citation(s), enforcement report-only -> exit 0'",
    "pnpm check:changeset-claims -> exit 0 -> 'No pending changeset names a file this change touches.'",
    "pnpm check:pending-changeset-literals -> exit 0 -> 'No test source names a pending changeset.'",
    "re-derived from the diff (41 test/src files that carry vi.mock doubles, plus a changeset): pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape / check:test-path-roots / check:metadata-write-doors / check:unreferenced-sources -> all exit 0 -> each prints its OK line; node scripts/check-changeset-presence.mjs -> exit 0 -> '41 source file(s) of 1 released package(s) changed, and this change declares 1 changeset(s)'",
    "lint, narrowed: eslint --format json over the 41 touched ts/tsx files, from packages/app-shell as its lint script runs -> exit 0 -> 41 files, 0 errors, 5 warnings. Every pre-existing file has the same error/warning counts as its merge-base blob (f1781be, linted through stdin); the new pin file has 0/0. Population: the 41 files, all reported in the JSON. Invariance: eslint.config.js declares no parserOptions.project or projectService, so the linting is not type-aware",
    "after the union: main moved to f3a0488 (no ConditionBuilder or useObjectFields file touched). Its 6 new or changed test files (StudioDesignSurface.interfacesCreatePage-11823, interfaceCreate, ObjectValidationsPanel x4) ran in an uncommitted merge, which was then aborted -> exit 0 -> 'Test Files 6 passed (6)'. HEAD stayed at 811fef2 and the tree is clean",
    "CI on 811fef2 at report time: 42 check runs, 9 success, 3 skipped, 30 in_progress, 0 failed (in_progress)"
    ],
    "reverse_check": "@811fef2, with the fix committed, through objectstack scripts/ablation-replace.mjs in wrap mode. The anchor 'client / .withPreviewDrafts(true) / .get' was replaced by the published-only 'client / .get'. Anchor x1 -> x0, blob f6b903e3 -> 72d5be5f, on-disk grep count 0. Expected beforehand: 3 red, 4 green. Observed: 'Tests 3 failed | 4 passed (7)'. The red ones were the draft-only hook pin, the overlay pin and the flow entry-condition pin; the published control, the not-found pin, the override pin and the flow published control stayed green. Restore proven: blob after restore == HEAD blob f6b903e3, git diff HEAD empty. The first round's leg (a9ac12d) ran the 39 double files under the same mutation, and all were green. The doubles themselves need no reverse check; they are test-only.",
    "deviations": [
    "The double member is a this-returning method (withPreviewDrafts() { return this; }), not an arrow that names the double. A self-reference inside a const initializer is TS7022 under strict, and the method also follows a test that reassigns get on the same object (ViewVariantInspector.requiredInGatedSection-6900). In StudioDesignSurface.autosaveSwitch-11232 it is vi.fn((): unknown => mockClient), because that suite mockClears every member. The first sweep caught this ('fn.mockClear is not a function'), and a follow-up commit fixed it.",
    "Two double comments ('useObjectFields -> client.get' in DataPillar.designerRegistryPopulated and ObjectHooksPanel.celGate) now name the read as client.withPreviewDrafts(true).get. They sit inside the doubles; no assertion is touched.",
    "main was merged twice in this round: f7f8e2b, before the double edits, and 811fef2, before the final union. The union ran on 811fef2. main's later move (f3a0488) was probed, not merged, so the union HEAD is the PR head.",
    "The gate lead 'git grep -l useObjectFields -- .test.' was replaced by the transitive-importer family, as in the first report."
    ],
    "files_changed": [
    "packages/app-shell/src/views/metadata-admin/previews/useObjectFields.ts (the object read only)",
    "packages/app-shell/src/views/metadata-admin/previews/useObjectFields.draftOverlay-11895.test.tsx (new, 7 pins)",
    ".changeset/11895-object-fields-draft.md (@object-ui/app-shell patch)",
    "39 test doubles under packages/app-shell/src/views/: metadata-admin ResourceEditPage.defaultGate, ResourceEditPage.requiredInGatedSection-6900, capabilityGateChannel-7234; inspectors ConditionBuilder (.test, .celGate, .clientMountRoots, .contextSubjects, .mountRoots, .mountScope, .placeholderRoots, .referenceValue, .subjectVocabulary), FlowReferenceField.objectPicker-11783, HookDefaultInspector.flag.i18n-10448, .rosterFailure, .reach-11820, ObjectFieldInspector.objectPicker-11783, PageBlockInspector (.colorLabelling, .definitionListItems-8279, .i18n, .pageHeaderBreadcrumb-11173, .retiredBlockProps, .sectionName), ViewColumnInspector.rosterFailure, .rosterPending, ViewVariantInspector (.celGate, .homeGate, .requiredInGatedSection-6900), unknownValueFlag.i18n-9652; previews flowFieldChrome.i18n-10862-s4; studio-design DataPillar.designerRegistryPopulated, ObjectHooksPanel.celGate, ObjectHooksPanel.newHookTarget-11820, StudioDesignSurface (.autosaveSwitch-11232, .listViewInspector-11823, .navEntryIdentity-11774, .selectionLeafScope, .studioCanvasLeaf, .zhPillarNames-11801). git diff over them: 39 files, one member each (plus the 2 comments)"
    ],
    "hypotheses": "Unchanged from report 6055355527. H1 holds: one request, and the framework's getMetaItem previewDrafts arm falls back to the active object. H2 holds: no runtime caller reaches the fetch, and the server answers non-authoring callers the published object. H3 holds and is pinned both ways. H4 is falsified in the easy direction: preview=draft needs no package."
    },
    "mcp_calls": "0",
    "api_writes": "3 relay writes in all, 2 in this round: POST /repos/objectstack-ai/objectui/pulls (pr_create, draft forced, objectui#11952, read back 9490 bytes identical) and POST /repos//issues/11895/comments (this report). The first round's report comment 6055355527 was the third. Not a REST write: git push of claude/issue-11895-object-fields-draft (3f01d03..82f85ab, then 82f85ab..811fef2, through 4ffbafe). Reads: REST GETs of comment 6055387753, PR 11952 and its check runs.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: 承接者:无 · noted, not filed. ConditionBuilder calls useObjectFields(objectName) even when a fields prop is supplied, so a mount that passes both sends a field read whose answer it discards. It is a wasted read, not a wrong answer, outside classes a/b/c. It is recorded in the PR's Acceptance notes."
    ]
    }

  8. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Review: ACCEPT — PR objectui#11952 (head 811fef2) · queueing held for a red main

    domain:ui seat 1 · session_01DrKzdPdyLLBW3qpZ4vtk7z · reviewed against the PR's file list and diff hunks, git merge-tree, its check runs and the dev's reports 6055355527 (round 1) and 6057121776 (round 2), read 2026-10-08T09:44Z.

    • Shape: draft → main. The first line is Fixes #11895, and Clause-②: no starts the next line.
    • Scope: 42 files, +303/−22, inside claim 6053914459 as amended by the ruling 6055387753.
      • useObjectFields.ts: the object read goes through client.withPreviewDrafts(true).get('object', name), one request. The signature, the override short-circuit, the result shape and the not-found sentence are unchanged.
      • A new pin file (7 tests) and an @object-ui/app-shell patch changeset.
      • The 39 test doubles: each gains only the member it lacked. Read on the diff: no test file deletes more than one line, and that line is a one-line double rewritten with the member. No assertion, fixture or test name changes. Two double comments that named the read are updated.
    • Clause-② re-read: no +export line, and no prop, type member or accepted input changes. no holds.
    • H1–H4, as measured:
      • H1: preview=draft answers the draft when one exists and the published object otherwise, in one request (pinned in the framework's own test).
      • H2: no runtime caller reaches the fetch, and the server answers a non-authoring caller with the published object anyway.
      • H3: not-found now fires only for a name with neither a draft nor a published object.
      • H4: no package is needed.
    • Tests:
      • Local union on 811fef2 (with main f1781be merged in): the full transitive family of the hook, re-derived on this HEAD, is 504 files and 4,522 tests, green across 6 lock holds. That includes the 39 doubles, the pin and the flow entry-condition suites. The type-check, the view-cache guard and the derived check:* set are green.
      • main's later move to f3a0488 (6 new or changed test files) was probed in an uncommitted merge and is green.
      • Reverse check: with the published-only read restored, 3 failed and 4 passed, as predicted. The draft-only, overlay and flow entry-condition pins went red, and the published, not-found and override controls stayed green. The restore was proven by blob hash.
    • Overlap: an in-memory git merge-tree against main f3a0488 is clean. objectui#11894 (seat 3) had not landed. The 9 ConditionBuilder double edits are one additive property each, as the notice on that card says.
    • CI at 811fef2: Bundle Analysis is red, and it is not this PR's failure. It is red on main from 19a7348 onward (objectui#11937, seat 2's p0 fix-forward). The rest is converging.
    • Queueing: held until Bundle Analysis is green on main again. The hook's module may sit on an eager path (the runtime view-config panel imports an inspector that uses it), and this seat does not stack bytes onto a red base. Then CI is re-read on a fresh merge ref, and the PR is queued with its assignee in one relay write.
    • Noted, not filed: ConditionBuilder calls the hook even when a fields prop is supplied, so a mount that passes both sends a read whose answer it discards. That is in the PR's Acceptance notes.

    Generated by Claude Code

  9. objectstack-fleet commented on Oct 8, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11952 → main 2fec2e0

    domain:ui seat 1 · session_01DrKzdPdyLLBW3qpZ4vtk7z · verified 2026-10-08T10:57Z.

    • Merged through the merge queue at 2026-10-08T10:53:18Z.
      • It was queued by relay request fw-20261008T103424Z-143148, after objectui#11956 (3c888c6) had turned main's Bundle Analysis green again.
      • CI on head 811fef2: 40 of 44 check runs passed, and the 3 skipped are the expected ones. The one red is Bundle Analysis: the base-branch failure named in ACCEPT 6057148685. That check does not run in the merge group.
      • check-governed-merges.mjs, re-run on the final file list: not governed, +303/−22.
    • Content check:
      • 2fec2e0 is one squash commit on parent 077d198 (objectui#11938, queued just ahead of it). It changes 42 files, +303/−22, the same as the PR.
      • All 42 files are blob-identical to the reviewed head 811fef2, and the whole diff's -U0 patch-id matches (67bf5bec…).
    • Card: closed completed by the PR's Fixes. pm:dispatched comes off right after this note, and is read back.
    • On record:
      • Studio's field pickers read the draft-overlaid object in one request. So an object that exists only as a draft offers its fields, and the flow Start node's entry-condition builder lists them as well as previous.
      • "Not found" now fires only for a name with neither a draft nor a published object.
      • Not filed: ConditionBuilder sends a read it discards when a fields prop is also supplied. That is in the PR's Acceptance notes.

    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:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions