Repository navigation
Commit aed9663
test(qa-scenarios): add the leave_balances mid-tier scenario card (#30690)
<!-- claude-projects-attribution: {"github_login":"os-elon-musk"} -->
_Requested by **Elon** · [project
thread](https://claude.ai/code/project/chan_01WedDzV26nniwxv7jC88B9D?thread=cmsg_01WedDzV26nniwxv7jC88B9D5az28vj5D7FMGPRyuQEdi1)_
Fixes #30479
Clause-②: no
Metadata-protocol: none
## 维护者速读
- **改了什么**:新增「请假与假期余额」中档场景卡:7 个对象(部门、员工、假期类型、假期余额、请假单、请假明细、审批步骤),3
天及以内只需经理审批、超过 3 天再加 HR 审批,批准后自动扣减余额;3 个角色其中 2 个按行限定;1 个请假天数报表;120 张请假单跨
7–10 月;10 道问数、6 个写操作、4 个 agent 动作。另配一份只属于这张卡的真值测试。
- **为什么改**:落实 cloud#3600 九个场景中的 HR 场景,让 AI
建应用、问数、代办写操作都有一张能用代码判分的请假与余额卡。
- **风险与回滚**:只加测试数据和测试,所在包私有、不发布,运行时零改动;回滚即删两个新文件。
- **先看**:① `docs/qa/scenarios/leave_balances.json` 的
`run.operations`(六个写操作及预期结果);② 同文件的 `run.questions` 与 `run.personas`;③
`packages/qa/scenarios/test/leave-balances.test.ts` 的 `EXPECTED_TRUTHS`。
- **两分钟验证**:`pnpm --filter @objectstack/scenarios test`,预期 5 个测试文件、73
个用例全绿。
- **需要你做的**:无,席位落地。
## What changes
- `docs/qa/scenarios/leave_balances.json` (new): the 请假与假期余额
leave-and-balances mid-tier card.
- Seven objects: `department`, `employee`, `leave_type` (annual, sick,
personal), `leave_balance` (one per employee, tracked type and leave
year), `leave_request`, `leave_day` (one line per date, a full day or a
half day) and `approval_step`.
- The route follows the days: up to 3 days goes to the department
manager only, more than 3 days to the manager and then HR (`e13`), and a
manager's own request goes to the HR manager (`e14`) at step 1. On the
last approval the balance's `used_days` (a roll-up of the approved
requests) rises and `remaining_days` (entitlement + carry-over - used)
falls.
- Two validation rules: a request cannot be submitted for more days than
its balance has left, and a sick request over 2 days needs a
certificate.
- Three personas: `employee` (own rows) and `dept_manager` (own
department) carry a row scope; `hr_officer` sees everything except the
medical note, which only the requester sees.
- Seed: 120 requests starting July to October 2026, with 307 day lines,
141 approval steps and 28 balances. The 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 4 agent actions (one
confirm-gated); `iterate` adds time off in lieu.
- `packages/qa/scenarios/test/leave-balances.test.ts` (new): this card's
own test file.
- It pins all 10 computed truths and the 9 per-persona truths against
hand-worked values, with the derivations beside them.
- It asserts the mid-tier floor and checks the seed against the rules
the card states: a request's days equal its weekday lines, a balance
equals its approved requests and the tenure rule, each request's steps
match its days and status, and both validation rules hold on every
submitted request. It also checks that `LR-0091` really exceeds its
balance, so the refused submission is a real refusal.
- It checks that every `where` in an applied post-state matches no
seeded row, so it can only name the row the write creates.
- It shows that a wrong seed cell and a wrong truth spec in a fixture
copy go red against those pins.
## Why
#30479 is one of the six mid-tier cards of the open scenario library.
This one covers HR: leave requests whose approval route depends on their
length, and balances that move when a request is approved.
## Tests
`pnpm --filter @objectstack/scenarios test`: 5 files, 73 tests passed.
`pnpm --filter @objectstack/scenarios typecheck`: exit 0, after building
the closure with `pnpm --filter '@objectstack/scenarios^...' build`.
`eslint --no-inline-config` on the new test file: exit 0.
## Acceptance notes
- **File names.** The card is `leave_balances.json`, not
`leave-balances.json` as the issue spells it: a card's `id` is a
snake_case machine name that must equal the file's basename. The test
file follows the model's spelling, `leave-balances.test.ts`.
- **No shared-file edit.** Since `travel_expense` landed,
`library.test.ts` checks only that the three control cards are present,
and the README's "Adding a card" step points at a per-card test file. So
this PR touches neither, and the sibling cards no longer conflict on a
shared line.
- **No schema field for some floor items.** As with `travel_expense`,
the approval flow, "declarative preferred" and a report as distinct from
a dashboard have no field in the schema. The card states them in
`prompt.detailed` (zh and en) and in `capabilities`, seeds
`approval_step` as an expected object, and declares the report under
`expect.dashboards`. Nothing in the loader grades them.
- **Capability tags.** Where a tag in the closed tag table of the open
scorer PR #30594 fits, the card uses that spelling
(`approvals.approval-flow`, `access-security.row-scope`,
`dashboards.widget-measures`, `records-forms.lookup`), so those tags are
judged once the scorer lands. Tags outside that table read `unscored`
there, not failed.
- **Seven operation kinds, six operations.** `file_and_submit_from_text`
covers two of them: entry from free text with lines, and the multi-step
chain (file the request, add a full day and a morning, submit, route to
the manager).
- **Routed approval step not asserted.** The post-state of
`file_and_submit_from_text` does not assert the routed step-1 row. A
`where` cannot name the new request's id, and `approver e05 · pending`
also matches seeded rows.
- **Dates.** `asOf` is 2026-09-30, so October's requests are the
upcoming ones and every draft or pending request is for future leave;
"last month" is August. Balances belong to leave year 2026/27, which
starts on 1 July, so the seed is the whole leave year so far. Days count
Monday to Friday; public holidays are out of scope and the `$comment`
says so.
- **Reference.** `objectstack-ai/templates` is not attached to this
session, so the domain is designed from the card text alone.
## Verification
<details>
<summary>scenarios test + typecheck</summary>
```
Test Files 5 passed (5)
Tests 73 passed (73)
os-verify-lock: VERDICT command-exit 0
> tsc --noEmit
os-verify-lock: VERDICT command-exit 0
```
</details>
<details>
<summary>one-time check: a wrong cell in the real card goes
red</summary>
`scripts/ablation-replace.mjs` set `LR-0098`'s `days` from 5 to 6 in
`docs/qa/scenarios/leave_balances.json` and ran this card's test file: 4
of 9 tests failed (the pinned truths, the lines check, the balance
check, and the fixture-copy pin, which reads the real card). It then
restored the file: blob equal to HEAD, `git diff HEAD` empty.
</details>
<details>
<summary>dispatch-gates --commands (50 commands, local, head
42b3e57)</summary>
All 50 derived commands exit 0 on head `42b3e57a` (the commit before the
`main` merge). The `--ran` reconciliation reads: 50 derived, 50 run, 0
NOT-MEASURED, 0 UNRUN, a derived zero because every line recorded its
exit code.
Four of them first exited 3 (PREREQUISITE NOT MET). Each exited 0 after
the step it names:
- `check-plugin-teardown-shape.mjs --self-test`, after `git fetch
--unshallow origin main` (the clone was shallow);
- `check:doc-formula-expressions`, `check:lean-entry-closure` and
`check:dual-build-cjs-loads`, after a full `turbo run build` (119 of 119
tasks).
The merge of `main` (9 commits) touches neither `docs/qa/scenarios/` nor
`packages/qa/scenarios/`, and the derivation on the merged head
`9bd3d3c9` names the same 50 commands. On that head the scenarios tests
(5 files, 73 tests) and typecheck pass again.
Changed lines: 1342 (+1342 / -0, 2 files), under the 3000 threshold.
</details>
## Human Minimum
- [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic.
(Not touched.)
- [x] 2. Every name I added uses the casing for its kind.
- [x] 3. No formatter ran; `pnpm lint` passes. (eslint on the new test
file locally; the repo-wide run is CI's.)
- [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.
(`@objectstack/scenarios` is `private: true` and publishes nothing, so
no changeset, `skip-changeset`, as `travel_expense` landed.)
- [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.
- [x] The *Before Submitting* list in CONTRIBUTING.md is done.
---
_Generated by [Claude
Code](https://claude.ai/code/session_016WV4cw5kGUbtDLQqKwTH7E)_
Co-authored-by: Claude <noreply@anthropic.com>1 parent 3da9a4c commit aed9663
2 files changed
Lines changed: 1342 additions & 0 deletions
0 commit comments