跨端: fix(设备闸): app state 交叉验证(矛盾⇒拿不准)+ 正例换成**真机实采**样本

pi 邮件 `6f902e1e` 指出两件,都对:

1. **我那条正例是自相矛盾的**:我用脚本把 `state #FOREGROUND` 对调,却漏了同一块的 `app state #X`
   ⇒ 造出 `state FG` + `app state BG` 这种**真机上不会出现**的 dump。于是"闸能放行"这条正例
   建在**非法输入**上 —— 它绿,但它没证明任何真机会发生的事。
   现在:正例改用 12:08 **真机实采**的 `fixtures/aa-dump-l-ours-foreground.txt`(两个 mission 都在、
   `state`/`app state` 一致、FG 是我们);
2. 顺手把"矛盾怎么办"钉成规则:同一块里 `state` 与 `app state` 打架 ⇒ **`unverified`**,
   **不许"挑一个信"**(那是把互相打脸的证据当成证据)。`app state` 缺失时不因此判未验
   (否则老格式 dump 会一律未验 —— 那是"更安静的失效")。

判据 8 条全绿;登记同步为 8。
This commit is contained in:
2026-09-15 12:10:49 +08:00
parent 329a2e2e81
commit 4880c31110
3 changed files with 37 additions and 17 deletions

View File

@ -35,18 +35,21 @@ test('★ 真机样本:两个 mission 并存,前台是**对方**(这份样
assert.ok(C.unverifiedReason(C.VERDICT_OTHER).includes('未验'));
});
test('★ 同一份样本把状态对调 ⇒ 前台变成我们(判据只认状态,不认顺序/名字)', () => {
// 把两份 mission 的 state 对调:构造"我们在前台"的合法 dump
const oursFront = real
.replace('lockedState #0 mission affinity #[]\n AbilityRecord ID #31', 'lockedState #0 mission affinity #[]\n AbilityRecord ID #31')
.split('\n')
.map((l) => l.includes('#FOREGROUND') ? l.replace('#FOREGROUND', '#BACKGROUND') : l)
.map((l) => l.includes('#BACKGROUND') && l.trim().startsWith('state') && l.includes('start time [84231]') ? l.replace('#BACKGROUND', '#FOREGROUND') : l)
.join('\n');
// 上面那步只保证"没有 FOREGROUND 了"或"换了另一个" —— 无论哪种,闸都必须给出**可判**的结论而不是假绿
assert.equal(C.foregroundVerdict(oursFront, OURS), C.VERDICT_OURS,
'★ 正例必须严格成立:状态对调后前台是我们 ⇒ 必须是 ours。否则这道闸的失效方式是"永远说未验"(更安静的失效)');
assert.equal(C.mayAssertOn(oursFront, OURS), true, 'ours ⇒ 允许断言(可用窗口不会也被判未验)');
test('★ 真样本②(12:08 实采):**我们真的在前台** ⇒ ours + 允许断言(严格正例)', () => {
// 此前这条正例是**对调状态合成**的,而且合成得自相矛盾(state FG 却 app state BG)——
// pi 抓到这点:那种样本不是合法 dump,正例等于建在非法输入上。现在换成**真机实采**的。
const oursReal = prose(join(HERE, 'fixtures', 'aa-dump-l-ours-foreground.txt'));
assert.equal(C.parseMissions(oursReal).length, 2, '真样本里两个 mission 都在(对方 + 我们)');
assert.equal(C.foregroundVerdict(oursReal, OURS), C.VERDICT_OURS);
assert.equal(C.mayAssertOn(oursReal, OURS), true, '★ 没有这条,"闸能放行"这个性质就没人验(失效方式是永远未验)');
assert.equal(C.foregroundBundle(oursReal), OURS);
});
test('★ 自相矛盾的 dump(state FG 但 app state BG)⇒ unverified,不许挑一个信', () => {
const contradictory = prose(join(HERE, 'fixtures', 'aa-dump-l-ours-foreground.txt'))
.split('\n').map((l) => l.includes('app state #FOREGROUND') && l.trim().startsWith('app state') ? l.replace('#FOREGROUND', '#BACKGROUND') : l).join('\n');
assert.equal(C.foregroundVerdict(contradictory, OURS), C.VERDICT_UNVERIFIED,
'同一块里 state 与 app state 打架 ⇒ 这份 dump 自己就不自洽 ⇒ 拿不准,而不是"挑一个信"');
});
test('★ 空 dump / 截断 / 格式变了 ⇒ unverified(缺证据 ≠ 没有那个现象)', () => {