恢复付款入口与常开基线(08-01-reimbursement-notification-progress-clarity)
ci-e2e / e2e (push) Canceled after 0s
ci-minimal / check-minimal (push) Canceled after 0s

这个提交包含在:
2026-08-01 23:12:23 +08:00
父节点 aa6b9bc373
当前提交 37b4ef2d10
共修改 20 个文件,包含 655 行新增和 244 行删除
@@ -1404,3 +1404,79 @@ DEPLOY_SERVER=<approved-ssh-target> \
```json
{"preserve_original":["FIELD_ENCRYPTION_KEY"]}
```
## Scenario: Formal Feature-Flag Baseline And Reversible QA Window
### 1. Scope / Trigger
- Trigger: a feature is formally enabled after an earlier short-window rollout, or a QA runner temporarily changes an existing feature flag.
- Purpose: prevent a new database, stale `.env` or QA cleanup path from silently returning a formal capability to its old disabled state.
### 2. Signatures
- Hard gate: `PAYMENT_BATCH_ENABLED=true` in `deploy/trialrun/.env.example` and the active production env.
- Database baseline: `payment_batch_v1` is first-created as `force_on/global`; `seed:config` still uses create plus `P2002` preservation and never updates an existing row.
- Deployment assertion: `smoke-trialrun-runtime.js` requires both the env value and effective `FEATURE_FLAG_KEYS.paymentBatch` result.
- QA snapshot helper: `node scripts/qa/payment-batch-flag-snapshot.mjs capture|restore --snapshot-file <private-json>`.
- Mobile entry: `/mobile/payment-batches`; PC remains `/payment-batches`.
### 3. Contracts
- Formal enablement is a three-layer contract: env hard gate, database feature flag and runtime smoke. Changing only one layer is incomplete.
- A short QA window captures `overrideMode`, `rolloutType`, `departmentIds` and `note` before its first flag write and restores those fields in `finally`; it must work when the caller starts either enabled or disabled.
- A formal-enable runner keeps `force_on` only after every acceptance step passes. Any failed step restores its caller snapshot, not a hard-coded `force_off`.
- Snapshot files use mode `0600`, contain configuration only and remain under the ignored QA report directory. Credentials and access tokens never enter the snapshot or output.
- Mobile links must target the mobile route. Enterprise-messenger routing maps a PC-only payment path back to the mobile workbench, so a PC path is not a valid mobile deep link even when the underlying page is responsive.
- Mobile and PC payment routes share the same permission guard; adding reachability must not broaden `payment.batch.manage|payment.batch.review|risk.review.manage`.
### 4. Validation & Error Matrix
- Env false or absent -> runtime smoke fails before deployment is accepted.
- Database flag absent/disabled -> runtime smoke fails with the feature-flag reason; do not hide the failure by changing navigation only.
- Seed meets an existing flag -> `P2002` is preserved with zero update/upsert calls.
- QA capture fails -> stop before the temporary window; do not run with an unknown rollback target.
- QA action or later matrix fails -> restore the caller snapshot and keep the run failed.
- Snapshot restore version conflicts -> fail visibly; never fall back to `force_off`.
- Mobile finance entry points at `/payment-batches` -> small-screen routing returns to `/mobile`; treat this as an unreachable entry and use `/mobile/payment-batches`.
- User lacks payment permission -> both routes remain forbidden even if the feature is enabled.
### 5. Good/Base/Bad Cases
- Good: env true + seed first-create force-on + effective runtime smoke pass; a temporary off/on test restores an original allowlist and note byte-for-byte.
- Base: a database already contains an operator-managed flag; deployment seed preserves it and runtime smoke reports whether it matches the formal baseline.
- Bad: change only the DB flag while env remains false, or make the mobile card visible but link it to a path that the mobile shell redirects home.
- Bad: every QA cleanup writes `force_off`, erasing a formal-on state that existed before the test.
### 6. Tests Required
- Seed unit test asserts first-create `force_on/global` and proves `P2002` performs no update/upsert.
- Snapshot helper tests cover an original `force_off` allowlist and an original `force_on` global configuration, including note restoration.
- Deployment contract test asserts the env baseline, runtime-smoke flag assertion and absence of fixed-off cleanup helpers.
- Mobile Playwright covers zero batches/zero exceptions with the feature enabled, a visible “进入付款” link, successful `/mobile/payment-batches` navigation and no finance card for an unrelated role.
- Run client typecheck/build, Prisma validate, shell/Node syntax, task/governance/process/sensitive gates and `git diff --check`.
- Public rollout additionally verifies active env, effective flag reason, finance navigation and eligible candidate count without creating, reviewing or executing a real batch.
### 7. Wrong vs Correct
#### Wrong
```bash
# Cleanup assumes disabled was always the caller's state.
PUT payment_batch_v1 '{"overrideMode":"force_off"}'
```
```tsx
<Link to="/payment-batches">进入付款</Link>
```
#### Correct
```bash
node scripts/qa/payment-batch-flag-snapshot.mjs capture --snapshot-file "$REPORT_DIR/flag-before.json"
# temporary test writes
node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT_DIR/flag-before.json"
```
```tsx
<Link to="/mobile/payment-batches">进入付款</Link>
```