* fix(process): normalize reactive rendered-frame timestamps A reactive `resize` action stamped `rendered_frames[].at_ms` with the raw elapsed time while every other rendered frame used `normalizeProcessElapsedTime`. Under the default 10ms time bucket the next frame buckets down and appears to move backwards, so `parseProcessCapture` rejected the entire capture with "rendered_frames: timestamps must be ordered". No test combined a reactive resize with time bucketing, so the path was never exercised. Thread the scenario's bucket into the reactive effect host and normalize the renderer call, matching what the scheduled-event resize path already did. `dispatched_at_ms` and `captured_at_ms` stay raw on both paths, so this changes only the one inconsistent clock. Also replace the wall-clock-scheduled interactive PTY scenario with terminal_text triggers. A fixed `at_ms` could fire before the child wrote its prompt under host contention, dropping the input and the resize echo. That rewrite is what surfaced this bug, and it now passes deterministically. Replace the 11 `if (!capability.available) return;` guards across the PTY suite with a shared it.skipIf gate. The old form reported a green pass with zero assertions on any host lacking native PTY authority; skips are now named and reported. Verified by reverting only the normalization, which reproduces the validation failure immediately. * fix(install): add recovery guidance to version-mismatch failure Every installer failure except one told the user what to do next. The version-mismatch failure reported only that the installed version did not match, with no remedy, which contradicts the requirement to keep diagnostics actionable for mismatch locations. Add a remedy in the same style as the neighbouring failures and drop the exemption the test carried for it, so all ten installer failures now assert they carry a recovery action. * ci: repair stale curated Windows lane and unblind the duplication gate The `windows-curated` job listed fifteen test files and nine of them no longer existed, including six `processOwnership*` files moved to `src/process/` in4ecc7a72. Vitest silently drops unmatched paths, so the job ran six files and reported success. Repoint the paths at their current locations and add a test that fails when any listed path is renamed or removed, so the lane cannot silently lose coverage again. The duplication gate ignored `**/*.test.ts`, so it reported zero clones across the test tree while running on every PR. Include test files, remove the two largest duplicated clusters, and ratchet the threshold to the measured result so the gate can no longer pass by not looking. Run local tests with two workers instead of one. A single worker made a full local run cost ~140s of wall time at ~100% CPU on a 10-core host, which was the largest dev-cycle cost in the repository. Local now matches the concurrency CI already proves green, and PTY scenarios moved to a serial project because they contend for host terminal resources. Full local suite: 140s to ~89s. Regenerate the managed conformance manifest. Commit4ecc7a72edited SKILL.md without refreshing the digest, so `npm run check:pr` has been failing on main since that commit. * test: replace implementation lock-in with behavioural assertions Audit the suite for tests that cost maintenance without catching defects, then remove the lock-in rather than the coverage. Replace assertions that froze incidental detail: - toolContracts.test.ts pinned group sizes and 50 lines of literal tool names, so any additive catalog change required a test edit. Replace with four structural invariants: global name uniquenes