Files
Dragan Spiridonov af4140efb9 feat(quality): oracle evals, durable-first tests, conservation guard + provider fixes
Apply Phoenix Architecture principles to AQE — regenerate internals, conserve the interface.

ADR-113 (evals are oracles, durable-first tests, mutation-score gate):
- src/validation/oracle-eval.ts grades a generated test by running it against a
  reference impl + operator mutants (reuses qe-arena, ADR-104); rejects assertion-less output
- oracle mode wired into scripts/run-skill-eval.ts (skips under the simulating runner; runs live)
- qe-test-generation rewritten durable-first; its eval flipped to oracle
- src/feedback/regenerability-gate.ts: mutation-score + regenerability gate (warn-default, opt-in block)
- live-validated: Claude sonnet-4-6 clears the oracle 5/5; weak local-model output correctly fails

ADR-114 (conservation layer, Phoenix essay 14):
- src/validation/conservation-guard.ts + scripts/conservation-guard.ts guard CLI commands,
  output schemas, and dashboard API against breaking removals (deprecation registry exempts)
- policy doc + baselines in verification/conservation/; wired into invariant-check.yml non-blocking

LLM provider fixes:
- OpenRouterProvider.type was 'openai' but registered under 'openrouter' -> ProviderManager
  failover never found it; fixed to 'openrouter' end-to-end
- Gemini default model gemini-1.5-pro was retired -> gemini-2.5-flash; models/pricing refreshed
  from the live Gemini ListModels API
- scripts/provider-health.ts (npm run providers:health)

Cheaper-model eval lanes:
- scripts/oracle-model-bench.ts + openrouter-models.ts; openai/gpt-oss-120b clears the oracle
  ~50-70x cheaper than Claude Sonnet; guide in docs/guides/cheaper-model-eval-lanes.md

Verification: npm run build ok; 115 new+touched tests pass; 724 LLM tests pass; lint + parity clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:37:46 +00:00

5.2 KiB

ADR-114: The Conservation Layer — Surface-Stability Contracts for AQE

Field Value
Decision ID ADR-114
Status Accepted (2026-06-27) — implemented (diff core + CLI/output-schema/dashboard surfaces + deprecation registry + policy); CI wired non-blocking
Date 2026-06-27
Author AQE Core
Review Cadence 3 months
Supersedes —
Related ADR-113 (the complementary half: regenerate internals), scripts/audit-mcp-tool-parity.mjs (pre-existing MCP guard), Phoenix essay 14 "UI Is a Conservation Layer"

WH(Y) Decision Statement

In the context of AQE aggressively regenerating its internals (the oracle-eval work of ADR-113, model routing, provider fixes) and Chad Fowler's "UI Is a Conservation Layer" arguing that the human-facing boundary must change slowly to buffer users from internal churn — "you can't mock trust, can't regenerate it, can't A/B test it recklessly,"

facing the fact that AQE's user/agent-facing surfaces (CLI commands, report output schemas, MCP tool names, the qe-dashboard API, rendered UIs) have no stability contract except a single MCP tool-parity audit — so a refactor or the 5-tree skill drift can silently rename/remove a command, output key, or tool, breaking users' scripts and CI with no loud failure ("metrics drift, support volume rises later"),

we decided for a conservation guard: capture each surface as a baseline, diff current vs baseline, and fail CI on breaking removals/renames that are not in a deprecation registry — additive changes always pass; plus a documented deprecation policy (announce → runtime warning → remove after a version window) and visual-regression for rendered UIs,

and neglected flag-level CLI granularity for v1 (lazy commander stubs hide options; command/alias names are the high-value contract), a bespoke MCP surface (the existing mcp:parity audit already guards it — we aggregate, not duplicate), and screenshotting the qe-dashboard (it renders no DOM in-repo — it is a WASM vector-store/clustering library, so its contract is its exported API),

to achieve the second half of "regenerate internals, conserve the interface": internals stay free to change while the external protocol stays stable and any break is caught loudly at CI, not by users,

accepting that baselines must be deliberately updated (--update) on intended changes, and that the guard ships non-blocking first so adoption never breaks a release until the team is comfortable rebaselining.


Current state (grounded, verified 2026-06-27)

Fact Evidence
MCP tool names already guarded verification/mcp-tool-parity-baseline.json (86 tools, monotone) + scripts/audit-mcp-tool-parity.mjs
CLI commands / output schemas / dashboard API had no guard no snapshot/contract test for aqe commands, report JSON keys, or qe-dashboard exports
AQE ships 39 CLI commands commander lazy registrations in src/cli/index.ts
555 output-schema keys across skills .claude/skills/*/schemas/output.json
qe-dashboard renders no DOM src/integrations/browser/qe-dashboard/* — no innerHTML/createElement; 14 exported symbols
Real visual-regression exists aqe visual test, CNNVisualRegression, VisualDiff (src/domains/visual-accessibility/)

The core problem in one line: AQE can change its internals safely (ADR-113) but had nothing stopping it from silently breaking the interface users built their scripts on.


Decision detail

1. Surface diff core (pure, tested)

src/validation/conservation-guard.ts: diffSurface(name, baseline, current, deprecated) → {added, removedBreaking, removedDeprecated, clean}; additive passes, removals break unless deprecated. aggregate + formatReport for CI output. Unit-tested (tests/unit/validation/conservation-guard.test.ts).

2. Surfaces + baselines

scripts/conservation-guard.ts statically extracts three surfaces → verification/conservation/*.json:

  • cli-commands (39) — command/alias names from src/cli/index.ts
  • output-schemas (555) — <skill>::<key> from each skill's output.json
  • dashboard-api (14) — exported symbols of qe-dashboard

MCP is covered by npm run mcp:parity; rendered UIs by aqe visual test.

3. Deprecation registry + policy

verification/conservation/deprecations.json lists deliberately-removed entries with a version window; the guard exempts them. Process documented in docs/guides/conservation-layer-policy.md (announce → runtime warning → remove after removeAfter).

4. CI

npm run verify:conservation (--ci, exit 1 on breaking) wired into .github/workflows/invariant-check.yml, non-blocking initially; flip to blocking once rebaseline-on-intended-change is routine.


Consequences

  • Positive: the human/agent protocol can't be broken silently; deprecations become explicit and slow; pairs with ADR-113 to deliver "absorb change internally, present continuity externally."
  • Cost: intended surface changes require a --update rebaseline (a deliberate, reviewable diff) and registry entry for removals.
  • Reversible: non-blocking until opted into blocking; baselines are plain JSON.