test: remove redundant cases and start comprehensive value tracker (#32269)

Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com>
This commit is contained in:
OrcaWin
2026-10-23 02:37:13 -07:00
committed by GitHub
co-authored by OrcaWin
parent c32d46850b
commit bc99add289
32 changed files with 17297 additions and 587 deletions
@@ -104,24 +104,6 @@ describe('client ip rate limiter', () => {
expect(otherClient.status).toBe(200)
})
it('gives a spoofed left-most hop no escape from the caller own bucket', async () => {
const app = limiterApp(new ClientIpRateLimiter({ now: () => 1_000 }))
// A caller that rewrites its own x-forwarded-for on every request still ends
// up behind the one value Cloud Run appended.
for (let index = 0; index < CAPACITY; index++) {
const allowed = await app.request('/probe', {
method: 'POST',
headers: { 'x-forwarded-for': `198.51.100.${index}, 203.0.113.7` }
})
expect(allowed.status).toBe(200)
}
const spoofed = await app.request('/probe', {
method: 'POST',
headers: { 'x-forwarded-for': '198.51.100.250, 10.1.1.1, 203.0.113.7' }
})
expect(spoofed.status).toBe(429)
})
it('skips the configured trusted proxies when counting from the right', async () => {
const app = limiterApp(new ClientIpRateLimiter({ now: () => 1_000, capacity: 1 }), 1)
// <client>, <cloud run>, <load balancer>: one trusted hop after the client.
@@ -1,4 +1,4 @@
import { afterEach, expect, it } from 'vitest'
import { afterEach, expect, it, vi } from 'vitest'
import type { PushDatabase } from './push-database.js'
import { DELIVERY_LEASE_MS, DurablePushStore } from './durable-push-store.js'
import {
@@ -54,11 +54,23 @@ it.skipIf(!durablePushTestDatabaseUrl)(
let committed!: () => void
const currentCommitted = new Promise<void>((resolve) => (committed = resolve))
const previous = previousRevisionClaim(db, clock, () => currentCommitted)
// Room for the previous claim to scan while this revision's lease is still uncommitted.
await new Promise((resolve) => setTimeout(resolve, 200))
paused.release()
try {
await vi.waitFor(async () => {
const waiting = await db.query(
"SELECT 1 FROM pg_stat_activity WHERE datname = current_database() AND wait_event_type = 'Lock' AND wait_event = 'advisory'"
)
expect(waiting).toHaveLength(1)
})
} finally {
paused.release()
try {
await current
} finally {
committed()
await previous
}
}
const leased = await current
committed()
expect(await previous).toBeNull()
const [row] = await db.query('SELECT lease_token FROM push_delivery_batches')
expect(leased?.lease).toBeTruthy()
@@ -1,7 +1,5 @@
import { createHash } from 'node:crypto'
import { afterEach, expect, it, vi } from 'vitest'
import { DurablePushStore } from './durable-push-store.js'
import { buildPushDelivery } from './push-delivery-message.js'
import type { PushDatabase } from './push-database.js'
import {
cleanupDurablePushFixtures,
@@ -9,22 +7,15 @@ import {
notification
} from './durable-push-store.test-fixture.js'
type QueryCall = { sql: string; params?: unknown[] }
afterEach(async () => {
vi.restoreAllMocks()
await cleanupDurablePushFixtures()
})
function traceDatabase(
database: PushDatabase,
calls: QueryCall[],
errors: unknown[]
): PushDatabase {
function captureDatabaseErrors(database: PushDatabase, errors: unknown[]): PushDatabase {
return {
dialect: database.dialect,
query: async (sql, params) => {
calls.push({ sql, params })
try {
return await database.query(sql, params)
} catch (error) {
@@ -32,7 +23,7 @@ function traceDatabase(
throw error
}
},
transaction: (run) => database.transaction((tx) => run(traceDatabase(tx, calls, errors))),
transaction: (run) => database.transaction((tx) => run(captureDatabaseErrors(tx, errors))),
lockQuotaScope: (key) => database.lockQuotaScope(key),
tryLockScope: (key) => database.tryLockScope(key),
tryLockSharedScope: (key) => database.tryLockSharedScope(key),
@@ -40,12 +31,8 @@ function traceDatabase(
}
}
function payloadHash(value: unknown): string {
return createHash('sha256').update(JSON.stringify(value)).digest('hex')
}
it('parses each leased row once with exact complete payload, serialized key order and SQL sequence', async () => {
const { db, store, clock } = await fixture()
it('parses each leased row once and preserves its complete serialized payload', async () => {
const { db, store } = await fixture()
const input = {
...notification(1),
body: 'Unicode: 🐋\ud800',
@@ -57,11 +44,8 @@ it('parses each leased row once with exact complete payload, serialized key orde
throw new Error('Missing delivery row')
}
const payload = String(row.payload_json)
const calls: QueryCall[] = []
const errors: unknown[] = []
const owner = new DurablePushStore(traceDatabase(db, calls, errors), clock)
const parse = vi.spyOn(JSON, 'parse')
const delivery = await owner.claim()
const delivery = await store.claim()
expect(delivery).toEqual({
id: row.batch_id,
registrationId: 'phone',
@@ -72,24 +56,6 @@ it('parses each leased row once with exact complete payload, serialized key orde
attempts: 1
})
expect(JSON.stringify(delivery?.notification)).toBe(payload)
expect(payloadHash(delivery?.notification)).toBe(payloadHash(input))
if (!delivery) {
throw new Error('Missing delivery')
}
const published = buildPushDelivery(delivery)
const expected = buildPushDelivery({ ...delivery, notification: input })
expect(JSON.stringify(published)).toBe(JSON.stringify(expected))
expect(payloadHash(published)).toBe(payloadHash(expected))
expect(calls.map(({ sql }) => sql.replace(/\s+/g, ' ').trim())).toEqual([
`SELECT * FROM push_delivery_batches WHERE state = 'pending' AND lease_until <= ? AND expires_at > ? AND due_at <= ? AND due_at > ? AND NOT EXISTS (SELECT 1 FROM push_delivery_batches busy WHERE busy.registration_id = push_delivery_batches.registration_id AND busy.state = 'pending' AND busy.lease_until > 0 AND busy.lease_until > ?${db.dialect === 'postgres' ? ' OFFSET 0' : ''}) ORDER BY due_at, created_at, batch_id LIMIT 1${db.dialect === 'postgres' ? ' FOR UPDATE SKIP LOCKED' : ''}`,
"SELECT (SELECT batch_id FROM push_delivery_batches WHERE registration_id = ? AND state = 'pending' AND expires_at > ? AND due_at > ? ORDER BY due_at, created_at, batch_id LIMIT 1) AS head, EXISTS (SELECT 1 FROM push_delivery_batches WHERE registration_id = ? AND state = 'pending' AND lease_until > 0 AND lease_until > ?) AS busy",
'SELECT notification_seq FROM push_dismissed_events WHERE host_fingerprint = ? AND notification_epoch = ? AND notification_id = ?',
'UPDATE push_delivery_batches SET lease_token = ?, lease_until = ?, attempts = attempts + 1 WHERE batch_id = ?'
])
expect(calls[1]?.params).toEqual(['phone', clock(), clock() - 300_000, 'phone', clock()])
expect(calls[2]?.params).toEqual(['host', 'epoch', 'notification-1'])
expect(calls[3]?.params).toEqual([delivery?.lease, clock() + 30_000, row.batch_id])
expect(errors).toEqual([])
expect(parse.mock.calls.filter(([value]) => value === payload)).toHaveLength(1)
})
@@ -126,7 +92,6 @@ it('reads changed retry bytes and a later writer update without carrying a parse
expect(retry?.lease).not.toBe(first.lease)
expect(retry?.notification).not.toBe(first.notification)
expect(JSON.stringify(retry?.notification)).toBe(retriedPayload)
expect(payloadHash(retry?.notification)).toBe(payloadHash(first.notification))
const retryParses = parse.mock.calls.filter(([value]) => value === retriedPayload).length
if (!retry) {
throw new Error('Missing retry delivery')
@@ -142,13 +107,12 @@ it('reads changed retry bytes and a later writer update without carrying a parse
const fresh = await store.claim()
expect(fresh).toEqual({ ...retry, notification: changed, lease: expect.any(String), attempts: 3 })
expect(JSON.stringify(fresh?.notification)).toBe(changedPayload)
expect(payloadHash(fresh?.notification)).toBe(payloadHash(changed))
expect(retry.notification.body).toBe('provider changed this retry')
expect(retryParses).toBe(1)
expect(parse.mock.calls.filter(([value]) => value === changedPayload)).toHaveLength(1)
})
it('keeps dismissed alerts on the original single-parse delete path without leasing', async () => {
it('parses dismissed alerts once and removes them without returning a delivery', async () => {
const { db, store, clock } = await fixture()
const input = notification(1)
await store.accept('host', 'phone', input)
@@ -156,12 +120,9 @@ it('keeps dismissed alerts on the original single-parse delete path without leas
'INSERT INTO push_dismissed_events(host_fingerprint, notification_epoch, notification_id, notification_seq, created_at) VALUES (?, ?, ?, ?, ?)',
['host', 'epoch', input.notificationId, 1, clock()]
)
const calls: QueryCall[] = []
const parse = vi.spyOn(JSON, 'parse')
expect(await new DurablePushStore(traceDatabase(db, calls, []), clock).claim()).toBeNull()
expect(await store.claim()).toBeNull()
expect(await store.pendingCount('phone')).toBe(0)
expect(calls.at(-1)?.sql).toBe('DELETE FROM push_delivery_batches WHERE batch_id = ?')
expect(calls.some(({ sql }) => sql.startsWith('UPDATE'))).toBe(false)
expect(parse.mock.calls.filter(([value]) => value === JSON.stringify(input))).toHaveLength(1)
})
@@ -219,7 +180,7 @@ it('preserves the exact database UPDATE error and retries with a fresh payload a
? "CREATE TRIGGER deny_lease BEFORE UPDATE ON push_delivery_batches BEGIN SELECT RAISE(FAIL, 'deny_lease'); END"
: 'ALTER TABLE push_delivery_batches ADD CONSTRAINT deny_lease CHECK (lease_until = 0)'
)
const owner = new DurablePushStore(traceDatabase(db, [], errors), clock)
const owner = new DurablePushStore(captureDatabaseErrors(db, errors), clock)
const parse = vi.spyOn(JSON, 'parse')
let caught: unknown
try {
@@ -88,23 +88,6 @@ describe('push host challenge store', () => {
})
})
it('spends no skew tolerance on its own expiry, so the ttl is the whole window', async () => {
const host = createPushHostKeypair(5)
const challenge = await store.issue(hostPublicKeyB64(host))
const proof = answerPushHostChallenge(challenge!, {
gatewayOrigin: GATEWAY_ORIGIN,
keypair: host,
now: () => clock
})!
// A proof that the host would still consider in-window is refused here: the
// gateway issued expires_at against this clock and needs no allowance.
clock += PUSH_LIMITS.challengeTtlMs + PUSH_LIMITS.clockSkewToleranceMs - 1
await expect(store.verify(challenge!.challengeId, proof)).resolves.toEqual({
ok: false,
reason: 'expired'
})
})
it('accepts a proof that lands just inside the ttl', async () => {
const host = createPushHostKeypair(26)
const challenge = await store.issue(hostPublicKeyB64(host))
@@ -157,21 +140,6 @@ describe('push host challenge store', () => {
})
})
it('rejects a proof bound to a different gateway origin', async () => {
const host = createPushHostKeypair(8)
const challenge = await store.issue(hostPublicKeyB64(host))
const reasons: string[] = []
expect(
answerPushHostChallenge(challenge!, {
gatewayOrigin: 'https://push.example.test',
keypair: host,
now: () => clock,
onInvalid: (reason) => reasons.push(reason)
})
).toBeNull()
expect(reasons.join()).toContain('gatewayOrigin')
})
it('rejects an unknown challenge id and a malformed public key', async () => {
await expect(store.verify('missing', Buffer.alloc(32, 9).toString('base64'))).resolves.toEqual({
ok: false,
@@ -4,12 +4,17 @@ import { reserveRequestConnection } from './push-background-database.js'
import { createPushServer } from './push-server.js'
import { testPushConfig } from './push-server-harness.test-fixture.js'
function concurrencyProbe() {
function concurrencyProbe(capacity: number) {
let active = 0
let peak = 0
const reached = Promise.withResolvers<void>()
const released = Promise.withResolvers<void>()
const hold = async <T>(value: T): Promise<T> => {
peak = Math.max(peak, ++active)
await new Promise((resolve) => setTimeout(resolve, 5))
if (active === capacity) {
reached.resolve()
}
await released.promise
active--
return value
}
@@ -22,7 +27,7 @@ function concurrencyProbe() {
tryLockSharedScope: async () => true,
close: async () => undefined
}
return { database, peak: () => peak }
return { database, peak: () => peak, reached: reached.promise, release: released.resolve }
}
it.each([
@@ -30,25 +35,32 @@ it.each([
[10, 9],
[1, 1]
])('lets background work hold at most poolMax - 1 of %i connections', async (poolMax, cap) => {
const probe = concurrencyProbe()
const probe = concurrencyProbe(cap)
const background = reserveRequestConnection(probe.database, poolMax)
await Promise.all(
const pending = Promise.all(
Array.from({ length: 12 }, (_, index) =>
index % 2 ? background.query('SELECT 1') : background.transaction(async () => undefined)
)
)
await probe.reached
probe.release()
await pending
expect(probe.peak()).toBe(cap)
})
it('bounds server claims before they enter the background pool', async () => {
const probe = concurrencyProbe()
const probe = concurrencyProbe(3)
const server = createPushServer({ ...testPushConfig(), databasePoolMax: 6 }, probe.database, {
fcmAccessToken: async () => 'token',
fcmTransport: async () => ({ status: 200, body: '{}' })
})
const pending = server.worker.runDue()
try {
await server.worker.runDue()
await probe.reached
probe.release()
await pending
} finally {
probe.release()
await server.worker.stop()
}
expect(probe.peak()).toBe(3)
+2 -11
View File
@@ -3,10 +3,7 @@ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
import { createPushHostKeypair } from './host-challenge-answering.test-fixture.js'
import type { PushDatabase } from './push-database.js'
import { createPushServer } from './push-server.js'
import {
createPushServerHarness,
testPushConfig
} from './push-server-harness.test-fixture.js'
import { createPushServerHarness, testPushConfig } from './push-server-harness.test-fixture.js'
describe('push gateway authentication and device routes', () => {
let harness: Awaited<ReturnType<typeof createPushServerHarness>>
@@ -108,13 +105,7 @@ describe('push gateway authentication and device routes', () => {
it('never returns the host fingerprint on the challenge itself', async () => {
const challenge = await harness.issueChallenge(createPushHostKeypair(22))
expect(Object.keys(challenge).sort()).toEqual([
'challengeId',
'ciphertextB64',
'expiresAt',
'gatewayEphemeralPublicKeyB64',
'nonceB64'
])
expect(challenge).not.toHaveProperty('hostFingerprint')
})
it('lets only the owning host delete a registration', async () => {
+4 -17
View File
@@ -56,7 +56,10 @@ describe('push gateway send route', () => {
await harness.flushDeliveries()
expect(harness.fcmRequests).toHaveLength(1)
expect(JSON.parse(harness.fcmRequests[0]!.body)).toMatchObject({
message: { token: FCM_TOKEN, data: { title: 'Agent needs input' } }
message: {
token: FCM_TOKEN,
data: { title: 'Agent needs input', tag: expect.stringMatching(/^[a-f0-9]{64}$/) }
}
})
const afterDeath = await harness.post(
@@ -167,22 +170,6 @@ describe('push gateway send route', () => {
).toBe(3)
})
it('sends a lone event through unchanged with its own collapse id', async () => {
const sessionToken = await harness.signIn(createPushHostKeypair(25))
const registrationId = await harness.registerAndroid(sessionToken)
await harness.post(
'/v1/send',
{ v: 1, registrationIds: [registrationId], notification: notification() },
sessionToken
)
await harness.flushDeliveries()
const message = JSON.parse(harness.fcmRequests[0]!.body) as {
message: { android: { notification: { tag: string } }; data: Record<string, string> }
}
expect(message.message.data.tag).toMatch(/^[a-f0-9]{64}$/)
expect(message.message.data.coalescedCount).toBeUndefined()
})
it('reports an error for a registration the host does not own', async () => {
const ownerToken = await harness.signIn(createPushHostKeypair(19))
const intruderToken = await harness.signIn(createPushHostKeypair(20))
+21 -27
View File
@@ -95,29 +95,6 @@ describe.each([
it('times out when no qualified artifact is produced', async () => {
await expect(execute(templateWait, [{}]).result).rejects.toThrow('Timed out')
})
it('prefetches Windows assets before waiting, then mints credentials after Mac succeeds', async () => {
const download = publishSteps.findIndex(
(step) => step.name === 'Download the staged Windows artifacts'
)
const wait = publishSteps.indexOf(macWait)
const token = publishSteps.findIndex((step) => step.name === 'Mint dev channel repo token')
expect(download).toBeLessThan(wait)
expect(wait).toBeLessThan(token)
await execute(macWait, [
{ jobs: [job('build-adhoc-mac')] },
{ jobs: [job('build-adhoc-mac', 'success')] }
]).result
})
it.each(['failure', 'cancelled', 'skipped', 'timed_out'])(
'refuses Windows publication when Mac ends with %s',
async (conclusion) => {
await expect(
execute(macWait, [{ jobs: [job('build-adhoc-mac', conclusion)] }]).result
).rejects.toThrow('macOS publication did not succeed')
}
)
})
it('keeps the release a draft until qualification and its cleanup finish', async () => {
@@ -136,8 +113,25 @@ it('keeps the release a draft until qualification and its cleanup finish', async
)
})
it('uses the same qualification wait on both platforms', () => {
const script = (steps) =>
steps.find((step) => step.name === 'Wait for the qualified orcad template').with.script
expect(script(windows.jobs['build-win'].steps)).toBe(script(macSteps))
it('prefetches Windows assets before waiting, then mints credentials after Mac succeeds', async () => {
const download = publishSteps.findIndex(
(step) => step.name === 'Download the staged Windows artifacts'
)
const wait = publishSteps.indexOf(macWait)
const token = publishSteps.findIndex((step) => step.name === 'Mint dev channel repo token')
expect(download).toBeLessThan(wait)
expect(wait).toBeLessThan(token)
await execute(macWait, [
{ jobs: [job('build-adhoc-mac')] },
{ jobs: [job('build-adhoc-mac', 'success')] }
]).result
})
it.each(['failure', 'cancelled', 'skipped', 'timed_out'])(
'refuses Windows publication when Mac ends with %s',
async (conclusion) => {
await expect(
execute(macWait, [{ jobs: [job('build-adhoc-mac', conclusion)] }]).result
).rejects.toThrow('macOS publication did not succeed')
}
)
@@ -57,31 +57,6 @@ function queuedBuilder(events, afterSign) {
}
describe('overlapping macOS notarization', () => {
it('reduces a modeled pair of 4-second Apple waits from 10 to 6 seconds', async () => {
vi.useFakeTimers()
try {
const started = Date.now()
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
const build = async ({ config }) => {
const artifacts = []
for (const arch of [1, 3]) {
await delay(1000)
await config.afterSign(context(arch))
artifacts.push(config.artifactBuildStarted({ arch }))
}
await Promise.all(artifacts)
return []
}
const result = buildWithOverlappingNotarization(options(build, () => delay(4000)))
await vi.runAllTimersAsync()
await result
expect(Date.now() - started).toBe(6000)
expect(2 * (1000 + 4000)).toBe(10000)
} finally {
vi.useRealTimers()
}
})
it('packs serially, overlaps Apple waits, and gates every artifact on both verified tickets', async () => {
const events = []
const x64 = deferred(),
@@ -310,16 +310,6 @@ describeBundling('the app bundle', () => {
}
}, 120_000)
it('builds the same buildId twice', async () => {
const first = await withScratch((scratch) =>
buildMobileWebAppBundle({ outDir: join(scratch, 'a') })
)
const second = await withScratch((scratch) =>
buildMobileWebAppBundle({ outDir: join(scratch, 'b') })
)
expect(first.manifest.buildId).toBe(second.manifest.buildId)
}, 120_000)
it('loads the entry as a module, so its route imports resolve', async () => {
const { manifest, html } = await readWrittenBundle()
// import() in a classic script is a syntax error, so the tag and the format are one fact.
@@ -516,11 +506,6 @@ describe('the Phase C budget', () => {
for (const imageCount of [0, 42, 120]) {
const chunks = mobileWebAppBundleMaxChunks(routeCount)
expect(mobileWebAppBundleMaxAssets(routeCount, imageCount)).toBe(chunks + imageCount + 1)
// The ordering claim itself: a bundle at the chunk ceiling is exactly at the asset
// ceiling, so no bundle can pass the chunk check and fail the asset one.
expect(chunks + imageCount + 1).toBeLessThanOrEqual(
mobileWebAppBundleMaxAssets(routeCount, imageCount)
)
}
}
})
@@ -54,12 +54,6 @@ describe('assertNodePtyPatchApplied', () => {
/\.symver glibc pins/
)
})
it('names the patch and the doc so the fix is findable', () => {
expect(() => assertNodePtyPatchApplied(stage("'ldflags': []", '// upstream'))).toThrow(
/config\/patches\/node-pty@1\.1\.0\.patch/
)
})
})
describe('slot naming', () => {
@@ -9,30 +9,6 @@ import { selectLatestStableReleaseTag } from './stable-release-tags.mjs'
const repos = []
function expectedLatest(tags) {
let latest = null
let latestParts = []
for (const tag of tags) {
const match = /^v(\d+)\.(\d+)\.(\d+)$/.exec(tag)
if (!match) {
continue
}
const parts = match.slice(1).map((part) => {
const value = Number.parseInt(part, 10)
return Number.isFinite(value) ? value : 0
})
let comparison = 0
for (let index = 0; index < 3 && comparison === 0; index++) {
comparison = parts[index] - (latestParts[index] ?? 0)
}
if (latest === null || comparison >= 0) {
latest = tag
latestParts = parts
}
}
return latest
}
function git(repo, args) {
execFileSync('git', args, { cwd: repo, stdio: 'ignore' })
}
@@ -77,78 +53,38 @@ afterEach(() => {
})
describe('selectLatestStableReleaseTag', () => {
it('orders numerically and skips prerelease tags', () => {
expect(selectLatestStableReleaseTag(['v1.4.9', 'v1.4.10', 'v1.5.0-rc.1', 'nightly'])).toBe(
'v1.4.10'
)
expect(selectLatestStableReleaseTag(['nightly'])).toBeNull()
})
it.each([0, 1, 12, 128, 1000])(
'selects numerically without changing %i Git tag strings',
(count) => {
const tags = Array.from(
{ length: count },
(_, index) => `v1.${(index * 37) % 17}.${(index * 101) % (count + 1)}`
)
it.each([
{ tags: ['v1.4.9', 'v1.4.10', 'v1.5.0-rc.1', 'nightly'], expected: 'v1.4.10' },
{ tags: ['v1.9.99', 'v1.10.0'], expected: 'v1.10.0' },
{ tags: ['v9.99.99', 'v10.0.0'], expected: 'v10.0.0' },
{ tags: [], expected: null },
{ tags: ['nightly', 'mobile-v1.2.3', 'v1.2.3-rc.1'], expected: null },
{ tags: ['v0001.4.003', 'v1.04.3'], expected: 'v1.04.3' },
{ tags: ['v1.04.3', 'v0001.4.003'], expected: 'v0001.4.003' },
{ tags: ['v1.2.3', 'v1.2.3\n'], expected: 'v1.2.3' },
{ tags: ['v1.2.3\r\n', 'v0.0.1'], expected: 'v0.0.1' },
{ tags: [`v${'9'.repeat(400)}.1.2`, 'v0.1.2'], expected: 'v0.1.2' },
{
tags: ['v9007199254740993.1.0', 'v9007199254740992.1.0'],
expected: 'v9007199254740992.1.0'
}
])(
'selects the newest stable tag without changing its spelling or input: $tags',
({ tags, expected }) => {
const input = [...tags]
const expected = expectedLatest(tags)
expect(selectLatestStableReleaseTag(Object.freeze(tags))).toBe(expected)
expect(tags).toEqual(input)
expect(selectLatestStableReleaseTag(['v0.0.0'])).toBe('v0.0.0')
}
)
it('preserves tie spelling, numeric fallback and invalid-tag admission', () => {
const cases = [
{ tags: [], expected: null },
{ tags: ['nightly', 'mobile-v1.2.3', 'v1.2.3-rc.1'], expected: null },
{ tags: ['v0001.4.003', 'v1.04.3'], expected: 'v1.04.3' },
{ tags: ['v1.04.3', 'v0001.4.003'], expected: 'v0001.4.003' },
{ tags: ['v1.2.3', 'v1.2.3\n'], expected: 'v1.2.3' },
{ tags: ['v1.2.3\r\n', 'v0.0.1'], expected: 'v0.0.1' },
{ tags: [`v${'9'.repeat(400)}.1.2`, 'v0.1.2'], expected: 'v0.1.2' },
{
tags: ['v9007199254740993.1.0', 'v9007199254740992.1.0'],
expected: 'v9007199254740992.1.0'
}
]
for (const { tags, expected } of cases) {
const input = [...tags]
expect(selectLatestStableReleaseTag(Object.freeze(tags))).toBe(expected)
expect(tags).toEqual(input)
}
it('skips unfilled entries in a sparse tag list', () => {
const sparse = []
sparse.length = 12
sparse[3] = 'v1.2.3'
sparse[8] = 'v2.0.0'
expect(selectLatestStableReleaseTag(Object.freeze(sparse))).toBe('v2.0.0')
})
it('selects the same latest spelling across ordinary version namespaces and repeated calls', () => {
let randomState = 673151
const next = () => {
randomState = (randomState * 1664525 + 1013904223) >>> 0
return randomState
}
for (let seed = 0; seed < 256; seed++) {
const tags = []
for (let index = 0, count = next() % 129; index < count; index++) {
const triple = [next() % 13, next() % 17, next() % 257]
const prefix = next() % 8 === 0 ? 'mobile-v' : 'v'
const suffix = next() % 9 === 0 ? '-rc.1' : ''
tags.push(`${prefix}${triple.join('.')}${suffix}`)
if (index % 11 === 0) {
tags.push(tags.at(-1))
}
}
const input = [...tags]
const expected = expectedLatest(tags)
expect(selectLatestStableReleaseTag(tags)).toBe(expected)
expect(tags).toEqual(input)
tags.push('v99.99.99')
expect(selectLatestStableReleaseTag(tags)).toBe('v99.99.99')
}
})
})
describe('checkDaemonProtocolCrossing', () => {
@@ -4,11 +4,9 @@ import path from 'node:path'
import { describe, expect, it } from 'vitest'
import {
collectIdOnlyStoreWrites,
findIdOnlyLookups,
findIdOnlyWrites,
listStoreOperationClasses,
NOT_HOST_TWINNED
listStoreOperationClasses
} from './check-id-only-store-write-ratchet.mjs'
const SOURCE = `export class RowOps {
@@ -76,17 +74,6 @@ describe('findIdOnlyWrites', () => {
})
})
describe('collectIdOnlyStoreWrites', () => {
it('scans every Store operations class and skips the documented exemptions', () => {
expect(listStoreOperationClasses(process.cwd()).length).toBeGreaterThan(10)
const entries = collectIdOnlyStoreWrites(process.cwd())
expect(entries).not.toContain('RepoLifecycleOperations.updateRepo')
for (const exempt of NOT_HOST_TWINNED.keys()) {
expect(entries).not.toContain(exempt)
}
})
})
const LOOKUP_SOURCE = `export class SetupOps {
updateProjectHostSetup(args: Args): Result | null {
const repo = setup.repoId
+42
View File
@@ -0,0 +1,42 @@
# Comprehensive test-value audit
Goal: read every test body, decide whether to keep, remove, consolidate or fix it, and validate each change against the behavior it protects. The audit is **in progress**.
[`progress.json`](./progress.json) reports current counts by area. [`inventory.jsonl`](./inventory.jsonl) lists candidate files with content hashes, discovery signals and review status. Scanning a file does not count as reviewing it. All cases in an unreviewed file remain pending.
## Discovery and completion
Run from the repository root after installing dependencies:
```sh
node docs/audits/test-value/refresh.mjs
```
The inventory scans tracked source files, reuses CI's unit-file discovery, parses JavaScript/TypeScript test declarations, and looks for native-language declarations, test filenames, test/support directories, regressions, reproductions, smoke checks and benchmarks. Named suites remain candidates even when the parser finds no declarations. Support files and reproduction scripts need manual classification; the file count is not a count of runnable suites.
Declaration counts are static hints: a parameterized declaration counts once, and custom factories or standalone assertions may have no recognized declarations. Reviewers must read the complete file, its tables/factories and relevant production paths, and record additional cases the parser misses. Discovery patterns must be extended when an unfamiliar runner is found. Final reconciliation must also check runner configurations and workflow/package entrypoints for omitted scripts. The initial inventory does not establish exhaustive completion.
A file is `reviewed` only when it has evidence, individual case decisions and a matching final SHA-256. Changed content becomes `stale`. Original hashes and source commits preserve decisions for removed/consolidated cases; the refresh command checks original hashes and rejects missing decisions for recognized original declarations. Generated files never promote pending candidates automatically.
Completion requires no unreviewed or stale test files, no unresolved support/script classifications, decisions for every actual case (including parameter tables and inline tests), and validation of all changes. Previously merged cleanup PRs do not substitute for this review.
## Ownership
| Area | Reviewer | Next batch |
| ------------------------------------------------ | ------------------------ | ------------- |
| `cloud/` | `audit_cloud` | `cloud-0002` |
| `config/` | `audit_config` | `config-0002` |
| `mobile/` | `audit_mobile` | `mobile-0002` |
| `src/`, `native/`, `tests/`, `.github/`, `docs/` | root; pending assignment | Pending |
Ownership is disjoint. Within an area, continue in tracked path order after the last batch; explicitly include nonstandard/inline cases and support candidates before closing the area.
## Review records
Each `reviews/<area>-<batch>.jsonl` row records the original path/hash/commit, final reviewed hash, reviewer, decision, rationale, production evidence, and case-level line/title/decision/reason. Lines and titles refer to the original source. New or changed assertions are explained in the corresponding case decision. Files with no test cases require an explicit classification and rationale rather than invented cases.
Batch validation lives in `reviews/<area>-<batch>-validation.json`. Local log paths identify session evidence; recorded commands/results and regression descriptions survive outside that session. A production mutation is temporary, must fail the retained tests and must be restored before committing. Mutations are targeted evidence, not a substitute for reviewing every case.
Initial batch: 105 files, 720 original declarations; 22 changed test files, 467 net lines removed. Validation: 256 mobile tests, 196 push tests with PostgreSQL, 180 with SQLite (16 intentional PostgreSQL-only skips), 11 desktop proof tests, and 113 config tests passed. Nine targeted mutations were caught. Production files were restored. PR/check/merge references will be added as batches land.
`docs/**` is ignored in this repository. Add audit artifacts explicitly with `git add -f`, and stage only completed batches.
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
{
"source_commit": "6d473f4bea1d190cb3649af97e04f1cdde170c5f",
"scanned_source_files": 37059,
"counts": {
"files": 16610,
"declarations": 119017,
"reviewed": 105,
"stale": 0,
"unreviewed": 16505,
"removed": 0
},
"reviewed_original_declarations": 720,
"areas": {
".github": {
"files": 2,
"reviewed": 0,
"stale": 0,
"unreviewed": 2
},
"cloud": {
"files": 224,
"reviewed": 35,
"stale": 0,
"unreviewed": 189
},
"config": {
"files": 600,
"reviewed": 35,
"stale": 0,
"unreviewed": 565
},
"docs": {
"files": 52,
"reviewed": 0,
"stale": 0,
"unreviewed": 52
},
"mobile": {
"files": 1260,
"reviewed": 35,
"stale": 0,
"unreviewed": 1225
},
"native": {
"files": 19,
"reviewed": 0,
"stale": 0,
"unreviewed": 19
},
"src/cli": {
"files": 182,
"reviewed": 0,
"stale": 0,
"unreviewed": 182
},
"src/main": {
"files": 6471,
"reviewed": 0,
"stale": 0,
"unreviewed": 6471
},
"src/packages": {
"files": 14,
"reviewed": 0,
"stale": 0,
"unreviewed": 14
},
"src/preload": {
"files": 20,
"reviewed": 0,
"stale": 0,
"unreviewed": 20
},
"src/renderer": {
"files": 5329,
"reviewed": 0,
"stale": 0,
"unreviewed": 5329
},
"src/shared": {
"files": 1264,
"reviewed": 0,
"stale": 0,
"unreviewed": 1264
},
"src/wsl-guest": {
"files": 45,
"reviewed": 0,
"stale": 0,
"unreviewed": 45
},
"tests": {
"files": 1128,
"reviewed": 0,
"stale": 0,
"unreviewed": 1128
}
},
"errors": []
}
+232
View File
@@ -0,0 +1,232 @@
import { createHash } from 'node:crypto'
import { execFileSync } from 'node:child_process'
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
import { resolve } from 'node:path'
import ts from 'typescript-api'
import { discoverUnitFiles } from '../../../config/scripts/ci-unit-files.mjs'
const directory = import.meta.dirname
const root = resolve(directory, '../../..')
const git = (...args) =>
execFileSync('git', args, { cwd: root, encoding: 'utf8', maxBuffer: 16 * 1024 * 1024 })
const digest = (source) => createHash('sha256').update(source).digest('hex')
const tracked = git('ls-files', '-z').split('\0').filter(Boolean).sort()
const trackedSet = new Set(tracked)
const unit = new Set(discoverUnitFiles(root))
const reviews = new Map()
const errors = []
const decisions = new Set(['keep', 'remove', 'consolidate', 'fix'])
function testRoot(expression) {
if (ts.isIdentifier(expression)) {
return expression.text
}
if (ts.isPropertyAccessExpression(expression)) {
return testRoot(expression.expression)
}
if (ts.isElementAccessExpression(expression)) {
return testRoot(expression.expression)
}
if (ts.isCallExpression(expression)) {
return testRoot(expression.expression)
}
return ''
}
function declarations(path, source) {
if (/\.[cm]?[jt]sx?$/.test(path)) {
const tree = ts.createSourceFile(path, source, ts.ScriptTarget.Latest, true)
const names = new Set(['it', 'test', 'fit', 'xit', 'xtest', 'ftest'])
const cases = []
for (const node of tree.statements) {
if (!ts.isImportDeclaration(node)) {
continue
}
if (
ts.isStringLiteral(node.moduleSpecifier) &&
/^(node:test|tape|ava)$/.test(node.moduleSpecifier.text) &&
node.importClause?.name
) {
names.add(node.importClause.name.text)
}
const bindings = node.importClause?.namedBindings
if (!bindings || !ts.isNamedImports(bindings)) {
continue
}
for (const binding of bindings.elements) {
if (names.has(binding.propertyName?.text ?? binding.name.text)) {
names.add(binding.name.text)
}
}
}
function visit(node) {
if (ts.isCallExpression(node) && names.has(testRoot(node.expression))) {
const title = node.arguments[0]
const expression = node.expression.getText(tree)
if (
title &&
node.arguments.length >= 2 &&
!/\.(step|extend|use|setTimeout|configure|info)\b/.test(expression) &&
(ts.isStringLiteralLike(title) || ts.isTemplateExpression(title))
) {
cases.push({
line: tree.getLineAndCharacterOfPosition(node.getStart(tree)).line + 1,
title: ts.isStringLiteralLike(title) ? title.text : title.getText(tree)
})
}
}
ts.forEachChild(node, visit)
}
visit(tree)
return cases
}
const patterns = {
rs: /#\[(?:(?:\w+::)?test|rstest)(?:\([^\]]*\))?\][\s\S]*?\bfn\s+(\w+)/g,
py: /^\s*(?:async\s+)?def\s+(test_\w+)\s*\(/gm,
rb: /^\s*(?:def\s+(test_\w+)|test\s+['"]([^'"]+))/gm,
swift: /^\s*(?:func\s+(test\w+)\s*\(|@Test\b[^\n]*\n\s*(?:@\w+[^\n]*\n\s*)*func\s+(\w+))/gm,
kt: /@(?:Test|ParameterizedTest)\b[^\n]*\n\s*fun\s+(`[^`]+`|\w+)/g,
go: /^func\s+(Test\w+)\s*\(/gm,
ps1: /^\s*It\s+['"]([^'"]+)/gim
}
const pattern = patterns[path.split('.').at(-1)]
return pattern
? [...source.matchAll(pattern)].map((match) => ({
line: source.slice(0, match.index).split('\n').length,
title: match.slice(1).find(Boolean)
}))
: []
}
for (const name of readdirSync(resolve(directory, 'reviews'))
.filter((name) => name.endsWith('.jsonl'))
.sort()) {
const rows = readFileSync(resolve(directory, 'reviews', name), 'utf8')
.trim()
.split('\n')
for (const line of rows.filter(Boolean)) {
const review = JSON.parse(line)
const label = `${name}:${review.path}`
if (!review.rationale || !review.evidence?.length || !review.reviewer) {
errors.push(`${label}: missing evidence`)
}
if (!review.sha256 || !review.reviewed_sha256 || !review.source_commit) {
errors.push(`${label}: missing hash or source commit`)
}
if (!Array.isArray(review.cases)) {
errors.push(`${label}: missing case decisions`)
}
for (const entry of review.cases ?? []) {
if (!entry.line || !entry.title || !entry.reason || !decisions.has(entry.decision)) {
errors.push(`${label}: invalid case decision`)
}
}
if (review.source_commit && review.sha256) {
const original = git('show', `${review.source_commit}:${review.path}`)
if (digest(original) !== review.sha256) {
errors.push(`${label}: original content hash mismatch`)
}
const expected = declarations(review.path, original)
const recorded = new Set((review.cases ?? []).map((entry) => entry.line))
for (const entry of expected) {
if (!recorded.has(entry.line)) {
errors.push(`${label}:${entry.line}: missing decision for ${entry.title}`)
}
}
}
const previous = reviews.get(review.path)
if (previous && previous.batch === name) {
errors.push(`${label}: duplicate review`)
}
reviews.set(review.path, { ...review, batch: name })
}
}
const rows = []
const counts = { files: 0, declarations: 0, reviewed: 0, stale: 0, unreviewed: 0, removed: 0 }
const areas = {}
let scanned = 0
for (const path of tracked) {
if (path.startsWith('docs/audits/test-value/')) {
continue
}
if (!/\.(?:[cm]?[jt]sx?|rs|py|rb|swift|kt|go|ps1|sh|bash|zsh|fish|c|cc|cpp|h)$/.test(path)) {
continue
}
scanned++
if (!existsSync(resolve(root, path))) {
if (reviews.get(path)?.decision !== 'remove') {
errors.push(`${path}: deleted without a reviewed removal decision`)
}
continue
}
const source = readFileSync(resolve(root, path), 'utf8')
const named =
/(?:\.(?:test|tests|spec|e2e)\.|(?:^|\/)test_[^/]+$|_test\.[^/]+$|(?:Tests|Checks)\.swift$|Test\.kt$)/.test(
path
)
const auxiliary =
/(?:^|\/)(?:tests?|__tests__|test-support|fixtures?|benches|benchmarks|repros?)(?:\/|[-.])|(?:^|[/.-])(?:repro\w*|regression\w*|benchmark\w*|bench|smoke)(?:[.-]|$)/i.test(
path
)
const cases = declarations(path, source)
if (!named && !auxiliary && !cases.length && !reviews.has(path)) {
continue
}
const sha256 = digest(source)
const review = reviews.get(path)
const status = review ? (review.reviewed_sha256 === sha256 ? 'reviewed' : 'stale') : 'unreviewed'
const signals = [
named && 'named',
cases.length && 'declarations',
auxiliary && 'auxiliary',
unit.has(path) && 'ci-unit'
].filter(Boolean)
rows.push({
path,
sha256,
declarations: cases.length,
signals,
status,
...(review ? { review: `reviews/${review.batch}` } : {})
})
counts.files++
counts.declarations += cases.length
counts[status]++
const area = path.startsWith('src/') ? path.split('/').slice(0, 2).join('/') : path.split('/')[0]
areas[area] ??= { files: 0, reviewed: 0, stale: 0, unreviewed: 0 }
areas[area].files++
areas[area][status]++
}
for (const [path, review] of reviews) {
if (trackedSet.has(path) && existsSync(resolve(root, path))) {
continue
}
if (review.decision !== 'remove') {
errors.push(`${path}: reviewed file is absent without a removal decision`)
}
rows.push({ path, sha256: review.sha256, status: 'removed', review: `reviews/${review.batch}` })
counts.removed++
}
const progress = {
source_commit: git('rev-parse', 'HEAD').trim(),
scanned_source_files: scanned,
counts,
reviewed_original_declarations: [...reviews.values()].reduce(
(sum, review) => sum + (review.cases?.length ?? 0),
0
),
areas,
errors
}
if (errors.length) {
console.error(errors.join('\n'))
process.exitCode = 1
} else {
writeFileSync(
resolve(directory, 'inventory.jsonl'),
`${rows.map((row) => JSON.stringify(row)).join('\n')}\n`
)
writeFileSync(resolve(directory, 'progress.json'), `${JSON.stringify(progress, null, 2)}\n`)
console.log(JSON.stringify(progress, null, 2))
}
@@ -0,0 +1,41 @@
{
"checks": [
{
"command": "ORCA_BACKGROUND_LAUNCH=1 ORCA_PUSH_DURABLE_TEST_POSTGRES_URL=<isolated PostgreSQL> pnpm test (cloud/apps/push)",
"result": "196 tests passed across 39 suites",
"log": "/tmp/orca-audit-cloud-0001-final-postgres.log"
},
{
"command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test (cloud/apps/push, SQLite)",
"result": "180 passed; 16 intentional PostgreSQL-only skips",
"log": "/tmp/orca-audit-cloud-0001-complete-sqlite.log"
},
{
"command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test src/main/runtime/push/push-host-proof.test.ts",
"result": "11 passed",
"log": "/tmp/orca-audit-cloud-0001-desktop-proof.log"
},
{
"command": "pnpm run typecheck (cloud/apps/push)",
"result": "passed",
"log": "/tmp/orca-audit-cloud-0001-complete-typecheck.log"
},
{
"command": "ORCA_BACKGROUND_LAUNCH=1 vitest run <two edited concurrency suites>",
"result": "6 passed after final cleanup edits",
"log": "/tmp/orca-audit-cloud-0001-final-concurrency.log"
}
],
"regressions_caught": [
"leased payload corrupted",
"spoofable client IP selected",
"challenge expiry given clock skew allowance"
],
"batch": "cloud-0001",
"platform": "Linux",
"reviewed_files": 35,
"format_and_lint": "passed",
"temporary_production_mutations": "restored; no production diff",
"root_review": "All 22 changed test diffs independently inspected; retained serializer integration, previous-revision SQL boundary and failure controls checked.",
"log_note": "Local log paths are evidence pointers for this session; results are retained here for durable review."
}
@@ -0,0 +1,35 @@
{"path":"cloud/apps/push/src/apns-client.test.ts","sha256":"4de5e44d0b2ef7dab8508935c85f14e9dbb19ab2d02026481497399f89c76f69","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Provider wire serialization, credential rotation and response classification exercise the real APNs client at its transport boundary.","evidence":["cloud/apps/push/src/apns-client.ts","cloud/apps/push/src/apns-authentication-token.ts"],"cases":[{"line":50,"title":"signs an ES256 provider token and caches it until the rotation point","decision":"keep","reason":"Decodes real ES256 token fields and raw signature width, then crosses the cache rotation boundary with an injected clock."},{"line":73,"title":"sends the specified headers, path, and alert body","decision":"keep","reason":"Inspects an actual client-produced APNs request including alert content, expiry and identity fields."},{"line":114,"title":"targets the sandbox host and keeps the individual collapse id","decision":"keep","reason":"Exercises sandbox host selection separately from the production host case."},{"line":126,"title":"classifies %i %s as a dead token","decision":"keep","reason":"Covers both HTTP status and provider-reason dead-token branches with parameterized responses."},{"line":142,"title":"treats %i %s with the appropriate retry policy","decision":"keep","reason":"Covers permanent versus retryable errors across provider status classes."},{"line":159,"title":"reports a transport failure as an error rather than throwing","decision":"keep","reason":"Exercises a throwing transport and verifies the client returns a retryable outcome."},{"line":173,"title":"does not collapse background dismissals with visible alerts","decision":"keep","reason":"Verifies a dismissal produces background headers and no alert collapse identity."},{"line":196,"title":"keeps the absolute deadline across retries and refuses expired delivery","decision":"keep","reason":"Advances the injected clock across retries and expiry while checking no extra send occurs."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"4de5e44d0b2ef7dab8508935c85f14e9dbb19ab2d02026481497399f89c76f69"}
{"path":"cloud/apps/push/src/apns-session-replacement.test.ts","sha256":"e6fb235c1dac08b79a00c641cefc19917219ad049d40d4f680bd05433babd913","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Protects the real HTTP/2 session cache from a late close/error emitted by an obsolete session.","evidence":["cloud/apps/push/src/apns-http2-transport.ts"],"cases":[{"line":14,"title":"keeps the replacement cached when the draining session closes later","decision":"keep","reason":"A transport boundary fake emits stale close/error events after replacement; request and close counts prove the replacement remains usable."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"e6fb235c1dac08b79a00c641cefc19917219ad049d40d4f680bd05433babd913"}
{"path":"cloud/apps/push/src/apns-stream-response.test.ts","sha256":"9732648e7c5d06049ca1eba9745d52b5247d4fc4556aedad5aaab9d3287bec03","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Exercises real stream event settlement and timeout behavior with a deterministic emitter.","evidence":["cloud/apps/push/src/apns-stream-response.ts"],"cases":[{"line":31,"title":"resolves with the status and the concatenated body","decision":"keep","reason":"Verifies request-body submission, numeric status conversion and concatenation across data chunks."},{"line":42,"title":"rejects when the peer resets the stream without an end or an error","decision":"keep","reason":"A close-only peer reset must reject instead of leaving the request pending."},{"line":51,"title":"keeps the resolved response when close follows a completed end","decision":"keep","reason":"End followed by close must retain the completed response."},{"line":60,"title":"keeps the original error when close follows a stream error","decision":"keep","reason":"Error followed by close must retain the original failure."},{"line":68,"title":"destroys the stream on timeout and surfaces the timeout error","decision":"keep","reason":"Invokes the registered timeout callback and checks stream destruction plus rejection."},{"line":76,"title":"reports a missing status header as zero rather than NaN","decision":"keep","reason":"An absent status header must produce zero, preventing NaN classification."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"9732648e7c5d06049ca1eba9745d52b5247d4fc4556aedad5aaab9d3287bec03"}
{"path":"cloud/apps/push/src/apns-topic-recovery.test.ts","sha256":"1ff7e37d4c3d31fbe80631e06f972890eb08ec2764ea6d0dbad962f577e89f99","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real API/store/worker integration distinguishes a topic configuration error from a dead device token.","evidence":["cloud/apps/push/src/apns-client.ts","cloud/apps/push/src/push-server.ts","cloud/apps/push/src/durable-push-worker.ts"],"cases":[{"line":9,"title":"delivers again after a topic error without re-registering the phone","decision":"keep","reason":"Two API sends separated by a provider topic failure must reach APNs without re-registering the phone; observability distinguishes the outcomes."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"1ff7e37d4c3d31fbe80631e06f972890eb08ec2764ea6d0dbad962f577e89f99"}
{"path":"cloud/apps/push/src/client-ip-rate-limit.test.ts","sha256":"17a9231f60a03230ee11326707d4909da2ffa7f1394bd3d055a119f2abaa54ca","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Retain token-bucket, trust-depth and eviction behavior; remove a second replay of changing spoofed left hops.","evidence":["cloud/apps/push/src/client-ip-rate-limit.ts","cloud/apps/push/src/push-server-limits.test.ts"],"cases":[{"line":17,"title":"admits exactly the per-minute allowance and refuses the next request","decision":"keep","reason":"Crosses the configured allowance exactly, detecting an admission off-by-one."},{"line":25,"title":"keeps one client ip from spending another one budget","decision":"keep","reason":"Exhausts one identity and proves another retains its budget."},{"line":32,"title":"refills over the window rather than resetting on a boundary","decision":"keep","reason":"A half-window clock advance checks continuous refill instead of a boundary reset."},{"line":46,"title":"bounds what it remembers when a flood of distinct ips arrives","decision":"keep","reason":"Distinct attacker identities must not grow tracked memory beyond its cap."},{"line":56,"title":"evicts the least recently used bucket without scanning the map","decision":"keep","reason":"Checks LRU victim and retained budgets while guarding against whole-map iteration on admission."},{"line":76,"title":"answers 429 with a rate_limited body once the bucket is empty","decision":"keep","reason":"Exercises middleware HTTP status and error-body mapping after budget exhaustion."},{"line":87,"title":"buckets on the last forwarded hop, the only one the platform appended","decision":"keep","reason":"Changes the spoofable left hop repeatedly, then tests same-client denial and another-client admission."},{"line":107,"title":"gives a spoofed left-most hop no escape from the caller own bucket","decision":"remove","reason":"Repeats the preceding middleware case's changing left-hop and fixed trusted right-hop scenario without an additional production branch."},{"line":125,"title":"skips the configured trusted proxies when counting from the right","decision":"keep","reason":"Configured proxy depth must select the client before the appended trusted proxy."},{"line":141,"title":"trusts nothing when the header is shorter than the configured depth","decision":"keep","reason":"A header shorter than configured trust depth must fall into the shared unknown-client budget."},{"line":156,"title":"ignores spoofable x-real-ip and uses a single shared bucket","decision":"keep","reason":"Changing x-real-ip without a trusted forwarded header must not evade the shared budget."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"60359029a7ba671166acd01992227d8fdfa2944e05eca81446dda4e2b501b44c"}
{"path":"cloud/apps/push/src/config.test.ts","sha256":"f0fea93e7656160809cf4105da483f362353ffa9433c74eed180fa64e607acbd","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Configuration parsing tests cover deployment defaults and input validation rather than copying exports.","evidence":["cloud/apps/push/src/config.ts"],"cases":[{"line":20,"title":"applies the documented defaults","decision":"keep","reason":"A minimal deployment input must yield operational defaults, including an explicit FCM project."},{"line":35,"title":"reads a full APNs credential and the overridable knobs","decision":"keep","reason":"Overrides and a generated APNs key must reach the parsed runtime configuration."},{"line":60,"title":"requires an explicit FCM project instead of silently targeting production","decision":"keep","reason":"Missing or blank FCM project must fail to prevent accidental production routing."},{"line":65,"title":"refuses a partial APNs credential","decision":"keep","reason":"Partial credentials and malformed key text must fail during startup parsing."},{"line":79,"title":"requires a canonical HTTPS origin outside loopback","decision":"keep","reason":"Rejects path-bearing or insecure public origins while allowing loopback development."},{"line":91,"title":"treats an empty optional variable as unset","decision":"keep","reason":"Empty optional environment values must resolve as unset."},{"line":98,"title":"treats blank defaulted environment settings as absent","decision":"keep","reason":"Whitespace-only defaulted values must match the minimal deployment configuration."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"f0fea93e7656160809cf4105da483f362353ffa9433c74eed180fa64e607acbd"}
{"path":"cloud/apps/push/src/desktop-host-proof-interop.test.ts","sha256":"defd6108769daf59df99768fea1e429119e17d3c8f9aa5f175cd26fd44d1b996","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"The independently implemented peer fixture is a deliberate wire-compatibility oracle, backed by a shared fixed transcript vector and live gateway challenge.","evidence":["cloud/apps/push/src/host-challenge-store.ts","cloud/apps/push/src/host-challenge-answering.test-fixture.ts","src/main/runtime/push/push-host-proof-vector.test.ts","cloud/packages/push-contract/src/push-host-proof-vector.json"],"cases":[{"line":13,"title":"the checked-in vector answers to the same proof the fixture host computes","decision":"keep","reason":"The checked-in cross-workspace vector checks fingerprint and transcript HMAC compatibility independently of random live challenges."},{"line":29,"title":"a live challenge from the store round-trips through the fixture host once","decision":"keep","reason":"A live gateway-issued encrypted challenge must verify once and reject replay; this exercises the producer against a separate peer implementation."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"defd6108769daf59df99768fea1e429119e17d3c8f9aa5f175cd26fd44d1b996"}
{"path":"cloud/apps/push/src/device-registration-delete-race.test.ts","sha256":"092c06bc09c4bfe01a7f14096e6d21837f6e096132f887e1ae7623bedf30fa56","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real PostgreSQL transaction interleaving checks a device deletion/registration race that in-memory serialization cannot model.","evidence":["cloud/apps/push/src/device-registry-store.ts","cloud/apps/push/src/push-database.ts"],"cases":[{"line":6,"title":"serializes deletion with a registration that has already read its row","decision":"keep","reason":"Pauses an actual registration read, observes the competing deletion blocked on a database lock, and verifies ordering after release; preserves the DB-only gate."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"092c06bc09c4bfe01a7f14096e6d21837f6e096132f887e1ae7623bedf30fa56"}
{"path":"cloud/apps/push/src/device-registry-store.test.ts","sha256":"bce3e17bfe1cc7c99dd981ef7503a0095f9c7282b680cba33c7e4c85877e621a","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real SQLite queries protect ownership, identity, rotation, revival and quota semantics; API tests additionally cover route wiring.","evidence":["cloud/apps/push/src/device-registry-store.ts","cloud/apps/push/src/push-server-auth.test.ts","cloud/apps/push/src/push-server-limits.test.ts"],"cases":[{"line":39,"title":"keeps one registration per host and device while replacing the token","decision":"keep","reason":"Re-registration must preserve registration identity while storing the new token and environment."},{"line":65,"title":"revives a registration that a re-registered token replaces","decision":"keep","reason":"A provider-dead registration must become live after the host rotates its token."},{"line":86,"title":"lets only the owning host delete a registration","decision":"keep","reason":"A foreign owner must not delete a row; the owner must successfully delete it."},{"line":99,"title":"scopes lookups and listings to the owning host","decision":"keep","reason":"Mixed owned/foreign lookup inputs and listings must remain scoped to the caller."},{"line":120,"title":"refuses a new device once the host reaches its registration cap","decision":"keep","reason":"The next new identity beyond the host quota must be rejected without insertion."},{"line":131,"title":"still lets a capped host re-register a device it already owns","decision":"keep","reason":"A full host must still update a device already registered."},{"line":140,"title":"frees a slot when a registration is deleted","decision":"keep","reason":"Deleting a row must release quota for a new identity."},{"line":150,"title":"counts the cap per host, not across the whole table","decision":"keep","reason":"Another host must remain able to register after the first host reaches its cap."},{"line":160,"title":"bounds list reads to the host device allowance","decision":"keep","reason":"Directly seeded oversized data isolates the query result limit from admission limits."},{"line":174,"title":"separates the same device id registered against two hosts","decision":"keep","reason":"Identical device IDs owned by different hosts must receive distinct registration IDs."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"bce3e17bfe1cc7c99dd981ef7503a0095f9c7282b680cba33c7e4c85877e621a"}
{"path":"cloud/apps/push/src/durable-push-claim-mixed-revision.test.ts","sha256":"f81592b439ac3bef5afb1ceac75f7a35e1f64b90586048b13074c466999730a6","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Keep the previous revision SQL as an explicit mixed-deployment participant; replace an unproven sleep interleaving with a real advisory-lock observation.","evidence":["cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/push-database.ts","cloud/apps/push/src/durable-push-store.test-fixture.ts"],"cases":[{"line":46,"title":"keeps a previous-revision claim from re-leasing a delivery this revision leased","decision":"fix","reason":"Observe the old revision blocked on the new claim's shared advisory lock before releasing its transaction; finally releases both participants even if the observation fails."},{"line":69,"title":"yields to a previous-revision claim that already holds the claim lock","decision":"keep","reason":"An older revision holding the exclusive claim lock must make the current implementation yield promptly and preserve the old lease."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"42e3be8228affeffbc5f5d13a1e23e463a457777084df43b1fb42080a4ed9bb8"}
{"path":"cloud/apps/push/src/durable-push-claim.test.ts","sha256":"5f4e34e63c93f6016d5aaf1278e1cd5488f110b017ae383f54d899ae05dbc550","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real queue transactions cover independent pool progress, per-device exclusion, rollback, lease recovery and bounded pruning.","evidence":["cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/push-background-database.ts"],"cases":[{"line":24,"title":"leaves completion, renewal, requests and another store independent of queued claims","decision":"keep","reason":"Holds three real claims while completion, renewal, request queries and another store progress using spare connections."},{"line":85,"title":"rolls back a failed claim and admits a fourth waiter while the other claims stay held","decision":"keep","reason":"A transaction failure must roll back the lease and release admission to a waiting claimant."},{"line":139,"title":"leases exactly one delivery per device across concurrent claimers","decision":"keep","reason":"Concurrent stores must lease only the head delivery for one device."},{"line":153,"title":"does not lease the row behind a head another claimer holds","decision":"keep","reason":"A locked device head must prevent leasing a later row for that same device."},{"line":173,"title":"claims another device while one claim transaction is still open","decision":"keep","reason":"A held claim for one device must not block another device."},{"line":192,"title":"keeps a device exclusive when an earlier-sorting row lands mid-claim","decision":"keep","reason":"Inserts an earlier-sorting row during a claim to check device exclusivity under clock skew."},{"line":216,"title":"never scans rows due before the notification TTL window","decision":"keep","reason":"Directly seeds stale due rows and observes actual scan results to protect bounded backlog work."},{"line":270,"title":"keeps claim, exclusion and prune correct without the queue index","decision":"keep","reason":"Removing the optional queue index must preserve exclusion, ordering and prune semantics."},{"line":289,"title":"deletes a batch once it is finished, and keeps it only for a retry","decision":"keep","reason":"Finishing removes a row; a valid retry retains it, and an out-of-lifetime retry removes it."},{"line":307,"title":"frees a device held by a crashed worker once its lease lapses","decision":"keep","reason":"A crashed worker's lease must fence other claims until its deadline, then permit recovery."},{"line":323,"title":"prunes a large backlog in bounded calls without touching live or leased work","decision":"keep","reason":"A large real database backlog must drain within per-call batch budgets without deleting leased or live work."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"5f4e34e63c93f6016d5aaf1278e1cd5488f110b017ae383f54d899ae05dbc550"}
{"path":"cloud/apps/push/src/durable-push-fixture-lock-isolation.test.ts","sha256":"5223fe0329fb41d90ddf236df3bc8b8ac0cb41f81c6a32bb5b9b6de182e2def3","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Fixture correctness affects every PostgreSQL concurrency suite; this executes actual scoped advisory locks across two isolated schemas.","evidence":["cloud/apps/push/src/durable-push-store.test-fixture.ts","cloud/apps/push/src/durable-push-store.ts"],"cases":[{"line":15,"title":"claims the same device in another schema while a fixture holds its device lock","decision":"keep","reason":"Checks distinct schemas in the same database, then holds one device lock and proves only that fixture is blocked."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"5223fe0329fb41d90ddf236df3bc8b8ac0cb41f81c6a32bb5b9b6de182e2def3"}
{"path":"cloud/apps/push/src/durable-push-payload-parse-budget.test.ts","sha256":"0d471ef6ba44d10245096c39c054188d246de21f6a49a761f7e78522c8140bc5","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Remove copied SQL sequence/arguments and repeated derived hashes; retain real stored payload fidelity, one-parse work bounds, fresh objects, rejection and rollback.","evidence":["cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/push-delivery-payload.ts","cloud/apps/push/src/durable-push-claim.test.ts","cloud/apps/push/src/durable-push-store.test.ts"],"cases":[{"line":47,"title":"parses each leased row once with exact complete payload, serialized key order and SQL sequence","decision":"consolidate","reason":"Keep exact delivery structure, serialized unknown fields/Unicode and one JSON parse; remove complete production SQL snapshot, redundant hashes and buildPushDelivery compared with itself."},{"line":96,"title":"keeps concurrent device claims separate and returns fresh payload objects","decision":"keep","reason":"Two device deliveries must contain distinct parsed objects and parse their respective stored payloads once."},{"line":112,"title":"reads changed retry bytes and a later writer update without carrying a parsed result across calls","decision":"consolidate","reason":"Keep exact serialized retry/later-writer bytes and fresh object checks; remove hashes already implied by the byte equality assertions."},{"line":151,"title":"keeps dismissed alerts on the original single-parse delete path without leasing","decision":"consolidate","reason":"Keep dismissal returning no delivery, removing its database row and parsing once; drop exact DELETE/UPDATE statement spelling checks."},{"line":168,"title":"preserves the existing trust boundary for an object missing notification fields","decision":"keep","reason":"An object payload lacking notification fields preserves the store's existing trust boundary without schema invention."},{"line":179,"title":"preserves invalid payload rejection and rolls back the lease for %s","decision":"keep","reason":"Parameterized malformed/scalar payloads must reject with the correct error and leave the real database row unchanged."},{"line":207,"title":"preserves the exact database UPDATE error and retries with a fresh payload after rollback","decision":"keep","reason":"A real SQLite trigger or PostgreSQL constraint forces lease failure; verify the same database error escapes, rollback restores the row and a subsequent claim succeeds."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"2feaf56d285c308ae65bc8c974f252b7717032c0a778ed677efc60d19d1ccc6b"}
{"path":"cloud/apps/push/src/durable-push-retention.test.ts","sha256":"0c0a4bc2ff6ec1c04f8255328e59c58d8793c911820848378e10df96cfd9387c","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Parameterized real database tests cover all three retention tables and PostgreSQL lock skipping, rather than table-name inventories.","evidence":["cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/durable-push-schema.ts"],"cases":[{"line":30,"title":"prunes expired $table rows across batches and preserves the cutoff","decision":"keep","reason":"Seeds more than one delete batch in every retention table and checks exact cutoff preservation plus idempotent follow-up."},{"line":50,"title":"skips a locked oldest $table row until its transaction releases it","decision":"keep","reason":"Locks the oldest expired row in a real transaction and proves pruning skips it until release while preserving the cutoff."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"0c0a4bc2ff6ec1c04f8255328e59c58d8793c911820848378e10df96cfd9387c"}
{"path":"cloud/apps/push/src/durable-push-store.test.ts","sha256":"340e9145d55764b53e06a014119df5331c6a40d3568b58e306c6b0d16223e137","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real persistence and state transitions cover acceptance, idempotency, quotas, ordering, dismissal and stale lease fencing.","evidence":["cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/push-queued-dismissal.ts"],"cases":[{"line":14,"title":"counts a logical event once across phones and separates the 300/15min dismissal budget","decision":"keep","reason":"Counts one event across recipient phones, maintains separate dismissal quota and recovers after the quota window."},{"line":28,"title":"queues one delivery per event and recovers work across service instances","decision":"keep","reason":"A second service instance must resume deduplicated work while stale lease completion cannot erase the reclaimed delivery."},{"line":61,"title":"never extends expiry and refuses conflicting duplicate content","decision":"keep","reason":"Conflicting duplicate content must fail and retries must not extend the event lifetime."},{"line":75,"title":"orders a due retry before a fresh first attempt without delaying the retry","decision":"keep","reason":"A due retry must precede a new event without waiting for a coalescing interval."},{"line":92,"title":"orders an expired first-attempt lease by creation time after a retry becomes due","decision":"keep","reason":"A seeded crashed delivery with a later ordering key must not overtake an already due retry."},{"line":112,"title":"rolls quota and payload back together if persistence fails","decision":"keep","reason":"A real missing-table write failure must roll quota and recipient persistence back together."},{"line":138,"title":"serializes concurrent instances at the quota boundary","decision":"keep","reason":"Concurrent stores competing for the final quota slot must admit exactly one event."},{"line":151,"title":"cancels unsent alerts and prevents an older replay after dismissal","decision":"keep","reason":"A dismissal must cancel unsent alerts and prevent delivery of an older replay to another phone."},{"line":167,"title":"does not resurrect an in-flight alert after a dismissal and transient provider failure","decision":"keep","reason":"An in-flight alert retried after dismissal must not be resurrected."},{"line":184,"title":"normalizes default alert kind (explicit first: %s)","decision":"keep","reason":"Both explicit/implicit alert insertion orders must deduplicate while conflicting content fails and dismissals remain distinct."},{"line":201,"title":"fences late renew and finish after an expired claim is dismissed","decision":"keep","reason":"Late renew/finish calls from an expired dismissed claim must be fenced; subsequent unrelated retry work still completes."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"340e9145d55764b53e06a014119df5331c6a40d3568b58e306c6b0d16223e137"}
{"path":"cloud/apps/push/src/durable-push-worker.test.ts","sha256":"2e5dee2186165646b3adebf91ba2b303d70ba7e9c3985e13ce720c7109c0d379","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Retain integration of real durable queue, device registry and dispatcher with fake provider outcomes; scheduler and throughput checks observe actual sends.","evidence":["cloud/apps/push/src/durable-push-worker.ts","cloud/apps/push/src/durable-push-store.ts","cloud/apps/push/src/push-dispatcher.ts"],"cases":[{"line":72,"title":"sends every burst event immediately with its original content and identity","decision":"keep","reason":"Real stored burst events must become separate provider requests retaining original titles, bodies and identities."},{"line":101,"title":"keeps untrackable bells and per-phone deliveries individually replaceable","decision":"keep","reason":"Untrackable bell events and multiple recipient phones must retain individually replaceable collapse identities."},{"line":125,"title":"preserves provider retry delay when waking a restarted worker","decision":"keep","reason":"Restarting a worker must honor the persisted provider delay and original expiry."},{"line":154,"title":"expires instead of shortening a provider delay beyond the delivery lifetime","decision":"keep","reason":"A retry delay beyond the lifetime must expire work instead of shortening the provider's requested delay."},{"line":171,"title":"rechecks the device before a persisted retry and does not send after unregistration","decision":"keep","reason":"Deleting the device before a persisted retry must prevent another provider request and clear queued work."},{"line":183,"title":"joins active work on shutdown and leaves unclaimed work for the next instance","decision":"keep","reason":"Shutdown must await an in-flight send, leave unclaimed work durable and release timers; a new worker resumes it."},{"line":223,"title":"stops queued claim admissions without leasing the remaining device","decision":"keep","reason":"Stopping while claim admission is queued must not lease a remaining device."},{"line":259,"title":"claims new devices while an earlier provider response is still pending","decision":"keep","reason":"A held provider response must not prevent newly enqueued devices being sent."},{"line":306,"title":"runs due work on its timer and releases the timer on stop","decision":"keep","reason":"Fake timers verify scheduled work actually sends and stopping clears the poll."},{"line":320,"title":"wakes only while started and keeps the durability poll","decision":"keep","reason":"Wakes before start or after stop must not claim work; active wakes send while the durability poll persists."},{"line":343,"title":"coalesces wakes arriving after every active drain found an empty queue","decision":"keep","reason":"Holds empty drain responses, enqueues during the gap, and checks coalesced wake processing sends promptly."},{"line":380,"title":"bounds claims by sends plus drains for a burst and a trickle of wakes","decision":"keep","reason":"A large real queue burst and idle trickle measure claims relative to completed sends, protecting database workload growth."},{"line":430,"title":"recovers from a failed automatic wake without leaving another timer","decision":"keep","reason":"A failing automatic claim must report safely and allow a later wake to deliver without leaking timers."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"2e5dee2186165646b3adebf91ba2b303d70ba7e9c3985e13ce720c7109c0d379"}
{"path":"cloud/apps/push/src/fcm-client.test.ts","sha256":"e42da4d3a80d04905324d24ec13c9ab45b19091d1169978ad0327d55f92e08d4","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Actual provider serialization and response classification are meaningful boundary assertions; no mocked output is being asserted against itself.","evidence":["cloud/apps/push/src/fcm-client.ts","cloud/apps/push/src/push-delivery-message.ts"],"cases":[{"line":54,"title":"posts the v1 send payload for the configured project","decision":"keep","reason":"Client must target the configured project and serialize complete FCM data content, identity and TTL."},{"line":82,"title":"carries every data value as a string and omits a null agent state","decision":"keep","reason":"Null state omission, string conversion, collapse identity and absence of auto-display notification data protect Android delivery semantics."},{"line":105,"title":"marks an unregistered token dead from the status or the error detail","decision":"keep","reason":"Both top-level status and nested provider detail must classify unregistered tokens as dead."},{"line":130,"title":"marks an invalid-argument that names the token dead, and others an error","decision":"keep","reason":"INVALID_ARGUMENT naming the registration token must retire it while a payload error leaves it live."},{"line":155,"title":"treats a server fault and a transport failure as errors","decision":"keep","reason":"Server errors and thrown transport failures must yield retryable client outcomes."},{"line":182,"title":"does not send when credential refresh crosses the absolute expiry","decision":"keep","reason":"Credential refresh that crosses expiry must suppress the transport call."},{"line":201,"title":"decreases retry TTL and refuses expired delivery before refreshing credentials","decision":"keep","reason":"Retries must reduce remaining TTL and expired work must not refresh credentials or send."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"e42da4d3a80d04905324d24ec13c9ab45b19091d1169978ad0327d55f92e08d4"}
{"path":"cloud/apps/push/src/host-challenge-store.test.ts","sha256":"03ce22cd06a48fb3f382da787cf77bf0a0fcd4a2f931582bb3c22ed1a85f1e90","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Keep real encrypted challenge, persisted digest, replay, exact expiry and proof checks; remove redundant expiry replay and a negative that validates only the fixture peer.","evidence":["cloud/apps/push/src/host-challenge-store.ts","src/main/runtime/push/push-host-proof.test.ts"],"cases":[{"line":29,"title":"completes a challenge, proof, and consume round trip","decision":"keep","reason":"A gateway-issued encrypted challenge must carry the expected expiry/fingerprint and verify through an independently implemented peer."},{"line":48,"title":"never stores material that reproduces the proof","decision":"keep","reason":"Database rows must store a digest rather than the usable acknowledgement proof."},{"line":61,"title":"rejects a replayed challenge","decision":"keep","reason":"A consumed proof must be rejected on replay."},{"line":76,"title":"rejects a challenge the moment its own ttl elapses","decision":"keep","reason":"TTL plus one millisecond must reject, protecting the exact no-skew expiry boundary."},{"line":91,"title":"spends no skew tolerance on its own expiry, so the ttl is the whole window","decision":"remove","reason":"Repeats the expired branch already checked at TTL plus one, farther into the same skew allowance."},{"line":108,"title":"accepts a proof that lands just inside the ttl","decision":"keep","reason":"A proof exactly on the TTL boundary must remain accepted."},{"line":120,"title":"keeps an expired row long enough to answer expired rather than unknown","decision":"keep","reason":"Pruning immediately after expiry must retain enough history to return expired instead of unknown."},{"line":136,"title":"refuses a wrong host: the box will not open and a foreign proof will not match","decision":"keep","reason":"Wrong-host box opening and foreign acknowledgement must fail, with the latter checked by the production verifier."},{"line":160,"title":"rejects a proof bound to a different gateway origin","decision":"remove","reason":"The assertion calls only test-fixture peer rejection for a foreign origin; shipped desktop peer rejection is exercised directly in push-host-proof.test.ts and gateway construction is exercised by live round trips."},{"line":175,"title":"rejects an unknown challenge id and a malformed public key","decision":"keep","reason":"Unknown challenge and noncanonical/wrong-width public keys must fail safely."},{"line":184,"title":"prunes challenges that fell out of the skew window","decision":"keep","reason":"Challenge pruning must remove rows only after the expiry retention window."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"b8c8bff08a4bfbe7bd1bcc66afeb9e587c65db041b8d9dbb8d9373fccdfd69d5"}
{"path":"cloud/apps/push/src/host-proof-concurrency.test.ts","sha256":"f18a7d4f77126887575400e736d71fa4c0ff425eab7a4b2a529767b9f5072f7e","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real database concurrency checks consume-once across independent host challenges; preserves PostgreSQL execution when configured.","evidence":["cloud/apps/push/src/host-challenge-store.ts","cloud/apps/push/src/push-database.ts"],"cases":[{"line":10,"title":"accepts independent proofs but consumes each challenge only once under concurrency","decision":"keep","reason":"Five simultaneous proof submissions for each of two live challenges must produce exactly two successful consumes and eight replay refusals."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"f18a7d4f77126887575400e736d71fa4c0ff425eab7a4b2a529767b9f5072f7e"}
{"path":"cloud/apps/push/src/host-session-store.test.ts","sha256":"2e2aed9de6bcc098341f562c808dd918fcab36d15e0bccd72b5ff91e69c58162","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real persisted sessions cover hashed storage, expiry boundaries, host rotation and pruning.","evidence":["cloud/apps/push/src/host-session-store.ts"],"cases":[{"line":23,"title":"mints a 24 hour session and stores only its hash","decision":"keep","reason":"A minted session must have expected TTL/entropy, store no raw bearer and resolve to its owner."},{"line":35,"title":"reports expiry separately from an unknown token","decision":"keep","reason":"Expired and unknown tokens must produce distinct failure reasons."},{"line":48,"title":"accepts a session on its final millisecond","decision":"keep","reason":"The final millisecond is an explicit expiry boundary case."},{"line":54,"title":"keeps one live session per host and prunes it once expired","decision":"keep","reason":"A second proof must invalidate the previous host session without harming another host, then pruning removes both expired rows."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"2e2aed9de6bcc098341f562c808dd918fcab36d15e0bccd72b5ff91e69c58162"}
{"path":"cloud/apps/push/src/push-authenticated-ip-limit.test.ts","sha256":"8318e83087165ebd509498865be8a14d67888a6cd5aa0a900803ff65e4bcd6b1","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Retains end-to-end authentication/IP budget composition across host identities and routes.","evidence":["cloud/apps/push/src/push-server.ts","cloud/apps/push/src/client-ip-rate-limit.ts"],"cases":[{"line":6,"title":"bounds valid hosts together across routes without letting key rotation reset the IP budget","decision":"keep","reason":"Exhausts a shared authenticated-IP allowance across independently authenticated hosts, checks two protected routes, then proves another IP and clock refill work."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"8318e83087165ebd509498865be8a14d67888a6cd5aa0a900803ff65e4bcd6b1"}
{"path":"cloud/apps/push/src/push-background-database.test.ts","sha256":"d9db5c2aedd05ad55a299a24cf052f96e5039311c333649fe105d06e2c168f34","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Replace 5ms scheduling sleeps with explicit held-operation gates while retaining actual admission and server-composition measurements.","evidence":["cloud/apps/push/src/push-background-database.ts","cloud/apps/push/src/push-server.ts"],"cases":[{"line":28,"title":"lets background work hold at most poolMax - 1 of %i connections","decision":"fix","reason":"Hold concurrent queries/transactions at explicit deferred gates until capacity is reached; verify poolMax minus one admission including the single-connection boundary without a wall-clock delay."},{"line":43,"title":"bounds server claims before they enter the background pool","decision":"fix","reason":"Observe three actual server claims reach the held database boundary, then release them and verify the peak; removes reliance on 5ms scheduling."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"32c8ceb40cedf963ce4a8912c5815762f05d83835936a36a1888225d4094333b"}
{"path":"cloud/apps/push/src/push-background.test.ts","sha256":"f4854846eea3045fe241f5537e3535aa55f2dbdfaf433dc8fa1d6dd0b9604199","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Fake-clock tests exercise scheduler admission, watchdogs, retry and backlog fairness; store/provider mocks are boundaries, with real DurablePushStore in the fairness case.","evidence":["cloud/apps/push/src/push-background.ts","cloud/apps/push/src/push-background-database.ts","cloud/apps/push/src/durable-push-store.ts"],"cases":[{"line":34,"title":"keeps one slow sweep per store while other stores keep their cadence","decision":"keep","reason":"A held delivery sweep must not overlap itself or block other cadence; stopping prevents later work."},{"line":57,"title":"reports a sweep that is still running a full interval after it started","decision":"keep","reason":"A held sweep must generate a single overdue warning at the watchdog boundary."},{"line":78,"title":"releases a failed sweep so the next scheduled sweep can recover","decision":"keep","reason":"A failed sweep must report safely and release its schedule for recovery."},{"line":91,"title":"resumes a saturated sweep at once rather than waiting out the interval","decision":"keep","reason":"Saturated bounded sweeps must resume immediately until the backlog drains, then return to interval cadence."},{"line":109,"title":"keeps a delivery claim behind one statement while a backlog drains back to back","decision":"keep","reason":"The real pruning loop under a simulated slow SQL boundary must permit a delivery claim behind at most one delete statement."},{"line":167,"title":"keeps validation mode free of sweeps and timers","decision":"keep","reason":"Validation mode must not start pruning or timers even after many intervals."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"f4854846eea3045fe241f5537e3535aa55f2dbdfaf433dc8fa1d6dd0b9604199"}
{"path":"cloud/apps/push/src/push-database-postgres-startup.test.ts","sha256":"9143eccb60b0bdfc04e6d57a19ed1cbdafabe18a8d2d5a65f6f2f7f4e2eff5cb","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Mocking pg at its external constructor/query boundary protects URL parsing, pool isolation and transaction retry ordering.","evidence":["cloud/apps/push/src/push-database.ts","cloud/apps/push/src/push-schema.ts"],"cases":[{"line":48,"title":"passes the Terraform socket URL unchanged to both active pools","decision":"keep","reason":"Active pools must receive an encoded Cloud SQL socket URL unchanged, preserving credential and socket parsing by pg."},{"line":54,"title":"parses socket credentials and query options before enforcing validation read-only","decision":"keep","reason":"Read-only mode must parse socket/query options first and append enforcement after caller options without applying schema writes."},{"line":78,"title":"applies the schema on an untimed pool that is gone before the serving pool opens","decision":"keep","reason":"Schema DDL must use an untimed pool closed before serving connections are created; actual schema statements must be applied there."},{"line":101,"title":"retries a transaction the pool statement_timeout aborted","decision":"keep","reason":"A statement-timeout abort must roll back/retry the whole transaction and return the later result."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"9143eccb60b0bdfc04e6d57a19ed1cbdafabe18a8d2d5a65f6f2f7f4e2eff5cb"}
{"path":"cloud/apps/push/src/push-delivery-lifecycle.test.ts","sha256":"4c7401a582b46b601cfd7bb5609ae90d65f1e8353b1b45da694ba9c56ce17c78","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real device storage and middleware interleavings guard token replacement and graceful request drain.","evidence":["cloud/apps/push/src/push-dispatcher.ts","cloud/apps/push/src/device-registry-store.ts","cloud/apps/push/src/push-request-drain.ts"],"cases":[{"line":46,"title":"does not retire a refreshed token after the old token fails","decision":"keep","reason":"Hold a provider response across token refresh; the stale dead-token outcome must not retire the replacement token."},{"line":66,"title":"rejects new requests during drain and waits for an admitted handler","decision":"keep","reason":"An admitted handler must delay drain completion while a later request is refused, then completion releases the drain."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"4c7401a582b46b601cfd7bb5609ae90d65f1e8353b1b45da694ba9c56ce17c78"}
{"path":"cloud/apps/push/src/push-dismissal-provider.test.ts","sha256":"da247da7b7c00f9925ac7e9804f010d545f627398abd7ed375fe0b0fde2a01c8","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Production serializers must prevent a dismissal from displaying or sounding like a new alert on either mobile platform.","evidence":["cloud/apps/push/src/apns-client.ts","cloud/apps/push/src/fcm-client.ts"],"cases":[{"line":6,"title":"dismissal provider payloads cannot display a new alert or play a sound","decision":"keep","reason":"Checks actual APNs background content and FCM data-only dismissal without title/message/sound/collapse fields."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"da247da7b7c00f9925ac7e9804f010d545f627398abd7ed375fe0b0fde2a01c8"}
{"path":"cloud/apps/push/src/push-execution-host-routing.test.ts","sha256":"736e52092b62fb3b3598ea921de38e60f9022d5997dd564746ff5ba4f2d8fa56","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Preserves a compatibility boundary for old messages and runtime host routing metadata across provider forms.","evidence":["cloud/packages/push-contract/src/send-messages.ts","cloud/apps/push/src/push-delivery-message.ts"],"cases":[{"line":15,"title":"carries the workspace server to APNs and FCM, and accepts older messages without one","decision":"keep","reason":"Real schema parsing and delivery construction must retain executionHost when present while accepting an older message without it."},{"line":30,"title":"accepts only runtime execution hosts","decision":"keep","reason":"Schema must reject local/SSH/empty/delimiter-bearing and oversized execution hosts."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"736e52092b62fb3b3598ea921de38e60f9022d5997dd564746ff5ba4f2d8fa56"}
{"path":"cloud/apps/push/src/push-notification-sound.test.ts","sha256":"544d6100a1f60c6d81c85eb2b1d0bdaa639925d3a313faca2253f0d4b55b3d55","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Cross-provider silent preference is a real product behavior guarded through schema and serializers.","evidence":["cloud/packages/push-contract/src/send-messages.ts","cloud/apps/push/src/push-delivery-message.ts","cloud/apps/push/src/apns-client.ts","cloud/apps/push/src/fcm-client.ts"],"cases":[{"line":7,"title":"carries a silent preference through validation to APNs and Android payloads","decision":"keep","reason":"A validated silent notification must omit APNs sound and select Android silent channel/data; the default APNs preference remains audible."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"544d6100a1f60c6d81c85eb2b1d0bdaa639925d3a313faca2253f0d4b55b3d55"}
{"path":"cloud/apps/push/src/push-pane-routing.test.ts","sha256":"82037785a57da4cb5182e73dcb5012dd42fd799a7920dd0a2a698b4d3876c186","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Pane metadata and legacy workspace-only messages need compatible provider conversion.","evidence":["cloud/packages/push-contract/src/send-messages.ts","cloud/apps/push/src/push-delivery-message.ts"],"cases":[{"line":5,"title":"preserves pane identity for both APNs and FCM, and accepts older workspace-only messages","decision":"keep","reason":"Parses both current and older notification shapes and checks paneKey survives delivery construction and string-data conversion."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"82037785a57da4cb5182e73dcb5012dd42fd799a7920dd0a2a698b4d3876c186"}
{"path":"cloud/apps/push/src/push-readiness.test.ts","sha256":"b5d5c8331616017e933e6f5d755adfa38d0e71f63fb309ae0a846e2246a37026","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"A deferred real readiness query proves single-flight and negative caching instead of asserting mock-provided status alone.","evidence":["cloud/apps/push/src/push-readiness.ts"],"cases":[{"line":5,"title":"shares a slow check and caches failures before retrying","decision":"keep","reason":"One hundred callers must share one pending query, reuse its failed result within the cache window and issue a fresh query after expiry."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"b5d5c8331616017e933e6f5d755adfa38d0e71f63fb309ae0a846e2246a37026"}
{"path":"cloud/apps/push/src/push-schema-boot-lock.test.ts","sha256":"9d95c2555c11cf665627fbe756959a744856a79d11bd7d094a61f6708a7b6333","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real PostgreSQL DDL lock contention verifies queue indexes exist after startup retries rather than merely checking their names in source.","evidence":["cloud/apps/push/src/push-database.ts","cloud/apps/push/src/push-schema.ts","cloud/packages/postgres-schema/src/apply-postgres-schema.ts"],"cases":[{"line":16,"title":"creates the queue index after waiting out a writer that holds the table","decision":"keep","reason":"Drops a live index, holds a conflicting writer lock, boots the actual database wrapper and queries pg_indexes plus retry/defer events."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"9d95c2555c11cf665627fbe756959a744856a79d11bd7d094a61f6708a7b6333"}
{"path":"cloud/apps/push/src/push-send-idempotency.test.ts","sha256":"d2f01324376f42616f1f228a8b21766f0e50bb6b89870d5097f0b0593afd28ff","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Real API/store/worker tests protect repeated deliveries across concurrency and mixed client alert representations.","evidence":["cloud/apps/push/src/push-server.ts","cloud/apps/push/src/durable-push-store.ts","cloud/packages/push-contract/src/send-messages.ts"],"cases":[{"line":9,"title":"returns queued for concurrent retries without double quota or delivery","decision":"keep","reason":"Concurrent API retries must share one quota event and delivery; changing epoch must deliver again."},{"line":38,"title":"accepts default alert kind equivalently through the API (explicit first: %s)","decision":"keep","reason":"Both explicit-first and implicit-first alert kind clients must deduplicate through API schema transformation, while content conflict returns an error."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"d2f01324376f42616f1f228a8b21766f0e50bb6b89870d5097f0b0593afd28ff"}
{"path":"cloud/apps/push/src/push-server-auth.test.ts","sha256":"8ea2698991cb567bca65119babaa666b8440bba0cf8d86f0fecd50e3a22d1d4a","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Retain real API authentication/device boundaries; focus a copied response-key list on the sensitive-field leak it exists to prevent.","evidence":["cloud/apps/push/src/push-server.ts","cloud/apps/push/src/device-registry-store.ts","cloud/apps/push/src/host-challenge-store.ts","cloud/apps/push/src/host-session-store.ts"],"cases":[{"line":22,"title":"answers health unconditionally and ready from the database","decision":"keep","reason":"Actual health/readiness routes must be available with a reachable database."},{"line":27,"title":"reports not ready when the database is unreachable","decision":"keep","reason":"A throwing external database must leave health available and make readiness fail."},{"line":48,"title":"completes challenge, session, register, list, delete","decision":"keep","reason":"Real sign-in, registration, listing and deletion must interoperate through routes and persist the expected effects."},{"line":66,"title":"refuses a request with no bearer, a bogus bearer, and an expired session","decision":"keep","reason":"Missing, malformed and expired bearer paths must return appropriate authentication failures."},{"line":79,"title":"refuses a replayed proof and an unknown challenge","decision":"keep","reason":"API replay and unknown challenge failures must map to stable wire errors."},{"line":109,"title":"never returns the host fingerprint on the challenge itself","decision":"consolidate","reason":"Replace an unrelated five-key response inventory with a direct absence assertion for the sensitive hostFingerprint field."},{"line":120,"title":"lets only the owning host delete a registration","decision":"keep","reason":"Foreign host deletion must return not-found and preserve the owner's row."},{"line":135,"title":"replaces the token on a re-registration and keeps one registration id","decision":"keep","reason":"Re-registering through the API must preserve ID while changing the stored token."},{"line":154,"title":"rejects a malformed registration body","decision":"keep","reason":"An invalid iOS registration must reach schema rejection and return the expected 400 body."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"14a032daf535423495d91b7802e91851deac8b3f1b068f8a47eed05d68d474d7"}
{"path":"cloud/apps/push/src/push-server-limits.test.ts","sha256":"e3b97c161cde963d100b67a519e321e092a2f30a46b4a3c92c237da8af92b1a2","reviewer":"audit_cloud","status":"reviewed","decision":"keep","rationale":"Full route wiring for body, authentication, IP and event limits remains valuable beyond individual limiter/store tests.","evidence":["cloud/apps/push/src/push-server.ts","cloud/apps/push/src/client-ip-rate-limit.ts","cloud/apps/push/src/device-registry-store.ts"],"cases":[{"line":43,"title":"refuses an oversized chunked body that declares no content length","decision":"keep","reason":"A streamed body without Content-Length must still be capped before schema parsing."},{"line":52,"title":"still refuses an oversized body that declares a content length","decision":"keep","reason":"Declared Content-Length must take the same payload cap path."},{"line":66,"title":"lets a chunked body under the cap through to schema validation","decision":"keep","reason":"A valid streamed challenge under the cap must reach the handler."},{"line":76,"title":"caps an authenticated oversized send as well","decision":"keep","reason":"Body limits must also cover the authenticated send route."},{"line":98,"title":"rate limits one client ip across both unauthenticated routes","decision":"keep","reason":"Two unauthenticated routes must share IP budget while unrelated and spoofed hops behave correctly."},{"line":149,"title":"lets a throttled client back in once the window refills","decision":"keep","reason":"Route integration must return admission after the injected refill window."},{"line":170,"title":"limits authenticated hosts independently behind the same IP","decision":"keep","reason":"Authenticated hosts behind one IP must retain individual host budgets without charging the handshake budget."},{"line":190,"title":"stops repeated forged bearers after the invalid-auth budget is exhausted","decision":"keep","reason":"Repeated invalid bearers must exhaust their separate budget without creating sessions or charging handshake state."},{"line":205,"title":"answers 409 once a host has registered its device allowance","decision":"keep","reason":"A full device registry must map admission refusal to HTTP 409 and preserve the bounded listing."},{"line":237,"title":"answers an unexpected route failure with a bare 500 and logs only the name","decision":"keep","reason":"A real closed-database error must yield a bare 500, redact SQL/table data from logs and increment observability."},{"line":255,"title":"charges a repeated registration id once and returns one result","decision":"keep","reason":"Schema deduplication of repeated registration IDs must yield one API result, queue row and event charge."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"e3b97c161cde963d100b67a519e321e092a2f30a46b4a3c92c237da8af92b1a2"}
{"path":"cloud/apps/push/src/push-server-send.test.ts","sha256":"0a6872d26f96250026e2022149f900fafbff983472b32a10f8b5e06dff9949ac","reviewer":"audit_cloud","status":"reviewed","decision":"change","rationale":"Retain real API/queue/provider delivery and failure paths; consolidate the lone-event collapse tag check into the existing FCM send integration.","evidence":["cloud/apps/push/src/push-server.ts","cloud/apps/push/src/durable-push-worker.ts","cloud/apps/push/src/fcm-client.test.ts"],"cases":[{"line":23,"title":"rejects a batch over the registration cap","decision":"keep","reason":"Too many registration targets must reach API schema rejection."},{"line":41,"title":"queues a send, delivers it to fcm, and reports a dead token on the next send","decision":"consolidate","reason":"Preserve queue, FCM body, dead-token persistence and listing checks; also absorb the lone-event tag assertion from the removed duplicate setup."},{"line":75,"title":"leaves a live registration alone when the provider reports a transient failure","decision":"keep","reason":"A transient provider failure must not mark the persisted device dead."},{"line":91,"title":"reports retries from the durable worker after the provider delay","decision":"keep","reason":"The durable provider delay must defer retry, then observability must distinguish error, retry and sent outcomes."},{"line":118,"title":"sends a burst as individual APNs alerts grouped by the host thread","decision":"keep","reason":"A real API burst must generate three independent APNs requests grouped by thread with distinct collapse identities."},{"line":170,"title":"sends a lone event through unchanged with its own collapse id","decision":"consolidate","reason":"Move its only unique tag-format assertion into the existing FCM delivery case; the complete FCM serializer assertion already excludes coalescedCount."},{"line":186,"title":"reports an error for a registration the host does not own","decision":"keep","reason":"Foreign and nonexistent registration targets must return errors without queueing the owner's row."},{"line":205,"title":"rate limits a host that exhausted its 15-minute allowance","decision":"keep","reason":"A filled real store quota must map to per-recipient rate_limited API results without another insertion."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"2253745abb45da3d5d4158b1322724f2080a295f0f2db231da5c5592a9cb9c77"}
@@ -0,0 +1,21 @@
{
"checks": [
{
"command": "ORCA_BACKGROUND_LAUNCH=1 pnpm exec vitest run --config config/vitest.config.ts <six changed config suites>",
"result": "113 tests passed across 6 suites",
"log": "/tmp/orca-config-audit-final.log"
}
],
"regressions_caught": [
"release tags sorted lexically",
"archive emitted before notarization",
"failed template qualification ignored"
],
"batch": "config-0001",
"platform": "Linux",
"reviewed_files": 35,
"format_and_lint": "passed",
"temporary_production_mutations": "restored; no production diff",
"root_review": "All 22 changed test diffs independently inspected; retained serializer integration, previous-revision SQL boundary and failure controls checked.",
"log_note": "Local log paths are evidence pointers for this session; results are retained here for durable review."
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,26 @@
{
"checks": [
{
"command": "ORCA_BACKGROUND_LAUNCH=1 pnpm test <35 audited mobile suites>",
"result": "256 tests passed across 35 suites",
"log": "/tmp/orca-mobile-0001-final-validation.log"
},
{
"command": "pnpm run check:tests-typecheck",
"result": "1112 test files; 5 intentional exclusions; 116 grandfathered files; no new failures",
"log": "/tmp/orca-mobile-0001-typecheck.log"
}
],
"regressions_caught": [
"carried base64 reencoded",
"mobile density unbudgeted",
"dialog accept/dismiss route swapped"
],
"batch": "mobile-0001",
"platform": "Linux",
"reviewed_files": 35,
"format_and_lint": "passed",
"temporary_production_mutations": "restored; no production diff",
"root_review": "All 22 changed test diffs independently inspected; retained serializer integration, previous-revision SQL boundary and failure controls checked.",
"log_note": "Local log paths are evidence pointers for this session; results are retained here for durable review."
}
@@ -0,0 +1,35 @@
{"path":"mobile/scripts/check-tests-typecheck-incremental-cache.test.ts","sha256":"6851e5a3dcd6028c9b5cd7a2f6e0422d1b4902f96d546d029a835d5a67181701","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Real compilers recheck changed dependencies and membership in a disposable project; retains cache invalidation coverage.","evidence":["mobile/scripts/check-tests-typecheck-ratchet.mjs","mobile/scripts/mobile-typecheck-compiler.mjs"],"cases":[{"line":8,"title":"rechecks changed types, diagnostics and test membership with $compiler","decision":"keep","reason":"Real JavaScript/native compiler passes observe clean, changed-type, added-file, nocheck and deleted-file states; catches stale diagnostics/membership rather than a mocked cache."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"6851e5a3dcd6028c9b5cd7a2f6e0422d1b4902f96d546d029a835d5a67181701"}
{"path":"mobile/scripts/check-tests-typecheck-ratchet.test.ts","sha256":"19badd5a3a8d6c3ca44e1c607b4d8ff372e4731d9a58d6bc6535ea607144b7b4","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain deterministic parsing and census behavior; exclusion parity protects tests from silently leaving the typecheck program.","evidence":["mobile/scripts/check-tests-typecheck-ratchet.mjs","mobile/tsconfig.test.json"],"cases":[{"line":16,"title":"names each file once however many errors it carries","decision":"keep","reason":"Deduplicates multiple real-format compiler diagnostics per file."},{"line":27,"title":"ignores the indented detail lines tsc prints under an error","decision":"keep","reason":"Prevents overload-detail prose from becoming invalid baseline paths."},{"line":38,"title":"normalises the Windows separators tsc prints to the baseline's POSIX ones","decision":"keep","reason":"Exercises Windows compiler paths and canonical baseline separators."},{"line":46,"title":"answers nothing for a clean run","decision":"keep","reason":"Empty compiler output must report no failing files."},{"line":52,"title":"drops comments and blanks when reading the baseline","decision":"keep","reason":"Baseline comments, blanks and whitespace are parsed rather than compared as fixtures."},{"line":59,"title":"reports a newly failing file as added and a newly clean one as stale","decision":"keep","reason":"Added and stale diagnostics are independently classified."},{"line":65,"title":"reports neither when the set is unchanged, which is the green path","decision":"keep","reason":"Unchanged failure set must pass the ratchet."},{"line":74,"title":"names a test file that is on disk but outside the program","decision":"keep","reason":"Detects excluded tests even when no type error is emitted."},{"line":83,"title":"names a .tsx shadowed by a .test.ts of the same basename","decision":"keep","reason":"TSX shadowing by a same-basename TS test is a concrete compiler-membership failure."},{"line":89,"title":"stays quiet for a file excluded on purpose","decision":"keep","reason":"Intentional exclusions must not fail the census."},{"line":96,"title":"reports an allowance whose file is gone, so the list cannot rot","decision":"keep","reason":"Removed files must retire stale exclusions."},{"line":100,"title":"reports an allowance whose file is back in the program","decision":"keep","reason":"Reincluded files must retire stale exclusions."},{"line":106,"title":"stays quiet when every file on disk is in the program","decision":"keep","reason":"Complete disk membership is the census passing path."},{"line":115,"title":"matches tsconfig.test.json exclude entry for entry","decision":"keep","reason":"Checks the live tsconfig exclusions against allowances and real files, preventing silent typecheck gaps; this inventory has an independent configuration boundary."},{"line":137,"title":"takes only the listed test paths as the program","decision":"keep","reason":"Filters compiler library/source listings to actual test paths."},{"line":144,"title":"takes only the diagnostics as the failures, from that same stream","decision":"keep","reason":"Diagnostic parsing remains distinct from file listings in one compiler stream."},{"line":148,"title":"strips a Windows root the same way it strips the separators","decision":"keep","reason":"Windows root removal must match canonical path normalization."},{"line":158,"title":"sees the directive in a leading line comment","decision":"keep","reason":"Leading line nocheck directive must be caught despite compiler success."},{"line":162,"title":"sees it in a leading block comment","decision":"keep","reason":"Leading block nocheck directive must be caught despite compiler success."},{"line":167,"title":"ignores it once code has started","decision":"keep","reason":"Post-statement comments are not TypeScript directives."},{"line":171,"title":"stays quiet for an ordinary header comment","decision":"keep","reason":"Ordinary header prose must remain allowed."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"19badd5a3a8d6c3ca44e1c607b4d8ff372e4731d9a58d6bc6535ea607144b7b4"}
{"path":"mobile/scripts/dependency-security-patches.test.ts","sha256":"27d764a6a1d6877d18e7c8c160a0bb85b55b2aab61cbf3624cdcd0d9dbfdbbf4","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain installed-package behavior checks: these resolve the actual Expo transitive dependencies and exercise patched parser/ASN.1 rejection.","evidence":["mobile/patches/braces@3.0.3.patch","mobile/patches/node-forge@1.4.0.patch","mobile/pnpm-lock.yaml"],"cases":[{"line":15,"title":"bounds %s during parsing","decision":"keep","reason":"Brace, paren and mixed deep patterns exercise the installed parser nesting cap."},{"line":25,"title":"bounds nested patterns in braces.%s","decision":"keep","reason":"Each installed public entry point rejects deep patterns while ordinary expansion still works."},{"line":31,"title":"bounds directly supplied ASTs in braces.%s","decision":"keep","reason":"Direct AST inputs bypass parsing and exercise recursion caps in all three consumers."},{"line":42,"title":"rejects extra DigestAlgorithm elements with NULL parameters: %s","decision":"keep","reason":"Real signed valid and extra-element DigestInfo encodings distinguish accepted signatures from malformed padding with and without NULL parameters."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"27d764a6a1d6877d18e7c8c160a0bb85b55b2aab61cbf3624cdcd0d9dbfdbbf4"}
{"path":"mobile/scripts/rpc-diff-report.test.ts","sha256":"e5082281aab5c0288bbfa6ad098c0071be44c6df1b56b3de20ef054810a30199","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain diff/report behavior on independent recordings, including repeated IDs and bounded reports.","evidence":["mobile/scripts/rpc-diff-report.ts","mobile/src/test-support/rpc-recording/golden-difference.ts","mobile/src/test-support/rpc-recording/golden-value-pool.ts"],"cases":[{"line":20,"title":"pairs a repeated checkpoint id by occurrence, not by its last appearance","decision":"keep","reason":"Repeated checkpoint IDs with different observations must pair by occurrence; matching only the final ID produces a spurious change."},{"line":33,"title":"groups one moved entry across the checkpoints that re-state it","decision":"keep","reason":"Changed cumulative entries are grouped across checkpoints and reported once with later-checkpoint count."},{"line":62,"title":"names identity, checkpoint-list, added and removed changes","decision":"keep","reason":"Identity, removed/new checkpoints and golden addition/removal are public diff output."},{"line":85,"title":"stops details at the byte cap and still names every changed golden","decision":"keep","reason":"Large real changes hit the output cap while retaining the final golden name."},{"line":100,"title":"decodes a pooled file of any format version","decision":"keep","reason":"Loose diff reader must decode pooled content across format-version changes; strict recording reader is a different contract."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"e5082281aab5c0288bbfa6ad098c0071be44c6df1b56b3de20ef054810a30199"}
{"path":"mobile/src/accounts/mobile-accounts-route.test.ts","sha256":"5661787f565ab26dc9ce8796af8014b54aa047e90725bae9ec5848b24e894a64","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain actual route encoding and deferred host-stack replacement behavior.","evidence":["mobile/src/accounts/mobile-accounts-route.ts","mobile/src/navigation/host-stack-navigation.ts"],"cases":[{"line":32,"title":"keeps the host id raw for the navigator to encode","decision":"keep","reason":"Raw host IDs cross the route-builder/navigator boundary without double encoding."},{"line":39,"title":"mounts the host before replacing it with the accounts route","decision":"keep","reason":"Drives state listeners from unmounted host to mounted encoded host, then verifies targeted replacement; this is navigation ordering."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"5661787f565ab26dc9ce8796af8014b54aa047e90725bae9ec5848b24e894a64"}
{"path":"mobile/src/accounts/mobile-accounts-screen-reset-credit.test.ts","sha256":"120dd2089b62de3a638b84d3238a66dc6634bd0bc274db17555ded79c602af7a","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain user-action integration with scarce reset credits, confirmed scope, WSL targeting, errors and Back navigation.","evidence":["mobile/src/accounts/MobileAccountsScreen.tsx","mobile/src/components/use-codex-reset-credit-action.ts","mobile/src/components/codex-reset-credit.ts"],"cases":[{"line":277,"title":"hides the scarce action when an older host does not advertise the capability","decision":"keep","reason":"Rendered old-host capability result hides the action and prevents mutation."},{"line":289,"title":"retries a lost reply under a new key and shows the outcome the host recorded","decision":"keep","reason":"Lost reply followed by remount produces a fresh idempotency key, same confirmed scope, bounded request and recorded host outcome notice."},{"line":326,"title":"shows the resumed outcome when the offer moved after a lost reply","decision":"keep","reason":"Subscription refresh changes offer revision while the reply can describe an older unresolved attempt; verifies notice and outgoing current scope."},{"line":364,"title":"keeps the exact confirmed scope when a subscription changes before confirmation","decision":"keep","reason":"Deferred confirmation retains the exact account/revision despite a changed subscription and displays host rejection."},{"line":411,"title":"passes the active WSL target when clearing the Codex selection","decision":"keep","reason":"Actual WSL snapshot drives the distinct targeted RPC and target arguments; protects old hosts that strip fields."},{"line":449,"title":"recovers from key generation failure without leaving the action busy","decision":"keep","reason":"Failed random-key generation clears busy state and permits a subsequent real request."},{"line":467,"title":"sends nothing when the confirmation is cancelled","decision":"keep","reason":"Cancelling a displayed confirmation executes the cancellation path and sends nothing."},{"line":478,"title":"puts a failed switch on screen, where Alert would be a no-op on the page","decision":"keep","reason":"Failed selection becomes a visible dismissible modal rather than native-only Alert."},{"line":495,"title":"leaves through the route handoff, which pops the shell from inside the page","decision":"keep","reason":"Rendered Back control invokes the shell-compatible route handoff."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"120dd2089b62de3a638b84d3238a66dc6634bd0bc274db17555ded79c602af7a"}
{"path":"mobile/src/agent-history/agent-history-back-handoff.test.tsx","sha256":"9cfe53a9c86376dd2e88482de77b790708b8003ddb023bf3372b8b7d59632894","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Consolidate three identical shell-Back mounts into one exact wire assertion with ready/empty preconditions; retain local-pop branch.","evidence":["mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx","mobile/src/navigation/route-handoff.web.ts","mobile/src/mobile-web-shell/bridge/bridge-port-pair-test-harness.ts"],"cases":[{"line":139,"title":"hands the pop to the shell, because this document has nowhere to go back to","decision":"keep","reason":"one screen tap now checks ready bridge, initially empty notify lane, exact versioned Back frame and no local pop."},{"line":148,"title":"pops this document when the page itself grew a stack","decision":"keep","reason":"Local document with history must pop locally and post no shell Back notification."},{"line":158,"title":"reaches the shell over the same channel the rest of the screen uses","decision":"consolidate","reason":"ready/empty-lane preconditions move into the first shell-Back case; another identical mount adds no distinct branch."},{"line":169,"title":"carries the protocol version every other frame carries","decision":"consolidate","reason":"exact versioned frame assertion moves into the first shell-Back case; wire coverage remains."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"6815312db342ee951b3d207a2fdbee12906b51acf565b3dbddeb8150dc4a60db"}
{"path":"mobile/src/agent-history/agent-history-current-host-capability.test.tsx","sha256":"296db9d41ac26c9675d34b72f70ad482431cd2442c073b2c7adf9cf595f30dc7","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain current-host negotiation across client/host/generation/reconnect boundaries, asserting resulting create/send operations rather than flags alone.","evidence":["mobile/src/agent-history/use-mobile-agent-history-state.ts","mobile/src/session/ai-vault-resume-launch.ts"],"cases":[{"line":42,"title":"requires current host evidence before owned resume: %s","decision":"keep","reason":"Four cutovers retire stale status, force legacy terminal.send while current status is pending, then enable owned-create command only after current-host evidence."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"296db9d41ac26c9675d34b72f70ad482431cd2442c073b2c7adf9cf595f30dc7"}
{"path":"mobile/src/agent-history/agent-history-pending-resume-ownership.test.tsx","sha256":"92a409081a7dfd7f2ab5bd23ffda212f77ceec8622a8cac08fe3e1bd9125041f","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain explicit asynchronous ownership boundaries: tests settle deferred metadata/preparation/create/send replies across host/client/generation/unmount changes.","evidence":["mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx","mobile/src/agent-history/use-mobile-resume-operation-ownership.ts","mobile/src/session/ai-vault-resume-launch.ts"],"cases":[{"line":90,"title":"pending resume keeps its owner: %s during %s","decision":"keep","reason":"18 parameter rows distinguish writes not yet sent from accepted writes after cutover; asserts real calls reached each deferred boundary, navigation suppression and fresh retry mutation IDs."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"92a409081a7dfd7f2ab5bd23ffda212f77ceec8622a8cac08fe3e1bd9125041f"}
{"path":"mobile/src/agent-history/agent-history-reply-schema.test.ts","sha256":"344b2a35edf3c4bbfe75184e2e5ffb7cb23e9e2c53f10f89836abbf9f14a1e3a","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain compatibility and required-container behavior at untrusted host reply boundaries.","evidence":["mobile/src/agent-history/agent-history-reply-schema.ts","mobile/src/agent-history/use-mobile-agent-history-state.ts"],"cases":[{"line":10,"title":"reads an unreadable capability list as a host that does not advertise the vault","decision":"keep","reason":"Malformed optional capabilities degrade to unsupported instead of crashing older hosts."},{"line":16,"title":"passes the rest of the status record through for the platform readers","decision":"keep","reason":"Unknown status fields remain available to platform readers."},{"line":22,"title":"requires both scan containers the ready screen publishes","decision":"keep","reason":"Missing/wrong sessions or issues containers are rejected before ready-state array consumers."},{"line":31,"title":"keeps a session row whose agent this build has never heard of","decision":"keep","reason":"Newer-host agent vocabulary is preserved for round-trip resume."},{"line":41,"title":"requires the resume repo list to be an array","decision":"keep","reason":"Repo container is required and iterable while rows remain opaque."},{"line":47,"title":"accepts any resume metadata object and refuses a payload that is not one","decision":"keep","reason":"Metadata requires an object but preserves unknown members for optional enrichment."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"344b2a35edf3c4bbfe75184e2e5ffb7cb23e9e2c53f10f89836abbf9f14a1e3a"}
{"path":"mobile/src/agent-history/agent-history-resume-target.test.ts","sha256":"01b940fcc5ab222b156e32758a8ee40f02fe3e0fa3b79ff169255d98b6184d7b","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain real local/SSH/WSL/runtime/folder resolution with candidate fallback and unavailable metadata.","evidence":["mobile/src/agent-history/agent-history-resume-target.ts","mobile/src/agent-history/agent-history-session-worktree.ts","mobile/src/agent-history/agent-history-resume-workspace-host.ts"],"cases":[{"line":80,"title":"resumes a WSL-stored session in an SSH workspace into that distro (#26818)","decision":"keep","reason":"WSL-stored transcript in SSH worktree is a named regression with real resolver output."},{"line":96,"title":"resumes a session scanned on the same SSH host in that host workspace","decision":"keep","reason":"SSH transcript can resume on its matching execution host."},{"line":110,"title":"blocks a session scanned on another SSH host","decision":"keep","reason":"Mismatched SSH host must be blocked before writes."},{"line":124,"title":"blocks a WSL Antigravity IDE transcript in an SSH workspace","decision":"keep","reason":"IDE-only WSL Antigravity transcript has a distinct inaccessible-host rule."},{"line":139,"title":"resumes a WSL-stored session in an SSH folder workspace","decision":"keep","reason":"WSL transcript compatibility also applies to folder-workspace ownership."},{"line":158,"title":"resolves project-scope rows to the matched session worktree before the route worktree","decision":"keep","reason":"Longest session workspace match takes precedence over route workspace."},{"line":177,"title":"falls back to the active route worktree when the session worktree is archived","decision":"keep","reason":"Archived match falls back to available route workspace."},{"line":200,"title":"blocks runtime targets when no supported fallback is available","decision":"keep","reason":"Runtime ownership with no supported candidate returns a user-facing block."},{"line":213,"title":"blocks SSH folder workspace targets for a host-local transcript","decision":"keep","reason":"Host-local transcript cannot use an SSH-owned folder."},{"line":233,"title":"skips an SSH session-worktree candidate in favor of a local active-worktree fallback","decision":"keep","reason":"Unsupported SSH session candidate must not hide supported local fallback."},{"line":256,"title":"blocks SSH session worktrees with the SSH message when no local fallback exists","decision":"keep","reason":"Without fallback the resolver must preserve the different-host message."},{"line":273,"title":"blocks folder workspaces whose candidate repos include a runtime owner","decision":"keep","reason":"Nested runtime-owned repo changes folder eligibility."},{"line":307,"title":"blocks folder workspaces when folder metadata is unavailable","decision":"keep","reason":"Missing folder metadata fails closed instead of assuming local."},{"line":329,"title":"blocks folder workspaces with mixed candidate repo hosts as unknown","decision":"keep","reason":"Mixed folder repo hosts produce unknown ownership and block."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"01b940fcc5ab222b156e32758a8ee40f02fe3e0fa3b79ff169255d98b6184d7b"}
{"path":"mobile/src/agent-history/agent-history-resume-writes.test.ts","sha256":"e56fdc8d984481ed07d93bdcf6da4821efe3dd233778432c9af1892711334fc5","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain genuine native/bridged transport parity plus independent expected verdicts for both writes, ambiguity and old-host preparation.","evidence":["mobile/src/session/ai-vault-resume-launch.ts","mobile/src/session/ai-vault-resume-preparation.ts","mobile/src/mobile-web-shell/bridge/bridge-port-pair-test-harness.ts"],"cases":[{"line":149,"title":"raises the host message on a refused create, on both transports","decision":"keep","reason":"Host create refusal crosses the real bridge and keeps its message."},{"line":176,"title":"keeps a lost reply ambiguous rather than reporting a failure the user can retry blindly","decision":"keep","reason":"Transport timeout ambiguity marker must survive the bridge and prevent blind retry."},{"line":198,"title":"leaves a write in flight when the shell goes ambiguous, not failed","decision":"keep","reason":"Disposing the actual bridge host during a sent create produces delivery-unknown."},{"line":209,"title":"raises the host message on a refused send, which no earlier case reached","decision":"keep","reason":"Reaches and refuses the second terminal.send write, independently of create refusal."},{"line":222,"title":"says the terminal is locked when the send is accepted and refuses in-band","decision":"keep","reason":"Accepted RPC envelope with locked input differs from RPC refusal."},{"line":235,"title":"resolves through both writes when the host takes them, on both transports","decision":"keep","reason":"Create and accepted send resolve on both transports; positive control reaches the second write."},{"line":248,"title":"resumes on the shared home when an older host cannot prepare, through the bridge too","decision":"keep","reason":"An older host missing preparation preserves legacy Codex home through the bridge."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"e56fdc8d984481ed07d93bdcf6da4821efe3dd233778432c9af1892711334fc5"}
{"path":"mobile/src/agent-history/agent-history-retry.test.tsx","sha256":"f4000975b384d3f20f5f391768473e180f0b4fbcebc8178c424f7dcfb0fd1d75","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain screen behavior for unreachable host, real reconnect handoff and host-owned catalog state.","evidence":["mobile/src/agent-history/use-mobile-agent-history-state.ts","mobile/src/agent-history/MobileAgentSessionHistoryPanel.tsx","mobile/src/transport/connection-retry-action.ts"],"cases":[{"line":116,"title":"is absent on the page, where nothing can re-dial","decision":"keep","reason":"Rendered waiting state has no page Retry when page cannot redial."},{"line":124,"title":"still renders natively and re-dials this host","decision":"keep","reason":"Rendered native Retry invokes reconnect for the current host."},{"line":134,"title":"loads again when the shell reconnects, which is what the missing Retry relies on","decision":"keep","reason":"Connected-client replacement actually triggers status read after page waited without Retry."},{"line":149,"title":"owns the history catalog across a client replacement for %s","decision":"keep","reason":"Same-host replacement preserves list while another-host replacement discards it; deferred catalog selects the new scope path."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"f4000975b384d3f20f5f391768473e180f0b4fbcebc8178c424f7dcfb0fd1d75"}
{"path":"mobile/src/agent-history/agent-history-scope-paths.test.ts","sha256":"c26a3f2be91dc92111d7bc691c686dc615d92cfd80a18e4aa434773a702e324d","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain scope/filter semantics, cross-host path equivalence and bounded normalization work.","evidence":["mobile/src/agent-history/agent-history-scope-paths.ts","src/shared/cross-platform-path.ts"],"cases":[{"line":19,"title":"workspace scope returns only the active worktree path","decision":"keep","reason":"Workspace scan sends only active workspace path."},{"line":25,"title":"project scope adds same-repo siblings but not other-repo worktrees","decision":"keep","reason":"Project scan includes same-repo siblings and excludes other repos."},{"line":32,"title":"all scope returns no scope hints (global recency list)","decision":"keep","reason":"All scope sends no narrowing hints."},{"line":36,"title":"returns no paths when there is no active worktree","decision":"keep","reason":"Absent active workspace must not invent scope paths."},{"line":40,"title":"caps project scope at 64 paths so the host RPC bound does not reject the request","decision":"keep","reason":"Large projects truncate at host RPC count bound while keeping active path first."},{"line":54,"title":"dedupes and skips non-absolute paths","decision":"keep","reason":"Duplicates and relative paths must not enter host scope hints."},{"line":62,"title":"retains first spelling and host-specific path equivalence","decision":"keep","reason":"Concrete Unicode/POSIX/Windows/UNC/WSL examples enforce host-specific equivalence and preserve first spelling."},{"line":95,"title":"normalizes each candidate once even with many duplicate siblings below the cap","decision":"keep","reason":"1,000 duplicate siblings prove linear normalization work below cap; assertions also check all distinct resulting paths."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"c26a3f2be91dc92111d7bc691c686dc615d92cfd80a18e4aa434773a702e324d"}
{"path":"mobile/src/agent-history/agent-history-session-card.test.ts","sha256":"1a34ec69169d029d83e8117c445ae49d9d6028f0cb96b6c228f939e198ab2f99","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain card/view-state and scope composition; remove assertion against a copied protocol literal that never compared host behavior.","evidence":["mobile/src/agent-history/agent-history-session-card.ts","mobile/src/agent-history/agent-history-sections.ts","mobile/src/agent-history/agent-history-current-worktree-badge.ts","mobile/src/agent-history/agent-history-current-host-capability.test.tsx"],"cases":[{"line":44,"title":"maps the card view-model with time-ago, label, and last message","decision":"keep","reason":"Composed display maps latest conversation, time, agent label and worktree membership."},{"line":53,"title":"omits the current-worktree badge when cwd is outside the active worktree","decision":"keep","reason":"Outside active path suppresses current-worktree marker."},{"line":58,"title":"falls back to a title for empty session titles","decision":"keep","reason":"Empty title receives visible fallback and null active path has no badge."},{"line":66,"title":"disables all resume buttons while one session is launching","decision":"keep","reason":"One launch disables all buttons while spinner belongs only to launched session."},{"line":75,"title":"keeps resume buttons enabled when no launch is in flight","decision":"keep","reason":"No launch leaves buttons available."},{"line":82,"title":"matches a nested cwd inside the worktree path","decision":"keep","reason":"Nested cwd belongs to active worktree."},{"line":88,"title":"is false without a cwd or active path","decision":"keep","reason":"Absent cwd or active path cannot imply membership."},{"line":95,"title":"hides empty sessions by default and groups remaining by folder","decision":"keep","reason":"Empty sessions are filtered while real conversation remains visible."},{"line":115,"title":"applies the search query across sections","decision":"keep","reason":"Search filters across section composition."},{"line":125,"title":"narrows Workspace scope to sessions whose cwd is inside the active worktree path","decision":"keep","reason":"Workspace scope narrows host union on the client."},{"line":140,"title":"narrows Project scope to the active worktree plus same-repo sibling paths","decision":"keep","reason":"Project scope includes siblings without foreign workspaces."},{"line":163,"title":"falls back to unnarrowed when a scoped tab has no scopeFilterPaths yet","decision":"keep","reason":"Loading scope metadata falls back to available sessions rather than false empty state."},{"line":183,"title":"shows all sessions under All scope regardless of path","decision":"keep","reason":"All scope must retain sessions outside active workspace."},{"line":205,"title":"stays in lockstep with the host AI_VAULT_RUNTIME_CAPABILITY string","decision":"remove","reason":"compares mobile constant only to a copied aiVault.v1 string; it never imports or exercises host negotiation. Actual status-to-resume compatibility remains covered."},{"line":213,"title":"hides the repeated current-worktree badge in Workspace scope","decision":"keep","reason":"Workspace-only view hides repetitive badge."},{"line":217,"title":"keeps the badge in mixed Project and All scopes","decision":"keep","reason":"Mixed scopes retain useful current-worktree badge."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"6934f06c8211bf509b2e0082a13bd045be6bb68a54166f41d3fe02201c415514"}
{"path":"mobile/src/agent-history/agent-history-session-worktree.test.ts","sha256":"a4c955347615c950da1354f940f1abed4b01044ddd81dae89ee36593a273bde5","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain matching, longest-path precedence, archive eligibility and WSL transcript mapping.","evidence":["mobile/src/agent-history/agent-history-session-worktree.ts"],"cases":[{"line":29,"title":"matches the active worktree when session cwd is inside it","decision":"keep","reason":"Nested active cwd resolves to current worktree."},{"line":38,"title":"prefers the longest active path match","decision":"keep","reason":"Longest workspace prefix wins over root prefix."},{"line":50,"title":"marks archived worktrees as unavailable for resume","decision":"keep","reason":"Archived match is identified and excluded from resume."},{"line":62,"title":"returns null when the session cwd has no active worktree match","decision":"keep","reason":"Unmatched session cannot acquire an arbitrary workspace."},{"line":72,"title":"matches WSL UNC worktree paths against Linux transcript paths","decision":"keep","reason":"Linux transcript path matches a WSL UNC workspace."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"a4c955347615c950da1354f940f1abed4b01044ddd81dae89ee36593a273bde5"}
{"path":"mobile/src/agent-history/worktree-navigation-actions.test.ts","sha256":"515bb23d9b1f2e15686cce01f4aa22ba904f7bde598658ceb0bdbff52294d3f2","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain action invocation destinations and capability/host gating, not merely an export inventory.","evidence":["mobile/src/agent-history/worktree-navigation-actions.ts","mobile/src/navigation/workspace-execution-host.ts"],"cases":[{"line":29,"title":"offers both screens on a host that advertises the vault","decision":"keep","reason":"Invokes actions and checks complete source-control/history destinations."},{"line":36,"title":"opens a server workspace on its server, and offers nothing when that server is unreachable","decision":"keep","reason":"Runtime server ownership reaches route and unreachable server offers no actionable destination."},{"line":58,"title":"offers only source control on a host that does not","decision":"keep","reason":"Older host has only supported source-control action."},{"line":78,"title":"encodes a host id that would otherwise stop being one segment","decision":"keep","reason":"Host IDs containing reserved characters remain one route segment."},{"line":85,"title":"encodes the worktree id the same way, which it already did","decision":"keep","reason":"Worktree IDs containing slash remain one route segment."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"515bb23d9b1f2e15686cce01f4aa22ba904f7bde598658ceb0bdbff52294d3f2"}
{"path":"mobile/src/app-update/app-update-checker.test.ts","sha256":"3e0f03fa75c0be36f55ee7325f3f62e926bc96a6029539d50956882477ad7639","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain deterministic fake-clock state-machine tests for cadence, persistence, joining, stop/restart and clock jumps.","evidence":["mobile/src/app-update/app-update-checker.ts"],"cases":[{"line":84,"title":"checks on cold start when it has never succeeded, and records the result","decision":"keep","reason":"Cold start checks once and persists actual successful result."},{"line":93,"title":"skips the cold-start check inside 24 h, then runs it when the day is up","decision":"keep","reason":"Loaded recent result survives until the exact daily timer boundary."},{"line":105,"title":"checks on foreground only once the last success is 24 h old","decision":"keep","reason":"Foreground only checks stale success independently of suspended timer."},{"line":119,"title":"retries a failed check after 1 h, not sooner, and keeps the last success time","decision":"keep","reason":"Failure retains last success and retries exactly one hour later."},{"line":136,"title":"bounds a check at 8 s and counts it as failed","decision":"keep","reason":"Unresolved source is bounded even if it ignores abort."},{"line":145,"title":"joins a manual check to one already in flight","decision":"keep","reason":"Two concurrent manual checks join one source request."},{"line":154,"title":"manual check reschedules the next automatic check a full day out","decision":"keep","reason":"Manual success cancels previous cadence and schedules a full day."},{"line":165,"title":"dismisses one version only; the next release offers again","decision":"keep","reason":"Dismissal affects one version and is persisted while next release reappears."},{"line":184,"title":"keeps a stored dismissal across restarts","decision":"keep","reason":"Stored version dismissal survives restart."},{"line":193,"title":"stops offering a stored update once the binary has been updated to it","decision":"keep","reason":"Installed version suppresses stale persisted update."},{"line":203,"title":"stops scheduling after stop()","decision":"keep","reason":"Stop cancels scheduled work across multiple intervals."},{"line":212,"title":"keeps the 1 h retry for a manual check that fails while the store is still loading","decision":"keep","reason":"Concurrent preference load cannot erase failure retry scheduling."},{"line":231,"title":"applies one load and runs one check across a StrictMode-style double start","decision":"keep","reason":"StrictMode double-start shares preference load and check."},{"line":248,"title":"arms no timer for a check that finishes after stop()","decision":"keep","reason":"Late source completion after stop must not recreate timer."},{"line":261,"title":"schedules the next check when a check in flight across stop and restart completes","decision":"keep","reason":"Restart while source is pending must still schedule after it resolves."},{"line":278,"title":"runs the check when the armed timer fires even if the clock stepped back","decision":"keep","reason":"Backward wall-clock jump cannot defer already-armed timer."},{"line":287,"title":"treats a stored check time ahead of the clock as never checked","decision":"keep","reason":"Future persisted clock is treated as never checked rather than postponing for a year."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"3e0f03fa75c0be36f55ee7325f3f62e926bc96a6029539d50956882477ad7639"}
{"path":"mobile/src/app-update/app-update-sources.test.ts","sha256":"28492a77e1430ca9a71706dd88c8b24d6768adb7cf19534873166f08626b173a","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain real recorded endpoint parsing and mocked HTTP boundaries with request routing, unpublished fallback and failure classification.","evidence":["mobile/src/app-update/app-update-source.ts","mobile/src/app-update/github-release-update-source.ts","mobile/src/app-update/app-store-update-source.ts","mobile/src/app-update/fixtures/github-mobile-android-tag-refs.json"],"cases":[{"line":41,"title":"orders plain versions numerically and ignores prerelease or malformed candidates","decision":"keep","reason":"Numerical version compare rejects old/equal/prerelease/malformed releases."},{"line":52,"title":"reads every mobile-android tag from the recorded refs","decision":"keep","reason":"Live-recorded refs fixture demonstrates filtering with known present/missing versions."},{"line":59,"title":"accepts the recorded release only because it is published with an APK","decision":"keep","reason":"Recorded release requires publication and APK rather than tag existence alone."},{"line":67,"title":"offers the newest tag above the installed version once its release is proven","decision":"keep","reason":"Newest validated mobile tag is offered through actual endpoint selection."},{"line":82,"title":"falls back past a tag whose build never published a release","decision":"keep","reason":"Unpublished newest tag falls back to older installable release."},{"line":98,"title":"probes at most three candidates and then reports current","decision":"keep","reason":"Three-probe limit bounds network work across unpublished tags."},{"line":113,"title":"makes no release probe when nothing is newer","decision":"keep","reason":"Current binary performs no release probe."},{"line":123,"title":"opens the release page GitHub reports, not one built from the tag","decision":"keep","reason":"URL comes from release response rather than reconstructing potentially renamed tag page."},{"line":141,"title":"rejects on a release probe 5xx instead of moving to an older candidate","decision":"keep","reason":"5xx stops fallback and marks check failed."},{"line":152,"title":"rejects when a release probe cannot reach GitHub","decision":"keep","reason":"Network rejection remains failed rather than current."},{"line":159,"title":"rejects when GitHub refuses, so the check counts as failed rather than current","decision":"keep","reason":"Tag endpoint rejection remains failed rather than current."},{"line":170,"title":"reads the store version and App Store page from the recorded lookup","decision":"keep","reason":"Recorded iTunes listing yields actual version/page."},{"line":177,"title":"offers the store version only when it is newer than the installed one","decision":"keep","reason":"Store only offers newer version."},{"line":188,"title":"stays quiet when the lookup has no listing for this bundle id","decision":"keep","reason":"No bundle listing returns current without crashing."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"28492a77e1430ca9a71706dd88c8b24d6768adb7cf19534873166f08626b173a"}
{"path":"mobile/src/app-update/use-wall-app-update.test.ts","sha256":"bcbac54820e70ad618e2874c96a39f0d7519ac615811b18f5709ef3892bbf689","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain distinct wall behavior: dismissal is intentionally ignored and page form exposes no app checker.","evidence":["mobile/src/app-update/use-wall-app-update.ts","mobile/src/app-update/use-wall-app-update.web.ts"],"cases":[{"line":18,"title":"offers a release dismissed on home, because it is the way past the wall","decision":"keep","reason":"Dismissed update remains available as the wall escape route."},{"line":28,"title":"offers nothing when no release is known","decision":"keep","reason":"Unknown release yields no offered update."},{"line":40,"title":"offers nothing","decision":"keep","reason":"Page-specific seam never offers native app update state."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"bcbac54820e70ad618e2874c96a39f0d7519ac615811b18f5709ef3892bbf689"}
{"path":"mobile/src/browser/MobileBrowserAddressField.test.tsx","sha256":"d2191931a80bcd5d61264d83f80deb05fe67613154125494f8211d44b968716d","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain rendered input keyboard props across web/iOS/Android; native enum and browser inputMode precedence differ.","evidence":["mobile/src/browser/MobileBrowserAddressField.tsx"],"cases":[{"line":58,"title":"asks for the URL keyboard through inputMode on the web","decision":"keep","reason":"Actual address component chooses browser URL inputMode."},{"line":64,"title":"leaves inputMode unset on both native platforms","decision":"keep","reason":"Actual native address component avoids overriding iOS URL or Android default keyboard."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"d2191931a80bcd5d61264d83f80deb05fe67613154125494f8211d44b968716d"}
{"path":"mobile/src/browser/browser-address-field-styles.web.test.ts","sha256":"5dff027883f4c17f0380d7a74268d67dd42c4c82dd0d1352b15e69c76fe5d737","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain iOS focus-zoom floor and label alignment; remove historical native pixel pins and spread-implementation parity.","evidence":["mobile/src/browser/browser-address-field-styles.web.ts","mobile/src/browser/browser-address-field-styles.ts","mobile/src/browser/browser-address-field-base-styles.ts","mobile/src/browser/mobile-browser-pane-styles.ts"],"cases":[{"line":27,"title":"raises the address field to the seam, above the focus-zoom floor","decision":"keep","reason":"Web address font reaches platform seam and 16px focus-zoom floor."},{"line":36,"title":"raises the key row input to the same seam","decision":"keep","reason":"Keyboard row also reaches focus-zoom floor."},{"line":43,"title":"keeps the overlaid label on the input size, so focus does not resize the address","decision":"keep","reason":"Overlaid label matches input metrics so focus does not jump address size."},{"line":52,"title":"gives the raised size a line box it fits in","decision":"keep","reason":"Line box accommodates raised glyph size."},{"line":60,"title":"renders at exactly the size it did before the split","decision":"remove","reason":"freezes previous native pixel values and theme literal rather than a behavioral requirement."},{"line":70,"title":"differs from the web style in nothing but the size","decision":"remove","reason":"pins private base-style omissions and object spread composition; semantic zoom/label checks remain."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"37819a9c37de53e331c9edd8448def97c0bdcefb06c749432d78cd6b09988327"}
{"path":"mobile/src/browser/browser-command-reply-schema.test.ts","sha256":"2b8e1b3c80416cc32d90542635e2077120443efdfa2d148207fc439b8db4f747","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain settled-URL salvage and nullish compatibility; remove a test that only asks Zod unknown to accept values.","evidence":["mobile/src/browser/browser-command-reply-schema.ts","mobile/src/browser/MobileBrowserPane.tsx"],"cases":[{"line":21,"title":"reads the settled URL the address bar takes","decision":"keep","reason":"Valid settled URL is preserved for address synchronization."},{"line":27,"title":"leaves the address bar alone for a reply that names no URL","decision":"keep","reason":"Omitted/malformed URL leaves address unchanged rather than rejecting envelope."},{"line":32,"title":"leaves the address bar alone for a null or absent result, as the pane always did","decision":"keep","reason":"Null/absent results preserve older-host behavior."},{"line":39,"title":"take every reply shape","decision":"remove","reason":"z.unknown acceptance tests the schema library directly with no product decision, reader or command invocation."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"1ae23cf81e85920de7286ce57e5489dea50f67ae55fe94cedc588a46a9612160"}
{"path":"mobile/src/browser/browser-frame-data-uri.web.test.ts","sha256":"339141a91ab0d048c552db6e44e2310b3e1bc5a331ebad1fa0a6e01b91dd79a8","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain bytes/fallback and independent conflicting-input assertion proving carried base64 is used; remove flaky comparative wall-clock microbenchmark.","evidence":["mobile/src/browser/browser-frame-data-uri.web.ts","mobile/src/browser/browser-frame-data-uri.ts"],"cases":[{"line":48,"title":"is byte-identical on both platforms for the frame the bridge decoded","decision":"keep","reason":"Actual native/web encoders agree on a realistic full frame."},{"line":59,"title":"uses the base64 the bridge carried rather than the bytes beside it","decision":"keep","reason":"Conflicting bytes/base64 independently detect unnecessary re-encoding without timing noise."},{"line":65,"title":"still paints a frame that carried no base64, which is every native frame","decision":"keep","reason":"Absent carried base64 uses native-compatible encoding."},{"line":73,"title":"costs less per frame than the re-encode it replaces","decision":"remove","reason":"benchmark depends on JIT/runner timing and duplicates deterministic carried-base64 assertion for the exact regression."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"08ddcccdbfdc64d05841ca3122a45ed6c2f3429c8e62e0bc6b93f51e08fbd4d8"}
{"path":"mobile/src/browser/browser-frame-layer-paint.web.test.ts","sha256":"5152ff6740157ffd9c2a1317f26fda7e08a0db194fadeb9fcc8a05e12ddd842f","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain actual DOM writes, real react-native-web structure, async decode/rejection and accessibility render behavior; remove wrong-platform misuse and no-op tests.","evidence":["mobile/src/browser/browser-frame-layer-paint.web.ts","mobile/src/browser/browser-frame-layer-paint.ts","mobile/src/browser/browser-frame-pacer.web.test.ts"],"cases":[{"line":44,"title":"throws from updateBrowserImageSource, because a DOM node has no setNativeProps","decision":"remove","reason":"deliberately calls native API with DOM node only to assert a TypeError; real RN Web paint contract is retained."},{"line":50,"title":"throws from updateBrowserLayerVisibility for the same reason","decision":"remove","reason":"repeats same wrong-platform TypeError for visibility; real opacity writes retained."},{"line":56,"title":"reports no decode of its own: the rendered Image fires onLoad natively","decision":"remove","reason":"asserts intentionally empty native decoder function does nothing; actual native-load handling remains in pacer cases."},{"line":64,"title":"paints the frame as a background-image on the element RN Web sizes","decision":"keep","reason":"Web paint writes the actual RN Web background surface and preserves host layout style."},{"line":78,"title":"flips the double buffer with one opacity write per layer","decision":"keep","reason":"Double-buffer visibility updates both DOM opacities."},{"line":86,"title":"tolerates a layer that has unmounted between the frame and the write","decision":"keep","reason":"Unmounted nullable refs do not throw during streamed writes."},{"line":91,"title":"reports a frame displayable only once it has decoded","decision":"keep","reason":"Decode callback must wait for asynchronous decode."},{"line":103,"title":"reports a frame that cannot decode, so the pending layer is not stranded","decision":"keep","reason":"Corrupt-frame rejection releases pending-layer failure path."},{"line":192,"title":"writes the frame onto the element react-native-web paints it on","decision":"keep","reason":"Uses real installed RN Web Image DOM, catching dependency structure changes hidden by synthetic fixture."},{"line":218,"title":"leaves the accessibility image alone when a frame is written imperatively","decision":"keep","reason":"Imperative write preserves accessibility image until render."},{"line":236,"title":"moves the accessibility image on the next render from the pane state","decision":"keep","reason":"Subsequent real React render refreshes accessibility image source."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"dc06cee063a117ce9dd27cdaec981fcbe47b9d74bad3881cf53fda994a48a76a"}
{"path":"mobile/src/browser/browser-frame-pacer.web.test.ts","sha256":"4ea9b5382113a77f5f0124808e70c0472b9189c7061163dfb1a3bbf3d86d676f","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain deterministic DOM-level pacing and decode state-machine behavior; every case drives actual production pacer and paint siblings.","evidence":["mobile/src/browser/browser-frame-pacer.ts","mobile/src/browser/browser-frame-layer-paint.web.ts"],"cases":[{"line":143,"title":"paints the first frame on both layers and shows it without a flip","decision":"keep","reason":"First frame initializes both layers and reports one visible frame."},{"line":154,"title":"decodes the next frame offscreen and shows it only once it has decoded","decision":"keep","reason":"Hidden decode precedes visible-layer flip."},{"line":169,"title":"keeps flipping frame after frame, reporting each once and publishing one source","decision":"keep","reason":"Sustained frames continue flipping and publish one initial React source."},{"line":180,"title":"paints at most one frame per interval","decision":"keep","reason":"Interval bounds paint cadence."},{"line":194,"title":"never re-points a decoding layer, and shows the newest frame once it settles","decision":"keep","reason":"Decoding layer cannot be repointed; queue retains newest frame."},{"line":213,"title":"shows a slow last frame when nothing newer waits","decision":"keep","reason":"Slow final frame remains eligible when queue is empty."},{"line":225,"title":"flips straight to a layer that already holds the frame, which reloads nothing","decision":"keep","reason":"Already-decoded cached source flips without a duplicate decode."},{"line":239,"title":"skips a frame sent again after it failed to decode, and keeps streaming","decision":"keep","reason":"Failed repeated source is skipped while later good frames stream."},{"line":255,"title":"hands the layer of an undecodable frame to the newest waiting frame","decision":"keep","reason":"Failed decode hands layer to queued newest frame."},{"line":269,"title":"settles a layer on a native load only for the source it holds","decision":"keep","reason":"Native load only settles the matching source, not a retired source."},{"line":290,"title":"keeps streaming after a layer remounts its Image","decision":"keep","reason":"Remounted Image recovers stream ownership and continues frames."},{"line":305,"title":"does not flip a decode that settles after a reset, but flips to it when it is sent again","decision":"keep","reason":"Reset invalidates queued flip while later repeat can reuse decoded layer."},{"line":319,"title":"holds a frame sent again after a reset until the earlier decode of it settles","decision":"keep","reason":"Repeat after reset waits for outstanding decode before flip."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"4ea9b5382113a77f5f0124808e70c0472b9189c7061163dfb1a3bbf3d86d676f"}
{"path":"mobile/src/browser/browser-screencast-budget-at-the-shell.test.ts","sha256":"75e73a6ea2c06a21a60773d3fd478055f0ae7c507ef129ae8952eb8f1429a90d","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain real encoder/bridge serialization and cap-drop integration. This independently checks the envelope/budget whose copied math tests were removed.","evidence":["mobile/src/browser/browser-screencast-request.web.ts","mobile/src/mobile-web-shell/bridge-host-subscriptions.ts","mobile/src/mobile-web-shell/bridge-host-test-harness.ts","config/scripts/mobile-web-app/mobile-web-app-frame-budget-sweep.test.ts"],"cases":[{"line":114,"title":"holds above what the shell spends on everything but the image","decision":"keep","reason":"Envelope bound exceeds serialized frame emitted by actual host stream."},{"line":120,"title":"is the same cost whatever the image is, which is what makes it an envelope","decision":"keep","reason":"Real shell envelope cost is independent of image length."},{"line":134,"title":"is exactly what this frame costs once every number is widened to a double","decision":"keep","reason":"Reconstructs real serialized envelope with maximal numeric widths; detects added fields or incorrect metadata budget."},{"line":145,"title":"covers every metadata field the protocol declares, not the ones this case sends","decision":"keep","reason":"Fixture must include all protocol metadata fields for the preceding integration check to cover the advertised bound."},{"line":168,"title":"encodes under the frame cap and the shell posts it","decision":"keep","reason":"Budget-derived bytes pass real encoding and host posting under cap."},{"line":175,"title":"is tight: the budget spends nearly the whole cap","decision":"keep","reason":"Real posted-frame size verifies budget is useful/tight, not merely conservative."},{"line":183,"title":"is a ceiling: an image past it is dropped by the shell, not sent over the cap","decision":"keep","reason":"Actual host drops over-budget encoded frame instead of exceeding wire cap."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"75e73a6ea2c06a21a60773d3fd478055f0ae7c507ef129ae8952eb8f1429a90d"}
{"path":"mobile/src/browser/browser-screencast-request.test.ts","sha256":"0abf505b25d66a301c434fa28fdc9af58f782644a32fc96a763927c8cc8b121c","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain public request behavior for desktop/mobile modes and measured viewport/frame limits.","evidence":["mobile/src/browser/browser-screencast-request.ts","mobile/src/browser/browser-screencast-request-parameters.ts"],"cases":[{"line":6,"title":"defaults to web view without changing the remote browser viewport","decision":"keep","reason":"Native default request preserves remote desktop viewport while bounding frame size."},{"line":22,"title":"requests mobile view with phone-sized viewport emulation","decision":"keep","reason":"Mobile request enables phone emulation with actual layout dimensions."},{"line":35,"title":"caps large phone layouts to the stream frame budget","decision":"keep","reason":"Large phone frame dimensions respect stream ceilings."},{"line":42,"title":"waits for a measured browser pane before subscribing","decision":"keep","reason":"Absent/zero layout delays subscription."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"0abf505b25d66a301c434fa28fdc9af58f782644a32fc96a763927c8cc8b121c"}
{"path":"mobile/src/browser/browser-screencast-request.web.test.ts","sha256":"eaf3818638d6f7c3dce2f41867915667e72a81c4e7482fb2abd9529c9e22a5eb","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Remove copied budget/envelope formulas and 200,000-sample JSON-number sweep; preserve actual shell serialization, density bounds, base64 padding and hot-path work guards.","evidence":["mobile/src/browser/browser-screencast-request.web.ts","mobile/src/browser/browser-screencast-budget-at-the-shell.test.ts","config/scripts/mobile-web-app/mobile-web-app-frame-budget-sweep.test.ts"],"cases":[{"line":39,"title":"is needed: the native request overflows the frame cap on a phone","decision":"keep","reason":"Native request genuinely exceeds bridge budget for reference phone; establishes need for web adjustment."},{"line":48,"title":"keeps the worst case frame inside one bridge message","decision":"keep","reason":"Web phone density yields bytes within declared wire cap."},{"line":58,"title":"asks for the scale the cap and the worst case together allow","decision":"fix","reason":"replace repeated sqrt formula with next-hundredth overflow assertion; catches under-requesting density without copying implementation."},{"line":68,"title":"leaves web mode byte-identical to the native request","decision":"keep","reason":"Web desktop/default requests agree with actual native builder."},{"line":75,"title":"never asks for more density than native, on a viewport the budget does not bind","decision":"keep","reason":"Nonbinding small viewport retains native density."},{"line":79,"title":"stops at one device pixel per CSS pixel on a viewport no scale would fit","decision":"keep","reason":"Impossible viewport clamps to one pixel per CSS pixel."},{"line":85,"title":"answers the native factor when there is no layout to budget against","decision":"keep","reason":"Absent/zero layout preserves native fallback without subscription."},{"line":93,"title":"spends the whole cap that the envelope leaves","decision":"remove","reason":"reimplements exact area-floor formula; real shell posted-frame cap and tightness tests independently cover result."},{"line":114,"title":"counts base64 padding, which the ratio does not for every image size","decision":"fix","reason":"retain exact 3001-byte -> 4004-char padding example; remove assertions about Math arithmetic/ratio supplied by the test."},{"line":122,"title":"leaves a frame of exactly the budgeted area inside the cap, padding included","decision":"remove","reason":"copied frame-size arithmetic duplicates real shell cap assertion."},{"line":128,"title":"measures the envelope rather than naming a number, and leaves it room to grow","decision":"remove","reason":"arbitrary envelope 200-byte/1% bounds are weaker than retained real serialization/tightness checks."},{"line":163,"title":"is at least what a real frame event costs at its widest","decision":"remove","reason":"hand-built widest envelope duplicates production skeleton; independent real host serializer check remains."},{"line":169,"title":"is a bound on every finite double, not on the one it was written with","decision":"remove","reason":"200,000 samples characterize JavaScript JSON formatting then assert same copied skeleton, without producing a different product oracle."},{"line":201,"title":"is not loose: it is within a metadata field of what that costs","decision":"remove","reason":"copied envelope slack comparison duplicates exact independently serialized shell reconstruction."},{"line":212,"title":"`skips discarded mobile density work in ${mode ?? 'default'} view`","decision":"keep","reason":"actual default/web outputs preserve native route and skip density/envelope work; remove redundant keys/stringified-output assertions after deep equality."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"778eec88b112f1dcface05e6279e9b45c748cad4e1899dfbecf8dc4f1f6e1c83"}
{"path":"mobile/src/browser/browser-touch-geometry.test.ts","sha256":"ff71b581c4a9c28b8cd0f7ed0f8b54aadb31ccb9529718d68bc6054f2240b7de","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain concrete recorded Chromium coordinate behavior, zoom inversion, radius/scroll conversion and invalid input boundaries.","evidence":["mobile/src/browser/browser-touch-geometry.ts"],"cases":[{"line":34,"title":"maps the visual center of a letterboxed desktop frame to the browser center","decision":"keep","reason":"Letterboxing maps visual center to expected source center."},{"line":56,"title":"inverts pan and zoom around the rendered frame center","decision":"keep","reason":"Forward screen projection is inverse of public map under nontrivial pan/zoom."},{"line":78,"title":"maps a tap on a page with no viewport meta into the page CSS pixels","decision":"keep","reason":"Recorded no-viewport-meta scale lands inside independently measured button bounds."},{"line":95,"title":"divides the frame device space by the page scale and adds no scroll offset","decision":"keep","reason":"Page scale converts device to CSS coordinates without adding scroll offset."},{"line":108,"title":"leaves web view mode where it was, at a page scale of one","decision":"keep","reason":"Unit page scale keeps existing desktop coordinates."},{"line":115,"title":"reads a missing or unusable page scale as one rather than dividing by it","decision":"keep","reason":"Invalid page scales fall back safely."},{"line":130,"title":"grows the touch radius by the page scale, because a CSS pixel is smaller","decision":"keep","reason":"Touch radius scales with actual CSS pixel size."},{"line":140,"title":"scrolls the page by the page scale, not by the frame fit alone","decision":"keep","reason":"Recorded phone scale converts screen flick into correct page scroll."},{"line":154,"title":"leaves a web view scroll and a pinched one where they were","decision":"keep","reason":"Unit/pinched scroll and absent geometry retain expected deltas."},{"line":168,"title":"rejects page-level touch coordinates instead of mixing coordinate spaces","decision":"keep","reason":"Page-level coordinates are rejected rather than silently mixing spaces."},{"line":172,"title":"clamps zoom offsets after the viewport geometry changes","decision":"keep","reason":"Viewport change bounds pan offsets without changing permitted zoom."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"ff71b581c4a9c28b8cd0f7ed0f8b54aadb31ccb9529718d68bc6054f2240b7de"}
{"path":"mobile/src/browser/browser-url.test.ts","sha256":"5d561ec40f2a081aa7cdfc067dd772461310d5c8d37f4a1b82d194a40c84b67a","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain public localhost/domain and file-address formatting behavior including Windows file URLs.","evidence":["mobile/src/browser/browser-url.ts"],"cases":[{"line":6,"title":"keeps localhost-style addresses as http URLs","decision":"keep","reason":"Local addresses use HTTP instead of interpreting port as scheme."},{"line":13,"title":"adds https for regular domains without a scheme","decision":"keep","reason":"Ordinary scheme-less domain uses HTTPS."},{"line":19,"title":"shows the decoded filename for local and Windows-style file URLs","decision":"keep","reason":"Native and Windows file URLs show decoded filename."},{"line":28,"title":"does not compact ordinary or malformed URLs","decision":"keep","reason":"Ordinary/malformed URL is not mislabeled as local file."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"5d561ec40f2a081aa7cdfc067dd772461310d5c8d37f4a1b82d194a40c84b67a"}
{"path":"mobile/src/browser/mobile-browser-address-sync.test.ts","sha256":"12f0ff9b3b024c9452c99f79f36c86923734e86d2174912389eff46339402d0a","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain focused/unfocused state transitions that protect edits while applying current remote URL.","evidence":["mobile/src/browser/mobile-browser-address-sync.ts","mobile/src/browser/MobileBrowserPane.tsx"],"cases":[{"line":6,"title":"syncs the tab URL when the input is not focused","decision":"keep","reason":"Unfocused changed remote URL synchronizes text."},{"line":18,"title":"defers tab URL sync while the user is editing","decision":"keep","reason":"Focus defers remote URL text overwrite."},{"line":30,"title":"syncs the latest tab URL when editing ends","decision":"keep","reason":"Blur applies latest URL even if URL itself is unchanged."},{"line":42,"title":"preserves externally updated address text when focus and tab URL are unchanged","decision":"keep","reason":"Unchanged focus/URL does not overwrite text produced by stream navigation."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"12f0ff9b3b024c9452c99f79f36c86923734e86d2174912389eff46339402d0a"}
{"path":"mobile/src/browser/mobile-browser-app-resume.test.tsx","sha256":"6c08813db46e8b9876641f0ec9a7fa9979b990b37dfc73aca7cd8bb5fdcf7625","reviewer":"audit_mobile","status":"reviewed","decision":"keep","rationale":"Retain actual mounted stream disposal/recreation across background and batched foreground transitions.","evidence":["mobile/src/browser/MobileBrowserPane.tsx","mobile/src/browser/use-mobile-browser-stream.ts"],"cases":[{"line":129,"title":"stops the stream in the background and starts a new one on return","decision":"keep","reason":"Background disposes subscription and foreground starts exactly one replacement."},{"line":141,"title":"starts a new stream when the leave and the return land in one render","decision":"keep","reason":"Batched leave/return still changes visit generation and disposes original stream."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"6c08813db46e8b9876641f0ec9a7fa9979b990b37dfc73aca7cd8bb5fdcf7625"}
{"path":"mobile/src/browser/mobile-browser-dialog-through-the-bridge.test.tsx","sha256":"4c413abc1e502d242efdfa67e44385bd3ec8591137fab21e3a04901e72998e9a","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Fix host fake so confirm outcome derives from the actual emitted RPC method, not an explicit test-supplied accept boolean; retain dialog lifecycle/race cases.","evidence":["mobile/src/browser/use-mobile-browser-commands.ts","mobile/src/browser/MobileBrowserPane.tsx","mobile/src/mobile-web-shell/bridge/bridge-port-pair-test-harness.ts"],"cases":[{"line":174,"title":"raises the second dialog and answers the confirm with OK","decision":"fix","reason":"host now accepts/dismisses according to observed RPC method, so OK outcome detects a misrouted command; still verifies delayed close and sequential dialogs."},{"line":201,"title":"keeps the card pressable and says so when the answer does not reach the page","decision":"keep","reason":"Host rejection keeps dialog and visible error, then retry advances via real bridged request."},{"line":229,"title":"kills the card buttons while an answer is in flight, so a double tap sends one","decision":"fix","reason":"title now states actual disabled-button assertions rather than claiming a double tap that setup never performed."},{"line":251,"title":"stamps the failure on the dialog that was answered, never on the one that replaced it","decision":"keep","reason":"Deferred failure from retired dialog must not poison replacement dialog."},{"line":283,"title":"answers the confirm with Cancel","decision":"fix","reason":"Cancel result comes from actual browser.dialogDismiss method instead of explicit false supplied to fake; retains frame/visible-close behavior."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"045d0e262c27480efc8cf5ad946f5df062ed5a129f41e9d227bade6ece84a308"}
{"path":"mobile/src/browser/mobile-browser-frameless-stream.test.tsx","sha256":"94117947933596f19427cad02192c05a319f725a645c3d55cb8597b3ea792345","reviewer":"audit_mobile","status":"reviewed","decision":"change","rationale":"Retain ready-without-pixels and binary-grant behavior; consolidate redundant granted-stream positive control into real-pixels case.","evidence":["mobile/src/browser/use-mobile-browser-stream.ts","mobile/src/browser/MobileBrowserPane.tsx"],"cases":[{"line":153,"title":"keeps showing the loading indicator instead of an empty black pane","decision":"keep","reason":"Ready event without image bytes cannot clear loading indicator."},{"line":163,"title":"clears the indicator once real pixels arrive","decision":"keep","reason":"actual binary callback clears spinner and renders bytes; now also asserts no missing-grant error as positive control."},{"line":185,"title":"never subscribes, and says so with the stream-error state it already has","decision":"keep","reason":"Missing grant suppresses subscription and renders explicit upgrade guidance."},{"line":197,"title":"subscribes when the shell names it, which is the control for the case above","decision":"consolidate","reason":"real-pixels case already requires granted subscription; move no-upgrade-error assertion there instead of another identical mount."}],"source_commit":"6d473f4bea1d190cb3649af97e04f1cdde170c5f","reviewed_sha256":"de5555b60c42a6945005a660f4af07eee3fad145c65d65cb6d23e29d51233b64"}
@@ -138,10 +138,17 @@ afterEach(() => {
describe("the agent-history panel's Back button inside the shell's page", () => {
it('hands the pop to the shell, because this document has nowhere to go back to', async () => {
const mounted = await mountPanel()
expect(mounted.pair.client.getShellSession()).not.toBeNull()
expect(postedNames(mounted.pair)).toEqual([])
pressBack(mounted.tree)
await mounted.pair.flush()
expect(postedNames(mounted.pair)).toContain(BRIDGE_NAVIGATE_BACK_NOTIFY)
const frames = mounted.pair.toShell
.map((json) => JSON.parse(json))
.filter((frame: { name?: string }) => frame.name === BRIDGE_NAVIGATE_BACK_NOTIFY)
expect(frames).toEqual([
{ v: BRIDGE_PROTOCOL_VERSION, type: 'notify', name: BRIDGE_NAVIGATE_BACK_NOTIFY }
])
expect(router.back).not.toHaveBeenCalled()
})
@@ -154,27 +161,4 @@ describe("the agent-history panel's Back button inside the shell's page", () =>
expect(postedNames(mounted.pair)).not.toContain(BRIDGE_NAVIGATE_BACK_NOTIFY)
expect(router.back).toHaveBeenCalled()
})
it('reaches the shell over the same channel the rest of the screen uses', async () => {
// The presence precondition for the absence above: a panel that never mounted, or a chevron
// whose handler never ran, would post nothing either and read as the local pop.
const mounted = await mountPanel()
expect(mounted.pair.client.getShellSession()).not.toBeNull()
expect(postedNames(mounted.pair)).toEqual([])
pressBack(mounted.tree)
await mounted.pair.flush()
expect(postedNames(mounted.pair).length).toBeGreaterThan(0)
})
it('carries the protocol version every other frame carries', async () => {
const mounted = await mountPanel()
pressBack(mounted.tree)
await mounted.pair.flush()
const frames = mounted.pair.toShell
.map((json) => JSON.parse(json))
.filter((frame: { name?: string }) => frame.name === BRIDGE_NAVIGATE_BACK_NOTIFY)
expect(frames).toEqual([
{ v: BRIDGE_PROTOCOL_VERSION, type: 'notify', name: BRIDGE_NAVIGATE_BACK_NOTIFY }
])
})
})
@@ -6,7 +6,6 @@ import {
isSessionInActiveWorktree
} from './agent-history-session-card'
import { buildMobileAgentHistorySections } from './agent-history-sections'
import { MOBILE_AI_VAULT_CAPABILITY } from './agent-history-capability'
import { shouldShowMobileCurrentWorktreeBadge } from './agent-history-current-worktree-badge'
const NOW = Date.parse('2026-06-29T00:00:00.000Z')
@@ -201,14 +200,6 @@ describe('buildMobileAgentHistorySections', () => {
})
})
describe('MOBILE_AI_VAULT_CAPABILITY', () => {
it('stays in lockstep with the host AI_VAULT_RUNTIME_CAPABILITY string', () => {
// Why: mobile's protocol-version copy lacks the host constant; this literal
// must match src/shared/protocol-version.ts AI_VAULT_RUNTIME_CAPABILITY.
expect(MOBILE_AI_VAULT_CAPABILITY).toBe('aiVault.v1')
})
})
describe('shouldShowMobileCurrentWorktreeBadge', () => {
it('hides the repeated current-worktree badge in Workspace scope', () => {
expect(shouldShowMobileCurrentWorktreeBadge('workspace')).toBe(false)
@@ -15,8 +15,6 @@ vi.mock(
import { TEXT_INPUT_FONT_SIZE } from '../platform/text-input-font-size'
import { typography } from '../theme/mobile-theme'
import { browserAddressFieldBase } from './browser-address-field-base-styles'
import { browserAddressFieldStyles } from './browser-address-field-styles'
import { browserAddressFieldStyles as browserAddressFieldStylesOnWeb } from './browser-address-field-styles.web'
import { mobileBrowserPaneStyles } from './mobile-browser-pane-styles'
@@ -55,26 +53,3 @@ describe('the browser pane text inputs on the web', () => {
)
})
})
describe('the browser address field natively', () => {
it('renders at exactly the size it did before the split', () => {
expect(browserAddressFieldStyles.input.fontSize).toBe(typography.metaSize)
expect(browserAddressFieldStyles.input.fontSize).toBe(12)
expect(browserAddressFieldStyles.input.lineHeight).toBe(16)
expect(browserAddressFieldStyles.fileLabel.fontSize).toBe(12)
expect(browserAddressFieldStyles.fileLabel.lineHeight).toBe(16)
})
// The split is one value, not a second style: everything the siblings do not differ on comes from
// the same object, so a padding or a colour cannot drift between the platforms.
it('differs from the web style in nothing but the size', () => {
expect(browserAddressFieldBase.input).not.toHaveProperty('fontSize')
expect(browserAddressFieldBase.input).not.toHaveProperty('lineHeight')
expect(browserAddressFieldStyles.input).toMatchObject(browserAddressFieldBase.input)
expect(browserAddressFieldStylesOnWeb.input).toMatchObject(browserAddressFieldBase.input)
expect(browserAddressFieldStyles.fileLabel).toMatchObject(browserAddressFieldBase.fileLabel)
expect(browserAddressFieldStylesOnWeb.fileLabel).toMatchObject(
browserAddressFieldBase.fileLabel
)
})
})
@@ -1,9 +1,6 @@
import { describe, expect, it } from 'vitest'
import type { z } from 'zod'
import {
browserCommandUnreadReplySchema,
browserNavigationSettledSchema
} from './browser-command-reply-schema'
import { browserNavigationSettledSchema } from './browser-command-reply-schema'
function reads<T>(schema: z.ZodType<T, unknown>, value: unknown): T {
const parsed = schema.safeParse(value)
@@ -13,10 +10,6 @@ function reads<T>(schema: z.ZodType<T, unknown>, value: unknown): T {
return parsed.data
}
function refuses(schema: z.ZodType<unknown, unknown>, value: unknown): boolean {
return !schema.safeParse(value).success
}
describe('a navigation reports the URL it settled on', () => {
it('reads the settled URL the address bar takes', () => {
expect(reads(browserNavigationSettledSchema, { url: 'https://example.test/' }).url).toBe(
@@ -34,11 +27,3 @@ describe('a navigation reports the URL it settled on', () => {
expect(reads(browserNavigationSettledSchema, undefined)).toBe(undefined)
})
})
describe('the twelve commands whose body nothing reads', () => {
it('take every reply shape', () => {
expect(refuses(browserCommandUnreadReplySchema, undefined)).toBe(false)
expect(refuses(browserCommandUnreadReplySchema, null)).toBe(false)
expect(refuses(browserCommandUnreadReplySchema, { ok: true })).toBe(false)
})
})
@@ -36,14 +36,6 @@ function makeFrame(image: Uint8Array, b64?: string): BrowserScreencastFrame {
}
}
function millisecondsPerFrame(run: () => string, frames: number): number {
const started = performance.now()
for (let index = 0; index < frames; index += 1) {
run()
}
return (performance.now() - started) / frames
}
describe('the browser frame data URI', () => {
it('is byte-identical on both platforms for the frame the bridge decoded', () => {
const image = makeImageBytes(FRAME_BYTES)
@@ -69,22 +61,4 @@ describe('the browser frame data URI', () => {
createBrowserFrameDataUri(makeFrame(image))
)
})
it('costs less per frame than the re-encode it replaces', () => {
const image = makeImageBytes(FRAME_BYTES)
const b64 = Buffer.from(image).toString('base64')
const frames = 100
const reEncode = (): string => `data:image/jpeg;base64,${Buffer.from(image).toString('base64')}`
const carried = (): string => createBrowserFrameDataUriOnWeb(makeFrame(image, b64))
// Both warmed in the same harness before either is timed, so neither pays for a cold JIT.
millisecondsPerFrame(reEncode, 20)
millisecondsPerFrame(carried, 20)
const reEncodeMs = millisecondsPerFrame(reEncode, frames)
const carriedMs = millisecondsPerFrame(carried, frames)
// Loose on purpose: the measured gap is three orders of magnitude, and a bound near it would
// make a shared CI runner's noise a red build rather than a finding.
expect(carriedMs).toBeLessThan(reEncodeMs / 10)
})
})
@@ -4,11 +4,6 @@ import { act, createElement, type ComponentType } from 'react'
import { createRoot } from 'react-dom/client'
import { describe, expect, it, vi } from 'vitest'
import type { Image, View } from 'react-native'
import {
updateBrowserImageSource,
updateBrowserLayerVisibility,
whenBrowserFrameDisplayable
} from './browser-frame-layer-paint'
import {
updateBrowserImageSource as updateBrowserImageSourceOnWeb,
updateBrowserLayerVisibility as updateBrowserLayerVisibilityOnWeb,
@@ -40,26 +35,6 @@ const asImageRef = (node: HTMLElement) => node as unknown as Image
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: same mismatch, for the layer refs the visibility writer takes.
const asViewRef = (node: HTMLElement) => node as unknown as View
describe('the native frame-path writes against an RN Web ref', () => {
it('throws from updateBrowserImageSource, because a DOM node has no setNativeProps', () => {
expect(() => updateBrowserImageSource(asImageRef(mountImageHost()), FRAME_URI)).toThrow(
/setNativeProps is not a function/
)
})
it('throws from updateBrowserLayerVisibility for the same reason', () => {
expect(() =>
updateBrowserLayerVisibility([asViewRef(mountLayer()), asViewRef(mountLayer())], 0)
).toThrow(/setNativeProps is not a function/)
})
it('reports no decode of its own: the rendered Image fires onLoad natively', () => {
const onDisplayable = vi.fn()
whenBrowserFrameDisplayable(FRAME_URI, { onDisplayable, onUndecodable: vi.fn() })
expect(onDisplayable).not.toHaveBeenCalled()
})
})
describe('the web siblings', () => {
it('paints the frame as a background-image on the element RN Web sizes', () => {
const host = mountImageHost()
@@ -1,15 +1,11 @@
import { describe, expect, it, vi } from 'vitest'
import { BRIDGE_MAX_MESSAGE_BYTES } from '../mobile-web-shell/bridge/bridge-caps'
import { BRIDGE_PROTOCOL_VERSION } from '../mobile-web-shell/bridge/bridge-envelope'
import { METADATA_KEYS } from '../transport/browser-screencast-protocol'
import { buildMobileBrowserScreencastRequest } from './browser-screencast-request'
import {
BASE64_BYTES_PER_CHARACTER,
base64Characters,
binaryEventEnvelopeBytes,
budgetedMobileViewDeviceScaleFactor,
buildMobileBrowserScreencastRequest as buildOnWeb,
mobileBrowserFrameAreaBudget,
WORST_CASE_JPEG_BYTES_PER_PIXEL
} from './browser-screencast-request.web'
@@ -53,16 +49,11 @@ describe('the mobile-view area budget', () => {
)
})
// Derived from the same constants the module derives from, not written down: a budget the test
// restates is a budget that agrees with itself and with nothing else.
it('asks for the scale the cap and the worst case together allow', () => {
const expected = Math.sqrt(mobileBrowserFrameAreaBudget() / (PHONE.width * PHONE.height))
it('uses the largest hundredth of density whose frame fits the cap', () => {
const scale = budgetedMobileViewDeviceScaleFactor(PHONE)
expect(scale).toBeLessThanOrEqual(expected)
// Floored to two decimals, so it is the largest such scale rather than merely a safe one.
expect(scale).toBeGreaterThan(expected - 0.01)
expect(
frameMessageBytes(mobileFrameArea(PHONE, { deviceScaleFactor: scale + 0.01 }))
).toBeGreaterThan(BRIDGE_MAX_MESSAGE_BYTES)
})
it('leaves web mode byte-identical to the native request', () => {
@@ -89,122 +80,8 @@ describe('the mobile-view area budget', () => {
})
})
describe('the budget derivation', () => {
it('spends the whole cap that the envelope leaves', () => {
const available = BRIDGE_MAX_MESSAGE_BYTES - binaryEventEnvelopeBytes()
const expectedArea = Math.floor(
(Math.floor(available / 4) * 3) / WORST_CASE_JPEG_BYTES_PER_PIXEL
)
expect(mobileBrowserFrameAreaBudget()).toBe(expectedArea)
// The ratio alone can claim two bytes base64 does not have, which at this margin is a frame
// the shell drops rather than sends.
expect(Math.floor(available / 4) * 3).toBeLessThanOrEqual(
Math.floor(available * BASE64_BYTES_PER_CHARACTER)
)
})
/**
* Why the budget counts whole base64 groups rather than three quarters of the room.
*
* The two agree at today's envelope size, because the room it leaves happens to divide by four.
* They do not agree in general, and the budget now spends the cap exactly, so the first envelope
* change that lands on another remainder would hand the shell a frame two characters over.
*/
it('counts base64 padding, which the ratio does not for every image size', () => {
expect(base64Characters(3001)).toBe(4004)
expect(Math.ceil(3001 / BASE64_BYTES_PER_CHARACTER)).toBe(4002)
// Same statement from the budget's side: room for 4002 characters is not room for 3001 bytes.
expect(Math.floor(4002 / 4) * 3).toBeLessThan(Math.floor(4002 * BASE64_BYTES_PER_CHARACTER))
})
// The budget spends the cap exactly, so anything the expansion under-counts is a dropped frame.
it('leaves a frame of exactly the budgeted area inside the cap, padding included', () => {
expect(frameMessageBytes(mobileBrowserFrameAreaBudget())).toBeLessThanOrEqual(
BRIDGE_MAX_MESSAGE_BYTES
)
})
it('measures the envelope rather than naming a number, and leaves it room to grow', () => {
const envelope = binaryEventEnvelopeBytes()
// Big enough to be the real shape, small enough that the budget is not swallowed by it.
expect(envelope).toBeGreaterThan(200)
expect(envelope).toBeLessThan(BRIDGE_MAX_MESSAGE_BYTES / 100)
})
})
/**
* The widest a finite double serializes as: sign, `0.`, the five zeros fixed notation writes just
* above 1e-6, and seventeen significant digits. Exponential form is one shorter, because ToString
* only leaves fixed notation below 1e-6.
*/
const WIDEST_JSON_DOUBLE = -0.000001234567890123456_7
/** A frame event with nothing in it but the widest every field can be, and no image. */
function widestEnvelope(metadataValue: number): string {
return JSON.stringify({
v: BRIDGE_PROTOCOL_VERSION,
type: 'event',
id: 'a'.repeat(22),
seq: Number.MAX_SAFE_INTEGER,
binary: {
b64: '',
format: 'jpeg',
frameSeq: Number.MAX_SAFE_INTEGER,
metadata: Object.fromEntries(METADATA_KEYS.map((key) => [key, metadataValue]))
}
})
}
describe('the envelope bound', () => {
// The budget's whole margin is whatever this over-estimates by, so an under-estimate is a frame
// over the cap rather than a rounding difference.
it('is at least what a real frame event costs at its widest', () => {
expect(binaryEventEnvelopeBytes()).toBeGreaterThanOrEqual(
widestEnvelope(WIDEST_JSON_DOUBLE).length
)
})
it('is a bound on every finite double, not on the one it was written with', () => {
// Deterministic rather than seeded off the clock: a bound that fails one run in a thousand is
// a bound nobody believes. Bit patterns for the exponential forms, then the band just above
// 1e-6 where fixed notation is widest.
const bits = new ArrayBuffer(8)
const asDouble = new Float64Array(bits)
const asBits = new BigUint64Array(bits)
let state = 0x9e37_79b9n
const next = (): bigint => {
state =
(state * 6_364_136_223_846_793_005n + 1_442_695_040_888_963_407n) & 0xffff_ffff_ffff_ffffn
return state
}
let widest = 0
for (let index = 0; index < 200_000; index += 1) {
asBits[0] = next()
const value = asDouble[0]
if (Number.isFinite(value)) {
widest = Math.max(widest, JSON.stringify(value).length)
}
const nearTheBoundary = -(1e-6 + Number(next() % 9_000_000n) * 1e-12)
widest = Math.max(widest, JSON.stringify(nearTheBoundary).length)
}
expect(widest).toBe(JSON.stringify(WIDEST_JSON_DOUBLE).length)
expect(binaryEventEnvelopeBytes()).toBeGreaterThanOrEqual(
widestEnvelope(WIDEST_JSON_DOUBLE).length
)
})
// Not the other way round either: an envelope bound big enough to be safe and loose enough to
// cost real pixels is a budget nobody can reason about.
it('is not loose: it is within a metadata field of what that costs', () => {
const widest = widestEnvelope(WIDEST_JSON_DOUBLE).length
expect(binaryEventEnvelopeBytes() - widest).toBeLessThan(
JSON.stringify(WIDEST_JSON_DOUBLE).length
)
})
it('counts base64 padding for an image that does not fill its last group', () => {
expect(base64Characters(3001)).toBe(4004)
})
describe('web-mode request work', () => {
@@ -234,12 +111,6 @@ describe('web-mode request work', () => {
}
expect(actual).toEqual(expected)
expect(actual.map((request) => Object.keys(request ?? {}))).toEqual(
expected.map((request) => Object.keys(request ?? {}))
)
expect(actual.map((request) => JSON.stringify(request))).toEqual(
expected.map((request) => JSON.stringify(request))
)
expect(envelopeCalls).toBe(0)
expect(sqrtCalls).toBe(0)
})
@@ -156,14 +156,14 @@ async function openPaneOverTheBridge() {
})
}
/** Settles the dialog reply the pane sent, the way a host that reached the stream would. */
const answerFromTheHost = async (accept: boolean): Promise<void> => {
const answerFromTheHost = async (): Promise<void> => {
const request = rpc.requests.at(-1)
if (!request || !request.method.startsWith('browser.dialog')) {
if (!request || !['browser.dialogAccept', 'browser.dialogDismiss'].includes(request.method)) {
throw new Error(`the pane's last request was ${request?.method ?? 'nothing'}`)
}
await act(async () => {
request.resolve(rpcSuccess(`req-${rpc.requests.length}`, {}))
page.answer(accept)
page.answer(request.method === 'browser.dialogAccept')
await pair.flush()
})
}
@@ -185,7 +185,7 @@ describe('the pane keeps its dialog card until the page is unblocked', () => {
// so it is still up here.
expect(cardMessages(pane.renderer)).toContain('first')
await pane.answerFromTheHost(true)
await pane.answerFromTheHost()
// The page moved on, so the card did too: the confirm, with a Cancel beside the OK.
expect(cardMessages(pane.renderer)).toContain('second')
expect(cardMessages(pane.renderer)).not.toContain('first')
@@ -193,7 +193,7 @@ describe('the pane keeps its dialog card until the page is unblocked', () => {
pressButton(pane.renderer, 'OK')
await pane.flush()
await pane.answerFromTheHost(true)
await pane.answerFromTheHost()
expect(pane.page.confirmValue()).toBe(true)
expect(cardMessages(pane.renderer)).not.toContain('second')
})
@@ -221,12 +221,12 @@ describe('the pane keeps its dialog card until the page is unblocked', () => {
// And the button still works: the retry reaches the host, which answers, and the page moves on.
pressButton(pane.renderer, 'OK')
await pane.flush()
await pane.answerFromTheHost(true)
await pane.answerFromTheHost()
expect(cardMessages(pane.renderer)).toContain('second')
expect(cardMessages(pane.renderer)).not.toContain('That answer did not reach the page.')
})
it('kills the card buttons while an answer is in flight, so a double tap sends one', async () => {
it('disables the answer button in flight and enables the next dialog buttons', async () => {
const pane = await openPaneOverTheBridge()
await act(async () => {
pane.page.start()
@@ -240,7 +240,7 @@ describe('the pane keeps its dialog card until the page is unblocked', () => {
// next dialog unseen. The button is what stops the second tap from ever being sent.
expect(findButton(pane.renderer, 'OK').props.disabled).toBe(true)
await pane.answerFromTheHost(true)
await pane.answerFromTheHost()
expect(pane.rpc.requests.length).toBe(inFlight)
// The next dialog arrives with live buttons of its own.
expect(cardMessages(pane.renderer)).toContain('second')
@@ -288,12 +288,12 @@ describe('the pane keeps its dialog card until the page is unblocked', () => {
})
pressButton(pane.renderer, 'OK')
await pane.flush()
await pane.answerFromTheHost(true)
await pane.answerFromTheHost()
pressButton(pane.renderer, 'Cancel')
await pane.flush()
expect(pane.rpc.requests.at(-1)?.method).toBe('browser.dialogDismiss')
await pane.answerFromTheHost(false)
await pane.answerFromTheHost()
expect(pane.page.confirmValue()).toBe(false)
expect(cardMessages(pane.renderer)).not.toContain('second')
})
@@ -171,6 +171,9 @@ describe('MobileBrowserPane with a stream that reports ready but sends no frames
})
expect(spinnerCount(renderer)).toBe(0)
expect(errorMessages(renderer)).not.toContain(
'Update the Orca app to stream browser tabs here.'
)
const source = renderer.root
.findAllByType('Image')
.map((image) => (image.props.source as { uri?: string } | null)?.uri)
@@ -193,13 +196,4 @@ describe('MobileBrowserPane against a shell with no binary screencast lane', ()
vi.mocked(useBrowserBinaryScreencastGrant).mockReturnValue(true)
}
})
it('subscribes when the shell names it, which is the control for the case above', async () => {
const { renderer, subscriptions } = await renderPane()
expect(subscriptions).toHaveLength(1)
expect(errorMessages(renderer)).not.toContain(
'Update the Orca app to stream browser tabs here.'
)
})
})