Repository navigation
Description
epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu
Filing-gate category: ② maintainer-directed task. Maintainer, verbatim (Claude Code session, 2026-11-06). After seeing the screenshots of the AI-built CRM app from the second live run, they asked 「这样的场景可以交付给客户吗?会不会太简单?」, then answered the seat's proposal 「我接受你的建议」:
- control cards are internal quality checks only, never exported or shown;
- mid-tier cards are shown in the gallery as starter templates;
- a delivery-grade tier is added (its scenario list goes to the maintainer for review separately).
The Chinese-UI requirement in the same proposal was withdrawn, verbatim: 「中文界面要求 不需要」. This card was edited to remove it.
Routing, verbatim: 「能交给车队开发的你不要自己开发」. Epic: objectstack-ai/cloud#3600.
Problem
Nothing in a card says whether it is a control card or a gallery candidate. Today the exporter (#30349) could publish a 2-object, 8-row control app (crm_account_with_contacts) as if it were a product.
Ask
- Card field
tier: a closed enumcontrol | starter | delivery, required on every card by the loader.- Set
controloncrm_account_with_contacts,invoice_with_line_itemsandproject_with_tasks. - Set
starteron the six mid-tier cards. - The loader refuses a
starterordeliverycard below a floor stated in the PR. Example floor: ≥ 5 objects, ≥ 100 seed rows, ≥ 10 questions, ≥ 3 personas. The six mid-tier cards already meet it (measured: 7–8 objects, 496–757 rows, 10 questions, 3 personas).
- Set
- Exporter rule. Scenario library T8: export verified scenario apps to a new gallery repository — source, run record, auto screenshots and the 8–10 问数 transcripts, rebuilt per release #30349 exports only
starteranddeliverycards whose run passed every half. Acontrolcard is never exported, whatever its score. This card owns the field; Scenario library T8: export verified scenario apps to a new gallery repository — source, run record, auto screenshots and the 8–10 问数 transcripts, rebuilt per release #30349 owns the filter. - Gallery wording.
objectstack-ai/gallery's README and AGENTS.md are hand-written. The epic seat updates them once this lands. Nothing for the lane there.
Out of scope: the delivery-grade scenario list (maintainer review first).
Reader: the domain:devx lane seat, from pm:queue.
Generated by Claude Code
Activity
[-]Scenario cards carry a tier (control | starter | delivery); starter and delivery builds must ship a complete zh-CN UI, checked by the scorer; control cards are never exported[/-][+]Scenario cards carry a tier (control | starter | delivery); control cards are internal checks and are never exported[/+]objectstack-fleet commentedon Nov 6, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: audit · p1 kept ·
domain:devx· 2026-11-06T14:56Z⛔ Not a claim.
tier(control | starter | delivery). The loader requires it and refuses astarterordeliverycard below a stated floor. The exporter (Scenario library T8: export verified scenario apps to a new gallery repository — source, run record, auto screenshots and the 8–10 问数 transcripts, rebuilt per release #30349) never exports acontrolcard.domain:devx,area:devpathandpm:queuekept. The Chinese-UI requirement was withdrawn by the maintainer (「中文界面要求 不需要」), and the body no longer carries it.Generated by Claude Code
objectstack-fleet commentedon Nov 7, 2026
domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA
Claim: PM loop round 1
Session:
session_012MmDsRzowAnXEmogPGSEKAAccount:
os-lizheng(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-30970-scenario-card-tierWorktree:
objectstack-issue-30970Domain:
domain:devxSeat:
domain:devx#3File surface:
packages/qa/scenarios/src/schema.ts,packages/qa/scenarios/src/loader.ts,packages/qa/scenarios/test/**(library/loader/refusal tests),docs/qa/scenarios/*.json(the nine cards gaintier),docs/qa/scenarios/README.md,packages/qa/scenarios/README.md(stop on breach; explain in the report). ⛔ Notsrc/export/**(the exporter filter belongs to #30349, on hold) and notsrc/runner/**(sibling #30982 in flight).Container & model:
M,mode:subagent,model: TIER_DEFAULT (dispatch-gates --tier: "no path-derived mandate: the surface hits none of the 4 declared glob(s)"; per-card judgment = default),effort: xhigh(p1)Clause-②: no
Metadata-protocol: none
Responsibility: n/a — not a defect card
Thread-read: 6687812774
Premise: holds (read at origin/main
719a502b:tierappears 0 times insrc/schema.tsandsrc/loader.ts; the nine cards are indocs/qa/scenarios/;@objectstack/scenariosisprivate: true, so no published accept set)Serial constraints cleared: #30982 (same
area:devpath, same package, claimed by this seat in the same round): its declared surface issrc/runner/**, disjoint from this card's. #30349 (on hold) owns the exporter filter and is not in flight. No open PR touchespackages/qa/scenariosordocs/qa/scenarios. Lane in-flight #30147 (facade codemods) does not intersect.objectstack-fleet commentedon Nov 7, 2026
domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA
os-dev-report
{ "issue": 30970, "status": "done", "branch": "claude/issue-30970-scenario-card-tier", "pr": "https://github.com/objectstack-ai/objectstack/pull/31091", "session": "session_012MmDsRzowAnXEmogPGSEKA", "premise_still_valid": true, "summary": "Every scenario card now carries a required card-level `tier` (closed enum control | starter | delivery, outside `expect`, so the scorer is untouched). The loader refuses a card with no tier, and holds a starter or delivery card to GALLERY_FLOOR in src/loader.ts. The floor is at least 5 expected objects, 1 expected dashboard, 100 rows on the largest seeded object, 2 personas with a row scope, 8 questions, 4 operations and 3 agent actions; each line it falls short of is refused at the field that falls short. A countsReason does not lower it, so the default-band countsReason refusal is not raised on a gallery card. The three golden cases are `control` and the six mid-tier cards `starter`; both READMEs are updated, and the library README states that the exporter does not read tier yet and exports no card today. Premise re-measured at objectstack@719a502b: `tier` appeared 0 times in src/schema.ts and src/loader.ts (positive control: `id` 12 times in schema.ts); the six mid-tier cards measure 7-8 objects, 496-757 seed rows, 10 questions and 3 personas, as the card said. Floor choice: the ruling makes mid-tier the starter tier, and docs/qa/scenarios/README.md already states the mid-tier floor, so the loader enforces that floor's countable lines rather than the card's example (10 questions, 3 personas, 100 seed rows in total). The difference is stated in the PR and raised under open_questions. No edit to src/export/** or src/runner/** was needed: namespacedScenario spreads the whole card, so tier carries through.", "tests": "At head 58f8f32e, objectstack-ai/objectstack. (1) Locked: `pnpm --filter @objectstack/scenarios build && pnpm --filter @objectstack/scenarios test` gave check-dts-emitted 10/10, Test Files 23 passed (23), Tests 357 passed (357), VERDICT command-exit 0. The first run, before this package's own dist/ existed, failed only test/build/built-consumer.test.ts ('build @objectstack/scenarios first'), a prerequisite. (2) Locked: `pnpm --filter @objectstack/scenarios typecheck` (tsc --noEmit) gave VERDICT command-exit 0; `tsc --listFiles` includes test/refusals.test.ts, test/library.test.ts and test/travel-expense.test.ts (3 of 3). (3) A verbose run of refusals + library gave Tests 80 passed. These include the 2 tier shape cases, the 13 gallery-floor cases (exactly at the floor, as starter and as delivery, loads clean; each line one step below gives exactly 1 refusal at its path; the 2-object, 8-row control declared starter gives exactly 4 refusals: expect.objects, expect.dashboards, run.seed, run.personas) and the library pin that the controls are exactly the tier-control cards. (4) Ablation, one per behaviour, via scripts/ablation-replace.mjs in WRAP mode. Tests import ../src/index.js, so no dist step was needed. A1, floor call disabled: anchor 1 to 0, blob 457c98e7ea26 to 2824a0caf452, Tests 10 failed and 63 passed (every floor case red), restored to blob == HEAD. A2, tier made optional: blob c50b65e4f733 to 89135e96ed87, 1 failed ('refuses a card with no tier'), restored. A3, band rule no longer skipped for gallery cards: blob 457c98e7ea26 to ca8f95cfa6b5, 2 failed with 'expected length 1 but got 2', which is more diagnostics, the predicted direction; restored. After all three, git status --porcelain is empty and both files hash to their HEAD blob. (5) Targeted: require('packages/qa/scenarios/dist/index.cjs') loads and exports SCENARIO_TIERS, GALLERY_TIERS, GALLERY_FLOOR (objects=5) and isGalleryTier. The packages/cli integration tier does not apply (no cli file touched).", "gates": "Local: `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived from the tree at 58f8f32e (21 paths, 289 changed lines vs merge base 719a502b) gave 52 commands. Each was run with its exit captured before any pipe and recorded as 'CMD :: exit N'. 50 exited 0. 2 exited 3 (PREREQUISITE NOT MET, read as NOT MEASURED): `node scripts/check-plugin-teardown-shape.mjs --self-test` (its positive control is pinned to commit 621a4876, which a shallow clone cannot reach; checker-health only) and `pnpm check:dual-build-cjs-loads` (needs every package's dist/; this tree built only the scenarios closure; declared narrowing, CI runs it over a full build). `--ran` reconciliation exited 0: '52 derived famil(ies) accounted for, 50 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3), 0 UNRUN'. origin/main still equalled BASE 719a502b (0 commits since), so no merge was needed. check-platform-checklist is not owed (no export renamed or deleted, only added). CI at PR head 58f8f32e when this report was written (27 check-runs): Check Changeset success, Governed Surface Queue Guard success, Check Documentation Links success; Lint (docs & code guards, CI release & engine gates, repo & tooling gates, ESLint, scoped slot lookup & corpus, scoped tooling self-tests, PM dispatch-gates, scoped query-options erasure), Type Check (workspace, source gates, consumer gates, debt ledger), PR-body guards, Flag docs affected by code changes and Check PR Size in_progress; opt-in, schedule and auto-label runs skipped. Not re-run locally; CI convergence is the PM's.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm", "mcp_calls": "0 — no MCP GitHub tool was called (reads went through `gh api` REST GETs; writes through the relay)", "api_writes": "2 relay strokes — (1) POST /repos/objectstack-ai/objectstack/dispatches carrying pr_create, which the relay executed as objectstack-fleet[bot] as POST /repos/objectstack-ai/objectstack/pulls (draft, #31091; body read back byte-identical, 10912 of 10912) plus POST /repos/objectstack-ai/objectstack/issues/31091/labels ['skip-changeset'] (read back via GET: labels ['skip-changeset'], draft true); (2) this os-dev-report comment, via scripts/pm/post-stamped.mjs, as POST /repos/objectstack-ai/objectstack/issues/30970/comments. Plus git push x3 to the feature branch (not REST). No card or PR assignee was written.", "open_questions": [ { "question": "Gallery-floor numbers: keep the README mid-tier floor's countable lines (this PR), or switch to the card's example floor?", "options": [ "A (this PR): at least 5 objects, 1 dashboard, 100 rows on one object, 2 row-scoped personas, 8 questions, 4 operations, 3 agent actions, the lines docs/qa/scenarios/README.md already states for mid-tier cards. Cost: none further.", "B: the card's example, at least 5 objects, 100 seed rows in total, 10 questions, 3 personas. Cost: one constant edit, six test-row edits, and a rewrite of the README mid-tier floor's 8-10 band and its 2-3 row-scoped-roles line, about 30 minutes." ], "recommendation": "A. Real business need: both pass all six measured starter cards and refuse all three controls. A's extra lines (dashboard, row-scoped roles, agent actions) are what a gallery template visibly needs, and B's total-row count passes a card that spreads 100 rows thin. Long-term soundness: A keeps one floor, stated once (README = loader); B leaves two floors unless the README is rewritten. Guarding against AI authoring errors: A is the stricter contract (more thin shapes refused loudly, and no countsReason escape). Startup-stage scope control: B has fewer lines, but A introduces no concept the README did not already declare. Trade-off: B follows the card's literal example, which a maintainer may prefer; switching is cheap." } ], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted in PR Acceptance notes, not filed: docs/qa/scenarios/README.md 'How truth is computed' says the computed truth 'has one of three shapes' and then lists five (existing wording, polish class, not a/b/c)", "carrier: none · noted, not filed: the six per-card 'meets the mid-tier floor' tests now overlap the loader's countable floor lines; kept, because they also pin the floor's lines that cannot be counted (approval or automation object, 3-month span, operation kinds)" ], "deviations": [ "Report shape: the dispatch order reads gates / line_budget / deviations / files_changed, but the os-dev.md template carries no line_budget, deviations or files_changed, so these three fields were added beside the template's.", "Floor numbers differ from the card's example floor (the card labels it 'Example'; the ruling says 'a floor stated in the PR'). Stated in the PR body and under open_questions.", "Changeset: skip-changeset, by AGENTS.md's criterion (private package plus docs/qa, nothing publishes). Earlier PRs on this same private package (30289, 30291, 30292) carried a patch changeset; that precedent was not followed.", "Commit trailers: the model-free pair AGENTS.md requires (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By.", "Local narrowing: check:dual-build-cjs-loads and check-plugin-teardown-shape --self-test are NOT MEASURED (exit 3 prerequisites; full workspace build and unshallow clone respectively). test:repo was not run locally: the diff touches no repo-project test and the derived list does not name it; it is declared to CI (Test Core)." ], "files_changed": [ "docs/qa/scenarios/README.md", "docs/qa/scenarios/asset_lifecycle.json", "docs/qa/scenarios/crm_account_with_contacts.json", "docs/qa/scenarios/invoice_with_line_items.json", "docs/qa/scenarios/leave_balances.json", "docs/qa/scenarios/order_to_cash.json", "docs/qa/scenarios/project_with_tasks.json", "docs/qa/scenarios/requisition_to_receipt.json", "docs/qa/scenarios/service_desk.json", "docs/qa/scenarios/travel_expense.json", "packages/qa/scenarios/README.md", "packages/qa/scenarios/src/loader.ts", "packages/qa/scenarios/src/schema.ts", "packages/qa/scenarios/test/asset-lifecycle.test.ts", "packages/qa/scenarios/test/leave-balances.test.ts", "packages/qa/scenarios/test/library.test.ts", "packages/qa/scenarios/test/order-to-cash.test.ts", "packages/qa/scenarios/test/refusals.test.ts", "packages/qa/scenarios/test/requisition-to-receipt.test.ts", "packages/qa/scenarios/test/service-desk.test.ts", "packages/qa/scenarios/test/travel-expense.test.ts" ], "line_budget": "n/a — no skills/** file touched; 21 files, +280 / -9 = 289 changed lines (under the 3000 human-merge threshold); every path is inside the claim's declared file surface" }objectstack-fleet commentedon Nov 7, 2026
domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA
ACCEPT → PR #31091 comment 6703142717 (head
58f8f32e). Landing:pr_ready+automerge_enableonce every check is green.objectstack-fleet commentedon Nov 7, 2026
domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA
Landed · 2026-11-07T06:34Z
added_to_merge_queue2026-11-07T06:14:40Z), merge commitddd646dcdomain:devxcards before vs afterState: closed
completedbyFixes #30970;pm:dispatchedremoved in the same pass. Follow-up owned elsewhere: the exporter filter ontieris #30349 (on hold). Theobjectstack-ai/galleryREADME / AGENTS.md wording belongs to the epic seat (card item 3).