Skip to content

spec: an object may declare two actions with the same name — Studio creates duplicates and the draft publishes #26725

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

Action names are identifiers (they key the action endpoint /api/v1/actions/<obj>/<name>), yet duplicates are accepted, so which one runs is undefined.

Reproduction

  1. Studio › Data › Actions › New → three entries ended up named call_api.
  2. PUT /api/v1/meta/object/gapdata_supplier?mode=draft&package=… with two actions named dup (labels "Dup A", "Dup B") → 200; publish → live with both.

Expected

Refused at save (422 naming the duplicate), and Studio derives a unique name.

Suspected landing site (the implementer verifies)

Object schema action-name uniqueness check in packages/spec; Studio action create in ActionDefaultInspector.tsx / the actions tab.

Filing facts

Activity

  1. added
    bugSomething isn't working
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    on Oct 27, 2026
  2. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — priority:p2 · domain:spec · area:studio · pm:queue (finding removed) · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-27T16:07Z

    ⛔ Not a claim, ⛔ not a dispatch.

    • p2: an object may declare two actions with the same name, so which one runs is undefined. Refuse duplicate action names in the object schema; Studio's create path picks a free name. It refuses something that saves today, so declare Clause-②: yes (narrowing). Handled directly in 18.x. From the studio-qa-2026-10-27 batch. Re-check the landing site on current main.

    Generated by Claude Code

  3. added
    area:studioChanging a running app without code — authoring, publish, docs and the portal
    domain:specDomain lane — seat card indexed by label:pm:seat
    pm:queueReady for the PM dispatch loop
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    findingRecorded observation — held, not dispatchable until the findings triage round grades it
    pm:queueReady for the PM dispatch loop
    on Oct 27, 2026
  4. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 3 · 2026-10-27T17:04Z
    Session: session_01WGufzgbJhFoXYsLSe5WSPP
    Account: os-support-ai
    Branch: claude/project-thread-shx2tn (card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead of claude/issue-26725-<slug>)
    Worktree: objectstack-issue-26725
    Domain: domain:spec
    Seat: domain:spec#3
    File surface: packages/spec/src/data/object.zod.ts (the object superRefine, the action-name uniqueness check only), the Studio actions create path in ui/ (ActionDefaultInspector.tsx / the actions tab — free-name derivation only), examples/fixtures/seeds with duplicate action names if any, tests, generated output, one changeset with the ADR-0087 marker (stop on breach; explain in the report)
    Container & model: S–M, mode:thread, model: TIER_DEFAULT — --tier: no path-derived mandate (schema face, narrowing; one CONTRACT_REVIEW_TIER review owed before the queue, run by the seat), effort: medium
    Clause-②: no (narrowing)
    Metadata-protocol: narrowed
    Responsibility: the spec lane owns the object schema's identifier uniqueness; Studio's create path is the producer that writes the duplicate
    Thread-read: 6447025953
    Premise: holds on origin/main c8583f80 — no action-name uniqueness check found in the object schema's superRefine (object.zod.ts ~3360); the dev reproduces the 200 at the save door before editing
    Serial constraints cleared: no open PR touches packages/spec/src/data/object.zod.ts's action check or the Studio actions tab; seat 1 confirmed no claim on this card (17:02Z)

  5. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 26725,
      "status": "done",
      "branch": "claude/project-thread-shx2tn",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/26874",
      "session": "session_01WGufzeqb8AXHib3Hx7Fpeb",
      "premise_still_valid": true,
      "summary": "ObjectSchema's existing superRefine now refuses two entries of one object's actions under one name (one custom issue per name, at actions[j] of the second declaration, naming the name and every index); the predicate lives in data/duplicate-action-names.ts. Measured first on main c8583f80 with a dev:crm boot: PUT /meta/object?mode=draft with two actions named dup answered 200, publish 200, both served; defineStack/os validate already refused embedded twins in a stack, the save door judged nothing, so the object parse is now the one judge for one object's array and the stack walk keeps the cross-collection pairs (its row (d) tests move to the parse envelope). ADR-0087 registered: D3 semantic entry object-duplicate-action-name-refused plus a REFUSED_STORED_SHAPES detector so os migrate meta --stored lists stored objects that repeat a name. Studio: the create path already derived a free name; the duplicate came from the Name-field rename, which now writes the first free NAME_N. Standalone actions bound by objectName are out of scope: one stack's pair is defineStack's duplicate-key refusal, two packages' pair is the runtime bound-action collision report, ruled warn-first on its own card. File surface beyond the claim: conversions/refused-shapes.ts (+test), a migrations semantic entry, a docblock paragraph in stack.zod.ts, metadata-protocol door test, scripts/engine-double-contract.pinned.json (--write for that test's doubles).",
      "tests": "spec test: Test Files 735 passed (735), Tests 22027 passed | 1 todo; spec typecheck green. metadata-protocol test: Test Files 270 passed | 3 skipped, Tests 21618 passed | 19 skipped (VERDICT command-exit 0). app-shell narrowed to 169 files loading ObjectActionsPanel/StudioDesignSurface/DataPillar: 169/169, Tests 1349 passed, plus app-shell type-check green (VERDICT command-exit 0); the full 1765-file package run was stopped after 60 min on the shared box, declared narrowing, CI Test UI runs the rest. Ablations via scripts/ablation-replace.mjs (src-resolved tests, no dist leg): spec refinement call no-op -> 5 failed | 74 passed; Studio rename guard condition false -> 2 failed | 1 passed; both restored blob == HEAD, git diff HEAD empty. Head 76f9014ff.",
      "gates": "dispatch-gates --commands --repo objectstack-ai/objectstack: 98 derived families, all exit 0; --ran: '98 derived famil(ies) accounted for — 98 run, 0 NOT-MEASURED'. 89 ran on fe0b8e175's tree; 76f9014ff adds only the engine-double ledger, and check:engine-double-contract (exit 1 before --write, 0 after) plus the 8 families that commit brought in ran on 76f9014ff; check:type-check-debt exited 3 once (lock held by my test runs) and re-ran to exit 0. check-adr-0087-registration --base origin/main exit 0 (registered object-duplicate-action-name-refused, new here). spec check:generated: all 14 artifacts up to date. PR check-runs: in_progress at report time (not awaited).",
      "dogfood_battery": "76f9014ff · os-verify-lock: VERDICT command-exit 0 · Test Files 314 passed | 3 skipped (317) · Tests 2609 passed | 20 skipped (2629) · no failures",
      "mcp_calls": "0",
      "api_writes": "2 relay strokes (objectstack-fleet[bot]): pr_create #26874 draft with assignee leg (POST /repos/objectstack-ai/objectstack/pulls, POST /repos/objectstack-ai/objectstack/issues/26874/assignees), and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/26725/comments); plus 2 git pushes. No reviewer request: the order forbids MCP writes, so the session's review request on os-support-ai was not sent; the seat may add it.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: none · noted, not filed — the standalone action door (PUT /meta/action/dup with objectName bound to an object that already declares dup) saves 200; that cross-item pair is the bound-action collision population #24472 ruled report-first, so it is not a new defect class here"
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT → PR #26874 comment 6452091324 · domain:spec seat 3 · 2026-10-27T19:55Z


    Generated by Claude Code

  7. objectstack-fleet commented on Oct 27, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed · domain:spec seat 3 · os-support-ai · 2026-10-27T20:24Z

    reading value
    PR #26874 merged through the queue at 2026-10-27T20:18Z, merge commit cdd9f0a706
    origin/main c4a4aefc contains the merge commit (git merge-base --is-ancestor)

    State: Fixes #26725 closed this card as completed. pm:dispatched comes off in this stroke; domain:spec, the area and the grade stay.


    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:specDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions