收口(08-11-internal-oa-finance-backoffice-positioning-and-navigation-reduction): 延期导航减法并固化最小验证
这个提交包含在:
@@ -2037,6 +2037,9 @@ node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT
|
||||
- Readback equals the PRE snapshot -> classify as not applied and stop. A later attempt requires a fresh approval/version lock unless the original approval explicitly defines another attempt.
|
||||
- Partial or mixed readback -> return `STOP_FOR_MANUAL_DECISION`, record a contract/product finding and stop. Do not continue POST, auto-rollback or conceal a broken atomicity contract with manual repair.
|
||||
- Execute an approved rollback only after the forward result is known and a defined POST acceptance condition actually fails. Capture rollback response, final readback and audit deltas separately from the forward evidence.
|
||||
- Make POST validation proportional to the changed dependency graph and failure cost. A reversible, zero-use feature flag with statically verified soft consumers does not justify probing unrelated domains merely because they are available.
|
||||
- For a confirmed low-risk, reversible configuration state, a POST witness failure defaults to preserving the applied state for read-only diagnosis. Rollback requires evidence of a real product regression, not merely a runner assertion, stale error accumulator or probe-contract mismatch.
|
||||
- Every HTTP probe must use the real route query contract. Page-error evidence must be sliced from immediately before one navigation until that navigation completes; errors accumulated during login, PRE or earlier pages cannot fail a later page witness.
|
||||
|
||||
### 4. Validation & Error Matrix
|
||||
|
||||
@@ -2046,6 +2049,7 @@ node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT
|
||||
- All targets at PRE -> stop with zero compensating writes.
|
||||
- Some targets changed -> `STOP_FOR_MANUAL_DECISION`; stop, preserve evidence and request an explicit human decision. Neither continue nor rollback is automatically correct.
|
||||
- Readback itself fails -> state remains unknown; stop with zero further mutation.
|
||||
- Expected POST is confirmed and the only witness fails -> preserve the applied state, open a fresh read-only context and classify the witness. Confirmed product regression may enter an explicitly approved rollback; runner/probe defect must not.
|
||||
|
||||
### 5. Good/Base/Bad Cases
|
||||
|
||||
@@ -2053,6 +2057,7 @@ node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT
|
||||
- Base: the batch response and observer agree, readback confirms the expected state, and the audit delta matches target count.
|
||||
- Bad: treat a page-only counter as authoritative for a context request and immediately restore the PRE values before checking current versions.
|
||||
- Bad: call rollback because it is pre-authorized while the forward result is still unknown.
|
||||
- Bad: add unrelated domain probes to a three-flag soft-disable, send unsupported query parameters, or let errors from an earlier page fail the current Portal witness.
|
||||
|
||||
### 6. Tests Required
|
||||
|
||||
@@ -2060,6 +2065,8 @@ node scripts/qa/payment-batch-flag-snapshot.mjs restore --snapshot-file "$REPORT
|
||||
- Cases for response success/counter agreement, response missing with POST state, response missing with PRE state, readback failure and partial/mixed state.
|
||||
- Assert mutation ordering: forward at most once -> uncertain readback/audit only -> either POST verification or stop -> rollback at most once only after a concrete approved acceptance failure.
|
||||
- Assert final evidence reconciles response count, readback versions, global audit delta and per-target changelog delta; no one source may silently override contradictions.
|
||||
- Derive every probe path/query from the actual route/client contract. For browser witnesses, reset or snapshot the page-error index before each navigation and assert only the new slice.
|
||||
- For low-risk reversible changes, test that a witness failure preserves POST state for diagnosis and that rollback is unreachable until a fresh read-only check confirms product regression.
|
||||
|
||||
### 7. Wrong vs Correct
|
||||
|
||||
@@ -2077,7 +2084,9 @@ forward response/counter disagree -> APPLY_UNCERTAIN
|
||||
-> GET every target version and state -> reconcile audit/changelog
|
||||
-> expected POST means continue verification
|
||||
-> PRE means stop
|
||||
-> rollback only after a real approved POST failure
|
||||
-> minimal dependency-matched witness
|
||||
-> witness failure preserves state for fresh read-only diagnosis
|
||||
-> rollback only after confirmed product regression and explicit approval
|
||||
```
|
||||
|
||||
## Scenario: Unique Production Administrator Break-Glass Reset
|
||||
|
||||
+34
-6
@@ -82,10 +82,10 @@
|
||||
|
||||
## 6. 收口
|
||||
|
||||
- [ ] 更新 task 证据,明确产品代码零改动、部署 0、最终 target=3、batch 次数、审计增量、项目本地准入失败和二级入口延后。
|
||||
- [ ] 重跑 task/governance/process/docs/sensitive/diff 门禁。
|
||||
- [ ] 中文 commit 引用 task 名;不自动推送。
|
||||
- [ ] 用户审查后归档,不创建二级入口或退役子 task。
|
||||
- [x] 更新 task 证据,明确产品代码零改动、部署 0、最终 target=3、两轮 batch/恢复、审计增量、项目本地准入失败和二级入口延后。
|
||||
- [x] 重跑 task/governance/process/docs/sensitive/diff 门禁。
|
||||
- [x] 中文 commit 引用 task 名;不自动推送。
|
||||
- [ ] 按用户批准归档,不创建二级入口、退役或第三次生产尝试 task。
|
||||
|
||||
## 7. 恢复后再次准入(任何新生产批准之前)
|
||||
|
||||
@@ -94,8 +94,27 @@
|
||||
- [x] 本地覆盖响应缺失但 GET 为 POST、响应缺失但 GET 为 PRE、GET 失败、部分状态;分别得到 `CONTINUE_POST`、`STOP_NOT_APPLIED`、`STOP_UNKNOWN`、`STOP_FOR_MANUAL_DECISION`。部分状态不自动继续或恢复,必须停止并申请人工决策。
|
||||
- [x] `node --check scripts/qa/feature-flag-apply-uncertainty.mjs` PASS;focused Vitest `5/5` PASS。
|
||||
- [x] `npm run prisma:validate` PASS。完整 server Vitest 发现既有付款单导出用例 1 条稳定失败(期望 200、实际 409);在干净 `HEAD` 临时 worktree 中单独运行同一标题仍失败,证明不是本次 QA 状态机/测试引入。本任务不修改付款业务代码,focused 状态机用例保持 5/5 PASS。
|
||||
- [ ] 重新执行生产 PRE,以现场 `version=49`(或届时新值)冻结三项目标和审计基线;不得沿用 `version=47`。
|
||||
- [ ] 形成新的生产批准包并等待明确批准;本次一次前向与一次恢复的批准均已消耗。
|
||||
- [x] 重新执行生产 PRE,以现场 `version=49` 冻结三项目标和审计基线;不得沿用 `version=47`。身份、运行 SHA、migration、窗口、flag snapshot、审计基线和三模块零使用证据均已重新取得。
|
||||
- [x] 形成新的生产批准包并等待明确批准;本次一次前向与一次恢复的批准均已消耗,新的 batch 尚未批准、尚未执行。
|
||||
|
||||
### 7.1 新一轮生产 PRE 的已确认部分(2026-08-13)
|
||||
|
||||
- 管理员只读现场:一次登录、正常 logout,配置 mutation=0、业务 mutation=0。三项仍恰好为 `force_on/global/departmentIds=[]/version=49/effective=true/reason=flag_force_on_global`;note 均非空且脱敏 hash 一致,未保存或输出原文。全局 `FEATURE_FLAG_UPDATED total=7`,三项目标 changelog 均为 2;公告、普通待办、周报和项目导航均存在,Portal 返回 200。
|
||||
- 运行门禁:唯一生产身份 PASS;运行 build 精确等于 `origin/main` 已部署候选,本地提交相对该候选在 `client/src`、`server/src` 和部署 compose 上产品差异为 0;数据库身份 PASS,migration `pending=none`,回滚窗口仍为 `OPEN / terminatedRows=0 / basis=terminated_status_count`。
|
||||
- 使用度首次查询:复用归档查询器严格执行一次,执行器报告该次 `queryExecuted=1 / zeroRetry=1`,内置合同确认只读事务、20 个模块齐全且全部 `queryStatus=PASS`。本地脱敏过滤器误用 `module` 而非真实字段 `moduleKey`,因此没有保留三个目标模块的当前聚合值;没有把该次成功查询伪写成“三模块仍为零”。
|
||||
- 使用度 supplemental:用户另行批准后又执行一次、明确标注为 supplemental 的只读查询;它不是首次查询的 retry。三个目标 `announcements`、`todos`、`weekly_reports` 均为 `totalRows=0`,30/90 天活动、真人 actor/event 和真人审计 actor/event 全部为 0,`effectiveFlagState=on`、`queryStatus=PASS`。该次自身 `queryExecuted=1 / zeroRetry=1`;本轮合计发生两次只读 census 执行,不合并成一次。
|
||||
- supplemental 脱敏过滤器按 `moduleKey` fail-closed:必须恰好命中三个唯一目标,完整数值字段必须为非负整数,query/flag 状态必须符合预期;少项、重复、字段异常或任一使用度非零均直接失败并停止。本次 `targetCount=3` 且全部通过。
|
||||
- 新锁输入:三个目标恰好为 `announcements_v1@49`、`todo_board_v1@49`、`weekly_report_v1@49`;全局审计基线 7、各目标 changelog 2。正式写入前仍须读取现场值做 version lock;若任一项不再为 49、状态/审计漂移或使用度前提改变,停止而不是沿用本批准包。
|
||||
|
||||
### 7.2 第二次生产 APPLY 与恢复(2026-08-13)
|
||||
|
||||
- 批准边界:严格一次三目标前向 batch;只有“前向已通过现场 readback 确认落地”且“真实 POST 验收失败”同时成立,才允许最多一次恢复。观察器信号本身不能触发恢复。本轮没有单项 PUT、workspace PATCH、其他 flag 或业务数据写入。
|
||||
- 写前门禁:身份、运行 build、数据库身份与 migration 再次 PASS,回滚窗口 `OPEN / terminatedRows=0 / basis=terminated_status_count`。同一执行现场确认三项目标仍为 `force_on@49`,全局审计 7、各目标 changelog 2,导航目标均存在。
|
||||
- 前向结果:`executeFeatureFlagApplyAttempt` 发送计数严格为 1;batch response `count=3`,无重试。无条件 readback 确认三项统一进入 `force_off@50/effective=false/reason=flag_force_off`,因此前向确实已落地。页面 observer 没有观察 `APIRequestContext` 请求只形成 `observerMismatch`,没有直接触发恢复。
|
||||
- 恢复触发:POST witness 报告两个门禁失败——合同台账列表 HTTP 400、Portal UI 失败。由于前向已确认落地且当时执行器把这两项判为真实 POST 失败,按获批分支严格一次恢复;恢复 batch response `count=3`,readback 为 `CONTINUE_POST`,三项统一回到 `force_on@51/effective=true/reason=flag_force_on_global`。配置 mutation 恰好为一次前向 + 一次恢复,业务 mutation=0。
|
||||
- 失败定性:恢复后用全新浏览器/API context 只读复核,证明两项均是 witness 假失败,而非产品故障。合同列表端点不接受 `page/pageSize`,执行器错误附加分页参数才得到 400;正确 `GET /api/contracts-registry` 为 200。Portal 门禁错误地复用了整段会话累计的 `pageErrors`;全新分段观察 `/portal` 正常、正文存在、无新 page error、无 API 5xx。报销、合同、采购、资产、项目五个保留域均为 200。
|
||||
- 审计与终态:全局 `FEATURE_FLAG_UPDATED` 从 7 到 13,增量 6;各目标 changelog 从 2 到 4,各增量 2,精确对应一次前向和一次恢复。导航已恢复,写后回滚窗口仍为 `OPEN / terminatedRows=0 / basis=terminated_status_count`,migration `pending=none`。
|
||||
- 结论:生产安全,但“关闭三项并保持关闭”的业务目标再次未达成。本次前向与恢复批准均已消费;`expectedVersion=49` 已失效,任何后续写入必须以现场 `version=51` 重新锁定并另获明确批准,禁止在本轮第三次尝试。
|
||||
|
||||
## 风险与停止条件
|
||||
|
||||
@@ -107,3 +126,12 @@
|
||||
## Residual Register
|
||||
|
||||
- `OPEN / OUT_OF_SCOPE`:付款批次 BIFF8 导出单测“exports a reviewed CCB batch as BIFF8 and records the first export”稳定得到 409、预期 200;完整 server suite 与干净 `HEAD` 临时 worktree 的单标题复现结果一致,证明不是本任务 QA 状态机改动引入。后续应独立定性运行依赖、模板门禁或产品行为,不得长期把该红灯当作可忽略背景噪音;本任务不修改付款业务代码或测试断言。
|
||||
- `OPEN / TOOLING`:归档后的 `run-production-usage-census.sh` 仍按活动 task 相对层级计算仓库根,并引用归档前 query 路径;直接运行会在本地 source deployment contract library 前失败,远端查询为 0。本轮通过进程内只读路径替换复用既有 runner,未修改归档证据;后续若正式复用该入口,应让 runner 对归档移动稳定或提供受测的显式仓库根,不把本地路径漂移误判为生产故障。
|
||||
- `OPEN / TOOLING`:第二次生产 APPLY 的恢复由两个 witness 假失败触发:合同列表探针使用了端点不支持的分页参数;Portal 门禁跨阶段累计 `pageErrors`。两者在全新只读 context 中均证明产品正常,但恢复已经执行。后续若再次尝试导航减法,必须先用本地用例固定“每个保留域使用真实查询合同”和“页面错误按单个导航阶段切片”,不得直接复用本次一次性 runner。
|
||||
|
||||
## Lifecycle Closeout
|
||||
|
||||
- 完成:财务与低频台账后台定位已进入事实源和治理 spec;项目 flag 因报销跨域降级被排除;三目标零使用与生产安全终态已有证据;QA 状态机 5/5;两轮前向和恢复的版本/审计链完整。
|
||||
- 未完成:公告、普通待办、周报仍为 `force_on@51`,导航减法未保持生效;POST 终态相关 AC 保持未勾选。
|
||||
- 停止原因:当前真人采用度过低,入口复杂度尚未形成真实摩擦;继续修建一次性生产 witness 的成本超过业务收益。两次成功写入被验证工具假失败撤销,说明下一步不应继续增加验证复杂度。
|
||||
- 替代条件:不创建即时工程任务。下一轮真实报销由管理要求走 OA;只有多名真人持续使用且出现导航摩擦时才重新评估导航减法。届时验证只覆盖实际依赖和最小 `/portal` witness,失败默认保留已知低风险现场做只读诊断,确认产品回归后才恢复。
|
||||
|
||||
+12
-6
@@ -76,15 +76,15 @@
|
||||
|
||||
## Acceptance Criteria
|
||||
|
||||
- [ ] AC1:产品口径和治理 spec 统一写明“财务与低频台账后台 + 核心流程真实采用”,不再保留 A/B 未决状态或 R63–R66 扩建主线。
|
||||
- [ ] AC2:git diff 证明产品运行代码、migration、依赖和部署配置零改动;首次恢复后的唯一代码例外是 `scripts/qa` 状态机及其 focused 单元测试,无构建、部署或 build SHA 变化。
|
||||
- [ ] AC3:本地关闭 `project_domain_v1` 后,分别验证未关联和已关联项目的报销详情;只有控件状态明确、已关联项目名称/编号仍正确且无可见错误才允许生产 target=4,否则固定 target=3。
|
||||
- [ ] AC4:生产 PRE 现场确认三个最终目标模块仍为零数据/零真人活动,三项 flag 目标恰好、effective on、版本已冻结,且完整记录原始 snapshot 与影响范围。
|
||||
- [x] AC1:产品口径和治理 spec 统一写明“财务与低频台账后台 + 核心流程真实采用”,不再保留 A/B 未决状态或 R63–R66 扩建主线。
|
||||
- [x] AC2:git diff 证明产品运行代码、migration、依赖和部署配置零改动;首次恢复后的唯一代码例外是 `scripts/qa` 状态机及其 focused 单元测试,无构建、部署或 build SHA 变化。
|
||||
- [x] AC3:本地关闭 `project_domain_v1` 后,分别验证未关联和已关联项目的报销详情;只有控件状态明确、已关联项目名称/编号仍正确且无可见错误才允许生产 target=4,否则固定 target=3。
|
||||
- [x] AC4:生产 PRE 现场确认三个最终目标模块仍为零数据/零真人活动,三项 flag 目标恰好、effective on、版本已冻结,且完整记录原始 snapshot 与影响范围。
|
||||
- [ ] AC5:仅在生产写入另获批准后,通过既有页面/API 严格执行一次最终 target batch `force_off`;无单项 PUT、无 workspace PATCH、无其他 flag 或业务数据写入。
|
||||
- [ ] AC6:POST 证明最终 target 均 effective off、version 各 `+1`、审计增量等于 target 数;对应导航消失,直达得到可解释的 disabled 结果。
|
||||
- [ ] AC7:Portal、报销、合同、采购、资产只读入口仍可加载;若项目 flag 入选,已关联项目显示仍正确;`project_linking_v1` 与其他 flag 不变,非认证业务 mutation 为 0。
|
||||
- [ ] AC8:阶段二和退役动作均未实施;合同、采购、资产继续保持可用,二级入口只作为事件触发的后续候选。
|
||||
- [ ] AC9:task validate、Trellis governance、process contract、docs registry、敏感扫描和 `git diff --check` 通过;生产证据不含账号、Secret、token 或原始业务 payload。
|
||||
- [x] AC8:阶段二和退役动作均未实施;合同、采购、资产继续保持可用,二级入口只作为事件触发的后续候选。
|
||||
- [x] AC9:task validate、Trellis governance、process contract、docs registry、敏感扫描和 `git diff --check` 通过;生产证据不含账号、Secret、token 或原始业务 payload。
|
||||
|
||||
## Out of Scope
|
||||
|
||||
@@ -112,3 +112,9 @@
|
||||
## Retry Admission After Restored Attempt
|
||||
|
||||
2026-08-12 获批的一次前向 batch 已成功落地,但本地页面观察器漏掉 `APIRequestContext` 写入并误触发预授权恢复;生产最终安全回到 `force_on@49`。下一次生产前向写入不是本次批准的延续:必须以现场 version 重新锁定、先在本地覆盖“写入成功但页面观察为 0”及全部不确定分支,并另获明确批准。本任务不得以修正文档为由自动再次写生产。
|
||||
|
||||
## Lifecycle Closeout
|
||||
|
||||
本 task 于 2026-08-13 按用户决定停止继续生产尝试并归档。正式产品定位、项目 flag 排除、本地不确定性状态机、两轮生产事实和安全终态均已完成记录;三项导航保持关闭这一原目标没有达成,AC5–AC7 保持未勾选。两轮前向 batch 都成功,随后分别因观察器和 POST witness 的假失败恢复,生产终态为三项 `force_on@51`、业务 mutation=0、回滚窗口 `OPEN/0`。
|
||||
|
||||
停止原因是成本收益不再成立,而不是产品无法关闭:当前仅有极低真人使用,三个多余入口没有可观察摩擦;继续建设验证装置的成本已经超过导航减法的即时价值。导航减法只在“多名真人持续使用且出现明确导航摩擦”时重新评估,并须创建或更新当时的 Trellis task,不沿用本 task 的版本锁或生产批准。当前下一步是管理动作——下一轮真实报销要求走 OA,用真人摩擦决定后续工程工作;在此之前不创建猜测性产品任务。
|
||||
|
||||
+1
-1
@@ -28,6 +28,6 @@
|
||||
"server/src/routes/featureFlags.js",
|
||||
".trellis/tasks/archive/2026-08/08-11-internal-oa-usage-census-and-simplification/research/production-usage-snapshot.json"
|
||||
],
|
||||
"notes": "定位与 PRE 已完成,project_domain_v1 已排除。2026-08-12 获批的一次三 flag batch 成功写入 force_off@48,但本地执行器漏计 APIRequestContext 请求并误触发预授权恢复;三项安全恢复为 force_on@49。最终审计增量全局 +6、各目标 +2,Portal 200、导航恢复、回滚窗口 OPEN/0、业务写入 0。一次只读核验因导航响应形状误判在 logout 前退出,遗留 1 个无人持有、自然过期的服务端 session。保持关闭的业务目标未完成;不得沿用 version=47 或本次批准重试。再次申请生产前必须本地覆盖观察矛盾及不确定分支、重新锁定现场 version 并另获批准。",
|
||||
"notes": "生命周期关闭:财务与低频台账后台定位、项目 flag 排除、QA 不确定性状态机和生产证据已完成。两轮三 flag 前向 batch 均成功,分别因观察器和 POST witness 假失败恢复;生产安全终态为三项 force_on@51、全局审计 13、各目标 changelog 4、业务 mutation 0、窗口 OPEN/0。导航减法未保持生效,AC5-AC7 未完成。用户决定停止第三次尝试:当前极低真人采用下收益不支持继续投入;仅在多名真人持续使用并出现导航摩擦时重新评估。下一步是管理要求下一轮真实报销走 OA,不创建即时工程任务。",
|
||||
"meta": {}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户