Commit Graph

2 Commits

Author SHA1 Message Date
Dragan Spiridonov 9c52823e0e fix(qe-browser): F1 — implement documented vibium-unavailable contract
Phase 5 UAT discovered the helper scripts surfaced missing-vibium as
`actual: "eval error: vibium binary not found..."` inside a `failed`
envelope. The Fallback Policy in SKILL.md said scripts MUST return
status:"skipped" with reason "browser-engine-unavailable" — that
contract was aspirational. F1 makes it real.

Contract changes:
- lib/vibium.js: ENOENT now throws typed VibiumUnavailableError (with
  stable code:"BROWSER_ENGINE_UNAVAILABLE" for cross-module duck typing)
- New helper unavailableEnvelope() returns canonical skipped envelope
  with top-level vibiumUnavailable:true and output.reason field
- New runOrSkip() wraps each helper's main() and converts the typed
  error into the documented envelope on the way out
- New rethrowIfUnavailable() helper used inside per-script catch blocks
  so missing-vibium bubbles past local "convert exception to fail()"
  handlers — assert.runConsoleCheck/runNetworkCheck/runBrowserSideCheck,
  batch's per-step catch, check-injection main, intent-score main,
  visual-diff main all updated
- emit() now returns exit code 2 for status:"skipped" (was 1)
- Exit code contract documented: 0=success, 1=failed, 2=skipped

Downstream skills can now branch on result.vibiumUnavailable === true
or exit code === 2 instead of grepping the actual string for
"vibium binary not found".

Tests: 103 (was 86). New file qe-browser-unavailable-e2e.test.ts spawns
each of the 5 helpers under PATH=/tmp/fake-bin (node only, no vibium)
and asserts exit 2 + skipped envelope shape. vibium-lib test grew from
10 to 22 cases including VibiumUnavailableError, runOrSkip, exit codes.

Smoke test: 10/10 PASS (was 9/9). New tc011 builds a fake bin dir
inside the smoke test work dir and verifies the contract end-to-end
against the actual installed scripts.

SKILL.md updated:
- Output Contract section now documents all 3 statuses with shapes
  and exit codes
- Fallback Policy replaced generic prose with concrete bash + Node
  snippets that branch on the structured field

ADR-091 Phase 6 documents the change and verification.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:56:11 +00:00
Dragan Spiridonov 3eb9ccc461 fix(qe-browser): Phase 4 — all 9 medium devil's-advocate findings
M1 visual-diff threshold: documented "max difference fraction" convention
matching Playwright maxDiffPixelRatio (was inconsistent in docs/code).

M2 fixture server bind: default to 127.0.0.1 instead of 0.0.0.0;
QE_BROWSER_FIXTURE_HOST=0.0.0.0 opt-in for explicit external access.
Codespaces auto-forwards 0.0.0.0 ports — this was an unintentional open
server every time the eval ran.

M3 path-traversal guard: replaced startsWith() with path.relative() +
'..'/absolute check. The old guard false-passed sibling-prefix dirs
(/foo vs /foo2) and was fragile on Windows mixed separators.

M4 ANSI escape injection: added sanitizeSnippet() that strips C0 controls
(0x00-0x1F except \t\n) and DEL (0x7F) from every check-injection
finding snippet. Prevents `cat findings.json` from triggering terminal
control sequences embedded in malicious page content.

M5 parseArgs --key=value: split on first '=' so --threshold=0.05 works
alongside --threshold 0.05. Only the first '=' is split so URL/base64
values survive intact.

M6 batch.js pre-validation: added validateAllSteps() that walks every
step's required fields BEFORE the first vibium call. Step 17 typo now
aborts before steps 1-16 execute their side effects.

M7 intent-score bare-x regex: anchored \bx\b in close_dialog scorer so
"fix", "exit", "extra", "sixteen" no longer match. Unicode × and ✕
unchanged.

M8 check-injection --exclude-selector: new flag drops subtrees from
the scan. Page text is read from a CLONE of <body> with matching
elements removed — live page unchanged. Lets docs sites scan their
own pages without self-flagging documentation about prompt injection.

M9 09-assets vibium catch: wrapped raw error with actionable recovery
guidance (`npm install -g vibium`, then re-run `aqe init`).

Hidden bug found and fixed during M2/M3 testing: fixtures/serve-skills.js
existed but had never been run because the repo root is ESM and the file
uses CommonJS require(). Added scoped fixtures/package.json with
"type": "commonjs" matching the existing scripts/package.json pattern.

Tests: 86 unit tests across 7 files (was 50). All pass.
Smoke test: 9/9 still passing against real Vibium v26.3.18.
Mirrored .claude/skills/qe-browser → assets/skills/qe-browser.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 08:13:40 +00:00