Phase 6 of ADR-126 closes the regulator-grade interpretability gap that
LSTM / Transformer trading signals leave by default. Given a `signalId`,
the new `trader-explain` skill builds a feature-contribution graph, runs
single-entry forward-push PageRank from the signal output node, and
persists the top-K ranked features to `trading-analysis` as a
`SignedAttributionArtifact`.
What's new:
- `plugins/ruflo-neural-trader/src/signed-attribution.{ts,mjs}` —
typed contract + runtime mirror for the Ed25519-signed attribution
envelope. Re-uses the Phase 4 signing scheme exactly (canonical body
excludes signature fields; verifier pins to a caller-supplied
`trustedPublicKey`, NOT the served `witnessPublicKey` — CWE-347 /
#1922). Adds `localSingleEntryPageRank` (seeded mulberry32 power-
iteration fallback, ~30 LOC), `singleEntryPageRank` (MCP dispatch with
local fallback, mirrors the SublinearAdapter pattern from Phase 3), and
`topKFeatures` (deterministic tie-breaking by node index).
- `plugins/ruflo-neural-trader/skills/trader-explain/SKILL.md` —
the call site. Retrieves the signal from `trading-signals`, extracts
per-feature contributions via `npx neural-trader --predict --explain
--json` (degrades to a z-score heuristic if the flag isn't shipped
upstream yet — artifact tagged `attribution_method:
"input-zscore-fallback"`), runs the PageRank, signs the result with
`signAttributionArtifact`, persists to `trading-analysis`, and surfaces
a markdown ranking table to the agent.
- `scripts/smoke-neural-trader-feature-attribution.mjs` — three-layer
regression smoke: (1) static contract on TS/MJS + skill, (2) Ed25519
crypto round-trip with tamper detection on feature scores AND on the
graphMetadata.seed (seed is signed-in), (3) reproducibility — same seed
→ byte-identical scores + ordering; different seed → scores differ.
- `.github/workflows/v3-ci.yml` — `neural-trader-feature-attribution-smoke`
job with path triggers on the three load-bearing artifacts.
- `plugins/ruflo-neural-trader/README.md` — new "Feature attribution"
section with schema, example output, regulator-compliance angle
(EU AI Act Article 13 / SEC Reg-AI), and reproducibility guarantee.
- `plugins/ruflo-neural-trader/scripts/smoke.sh` — step 2 now requires
9 skills (was 8), adding `trader-explain` to the contract check.
Acceptance (ADR-126 Phase 6):
- All 36 checks in `scripts/smoke-neural-trader-feature-attribution.mjs`
pass: static contract, crypto round-trip, reproducibility, mass
conservation, signed-seed coverage.
- No regression: portfolio-cg smoke, backtest-signing smoke, pipeline
smoke all still green.
- Plugin smoke: 11 passed, 0 failed.
- `npx neural-trader --help` still clean (no upstream regression).
With Phase 6 landed, all six phases of ADR-126 are now complete:
Phase 1 — Namespace alignment
Phase 2 — TTL + dedup lifecycle (#2069)
Phase 3 — Portfolio CG via sublinear (#2070)
Phase 4 — Ed25519-signed backtest artifacts (#2071)
Phase 5 — SendMessage risk-gate pipeline (#2071)
Phase 6 — Feature attribution via single-entry PageRank (this PR)
Closes#2068
Co-Authored-By: RuFlo <ruv@ruv.net>
Wires the Conjugate-Gradient mean-variance solve `Σ · x = μ` that ADR-123
Wedge 8 promises a 40-60× speedup over the legacy Neumann series. Ships:
- `src/sublinear-adapter.ts` — `SublinearAdapter` class per the ADR-123
§262-289 contract: `solveCG(matrix, vector, opts)`, `isMcpAvailable()`,
`SolveResult` shape, SPD/symmetric sanity checks, graceful degrade with
`degraded: true` for malformed inputs.
- `src/sublinear-adapter.mjs` — runtime mirror so the smoke + bench
import the adapter directly (the plugin has no build step). Both files
ship a self-contained ~50-LOC CG kernel + Jacobi/Neumann baseline; the
same call site picks up the full native-WASM speedup automatically
once `mcp__ruflo-sublinear__solve` is registered.
- `skills/trader-portfolio-cg/SKILL.md` — declares `mcp__ruflo-sublinear__solve`
in allowed-tools, writes weights with provenance metadata
(`method: 'cg-sublinear' | 'cg-local' | 'neumann-fallback'`) to the
canonical `trading-risk` namespace (ADR-126 Phase 1 alignment),
documents the `RUFLO_NEURAL_TRADER_DISABLE_CG` disable flag, defines
the legacy `npx neural-trader --portfolio optimize` fallback.
- `benchmarks/portfolio-cg.bench.mjs` — CG vs Neumann on synthetic SPD
covariance at n ∈ {16, 64, 256}. Fixed-seed parity check
(`||cg − neumann||_∞ < 1e-4`) and per-size latency table.
- `benchmarks/results/cg-baseline-20260520T022220Z.md` — captured
baseline numbers. CG at n=256 is sub-ms (0.31 ms). The JS-vs-JS gap is
~1.5-1.9× because both kernels JIT to similar efficiency; the upstream
40-60× figure is for native-CG vs native-Neumann in
`sublinear-time-solver@1.7.0`, which the skill picks up automatically
through the MCP dispatch.
- `scripts/smoke-neural-trader-portfolio-cg.mjs` — three-layer regression
smoke: static adapter contract (class shape, method signatures, MCP
tool name), static skill contract (allowed-tools, namespace, disable
flag, fallback path), runtime correctness (textbook 2×2 SPD case
A=[[4,1],[1,3]], b=[1,2] → x=[1/11, 7/11] within 1e-6, plus degraded
paths for non-square / non-symmetric input).
- `.github/workflows/v3-ci.yml` — `neural-trader-portfolio-cg-smoke` job
on the same shape as the kg-extract guard; triggers on adapter, skill,
or smoke changes.
- `README.md` + plugin-smoke skill-count bumped from 7 → 8.
Acceptance:
- `bash plugins/ruflo-neural-trader/scripts/smoke.sh` → 11/11 pass.
- `node scripts/smoke-neural-trader-portfolio-cg.mjs` → all 17 checks pass.
- Bench parity: `||cg − neumann||_∞ < 1e-4` at every n ∈ {16, 64, 256}.
Refs: #2068, ADR-126 Phase 3, ADR-123 §162 Row 8 (Wedge 8).
Co-Authored-By: RuFlo <ruv@ruv.net>
Resolves the three-way namespace mismatch surfaced by the ADR-126 dossier:
- README.md L198-205 previously claimed 4 (-strategies/-backtests/-risk/-analysis)
- docs/adrs/0001 L63 previously claimed 4 (-backtests/-risk/-signals/-models)
- skills/trader-signal/SKILL.md L35 actually writes to `trading-signals`
- skills/trader-train/SKILL.md L28 actually writes to `trading-models`
The smoke test previously validated only the README's four, so signal data
silently landed in `trading-signals` (undeclared) and model results landed
in `trading-models` (undeclared), invisible to any downstream consumer
scoped to the documented namespace surface.
Aligns README, ADR-0001, smoke.sh, and trader-train on the canonical 5
defined by ADR-126 Phase 1:
trading-strategies — strategy definitions, parameters, regime mappings
trading-backtests — backtest results (long-lived; signed in Phase 4)
trading-risk — risk model state, VaR/CVaR, circuit-breaker triggers
trading-analysis — regime classifications + model-training results
trading-signals — short-lived signal events (TTL in Phase 2)
trader-train now stores model results to `trading-analysis` (was the
undeclared `trading-models`). smoke.sh check 8 now asserts the fifth
namespace claim. All 11 smoke checks still pass.
Refs #2068
Co-Authored-By: RuFlo <ruv@ruv.net>
Updates the README prerequisites block to reflect the 2.8.x line
(flag-style CLI dispatcher with --backtest, --signal, --risk,
--portfolio, --regime, --train, --predict, --strategy-create) and
the analytical layers (walk-forward, Monte Carlo, multi-symbol,
benchmark, optimize, Kelly).
Extends runtime-smoke.sh with three new gates for the strategies
shipped in 2.8.10 and 2.8.11:
17. pairs strategy returns metrics
18. adaptive strategy picks regime
19. multi-indicator strategy runs
All 20 gates pass against neural-trader 2.8.11.
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
The existing `plugins/ruflo-neural-trader/scripts/smoke.sh` is
structural-only (file presence, README pins, frontmatter checks). It
passed for months while `npx neural-trader --backtest` and every
other documented invocation crashed at runtime — those gaps got
caught and fixed across neural-trader 2.7.5 → 2.8.1.
This adds a sibling `runtime-smoke.sh` that actually:
1. Installs `neural-trader@2.8.1` in a clean tmpdir
2. Runs each of the 8 commands the plugin's skills document
3. Parses the JSON output and asserts `.command == <expected>`
4. Confirms `require('neural-trader')` exposes >= 100 exports
5. Confirms `--version` returns the pinned major.minor
Run:
bash plugins/ruflo-neural-trader/scripts/runtime-smoke.sh
Current result: 11 passed, 0 failed.
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>
ADR-117 (Accepted): dispatch heavy neural-trader jobs — multi-year
walk-forward backtests, big Monte-Carlo, parameter sweeps, LSTM/
Transformer/N-BEATS training — to an Anthropic Managed Agent cloud
container via the managed_agent_* tools (ADR-115, shipped in
3.7.0-alpha.27). No new MCP tools, no new deps — pure orchestration.
- skills/trader-cloud-backtest/SKILL.md — the recipe (provision-once,
pre-flight-cheap, run, ingest-locally, terminate-eagerly); allowed-tools
scoped to managed_agent_* + memory_* + agentdb_pattern-store + Bash/Read
(no wildcard grant).
- commands/trader.md — /trader cloud <backtest|train|sweep> subcommand.
- scripts/smoke.sh — step 2 now asserts the 7th skill exists, references
managed_agent_create, and names the local trader-backtest fallback;
step 10 ADR-status check corrected to Accepted.
- README.md — overview + skills table + Architecture-Decisions +
Related-Plugins cross-refs to ADR-117 / ADR-115 / ruflo-agent.
- ADR-115 Links — cross-refs ADR-117 as the first consumer.
Cost-optimization rules baked into the recipe (the "optimize" requirement):
install-once in initScript, reuse the environment, pre-flight cheap
(1-path/3-month smoke before a 1000-path/multi-year run), batch sweeps into
one prompt, terminate eagerly + GC orphans (#1931), cheap agent model
(Haiku/Sonnet — the compute is the Rust engine, not the LM; ADR-026),
estimate before kicking off.
Validation: ruflo-neural-trader smoke 11/0, ruflo-agent smoke 12/0,
audit-cli-mcp-tools / audit-tool-descriptions / audit-plugin-packages /
audit-hook-commands all green. No new MCP tools → cli build unaffected.
Co-authored-by: Reuven <cohen@ruv-mac-mini.local>