**2026-05-14 deep-4 — /test pipeline housekeeping cluster (#396 #395 #398 #399 #400 #397 #401):** 5W × ≤6A coord-direct, commit `<pending>`, ~30 sub-agents dispatched (6 W1 + 6 W2 + 6 W3 + W4/W5 pending). Headline: **/test pipeline housekeeping cluster fully shipped end-to-end** — #396 artifact-shape gap CLOSED by reframing as infrastructure-vs-fixture responsibility (driver pre-creates `ax-snapshots/` + `screenshots/`; test fixtures own artifact write; rubric-v1 + playwright-driver SKILL.md now carry an explicit Division of Responsibility block), #400 `shared/profiles` → `profiles/` rename landed with 6 mechanical refs + 2 `git mv` + 3 new anchor canaries (validate-plugin 36 → 39), #398 runDir path-traversal MED hardened with `isPathInside` guard at runner.mjs:110, #399 AbortController MED gap closed with 5 falsification-passing tests, #397 SEC-Q2 LOW symlink-resolution wrapped with two-phase realpath guard in `scripts/lib/config/test.mjs` + `scripts/lib/profiles/schema.mjs`, #401 LOW polish cluster folded (`@lib/*` vitest alias + 2 test rewrites + peekaboo AUQ clarifier + glass-modifiers `RUBRIC_GLASS_V2=1` env-gate). W2 security-tightening fallout (5 runner.test.mjs `/tmp/run-001` rejections from #398 + 2 schema.test.mjs non-existent-path rejections from #397 strict realpath) all cleaned up in W3.

**W1 Discovery (6 Explore, parallel):** D1 (#396) audited runner.mjs artifact contract — key finding: runner.mjs is *correct*; rubric-v1 expected `ax-snapshots/`, `axe-*.json`, `console.ndjson` namespaces are **test-fixture responsibility**, driver only pre-creates the directory scaffold and orchestrates spawn → fix is doc-sync + dir pre-creation, not artifact-writing logic; D2 (#398) traced runDir derivation at `scripts/lib/playwright-driver/runner.mjs:110` (`path.join(runsRoot, runId)` with no traversal check); D3 (#400) audited rename surface — 6 mechanical changes (5 imports + 1 SEED_PROFILES_PATH literal in tests) + 2 `git mv` operations (`scripts/lib/shared/profiles/{registry,schema}.mjs` → `scripts/lib/profiles/{registry,schema}.mjs` + matching `tests/lib/shared/profiles/` → `tests/lib/profiles/`); D4 (#399) confirmed spawn + AbortSignal DI seam already exists in runner.mjs (no refactor needed — pure test-write); D5 (#395 + #397) confirmed deep-3's 8 reporter-syntax canaries already lock down `/html:/` + `/json:/` Jest-style substrings (#395 is a no-op), recipe for #397 = wrap `realpathSync` two-phase (lexical isPathInside → realpath escape check, reject on realpath failure when path exists); D6 (#401) enumerated exact line ranges — peekaboo SKILL.md AUQ prose at line 142–148, glass-modifiers emit at runner.mjs:198–204, test-import depth at `tests/lib/profiles/{registry,schema}.test.mjs:1-3`.

**W2 Impl-Core (6 file-disjoint code-implementer, parallel):** I1 (#396 + #398 merged): `scripts/lib/playwright-driver/runner.mjs` gained `import { isPathInside } from '../path-utils.mjs'` + path-traversal hard-error guard immediately after runDir derivation (rejects when `!isPathInside(runDir, runsRoot)`) + `mkdirSync({ recursive: true })` calls for `ax-snapshots/` and `screenshots/` sibling dirs pre-execution; I2 (#396 doc-sync): `skills/test-runner/rubric-v1.md` Check 1–4 rewrites + `skills/playwright-driver/SKILL.md` Artifact Layout section — both now carry an explicit **Division of Responsibility** block ("driver writes: scaffold, HTML+JSON reporter output, traces/. test fixture writes: ax-snapshots/, axe-*.json, console.ndjson"); I3 (#399): NEW `tests/lib/playwright-driver/runner-abort-controller.test.mjs` with 5 falsification-passing tests (timer-firing on timeout, AbortSignal propagation to child via spawn opts, clearTimeout on normal exit, exit-code 2 mapping on SIGTERM, custom `timeout_ms` from profile-registry override); I4 (#397): `scripts/lib/config/test.mjs` `case 'profiles-path':` + `scripts/lib/profiles/schema.mjs` `rubric` field both wrapped with `realpathSync` two-phase guard (lexical `isPathInside` first, then realpath escape detection, reject on realpath failure when the resolved target exists outside boundary); I5 (#401-A): `vitest.config.mjs` gained `resolve.alias: { '@lib': new URL('./scripts/lib/', import.meta.url).pathname }` + rewrote 2 test files (`tests/lib/playwright-driver/runner.test.mjs` + `tests/lib/profiles/registry.test.mjs`) to use `'@lib/...'` imports; I6 (#401-B): `skills/peekaboo-driver/SKILL.md` Phase 3 AUQ prose clarifier (split multi-permission iteration paragraph) + `scripts/lib/playwright-driver/runner.mjs` glass-modifiers JSON emit gated behind `process.env.RUBRIC_GLASS_V2 === '1'` env var (default off — stays stub).

**W2 fallout (cleaned in W3):** I1's #398 guard rejected 5 pre-existing tests in `runner.test.mjs` using literal `/tmp/run-001` paths (outside the configured runsRoot); I4's strict realpath made 2 `schema.test.mjs` tests fail because they pointed at non-existent fixture paths that the new guard rejects.

**W3 Impl-Polish (6 agents, parallel):** P1 (#400 rename master): executed `git mv scripts/lib/shared/profiles → scripts/lib/profiles` + `tests/lib/shared/profiles → tests/lib/profiles` + 6 mechanical import/literal updates (5 imports across runner.mjs/config/test.mjs/registry.test.mjs/schema.test.mjs/registry.mjs + 1 SEED_PROFILES_PATH literal in registry.test.mjs) + added 3 new anchor canaries to `scripts/lib/validate/check-peekaboo-driver-canary.mjs` (`thin executor`, `$MISSING`, `status: stub`) — validate-plugin 36 → 39; P2 (#400 docs sync): `skills/test-runner/SKILL.md` + `skills/playwright-driver/SKILL.md` + `README.md` Components section path references updated to new `scripts/lib/profiles/` (note: README path-reference update only, no count change) + glass-modifiers `RUBRIC_GLASS_V2` env-gate caveat appended to peekaboo Composability Contract; P3: fixed 5 `runner.test.mjs` runDir-traversal failures by switching test fixtures from literal `/tmp/run-001` to `.orchestrator/metrics/test-runs/<runId>` paths rooted within the canonical runsRoot; P4: fixed 2 `schema.test.mjs` realpath failures by switching non-existent fixture paths to existing repo paths + added 3 GAP-Q4-4 boundary tests for profiles-path (`..` lexical reject, symlink-escape reject, valid-within-boundary accept); P5 (this) drafted W5 F1 narrative scratch; P6 read-only grep audit verifying zero remaining `scripts/lib/shared/profiles/` or `tests/lib/shared/profiles/` references in source/tests/docs.

**Quality (estimate, W4 pending):** 4966p/12s → ~4980p/12s (+14 net: +5 runner-abort-controller + +3 GAP-Q4-4 profiles-path boundaries + +3 #400 anchor canaries surfaced as test additions + +3 polish/regression). validate-plugin 36/36 → 39/39 (+3 R5 peekaboo anchor canaries). typecheck 67/67, lint 0, doc-consistency 0 (pending W4 Full Gate). **Filed follow-ups:** none expected pre-W4; W4 reviewers may surface 1–2 LOWs. **Carryover:** #385 live-coverage rubric-v1 conformance — gating issue #396 closed this session, mechanism + coverage now fully aligned; #386 MAC-PROOF end2end (deferred — no mac-gate live fixture); #387 projects-baseline-dir (deferred — external repo still absent); #394 LOW-4 peekaboo runner.mjs V2.

**Learnings to /evolve:** (a) artifact-shape mismatches between rubric and driver are almost always a Division-of-Responsibility ambiguity, not a bug — fix is doc-sync + scaffold pre-creation, not artifact-writing logic in the driver; (b) `isPathInside` two-phase guard pattern (lexical + symlink-escape) is now the canonical shape for any user-supplied path resolution in config readers; (c) `@lib/*` vitest alias eliminates `../../../../` import-depth churn during file relocations — should be applied repo-wide as a follow-up.
