#1400 rewrote the orchestrator agent's expected schema from a speculative JSON manifest to the real gzipped-tarball layout observed during the #1399 dogfood. Nothing enforced that contract. PR-T4 adds a validator script + 7 fixture tests so a future refactor can't silently break bundle parsing.
<project-name>/ ├── README.md required ├── chats/ │ └── chat*.md required — ≥ 1 └── project/ └── *.html optional — absent = incomplete bundle
| # | Fixture | Expected exit | Why |
|---|---|---|---|
| 1 | complete (README + chats + project/*.html) | 0 | happy path |
| 2 | incomplete (README + chats, no project/) | 0 | assistant waiting for clarification — still valid |
| 3 | no README.md | 1 | structural violation |
| 4 | no chats/ dir | 1 | transcript is load-bearing (per README) |
| 5 | empty chats/ dir | 1 | no .md files = no transcript |
| 6 | non-tarball input (plain text) | 1 or 2 | not a real bundle |
| 7 | missing input path | 2 | tool/IO error, not a schema issue |
$ bash tests/unit/test-handoff-bundle-schema.sh ✓ complete bundle validates (exit 0) ✓ incomplete bundle validates (exit 0 — expected) ✓ missing README.md → exit 1 ✓ missing chats/ → exit 1 ✓ empty chats/ → exit 1 ✓ non-tarball rejected (exit 1) ✓ missing input → exit 2 (tool/IO error) ════════════════════════════════════════════════════════════════ Total: 7 | Passed: 7 | Failed: 0 ════════════════════════════════════════════════════════════════
PR-T1 formatStars unit test ✓ merged (#1412) PR-T2 sync_versions round-trip ✓ merged (#1414) PR-T3 Playwright E2E for landing #1415 (open, CI running) PR-T4 handoff bundle schema validator ← THIS PR PR-T5 stub-fallback integration test last