Bumps version + fleetVersion across package.json, plugin.json, and the
two skills-manifest.json copies. Adds CHANGELOG entry and release notes.
Includes one fix discovered during the release-prep e2e: stale-lock
recovery in shared-rvf-adapter's openOrCreateRvf ladder. The native
@ruvector/rvf-node binding leaves a `<file>.lock` artifact when a
process exits without calling close() — a behaviour that frequently
happens in CLI flows and that the previous always-create code path
masked. With open-or-create, those stale locks now surfaced as
`LockHeld` errors. The ladder now detects that, removes the stale
lock file, and retries open exactly once. E2E confirms RVF native
recovers cleanly across init → CLI invocation boundaries.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bundles fixes for issue #439 (Windows install) and Jordi RUFLO patches
P020 (RVF init) + P220 (hypergraph blindness).
Windows install (#439):
- Move hnswlib-node from dependencies to optionalDependencies so
installs no longer fail on Windows boxes without VS C++ Build Tools;
HnswAdapter already routes around a missing native binary at runtime.
- Replace HNSWIndex.ts top-level static `import hnswlib from
'hnswlib-node'` with a lazy require so package load survives the
optional dep being absent.
- README + preinstall.cjs: document toolchain requirement and disclose
that the JS fallback degrades to O(N) brute-force when @ruvector/gnn
is also unavailable (Windows default — no win32 prebuilds ship).
- ADR-090 amendment reconciling the move with ADR-081's optional-dep +
JS-fallback intent.
RVF init (Jordi P020):
- Root cause: shared-rvf-adapter and pattern-store factory always called
RvfDatabase.create(), which throws `0x0303 FsyncFailed` when the .rvf
file already exists from a prior init phase. Reproduced and confirmed
against both 0.1.7 and 0.1.8 native binaries — not a version
regression.
- Fix: race-tolerant open-or-create ladder (try open → fall back to
create → retry open on create-race) with dim() verification after
open. Mismatched-dim files are closed and the caller degrades rather
than corrupting silently.
Hypergraph (Jordi P220):
- Drop the original flushEdgesToHypergraph (wrote `call`/`extends`
edges that no production query reads — devil's-advocate review caught
it as dead infrastructure).
- Add HypergraphEngine.synthesizeTestCoverage that re-tags test files
type='test' and writes `covers` edges from each test file to
functions in the source files it imports. This is the shape
findUntestedFunctions / findImpactedTests actually filter on.
- E2E verified: untested correctly excludes covered functions;
impacted-tests returns the right test file.
Verification:
- `npm run build` clean
- 276/276 tests pass across HNSW, RVF, KG, coordinator, hypergraph
suites (5 new synthesizeTestCoverage tests; replaced 4 obsolete
flush tests)
- E2E on fresh project: aqe init clean, hooks stats shows
nativeAvailable: true, code index logs `Test coverage synthesized`,
hg untested + hg impacted return useful results
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
New file from patch 420 had no tests. Adds tests/unit/mcp/handlers/
trajectory-judge.test.ts with 7 cases:
• no-op when ANTHROPIC_API_KEY is unset (env-var gate)
• no-op when feedback column is absent (schema-presence check)
• happy path writes structured {quality, reasoning, improvement}
• quality clamped to [0,1] and fenced ```json``` parses correctly
• malformed responses don't write partial feedback
• batch cap at 5 rows even when more are unscored
• ANTHROPIC_BASE_URL is honored for the fetch target (patch 380 wiring)
Mocks getUnifiedMemory + globalThis.fetch — never reaches Anthropic.
Caught during pre-PR gap audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleTaskOrchestrate has two execution paths: workflowOrchestrator.
executeWorkflow() (line 388) when the task type maps to a registered
workflow, and queen.submitTask() (line 428) otherwise. The initial
patch 360 wiring only added patternHints to the submitTask payload —
the workflow branch routes through the same task_orchestrate request
but with a separate workflowInput object, so it was still routing
blind to the long-term pattern catalog.
Symmetric mapping now: both branches receive the same patternHints
array (patternId, name, description, confidence, similarity, canReuse)
derived from the single searchPatterns call we already do once per
request.
Caught during pre-PR gap audit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Caught during pre-PR gap audit: per CLAUDE.md release rule, version
bumps must update every hardcoded reference. Missed CHANGELOG.md and
docs/releases/README.md in the initial bump commit.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src/adapters/claude-flow/trajectory-bridge.ts:184 opened a separate
.agentic-qe/trajectories.db with its own `trajectories` schema —
unrelated to qe_trajectories on memory.db, and a direct violation of
the project's "Unified persistence system: all data goes through
SQLite (better-sqlite3) — one DB, one schema" rule from CLAUDE.md.
Symptom: feedback written via TrajectoryBridge.endTrajectory() never
showed up in any of the queries that read qe_trajectories.
persistTrajectory() now opens the unified memory DB via getUnifiedMemory(),
maps the bridge's columns onto qe_trajectories (started_at/completed_at
epoch ms → ISO datetime), and writes the same row everyone else reads.
Feedback column is added defensively in case TrajectoryTracker.ensureSchema
hasn't run yet on a fresh install.
Also rewords stale references in patches 420 (task-handlers.ts comment +
trajectory-judge.ts file header) that described the broken state as
"the hook-created rows are unreachable from the bridge" — with this fix
those rows are reachable, the bridge writes to the same table the judge
reads.
The trajectories.db path is kept in init.ts:330 reset --all list so
upgraded installs still get the legacy file cleaned up; comment notes
it's no longer a live writer target.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TrajectoryBridge writes feedback into a separate trajectories.db, so
the hook-created rows (cli-hook-post-task and friends) that land in
the unified memory.db are unreachable from it. Result: 719 hook-
created trajectories observed in one session, 0 with feedback.
Adds a fire-and-forget judge that runs at the end of handleTaskOrchestrate.
Picks the 5 most-recent rows where feedback IS NULL AND ended_at IS NOT
NULL, asks Claude Haiku for a structured {quality, reasoning,
improvement} verdict, and writes the JSON into qe_trajectories.feedback.
Quality is embedded in the JSON since the canonical schema has no
quality column — avoids a schema migration.
Opt-in behind AQE_TRAJECTORY_JUDGE=1 because each call costs real
money. Uses the proxy-aware base URL from patch 380. Bounded to 5
rows per task_orchestrate call so cost is predictable. PRAGMA-checks
the feedback column first (TrajectoryTracker may not have migrated
yet on a fresh install) — no-ops gracefully otherwise.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SQLitePatternStore.recordUsage() updates qe_patterns.{usage_count,
successful_uses, success_rate, quality_score} but is only invoked
through the HandleTaskOutcomeRecord MCP path. The hook flow that
already fans out per-pattern experience_applications rows (patches
160+300) never called it — quality_score stayed pinned at the
bootstrap value (~0.30 floor in observed data, with 88/89 patterns
never crossing into long-term).
Added inline SQL inside the persistTaskOutcome bridge loop, after the
experience_applications insert per pattern_id. Uses the same formula
as recordUsage(): confidence * 0.3 + min(usage_count/100, 1) * 0.2 +
success_rate * 0.5. Reads confidence from the pattern row first
because UPDATE expressions can't reference it directly.
Fail-soft per pattern — a deleted-but-referenced pattern_id won't
break the rest of the loop.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two sites in hooks-dream-learning.ts write captured_experiences rows
without computing an embedding:
• persistCommandExperience (sources: cli-hook-post-command,
cli-hook-post-edit) — the entry point for editing-hooks.ts and
command-hooks.ts post-command writes
• persistTaskOutcome (source: cli-hook-post-task) — patch 060 path
for the post-task hook
In a recent pulse 1203/1902 (63%) captured_experiences rows had
embeddings; ~85% of new writes since v3.9.19 came through these two
hook paths and contributed zero embeddings, leaving HNSW C cold for
hook-driven workloads. Patch 350 backfills on next boot, but new rows
were unsearchable until restart.
Both writers now fire a no-await IIFE after the INSERT (after txn()
in the post-task case) that imports computeRealEmbedding, computes
text=`${domain}: ${task}`.slice(0,512), and UPDATEs the row in place
through the same DB handle. Fail-soft — the row is already persisted,
so a missed embedding is recoverable by patch 350 on next boot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
handleTaskOrchestrate already calls reasoningBankService.getExperienceGuidance()
to surface relevant past trajectories (HNSW C). The catalog of consolidated
long-term patterns (HNSW A on qe_patterns) was never consulted on the request
path — only HNSW C influenced routing.
Adds a parallel searchPatterns(task, {limit:5, domain}) call and forwards
the top hits as `patternHints` on submitTask payload (patternId, name,
description, confidence, similarity, canReuse). canReuse=true when the
pattern is in the long-term tier.
Fail-soft: empty array on error so a misbehaving HNSW A path can't
break task orchestration.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
TrajectoryTracker.endTrajectory() writes the qe_trajectories row with
embedding=NULL and there's no follow-up worker that fills it in.
Hook-side trajectory inserts (cli-hook-post-task) leave the column
NULL too. With 719 hook-created rows and 0 embeddings observed, kNN
over historical trajectories collapses to "no candidates" and
ReasoningBank can't surface similar past runs.
Adds a second fire-and-forget IIFE in ExperienceReplay.initialize()
adjacent to patch 350, scoped to qe_trajectories. PRAGMA-checks the
embedding column first since TrajectoryTracker.ensureSchema() is what
adds it — the backfill no-ops gracefully if it hasn't run yet. Cap=200
per boot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ExperienceReplay.initialize() loaded the HNSW index from rows with
embeddings, but most hook-side INSERT paths (cli-hook-post-command,
patch-060-post-task, etc.) wrote captured_experiences rows without
calling computeRealEmbedding(). Result: HNSW C index stayed cold —
in one observed pulse 1203 of 1902 rows (63%) had embeddings; 85% of
new writes since v3.9.19 had none, so kNN recall converged on the
same handful of points.
Adds a fire-and-forget IIFE in initialize() after loadEmbeddingIndex()
that selects up to 200 ghost rows (embedding IS NULL AND
consolidated_into IS NULL), computes embeddings with the same
real-embeddings config the canonical writer uses, UPDATEs the row, and
adds the vector to the live HNSW index so it's immediately searchable
without restart. Cap=200/boot to bound work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The ReasoningBankService singleton + EnhancedReasoningBankAdapter +
HNSW A index population happened lazily on first task_orchestrate
call. Result: the first routing decision after a cold MCP start paid
the full HNSW build cost on the request path, and any racing call
returned before the HNSW A index was ready, falling through to
context-only matches.
Fire-and-forget IIFE after CrossPhaseHooks eager init so the warm-up
runs in parallel with the rest of MCP startup. No await — boot is not
blocked. Logs `[MCP] ReasoningBank prewarmed` on success or a WARNING
on failure (non-fatal; service will still lazy-init on first call).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
checkAndTriggerDream() dynamically imports createDreamEngine, which
opens a fresh DB handle via getUnifiedPersistence(). The hook-side
(patch 260) and worker-side busy_timeout pragmas never reached this
dynamically-imported path, so cycles ran with the platform default 0ms
timeout. Under concurrent MCP-worker contention on the WAL write-lock,
SQLITE_BUSY hit immediately and cycles were marked failed. Observed
failure rate: 199/279 (71%) in a single session.
Aligns with the 60s asymmetric prioritization the hook subprocesses
already use — workers fail fast, dream cycles wait patiently.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Both Claude provider implementations hardcoded the api.anthropic.com
fallback when no baseUrl was passed in config, ignoring the
ANTHROPIC_BASE_URL environment variable that proxies (Claude Max Proxy,
LiteLLM, etc.) rely on. Result: AQE LLM calls bypassed configured
proxies and went straight to Anthropic.
Mirrors the env-var precedence already used by
context/compaction/llm-caller-adapter.ts:23.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The handler probed `getVectorCount` on the unified memory manager, but
the method is `vectorCount()` (unified-memory.ts:755). The mismatch made
the typeof check fail silently, so `aqe_health` returned hnswStats
with vectorCount stuck at 0 even when the vectors table had hundreds of
rows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ADR_DIR pointed at `implementation/adrs` but the ADR files actually live
under `docs/implementation/adrs`. Both the bash statusline and its CJS
helper looked in the wrong place, so adrCount was always 0 in the
statusline output. Updated both to the correct path.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comments referenced an external patch series identifier and
a contributor's first name. Those refs aren't useful to anyone reading
this repo on its own and rot quickly. Replaced with the underlying ADR
references (ADR-058 embedding-locality, etc.) where they apply.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hook subprocesses (`npx aqe hooks ...`) opened the shared memory.db with
the platform default 5s busy_timeout. Under contention from MCP-daemon
workers holding the WAL write-lock during dream-cycle / pattern-promotion
(which can block writers for several seconds), hook writes failed with
SQLITE_BUSY.
Added `applyHookBusyTimeout(db)` in hooks-shared.ts that runs
`PRAGMA busy_timeout = 60000` on a connection. Hook subprocesses are
short-lived, so the longer timeout is bounded to that lifetime and
doesn't affect the MCP daemon's own connections.
Workers in MCP keep the platform default (5s) so they fail fast and
retry on the next tick, yielding the lock to hooks under contention —
the asymmetric prioritization ADR-001 Option C requires.
Wired at every hook handler db-open site:
• task-hooks.ts pre-task
• command-hooks.ts post-command
• routing-hooks.ts route persistence
• hooks-dream-learning.ts (5 db handle sites incl. persistTaskOutcome,
updateHookRouterQValue, updateRoutingOutcomeQuality)
Also bumps the busyTimeout in createHybridBackendWithTimeout from 5000
to 60000 so HybridMemoryBackend consumers in hook subprocesses get the
same patient timeout.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two related fixes in HypergraphEngine.buildFromIndexResult — the path
that turns a code index result into hypergraph_nodes/_edges rows.
1) Phase 2 (entity nodes) now also writes a `contains` edge from each
file node to each contained entity (function, class, interface, etc).
Previously only Phase 3 imports edges were persisted, so
hypergraph_edges held import edges only and queries like
`findUntestedFunctions`, `findImpactedTests`, `findCoverageGaps`
returned empty regardless of indexing activity.
2) Phase 3 (import edges) now resolves relative import specifiers
(`./foo`, `../bar/baz`) against the source file's directory and
probes common TS/JS/Python extensions plus `/index.ext` for
directory imports. Previously a relative import never matched any
hypergraph_nodes row and was silently skipped — only bare-module
imports could match, which excluded most intra-repo imports.
Schema: extends EdgeType with 'contains' and adds it to the stats
aggregator's Record initializer so getStats() compiles.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
task-executor.ts:445 hardcoded `qualityScore: success ? 0.8 : 0.2` when
recording routing outcomes via QualityFeedbackLoop. That produced only
two distinct quality_score values across all task outcomes, defeating
the point of treating quality_score as a learnable signal.
Replaced with the same 6-dim formula used by the post-task hook UPDATE
path: 0.25 * success + 0.325 baseline + 0.10 * duration_tier (where
duration_tier is a stepped function of execution time). Yields 6+
distinct quality bands per success/fail × duration combination.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CrossPhaseHooks singleton in hooks/cross-phase-hooks.ts is created on
first use, but its initialize() (config-load + memory wiring) only runs
when explicitly called. Result: under sustained queen-event-handlers
traffic, kv_store / qcsd-memory rows did not grow because the executor's
config was never parsed.
Calling getCrossPhaseHookExecutor().initialize() right after the MCP
server is Ready means hook events fire through a fully-initialized
executor from the first request.
Also drops the patch-source attribution from the existing patch-250
comment block to keep comments evergreen for downstream readers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires task-hooks.ts both
sides:
PRE-TASK now emits and persists:
• selectedPatternIds: top-5 pattern UUIDs from routing.patterns (050)
• historicalBest: agent + avgQuality + n from past success=1 rows (090)
• priorVerdicts: kv_store namespace='verdicts' last 7d (100)
• estimatedTokenSavings: SUM(average_token_savings) for selected
patterns from qe_patterns (300)
• lowConfidence: confidence < 0.5 flag (320)
• bridgeKey: hash of description; identifies the kv_store task-bridge
entry written for post-task to consume (160 + 280)
• routing_outcomes sentinel row (success=0, quality_score=-1) for
post-task UPDATE (150 split-write)
The kv_store task-bridge entry payload now carries the full structural
state (taskType, priority, domain, complexityBucket) plus
estimatedTokenSavings — the q-learning integration in post-task derives
state_key from this without recomputing.
POST-TASK now wires three helpers (defined in hooks-dream-learning.ts):
• persistTaskOutcome — writes captured_experiences + per-pattern
experience_applications + qe_trajectories single+multi-step +
dream_insights.applied bump (060/110/120/160/180/300)
• updateRoutingOutcomeQuality — UPDATEs the sentinel from pre-task
with 6-dim outcome quality (150)
• updateHookRouterQValue — Bellman update at the structural
state_key from the bridge (280)
End-to-end smoke verified: pre-task writes sentinel, post-task UPDATEs
to quality=0.635 (0.25*1 + 0.325 + 0.10*0.6 for 850ms duration),
lowConfidence flag preserved across the UPDATE.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
routing-hooks.ts wrote routing_outcomes with quality_score = routing
confidence — semantically inconsistent with the rest of the hook flow,
which now treats quality_score as "outcome quality after task ran"
(6-dim formula). Routing confidence stays in decision_json where it
belongs.
Now writes a sentinel (success=0, quality_score=-1) so post-task can
UPDATE it with the actual outcome quality. lowConfidence flag (patch
320) is captured in decision_json and the error column for diagnostic
visibility.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Test outcome wiring (patches 070, 170, 310 — all in command-hooks.ts post-command:
Patch 310: persistCommandExperience was called unconditionally for every
Bash command, flooding captured_experiences with noise from git/ls/etc.
The framework regex `isTestCmd` was computed at line 200 but never gated
the persistence call. Now gated to test/build/lint commands. The original code
observed 875+ noise rows at 8.2% success rate diluting pattern signal —
this closes that flood.
Patch 070: when a recognized test framework runs, INSERT a test_outcomes
row capturing framework, language, pass/fail, and execution time.
Provides raw signal for the 6-dim quality calculator.
Patch 170: when coverage/coverage-summary.json is present (Istanbul),
parse and INSERT a coverage_sessions row with after_lines/branches/
functions, reusing the prior session's after_* values as before_* so
delta is calculable.
reasoningBank.recordOutcome remains unconditional — it's the broader
metric path, not the pattern-specific captured_experiences write.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
PatternStore.initializeHNSWInternal() had the embedding-load loop only
in the legacy fallback branch. When useUnifiedHnsw=true (our default per
ADR-071), the function returned at the unified-bridge branch BEFORE the
load loop ran. Result: existing qe_pattern_embeddings rows were never
ingested into the unified HNSW backend on first init; routing fell back
to context-only matches (cosine score 0).
PatternStore.getStats() reported the pre-init zero state because it
explicitly skipped lazy-loading. So `aqe hooks stats --json` returned
nativeAvailable:false, vectorCount:0 even on a populated store.
Patch 030: extracted loadEmbeddingsIntoHNSW() and call it from both the
unified-bridge path AND the legacy path. Cap respected (maxElements).
Logs `[PatternStore] Loaded N embeddings into HNSW (skipped M)`.
Patch 040: getStats() now awaits ensureHNSW() before reading state, so
stats reflects the actual HNSW backend (paying the bounded lazy-init
cost — the contract a stats command should fulfill).
Together these close the unified-HNSW boot gap that left routing
defaulting to context-only matches with vectorCount:0.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three live writers in our codebase write to qe_patterns:
1. learning/sqlite-persistence.ts:497 SQLitePatternStore.storePattern (canonical)
2. cli/.../hooks-dream-learning.ts:305 consolidateAgentPatterns (bypass)
3. workers/.../learning-consolidation.ts:412 createPatternsFromCandidates (bypass)
Only the canonical writer paired the row with a qe_pattern_embeddings
row (via computeRealEmbedding inside RealQEReasoningBank.storeQEPattern).
Writers 2 & 3 produced "ghost" patterns: rows with no vector that load
into in-memory cache but stay invisible to HNSW pattern recall.
Observed symptom: vectorCount: 0 reported even after 79 patterns
load, because 10 ghosts had no embedding.
Adds src/learning/embed-and-insert-pattern.ts with ensurePatternEmbedding(),
a fail-soft helper that:
- Builds text = `${name} ${description} ${tags.join(' ')}`
- Calls computeRealEmbedding(text) (Xenova/all-MiniLM-L6-v2, 384-dim)
- Writes qe_pattern_embeddings row alongside the existing qe_patterns INSERT
- Logs at debug level on failure (pattern row already persisted —
embedding can be backfilled later)
Routes both bypass writers through the helper so all 3 writers speak
the same embedding-config "lingua franca" (per ADR-058 locality).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Claude Code dispatches subagent tools as both `Task` (legacy) and `Agent`
(current). Our PreToolUse/PostToolUse hook matchers only matched `^Task$`,
so AQE pre-task and post-task hooks never fired on real Agent dispatch.
Result: 0 deltas in captured_experiences / experience_applications /
qe_trajectories on actual subagent invocation.
Updated:
- src/init/phases/07-hooks.ts (template that ships to users via aqe init)
- .claude/settings.json (this project's working config)
Empirically verified by upstream Tier-A e2e: pre-fix 0 deltas, post-fix
full delta set across patches 060/110/120/130/150/180/190.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
src/mcp/entry.ts:211 explicitly overrode getDaemon({autoStart:false}),
neutering workerManager.startAll() — the only call path that schedules
per-worker setInterval timers. The "[MCP] Background workers started
(11 workers)" log reads totalWorkers (registered count), not scheduled
count, so the 11 background workers register but never tick in any
process spawned from this entry (Claude Code MCP, start-daemon.cjs,
manual node bundle.js).
DEFAULT_CONFIG.autoStart=true (workers/daemon.ts:53) is the canonical
default. The shutdownDaemon() call at entry.ts:44 already uses the
default; this aligns the startup path with it.
Diagnosed via external investigation — see ADR-001
"AQE workers WAL contention strategy". gitnexus impact: LOW risk,
1 affected caller.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mirrors the ruvnet/ruflo pattern (verified live in ~/.claude/plugins/marketplaces/ruflo/):
- .claude-plugin/marketplace.json — required manifest that lists agentic-qe-fleet
with relative source path, making this repo a valid Claude Code marketplace
- .claude-plugin/plugin.json — top-level metadata (engines, MCP server,
keywords) so the marketplace itself can be referenced
- .claude-plugin/README.md — picker-surface description with install command
and plugin overview
- README.md — new "Claude Code Plugin (Alternative Install)" section showing
/plugin marketplace add path and plugin-vs-aqe-init comparison; removed the
"once published" caveat now that the marketplace files exist
Users can now install via:
/plugin marketplace add proffesor-for-testing/agentic-qe
/plugin install agentic-qe-fleet
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per /release skill §4b — release notes file at docs/releases/v3.9.17.md
plus index row at the top of docs/releases/README.md.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The shipped `aqe init` template wired the UserPromptSubmit hook to
`npx agentic-qe hooks route --task "$PROMPT" --json`, but Claude Code
never exposed `$PROMPT` as an env var — only stdin event JSON. The
shell expanded `"$PROMPT"` to `""`, so every prompt routed as empty
and `routing_outcomes.task_json` was persisted as `{"description":""}`,
silently breaking the routing learning loop.
- routing-hooks.ts: demote --task to optional, read stdin (500ms
timeout) and extract prompt/user_prompt/command/tool_input.{prompt,
description}; explicit error when neither source has content
- 07-hooks.ts and init-wizard-hooks.ts: drop --task "\$PROMPT", let
the CLI read stdin directly. isAqeHookEntry still matches both
shapes, so existing projects upgrade on `aqe init --upgrade`
- tests/unit/cli/commands/hooks-routing.test.ts: 10 unit tests
covering all event shapes, fallback ordering, malformed JSON, and
empty-field skip
Verified end-to-end: piped a Claude Code-shaped event into a fresh
project's configured route command and confirmed task_json captures
the real prompt instead of "". Backward-compat manual --task usage
preserved.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>