Non-destructive `aqe init` for `.claude/settings.json`: merge instead of
overwrite, preserve user-owned config (custom statusLine, explicit
includeCoAuthoredBy, AQE_* env overrides, other tools' hooks such as ruflo /
Claude Flow), deep-merge v3 sections, and back up the pristine original before
writing (mirroring CLAUDE.md.backup).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Devil's-advocate review of today's branch surfaced three real gaps:
C1 (init-handler.ts): bare `aqe init --no-claude` (no --with-<platform>, no
--no-database) fell through to runStandardInit and installed the FULL Claude
surface it was meant to suppress — `noClaude` was only honored inside the
modular path. Add `options.claude === false` to the modular routing guard.
Verified: `aqe init --no-claude --minimal` now writes no .claude/, .mcp.json,
or CLAUDE.md (and the empty-install warning fires).
C2 (continuedev-installer.ts): the mergeExistingYamlConfig branch (existing
`mcpServers:` + --overwrite) appended a hardcoded AQE_MEMORY_PATH entry,
ignoring memoryBackend — re-introducing the #533 db leak in db-free installs
over an existing Continue config. Honor memoryBackend on this path too. Adds a
regression test.
H3 (entry.ts): main() ran fire-and-forget, so a startup rejection that escaped
its internal try hit the unhandledRejection handler (which intentionally keeps
the server alive at runtime) → on the in-process `agentic-qe mcp` path the CLI
wrapper would hang on its never-resolving promise with a zero exit. main() now
`.catch`es and exits non-zero on a failed startup. Re-verified #528/#535
integration tests still green (normal startup unaffected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
#533 — database-free mode now propagates AQE_MEMORY_BACKEND=memory to ALL
generated MCP-server configs, not just opencode.json. Previously `aqe init
--no-database` left .mcp.json (Claude Code) and every editor config pointed at
.agentic-qe/memory.db, so launching the server via any non-OpenCode client
recreated the db at runtime and defeated the db-free install.
- platform-config-generator.ts: generateMcpConfig(platformId, { memoryBackend })
emits AQE_MEMORY_BACKEND=memory and drops AQE_MEMORY_PATH for JSON/TOML/YAML;
covers the 8 shared-generator installers (cursor/cline/continuedev/kilocode/
roocode/windsurf/codex/copilot).
- 08-mcp.ts (.mcp.json) and kiro-installer.ts: same db-free env; learning/workers
not forced on in db-free mode (they persist to SQLite).
- 09-assets.ts threads memoryBackend into every platform installer.
Default (persistent) output is byte-for-byte unchanged.
#532 — new opt-in `--no-claude` suppresses the default Claude Code surface
(.claude/ skills+agents, .mcp.json, CLAUDE.md, governance, hooks) so
`--with-<platform>` flags become the only install targets — e.g. a clean
OpenCode-only install. Gated via shouldRun/run on phases 07-hooks, 08-mcp,
09-assets (core assets), 11-claude-md, 13-governance. Default install
(no flag) is unchanged; warns if --no-claude is passed with no platform.
Verified by real `aqe init` runs in temp dirs:
- `--no-database --with-codex --with-cursor`: .mcp.json/.cursor/.codex all emit
AQE_MEMORY_BACKEND=memory; no memory.db created.
- `--no-claude --with-opencode --no-database`: .opencode present; .claude/,
.mcp.json, CLAUDE.md all absent.
- default `--with-cursor`: full surface + persistent env intact (no regression).
Adds db-free + no-claude cases to platform-config-generator and mcp-phase tests;
all 406 init unit tests pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Phase 1 of the #488 production-readiness work, scoped to additive changes
that don't need new ADRs. Two parts:
B.1 — Daemon pidfile points at the npx wrapper, not the MCP server
=================================================================
When neither `node_modules/.bin/aqe-mcp` nor the local
`node_modules/agentic-qe/dist/mcp/bundle.js` exists, the generated
`.agentic-qe/workers/start-daemon.cjs` falls back to
`npx --yes agentic-qe mcp`. `npx` exits as soon as it has forked the real
bundle, so `child.pid` is the wrapper PID and the pidfile becomes stale
within seconds. `aqe daemon status` then misreports liveness, and the
idempotency check at the top of the script spawns duplicate daemons —
the reporter hit this twice in two days.
Adds `require.resolve('agentic-qe/dist/mcp/bundle.js')` as a 3rd
candidate (try/catch-guarded) before the npx fallback. Globally-installed
shops (`npm install -g agentic-qe` — the typical case the npx fallback
was silently covering) now spawn the real bundle directly; `child.pid`
is the long-lived MCP server.
The npx fallback remains as a last resort for environments where neither
local nor global install is reachable. When taken, it now logs a WARNING
to daemon.log so operators can see the degraded pidfile contract.
B.2 — `learning:loop-health` observability
==========================================
Operators previously had no way to answer "is the self-learning loop
closing end-to-end?" without DB-mining `dream_cycles`, `kv_store`, and
`routing_outcomes` separately. Cumulative counters in `aqe learning
stats` grow forever and can't distinguish "healthy loop" from "loop
wedged 3 hours ago with stale totals".
Adds a single `learning:loop-health` kv key with per-component liveness:
{ overallLastSuccess, bootedAt,
components: {
bridge?: { lastSuccessAt, ticksSinceBoot, ... },
learningWorker?: { lastSuccessAt, ticksSinceBoot, ... },
dreamScheduler?: { lastSuccessAt, ticksSinceBoot, ... },
} }
Writers (best-effort — observability MUST NOT crash the observed
component):
- CapturedExperienceBridge.drainSafe — every poll, success or failure
- LearningConsolidationWorker.doExecute — end of every tick
- DreamScheduler reserved for #488 Phase 2 (the field stays
undefined until Phase 2 wires the scheduler in-kernel)
Surface:
- new `aqe learning loop-health [--json]` CLI subcommand with
component verdicts (live/stale/never-ran) based on per-component
staleness thresholds (bridge: 30s; worker/scheduler: 2h)
- distinct from the existing `aqe learning health` (per-domain
regret analysis); the new command is pipeline liveness, not
agent outcome quality
Tests:
- 12 tests in tests/unit/learning/loop-health.test.ts for the
utility: fresh record creation, tick counters, error capture +
clear-on-success, multi-component isolation, overallLastSuccess
max-computation, throw-safety when the memory backend is broken,
staleness verdicts
- 4 tests in tests/unit/init/phases/10-workers-daemon-pidfile.test.ts
asserting the require.resolve probe is present, try/catch-guarded,
ordered before the npx fallback, and that the warning string is
in place
541/541 tests pass across CLI commands, workers, bridge, learning,
and init/phases suites; build clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes GAP-05, GAP-08, and final verification gate.
- Linux ARM64 diagnostic (GAP-05): browser-engine-installer now probes
os.platform + os.arch on Linux aarch64 and checks a prioritized list
of system chromium locations (/usr/bin/chromium, chromium-browser,
google-chrome, google-chrome-stable, /snap/bin/chromium). When a
browser is found, the install result carries a PlatformHint with an
exact VIBIUM_BROWSER_PATH export the user can copy. When none exists,
the hint surfaces the `apt-get install chromium` remediation. Wired
into phase 09 so it prints after vibium install succeeds. 5 new tests.
- GAP-08: added explicit `vibium --headless viewport 1280 720` setup
steps to yaml tc006/tc007 so the eval runs are as deterministic in CI
as smoke-test.sh. Updated smoke-test.sh header to note the yaml is
the canonical spec and the bash script is its mirror (covers the F1
vibium-missing contract case the yaml can't express cleanly).
- Trust tier promoted 2 -> 3 now that (a) the eval is runnable via
CommandEvalRunner, (b) CI runs both unit tests + smoke gate, and
(c) all 11 dependent browser-using skills either reference qe-browser
or document their intentional Playwright fallback.
- ADR-091 status: Proposed -> Implemented (2026-04-21).
- v3-adrs.md index reflects 80 Implemented / 6 In Progress / 3 Proposed.
Verification: 643/643 tests pass across tests/unit/validation/,
tests/unit/init/, tests/unit/scripts/qe-browser-*.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Closes GAP-01, GAP-02, GAP-04 from the ADR-091 completion plan.
- Adds CommandEvalRunner (src/validation/command-eval-runner.ts) as a
sibling to ParallelEvalRunner. Detects shell-based eval suites by
shape (first test case has input.command), runs setup/main commands
sequentially, and evaluates exit_code, json_fields (dotted JSONPath),
severity_at_least (none < low < medium < high < critical), and
candidate_count_at_least assertions against script JSON envelopes.
- Wires aqe eval run to dispatch to CommandEvalRunner when the suite is
command-mode. LLM-prompt suites keep the pre-existing path unchanged.
- Activates .claude/skills/qe-browser/evals/qe-browser.yaml (status:
active, was design-spec) and removes the honesty note now that the
runner supports its structured assertions.
- Pins vibium@^26.3.18 via DEFAULT_VIBIUM_SPEC in browser-engine-
installer.ts so npm install -g can't silently upgrade past a version
we've verified against the 5 primitives + smoke tests.
- Drops qe-browser SKILL.md trust_tier from 3 to 2 pending eval
integration in CI; will re-promote once the full verification pass
runs green.
Tests: 31 new cases (pure helpers, runner integration with injected
runner, happy-path against the shipped qe-browser.yaml).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
H1: detectVibium reads stderr too, extracts semver
- Many Go CLIs (including some Vibium versions) write --version to stderr.
detectVibium used to read only stdout and silently report 'unknown',
polluting the init log with "vibium unknown (already installed)".
- Fix: read both streams, prefer stdout, extract bare semver via regex
so output is consistent regardless of where the binary printed it.
- Tests: 5 new regression tests covering stderr fallback, stdout
preference, verbose-format extraction, prerelease tags, fallback to
first whitespace token.
- Note: result.version is now bare semver (26.3.18) not v-prefixed.
Two pre-existing tests updated to match the new contract.
H2: instructions_in_html_comment dead-code regex
- TreeWalker(SHOW_COMMENT).textContent yields the inner text without
the <!-- --> delimiters, so the pattern that requires the literal
`<!--` prefix could never fire. Eval tc009 was passing only by
accident (ignore_previous_instructions fired on the same fixture).
- Fix: re-wrap each comment node in `<!-- ... -->` before adding to
the hidden bucket. Pattern can now actually match.
- Tests: 2 new regression tests asserting wrapped form fires AND
unwrapped form does NOT fire (the bug condition).
H4: e2e-flow-verifier frontmatter regression — INVALID FINDING
- Verified against `git show main:` — the skill always had minimal
frontmatter on main (just name + description + user-invocable: true)
and has no evals/, schemas/, or scripts/ directories. It is a
user-invocable runbook-style skill, not trust-tier-3 validated.
No metadata to restore. Marked closed-as-invalid.
H6: log-before-spawn for the 3-minute Vibium install
- spawnSync(npm install -g vibium) blocks the event loop for up to
3 minutes on a cold cache while Chrome for Testing downloads. The
user saw zero output and would Ctrl-C thinking init hung.
- Fix: pre-flight detect call (5s timeout) short-circuits when vibium
is already installed (the common case, no banner). When the install
is actually about to run, log a clear "this can take 1-3 minutes,
downloads Chrome for Testing lazily" message BEFORE the spawn.
H7: scoped scripts/package.json survives npm pack — VERIFIED
- `npm pack --dry-run` confirms both
.claude/skills/qe-browser/scripts/package.json (225B) AND
assets/skills/qe-browser/scripts/package.json (225B)
are included in the published tarball. The CommonJS override that
keeps the helper scripts loadable from an ESM project root will
survive `npm install` after publication. No fix needed.
H8: eval yaml uses unsupported runner fields
- The structured assertion fields I invented (exit_code, json_fields,
severity_at_least, candidate_count_at_least) are NOT understood by
src/validation/parallel-eval-runner.ts which expects must_contain
keyword matching. The yaml as written is unrunnable.
- Honest fix: relabel evals/qe-browser.yaml as `status: design-spec`
with a HONESTY NOTE explaining it's a specification, not a runnable
eval. Two paths to make it runnable documented (extend runner OR
rewrite as must_contain).
- Parallel artifact: scripts/smoke-test.sh — a runnable bash smoke
test against the same pinned httpbin.org fixtures. THIS is the
script that gates PR-reopen per ADR-091 Phase 3.
Bonus from B2/H5 already in Phase 1:
- assert.js now uses ?? instead of || for the `expected` field
Verification:
- 50/50 qe-browser tests pass (was 36 in initial PR; +14 regressions)
- 392/392 init tests pass (was 387; +5 from H1)
- npm run build: clean
- npm pack --dry-run: scoped package.json confirmed in tarball
- eslint on all touched source files: clean
- bash -n smoke-test.sh: syntax OK
Phase 3 (real Vibium smoke test) still gating PR reopen per ADR-091.
Branch intentionally not pushed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces a new AQE-owned browser automation skill layered over Vibium
(WebDriver BiDi, single 10MB Go binary) and migrates 11 browser-using QE
skills to use it instead of raw Playwright/Puppeteer.
qe-browser ships five helper scripts that add QE primitives Vibium doesn't
cover on its own: typed assertions (16 check kinds), batch execution,
pixel-diff against stored baselines, prompt-injection scanning, and
semantic intent scoring ported from gsd-browser (MIT/Apache). Each script
emits a structured JSON envelope matching schemas/output.json so hooks
and CI gates can parse results without stdout grep.
Vibium installs automatically during `aqe init` via a new graceful
browser-engine installer that shells out to `npm install -g vibium` and
reports `already-installed`, `installed`, `install-failed`, or
`npm-unavailable` without ever failing init.
Skills migrated to reference qe-browser:
a11y-ally, e2e-flow-verifier, qe-visual-accessibility,
security-visual-testing, visual-testing-advanced, testability-scoring,
compatibility-testing, accessibility-testing, localization-testing,
observability-testing-patterns, enterprise-integration-testing.
Ships 36 new unit tests (browser-engine-installer, assert, intent-score,
check-injection) with dependency-injected spawners to avoid touching real
processes. All 387 existing init tests still pass.
Eval harness uses pinned public fixtures (httpbin.org, Vibium docs at a
pinned tag) plus a local static server that wraps this repo's own skill
docs, per the "no synthetic fixtures" rule.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Addresses the v3.9.1–v3.9.4 init regression series by building the
verification layer that #401's post-mortem identified as structurally
missing. The load-bearing changes are the real-public-repo corpus, the
--json contract, and the restructured error handling in phase 06 that
stops lying about success when a non-critical phase fails.
What landed
-----------
* `aqe init --json` — structured InitResult output with schema version 1,
stricter exit code semantics (exit 1 on any step.status='error', not
just on critical failures). Stable contract for the gate and other CI
consumers. See InitJsonOutput in src/cli/handlers/init-handler.ts.
* Phase 06 no longer swallows errors as `{status:'skipped',entries:0}`.
Exceptions now propagate to BasePhase.execute() and surface as real
step errors in result.steps[]. Fixed a resource leak where kgService
and memory handles were leaked on the old throw path — now always
released via try/finally.
* `tests/fixtures/init-corpus/` — 4 pinned real public repo fixtures:
- tiny-ts (developit/mitt@b240473)
- mid-ts (sindresorhus/p-queue@55d306b)
- multi-lang-real (ruvnet/RuView@2a05378 — contains the exact 28,745
byte / 776 line ruview_live.py that triggered the v3.9.4 hang,
sha256-pinned to detect content drift)
- self-dogfood (agentic-qe HEAD)
* 22 gate assertions per fixture (A1–A22) including per-step JSON error
inspection (A10), snapshot-based KG threshold with tolerance (A12),
skills/agents/MCP/CLAUDE.md/workers/config.yaml presence (A13–A20),
subthreshold stall detection (A21), and a second init pass that
exercises the delta-scan path of phase 06 — the actual surface that
hung in v3.9.1 ruview, never reached on a first init (A22).
* `.github/workflows/npm-publish.yml` — new pre-publish-gate job that
runs the corpus against the freshly-built tarball before publish; new
tests-on-tag-sha job that re-runs fast unit tests on release events
(replaces a buggy gh-api conditional caught by devils-advocate review).
* `.github/workflows/post-publish-canary.yml` — fires on real publishes
only (not workflow_dispatch dry runs), polls npm CDN via
`npm install --dry-run` (actual CDN edge, not just registry metadata),
runs the corpus against the published package, opens P0 issue on
failure via heredoc-based body.
* Watchdog effectiveness unit test
(tests/unit/init/phases/code-intelligence-watchdog.test.ts) — 4 cases
proving the async-stall branches of runBoundedScan work and
documenting the sync-block limitation that requires AQE_SKIP_CODE_INDEX
or (future) worker isolation.
* `.github/PULL_REQUEST_TEMPLATE.md` — single load-bearing checkbox:
every PR failure mode must have a test or tracking issue. Honor-system
today; CI enforcement tracked in #408.
* AQE_SKIP_CODE_INDEX promoted from "temporary escape hatch" to
permanent supported flag in docs, CLI help, and source comments.
Verification
------------
* `npm run typecheck` — clean
* `npx eslint` on all touched files — clean (plus fixed two pre-existing
unused-var warnings in init-handler.ts)
* `aqe security --sast` on all touched source + workflows — 0 findings
* `tests/unit/init/` — 377/377 pass
* `tests/unit/init/phases/code-intelligence-watchdog.test.ts` — 4/4 pass
* Local gate against `npm pack`'d tarball — 4/4 fixtures pass all 22
assertions including double-init delta path and sha256 regression
marker
Failure paths independently verified (F1–F5 injection tests):
* F1 wrong sha256 → A3 fires, exit 2, actionable diagnostic
* F2 absurd expectedKgEntries → A12 fires, exit 2, shows actual vs expected
* F3 expectedElapsedSec=0 → A21 fires on slow fixture, A22 fires on
double-init — same `(( elapsed > soft_cap ))` math proven in both paths
* F4 injected throw in runCodeIntelligenceScan → reproduces the exact
v3.9.3 failure shape (`success:true` at orchestrator level, phase 06
step.status='error'). A7 catches via strict exit code, A10 catches as
defense-in-depth via jq inspection. Both layers verified.
* F5 expectedSkillsInstalled=999 → A13 fires with OR-logic showing
json=84, disk=84 vs expected 999
Deferred with tracking issues
-----------------------------
* Phase 06 worker_threads isolation — #407
* PR template CI enforcement — #408
* VERIFICATION.md + release-notes matrix — #409
* Chaos workflow for pathological inputs — #410
* Mirror init-corpus tarballs to GitHub Releases — #411
What is NOT verified
--------------------
* The CI workflows have never actually executed in a real Actions run.
YAML syntactically validated via js-yaml, job dependency graph traced,
but the first real execution is the first push of this branch.
* The canary on a real publish is unexecuted. First execution is the
first real release after merge.
Refs: #401, #407, #408, #409, #410, #411
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Prior to ADR-090, NativeHnswBackend wrapped @ruvector/router's
VectorDb, whose constructor unconditionally wrote a `vectors.db`
redb file to the current working directory — outside .agentic-qe/,
in a non-SQLite format, never read by anything in production
(its HNSW search returned wrong answers anyway). Users who upgrade
from a pre-#399 version will have an orphaned multi-MB file in
their project root.
`aqe init` now detects `./vectors.db` in the project root and logs
a one-time warning identifying it as stale from the @ruvector/router
era and suggesting `rm vectors.db` for cleanup.
The phase does NOT auto-delete the file. Per CLAUDE.md data
protection rules, AQE never overwrites, replaces, recreates, or
removes any .db file without explicit user confirmation. Users who
want to clean up run `rm vectors.db` themselves.
Three new unit tests in `tests/unit/init/phases/database-phase.test.ts`:
- asserts the warning fires when `./vectors.db` is present
- asserts the warning does not fire when it is absent
- asserts the phase never calls fs.unlinkSync or fs.rmSync,
regardless of whether vectors.db exists (data protection guard)
Refs #399, ADR-090.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hook commands using relative paths (node ./dist/...) or git rev-parse
broke when invoked from subfolders. Adopted the CLAUDE_PROJECT_DIR
pattern (sh -c 'exec node "${CLAUDE_PROJECT_DIR:-.}/..."') which
Claude Code sets to the project root.
Also: removed ruflo/claude-flow permissions from _aqePermissions
injected by aqe init — only AQE-specific entries belong in user
settings. Fixed init-wizard-hooks.ts missing union-merge for
permissions (#362 parity).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hook commands generated by `aqe init` embedded the host machine's absolute
path as a fallback, making settings.json non-portable across machines.
Since Claude Code runs hooks with cwd=project root and the helpers already
use __dirname internally, simple relative paths are sufficient.
Also fixes stale test that asserted on old `permissions` key renamed to
`_aqePermissions` in #362.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Hook deduplication: add brain-checkpoint.cjs and .claude/helpers/ patterns to
AQE_COMMAND_PATTERNS so mergeHooksSmart() correctly identifies them as AQE-owned
and removes old copies on re-init instead of accumulating duplicates.
Governance time budget: change budgetRemaining.timeMs to measure idle time since
last action instead of total elapsed since first action, and increase default
idleTimeoutMs from 5 to 15 minutes. Prevents false "Budget exhausted: time"
blocks on requirements_validate and coverage_analyze_sublinear.
Closes#344, closes#345
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Update ADRs 075-079 with implementation details, revise the
multi-language GOAP implementation plan, add test generators and
parser test fixtures for Go, Rust, Java, C#, Swift, Python.
Include skill validation schema updates and v2→v3 migration skill.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>