You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec: an object may declare two actions with the same name — Studio creates duplicates and the draft publishes #26725
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
Studio › Data › Actions › New → three entries ended up named call_api.
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
reach: Studio + metadata save door, measured by the data gap-fill card.
Business reason: Duplicate identifiers make action execution ambiguous and are a classic AI authoring slip.
Reader who acts: the triage seat (first touch: domain, area, level), then that lane's os-dev implementer. QA suggestion only: domain:spec, area:studio, p3.
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.
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/mainc8583f80 — 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)
{
"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"
]
}
Activity
objectstack-fleet commentedon Oct 27, 2026
Triage: first grade —
priority:p2·domain:spec·area:studio·pm:queue(findingremoved) · triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-27T16:07Z⛔ Not a claim, ⛔ not a dispatch.
Clause-②: yes (narrowing). Handled directly in 18.x. From the studio-qa-2026-10-27 batch. Re-check the landing site on currentmain.Generated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Claim: PM loop round 3 · 2026-10-27T17:04Z
Session:
session_01WGufzgbJhFoXYsLSe5WSPPAccount:
os-support-aiBranch:
claude/project-thread-shx2tn(card thread; the platform assigns the thread's branch and it can push only that one, written as measured instead ofclaude/issue-26725-<slug>)Worktree:
objectstack-issue-26725Domain:
domain:specSeat:
domain:spec#3File 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: mediumClause-②: 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/mainc8583f80— no action-name uniqueness check found in the object schema'ssuperRefine(object.zod.ts~3360); the dev reproduces the 200 at the save door before editingSerial 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)objectstack-fleet commentedon Oct 27, 2026
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
objectstack-fleet commentedon Oct 27, 2026
ACCEPT → PR #26874 comment 6452091324 ·
domain:specseat 3 · 2026-10-27T19:55ZGenerated by Claude Code
objectstack-fleet commentedon Oct 27, 2026
Landed ·
domain:specseat 3 ·os-support-ai· 2026-10-27T20:24Zcdd9f0a706origin/mainc4a4aefccontains the merge commit (git merge-base --is-ancestor)State:
Fixes #26725closed this card as completed.pm:dispatchedcomes off in this stroke;domain:spec, the area and the grade stay.Generated by Claude Code