LT Scan-Cycle Runbook¶
⛔ DECOMMISSIONED 2026-04-27 (PR #41):
aegis-lt-scan-cyclecontainer は Saxo lane 撤退に伴い停止 + 撤去済み。compose / deploy workflow は repo から削除され、maintenance-decommission-lt-scan-cycle.ymlが対象 container を Synology から取り除いた。本ランブックは historical reference として残置。再起動を試みる場合は Saxo 撤退の前提が変わったか確認すること。CRITICAL (historical):
--dry-runis the current observation mode forlt-scan-cycle. Do NOT switch to--livewithout explicit user approval + a WORK_LOG entry. The observation mode is a RUNTIME FLAG on the binary — not a separate service.
What It Is¶
lt-scan-cycle is the Rust-native scan cycle service, forward-looking successor to the Python paper_trading.py scan loop. It runs beside Python PT in --dry-run mode and writes a parity_log.jsonl that can be compared against the Python replay log with aegis_v3/scripts/parity_diff.py.
Design rule (2026-04-10): Rust is the source of truth going forward. Silent env var defaults are forbidden in the compose file — every required variable uses ${XXX:?message} so bringup fails loudly if the Synology .env file is missing anything.
Prerequisites¶
Central Synology .env at /volume1/aegis/.env MUST provide:
POLYGON_API_KEY— real runtime requirement.PolygonLiveProvider::new()callsenv::var("POLYGON_API_KEY")and errors out if unset. The deploy workflow runs compose with--env-file /volume1/aegis/.envso this key is loaded from the central file.
Saxo credentials are NOT env-var-driven:
- The binary reads Saxo tokens from the mounted token cache file at
/data/tokens/saxobank_tokens_live.json(host:/volume1/aegis/tokens/saxobank_tokens_live.json), managed by the token-keeper container. - The legacy
docker-compose.ymlforaegis-lt-rustpassesSAXOBANK_*_LIVEas a dead env-var passthrough (all empty in the running container). Cleaning that up is tracked as Track B work and does NOT block lt-scan-cycle bringup.
Paths that MUST exist on Synology:
/volume1/aegis/.env(must containPOLYGON_API_KEY=...)/volume1/aegis/tokens/saxobank_tokens_live.json(managed by token-keeper container)/volume1/aegis/wft_state/lt_rust/(created bydeploy.sh prepare_lt_rust_host_dirs)/volume1/aegis/repo/aegis_v3/configs/scenarios/lt_wft.yaml(multi-scenario wrapper; includes LT_RC + WFT_A..I + PT_XW = 11 scenarios)/volume1/aegis/repo/aegis_v3/configs/scenarios/lt_rc.yaml(individual scenario referenced by lt_wft.yaml; also used for single-scenario fallback via--config)
Multi-scenario vs single-scenario mode¶
As of 2026-04-11 the compose file runs lt-scan-cycle in multi-scenario mode via --scenario-multi-yaml=/config/scenarios/lt_wft.yaml. This matches the Python aegis-wft container running run_multi_scenario_pt.py --config pt_wft.yaml (11 scenarios: LT_RC priority + WFT_A..I + PT_XW).
To fall back to single-scenario LT_RC only (emergency rollback or isolated debugging), edit the compose command to:
command:
- "--config=/config/scenarios/lt_rc.yaml"
- "--state-dir=/data/state/lt_rust/lt_scan_cycle"
- "--parity-log=/data/state/lt_rust/parity_log.jsonl"
- "--dry-run"
- "--scan-interval=120"
Multi-scenario mode additionally emits the comparison log at /data/state/lt_rust/lt_wft_comparison.jsonl every 20 WFT batches (Sprint 1b) and runs with --scan-interval=45 to match Python's shared_scan_interval_sec: 45.
How To Start¶
Preferred: trigger the dedicated workflow.
Manual (operator on Synology, emergency only):
cd /volume1/aegis/repo/aegis_v3/lt-rust-docker
docker compose -f docker-compose.lt-scan-cycle.yml up -d --build aegis-lt-scan-cycle
How To Stop¶
cd /volume1/aegis/repo/aegis_v3/lt-rust-docker
docker compose -f docker-compose.lt-scan-cycle.yml stop aegis-lt-scan-cycle
Delete the container entirely:
cd /volume1/aegis/repo/aegis_v3/lt-rust-docker
docker compose -f docker-compose.lt-scan-cycle.yml rm -f aegis-lt-scan-cycle
How To Verify It Is Working¶
Container status (read-only SSH OK):
ssh fukutani.ryo@192.168.42.252 "sudo /usr/local/bin/docker ps --format 'table {{.Names}}\t{{.Status}}' | grep aegis-lt-scan-cycle"
Recent logs:
ssh fukutani.ryo@192.168.42.252 "sudo /usr/local/bin/docker logs aegis-lt-scan-cycle --tail 100 2>&1"
Parity log append confirmation:
Recent scan summary:
ssh fukutani.ryo@192.168.42.252 "jq -c 'select(.record_kind==\"scan\") | {ts,scan_count,entries:(.entries_proposed|length),exits:(.exits_proposed|length),errors}' /volume1/aegis/wft_state/lt_rust/parity_log.jsonl | tail"
How To Read The Parity Log¶
Schema/version surface:
jq -c '{schema_version,record_kind,scan_count,ts}' /volume1/aegis/wft_state/lt_rust/parity_log.jsonl | tail
Entry details:
jq -c 'select(.record_kind=="scan") | .entries_proposed[]? | {underlying,short_strike,long_strike,expiry,status}' /volume1/aegis/wft_state/lt_rust/parity_log.jsonl | tail
Exit details:
jq -c 'select(.record_kind=="scan") | .exits_proposed[]? | {underlying,short_strike,long_strike,expiry,close_reason,status}' /volume1/aegis/wft_state/lt_rust/parity_log.jsonl | tail
What To Look For In Parity Comparison¶
schema_versionis 1 on both sidesrecord_kind=="scan"count grows continuously- Rust/Python VIX within ±0.1
entries_proposed/exits_proposedcounts do not drift significantly- Entry
underlying,short_strike,long_strike,expirymatch errorsare not continuously increasing
Comparison command:
python3 aegis_v3/scripts/parity_diff.py \
--rust-log /volume1/aegis/wft_state/lt_rust/parity_log.jsonl \
--python-log /volume1/aegis/wft_state/pt_replay_log \
--tolerance-seconds 60
Rollback¶
lt-scan-cycle is completely independent from Python PT. Stopping it does not affect aegis-wft (Python PT).
ssh fukutani.ryo@192.168.42.252 \
"cd /volume1/aegis/repo/aegis_v3/lt-rust-docker && \
sudo /usr/local/bin/docker compose -f docker-compose.lt-scan-cycle.yml stop aegis-lt-scan-cycle"
Cutover Path (future, not active)¶
This is the G1 Day 10 cutover — replacing Python aegis-wft with Rust lt-scan-cycle --scenario-multi-yaml --live as the live LT_RC + WFT + PT_XW execution engine.
Pre-cutover checklist¶
- 5 business days clean parity observation
/data/state/lt_rust/lt_wft_comparison.jsonlvs Pythondata/pt_wft_comparison.jsonlcompared per 15-min interval- Per-scenario equity delta ≤ ±$50
- Per-scenario trade count delta = 0
- LT_RC fill parity: Saxo precheck result identical (dry-run)
- No circuit breaker escalations during observation window
- User WORK_LOG entry explicitly approving cutover
- Python
aegis-wftstopped BEFORE Rust switches to live (no dual-write)
Cutover sequence¶
# a. Stop Python aegis-wft FIRST
ssh fukutani.ryo@192.168.42.252 "sudo /usr/local/bin/docker stop aegis-wft"
# b. Wait 30s for any in-flight Saxo calls to flush
sleep 30
# c. Edit docker-compose.lt-scan-cycle.yml to swap --dry-run → --live
# (change only that line; multi-scenario flags stay unchanged)
# d. Commit + push + let GHA deploy-lt-scan-cycle.yml redeploy the container
# e. Verify LT_RC scan loop starts submitting orders via `docker logs aegis-lt-scan-cycle`
Rollback (within 2 minutes)¶
# Stop Rust lt-scan-cycle (edits take effect via the ad-hoc docker stop,
# no git revert needed since the compose file still has both flag sets in
# source control and we only need to bring Python back up fast)
ssh fukutani.ryo@192.168.42.252 \
"cd /volume1/aegis/repo/aegis_v3/lt-rust-docker && \
sudo /usr/local/bin/docker compose -f docker-compose.lt-scan-cycle.yml stop aegis-lt-scan-cycle"
# Start Python aegis-wft back up (image still present)
ssh fukutani.ryo@192.168.42.252 "sudo /usr/local/bin/docker start aegis-wft"
Post-cutover monitoring (24h on-call)¶
- Watch
aegis-lt-scan-cyclelogs for ENTRY/EXIT submissions - Verify Saxo trade confirmations arrive in the token-keeper-managed account
- Watch
lt_wft_comparison.jsonlfor scenario-level drift - Retain Python
aegis-wftcontainer image for 1 week rollback safety