Skip to content

Scenario cards carry a tier (control | starter | delivery); control cards are internal checks and are never exported #30970

Closed

Description

@objectstack-fleet

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

  1. Card field tier: a closed enum control | starter | delivery, required on every card by the loader.
    • Set control on crm_account_with_contacts, invoice_with_line_items and project_with_tasks.
    • Set starter on the six mid-tier cards.
    • The loader refuses a starter or delivery card 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).
  2. 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 starter and delivery cards whose run passed every half. A control card 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.
  3. 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

  1. added
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    domain:devxDomain lane — seat card indexed by label:pm:seat
    area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterate
    on Nov 6, 2026
  2. changed the title [-]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[/+] on Nov 6, 2026
  3. objectstack-fleet commented on Nov 6, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: none

    Triage: audit · p1 kept · domain:devx · 2026-11-06T14:56Z

    ⛔ Not a claim.


    Generated by Claude Code

  4. added
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Nov 7, 2026
  5. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA

    Claim: PM loop round 1
    Session: session_012MmDsRzowAnXEmogPGSEKA
    Account: os-lizheng (the seat's linked user as GET /user answers it; always the card's assignee)
    Branch: claude/issue-30970-scenario-card-tier
    Worktree: objectstack-issue-30970
    Domain: domain:devx
    Seat: domain:devx#3
    File 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 gain tier), docs/qa/scenarios/README.md, packages/qa/scenarios/README.md (stop on breach; explain in the report). ⛔ Not src/export/** (the exporter filter belongs to #30349, on hold) and not src/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: tier appears 0 times in src/schema.ts and src/loader.ts; the nine cards are in docs/qa/scenarios/; @objectstack/scenarios is private: 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 is src/runner/**, disjoint from this card's. #30349 (on hold) owns the exporter filter and is not in flight. No open PR touches packages/qa/scenarios or docs/qa/scenarios. Lane in-flight #30147 (facade codemods) does not intersect.

  6. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    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"
    }
  7. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA

    ACCEPT → PR #31091 comment 6703142717 (head 58f8f32e). Landing: pr_ready + automerge_enable once every check is green.

  8. objectstack-fleet commented on Nov 7, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:devx#3 · os-lizheng · session_012MmDsRzowAnXEmogPGSEKA

    Landed · 2026-11-07T06:34Z

    Probe Reading
    PR #31091 MERGED 2026-11-07T06:32:26Z through the merge queue (added_to_merge_queue 2026-11-07T06:14:40Z), merge commit ddd646dc
    Open domain:devx cards before vs after only #30970 left the open set; no card was closed by a stray closing keyword

    State: closed completed by Fixes #30970; pm:dispatched removed in the same pass. Follow-up owned elsewhere: the exporter filter on tier is #30349 (on hold). The objectstack-ai/gallery README / AGENTS.md wording belongs to the epic seat (card item 3).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

area:devpathThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:devxDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions