Repository navigation
Commit 9b24f3c
test(qa-scenarios): add the asset_lifecycle mid-tier scenario card (#30695)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9DUKRk5mV2mBg59GDjf3Z61H)_
Fixes #30481
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:新增「资产全生命周期」中档场景卡(8 个对象、报废审批、3 个角色其中 2 个按行限定、1
个按类别统计账面价值的报表、120 件资产跨 6–9 月采购、10 道问数、6 个写操作、6 个 agent
动作),并配一份只属于这张卡的真值测试。
- **为什么改**:落实 cloud#3600 九个场景中的资产场景,让 AI
建应用、问数、代办写操作都有一张能用代码判分的资产管理卡(领用与归还、维修、按月折旧、报废审批)。
- **风险与回滚**:只加测试数据和测试,所在包私有、不发布,运行时零改动,也没碰兄弟卡共用的文件;回滚即删两个新文件。
- **先看**:① `docs/qa/scenarios/asset_lifecycle.json` 的 `run.questions` 与
`run.operations`;② `packages/qa/scenarios/test/asset-lifecycle.test.ts`
的 `EXPECTED_TRUTHS` 及旁边的手算注释;③ 同一文件里「seed internally
consistent」用例(折旧、账面价值、资产状态与单据对得上)。
- **两分钟验证**:`pnpm --filter @objectstack/scenarios test`,预期 5 个测试文件、70
个用例全绿。
- **需要你做的**:无,席位落地。
## What changes
- `docs/qa/scenarios/asset_lifecycle.json` (new): the 资产全生命周期
asset-lifecycle mid-tier card.
- Eight objects: `department`, `employee`, `asset_category` (useful
life, straight-line method), `asset` (the main object, with roll-up
`accumulated_depreciation` and formula `book_value`), `checkout`
(checkout and return, with the return condition), `repair_order`,
`disposal_request` (the scrap approval, routed to the manager of the
asset's department) and `depreciation_entry` (one per asset per month,
posted by a scheduled flow).
- Three personas: `employee` (the assets they hold, their own checkouts,
repairs and scrap requests; no cost, book value, depreciation, budget or
home address) and `dept_manager` (their department; no home address)
carry a row scope; `asset_admin` sees every asset but no asset budget.
- Seed: 120 assets bought June to September 2026, 92 checkouts (64 kits,
re-issues after repair, 14 loaners), 19 repair orders, 7 scrap requests
in all four states and 297 depreciation entries. The arithmetic rule
that generated it is stated in the card's `$comment`, so every truth can
be worked by hand.
- Run half: 10 questions, 6 operations and 6 agent actions (one
confirm-gated); `iterate` adds transfers between departments.
- `packages/qa/scenarios/test/asset-lifecycle.test.ts` (new): this
card's own test file.
- It pins all 10 computed truths and the 10 per-persona truths against
hand-worked values; each derivation is written next to its pin.
- It asserts the mid-tier floor, and the seed's own consistency: each
depreciation entry is cost over useful life, posted after the purchase
month and never from the disposal month on; accumulated depreciation
sums the entries and book value is cost minus it; each asset's status
and custodian follow its checkouts, repairs and scrap request; each
scrap request's approver is its department's manager.
- It asserts each operation targets a row in the state its story needs
(an overdue loaner, a submitted request the manager holds, an approved
request, the employee's own request, an asset in repair, serials and a
checkout key that do not exist yet).
- It shows that a wrong seed cell and a wrong truth spec in fixture
copies go red against those pins.
## Why
The card is one of the six mid-tier scenarios ruled on
objectstack-ai/cloud#3600 (Q1 = v3, 「同意这9个场景」). It gives the asset
domain a scenario whose answers, permission boundaries and write
outcomes are graded by code only.
## Tests
`pnpm --filter @objectstack/scenarios test`: 5 files, 70 tests passed
(64 before this PR). `pnpm --filter @objectstack/scenarios typecheck`:
exit 0, after building the closure with `pnpm --filter
'@objectstack/scenarios^...' build`; `tsc --listFiles` lists the new
test file. Both read at head `0ffa47e2a6`.
## Acceptance notes
- **File name.** The card is `asset_lifecycle.json`, not
`asset-lifecycle.json` as the issue spells it: a card's `id` must be a
snake_case machine name equal to the file's basename. #30600 made the
same call.
- **No shared file touched.** Since #30600, `test/library.test.ts` names
only the three control cards and the README's "Adding a card" step
already sends a mid-tier card's pins to its own file. So this PR adds
two files and edits none, and it cannot conflict with the sibling cards.
- **Seven operation kinds, six operations.**
`receive_and_issue_from_text` covers two of them: entry from free text
with lines (three monitors on one delivery) and the multi-step chain
(register them, then check one out). The workflow step is
`manager_approves_scrap`; its progress query is the question
`dr_0005_waiting_on`, asked about a different request so its truth stays
a fact of the seed.
- **New rows in post-states.** A `where` cannot name a created row's id,
so the new checkout is matched by `employee` plus `checked_out_on`; the
test proves no seeded checkout has that pair, and that the three
delivered serials are new.
- **Overdue means due before `asOf`.** `overdue_loaners_by_department`
filters `due_on lt "2026-10-31"`, the card's `asOf`; the window
operators only name ranges that end on `asOf`.
- **No schema field for some floor items.** An approval or automation
flow, "declarative preferred" and a report as distinct from a dashboard
have no field. The card states them in `prompt.detailed` (zh and en) and
`capabilities`, seeds `disposal_request` as an expected object, and
declares the report under `expect.dashboards`. Nothing in the loader
grades them.
- **Capability names.** The loader checks only the area half of a tag.
`approvals.dynamic-approver-routing` is a checklist id; the rest follow
the descriptive names `travel_expense.json` uses.
- **Changeset.** `packages/qa/scenarios` is `"private": true` and the
diff touches no published package, so this PR carries no changeset and
asks for `skip-changeset`, as #30600 did.
- **Sources.** The domain is designed from the card text and general
business knowledge; the card's Reference line names no template.
objectstack-ai/cloud#3567 was not read.
## Verification
<details>
<summary>scenarios test + typecheck (head 0ffa47e)</summary>
```
Test Files 5 passed (5)
Tests 70 passed (70)
> tsc --noEmit
os-verify-lock: VERDICT command-exit 0
```
Run after merging `main` and a whole-tree `pnpm build` (118 of 118 turbo
tasks).
</details>
<details>
<summary>dispatch-gates --commands (50 commands, local, head
0ffa47e)</summary>
All 50 derived commands exit 0 on head `0ffa47e2a6`. The `--ran`
reconciliation reads: 50 derived, 50 run, 0 NOT-MEASURED, 0 UNRUN, a
derived zero because every line recorded its exit code.
The first run, on the pre-merge head `11193506f5`, had 47 exit 0 and 3
exit 3 (PREREQUISITE NOT MET, nothing measured). Each cleared once its
prerequisite was in place:
- `check-plugin-teardown-shape.mjs --self-test`, after `git fetch
--unshallow origin main` (its fixture is a pinned commit a shallow clone
lacks);
- `check:lean-entry-closure` and `check:dual-build-cjs-loads`, after the
whole-tree build.
</details>
Changed lines: 1294 (+1294 / -0, 2 files), under the 3000 threshold
(`dispatch-gates.mjs` reading).
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(No schema touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [ ] 3. No formatter ran; `pnpm lint` passes. (No formatter ran, and
eslint on the new test file exits 0; the repo-wide `pnpm lint` is left
to CI's `Lint & Repo Gates`.)
- [x] 4. One issue, one branch off an up-to-date `main`, one pull
request.
- [x] 5. A change that publishes carries a changeset, and both
declaration lines are filled in here and in the changeset. (Publishes
nothing: private package.)
- [x] 6. `packages/spec` touched: its generated artifacts are
regenerated and committed. (Not touched.)
- [x] 7. No hand edits to `content/docs/references/`,
`packages/*/CHANGELOG.md` or `content/docs/releases/`.
- [x] 8. Title, body and comments are in English.
- [ ] The *Before Submitting* list in CONTRIBUTING.md is done. (Every
item but the repo-wide `pnpm lint`, left to CI as in item 3.)
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4czx7zatyA1s7R4yPhB)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent a599cc1 commit 9b24f3c
2 files changed
Lines changed: 1294 additions & 0 deletions
0 commit comments