- This spec encodes the target strategy-conveyor model for personal sandbox deployment.
- Seeded Markov bundles include discovery-controller/scan/signal/execution_plan/monitor.
- Live execution is modeled as an optional adapter workflow (not seeded by default).
- Polymarket entry actions require explicit confirmation before live calls (optional auto-unwind policy for risk-off).
- Monitoring starts after first non-zero fill (partial or complete).
- Event payloads are treated as uniformly typed
payloadenvelopes. - Valid
markov.scan.requestevents explicitly requestmarkov-scan@latest. markov.scan.completedexplicitly requestsmarkov-signal@latest.markov.discovery.requestis wired to a discovery controller workflow, which emitsmarkov.scan.requestfor selected candidates (no direct discovery->scan shortcut hook).- Candidate fanout is capped at chain-spawn time via
max_new_chains_per_run. - Discovery candidate handling is single-path via CandidateDecision (reject or spawn, never both).
- Discovery filtering now includes keyword relevance and max-hours bounds.
- Signal completion emits explicit gate fields (
edge_score,cost_adjusted_edge_bps,fair_value_confidence). - Signal supports
outcome=Autoand selectsYesorNoby higher post-cost edge. - Signal gate now includes
min_cost_adjusted_edge_bpsthreshold (default 5 bps). - Expected slippage has a dynamic component from risk-cap vs liquidity, not only static input.
- Live execution requests transition chain state through
execution_requested->execution_in_progress. - Runtime-budget and slippage/retry breach paths fail fast to
markov.execution.failed+markov.chain.error. - Schedule gating enforces
is_enabled,expires_at, andmax_executionsbefore discovery emission. - Risk allocation uses max-loss basis with per-chain
RiskReservationledger (active/released). markov.execution.unwind.requestis now wired to start the execution workflow.- Execution requests are validated against stored ExecutionApproval records, not only payload claims.
- Approval expiry handling releases reserved risk and marks chain execution_approval_expired.
- Stage transitions now include explicit prior-state guards and idempotency preconditions.
- ChainError.chain_id is nullable for malformed payload paths.
- Scan/monitor orderbook calls are token-ID-first (
getPredictionOrderbook.tokenId) with market metadata fallback resolution. - Numeric/text metric extraction is key-scoped during deep traversal, preventing token IDs from being misread as market metrics.
A strategy is the root policy object, not a single market and not a single run.
Lineage:
- strategy_id -> long-lived policy (discovery, signal, risk, execution, monitoring, learning)
- scheduled_run_id -> one recurring tick of that strategy
- chain_id -> one market/outcome chain from that run (strategy_id + run_id + market_id + outcome)
Chain ID canonicalization:
chain_id = lower(trim(strategy_id)) + ":" + lower(trim(scheduled_run_id)) + ":" + lower(trim(market_id)) + ":" + lower(trim(outcome))- Delimiter is fixed (
:) and all parts must be lowercased/trimmed before composition.
Primary optimizer target:
- Sharpe ratio (risk-adjusted returns), with PnL as secondary metric.
Edge model:
- base_edge from deterministic numeric features (fair_value, implied_price, costs, liquidity, volume, volatility).
- Fair-value source hierarchy is explicit:
model -> orderbook_mid -> time_decay_blend(with declared fallback). edge_scoremust be reproducible from declared components (mispricing_bps,fees_bps,expected_slippage_bps, liquidity/volume penalties, confidence weight).- Reflection/reasoning contributes bounded edge_adjustment.
- Recommended guardrail: reflection influence capped at +/-0.03 edge_score.
- Reflection never bypasses hard risk gates.
Learning mode:
workflow evalcomputes built-in run metrics plus optional custom metrics from workflow outputs.workflow optimizecreates optimization-run bookkeeping/rollback metadata only.- Automatic mutation/promotion is not built into harness commands; it must be orchestrated externally.
Recommended:
- One recurring schedule per strategy.
- No per-candidate one-time schedules for fanout (avoids schedule explosion and stale jobs).
- Schedule must be active, not expired, and below max execution count.
Use structured schedule target (workflow or command) and emit markov.discovery.request each run. Seeded discovery-controller wiring converts that into accepted-chain scan fanout.
Example schedule payload shape (createScheduledPrompt):
{
"name": "Markov Crypto Discovery",
"target": {
"kind": "workflow",
"workflowId": "markov-discovery-trigger",
"revision": "latest",
"inputs": {
"strategy_id": "strat-crypto-sharpe",
"query": "crypto"
}
},
"trigger": {
"type": "time_based",
"schedule": {
"pattern": "recurring",
"cronExpression": "*/15 * * * *",
"timezone": "America/Los_Angeles"
}
},
"metadata": {
"agent": {
"name": "predictions",
"strategy": "fixed"
}
}
}What is available in commands.ts today:
workflow eval <run-id>evaluates run artifacts using built-in metrics (total_duration_ms,step_count,success_rate,error_rate,output_size_bytes).- Custom metrics are supported only when defined in
workflow.evaluation.custom_metricsand sourced from step results (steps.<stepId>.<path>). workflow optimize <workflow-id> [--baseline <run-id>]creates an optimization run record; it does not automatically mutate workflow code.workflow optimize-listandworkflow rollbackmanage optimization records and rollback metadata.
Implication for alpha loops:
- Sharpe/edge quality must be emitted as numeric step outputs and wired as custom metrics.
- GEPA/champion-challenger promotion must be implemented by an external controller workflow/agent, not by built-in optimize alone.
Required custom metric contract (minimum):
strategy_sharpefromsteps.monitor.result.strategy_sharpeedge_realization_bpsfromsteps.monitor.result.edge_realization_bpsrealized_slippage_bpsfromsteps.monitor.result.realized_slippage_bps
Seeded now:
- markov-discovery-controller@latest
- markov-scan@latest
- markov-signal@latest
- markov-execution-plan@latest
- markov-monitor@latest
Optional extension (live adapter):
- markov-execution@latest
Seeded now:
- markov-discovery-controller-start
- markov-scan-start
- markov-signal-on-scan
- markov-exec-plan-on-signal
- markov-monitor-on-execution-filled
- markov-monitor-on-tick
Optional extension (live adapter):
- markov-execution-on-plan
- markov.discovery.request
- markov.scan.request
- markov.scan.completed
- markov.signal.completed
- markov.signal.rejected
- markov.execution_plan.completed
- markov.execution.approval_required
- markov.execution.request
- markov.execution.started
- markov.execution.partial_fill
- markov.execution.completed
- markov.execution.failed
- markov.execution.filled
- markov.execution.unwind.request
- markov.monitor.tick
- markov.monitor.active
- markov.monitor.invalidated
- markov.chain.error
Inputs:
- strategy_id, scheduled_prompt_id, scheduled_run_id, query, keyword
- risk_pct_per_trade, account_size_usd, max_slippage_bps, outcome
- max_candidates_per_discovery, max_new_chains_per_run
- min_hours_until_end, max_hours_until_end, min_volume_24h_usd, min_liquidity_usd, min_keyword_relevance
Behavior:
markov.discovery.requestrunsmarkov-discovery-controller@latest.- Enumerates candidate markets from discovery sources.
- Filters by quality (time-to-end window, liquidity, volume, keyword relevance, data completeness).
- Ranks candidates and selects top-N for this run.
- Computes exactly one candidate decision per market/outcome (accepted or rejected).
- Rejects candidates when active position already exists for same market/outcome.
- Rejects candidates when strategy/portfolio risk is exhausted.
- Enforces
max_new_chains_per_runbefore chain identity spawn. - Uses a single discovery request per run with
reasonannotated as periodic scan vs periodic re-evaluation.
On success:
- Emits one markov.scan.request per selected candidate chain.
Inputs:
- chain_id, strategy_id, scheduled_prompt_id, scheduled_run_id
- market_id, outcome
- token_id, yes_token_id, no_token_id (optional, resolved if absent)
- risk_pct_per_trade, account_size_usd, max_slippage_bps
Behavior:
- Validates scan payload bounds and runs
markov-scan@latestfrommarkov.scan.requesthook wiring. - Pulls market snapshot and resolves Yes/No token IDs from market outcomes (
outcomes[]/clob_token_ids). - Calls orderbook tool with
tokenId(tool-contract-aligned) and falls back to alternate outcome token when needed. - Computes risk cap and market-quality snapshot.
- Persists chain state under markov_chain:{chain_id}.
On success:
- Emits markov.scan.completed.
Inputs:
- Scan payload plus model inputs:
- fair_value_price, fair_value_source, fair_value_confidence, edge_uncertainty_bps, model_version
- orderbook_imbalance, volatility_proxy, fees_bps, expected_slippage_bps
- min_cost_adjusted_edge_bps
Behavior:
- Computes pseudo-greeks and inefficiency.
- Uses fair-value source hierarchy (
input -> prior_state -> orderbook_mid) when explicit fair value is absent. - If requested outcome is
Auto, evaluates Yes vs No cost-adjusted edge and selects side with stronger edge. - Carries forward
token_idfor the selected outcome plusyes_token_id/no_token_idfor downstream execution/monitoring. - Computes deterministic base_edge and bounded final edge_score.
- Writes signal summary and reflection logs.
- Applies execution gate (edge, confidence, uncertainty, cost-adjusted edge threshold).
On success:
- Emits markov.signal.completed (gate passed) or markov.signal.rejected (gate failed), always with explicit gate fields.
- markov-exec-plan-on-signal requests markov-execution-plan@latest only for markov.signal.completed.
Inputs:
- Accepted signal payload.
Behavior:
- Builds executable intent (entry_price_band, size_cap_usd, stop_policy, compounding_policy, execution cadence).
- Enforces per-trade max-loss cap and remaining strategy/portfolio risk availability.
- Uses position
risk_usdplus activeRiskReservationtotals for exposure accounting. - Creates active
RiskReservationon approved plan and releases on terminal execution outcomes. - Enforces one active execution plan/reservation/approval tuple per chain via idempotency guards.
- Defaults to paper intent and transitions chain state to
execution_approval_required. - Creates approval contract fields (
approval_token,approval_expires_at,requested_at) for live escalation. - Emits markov.execution.approval_required for live intent escalation.
On success:
- Emits markov.execution_plan.completed.
Inputs:
- markov.execution.request payload with explicit confirmation fields.
Behavior:
- Runs live order placement (TWAP-style slices / partial fill handling) within max slippage and runtime limits.
- Uses Predictions tool
tradePredictionMarketfor slice entry/exit. - Uses Predictions tool
cancelPredictionOrderfor stale/violating open orders. - Uses
getPolymarketPositionsandgetPolymarketOrderHistoryfor reconciliation. - Enforces policy gate: confirmation required before trade/cancel/redeem/funding actions.
- Requires confirmation provenance (
confirmed_by_user,approved_by_user_id,approval_token) validated against storedExecutionApproval. - Requires
slice_count * slice_interval_seconds <= max_execution_runtime_secondsbefore workflow launch. - Transitions chain status to
execution_requestedon accepted request andexecution_in_progressonmarkov.execution.started. - Runtime-budget breaches explicitly persist chain
execution_failedstate and emit chain error snapshot. - Emits partial updates as fills accumulate and final completion/failure terminal events.
- Persists per-slice attempts and aggregate fill summary for post-trade attribution.
- Periodic strategy ticks expire stale pending approvals and release reserved risk.
On success:
- Emits markov.execution.partial_fill for in-flight progress.
- Emits markov.execution.completed when fill ratio threshold is reached.
- Emits markov.execution.filled to trigger monitor on both partial and complete fills.
On failure:
- Emits markov.execution.failed and markov.chain.error.
Inputs:
- markov.execution.filled payload or markov.monitor.tick payload.
Behavior:
- Starts once there is non-zero filled exposure (partial or complete).
- Refreshes market and evaluates alpha decay/invalidation.
- Reuses persisted
token_idfor orderbook refresh (with snapshot-based token fallback by outcome). - Metric refresh reads only key-matched nested fields (
hours_until_end,volume_24h_usd,liquidity_usd) and ignores unrelated scalar leaves. - On invalidation, emits
markov.execution.unwind.requestto force risk-off action. - Emits active vs invalidated state.
On success:
- Emits markov.monitor.active or markov.monitor.invalidated.
Inputs:
- markov.execution.unwind.request payload (from monitor invalidation).
Behavior:
- Starts
markov-execution@latestwith unwind intent. - Supports auto-unwind via policy flag or explicit user confirmation fields.
- Reuses execution telemetry/events for traceability.
- Portfolio hard cap + per-strategy cap both enforced.
- Execution mode defaults to
paperuntil explicit confirmation. - Trade/cancel/redeem/funding actions require confirmation by policy.
- Execution runtime is bounded (max slices + max runtime + slippage breach retries).
- Suggested defaults:
- default_execution_mode = paper
- require_trade_confirmation = true
- allow_auto_unwind_on_invalidation = true
- approval_ttl_minutes = 10
- daily_realized_loss_pause_pct = 1.5
- consecutive_invalidations_pause_count = 4
- consecutive_slippage_breach_pause_count = 3
- slippage_breach_multiplier = 2
- stale_data_max_runs = 3
- max_execution_slices = 8
- min_slice_interval_seconds = 20
- max_execution_runtime_seconds = 900
- max_slippage_breach_retries = 2
- min_fill_ratio_for_completion = 0.95
Core fields:
- All custom events use
payloadenvelope (no bare top-level chain_id emits). - chain_id, strategy_id, scheduled_prompt_id, scheduled_run_id, query
- market_id, token_id, yes_token_id, no_token_id, outcome, requested_outcome, selected_outcome
- risk_pct_per_trade, account_size_usd, max_slippage_bps
- max_hours_until_end, min_keyword_relevance, keyword_relevance
- edge_score, cost_adjusted_edge_bps, hours_until_end, volume_24h_usd, liquidity_usd
- implied_price, yes_price, no_price, fair_value_price, fair_value_source, fair_value_confidence, edge_uncertainty_bps, model_version
- orderbook_imbalance, volatility_proxy, fees_bps, expected_slippage_bps
- min_cost_adjusted_edge_bps
- execution_mode, confirmed_by_user, approved_by_user_id, approval_token, approval_expires_at
- slice_count, slice_interval_seconds, order_id, slice_index
- filled_size_usd, average_fill_price, fill_ratio, realized_slippage_bps
- risk_usd (active positions), reserved_risk_usd (risk reservations)
- timestamp, source_stage
Discovery start (strategy run):
Note: seeded bundles bind this event to markov-discovery-controller@latest, which emits markov.scan.request for accepted candidates.
event emit markov.discovery.request '{"strategy_id":"strat-crypto-sharpe","scheduled_prompt_id":"sp-123","scheduled_run_id":"run-2026-02-26T15:00Z","query":"crypto","keyword":"crypto","outcome":"Auto","risk_pct_per_trade":0.5,"account_size_usd":10000,"max_slippage_bps":50,"max_hours_until_end":336,"min_keyword_relevance":0.2}'Direct scan start (single market/manual):
event emit markov.scan.request '{"chain_id":"strat-crypto-sharpe:run-2026-02-26T15:00Z:market-abc:yes","strategy_id":"strat-crypto-sharpe","scheduled_run_id":"run-2026-02-26T15:00Z","market_id":"market-abc","outcome":"Yes","risk_pct_per_trade":0.5,"account_size_usd":10000,"max_slippage_bps":50}'Execution start (usually emitted by execution_plan):
event emit markov.execution.request '{"chain_id":"strat-crypto-sharpe:run-2026-02-26T15:00Z:market-abc:yes","strategy_id":"strat-crypto-sharpe","market_id":"market-abc","outcome":"Yes","execution_mode":"live","confirmed_by_user":true,"approved_by_user_id":"usr-123","approval_token":"appr-8d31","approval_expires_at":"2026-02-26T15:20:00Z","size_cap_usd":2500,"entry_price_band_low":0.59,"entry_price_band_high":0.62,"max_slippage_bps":50,"slice_count":6,"slice_interval_seconds":30,"source_stage":"execution_plan"}'Run evaluation / optimization (harness commands):
workflow eval run_abc123
workflow optimize markov-signal@latest --baseline run_abc123
workflow optimize-list markov-signal@latest --jsonExecution fill (starts monitor):
event emit markov.execution.filled '{"chain_id":"strat-crypto-sharpe:run-2026-02-26T15:00Z:market-abc:yes","market_id":"market-abc","outcome":"Yes","filled_size_usd":2500,"average_fill_price":0.61,"timestamp":"2026-02-26T15:12:00Z","source_stage":"execution_filled"}'Monitor re-check:
event emit markov.monitor.tick '{"chain_id":"strat-crypto-sharpe:run-2026-02-26T15:00Z:market-abc:yes"}'flowchart LR
A["recurring strategy schedule tick"] --> B["markov.discovery.request"]
B --> C["discovery filter + rank + top-N select"]
C --> D["emit markov.scan.request (fanout per candidate)"]
D --> E["markov-scan@latest"]
E --> F["markov.scan.completed"]
F --> G["markov-signal@latest"]
G --> H{"edge/confidence gate"}
H -->|"reject"| I["markov.signal.rejected"]
H -->|"accept"| J["markov.signal.completed"]
J --> K["markov-execution-plan@latest"]
K --> L["markov.execution_plan.completed"]
L --> M["markov.execution.approval_required"]
M -->|"confirmed"| N["markov.execution.request"]
N -->|"runtime budget breach"| T["markov.execution.failed"]
N --> O["markov-execution@latest (optional adapter)"]
O --> P{"fill ratio reached?"}
P -->|"partial"| Q["markov.execution.partial_fill"]
Q --> S["markov.execution.filled"]
P -->|"complete"| R["markov.execution.completed"]
R --> S["markov.execution.filled"]
O -->|"failed"| T["markov.execution.failed"]
S --> U["markov-monitor@latest"]
V["markov.monitor.tick"] -.-> U
U --> W{"thesis + alpha decay check"}
W -->|"valid"| X["markov.monitor.active"]
W -->|"invalidated"| Y["markov.monitor.invalidated"]
Y --> AA["markov.execution.unwind.request"]
AA --> AB["markov-execution@latest (unwind intent)"]
O -->|"error"| Z["markov.chain.error"]
U -->|"error"| Z
- Invalid payload -> markov.chain.error
- Missing market data -> markov.chain.error
- Signal below edge/confidence threshold -> markov.signal.rejected
- Live execution request without confirmation -> markov.execution.approval_required (no trade call)
- Approval token missing/expired -> markov.execution.approval_required (refresh required)
- Approval expired while pending -> execution_approval_expired + reservation release + markov.chain.error
- Execution request runtime-budget breach -> markov.execution.failed + markov.chain.error
- Execution slippage/retry breach in workflow completion payload -> markov.execution.failed + markov.chain.error
- Execution fill ratio below completion threshold -> markov.execution.partial_fill (monitor still starts on filled exposure)
- Monitor before any fill -> skipped + markov.chain.error (no filled exposure yet)
- Missing required eval custom metrics -> eval fails quality gate, no auto-promotion
- Duplicate stage transitions -> dropped by idempotency guard
- Risk cap exhausted -> candidate rejected (no chain spawn)
- Expired/disabled/over-limit schedule -> no discovery run emitted
- Market Metadata Enrichment
- Resolve accepted numeric market IDs to canonical metadata (question, slug, outcomes, end time) during discovery.
- Persist metadata into chain state and run artifacts for operator review.
- Fair Value Model Upgrade
- Replace orderbook-only fallback with a pluggable fair-value model per strategy.
- Version and log model inputs so edge derivation is auditable.
- Signal Gate Calibration
- Calibrate min_cost_adjusted_edge_bps, confidence floor, and uncertainty bounds from realized runs.
- Add regime-specific thresholds by liquidity and time-to-resolution buckets.
- Live Execution Wiring
- Enable markov-execution@latest adapter with confirmation, slice controls, and unwind flows.
- Add explicit close policy for invalidation and stale-alpha exits.
- Portfolio and Scheduler Guardrails
- Add max-concurrent-chain and cooldown controls per strategy.
- Add schedule-level risk throttles to prevent over-allocation across simultaneous candidates.
- Observability and Evaluation Loop
- Emit compact per-run summaries (accepted/rejected reasons, reserved risk, gate outcomes, slippage).
- Run recurring workflow eval and staged workflow optimize with human promotion gates.
- Hardening Tests
- Add integration fixtures for discovery -> scan -> signal with realistic payloads.
- Add regression tests for Auto side selection, dynamic slippage, and duplicate-event idempotency.