1418 Commits

Author SHA1 Message Date
Dragan Spiridonov 84efff4397 Merge pull request #437 from proffesor-for-testing/fix/jordi-wire-gap-patches
chore(release): v3.9.19
v3.9.19
2026-05-05 19:55:53 +02:00
Dragan Spiridonov ec451c419f chore(release): bump version to v3.9.19 2026-05-05 16:53:24 +00:00
Dragan Spiridonov ae50f7d3f6 fix(statusline): correct ADR directory path
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>
2026-05-05 16:22:38 +00:00
Dragan Spiridonov 9e95f17a91 chore(comments): drop external patch-source attributions
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>
2026-05-05 16:20:07 +00:00
Dragan Spiridonov 39febbb35d fix(hooks): apply 60s busy_timeout pragma at hook DB opens (ADR-001 Option C)
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>
2026-05-05 16:20:07 +00:00
Dragan Spiridonov 4f000463a8 fix(code-intelligence): persist file→entity contains edges + resolve relative imports
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>
2026-05-05 16:20:07 +00:00
Dragan Spiridonov 39020c83a7 fix(coordination): use 6-dim quality formula in task-executor outcome
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>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov 9b489a346b fix(mcp): eagerly initialize CrossPhaseHooks at startup
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>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov d5051576b4 feat(hooks): wire pre/post-task to full experience + routing pipeline
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>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov 64407db60c fix(hooks): align aqe hooks route writer with split-write semantics (patch 150)
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>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov 3f434723a3 feat(hooks): persistTaskOutcome + Q-learning + routing-quality helpers
New helpers in hooks-dream-learning.ts (re-exported via hooks-shared.ts):

persistTaskOutcome — rolls related fixes
into one transaction:
  • captured_experiences row (source='cli-hook-post-task')
  • experience_applications base row + 1 per pattern_id from bridge
  • Reads + deletes kv_store namespace='task-bridge' (one-shot)
  • Distributes estimatedTokenSavings/N across per-pattern apps (300)
  • Single-step qe_trajectories row (120)
  • Multi-step stitch from siblings sharing taskId in last 1h (180)
  • dream_insights.applied += 1 for top-3 actionable rows on success (110)
Returns experienceId, qualityScore, bridge payload, and counters for
the q-learning integration to consume.

updateHookRouterQValue — patch 280: Bellman update with proper column
semantics per ADR-061/087:
  • algorithm='q-learning' (not 'asymmetric-hebbian'; that label is
    for ReasoningBank confidence updates, NOT Q-learning state-action)
  • agent_id='aqe-hook-router' (per-instance partition; doesn't collide
    with canonical RuVector q-router agent_id='q-router')
  • state_key='${taskType}|${priority}|${domain}|${complexityBucket}'
    (structural; verbatim q-learning-router.ts:591)
  • action_key=agent name; id schema 'q-learning:aqe-hook-router:...'
  • Q ← Q + α(r + γ·max_a' Q(s',a') − Q), α=0.1, γ=0.9
  • Asymmetric reward: success +0.1, failure −1.0

updateRoutingOutcomeQuality — patch 150: UPDATEs the most-recent
routing_outcomes row with quality_score=-1 sentinel, applying the
6-dim formula 0.25*success + 0.325 + 0.10*duration_tier. Prefers rows
matching the actual used_agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov c9931174ce fix(hooks): gate noise captures + add test_outcomes/coverage_sessions writers
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>
2026-05-05 16:20:06 +00:00
Dragan Spiridonov 8867095888 fix(learning): load embeddings into unified HNSW + lazy-init for stats
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>
2026-05-05 16:20:05 +00:00
Dragan Spiridonov c78011a1d4 fix(learning): unify pattern-embedding writers
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>
2026-05-05 16:20:05 +00:00
Dragan Spiridonov fff194cc6a fix(hooks): expand Task matcher to ^(Task|Agent)$
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>
2026-05-05 16:20:05 +00:00
Dragan Spiridonov 9e4f23e3bc fix(mcp): restore getDaemon() autostart default
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>
2026-05-05 16:20:04 +00:00
Dragan Spiridonov 78ff75f462 Merge pull request #436 from proffesor-for-testing/release/v3.9.18
chore(release): v3.9.18 — 4 MCP fixes + agentic-qe-fleet plugin
v3.9.18
2026-04-30 17:24:16 +02:00
Dragan Spiridonov 8b4e54fd8e docs(plugin): publish as Claude Code marketplace + add README install section
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>
2026-04-30 14:50:11 +00:00
Dragan Spiridonov cf1072370f docs(readme): add agentic-qe-fleet plugin install + usage section
User-facing instructions for the plugin shipped in v3.9.18:
- Install paths (local checkout via --plugin-dir, marketplace once published)
- What's bundled (11 agents, 9 commands, 9 skills, auto MCP)
- Usage examples (slash commands, Task tool agent invocation)
- Plugin vs aqe init comparison table

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:46:19 +00:00
Dragan Spiridonov 25b6bd732a chore(release): v3.9.18
Bundles 4 MCP bug fixes + new agentic-qe-fleet plugin.

Fixed:
- coverage_analyze_sublinear and other domain MCP tools blocked by governance
  after 1-2 calls (synthetic token-slope estimate replaced with 0; throttle
  decisions are no longer treated as hard blocks)
- test_generate_enhanced framework=jest produced unrunnable tests (now emits
  @jest/globals imports instead of invalid 'jest' package import)
- generated test imports referenced /tmp/aqe-temp-* paths (importPathOverrides
  plumbed through IGenerateTestsRequest end-to-end)
- qe/coverage/gaps error message misled callers about which input failed
  (distinguishes coverageFile-empty vs autodiscovery-miss)

Added:
- test_generate_enhanced schema exposes framework, filePath, coverageGoal,
  aiEnhancement, detectAntiPatterns parameters
- agentic-qe-fleet Claude Code plugin (11 agents, 9 commands, 9 skills)
- scripts/smoke-test-fixes.sh and scripts/plugin-load-test.sh

Verification:
- 1301 MCP unit tests pass (zero regressions)
- 734 targeted unit tests pass across changed code
- aqe init --auto in fresh project succeeds (85 skills, 60 agents)
- End-to-end MCP smoke test passes 5/5
- qe-coverage-specialist agent re-spawn confirms NO_GOVERNANCE_BLOCK
- Plugin structural validation: 14/14 checks pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 14:39:10 +00:00
Dragan Spiridonov 90dd17218a Merge pull request #435 from proffesor-for-testing/fix/userpromptsubmit-stdin-routing
fix(hooks): UserPromptSubmit reads stdin event JSON for routing (v3.9.17)
v3.9.17
2026-04-27 08:54:58 +02:00
Dragan Spiridonov 087caf41dd docs(release): add v3.9.17 release notes
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>
2026-04-27 06:28:12 +00:00
Dragan Spiridonov 5f711b5b3e fix(hooks): UserPromptSubmit reads stdin event JSON for routing (v3.9.17)
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>
2026-04-27 06:19:05 +00:00
Dragan Spiridonov 19e8796dcb Merge pull request #434 from proffesor-for-testing/dependabot/npm_and_yarn/npm_and_yarn-e9ce4f7be9
chore(deps): bump uuid from 9.0.1 to 14.0.0 in the npm_and_yarn group across 1 directory
2026-04-25 16:28:41 +02:00
dependabot[bot] 099b1588dc chore(deps): bump uuid in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [uuid](https://github.com/uuidjs/uuid).


Updates `uuid` from 9.0.1 to 14.0.0
- [Release notes](https://github.com/uuidjs/uuid/releases)
- [Changelog](https://github.com/uuidjs/uuid/blob/main/CHANGELOG.md)
- [Commits](https://github.com/uuidjs/uuid/compare/v9.0.1...v14.0.0)

---
updated-dependencies:
- dependency-name: uuid
  dependency-version: 14.0.0
  dependency-type: direct:production
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-24 12:50:56 +00:00
Dragan Spiridonov 9ed1bda600 Merge pull request #433 from proffesor-for-testing/feat/brain-search-and-diff
chore(release): v3.9.16
v3.9.16
2026-04-24 14:49:25 +02:00
Dragan Spiridonov baecaf9865 chore(release): bump version to v3.9.16
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 12:20:33 +00:00
Dragan Spiridonov 2a6036c2b8 feat(completions): add brain + upgrade to bash/zsh/fish/PowerShell
Tab completion was not generating suggestions for the newly shipped
\`aqe brain diff\`, \`aqe brain search\`, or \`aqe upgrade\` commands on
any shell. This commit wires them in so users with installed completion
scripts get the same discoverability as for older commands.

Changes:
  - src/cli/completions/index.ts
    * COMMANDS map: add \`brain\` (with subcommands export/import/info/
      diff/search/witness-backfill) and \`upgrade\` (with --json/--strict).
    * Bash generator: extend top-level commands string and add case
      branches for \`brain\`/\`upgrade\`, with a \`--domain\` completion
      sourced from the existing domains list for \`brain search\`.
    * Zsh generator: add entries to the \`commands\` array and two new
      subcommand dispatch cases.
    * Fish generator: add top-level completions plus per-subcommand
      guards using __fish_seen_subcommand_from.
    * PowerShell \$AQE_COMMANDS map: add both entries.
  - tests/unit/cli/completions.test.ts
    * New assertions: COMMANDS.brain + .upgrade shape, and that all four
      shell generators contain both names.

Also cleaned up 22 pre-existing no-useless-escape lint errors in the
same file (unnecessary \\$ inside JS template literals — \$ alone is
already literal). Kept \${files[@]} because ${ would trigger JS
template interpolation. Output of generateBashCompletion() is
byte-identical after this cleanup; verified via 'bash -n' syntax check.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 10:02:33 +00:00
Dragan Spiridonov abdc867853 feat(cli): add aqe upgrade native-binding advisor command
Closes #383 item 2.

`aqe upgrade` is a read-only advisory command that detects which optional
native bindings load on this platform and recommends installs / flag
changes. It does NOT mutate feature flags, env vars, or config files — it
only tells the user what they'd gain.

Detects:
  - better-sqlite3, web-tree-sitter (required)
  - hnswlib-node (canonical HNSW, default since ADR-090)
  - @ruvector/rvf-node (persistent HNSW + RVF export)
  - @ruvector/solver-node (sublinear PageRank)
  - @ruvector/attention (Flash Attention SIMD)
  - @ruvector/gnn (GraphMAE acceleration)

Output:
  - Platform + AQE version
  - Per-package status (loaded / missing / required-missing) with the
    first line of any load error
  - Current flag state (after RUVECTOR_* env overrides)
  - Recommendations: install hints for missing optionals, warnings when
    a RUVECTOR_* forces a flag on but the native is missing
  - Summary line with required-OK status and optional loaded/total

Flags:
  --json      machine-readable report
  --strict    exit 1 if any recommended optional native is missing

Exit codes:
  0  all good (or --strict not passed with optionals missing)
  1  --strict and ≥1 optional missing
  2  a required native failed to load

Tests:
  - 17 unit tests (tests/unit/cli-upgrade.test.ts) exercise pure
    detection / recommendation / exit-code logic with injected probes
  - 5 integration tests (tests/integration/cli/upgrade-command.test.ts)
    spawn the built CLI and assert the JSON contract, human sections,
    exit-code contract, and RUVECTOR_* env override plumbing

Also fixes a pre-existing lint error (unused originalConsoleInfo in
src/cli/index.ts) per the edit-touched-file convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 08:22:33 +00:00
Dragan Spiridonov e306b81caf feat(brain): add brain diff and brain search CLI commands
Closes the remaining actionable items in #332:

- C-10: `aqe brain diff <a> <b>` compares two brain exports.
  * Manifest-level always (version, checksum, per-table record counts,
    domain delta). Works for JSONL and RVF exports.
  * Record-level added/removed/changed IDs when both sides are JSONL.
    Append-only tables (witness_chain, qe_pattern_usage) report counts
    only — their dedup columns are identity.
  * Flags: --table <name>, --verbose, --json. Exit 0 when identical,
    1 on any difference.

- C-09: `aqe brain search -i <export>` runs offline filtered search.
  * Default table qe_patterns; `--table <name>` targets any of the 25.
  * Filters: --domain (repeatable), --pattern-type, --since, --until,
    --query (substring across name + description, case-insensitive),
    --limit (default 20), --json.
  * Returns {totalScanned, totalMatched, hits, truncated}. Exit 3 when
    no rows match (distinct from errors).

C-02, C-03, C-05, C-13 are won't-fix and documented on #332 at close.

Tests: 23 new unit tests covering identical/different exports, PK vs
append-only table semantics, domain deltas, combined filters, date
ranges, limits, and error paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 07:15:33 +00:00
Dragan Spiridonov e5fdae55dc Merge pull request #431 from proffesor-for-testing/feat/adr-091-qe-browser-complete
chore(release): v3.9.15
v3.9.15
2026-04-22 15:05:31 +02:00
Dragan Spiridonov 70e900e0f9 feat(adr-091): promote to Implemented + trust_tier 3
Promotion gate documented in ADR-091 (first green CI run of
test-qe-browser.yml against real Vibium) cleared at commit
fae9fd30 / workflow run 24776640163 on 2026-04-22:

- Unit tests (CommandEvalRunner + primitives): success
- Smoke test (real Vibium + httpbin): success
- aqe eval run against real Vibium: success

Changes:
- .claude/skills/qe-browser/SKILL.md: trust_tier 2 -> 3
- ADR-091 status: In Progress -> Implemented, gate marked cleared
- v3-adrs.md index row: In Progress -> Implemented, counts
  updated 79->80 Implemented, 7->6 In Progress

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:54:51 +00:00
Dragan Spiridonov fae9fd30dc fix(qe-browser): add --headless to yaml eval setup steps
The smoke job is green (--no-sandbox wrapper works), but the eval
job failed because `aqe eval run` dispatches the yaml's setup
steps verbatim, and those steps used `vibium go URL` without the
global `--headless` flag. On a CI runner with no display that
tries to open a visible Chrome window and fails.

Six setup steps were affected (tc001, tc002, tc003, tc008, tc010,
tc011 equivalents). Tc006/tc007 already had --headless from commit
23416e15. Smoke-test.sh was unaffected because it prefixes
`vibium --headless` for all its invocations.

No logic changes; aligns yaml with smoke-test.sh convention.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:49:46 +00:00
Dragan Spiridonov 2d35843ce8 fix(ci): wrap Vibium's Chrome to force --no-sandbox on GitHub runners
Diagnostic output from commit a4cb96c5 confirmed the residual error
is chromedriver's classic "HTTP 500 create session" — Chrome's
setuid sandbox can't initialize in the GitHub Actions runner
environment (no user namespaces, restricted proc).

Fix: after `vibium install` lays down Chrome for Testing into
~/.cache/vibium/chrome-for-testing/<version>/chrome, replace the
binary with a tiny bash wrapper that execs the real Chrome with
--no-sandbox --disable-dev-shm-usage appended. The wrapper lives
inside the ephemeral runner cache; nothing is shipped to users.

This is the standard workaround used by every Chromium-based CI
runner (Puppeteer, Playwright, chromedp all do the equivalent).

Also removed the diagnostic step added in a4cb96c5 (job green is
the signal now).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:43:27 +00:00
Dragan Spiridonov a4cb96c524 diag(ci): probe residual Vibium failure post-install (not a fix) 2026-04-22 11:38:41 +00:00
Dragan Spiridonov 8a90c10f18 fix(ci): run vibium install to download Chrome for Testing + chromedriver
Root cause confirmed via diagnostic (commit 0f810e3f): Vibium's
auto-launch fails with "failed to create session: HTTP 500"
because the npm package ships only the Go binary — the actual
browser and chromedriver are downloaded on demand by
`vibium install`. The earlier VIBIUM_BROWSER_PATH fix was a red
herring: Vibium drives its own bundled Chrome for Testing over
WebDriver, not the runner's system google-chrome.

Reverted:
- "Discover browser path for Vibium" step (both jobs)
- "DIAGNOSTIC — probe Vibium failure" step (smoke job)

Added:
- "Download Chrome for Testing + chromedriver" step running
  `vibium install` after the npm install, in both smoke and eval
  jobs. This matches the documented setup flow for Vibium.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:34:49 +00:00
Dragan Spiridonov 0f810e3f71 diag(ci): probe Vibium smoke failure — not a fix
TEMPORARY diagnostic step to surface the real error Vibium's
wrapper is hiding behind "stdout: null, stderr: null". Runs a
battery of probes with continue-on-error so the step never blocks
the smoke job itself, and prints enough to diagnose whether the
Go binary is crashing on sandbox, /dev/shm, missing lib, or
something else.

To be reverted in the next commit once the root cause is known.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:29:29 +00:00
Dragan Spiridonov 956fd26ef1 fix(ci): discover browser path for Vibium on GitHub runners
Root cause of PR #431 smoke/eval job failures: Vibium installs
correctly (reports v26.3.18) but exits 1 when it tries to drive a
browser, because the runner has no Chrome on its default PATH for
Vibium's discovery — smoke-test tc002/tc004/tc006-8/tc010 all crash
with `vibium eval exited 1` in child_process, while tests that
don't need a real navigation (tc003, tc005, tc011) pass.

Fix: added a preflight step in both `smoke` and `eval` jobs that
probes the same prioritized list the installer uses
(google-chrome → google-chrome-stable → chromium-browser → chromium),
exports VIBIUM_BROWSER_PATH via $GITHUB_ENV, and fails loudly if
none is found. Ubuntu-24.04 runners have google-chrome preinstalled,
so this resolves without adding any third-party action.

This mirrors the remediation that `aqe init --auto` prints on Linux
ARM64 hosts — the mechanism is identical, just applied on AMD64 CI
where Vibium's auto-detection also fails.

Validated with actionlint 1.7.1 (clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 11:14:04 +00:00
Dragan Spiridonov fe568cfe45 fix(ci): set least-privilege permissions on qe-browser workflow
Resolves 3 CodeQL findings (205/206/207) from github-advanced-security
on PR #431: "Workflow does not contain permissions".

Added workflow-level `permissions: contents: read`. The workflow only
reads the repo and uploads an artifact — no writes to code, issues,
PRs, or releases. The artifact upload uses the ephemeral job token,
not the `actions: write` permission.

Validated with actionlint 1.7.1 (clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 09:18:20 +00:00
Dragan Spiridonov 5f58b279d6 chore(release): bump version to v3.9.15
ADR-091 (qe-browser) promoted to Implemented, trust tier 3:
- new aqe eval run --skill <skill> CLI executes skill eval YAMLs as real CommandRunner evals
- aqe init --auto on Linux ARM64 now prints an exact VIBIUM_BROWSER_PATH hint
- docs/guides/qe-browser-getting-started.md for new users
- .github/workflows/test-qe-browser.yml gates PRs with vitest + smoke + eval jobs
- qe-browser eval YAML reworked to use httpbin.org (no fixture server needed)
- Vibium pin comment corrected: ^26.3.18 is a major-line pin, not major.minor

No breaking changes. CLI surface additions only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-22 07:17:14 +00:00
Dragan Spiridonov cda9fb3c05 fixup(adr-091): address devil's-advocate review
Single commit covering the 5 concrete findings from the qe-devils-advocate
review. Does not change the underlying CommandEvalRunner design or the 5
primitives — only the claims around them and the fixture-server hack the
yaml had silently inherited.

1. Revert promotion (G4): trust_tier 3 -> 2 in SKILL.md; ADR-091 status
   Implemented -> In Progress (2026-04-21); v3-adrs.md index row updated
   to reflect the same; counts 80->79 Implemented, 6->7 In Progress.
   The promotion gate is now documented on ADR-091: first green CI run
   of test-qe-browser.yml (unit + smoke + eval against real Vibium)
   before trust_tier 3 / Implemented are restored.

2. Fix yaml fixtures (G3): tc006/tc007 were referencing
   http://localhost:8088/qe-browser/SKILL.md.html which required an
   unstarted fixture server, so `aqe eval run --skill qe-browser` would
   have failed on a fresh machine. Rewrote both to use https://httpbin.org/html
   (matching scripts/smoke-test.sh tc006/tc007). Dropped tc009
   (poisoned-page check-injection) from the yaml — it needs the local
   serve-skills.js fixture which is out of scope for CommandEvalRunner.
   Documented as an explicit GAP comment in the yaml, noted that
   check-injection.js's severity path is unit-tested at
   tests/unit/scripts/qe-browser-check-injection.test.ts. yaml test_cases
   now 11 -> 10.

3. Correct pin comment wording (G1): the previous comment claimed
   ^26.3.18 was a "major.minor-line pin" — that's factually wrong under
   npm semver (^26.3.18 accepts 26.99.x). Rewritten to accurately
   describe the intent: major-line pin that blocks 27.0+ while
   allowing auto-uptake of 26.x security patches and additive features,
   with scripts/smoke-test.sh as the belt-and-suspenders API contract
   gate. The spec itself stays ^26.3.18 — the correct choice for an
   end-user tool under semver assumptions.

4. Add eval CI job (G2): third job in test-qe-browser.yml runs
   `aqe eval run --skill qe-browser --model claude-3.5-sonnet` against
   a real Vibium install after `npm run build`. This is the
   end-to-end proof that CommandEvalRunner dispatch works that the
   previous unit tests (all mocked CommandRunner) could not provide.
   Uploads the eval JSON as a CI artifact for post-run inspection.
   actionlint clean.

5. Scrub stray files (G5): git rm the coherence-comparison artifacts
   from docs/reports/ that were swept in by a prior `git add -A`, and
   added a focused pattern to .gitignore (not a blanket docs/reports/
   ignore) so future sessions can't repeat the mistake while intentional
   reports can still be committed.

Tests: 51 relevant tests still pass (3 files: command-eval-runner,
command-eval-runner-qe-browser, browser-engine-installer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 14:09:37 +00:00
Dragan Spiridonov 23416e1577 feat(adr-091): promote to Implemented — trust_tier 3, platform hint, yaml/smoke unify
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>
2026-04-21 12:25:26 +00:00
Dragan Spiridonov 4739237cd8 feat(adr-091): migrations, getting-started doc, CI workflow
Closes GAP-03a, GAP-03b, GAP-06, GAP-07, and revises GAP-09.

- Rephrased Playwright references in enterprise-integration-testing and
  pentest-validation to point at qe-browser (batch/assert/check-injection)
  so the AQE fleet's canonical browser runner is the recommended tool.
- accessibility-testing: added qe-browser / a11y-ally as the preferred
  axe-core integration path ahead of the Playwright example; Playwright
  kept as a documented fallback for Firefox/Safari or existing suites.
- GAP-09 revised: visual-testing-advanced and compatibility-testing are
  intentional hybrids, not unfinished migrations. visual-testing-advanced
  already labels its Playwright section "LEGACY: fallback"; compatibility
  -testing notes that Vibium's BiDi backend is Chrome-only at v26.3.x so
  Firefox/Safari coverage still needs Playwright. Both stay as-is.
- Added docs/guides/qe-browser-getting-started.md covering install,
  smoke check, eval run, and the 5 primitives with example CLI calls.
- Added .github/workflows/test-qe-browser.yml gating PRs that touch the
  skill, its installer, or CommandEvalRunner. Job 1 runs the full vitest
  sweep; Job 2 installs a pinned Vibium and runs smoke-test.sh against
  real httpbin fixtures.
- Workflow validated with actionlint 1.7.1 (clean).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 12:16:44 +00:00
Dragan Spiridonov 2fe2300c68 feat(adr-091): make qe-browser.yaml a runnable eval + pin Vibium
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>
2026-04-21 12:12:40 +00:00
Dragan Spiridonov 10d2ab823b Merge pull request #430 from proffesor-for-testing/fix/v3-9-14-p0-blockers
chore(release): v3.9.14 — security & supply-chain hardening
v3.9.14
2026-04-20 18:40:43 +02:00
Dragan Spiridonov 55afae9f62 fix(release): truly regenerate package-lock to include optional-chain transitives
Previous regen (commit 80c8186a) still failed CI `npm ci` with the same
EUSAGE / Missing packages error. Root cause: the optional dependency
chain @claude-flow/browser + @claude-flow/codex transitively requires
@claude-flow/cli, @ruvector/*, agentdb, agentic-flow, and a wide set of
transitive packages (~880 entries). My earlier local `npm ci
--ignore-scripts` and `npm install` sequence produced a lockfile that
was missing those entries — likely because a mid-sequence step resolved
optional packages via a platform filter that excluded what CI actually
needs.

This commit: clean `rm -rf node_modules package-lock.json && npm
install` followed by strict `npm ci` verification (no --ignore-scripts).
Result is a 17874-line lockfile (up from 15239) with all transitive
deps properly enumerated, and `npm ci` exits 0 cleanly from an empty
node_modules.

Verified:
- Fresh `rm -rf node_modules package-lock.json && npm install` → 1117
  packages resolved, lockfile stable at 17874 lines.
- `rm -rf node_modules && npm ci` → exit 0, no EUSAGE, no Missing errors.
- `node_modules/@claude-flow/cli` now present in lockfile as required by
  @claude-flow/browser's peer/dep chain.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 16:23:43 +00:00
Dragan Spiridonov 80c8186aa5 fix(release): regenerate package-lock.json to sync with package.json
CI (`npm ci`) on PR #430 failed with:

    npm error `npm ci` can only install packages when your package.json
    and package-lock.json or npm-shrinkwrap.json are in sync.
    npm error Missing: @claude-flow/cli@3.5.80 from lock file
    npm error Missing: @ruvector/attention@0.1.32 from lock file
    ... (dozens of transitive deps)

Root cause: when @claude-flow/guidance was moved from dependencies to
optionalDependencies, my local `npm install` ran in a Codespace where
@claude-flow/cli and related ruflo packages were already extraneous on
disk from a previous `claude mcp add ruflo` step. The npm install
reconciled node_modules to the new package.json but produced a lockfile
that assumed those extraneous packages were still available at resolve
time — so when CI did a clean `npm ci`, the lockfile claimed resolution
info that wasn't there.

Fix: remove node_modules + package-lock.json and regenerate from
package.json alone. New lockfile has +988 resolved packages (was missing
transitive deps pulled in by the optional @claude-flow/* chain) and
passes `npm ci --ignore-scripts` with exit 0.

Verified:
- `npm run build` — CLI + MCP bundles build
- `node dist/cli/bundle.js --version` → 3.9.14
- `npm audit --omit=dev` — 0 vulnerabilities (6 highs remain in dev-only
  eslint/typescript-eslint chains — separate backlog item)
- `npm ci --ignore-scripts` — exit 0, 988 packages resolved cleanly

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 16:02:36 +00:00
Dragan Spiridonov e7a78910d5 chore(release): bump version to v3.9.14
Security + supply-chain hardening closes five P0 release blockers from
the v3.9.13 QE audit. Shipped tarball drops 52% (19.9 MB → 9.6 MB),
15 critical runtime npm vulnerabilities are eliminated, and a
command-injection path in `aqe learning repair` is closed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 14:14:14 +00:00
Dragan Spiridonov 9d3b514ee2 fix(v3.9.14): second P0/P1 wave from QE audit follow-up
Security (M-01):
- src/cli/commands/learning.ts: replace execSync shell-interpolation of
  the user-controlled --file path with execFileSync + file-descriptor
  redirect (dump) and stdin input (import). Closes the command-injection
  vector where a crafted --file="x.db\"; rm -rf ~; \"" would escape
  shell quoting.

Build / supply chain:
- Move @claude-flow/guidance from dependencies to optionalDependencies
  alongside @claude-flow/browser. No stable successor exists on npm;
  usage is already guarded by try/await-import/catch fallbacks so
  demotion is safe. Documented in the follow-up research memo.
- Lazy-load @faker-js/faker via dynamic import in
  test-data-generator.ts and mark it external in build-cli.mjs /
  build-mcp.mjs. Faker is a devDep that previously got statically
  bundled into shipped artefacts because of a top-level import. Tarball
  drops 11.5 MB → 9.6 MB.

MCP API contracts:
- Add required:true to semantically load-bearing first parameters on
  agent_metrics (agentId), coverage_analyze_sublinear (target),
  accessibility_test (url), defect_predict (target), code_index
  (target). Verified via live MCP tools/list handshake — required
  arrays now match the intent documented in each tool's description.

CI hygiene:
- Rewrite the "Commit telemetry data" step in
  qcsd-production-trigger.yml to push to a bot branch and open a PR
  instead of `git push` direct to protected main. That push failed 8/10
  runs on the last audit cycle because of branch protection. Artifact
  upload (already present) continues to preserve the raw payload for 90
  days regardless of whether the PR lands.

Verification:
- npm audit --omit=dev: 0 critical, 0 high (1 moderate in
  @mapbox/node-pre-gyp — extraneous, pre-existing, unrelated).
- npm run typecheck: clean.
- CLI + MCP bundles rebuild successfully.
- 253/253 test-generation unit tests pass.
- Live MCP protocol smoke: new required flags visible on tools/list;
  advisor_consult guard from v3.9.14 round 1 still fires.

Memory note: see feedback_cli_is_aqe.md — CLAUDE.md ruflo refs were
audited. Only 2 lines (204, 260-261) are stale AQE-product refs; the
other 11 swarm/memory/daemon ruflo commands are legitimate
orchestration-layer invocations and are intentionally retained.
Deferred to a dedicated docs PR so the user can review each.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:47:06 +00:00
Dragan Spiridonov 7a254cbd45 docs(qe): add v3.9.13 follow-up research memos (guidance strategy, agent recount)
Two targeted investigations opened while triaging the v3.9.13 QE audit
priority matrix:

1. @claude-flow/guidance strategy — recommendation: keep + demote to
   optionalDependencies. No newer stable version exists on npm; usage is
   already try/await-import/catch-fallback pattern across 62 refs in 17
   files; the only real concern (protobufjs CWE-94 supply-chain) is
   already neutralised by the v3.9.14 overrides.

2. QE agent count recount — verdict: the "60 specialized QE agents"
   claim is honest, not an overcount. Primary fleet is 53 qe-*.md under
   .claude/agents/v3/ plus 7 TDD/review subagents under
   .claude/agents/v3/subagents/qe-*.md. Both sets mirror into
   assets/agents/v3/ for npm distribution. The earlier honesty-audit
   finding undercounted by 7 because subagents were not enumerated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-20 12:46:40 +00:00