Repository navigation
qa: a repeatable Studio task benchmark — "build a repair-ticket app with no code", timed, counting red errors — as the Studio UX baseline #22137
Description
Filing gate ③ — a task the maintainer dispatched directly.
Who acts on it: pm-dispatch execution seat → a mode:cloud session. ⛔ Not a claim. Filed on the maintainer's word: 「用真实用户做任务测试:找低代码作者完成"零代码搭一个工单应用",记录完成时间和遇到红色报错的次数,作为 Studio 体验的基准指标。」 and 「这个可以单卡一个云卡片推进」.
Task
A repeatable task benchmark for Studio: one fixed scenario, build a repair-ticket app with no code, timed, counting every red error the author meets. The numbers become the Studio UX baseline and are re-measured each release.
Dispatch
mode:cloud on the maintainer's word: browser / dogfood work that outlives a PM session. The cloud session drives the scenario in a real browser against a fresh showcase boot, following the dogfood-verification and checklist RUNNER.md conventions.
Scenario (fixed script)
As a fresh admin in Studio: create package Repair Center → object Repair Ticket (Name text, Status picklist New / In Progress / Done, Due Date date, Problem Description long text, Technician lookup to user) → group the form → validation "Due date required when Done" → automation "notify the technician when Done" → app with navigation → publish. Then as an end user: create a ticket, move it to Done, see the notification arrive.
Measured per run
- Wall time per step and in total.
- Red error banners shown (count and verbatim text) and 4xx/5xx answers to draft and publish writes.
- Places the author had to type JSON, CEL or a machine name.
- Dead ends: steps that cannot be completed without code (named, with the step).
Acceptance
- The scenario script and a scoring sheet committed under
docs/qa/(next to the platform checklist'sruns/). - A baseline run record with the numbers, on a named commit of both repositories.
- Every red banner and dead end cross-referenced to an existing card, or filed.
- A short protocol for 3–5 sessions with real low-code authors (same scenario, think-aloud, same metrics); the maintainer recruits the people, the agent prepares the material.
Expected to show up in the baseline
objectstack-ai/objectui#11786 (red banners on intermediate states), objectstack-ai/objectui#11788 (Notify recipients only as JSON), objectstack-ai/objectui#11790 (Interfaces gaps), objectstack-ai/objectui#11772 (flow edges left behind). The benchmark measures them; it does not re-file them.
Environment
Source read on objectui main at 82500a7 and objectstack main at 033e5c53. Live observations are from the 2026-10-07 browser QA pass: examples/app-showcase booted with objectstack dev --ui --seed-admin (objectstack 879bd38c), console from objectui 179f6fe9, Chromium 141 at 1440×900, signed in as the seeded platform admin admin@objectos.ai.
Duplicate check
- Semantic issue search on objectstack-ai/objectstack (open and closed), usability benchmark task build an app with no code time to complete error count baseline: 0 hits (0 open, 0 closed).
- Semantic issue search on objectstack-ai/objectstack (open and closed), dogfood scenario measure time and errors for an author building a ticket app in Studio: 0 hits (0 open, 0 closed).
- No hit describes this.
- Positive control in the same run: related lists action id refusal notice shown to end users on objectstack-ai/objectui returned related lists: an action id the related object does not define draws a refusal notice above the list for whoever opens the record — an authoring fault shown to end users; scope it to authors/admins objectui#11768: yes.
Dedupe words: studio usability benchmark task time · no-code app build error count baseline · dogfood task scenario metrics
Filed by Claude Code (session session_01D76mrPJrSSdaKRxR2rvrMG) from the 2026-10-07 QA pass and a follow-up source reading.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 8, 2026
Grade (triage):
domain:devxadded;priority:p3andpm:queuestand.docs/qa/(the scenario script, the scoring sheet and the baseline run record), which is thedomain:devxlane's (docs/qa/**).mode:cloudtask (「这个可以单卡一个云卡片推进」), so the claiming seat sends it to a cloud session.Triage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-08T02:57Z. ⛔ Not a claim, ⛔ not a dispatch.objectstack-fleet commentedon Oct 8, 2026
Claim: PM loop round 1 (session seat taking over an unclaimed card on the maintainer's word: 「这个谁在处理?没人处理你可以接手你可以派云卡片」)
Session:
session_01D76mrPJrSSdaKRxR2rvrMGAccount:
os-zhuangBranch:
claude/issue-22137-studio-task-benchmarkWorktree:
objectstack-issue-22137(inside the cloud session's own container)Domain:
domain:devxFile surface:
docs/qa/studio-benchmark/**only (new directory: scenario script, scoring sheet, baseline record, real-user session protocol). ⛔ Notdocs/qa/platform-checklist/**— #22140 is in the merge queue there. Stop on breach; explain in the report.Container & model:
L,mode:cloud,model: opus — the default tier; dispatch-gates --tier: "no path-derived mandate", so the PM's per-card call (browser-driven benchmark authoring)Clause-②: no
Responsibility:
n/a — not a defect cardThread-read: 6051250769
Serial constraints cleared:
objectstack-ai/objectstack#22140 (docs/qa/platform-checklist/**, queued) — disjoint by the file surface above; in-flight domain:devx claims (22131, 22085, 22082, 22080, 22014) declare scripts/, .github/workflows/ and .changeset/ — none touches docs/qa/Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
Dispatched 2026-10-08T03:28Z — round 1,
mode:cloud: sessionsession_01VenZErfhzG51sJBHRM1pWJ("⚡ devx #22137 Studio task benchmark"), branchclaude/issue-22137-studio-task-benchmarkper the claim above. Delivery is a draft PR plus oneos-dev-reportcomment on this card; the dispatching seat collects both, subscribes to the PR and archives the session once the card is final.Generated by Claude Code
objectstack-fleet commentedon Oct 8, 2026
os-dev-report
{ "issue": 22137, "status": "done", "branch": "claude/issue-22137-studio-task-benchmark", "pr": "https://github.com/objectstack-ai/objectstack/pull/22180", "session": "session_01VenZErfhzG51sJBHRM1pWJ — this cloud run (dispatch claim 6051545275 names this branch; verified before the first write)", "premise_still_valid": true, "summary": "All four acceptance items are in docs/qa/studio-benchmark/ (draft PR 22180, Fixes #22137). (1) SCENARIO.md, SCORING.md and SCORESHEET.md hold the fixed script and the scoring sheet. (2) The baseline is baselines/2026-10-08-8fc50b76-a58626c8.md, measured on objectstack 8fc50b7647d30db2a0837877cf251c1163a3239e and objectui a58626c88dc85954bd0af24f16ebb69454c03eee (pin and console stamp equal). Headline: completed 8/8 · 1511 s · 16 red errors · 13 failed writes · 10 code-shaped entries · 0 dead ends. (3) Every red error and code-shaped entry carries a card reference: objectui#11786, objectui#11772, objectui#11788 and objectstack#22032, or one of the findings below. objectui#11790 was not reached: Studio's Create app dialog built the navigation without opening Interfaces. (4) USER-SESSIONS.md is the protocol and kit for 3–5 real-author sessions. Every step finished in the console. Two pieces of rework the script does not name came up at step 8: granting the end user access to the new object, and rewriting a validation rule. The condition builder had compiled that rule to CEL the runtime cannot evaluate, so no ticket could ever be set to Done. Docs only; skip-changeset applied; PR assignee os-zhuang.", "tests": "Builds (under os-verify-lock): pnpm build --concurrency=2, then pnpm objectui:build → VERDICT command-exit 0; console reports 'dist ready … from objectui@a58626c88dc8'. Boot: OS_PORT=41873 objectstack dev --ui --seed-admin -p 41873 -d file:(scratch)/data.db → health 200. Every step was verified through REST: draft reads with ?state=draft&package=com.acme.repair; publish-drafts → 200 (3 times); published reads of the object, flow and app. Forged create with status=done and no due date → 400 'could not be evaluated (runtime: no such overload: !null)' before the repair, and 400 'Set a due date before marking the ticket Done.' after it. A Done write with a due date → 400 '!string' (reproduced 3 times: two forged requests and the end user's UI PATCH). GET /api/v1/data/sys_inbox_message as the technician → 1 row, 'Ticket Leaking tap in room 204 is Done'. Local gates: node scripts/pm/dispatch-gates.mjs --commands derived 12 families, all run at 686dc621, each exit 0: check-ci-filter-parity, check-closing-keyword-parity (and --self-test), check-comment-mask-corpus, lint check:doc-formula-expressions, check:cross-package-test-inputs, check:doc-authoring, check:driver-memory-census, check:gitlink-declared, check:nul-bytes, check:refd-timer-probe, check:watch-hint-literal. dispatch-gates --ran → 12 derived, 12 run, 0 NOT-MEASURED. No package touched, so no build closure or package test is owed. The pre-push hook accepted both commits' trailer pair. CI: not awaited, per the dispatch contract.", "mcp_calls": "12 — mcp__github__issue_read ×3 (22137 get + get_comments; one objectui read refused: repo not in session scope), mcp__github__search_issues ×9 (objectstack dedupe). No GitHub write tool. Also claude-code-remote add_repo ×2 (objectui read: already public; push: refused by the auto-mode classifier).", "api_writes": "3 relay strokes, all through scripts/pm as objectstack-fleet[bot]: (1) fleet-write/dispatch.mjs pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft forced; 5566 bytes sent and stored identically) = PR 22180; (2) label-write.mjs → labels_add skip-changeset + assign os-zhuang on 22180 (read-back matches; size/m from the labeler kept); (3) post-stamped.mjs → this comment, POST /repos/objectstack-ai/objectstack/issues/22137/comments. Not REST: git push ×3 (empty branch probe, WIP, baseline). Unauthenticated GETs to api.github.com for four objectui issues were refused by the session proxy (reads; no data).", "open_questions": [ { "question": "Where should the baseline live: a committed file in docs/qa/studio-benchmark/baselines/ or a qa-run-style issue?", "options": ["A: a committed markdown file keyed to both SHAs (done)", "B: a qa-run issue per release"], "recommendation": "A, because the next release has to diff its run against the previous one field by field, next to the script it scores. The qa-run title grammar (qa-rollup.mjs) counts checklist verdicts, and the checklist runs/ folder is git-ignored. Reasoning recorded in the README and the PR body." }, { "question": "The objectui dedupe search the dispatch asked for is NOT MEASURED. This session cannot read objectstack-ai/objectui issues: MCP refuses the repo as out of scope, the add_repo push attach was refused by the auto-mode classifier, and the fleet --read path was refused too. Who runs the objectui dedupe for the three objectui findings below before they are filed?", "options": ["A: the PM seat runs it with objectui access before filing", "B: attach objectui to a later session and rerun"], "recommendation": "A — the dedupe words are in each finding; the pinned source line is named, so a filer can also check whether a newer objectui main already changed it." }, { "question": "Counting conventions this run had to decide. One red error per red element per author gesture. A free-text field with a browser suggestion list counts as code-shaped. End-user record writes in step 8 count as failed writes. Unscripted rework is timed as sub-steps 8a/8b where it was found. Keep these for the next run?", "options": ["A: keep, as written in SCORING.md", "B: the maintainer adjusts before the second run (and the baseline is re-scored from its own tables)"], "recommendation": "A — they are written into SCORING.md, so the next run applies the same ones; every count in the baseline lists its rows, so re-scoring under other rules is mechanical." }, { "question": "objectui#11790 (Interfaces gaps) was not reached: Studio's Create app dialog builds the app and its navigation without opening Interfaces. Should the fixed script require building navigation in Interfaces?", "options": ["A: keep the script fixed — what a low-code author would do first is what it measures", "B: change step 6 to name Interfaces (starts a new baseline)"], "recommendation": "A — changing the script resets the comparison; cover Interfaces in the human sessions or a separate script." } ], "out_of_scope_findings": [ "class: a · reach: Studio → object Validations → condition builder 'record.due_date' + 'is empty / false' compiles to `record.status == 'done' && !record.due_date`. Publish answers 200, then every Done write is refused: PATCH /api/v1/data/repair_ticket/ID → 400 VALIDATION_FAILED reason unevaluable, fault 'runtime: no such overload: !string' (with a date set) or '!null' (without one), shown to the end user as a red toast. · evidence: objectui a58626c88dc8 packages/app-shell/src/views/metadata-admin/inspectors/ConditionBuilder.tsx:484 emits `!SUBJECT` for falsy, and line 483 emits a bare SUBJECT for truthy; baseline red error 16 and failed write 13; reproduced 3 times. The builder also has no 'is null' operator, so the only repair is raw CEL. The publish and save door gave no verdict: sibling of objectstack-ai/objectstack#22032 (open), which names validation conditions. Producer half lands in objectui. · dedupe: condition builder is empty negation CEL · no such overload !string validation rule · ConditionBuilder falsy operator non-boolean field", "class: a · reach: Studio → Automations → flow designer. The edge '+' and the node's 'Add connected node' both insert a create_record node without offering a type. The node inspector's Node Type list has no notify (the palette has it), and no gesture connects two existing nodes. In the baseline this produced 12× PUT /api/v1/meta/flow/NAME?mode=draft → 422 INVALID_METADATA, plus the edge-left-behind chips of objectui#11772. · evidence: baseline red errors 4–14 and warnings; insertOnEdge defaults type to 'create_record' (objectui a58626c8 FlowCanvas.tsx, insertOnEdge signature). Recovery took select Start → palette → Notify, delete the orphaned End, re-add End from Notify. · dedupe: flow designer insert node default create_record · node type dropdown missing notify · connect existing flow nodes edge", "class: a · reach: Studio → Automations → Start node 'Record updated' on an object that is still an unpublished draft. The entry-condition builder offers only 'previous' as a subject, because GET /api/v1/meta/object/repair_ticket answers 404 for a draft-only object, so the author must switch to Expression and type CEL. · evidence: baseline code-shaped entry 8; network log shows the 404s on the published read while the draft exists. · dedupe: flow entry condition builder draft object fields · condition subject list only previous · flow designer reads published object not draft", "carrier: none (承接者:无) · noted, not filed — machine-name inputs where a picker or a derived value is expected. Package ID is not derived from the display name. Picklist option values are not derived from labels: label first, value empty → red 'empty options' banner. Lookup 'Related object' and trigger 'Object' are free text with a browser suggestion list. The condition value for a select field is free text, and typing the label 'Done' compiled to `record.status == 'Done'` (observed, not executed: the stored value is 'done', so the rule would never fire). Baseline code-shaped entries 1–7. · dedupe: picklist value derived from label · lookup related object free text datalist · condition builder select value picker", "carrier: none (承接者:无) · noted, not filed — a published object grants end users nothing, and neither Studio nor the publish result says so. The end user meets 'You don't have access' (baseline red error 15, class missing-step); the grant takes Studio → Access plus Setup → Users. Deny-by-default is correct, so this is guidance, not a defect. · dedupe: publish new object no access end user prompt permission set", "carrier: none (承接者:无) · noted, not filed — observations, not reproduced twice. (1) Renaming a form group saves the label, but the canvas header keeps 'New group'; keys are new_group / new_group_2. (2) The flow toolbar keeps 'Trigger: autolaunched' after the Start node becomes Record updated. (3) Condition subjects show machine paths, not labels. (4) The end-user console makes background reads that answer 403 (meta/_drafts, sys_activity, sys_comment), and the record panel tells the user they cannot view activity or comments on their own ticket." ] }Generated by Claude Code
docs(qa): a repeatable Studio task benchmark — repair-ticket scenario…