Guided fictional market-regime run
scripts/run_market_regime_daily.py guides the market-regime-daily workflow
through readiness, run-scoped artifact handoff, a human posture decision, and a
local journal handoff. This is an offline learning path using the committed
fictional component fixtures, evaluated at 2026-01-15T13:08:00+00:00. It does
not describe today’s market and is not a buy/sell signal.
Start and decide
Use the repository’s locked environment (uv sync --locked --extra dev). No
provider credentials are required. Run from the repository root:
uv run python scripts/run_market_regime_daily.py --run-dir reports/regime-demo
Choose a new directory for every run. Existing directories, including empty
ones, are refused. The runner validates the workflow and fixture contracts,
loads the required native scorer APIs, and snapshots the fixtures. It computes
breadth and uptrend reports, explicitly skips optional top-risk analysis, and
computes an Exposure Coach proposal from only that run’s outputs. To include
top-risk analysis, add --include-top-risk when creating the run. That setting
cannot be changed on resume.
In an interactive terminal the command displays the proposal and asks for
allow, restrict, or cash-priority. Entering a posture explicitly records
your fictional decision. A blank answer pauses the run. With redirected input
or CI, the runner always pauses after displaying the proposal and returns 3:
uv run python scripts/run_market_regime_daily.py \
--run-dir reports/regime-demo --status
uv run python scripts/run_market_regime_daily.py \
--run-dir reports/regime-demo --resume --decision restrict
--decision is accepted only with --resume at AWAITING_DECISION. A proposal
does not record permission automatically. Completed runs reject further
decisions; --resume without a decision validates and displays the existing
completed outcome without rewriting artifacts or step timestamps.
Evidence and boundaries
The run directory holds:
manifest.json: UUID run ID, readiness, fixed evaluation time, wall-clock execution timestamps, source hashes, input snapshots, step states, consumed artifact hashes, and the explicit decision.inputs/*.json: fictional fixture snapshots bound to the run ID.01_*.jsonthrough03_*.json: native component-score analyses. Step 3 has no output in the default path and is markedSKIPPED, never passed.04_exposure_proposal.json: native Exposure Coach calculation, including strict breadth/uptrend provenance and its fixed-time freshness check.journal.json: the decision and proposal hash, withdestination: trader-memory-coreanddelivery: handoff_only.
The journal is not delivered to trader-memory-core. The runner does not read or modify real thesis/journal state, portfolios, accounts, or dotenv files. It does not inspect or record credentials. Native scorers consume complete, fictional component scores; provider fetches, individual component calculators, live API behavior, and broker actions are outside this path. The runner calls the native Exposure Coach Python API; it does not execute that skill’s CLI.
Before handoff and resume, the runner checks fixed paths, step dependencies, schemas, source hashes, input/output bytes, and run IDs. It recomputes saved results with native APIs for validation, without repeating completed step publication. It refuses changed/missing artifacts and source revisions rather than adopting files from another run. Hashes detect corruption and unintended edits; this local manifest is not a signed or tamper-proof audit record.
The current implementation shares the market fixture validation and scorer
helpers in workflow_replay.py. Changes to those helpers, the runner, workflow,
fixtures, or relevant native skill scripts require a new run directory. The
existing deterministic replays remain the independent numeric baseline.
Interruption and recovery
Every step checkpoints RUNNING before execution and checkpoints its output
hash and completion afterward. Completed checkpoints resume without rewriting
their artifacts. Failed or interrupted RUNNING steps are never retried or
promoted automatically; preserve that directory and start a fresh run. A crash
during journal publication similarly requires a fresh run.
A .lock file prevents concurrent writers. --status briefly acquires the
same lock while validating; it does not update the manifest. A normal exit or
Python interruption removes its own lock. SIGKILL or power loss can leave one
behind. The runner never deletes an existing lock:
- Read the PID in the exact run’s
.lockfile and inspect it withps -p PID -o pid=,command=. PID reuse is possible: verify the command and run directory. - If the owning process is still running, let it finish. Do not remove its lock.
- Only after establishing that no process owns this run, remove that exact
.lockfile manually. Preserve all JSON artifacts and the manifest. - Run
--status. If any step isRUNNING/FAILED, a hash differs, or the manifest is incomplete, keep the evidence and use a new directory. Do not edit status fields, hashes, or delete outputs to force resume.
Manifest replacement uses a flushed temporary file followed by atomic rename.
An abandoned .tmp file is refused rather than reused. Symlinks in run paths
are refused. Recovery is intentionally conservative.
Exit codes: 0 completed; 3 awaiting a decision; 1 failed/blocked
readiness, integrity, lock, or execution; 2 invalid arguments or missing
runtime dependencies. An integrity failure prints BLOCKED and preserves the
previous manifest for diagnosis; it does not rewrite its status to claim an
execution occurred. Dependency setup failures happen before a new run is
allocated.
Issue #513 remains open for delivery to the journal destination and any future live-data or additional-workflow support. This runner alone does not authorize or implement those extensions.