INTEGRATION-CONTRACT.md
Test Expansion Integration Contract
Generated by session: test coverage expansion
Agent: claude-sonnet-4-6
Priority 1: T-001–T-010 (10 files), Priority 2: T-011–T-024 (11 files, T-011–T-014 in one file), Priority 3: T-025, T-030, T-037 (3 files)
Test File Index
| File | T-numbers | Asserts | Notes |
|---|---|---|---|
recovery-words.integrity.test.ts |
T-001 | 5 | Word list structural checks |
recovery-words.checksum.test.ts |
T-002 | 6 | Round-trip, swap, order, length |
recovery-words.kek.test.ts |
T-003 | 5 | HKDF round-trip, zero-pad bytes[16-31] |
totp.rfc6238.test.ts |
T-004 | 6 | RFC 6238 SHA1/256/512, boundary, URI parse |
peachPayload.bomb.test.ts |
T-005 | 4 | MV-4 length-bomb, decompressZstd NOT called |
backup.nfc-fallback.test.ts |
T-006 | 5 | LV-1 NFD-sealed unlocked with NFC |
pdf-import.sandbox.test.ts |
T-007 | 4 | MV-9 pdf.js hardened flags |
messages.batch-sanitize.test.ts |
T-008 | 6 | PCH-01-016 canary/tag sanitization |
license.verification.test.ts |
T-009 | 6 live + 2 todo | Ed25519 verify, structural checks |
chirp-transport.probe.test.ts |
T-010 | 3 | PCH-01-018 no-msg3 identity leak |
content.orchestration.test.ts |
T-011–T-014 | 0 live + 11 todo | All stubs (index.ts exports nothing) |
passkey.ed25519.test.ts |
T-015 | 7 | Ed25519 COSE, sign, verify, replay |
vault.rotate-sync-identity.test.ts |
T-016 | 7 | MV-8 rotation: new key, clear peers, recovery pending |
codex.multistripe.test.ts |
T-017 | 5 | Multi-stripe RS round-trip + erasure |
vault.corruption.test.ts |
T-018 | 5 live + 1 todo | corrupted vs wrong_key discrimination |
argon2id.kat.test.ts |
T-019 | 6 (1 skipped until KAT generated) | KDF params, determinism, NFC |
sync.disconnect.test.ts |
T-020 | 4 | PCH-01-026 #9 onerror/onclose |
import.csv.formulainjection.test.ts |
T-021 | 6 | Formula-injection round-trip |
messages.edit-origin.test.ts |
T-022 | 5 | GET_ENTRY_FOR_EDIT origin check, includeAll guard |
vault.consolidate-passkeys.test.ts |
T-023 | 4 | mergePasskeyOnlyIntoLogins lastUsed sort |
vault.purge-trash.test.ts |
T-024 | 5 | Retention cutoff, 0-day guard, count |
generate.empty-pool.test.ts |
T-025 | 8 | Empty pool fallback, 128-char, min/max |
ml.predictbatch.test.ts |
T-030 | 8 | Batch sizes, softmax stability |
clipboard.double-failure.test.ts |
T-037 | 4 | alreadyWritten skip, double-fail no throw |
Total: 24 test files, ~150 live assertions, ~13 todos
Real Crypto vs. Mocked Crypto (Priority 1)
All Priority 1 tests use real cryptographic primitives:
| Test | Crypto used | Notes |
|---|---|---|
| T-001 | None | Word list structural |
| T-002 | SubtleCrypto AES-GCM, HKDF, BLAKE2s | Via recovery-words.ts |
| T-003 | SubtleCrypto HKDF, AES-GCM | Via recovery-words.ts |
| T-004 | HMAC-SHA1/256/512 | Via otpauth library |
| T-005 | Argon2id (m=65536, t=3, p=4), AES-GCM | vi.mock("../../crypto/zstd") for decompressZstd spy only |
| T-006 | Argon2id, AES-GCM | Via backup.ts / derivePeachExportKey |
| T-007 | None | vi.mock("pdfjs-dist") — testing flag plumbing only |
| T-008 | Argon2id, AES-GCM | Via full vault setup |
| T-009 | Ed25519 sign/verify | Local validateLicenseWithKey re-impl; prod validateLicense for structural checks |
| T-010 | Curve25519 DH (Noise XX) | Via noiseXXInitiator + noiseXXResponder |
New Dependencies Added
These dependencies are used in the draft tests but are already present in package.json:
@stablelib/x25519— Noise XX keypair generation (T-010, T-020)pako— deflate/inflate for Codex tests (T-017)hash-wasm— Argon2id in T-005, T-006 (already used by production code)otpauth— TOTP generation (T-004, already used by production code)
No new npm dependencies are required.
Source Refactoring Recommendations
Required for T-011–T-014 (content orchestration) — HIGH PRIORITY
src/content/index.ts exports nothing. All orchestration state and functions
are module-private. To make the autofill orchestration tests runnable:
// src/content/index.ts — add these exports
export { handleFieldInteraction } // T-012
export { initialScan } // T-013
export { wasAlreadyFilled } // T-011
export const _testSeam = {
getLastDismissedField(): HTMLInputElement | null { return lastDismissedField; },
setLastDismissedField(f: HTMLInputElement | null): void { lastDismissedField = f; },
isDismissLocked(): boolean { return dismissLock !== null; },
DISMISS_LOCK_MS,
} as const;
Additionally, the vitest environment for this file must be switched to jsdom:
// vitest.config.ts
environmentMatchGlobs: [
['src/drafts/TEST-EXPANSION/content.orchestration.test.ts', 'jsdom'],
]
Required for T-009f/g (license + chrome storage)
Add validateLicenseWithKey(keyString: string, pubKeyHex: string) as an
exported helper in src/background/license.ts with injectable public key.
Currently the public key is hardcoded in a private importPublicKey() closure.
Required for T-018f (consumeLastUnlockFailureReason integration)
The test needs chrome.storage mock setup identical to T-008/T-016. The
it.todo can be promoted to a live test using the same mock pattern.
Recommended for T-019a (Argon2id KAT)
Add a script at scripts/gen-argon2id-kat.ts to generate the expected hash
vector and update KAT_EXPECTED_HEX in argon2id.kat.test.ts. Run once and
commit. See the inline documentation in the test for the script body.
Environment Requirements
| Test file | Environment | Notes |
|---|---|---|
| All current tests | node (default vitest env) |
— |
content.orchestration.test.ts |
jsdom required |
Needs environmentMatchGlobs |
sync.disconnect.test.ts |
node |
Uses vi.stubGlobal("WebSocket", ...) |
CI/Exclusion Configuration
The vitest.config.ts already excludes src/drafts/** from CI runs:
exclude: ["src/drafts/**"]
Draft tests do not run automatically. To promote a test file to CI, move it
from src/drafts/TEST-EXPANSION/ to the canonical location beside its source
(e.g., src/background/vault.rotate-sync-identity.test.ts) and remove it from
the drafts directory.
T-numbers Not Implemented
The following Priority 3 test numbers from the brief were not implemented in this session (available for follow-up):
- T-026: timestamp-clamp + manifest-freshness boundaries
- T-027: full Codex UI round-trip e2e
- T-028: autofill real-page e2e
- T-029: inject.ts selector keyboard nav
- T-031: SW eviction mid-operation
- T-032: backup.ts empty-password contradiction
- T-033: Firefox passkey e2e
- T-034: LV-2 homograph UI surfacing
- T-035: HIBP breach-check integration
- T-036: reed-solomon try/finally free
- T-038: favicon canvas fallbacks
- T-039: chirp pairing failure paths
- T-040: sync alert queue bounds
Summary Statistics
| Category | Count |
|---|---|
| Priority 1 files | 10 |
| Priority 2 files | 11 |
| Priority 3 files | 3 |
| Total draft files | 24 |
| Live assertions (approx.) | ~150 |
it.todo stubs |
~13 |
| Tests using real crypto | All P1 (except T-007 which tests flag plumbing) |
| Tests requiring source refactoring | T-011–T-014 (index.ts exports) |