test: tighten two checks in the persisted-field harness (closes #379)
check / check (push) Waiting to run
e2e / e2e-chrome (push) Waiting to run
e2e / e2e-firefox (push) Waiting to run

The polarity check now counts only `hostile` and `falsy` values, not a
`hostileRestore` value, which reaches only the views its entry names.
The stale index 5 moves into `hostile` for `selectedWallet` and
`selectedAddress`, as each field's one value still truthy after the
floor.

Each `hostileRestore` entry declares whether its boot lands on its view
or falls back to Home, and the test checks the one view on screen. A
value driven onto every restorable view lists only the views it falls
back on, so a view added later is driven by default.

The header and the README restate the remaining limits as built and say
which boots are held to where the popup lands.

Model: opus-5-5
This commit was merged in pull request #498.
This commit is contained in:
2026-10-07 11:59:14 +02:00
parent 447d714313
commit bb60b399ec
3 changed files with 173 additions and 56 deletions
+144 -48
View File
@@ -49,22 +49,37 @@
// every path a stored record takes, and the difference is the whole of what
// this file does not cover:
//
// - Only the values in the table, in the SLOT arrangement below: four value
// combinations per view, not the product of twelve fields. A dereference
// reached only under a pairing no slot produces is not driven at all.
// - Only what a stored record reaches by ITSELF. A view only forward
// navigation opens, and anything behind a click, is not driven.
// - Nothing about the paths a HEALTHY profile takes, which is most of the
// popup. This file is a floor under one defect class, not a proof about
// the renderers.
// - Only the values in the table, in the SLOT arrangement below. On the
// restore path the twelve fields the router does not read are corrupted
// together, every field on the same slot, so a view gets four value
// combinations of them, not their product. A branch entered only when one
// of them is truthy and another falsy is reached only where the falsy
// slot happens to pair a field that cannot be falsy with one that is.
// Each field the router reads is corrupted alone, over an otherwise
// well-formed record.
// - Only what a stored record reaches by ITSELF, as the boot renders it. A
// view only forward navigation opens, anything behind a click, and
// anything behind a timer (bootPopup() records every interval, and this
// file never runs one) is not driven.
// - Of the paths a HEALTHY profile takes, only its boot onto each
// restorable view ("the base profile the sweep corrupts" below). The rest
// of the popup is not covered: this file is a floor under one defect
// class, not a proof about the renderers.
//
// Within that boundary it is unconditional: if one of these boots leaves the
// popup unhealthy or off the view it stored, this file goes red — including
// when it takes two corrupted fields at once, because the verdict is the
// combined boot itself and the per-field re-boot below can only decorate the
// message. That last part is the one thing an earlier version got wrong: it
// asserted on the per-field list, so an observed dead popup that no single
// field reproduced was reported green.
// Within that boundary it is unconditional: if a boot that corrupts a field
// leaves the popup unhealthy, this file goes red — including when it takes
// two corrupted fields at once, because the verdict is the combined boot
// itself and the per-field re-boot below can only decorate the message. That
// last part is the one thing an earlier version got wrong: it asserted on the
// per-field list, so an observed dead popup that no single field reproduced
// was reported green.
//
// Where the popup lands is held for some of those boots and not others. The
// combined boot must land on the view it stored, and each `hostileRestore`
// value must land on its view, or fall back to Home, as its entry declares.
// The boots in "a hostile routing value restoring onto" are held to health
// alone, because a value in a field the router reads legitimately changes
// which view renders; so are the boots onto Home, which store no view.
//
// Booting every field separately at every value would be several hundred boots
// and most of the suite's budget; this is forty-four. Widening it further is
@@ -128,7 +143,11 @@ const sweptValues = (row) => [...row.hostile, ...(row.falsy || [])];
// list short and pointed. `floorOnly` is extra values checked against the
// floor alone, which is pure and free. `hostileRestore` is extra values driven
// through the restore path only, for a value that means nothing until a
// particular branch's gate has let it past.
// particular branch's gate has let it past. Each of its entries names the
// `views` it is driven onto and declares whether the boot lands on them
// (`restored: true`) or falls back to Home (`restored: false`), so a value
// written for one renderer cannot stop reaching it unnoticed. A value driven
// onto every restorable view is written with everyRestorableView() below.
//
// `falsy` is the other POLARITY of a swept field, driven for the same reason.
// It is not a value src/ never writes — for three of these fields it is the
@@ -139,6 +158,23 @@ const sweptValues = (row) => [...row.hostile, ...(row.falsy || [])];
// falsy value stored under that field comes back TRUTHY from the floor, so no
// `!state.x` branch is reachable from a stored record at all.
// The `hostileRestore` entries for a value driven onto every restorable view:
// it falls back to Home on the views listed in `fallsBackOn` and lands on every
// other one, so a view added to RESTORABLE_VIEWS is driven, and expected to
// land, without editing the row.
function everyRestorableView(value, fallsBackOn) {
return [
{ value, views: fallsBackOn, restored: false },
{
value,
views: [...RESTORABLE_VIEWS].filter(
(view) => !fallsBackOn.includes(view),
),
restored: true,
},
];
}
const CONTRACT = [
{
field: "wallets",
@@ -280,28 +316,38 @@ const CONTRACT = [
kind: KIND.SCALAR,
// The prototype members are the whole point: `wallets["map"]` is
// TRUTHY, so hasValidAddress()'s `&&` does not short-circuit and
// `.addresses[…]` throws. A stale INTEGER is the safe case.
hostile: ["map", "__proto__", { a: 1 }],
// `.addresses[…]` throws. A stale INTEGER is the safe case and has to
// stay so. 5 is one, out of range for the one wallet in the profile,
// and it is also this field's truthy polarity: every other value here
// comes back from the floor as null.
hostile: ["map", "__proto__", { a: 1 }, 5],
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
holds: isIndexOrNull,
// SCALAR, and swept anyway: the restore path is precisely why this
// field gained a floor, so the sweep is the regression guard on it.
alsoSweep: true,
routes: true,
// A stale INTEGER index, which reaches the restore path by a different
// route from the prototype members above — falsy or out of range
// rather than truthy — and has to keep being the safe case.
hostileRestore: [{ value: "length" }, { value: 5 }],
// Comes back from the floor as null, which hasValidAddress() reads as
// nothing selected: the popup falls back to Home on the five views
// that need an address, and lands on every other one.
hostileRestore: everyRestorableView("length", [
"address",
"address-token",
"receive",
"transaction",
"confirm-tx",
]),
},
{
field: "selectedAddress",
kind: KIND.SCALAR,
hostile: ["map", "__proto__", { a: 1 }],
// 5 for the same reason as in selectedWallet: a stale index, and the
// one value here still truthy after the floor.
hostile: ["map", "__proto__", { a: 1 }, 5],
floorOnly: ["length", "constructor", "toString", "0", -1, 1.5, true],
holds: isIndexOrNull,
alsoSweep: true,
routes: true,
hostileRestore: [{ value: 5 }],
},
{
field: "currentView",
@@ -325,7 +371,9 @@ const CONTRACT = [
// container shapes below onto every restorable view; hostileRestore
// adds the records that PASS a branch's gate and then hand its
// renderer something it dereferences, which is where the entries are
// actually decided.
// actually decided. The guard refuses each single-view record below,
// so each is declared to fall back to Home: one that started landing
// would be reaching the renderer it was written against.
hostile: [42, "notarecord", { a: 1 }, [1, 2]],
// `structuredClone(saved.viewData || {})`: the container is never falsy
// in state whatever was stored, so no `!state.viewData` branch exists to
@@ -334,11 +382,20 @@ const CONTRACT = [
hostileRestore: [
// success-tx passes on `data.hash`, and renderSuccess() then calls
// toAddressHtml(d.to) -> addressTitle() -> address.toLowerCase().
{ value: { hash: "0x1" }, views: ["success-tx"] },
{ value: { hash: "0x1", to: 42 }, views: ["success-tx"] },
{
value: { hash: "0x1" },
views: ["success-tx"],
restored: false,
},
{
value: { hash: "0x1", to: 42 },
views: ["success-tx"],
restored: false,
},
{
value: { hash: "0x1", to: ADDRESS, decoded: { details: 7 } },
views: ["success-tx"],
restored: false,
},
{
value: {
@@ -347,12 +404,25 @@ const CONTRACT = [
decoded: { details: [{ address: 42 }] },
},
views: ["success-tx"],
restored: false,
},
// error-tx passes on `data.message`, same dereference.
{ value: { message: "boom" }, views: ["error-tx"] },
{ value: { message: "boom", to: 42 }, views: ["error-tx"] },
{
value: { message: "boom" },
views: ["error-tx"],
restored: false,
},
{
value: { message: "boom", to: 42 },
views: ["error-tx"],
restored: false,
},
// transaction passes on `data.tx`.
{ value: { tx: { hash: "0x1" } }, views: ["transaction"] },
{
value: { tx: { hash: "0x1" } },
views: ["transaction"],
restored: false,
},
{
value: {
tx: {
@@ -363,9 +433,14 @@ const CONTRACT = [
},
},
views: ["transaction"],
restored: false,
},
// confirm-tx passes on `data.pendingTx`.
{ value: { pendingTx: { amount: "1" } }, views: ["confirm-tx"] },
{
value: { pendingTx: { amount: "1" } },
views: ["confirm-tx"],
restored: false,
},
{
value: {
pendingTx: {
@@ -376,6 +451,7 @@ const CONTRACT = [
},
},
views: ["confirm-tx"],
restored: false,
},
// wait-tx passes on `pendingWait.hash`; restoreWait() has checked
// the fields below it since it was written, and this is the
@@ -388,20 +464,29 @@ const CONTRACT = [
},
},
views: ["wait-tx"],
restored: false,
},
// A record that passes EVERY branch's gate at once, driven onto
// every restorable view: a branch a view does not read must stay
// one it does not read, and each renderer must survive the fields
// another branch left behind.
{
value: {
// one it does not read. The five views with a viewData branch
// refuse it; every other one renders it, and must survive the
// fields every branch left behind.
...everyRestorableView(
{
hash: "0x1",
message: "boom",
tx: { hash: "0x1" },
pendingTx: { amount: "1" },
pendingWait: { hash: "0x1" },
},
},
[
"confirm-tx",
"transaction",
"wait-tx",
"success-tx",
"error-tx",
],
),
],
},
{
@@ -583,6 +668,11 @@ const HEALTHY = { errors: [], blank: false };
// set is all-truthy by construction, so without a falsy slot a dereference
// behind `if (!state.x)` is never reached on the boot that corrupts x — the
// same falsy-collapse blind spot the fields below were floored for.
//
// Only `hostile` and `falsy` values count. Those go through the sweep below,
// which covers every restorable view; a `hostileRestore` entry is driven onto
// only the views it names, so a polarity it alone supplied might reach a single
// renderer.
describe("both polarities of every swept field are driven", () => {
const FALSY_STORED = [0, "", false, null];
const floored = (field, value) =>
@@ -606,10 +696,9 @@ describe("both polarities of every swept field are driven", () => {
test(`${row.field}: truthy and falsy`, () => {
// What the boots below actually drive, floored the way a renderer
// sees it — not what the row says it drives.
const driven = [
...sweptValues(row),
...(row.hostileRestore || []).map((entry) => entry.value),
].map((value) => floored(row.field, value));
const driven = sweptValues(row).map((value) =>
floored(row.field, value),
);
expect({
truthy: driven.some((value) => Boolean(value)),
@@ -865,20 +954,27 @@ describe("every field the router does not read, corrupted at once, onto", () =>
// The values that only mean something on the restore path: a viewData that
// PASSES a branch's gate and then hands its renderer something dereferenced,
// and the index values whose route through hasValidAddress() differs from the
// row's own hostile set.
// and a selectedWallet the floor turns into nothing selected. Each boot must
// throw nothing and show exactly the view its entry says it lands on: its own,
// or Home, so a value written for one renderer cannot stop reaching it and
// still pass.
describe("a restore-only hostile value onto", () => {
for (const row of CONTRACT) {
for (const entry of row.hostileRestore || []) {
for (const view of entry.views || RESTORABLE_VIEWS) {
for (const view of entry.views) {
test(`${view}: ${row.field} = ${JSON.stringify(
entry.value,
)}`, async () => {
await expect(
bootHealth(
restoringOnto(view, { [row.field]: entry.value }),
),
).resolves.toEqual(HEALTHY);
const env = await bootPopup(
restoringOnto(view, { [row.field]: entry.value }),
);
expect({
errors: env.pageErrors,
visible: env.visibleViews(),
}).toEqual({
errors: [],
visible: [entry.restored ? view : "main"],
});
});
}
}