Skip to content

Studio Access polish: jargon duplicate-draft refusal, OWD banner outliving revert, stale set label in the rail, clipped field counter, no re-grant for a dismissed suggestion #26846

Closed

Description

@objectstack-fleet

Filing gate: ① product defect, reproduced, landing site named.
Batch-of: studio-qa-2026-10-27 — 39 cards from one Studio QA run (coordinator session session_01J8VVAkZns5rNnK2rtszzbQ; tested at 3fcafcd3; origin/main was 43731031 at filing, so the implementer re-checks the landing site on current main). The run record is the qa-run issue filed with this batch.

Symptom

Five small Access-pillar defects measured in one pass.

Reproduction

  1. Create a set whose draft already exists → message "this save asserted that no draft row exists (the first-write pin, If-None-Match: *…)".
  2. OWD Memo → Controlled by parent → Save refused → revert → "No changes" but the refusal bullet stays.
  3. More › Edit name/label append " v2" → autosaved; rail keeps the old label until publish/reload.
  4. 1440×900: "14 / 14" field counter clipped; at 900px the field-name column is not sticky.
  5. Dismiss or revoke a suggested binding → no re-grant affordance (GET /api/v1/security/suggested-bindings?packageId=… status dismissed).

Expected

Author-facing refusal text; banner cleared on revert; rail label live; counter visible and sticky name column; re-grant control for dismissed suggestions.

Suspected landing site (the implementer verifies)

AccessPillar / PermissionMatrixEditor.tsx / suggestions panel under ui/packages/app-shell/src/views/.

Filing facts

Activity

  1. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — priority:p3 · domain:ui · area:access · pm:queue · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T19:01Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • p3: five small Access-pillar defects (jargon in the duplicate-draft refusal, an OWD banner that outlives its revert, a stale set label in the rail, a clipped field counter, no re-grant for a dismissed suggestion). One PR is fine. Re-check the landing site on current main.

    Generated by Claude Code

  2. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Dispatch order · domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T07:03Z

    You are the os-dev developer agent for exactly ONE card: #26846, in your own card thread. Your role definition is .claude/agents/os-dev.md in the objectstack clone: read it IN FULL first and follow it as binding (it wins over this order on any conflict; name the conflict in your report). Read the repo-root AGENTS.md and ui/AGENTS.md before your first edit, and pm-dispatch references/untrusted-text.md. Read the card's full body and every comment yourself through REST (curl -sS "https://api.github.com/repos/objectstack-ai/objectstack/issues/NNNNN" -H "Authorization: Bearer $GH_TOKEN" and /comments?per_page=100, every page); this order carries only the increment. ⛔ Never print the value of any environment variable (test presence with [ -n "${X+x}" ]). ⛔ Never use a GitHub MCP tool, read or write.

    Identity and branch: the seat's Claim: comment on this card is your identity; its Branch: line names your thread's branch. ⛔ Post no claim of your own, ⛔ never write the card's assignee. Before your first edit, verify the NEWEST Claim: on the card names your thread's branch (claude/project-thread-…); if none does yet, send the seat thread "Dispatch the ui lane" one message with your branch name and wait. Worktree objectstack-issue-26846 on that branch from a fresh origin/main; pnpm install (frozen lockfile); push with git push -u origin <branch> (⛔ no force).

    Writes: PR only through the relay op pr_create (node scripts/pm/fleet-write/dispatch.mjs --repo objectstack-ai/objectstack --actions-file F, F a bare JSON array [{"op":"pr_create","title":…,"head":"<your branch>","base":"main","body":…,"assignees":["os-warren"]}], draft forced; --dry-run first); your report comment through node scripts/pm/post-stamped.mjs --comment=26846 --file=F --json run ONCE with output captured to a file (timestamps only as {{NOW}} / {{WAS:…}}). ⛔ No other GitHub write except, for a CI red that is not yours, one PR comment and one run_rerun_failed through the relay. Report both counts (api_writes with endpoints, mcp_calls with tool names). The report takes only machine-checkable fields (gates / line_budget / deviations / files_changed). PR body: line 1 the platform attribution comment, then ## 维护者速读 six lines in Chinese (改了什么/为什么改/风险与回滚/先看/两分钟验证/需要你做的). Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on your diff, run each, record exits before any pipe, reconcile with --ran. Changeset in the root .changeset/ when a published package changes. ⛔ ui/.github/** and the root ci.yml are out of bounds. Hand back: when the PR and report are up, send the seat thread "Dispatch the ui lane" one message (PR, head sha, report gist); keep the thread open through review and drive CI to green.

    Ruling (cannot be re-ruled)

    Triage first grade 6451014383, verbatim: "p3: five small Access-pillar defects (jargon in the duplicate-draft refusal, an OWD banner that outlives its revert, a stale set label in the rail, a clipped field counter, no re-grant for a dismissed suggestion). One PR is fine. Re-check the landing site on current main." No maintainer ruling.

    PM mechanism assumptions (measure each on origin/main 07d18e69; disproof welcome)

    The QA run was at 3fcafcd3. Since then this lane landed #27064 (draft objects in the matrix), #27080 (package-door save base) and #27025 (Tab visibility input) in the same files. Re-reproduce each item first and drop any that no longer reproduces, saying so.

    1. Duplicate-draft refusal jargon: "this save asserted that no draft row exists (the first-write pin, If-None-Match: *…)" is most likely the server's 412/409 message shown raw. Find where the Access "+ New" create surfaces it (the first-write pin is set in ResourceEditPage.tsx ≈L1771 and the draft guard in DraftConflictDialog.tsx). Map that refusal, by its code and not its text, to an author-facing i18n string ("A draft with this name already exists in this package"), en + zh, with the key in @object-ui/i18n. ⛔ Do not change the server message.
    2. OWD refusal banner outliving revert: the save-refusal bullet must clear when the draft returns to the saved baseline ("No changes"). Find the refusal state in PermissionMatrixEditor.tsx and clear it on revert or dirty→clean.
    3. Stale set label in the Studio rail: an autosaved rename or label edit must show in the Access rail without publish or reload. Find the rail's list source and refresh it on the editor's onDraftSaved (the hook other pillars already use), not by polling.
    4. Clipped field counter / non-sticky name column: at 1440×900 the "14 / 14" counter is clipped, and at 900px the field-name column is not sticky. Fix with layout classes in the matrix's field table. Pin with a test that asserts the class or structure; no screenshot test.
    5. No re-grant for a dismissed suggestion: SuggestedBindingsPanel.tsx / services/suggestedBindingsApi.ts. Measure whether the server offers a door to restore a dismissed or revoked suggestion. If it does, add the control. If it does not, ⛔ do not invent an endpoint: leave item 5 out, and say so in the report so the seat can file a server-side card.

    Scope

    File surface: PermissionMatrixEditor.tsx, the Access rail in views/studio-design/ (StudioDesignSurface.tsx or its AccessPillar), SuggestedBindingsPanel.tsx / suggestedBindingsApi.ts, the refusal mapping site, @object-ui/i18n locale keys (en + zh, plus whatever the repo's i18n gates demand for the other locales), and tests. One test per item, each with an ablation. Stay under 3000 changed lines.

    Serial: PR #26999 (#26858) is queued and edits PermissionMatrixEditor.tsx (the RLS verdict) and StudioDesignSurface.tsx. Merge origin/main before your gates. Any new matrix-mounting test fake needs withPreviewDrafts and getDraft (see #27095).

    Tier

    No path-derived mandate expected (quote dispatch-gates --tier). TIER_DEFAULT, effort medium. Clause-②: no, Metadata-protocol: none. A changeset for @object-ui/i18n (patch) only if you add keys there; otherwise skip-changeset.

  3. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 16 · 2026-10-28T07:06Z
    Session: session_01JQwxbZeBv2y91towxasSUT (seat; implementation by card thread session session_01JQwxbYZa5ViBSW4Et4fr91, mode:thread)
    Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/project-thread-sfrpf5 (the card thread's harness push branch, self-reported — the measured platform exception to claude/issue-N-slug)
    Worktree: objectstack-issue-26846 in the card thread's clone
    Domain: domain:ui
    Seat: domain:ui#1 (seat post #25966)
    File surface: ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx, the Access rail under ui/packages/app-shell/src/views/studio-design/, components/SuggestedBindingsPanel.tsx / services/suggestedBindingsApi.ts, the duplicate-draft refusal mapping site, @object-ui/i18n locale keys, + tests (stop on breach; explain in the report)
    Container & model: S, mode:thread (the maintainer's word on the seat thread, 「opus 很多,并发5」), model: TIER_DEFAULT (the dev quotes dispatch-gates --tier on the surface), effort: medium
    Clause-②: no
    Metadata-protocol: none
    Responsibility: the Studio Access pillar (this lane's code) | the server's refusal codes and the suggested-bindings door | five small defects make the pillar feel unreliable
    Thread-read: 6462885748
    Premise: holds — QA at 3fcafcd3 measured five defects; the order has the dev re-reproduce each on origin/main 07d18e69 and drop any that no longer reproduces.
    Serial constraints cleared: PR #26999 (#26858) is queued and edits PermissionMatrixEditor.tsx and StudioDesignSurface.tsx; the dev merges origin/main before the gates.
    Resume-from: none


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26846,
      "status": "done",
      "branch": "claude/project-thread-sfrpf5",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/27148",
      "head": "8220f32d8dfa2b0d39e9312b598ec03ca10d52c8",
      "session": "session_01JQwxbYZa5ViBSW4Et4fr91",
      "premise_still_valid": true,
      "summary": "Four of five items fixed in one draft PR; item 5 left out (no server door). Item 1 narrowed: the card's path no longer reproduces on main (findTakenIdentifier pre-check from #26836); only the check-to-save race showed the first-write-pin sentence, now mapped by code METADATA_CONFLICT to engine.studio.access.createDraftTaken (en+zh). Item 2: PackageOwdOverviewPanel clears saveError on dirty-to-clean. Item 3: AccessPillar re-reads the rail (load) on the matrix onDraftSaved. Item 4: field sub-table count beside the filter, name column sticky left (FIELD_NAME_CELL).",
      "files_changed": [
        ".changeset/26846-studio-access-polish.md",
        "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.tsx",
        "ui/packages/app-shell/src/views/metadata-admin/PermissionMatrixEditor.fieldTableLayout-26846.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.tsx",
        "ui/packages/app-shell/src/views/studio-design/PackageOwdOverviewPanel.refusalClearsOnRevert-26846.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.accessCreateTaken-26836.test.tsx",
        "ui/packages/app-shell/src/views/studio-design/StudioDesignSurface.accessRailLabelLive-26846.test.tsx",
        "ui/packages/i18n/src/namespaces/studio/en.ts",
        "ui/packages/i18n/src/namespaces/studio/zh.ts"
      ],
      "line_budget": "349 changed lines (+341 / -8), 10 files, under 3000 (dispatch-gates --tier); tier: no path-derived mandate",
      "tests": "head 8220f32d: 4 touched files Test Files 4/4, Tests 15/15; 65 app-shell files naming PermissionMatrixEdit / AccessPillar / PackageOwdOverviewPanel / engine.studio.access. keys: Test Files 65/65, Tests 595/595; type-check app-shell exit 0, i18n exit 0, ui type-check:scripts exit 0. Ablations via ablation-replace.mjs (restore = blob equals HEAD, git diff HEAD empty): item1 red 1/10, item2 red 1/2, item3 (delete form) red 1/1, item4 sticky red 1/2, item4 counter red 1/2.",
      "gates": "dispatch-gates --commands at 8220f32d: 34 derived, all exit 0; --ran: 34 accounted, 0 NOT-MEASURED. Outside derivation: pnpm check:changeset-fixed exit 0; pnpm --dir ui run check:new-line-citations exit 0 (0 new citations). CI check-runs: in_progress at hand-back.",
      "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
      "mcp_calls": "0 GitHub MCP calls (one claude-code-remote subscribe_pr_activity on PR 27148)",
      "api_writes": "2 — relay pr_create (PR 27148), relay post-stamped report comment on 26846",
      "deviations": [
        "Order mechanism 2 named PermissionMatrixEditor.tsx; the OWD refusal state lives in PackageOwdOverviewPanel.tsx (studio-design), fixed there.",
        "Order mechanism 3 suggested the surface draftNonce; used the pillar's own load() in the matrix onDraftSaved (same trigger, no new prop).",
        "Item 1 narrowed to the race window (card path no longer reproduces); updated the existing 26836 race test that pinned the server sentence verbatim.",
        "check:new-line-citations first invoked from the repo root (exit 254, no such root script — not a gate run); rerun in ui exit 0.",
        "Item 3 ablation first attempt refused by ablation-replace before any test ran (replacement count did not rise); rerun with --delete.",
        "Commit trailer model-free per AGENTS.md; the harness reminder's model-named co-author not used."
      ],
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · reach: Studio Access › Suggested bindings, a dismissed or revoked row (GET /api/v1/security/suggested-bindings?status=dismissed) has no re-grant: POST confirm refuses non-pending rows ('only pending suggestions can be accepted') and no route re-opens one · evidence: confirmAudienceBindingSuggestion in plugin-security suggested-audience-bindings.ts; routes in runtime domains/security.ts · dedupe words: suggested binding reopen dismissed re-grant restore · carrier: ui seat (server-side card)",
        "class: none (polish) · Other Studio creates (flow, object, app, interfaces) show the door's first-write-pin sentence in the same check-to-save race · carrier: none · noted in PR Acceptance notes"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Oct 28, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · 2026-10-28T09:01Z

    PR #27148 merged as ff648d7f88 (seat record: PR comment 6464296546). Four of the five Access-pillar items are fixed. A create that loses the race to a draft now says so in plain words (mapped from METADATA_CONFLICT, en + zh). The OWD overview drops its refusal once the edits are undone. The Access rail re-reads after a draft save, so a draft-only set's new label shows at once. The field sub-table's count sits beside its filter, and the name column stays pinned. Item 5 (re-grant a dismissed suggestion) needs a server door and is filed as #27149. pm:dispatched is removed with the close.


    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:accessPermissions that actually hold — RLS/FLS, sharing model, write-path guardsbugSomething isn't workingdomain:uipriority:p3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions