Skip to content

feat(scenarios): scenario-card schema + loader (private packages/qa package) and nine open seed cards - #30340

Merged
objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-30289-scenario-card-schema
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 8 commits into
mainfrom
claude/issue-30289-scenario-card-schema

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #30289

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么: 新增私有包 @objectstack/scenarios(packages/qa/scenarios,不发布、无构建步骤、不调用任何模型),内含场景卡的 zod schema 与加载器。按 v3 场景库的地基改造:五项 schema 扩展(跨对象关系路径、卡级 asOf 加相对时间窗、按角色的行范围与逐角色真值、"种子答不了"的真值类型、写操作声明预期结果),外加可选的 run.agentActions。三张简单对照卡(发票、项目任务、CRM)升级到新格式,每张 8–9 个问数、5 个写操作。删除 6 张退役卡。README 写明 v3 场景库形态和中档卡的能力底线。
  • 为什么改: cloud#3600 Q1 = v3(「同意这9个场景」)与执行参数裁决;席位选定方案 A:本 PR 只做地基,6 张中档卡各自成单(Scenario library: mid-tier card — 差旅报销 travel expense (8–10 问数 + 4–6 write operations, row-scoped personas) #30476–Scenario library: mid-tier card — 资产全生命周期 asset lifecycle (8–10 问数 + 4–6 write operations, row-scoped personas) #30481,均 Blocked-by 本 PR,合并后并行派发),以免单个 PR 超过 3000 行人工合并线。
  • 风险与回滚: 不触 packages/spec、不改公开面、包为 private: true、expect 形状未动(与已合并的评分器共用)。仓级改动只有已登记的三处(cross-package 声明、turbo 输入、ci-filter-parity 自测按名记录)。回滚 = revert 本 PR。
  • 先看: ① packages/qa/scenarios/src/loader.ts 的 checkPath / resolverFor(关系路径)和逐角色真值那一段;② packages/qa/scenarios/src/schema.ts 的 OutcomeSchema 与 guardrail 对 outcome 的要求(在 loader.ts);③ docs/qa/scenarios/invoice_with_line_items.json(新格式的完整样子)。
  • 两分钟验证: pnpm --filter @objectstack/scenarios test,预期 Test Files 3 passed、Tests 59 passed;把 invoice_with_line_items.json 里 sales_rep 的 scope 删掉再跑,library 用例转红,amount_per_invoice 的逐角色真值对不上。
  • 需要你做的: 无,席位落地。

What changes

Rescope (seat decision, option A). This PR is now the v3 library's foundation. The six mid-tier cards (travel expense, order-to-cash, service desk, leave and balances, requisition-to-receipt, asset lifecycle) are sub-issues #30476, #30477, #30478, #30479, #30480 and #30481. Each is Blocked-by this PR and each lands as its own PR. No mid-tier card content is written here.

New private package @objectstack/scenarios (packages/qa/scenarios), named after its siblings (private: true, exports at src, no build step, zod its only runtime dependency). It has three files:

  • src/schema.ts: the card shape, with strict objects throughout.
  • src/truth.ts: the declarative truth evaluator.
  • src/loader.ts: loadScenarios(dir, { areas }) returns typed cards with truths and personaTruths, or { file, path, message } refusals. A card with any refusal is not returned.

The five schema extensions. Each one extends a closed enum or shape; nothing is loosened.

  1. Relation paths. filter[].field and groupBy accept lookup.field and deeper paths, such as invoice.status or account.industry. Every hop except the last must be a lookup declared in expect.lookups, and the path resolves through the seed rows' lookup ids. A hop that is not a declared lookup is refused at the exact path, and so is a final field that no seed row of the reached object carries.

  2. Time windows. asOf is a card-level fixed date, validated as an ISO date. The new filter op in_window takes a value of { kind: last_days, days } or { kind: calendar, period: this_month | previous_month | this_year | previous_year }, and the window is inclusive and computed against asOf. The loader refuses three things: a window when the card has no asOf, a window over a field that holds no date, and a window value given to any op other than in_window.

  3. Persona row scope. A persona can now declare scope: [{ object, field, value }], where field may be a path (invoice.owner scopes invoice lines through their invoice). A question can list personas, and the loader then also computes that question's truth as each listed persona. Before evaluating, it applies the persona's scope rules for the question's object. If the question reads an object or field the persona hides, the persona's truth is { kind: withheld }, meaning a correct reply declines.

  4. Unanswerable. A new truth kind { kind: unanswerable, reason } says the seed cannot answer, so a correct reply says so instead of giving a figure. It joins the closed enum.

  5. Declared write outcomes. Every operation now names the persona it acts for (actingAs) and declares an outcome, so it can be graded by code alone. The outcome is one of:

    • applied, with the post-state rows: each named by a seeded id, or by where for a row the write creates, and carrying either the values the data API must read back or absent. It can also name an auditActor.
    • refused, with a code from the spec's closed ErrorCode catalogue (@objectstack/spec/api: StandardErrorCode plus the registered ledger, ADR-0112). The catalogue is read from the spec export and never restated here, so the package now depends on @objectstack/spec and its vitest config aliases that import to spec source.
    • pending_confirmation.

    The guardrail checks are now held to the outcome: write_validation needs a refused create or update, confirm_irreversible needs pending_confirmation, and audit_actor needs applied with an auditActor.

Two smaller additions:

  • The optional run.agentActions lists at least 3 agent-callable business actions, { name, object, summary, confirm }, with at least one confirm-gated.
  • The counts band: a card needs at least 5 questions and 2 operations. Below the default of 8 questions or 4 operations it must state why in run.countsReason, or it is refused.

The three controls, upgraded. The expect blocks are untouched: the corpus derivation still equals the cards.

Card Questions Operations New-arm questions
invoice_with_line_items 9 5 cross-object invoice.status; issued_on this month (asOf 2026-10-31); 2 per-persona (sales_rep row scope, warehouse_staff withheld); 1 unanswerable
project_with_tasks 8 5 cross-object filter and groupBy through project.status; 2 per-persona (contractor scope by assignee, client_viewer and contractor withheld); 1 unanswerable
crm_account_with_contacts 8 5 cross-object filter and groupBy through account.industry; 2 per-persona (sdr scope via account.owner, partner_user withheld); 1 unanswerable

Each control's five operations are the same set of outcomes: one applied create, one applied update with an audit actor, one confirm-gated delete, one refused validation violation (VALIDATION_FAILED, the code the engine's record validator answers for a per-field refusal), and one refused overstep by a persona without the right (PERMISSION_DENIED). Each control also declares 3 agent actions, one of them confirm-gated.

Retired: the six earlier generic cards are removed with their test pins. docs/qa/scenarios/README.md now describes the v3 shape, the new arms, and the mid-tier capability floor, including the declarative floor: flows, formulas and validation rules are preferred over code hooks, so a graduated app can install unmanaged.

Repo wiring forced by existing gates, unchanged from the first round:

  • scripts/cross-package-test-inputs.mjs and turbo.json declare that this suite reads docs/qa/scenarios/*.json and the checklist area files.
  • scripts/check-ci-filter-parity.mjs names that glob in its rollback pin, which moves from 20 to 21.

Tests

  • test/library.test.ts: the three controls load with zero refusals. All 25 truths are asserted against hand-worked values, and so are the 10 persona truths.
  • test/refusals.test.ts: 38 refusal cases in a table, each asserting the single refusal's file, exact path and message class. There are also 2 controls (the unmutated card loads clean, and a below-band card loads once it states countsReason), plus id/basename and invalid-JSON cases. The new arms are covered by:
    • relation paths: 2 cases;
    • time windows: 4 cases;
    • persona scope and personas: 2 cases;
    • unanswerable: 1 case;
    • outcomes and actingAs: 5 cases;
    • guardrail-to-outcome matching: 2 cases;
    • agent actions: 2 cases;
    • countsReason: 1 case.
  • test/truth.test.ts: the evaluator, now including unanswerable, the resolver for paths, and window ranges across month and year edges (including a leap February).

Acceptance notes

  • Size: 2532 changed lines vs the merge base (+2529 / -3, 20 files), under the 3000 human-merge line. This was measured before every push.
  • Persona rows: a persona must still hide at least one object or field (the earlier rule). So no persona is all-seeing, and an operation acts as a scoped persona. That fits the per-persona grading, but it means a card has no admin persona. If the mid-tier cards need one, relaxing that rule is a follow-up decision, not made here.
  • Withheld semantics: a question reading an object or field a persona hides answers withheld for that persona. That includes a hidden field reached through a relation path, and the lookup field used as a hop counts as read.
  • Not wired to the CLI or the scorer, which is sibling work. The expect shape is untouched.

Verification

Package suite and typecheck (head c1affd3)
pnpm --filter @objectstack/scenarios test       Test Files  3 passed (3) · Tests  59 passed (59)
pnpm --filter @objectstack/scenarios typecheck  exit 0
os-verify-lock: VERDICT command-exit 0
Ablations: one per new arm, through scripts/ablation-replace.mjs, each restored byte-identical (git hash-object == HEAD blob), git diff HEAD empty after
  • relation_paths: mutation landed (blob 73dcb24d->3fb8af58), 32 test(s) red, e.g. ['loads every card with no refusal', 'holds exactly the three control cards (the mid-tier cards land on their own)']; restored: True
  • time_windows: mutation landed (blob d562f0d3->9254d982), 2 test(s) red, e.g. ["invoice_with_line_items: every question's computed truth is the hand-worked one", 'computes windows against asOf, inclusive, across month and year edges']; restored: True
  • persona_row_scope: mutation landed (blob 73dcb24d->c801f0c3), 3 test(s) red, e.g. ["invoice_with_line_items: every question's computed truth is the hand-worked one", "project_with_tasks: every question's computed truth is the hand-worked one"]; restored: True
  • persona_withheld: mutation landed (blob 73dcb24d->39a838a3), 3 test(s) red, e.g. ["invoice_with_line_items: every question's computed truth is the hand-worked one", "project_with_tasks: every question's computed truth is the hand-worked one"]; restored: True
  • unanswerable: mutation landed (blob d562f0d3->f9a664ff), 4 test(s) red, e.g. ["invoice_with_line_items: every question's computed truth is the hand-worked one", "project_with_tasks: every question's computed truth is the hand-worked one"]; restored: True
  • outcome_guardrail_match: mutation landed (blob 73dcb24d->d93bd217), 1 test(s) red, e.g. ['refuses a confirmation check on an operation that does not wait']; restored: True
  • outcome_post_state_id: mutation landed (blob 73dcb24d->f1d67cba), 1 test(s) red, e.g. ['refuses a post-state naming a created row by an id the seed lacks']; restored: True
  • agent_actions_confirm: mutation landed (blob 73dcb24d->58d94865), 1 test(s) red, e.g. ['refuses agent actions with none confirm-gated']; restored: True
  • counts_reason: mutation landed (blob 73dcb24d->313cb397), 1 test(s) red, e.g. ['refuses fewer than 8 questions without a stated reason']; restored: True
  • refusal_code_catalogue: mutation landed (blob 47f63c7d->163daac4), 1 test(s) red, e.g. ['refuses a refusal code outside the platform error-code catalogue']; restored: True
  • git diff HEAD empty: True
Derived gates (dispatch-gates --commands, reconciled with --ran)

Tree: objectstack-ai/objectstack at c1affd3 (merge base f506b79). dispatch-gates --ran reported 92 derived famil(ies) accounted for — 88 run, 4 NOT-MEASURED (3 DERIVED from a recorded exit 3, 1 claimed) and exited 0.

  • 88 run, every one exit 0. Among them: check:cross-package-test-inputs, check:ci-filter-parity, check:pm-dispatch-gates, check:test-source-alias, check:type-check-coverage, check:undeclared-dep-imports, check:doc-authoring, check:issue-citations, check:nul-bytes, check:changeset-fixed, check-changeset-no-major and check-empty-changeset.
  • check:error-code-casing (ADR-0112 D1) was red in CI on c3a257d because the refusal test used a lowercase literal. Reproduced locally with pnpm check:error-code-casing: exit 1, packages/qa/scenarios/test/refusals.test.ts:83 'validation_error' (assignment). On c1affd3 it exits 0 (no unlisted lowercase error codes in 11258 scanned file(s)). The fix is a closed vocabulary rather than an exemption: the refusal-code field is now the spec's ErrorCode, and the test probes a SCREAMING code outside the catalogue (VALIDATION_BROKEN).
  • NOT MEASURED, exit 3 PREREQUISITE NOT MET (built dist/ of packages this diff does not build): check:dual-build-cjs-loads, check:lean-entry-closure, @objectstack/lint check:doc-formula-expressions.
  • NOT MEASURED, timed out under the local 420 s wrapper (CI-owned whole-workspace census): check:type-check-debt.
  • CI on c1affd3 at report time: 58 check-runs success and the rest skipped. None red.

Generated by Claude Code

@github-actions github-actions Bot added size/xl dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/scenarios, touching 99 documentable anchor(s). ⚠️ 5 changed file(s) yielded no anchor (packages/qa/scenarios/README.md, packages/qa/scenarios/package.json, packages/qa/scenarios/src/index.ts, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

21 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 0c7e95aff5996de2313b4668760eafee2e50172d.

⛔ 5 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/qa/scenarios/README.md, packages/qa/scenarios/package.json, packages/qa/scenarios/src/index.ts, …) — pages documenting those are invisible to this run
  • 50 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 103 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 0c7e95aff5996de2313b4668760eafee2e50172d → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 81e722ef4db8370682478a0e106f8189915e6a65 — the merge of head c1affd3f516a0afb92ef645bf45a53548a488f1e into base 0c7e95aff5996de2313b4668760eafee2e50172d, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 81e722ef4db8370682478a0e106f8189915e6a65 && git checkout 81e722ef4db8370682478a0e106f8189915e6a65
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 0c7e95aff5996de2313b4668760eafee2e50172d c1affd3f516a0afb92ef645bf45a53548a488f1e && git checkout -B drift-repro 0c7e95aff5996de2313b4668760eafee2e50172d && git merge --no-ff c1affd3f516a0afb92ef645bf45a53548a488f1e

node scripts/docs-audit/affected-docs.mjs --json 0c7e95aff5996de2313b4668760eafee2e50172d

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 0c7e95aff5996de2313b4668760eafee2e50172d → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

席位核对

Verdict: ACCEPT at head 038f4be0d81bfaff184a620879a88c96421b46ae, landing HELD.

  • Waits-on: objectstack-ai/cloud#3600 decision comment 6651166095, Q1 (the maintainer confirms the 9 seed scenarios before they land; maintainer verbatim: 「这个场景的清单是需要我人工审核和确认的」).
  • Even after Q1 is ruled, landing also waits for every check-run on the then-current head to be green.
  1. Shape.
    • Draft PR → main. Line 1 is Fixes #30289. Clause-②: no and Metadata-protocol: none sit at line start in the body and the changeset.
    • 26 files, +2835/−3, under the 3000-line human-merge threshold.
  2. Scope (read via the PR files endpoint).
    • The new private package @objectstack/scenarios (private: true, exports at src, only zod as a dependency) and 9 cards plus a README under docs/qa/scenarios/.
    • The changeset is right for a private package: .changeset/config.json has privatePackages: { version: true, tag: false }.
    • The three extra files (scripts/cross-package-test-inputs.mjs, turbo.json, scripts/check-ci-filter-parity.mjs) are an accepted deviation: an existing gate forced them, the dev reported them, and the claim surface is amended on the card. None is governed.
    • No packages/spec change; no public surface.
  3. Ruling compliance.
    • The expect shape matches the card field for field: nothing renamed, no optional key added.
    • The 9 ids are exactly the card's list (5 corpus, 4 generic).
    • Spot-checked book_library.json. It has all six layers. Capabilities are drawn from checklist areas, and the category is from the closed enum. There are 5 questions and 2 bilingual personas.
    • No customer-derived content; no model call.
  4. Tests and gates.
    • Dev side, with commands and output: 3 files / 42 tests, all 44 computed truths equal hand-worked values; 21 refusal cases each assert file + exact path; one ablation turns the truth suite red and restoring returns it to green.
    • dispatch-gates: 92 derived, 84 run with exit 0, 8 NOT MEASURED (6 for missing prerequisites, 2 timed out locally; all go to CI).
    • Check-runs on 038f4be0 at review time: 11 success, 7 skipped, 16 in_progress, 0 failure.
  5. Out-of-scope findings. Corpus ↔ card drift has no pin yet. Carrier: objectstack#30291, whose CLI wiring is the natural owner of a drift check. The shallow-clone teardown self-test is environment-only, with no carrier.
  6. Landing. After Q1 is ruled A (or rework lands per Q1 = B) and every check is green, the seat flips this PR to ready and enables auto-merge through the relay.

… row scope, unanswerable, write outcomes, agent actions; controls upgraded; six retired cards removed

Claude-Session: https://claude.ai/code/session_01Q4EjjPXwX1YfHyKJUh4agu
Co-authored-by: Claude <[email protected]>
… ErrorCode catalogue

The control cards' validation refusals now expect VALIDATION_FAILED, the
code the engine's record validator answers for a per-field refusal.

Claude-Session: https://claude.ai/code/session_01Q4EjjPXwX1YfHyKJUh4agu
Co-authored-by: Claude <[email protected]>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

epic:#3600#1 · hotlong · session_01Q4EjjPXwX1YfHyKJUh4agu

席位核对 (patch round 1, option A: foundation)

Verdict: ACCEPT at head c1affd3f516a0afb92ef645bf45a53548a488f1e. The previous ACCEPT on 038f4be0 is spent, and the landing hold is lifted: Q1 is fully ruled on objectstack-ai/cloud#3600.

  1. Shape.
  2. Size and scope (read via the PR files endpoint).
    • 20 files, +2529/−3, under 3000.
    • The new private package @objectstack/scenarios; the 3 control cards; the README; the 6 retired cards deleted.
    • The three gate-forced repo files are already amended on the claim.
    • The added @objectstack/spec workspace dependency serves to read ErrorCode from its export, not to restate it. That is the right fix for the ADR-0112 casing gate (reproduced red at c3a257d5, green here; no EXEMPT_FILES entry).
    • No packages/spec change; no public surface.
  3. Ruling compliance.
    • The five schema arms are present: relation paths, asOf + in_window, persona row scope with per-persona truths and withheld, the unanswerable kind, and declared operation outcomes (applied / refused-with-catalogue-code / pending_confirmation).
    • Optional run.agentActions (≥3, ≥1 confirm-gated) and the counts band with countsReason are present.
    • expect is untouched.
    • Controls: invoice 9 questions / 5 operations, project 8/5, CRM 8/5, within the delegated band.
    • No mid-tier card here.
  4. Tests and gates.
    • 3 files / 59 tests. 25 truths and 10 persona truths are hand-worked.
    • 38 tabled refusals, each asserting file + exact path.
    • One ablation per new arm (10), each red then restored byte-identical.
    • dispatch-gates: 92 derived, 88 run with exit 0, 4 NOT MEASURED with reasons (all CI-owned).
    • Check-runs on c1affd3f: 58 success, 7 skipped, 0 failure, 0 pending, so the PR is queue-eligible.
  5. Out-of-scope findings.
  6. Landing. The seat flips this PR to ready and enables auto-merge through the relay in the next stroke. When it merges, Scenario library: mid-tier card — 差旅报销 travel expense (8–10 问数 + 4–6 write operations, row-scoped personas) #30476–Scenario library: mid-tier card — 资产全生命周期 asset lifecycle (8–10 问数 + 4–6 write operations, row-scoped personas) #30481 unblock into the devx lane's pm:queue.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 14:28
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit 58085ff Nov 5, 2026
70 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-30289-scenario-card-schema branch November 5, 2026 15:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file documentation Improvements or additions to documentation size/xl tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Scenario library T1a: scenario-card schema + loader (private packages/qa package) and 9 open seed cards under docs/qa/scenarios/

1 participant