fix(folio): author spec weights to match testing intent

Revert the account saturation gate: it starved newAccountBalanceIsZero
once it tripped, and a magic account count is app-state tuning, not
intent. Instead weight the generators by what the properties need:
the transaction chain leads, account creation stays exercised, and
doubleTaps gets explicit weight everywhere because double-submission
idempotency is what the spec is testing for.
This commit is contained in:
pj committed 2026-06-05 18:05:49 +05:30
1 parent 0b34af7e7a
commit 691e2e985f
1 file changed
+11 -13
+11 -13
View File
@@ -10,7 +10,7 @@ import {
weighted,
whenRoute,
} from "@sanderling/spec";
import { defaultActions } from "@sanderling/spec/defaults";
import { defaultActions, doubleTaps } from "@sanderling/spec/defaults";
import {
computeHomeTotalBalance,
parseTypedAmount,
@@ -134,15 +134,8 @@ const login = actions(() => {
const accountNames = from(["Checking", "Savings", "Travel", "Emergency Fund", "Investments"]);
// Saturation gate: a few accounts are enough to exercise every balance
// property. Without the gate the short add-account loop (2-3 steps, back to
// home) outcompetes the 5-step transaction chain at every re-draw and the
// run fills with account creation instead of transactions.
const ACCOUNTS_ENOUGH = 3;
const addAccount = whenRoute(route, ["home", "add-account"], () => {
if (route.current === "home") {
if (accounts.current.length >= ACCOUNTS_ENOUGH) return [];
const btn = addAccountButton.current;
return btn ? [Tap({ on: btn })] : [];
}
@@ -181,11 +174,16 @@ export const properties = {
export const setup = login;
// Targeted depth (addAccount / addTxn) drives the deep flows; defaultActions
// adds breadth so the fuzzer wanders the whole app and types edge-case values
// into every field, stressing the balance invariants above.
// Weights declare testing intent. The transaction chain is the focus: it is
// the deepest flow and both balance properties observe it. Account creation
// stays in the mix because newAccountBalanceIsZero needs fresh accounts to
// fire. doubleTaps gets explicit weight on every screen because rapid
// double-submission is a failure mode these forms must be idempotent under.
// defaultActions adds breadth so the fuzzer wanders the whole app and types
// edge-case values into every field.
export const actionsRoot = weighted(
[50, addAccount],
[30, addTxn],
[25, addAccount],
[45, addTxn],
[10, doubleTaps],
[20, defaultActions],
);