Manual Test Runbook — R4: CAB Automation (via E7)¶
Owner: Sagar | Time: ~5 min (Parts A/B offline) · +10 min (Part C live) | Cloud: none for Parts A/B · GitHub read for Part C
Promotes R4 (
apps/cab-automation/) from 🟦 Code Complete → 🟩 Shipped. Parts A/B are offline ($0). Part C lists real PRs and files CAB review issues through the E7 CLI — extending the proven E7 ticketing bridge (I5/R2/K4).
Prerequisites¶
- Local tooling:
node >= 20,npm - (Part C only)
ghauthenticated to a repo; E7 (apps/ticket-platform/) built; tracker creds in env (e.g.GITHUB_TOKENfor--platform github) - Working directory:
apps/cab-automation
Steps¶
Part A — build, typecheck, unit tests (offline, ~3 min)¶
bash cd apps/cab-automation npm install npm run typecheck npm test
Expected: typecheck clean; 24 tests pass across assess.test.ts
(normalize both gh + flat shapes; label / risk-path / emergency / none
assessment; case-insensitivity; custom policy; sample end-to-end; dedupe),
render.test.ts (CAB vs. emergency header, reasons + matched paths never
invented, backout + approval checklist), and ticket.test.ts (E7 argv,
output parse, dry-run, emergency labelling).
Part B — offline dry-run on the sample (~2 min, $0)¶
-
```bash npm run build node dist/index.js --input examples/changes.sample.json --out-dir ./out
Expected: `dry-run — 2 of 4 change(s) require CAB`. The 4 sample PRs: #301 (`impact:high` + touches `modules/`,`live/` → high), #302 (docs only → none), #303 (`break-glass` + `policy/` → high, emergency/retroactive), #304 (test only → none). Inspect: ```bash cat out/cab-records.json # requiring_cab=2; reasons list the matched labels/paths ls out/cab-*.md # cab-301.md, cab-303.md -
(Custom policy) Confirm the policy is configurable — treat
docs/as risky:
Expected: #302 now also requires CAB (touches docs/).
Part C — live list + file via E7 (~10 min)¶
- Build E7 and confirm the CLI contract:
( cd ../ticket-platform && npm install && npm run build )
node ../ticket-platform/dist/index.js --platform dry-run --title t \
--body "x" --dedupe-key k --output /tmp/e7.txt && cat /tmp/e7.txt
Expected: /tmp/e7.txt contains ticket_id=…, ticket_url=…,
ticket_updated=… — the contract R4 parses.
- Assess real open PRs and file CAB issues for the high-impact ones (GitHub shown):
export GITHUB_TOKEN=… # repo-scoped
node dist/index.js \
--state open \
--platform github --repo <org>/<test-repo> \
--ticket-cmd "node ../ticket-platform/dist/index.js" \
--out-dir ./out
Expected: one CAB issue per qualifying open PR, labelled cab +
change-management + impact:* (+ emergency where applicable), body = the
CAB review with the matched reasons. Re-run the same command and confirm
each outcome is updated: true in out/cab-records.json (idempotent — no
duplicates).
- (Gate use) Confirm
--fail-on-cab trueexits non-zero when an open PR needs CAB — this is how a branch-protection check blocks merge until CAB review is recorded.
Pass criteria¶
- Part A — typecheck clean; 24 tests pass
- Part B — sample dry-run flags 2/4 (default policy), 3/4 (docs/ added to risk paths)
- Part C — a real high-impact PR files a CAB issue via E7; a second run updates it (no dup)
- Generated
cab-records.json+cab-*.mdlook correct (reasons match labels/paths, no fabrication)
Failure mode¶
Over- or under-scoping CAB — too many low-value PRs flagged, or a risky path
missed. Tuned via --cab-labels / --risk-paths / --emergency-labels; the
per-change dedupe key (cab=<n>) keeps re-runs updating one ticket. R4 only ever
cites the matched labels/paths as the reason — it never invents a justification.
Documented in the README.
Cost impact¶
$0 — pure orchestration; read-only gh pr list. No resources created.
Removal path¶
Delete apps/cab-automation/. Nothing external is provisioned.
Sign-Off¶
| Field | Value |
|---|---|
| Part A (unit tests) | ☐ PASS |
| Part B (offline dry-run) | ☐ PASS |
| Part C (live via E7) | ☐ PASS / ☐ skipped |
| Tester | |
| Date | |
| Result | ☐ PASS |