test(mobile): register the container-requirement mutant the matrix kills

`f1b695f161` proved by hand that making `linearAccountStatusSchema` `.nullable()`
fails the `result-null` partition of `tasks.provider-load`'s matrix, and left it
unregistered. Registering it in `pilot-mutants.test.ts` is not available: that
suite drives the manifest scenario as written, and `tk-provider-load` scripts a
fulfilled `linear.status`, which a nullable container accepts exactly as the
required one does. The mutation only has somewhere to diverge under a partition
the pilot never reaches.

`family-mutants.test.ts` drives one named variant of a family's matrix instead,
against that variant's own slice of the golden, and each entry names both the
partition that kills the mutation and one that cannot see it — so the entry
records where the coverage is rather than that some golden went red. The
comparison is the whole recorded variant, the way the family suite compares:
under a last-state projection this mutant survives, because the divergence is in
the settlement and both paths reach the same final screen state.

Nothing under `mutants/` is pinned by a golden header, so this moves no
recording.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
This commit is contained in:
Jinwoo-H
2026-09-17 00:43:17 -04:00
parent c3ebb298f3
commit 542c1c38ed
2 changed files with 110 additions and 0 deletions
@@ -0,0 +1,99 @@
import { resolve } from 'node:path'
import { describe, expect, it } from 'vitest'
import { familyGoldens } from '../derived-goldens'
import { readGolden } from '../golden-recording'
import { pilotMountAdapters } from '../pilot-mount-adapters'
import { runRecordingMutant } from '../run-recording'
import { readScenarios } from '../scenario-input'
import { vitestRecordingScheduler } from '../vitest-recording-scheduler'
import { operationMutation, type Mutation } from './operation-mutations'
import type { Recording } from '../recording-scenario'
const root = resolve(import.meta.dirname, '../../../../..')
const input = readScenarios(
process.env.RPC_FOUNDATION_SCENARIOS ??
resolve(root, 'mobile/rpc-foundation/pilot-scenarios.json')
)
const goldens = process.env.RPC_FOUNDATION_GOLDENS ?? resolve(root, 'mobile/rpc-foundation/goldens')
/**
* A mutant whose divergence only exists under one reply partition, so the pilot suite cannot hold
* it: `pilot-mutants.test.ts` drives the manifest scenario as written, and a scenario that scripts
* a fulfilled reply never reaches the shape the mutation is about.
*
* The comparison is the whole recorded variant, not its last visible state, because that is what
* the family suite compares: a container requirement can diverge at the settlement and reach the
* same final state, which is how `linear-status-nullable` survives a last-state projection.
*
* Loosening a *container* requirement is that class, and the reply matrix is the only part of the
* corpus that reaches it — the matrix varies the envelope a host sends and never the shape of a row
* inside a result, which is why a row requirement stays a unit pin. Each entry names the partition
* that kills the mutation and one that cannot see it, so an entry states where the coverage is
* rather than only that some golden went red.
*/
const MATRIX_MUTANTS: readonly {
mutation: Mutation
family: string
site: string
killedBy: string
blindTo: string
}[] = [
{
mutation: 'linear-status-nullable',
family: 'tasks.provider-load',
site: 'linear.status#1',
killedBy: 'result-null',
blindTo: 'normal'
}
]
/** The variant's own slice of the family golden, which records every variant in one file. */
function variantBaseline(golden: Recording, scenarioId: string): Recording {
const prefix = `${scenarioId}:`
const checkpoints = golden.checkpoints
.filter((checkpoint) => checkpoint.id.startsWith(prefix))
.map((checkpoint) => ({ ...checkpoint, id: checkpoint.id.slice(prefix.length) }))
if (!checkpoints.length) {
throw new Error(`No recorded checkpoints for ${scenarioId} in ${golden.scenario}`)
}
return { scenario: scenarioId, checkpoints }
}
async function verdict(
entry: (typeof MATRIX_MUTANTS)[number],
partition: string
): Promise<'killed' | 'survived'> {
const derived = familyGoldens(input.scenarios).find(
(golden) => golden.family === entry.family && golden.site === entry.site
)
if (!derived) {
throw new Error(`No matrix golden for ${entry.family} at ${entry.site}`)
}
const scenario = derived.scenarios().find((candidate) => candidate.id.endsWith(`.${partition}`))
if (!scenario) {
throw new Error(`No ${partition} variant of ${derived.id}`)
}
const { adapters, assertMutationApplied } = pilotMountAdapters(root, {
device: scenario,
mutation: operationMutation(entry.mutation)
})
const result = await runRecordingMutant(
scenario,
adapters[scenario.operation],
vitestRecordingScheduler(),
variantBaseline(readGolden(goldens, derived.id).recording, scenario.id)
)
assertMutationApplied()
return result.verdict
}
describe('reply-matrix partitions hold the container requirements', () => {
for (const entry of MATRIX_MUTANTS) {
it(`${entry.family} ${entry.site}: ${entry.killedBy} kills ${entry.mutation}`, async () => {
expect(await verdict(entry, entry.killedBy)).toBe('killed')
}, 30_000)
it(`${entry.family} ${entry.site}: ${entry.blindTo} cannot see ${entry.mutation}`, async () => {
expect(await verdict(entry, entry.blindTo)).toBe('survived')
}, 30_000)
}
})
@@ -255,6 +255,17 @@ export const OPERATION_MUTATIONS = {
before: ' await ensureSessionTabs().catch(() => null)',
after: ' await ensureSessionTabs()'
},
// Accepts a `null` Linear status as the status itself, which is the container requirement the
// whole domain rests on: main read `status.connected` off that null and threw the property-read
// TypeError the Tasks screen showed as its load error. Only the `result-null` partition of the
// matrix can see it, so `family-mutants.test.ts` drives that variant rather than the pilot.
'linear-status-nullable': {
file: 'task-list-reply-schema.ts',
before: ` activeWorkspaceId: salvagedOptional('activeWorkspaceId', z.string().nullable())
})`,
after: ` activeWorkspaceId: salvagedOptional('activeWorkspaceId', z.string().nullable())
}).nullable()`
},
// Collapses the assignable-user row's explicit `avatarUrl: null` into absence, so a host that
// reported "this user has no avatar" becomes indistinguishable from one that does not report
// avatars at all, and the picker draws its initials placeholder for both. The null-collapse class