修正门禁测试定位与动画测量(09-17-company-loan-principal-repayment)

保留原断言、容差及运行配置;两份完整测试定向 61/61 通过,补充可复用规约及失败门禁记录。提交后对固定干净候选重跑 verify-candidate。真实回单、人员与独立业务验收仍待核实;本轮不推送、不部署。
这个提交包含在:
2026-10-08 09:49:20 +08:00
父节点 1dd840655b
当前提交 4c37fceeb4
共修改 4 个文件,包含 34 行新增和 4 行删除
@@ -159,6 +159,7 @@ and verification contracts. Do not copy broad aesthetic guidance from
- At the narrowest viewport where the fixed sidebar appears, the main detail column is at least `560px`, the action column is at least `280px`, and the document has no horizontal overflow beyond a `2px` rendering tolerance.
- Business identifiers and normal company-time date strings use DOM line-count or geometry assertions; `position: sticky` alone is not proof that the layout is usable.
- A Playwright spec writes screenshots only to its own current output directory. Never reuse an archived Trellis task's evidence directory as a live test target.
- When comparing alignment inside an animated ancestor (for example `.page-scaffold`'s `page-reveal`), read all relevant `getBoundingClientRect()` values in one synchronous browser `evaluate`/`evaluateAll` callback. Sequential `boundingBox()` calls, or calls grouped with `Promise.all`, can observe different animation frames. Keep the original alignment assertions and pixel tolerances; do not hide the race with sleeps, retries or disabled page animations.
### 4. Validation & Error Matrix
@@ -997,7 +998,9 @@ point and prevents each route from inventing a different title/action layout.
hidden unless `leave.manage`; attendance correction forms are hidden unless
`attendance.manage`. Labels must say `OA 应急` / `提交应急审批` /
`提交应急修正` instead of generic `提交`.
- When a page repeats the same title in the page header and a content section,
- Title assertions should use an exact heading role within the intended panel;
broad `getByText("票据详情")` can also match transient loading copy. When a
page repeats the same title in the page header and a content section,
Playwright assertions should target the page header (`h1` or the header test
id) rather than `getByRole("heading", { name })`, which becomes ambiguous.
- `个人设置` stays in the topbar account menu. Notifications stay in the topbar