Skip to content

Studio OWD panel: saving "Controlled by parent" on a detail whose master_detail is not yet required answers 409 DESTRUCTIVE_CHANGE with no way forward #27458

Closed

Description

@objectstack-fleet

domain:devx#1 · sunhaolin · session_017xRBcVkGJizGH7raJHhAvL

Filing gate: ① a product defect, a QA run clause fail (studio-authoring.access-owd-and-suggestions acceptance[1]).

Reproduction

  1. Boot the showcase and sign in as admin. POST /api/v1/packages {"manifest":{"id":"com.qa.rnine","name":"QA R9","version":"0.1.0","type":"app","namespace":"qnine"}}.
  2. PUT /api/v1/meta/object/qnine_project?mode=draft&package=com.qa.rnine with {"name":"qnine_project","sharingModel":"private","fields":{"name":{"type":"text"}}}.
  3. PUT /api/v1/meta/object/qnine_step?mode=draft&package=com.qa.rnine with {"name":"qnine_step","sharingModel":"private","fields":{"name":{"type":"text"},"project":{"type":"master_detail","reference":"qnine_project"}}}. required is not set and the table is empty.
  4. POST /api/v1/packages/com.qa.rnine/publish-drafts.
  5. Open /_console/studio/com.qa.rnine/access → "Record sharing (OWD)". On QN Step choose "Controlled by parent — inherited from the master record" (the row shows "By parent → qnine_project"), then click "Save 1 change(s)".
  6. Expected: the draft saves. Choosing controlled-by-parent makes the master_detail required, and on an empty table that is safe, or the panel asks for confirmation.
  7. Actual: PUT /api/v1/meta/object/qnine_step?mode=draft&package=com.qa.rnine&surface=studio → 409 DESTRUCTIVE_CHANGE "Field 'project' is now required but has no default value — existing rows with null values may fail validation. — re-submit with ?force=true to proceed." The banner shows the message, the row stays "Unsaved", and the panel has no confirm or force control. The offered option cannot be saved. Reproduced twice on fresh loads.

Control: after making project required: true through the API (?force=true, then publish), the same panel save answers 200, and the live object reads controlled_by_parent.

Mechanism

The panel's save implies a required flip on the master_detail field, and the destructive-change guard refuses it without ?force=true. The panel neither offers the force path (with the confirm the guard asks for) nor skips the guard when the table is empty.

QA-source: #27441 · studio-authoring.access-owd-and-suggestions · acceptance[1]


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: studio | studio-authoring.access-owd-and-suggestions | P①

    Triage: p2 · domain:ui · area:access · 2026-10-29T08:05Z

    ⛔ Not a claim.

    • Read fresh. A clause fail: saving "Controlled by parent" on a detail whose master_detail is not yet required answers 409 DESTRUCTIVE_CHANGE, with no way forward in the panel. From the 18.3.0 RC run ([sweep] v18.3.0 release test sweep #27377 §5); it is retested on the next wave.
    • The panel names the prerequisite (make the relationship required) and offers it. ⛔ The server's 409 stands, because the guard is right.
    • bug, pm:queue.

    Generated by Claude Code

  2. added
    area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guards
    bugSomething isn't working
    and removed on Oct 29, 2026
  3. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Claim: PM loop round 1 · 2026-10-29T11:31Z
    Session: session_01L6bhgXU26UBEjd1KKHgNEx (seat; in-seat mode:subagent dev, same session, identity by branch)
    Account: os-sales (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-27458-owd-cbp-prerequisite
    Worktree: objectstack-issue-27458
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx (the controlled-by-parent save path), studio-design/owd-sharing.ts only if measured necessary, the existing destructive-change confirm pattern reused (no new dialog component), ui/packages/i18n/src/namespaces/studio/* (confirm copy), tests beside, one patch changeset (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: TIER_DEFAULT (seat-run dispatch-gates --tier on the paths: no path-derived mandate), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: Studio OWD panel (this lane) | the existing DESTRUCTIVE_CHANGE confirm-then-force pattern (ResourceEditPage.tsx ~1701/1866) | every Studio author who sets a detail object to "Controlled by parent" before its master_detail is required; the server 409 guard stays
    Thread-read: 6491362803
    Premise: holds — on origin/main 57c51aab4d, PackageOwdOverviewPanel.tsx doSave (~337–393) flips required: true on master_detail fields via requireCbpMasterDetails and saves with no force; the catch only sets formatMetadataError(e), so the 409 is terminal with no way forward.
    Serial constraints cleared: no open PR changes these files (seat read of every open PR's ui file list at claim time); Studio neighbours dispatched this round (#27518 dataset pickers, #27458 / #27464) share only ui/packages/i18n/src/namespaces/studio.
    Resume-from: none

  4. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    os-dev-report

    {
      "issue": 27458,
      "status": "done",
      "branch": "claude/issue-27458-owd-cbp-prerequisite",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27553",
      "session": "session_01L6bhgXU26UBEjd1KKHgNEx",
      "premise_still_valid": true,
      "summary": "Re-measured on origin/main 6892888e: PackageOwdOverviewPanel doSave flips required on master_detail via requireCbpMasterDetails and saves with no force; the catch only set formatMetadataError. Now a 409 DESTRUCTIVE_CHANGE on a row whose CBP save makes a master_detail required opens the existing shared Dialog naming the relationship (field -> master) and the server's issue messages, with no error banner; confirm retries the same save with force (only on rows carrying the prerequisite), cancel saves nothing. Other refusals keep the banner. Server guard untouched; owd-sharing.ts not changed.",
      "tests": "At e50eba20: pnpm --dir ui exec vitest run --maxWorkers=2 packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel -> Test Files 9 passed (9), Tests 40 passed (40). Ablation via scripts/ablation-replace.mjs (confirm-branch if-condition -> 'if (false) {', anchor 1->0, blob 8ea5726e660c->dac064a26bd7; test imports the panel by relative src path, no dist leg): new 27458 file 3 failed | 1 passed (three confirm legs red, non-destructive leg green); restore blob == HEAD, git diff HEAD empty. pnpm --filter @object-ui/app-shell --filter @object-ui/i18n run type-check -> exit 0. pnpm --dir ui exec vitest run packages/i18n/ -> 89 files / 1374 tests passed.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack at e50eba20: 34 derived, each run with exit captured before any pipe, all exit 0 (incl. pnpm --dir ui exec vitest run scripts/ -> 169 files passed, 4901 tests). --ran reconciliation: 34 derived, 34 run, 0 NOT-MEASURED (derived zero, all with exit codes). Extra: pnpm check:changeset-fixed exit 0 (roster under .changeset); pnpm --dir ui run check:new-line-citations exit 0, 0 new citations (first attempt as a root script was MODULE_NOT_FOUND-class exit 254, script lives in ui/). CI check-runs on e50eba20 at report time: Check Changeset, Check PR Size, Governed Surface Queue Guard, Lint (ESLint, PM dispatch-gates, repo & tooling, CI/release/engine, docs & code guards, scoped x3), PR-body guards, Type Check (workspace, source, consumer, debt ledger) all in_progress; opt-in harnesses skipped.",
      "line_budget": "216 changed lines (+214/-2, 5 files) vs 250 cap",
      "files_changed": [
        ".changeset/27458-owd-cbp-prerequisite-confirm.md",
        "ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx",
        "ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.cbpPrerequisiteConfirm-27458.test.tsx",
        "ui/packages/i18n/src/namespaces/studio/en.ts",
        "ui/packages/i18n/src/namespaces/studio/zh.ts"
      ],
      "deviations": [
        "Dispatch order vs os-dev.md (os-dev wins): PR body footer uses the session-URL form required for PR bodies instead of the bare footer the dispatch named; declaration lines (Fixes/Clause-②/Metadata-protocol) placed before ## 维护者速读 per os-dev ('声明行后'), not after it.",
        "Panel imports readFields from metadata-admin/previews/object-fields-io.js (already a dependency of owd-sharing.ts) to name the relationship; within the declared panel surface."
      ],
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0",
      "api_writes": "2 — relay pr_create (POST /repos/objectstack-ai/objectstack/pulls via fleet-write dispatch, repository_dispatch HTTP 204); issue comment POST /repos/objectstack-ai/objectstack/issues/27458/comments via post-stamped. Plus git push (not REST).",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — multi-row save: rows written before the refused row are already drafts when the confirm opens; cancel leaves them (non-destructive). Recorded in PR Acceptance notes."
      ]
    }
  5. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Landing refused · 2026-10-29T12:52Z · PR #27553 · ACCEPT 6497175213 at head e50eba20be.

    • Command: node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F = [{"op":"pr_ready","pull":27553},{"op":"automerge_enable","pull":27553}].
    • Refusal: the seat container's auto-mode permission classifier denied the call before it ran ("Merge Without Review"). Nothing was sent; the PR stays draft, CI green, mergeable_state: clean.
    • Per pm-dispatch landing rule (classifier refusal ⇒ stop, report to the maintainer, no other spelling), the seat has stopped and reported. The PR waits for the maintainer: either their own ready + merge, or a permission change that lets this seat run the landing op.
  6. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    Landing refused again · 2026-10-29T13:06Z · PR #27553 · head e50eba20be unchanged since ACCEPT 6497175213.

  7. objectstack-fleet commented on Oct 29, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:ui#1 · os-sales · session_01L6bhgXU26UBEjd1KKHgNEx

    落地 · 2026-10-29T19:53Z

    读数 结果
    PR #27553 MERGED 2026-10-29T19:47Z,合并提交 c63e9980ca 在 origin/main 上
    git grep -c "cbpPrerequisite" origin/main -- ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx 2(阳性对照 useState = 7)

    状态:卡已由 PR 关闭,摘 pm:dispatched。

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

Metadata

Metadata

Assignees

Labels

area:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:uipriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions