# Developer Command Reference

> **Generated — do not edit by hand.**
> `python backend/tools/generate_developer_command_reference.py`
> regenerates this file from the repository, and `backend/scripts/ci/check_developer_command_reference.sh` fails the build if the committed copy differs. Edit the source of a command — its parser, its docstring, `run_all.sh` — and regenerate; an edit made here is reverted by the next run.

Every command below was discovered by reading this checkout: argparse parsers by AST, gates out of `run_all.sh`'s own `run` lines, tests by walking the test tree, migrations out of their `Migration(...)` literals, renderers out of `capture_scheduler.deploy.MODES`. Nothing is a remembered cheat sheet, which is the point: a command reference is read exactly when the reader does not already know the answer, so it is the worst possible document to maintain by memory.

**This checkout:** 19 CLI modules, 37 sub-commands, 12 shell scripts, 13 CI gates, 233 test files, 8258 test functions, 31 migrations.

Commands are written to be pasted from the **repository root**. Where one must run elsewhere it carries an explicit `cd` — the packages live under `backend/`, so `python -m signal_validator` is written `cd backend && python -m signal_validator`.

---

## Contents

- [Quick Start](#quick-start)
- [Development](#development)
- [Testing](#testing)
- [CI](#ci)
- [Scheduler](#scheduler)
- [Research Platform](#research-platform)
- [Dataset Programme](#dataset-programme)
- [Platform Broker](#platform-broker)
- [Strategy Registry](#strategy-registry)
- [Database](#database)
- [Maintenance Scripts](#maintenance-scripts)
- [Stress Tests](#stress-tests)
- [Debugging](#debugging)
- [Deployment](#deployment)
- [Emergency](#emergency)
- [Release Checklist](#release-checklist)

---

## Quick Start

Everything below is discovered from this checkout. Commands are written to be pasted **from the repository root**; where a command must run somewhere else it says so with an explicit `cd`.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python -m venv backend/venv` | Create the backend virtual environment | First checkout only | `backend/venv/bin/python` exists — the interpreter `run_all.sh` prefers |
| `source backend/venv/bin/activate` | Activate the backend virtual environment | Every shell that will run backend commands | The prompt gains the venv prefix |
| `deactivate` | Leave the virtual environment | When finished | The prompt prefix disappears |
| `pip install -r backend/requirements.txt` | Install backend dependencies | First checkout, and after any change to `requirements.txt` | Every declared dependency installed |
| `$EDITOR backend/.env` | Create the local environment file | First checkout — neither `.env` nor `.env.example` is committed, so there is nothing to copy | `backend/.env` holds at least the variables listed below |
| `cd backend && python main.py` | Start the API (uvicorn, `0.0.0.0:8000`, reload on) | Local development | Interactive API docs at http://localhost:8000/docs |
| `Ctrl-C` | Stop the API | When finished | The reloader and worker exit |
| `cd frontend && npm run dev` | Start the frontend dev server (`vite`) | Local development | Vite serves the app and prints its URL |
| `cd frontend-admin && npm run dev` | Start the frontend-admin dev server (`vite`) | Local development | Vite serves the app and prints its URL |

The environment CI sets for the whole gate, read out of `.github/workflows/ci.yml`. It is the minimum a checkout needs; the values there are CI's, not yours.

| Variable | Value in CI |
|---|---|
| `ADMIN_JWT_SECRET` | `ci-only-admin-secret-not-used-in-any-deployment!!` |
| `APP_ENV` | `ci` |
| `DB_HOST` | `127.0.0.1` |
| `DB_NAME` | `market_scanner` |
| `DB_PASSWORD` | `root` |
| `DB_PORT` | `3306` |
| `DB_USER` | `root` |
| `JWT_SECRET` | `ci-only-jwt-secret-not-used-in-any-deployment!!` |
| `LOG_LEVEL` | `WARNING` |
| `MYSQL_DATABASE` | `market_scanner` |
| `MYSQL_ROOT_PASSWORD` | `root` |
| `PYTHON` | `python` |
| `PYTHONDONTWRITEBYTECODE` | `1` |

Restarting the backend is stop-then-start: `python main.py` runs uvicorn with `reload=True`, so a source edit restarts the worker on its own and only a configuration or dependency change needs the process itself cycled.

## Development

The commands worth running on an ordinary day: what state is the system in, and did I break anything.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler status` | The scheduler's current position | Start of the day, and before asking anyone | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m platform_broker status` | The platform broker's current position | Start of the day, and before asking anyone | Prints what is configured, and is it usable. Exit 0 on success. |
| `cd backend && python -m pytest tests -q` | The whole test suite | Before every commit | 8258 test functions across 233 files |
| `bash backend/scripts/ci/run_all.sh` | The full gate — 13 checks | Before every push | `OK — every gate passed.` |
| `python backend/scripts/db_migrate.py status` | What revision the local database is at | After pulling a change that touches `db/migrations/` | One row per known migration |
| `python backend/tools/generate_developer_command_reference.py` | Regenerate this document | After adding a CLI, script, gate, migration or test | `docs/DEVELOPER_COMMAND_REFERENCE.md` rewritten |

## Testing

233 test files, 8258 test functions and 1254 test classes, counted statically from the tree. Parametrised tests collect as more than one case, so the number pytest reports is higher — these counts are what the source declares.

Every pytest invocation below runs from `backend/`.

### By directory

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m pytest tests -q` | The whole suite | Before every commit | 8258 test functions |
| `cd backend && python -m pytest tests/intelligence -q` | tests/intelligence — 9 files | While working in that area | 117 test functions |
| `cd backend && python -m pytest tests/operations -q` | tests/operations — 22 files | While working in that area | 510 test functions |
| `cd backend && python -m pytest tests/v2 -q` | tests/v2 — 89 files | While working in that area | 2621 test functions |

The 113 modules directly under `tests/` have no directory of their own — `pytest tests` is the only selection that covers them as a group, and each is individually runnable from the table below.

### By file

Every test module in the repository. The count is the number of `def test_*` functions the file declares.

| Command | Test Functions | Test Classes |
|---|---|---|
| `cd backend && python -m pytest tests/intelligence/test_api.py -q` | 12 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_engine_purity.py -q` | 4 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_explain_and_rationale.py -q` | 16 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_isolation.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_observation.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_regression.py -q` | 6 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_scoring_and_ranking.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_stats.py -q` | 16 | 0 |
| `cd backend && python -m pytest tests/intelligence/test_views.py -q` | 25 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_api.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_api_health.py -q` | 17 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_audit_lifecycle.py -q` | 13 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_console_api.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_dashboard.py -q` | 36 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_decisions.py -q` | 14 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_end_to_end.py -q` | 11 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_isolation.py -q` | 14 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_regression.py -q` | 11 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_store.py -q` | 23 | 0 |
| `cd backend && python -m pytest tests/operations/test_ops_views.py -q` | 31 | 0 |
| `cd backend && python -m pytest tests/operations/test_shadow_signals.py -q` | 87 | 9 |
| `cd backend && python -m pytest tests/operations/test_shadow_signals_ui_contract.py -q` | 32 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_api.py -q` | 21 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_config.py -q` | 11 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_engine.py -q` | 26 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_evidence.py -q` | 17 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_isolation.py -q` | 14 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_lifecycle.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_rules.py -q` | 51 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_statistics.py -q` | 18 | 0 |
| `cd backend && python -m pytest tests/operations/test_validation_store.py -q` | 18 | 0 |
| `cd backend && python -m pytest tests/test_ai_auto_scan.py -q` | 111 | 13 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_candle_reliability.py -q` | 74 | 16 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_evidence_ledger.py -q` | 62 | 14 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_evidence_logging_switch.py -q` | 35 | 8 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_quality_collector.py -q` | 77 | 17 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_recovery_walk.py -q` | 49 | 12 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_trade_geometry.py -q` | 33 | 5 |
| `cd backend && python -m pytest tests/test_ai_auto_scan_ui_contract.py -q` | 56 | 9 |
| `cd backend && python -m pytest tests/test_analyse_outcome_semantics.py -q` | 31 | 7 |
| `cd backend && python -m pytest tests/test_api_authentication.py -q` | 10 | 0 |
| `cd backend && python -m pytest tests/test_bootstrap_determinism.py -q` | 9 | 3 |
| `cd backend && python -m pytest tests/test_broker_authentication.py -q` | 7 | 0 |
| `cd backend && python -m pytest tests/test_broker_http_forensics.py -q` | 68 | 13 |
| `cd backend && python -m pytest tests/test_broker_identity_experiment.py -q` | 55 | 10 |
| `cd backend && python -m pytest tests/test_broker_lifecycle.py -q` | 25 | 7 |
| `cd backend && python -m pytest tests/test_broker_request_coordination.py -q` | 31 | 5 |
| `cd backend && python -m pytest tests/test_candle_coverage_and_export.py -q` | 15 | 2 |
| `cd backend && python -m pytest tests/test_capability_alignment.py -q` | 43 | 0 |
| `cd backend && python -m pytest tests/test_capture_scheduler.py -q` | 132 | 21 |
| `cd backend && python -m pytest tests/test_capture_scheduler_console.py -q` | 42 | 7 |
| `cd backend && python -m pytest tests/test_capture_scheduler_lock.py -q` | 48 | 8 |
| `cd backend && python -m pytest tests/test_capture_scheduler_ui_contract.py -q` | 31 | 0 |
| `cd backend && python -m pytest tests/test_cli_operations_catalogue.py -q` | 21 | 0 |
| `cd backend && python -m pytest tests/test_cli_operations_checklist.py -q` | 35 | 0 |
| `cd backend && python -m pytest tests/test_cli_operations_registry.py -q` | 26 | 0 |
| `cd backend && python -m pytest tests/test_cli_operations_service.py -q` | 38 | 0 |
| `cd backend && python -m pytest tests/test_cli_operations_ui_contract.py -q` | 30 | 0 |
| `cd backend && python -m pytest tests/test_confirm_trade_flow.py -q` | 44 | 7 |
| `cd backend && python -m pytest tests/test_contract_funnel.py -q` | 29 | 6 |
| `cd backend && python -m pytest tests/test_contract_quantity.py -q` | 31 | 6 |
| `cd backend && python -m pytest tests/test_contract_registration_horizon.py -q` | 10 | 3 |
| `cd backend && python -m pytest tests/test_data_audit.py -q` | 30 | 0 |
| `cd backend && python -m pytest tests/test_db_migrations.py -q` | 51 | 9 |
| `cd backend && python -m pytest tests/test_decision_log.py -q` | 47 | 11 |
| `cd backend && python -m pytest tests/test_developer_command_reference.py -q` | 47 | 0 |
| `cd backend && python -m pytest tests/test_ema_crossover_v2_production.py -q` | 25 | 5 |
| `cd backend && python -m pytest tests/test_ema_crossover_v3_decommissioned.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/test_exit_model_amendment.py -q` | 44 | 5 |
| `cd backend && python -m pytest tests/test_filter_strategy_scoping.py -q` | 11 | 3 |
| `cd backend && python -m pytest tests/test_governed_scan_configuration.py -q` | 45 | 7 |
| `cd backend && python -m pytest tests/test_historical_market_data_pipeline.py -q` | 69 | 0 |
| `cd backend && python -m pytest tests/test_index_scanner_governed_configuration.py -q` | 49 | 10 |
| `cd backend && python -m pytest tests/test_lazy_option_chain.py -q` | 21 | 6 |
| `cd backend && python -m pytest tests/test_market_data_day_exports.py -q` | 45 | 7 |
| `cd backend && python -m pytest tests/test_market_intelligence.py -q` | 81 | 0 |
| `cd backend && python -m pytest tests/test_market_pages_ui_contract.py -q` | 34 | 8 |
| `cd backend && python -m pytest tests/test_market_sessions.py -q` | 70 | 13 |
| `cd backend && python -m pytest tests/test_nfo_session_tail.py -q` | 17 | 4 |
| `cd backend && python -m pytest tests/test_non_shrink_ingestion.py -q` | 19 | 5 |
| `cd backend && python -m pytest tests/test_option_chain_acquisition.py -q` | 17 | 6 |
| `cd backend && python -m pytest tests/test_option_expiry_floor.py -q` | 16 | 5 |
| `cd backend && python -m pytest tests/test_option_geometry_harness.py -q` | 18 | 5 |
| `cd backend && python -m pytest tests/test_option_stop_model.py -q` | 28 | 6 |
| `cd backend && python -m pytest tests/test_option_trade_quality_integration.py -q` | 24 | 5 |
| `cd backend && python -m pytest tests/test_optional_stores.py -q` | 5 | 1 |
| `cd backend && python -m pytest tests/test_ownership_enforcement.py -q` | 80 | 12 |
| `cd backend && python -m pytest tests/test_paper_position_monitor.py -q` | 89 | 14 |
| `cd backend && python -m pytest tests/test_paper_risk_isolation.py -q` | 51 | 11 |
| `cd backend && python -m pytest tests/test_paper_trading_report.py -q` | 77 | 12 |
| `cd backend && python -m pytest tests/test_partial_candle_containment.py -q` | 25 | 6 |
| `cd backend && python -m pytest tests/test_phase10_stage4.py -q` | 41 | 8 |
| `cd backend && python -m pytest tests/test_phase12_option_chain.py -q` | 33 | 9 |
| `cd backend && python -m pytest tests/test_phase13_optimisation.py -q` | 67 | 8 |
| `cd backend && python -m pytest tests/test_phase75_replay.py -q` | 36 | 8 |
| `cd backend && python -m pytest tests/test_phase7_remediation.py -q` | 28 | 6 |
| `cd backend && python -m pytest tests/test_phase9_lifecycle.py -q` | 44 | 7 |
| `cd backend && python -m pytest tests/test_platform_broker.py -q` | 69 | 9 |
| `cd backend && python -m pytest tests/test_platform_broker_api.py -q` | 26 | 6 |
| `cd backend && python -m pytest tests/test_position_access_intent.py -q` | 42 | 8 |
| `cd backend && python -m pytest tests/test_position_collection_access_intent.py -q` | 69 | 12 |
| `cd backend && python -m pytest tests/test_position_initial_stop_persistence.py -q` | 13 | 5 |
| `cd backend && python -m pytest tests/test_position_monitor_underlying.py -q` | 29 | 7 |
| `cd backend && python -m pytest tests/test_position_risk_unit.py -q` | 32 | 7 |
| `cd backend && python -m pytest tests/test_production_stop_execution.py -q` | 31 | 7 |
| `cd backend && python -m pytest tests/test_profit_capture_trail.py -q` | 40 | 8 |
| `cd backend && python -m pytest tests/test_registry_direct_authority.py -q` | 36 | 7 |
| `cd backend && python -m pytest tests/test_replay_run_provenance.py -q` | 22 | 6 |
| `cd backend && python -m pytest tests/test_replay_workstation.py -q` | 49 | 9 |
| `cd backend && python -m pytest tests/test_research_dashboard.py -q` | 47 | 8 |
| `cd backend && python -m pytest tests/test_research_engine_conformance.py -q` | 13 | 0 |
| `cd backend && python -m pytest tests/test_runner_target_model.py -q` | 18 | 5 |
| `cd backend && python -m pytest tests/test_scan_quota_enforcement.py -q` | 85 | 13 |
| `cd backend && python -m pytest tests/test_sector_indicators.py -q` | 59 | 12 |
| `cd backend && python -m pytest tests/test_sector_reasoning.py -q` | 34 | 6 |
| `cd backend && python -m pytest tests/test_session_capture_job.py -q` | 59 | 9 |
| `cd backend && python -m pytest tests/test_session_data_pipeline.py -q` | 38 | 0 |
| `cd backend && python -m pytest tests/test_session_evidence_instrumentation.py -q` | 62 | 9 |
| `cd backend && python -m pytest tests/test_shadow_capture.py -q` | 59 | 9 |
| `cd backend && python -m pytest tests/test_shadow_command_center.py -q` | 101 | 10 |
| `cd backend && python -m pytest tests/test_shadow_command_center_ui_contract.py -q` | 33 | 0 |
| `cd backend && python -m pytest tests/test_shadow_coverage.py -q` | 46 | 10 |
| `cd backend && python -m pytest tests/test_shadow_forensics.py -q` | 58 | 9 |
| `cd backend && python -m pytest tests/test_shadow_mode_package.py -q` | 58 | 9 |
| `cd backend && python -m pytest tests/test_shadow_operations_dashboard.py -q` | 170 | 18 |
| `cd backend && python -m pytest tests/test_shadow_operations_ui_contract.py -q` | 44 | 6 |
| `cd backend && python -m pytest tests/test_shadow_outcome.py -q` | 68 | 11 |
| `cd backend && python -m pytest tests/test_shadow_quality_calibration.py -q` | 85 | 12 |
| `cd backend && python -m pytest tests/test_shadow_quality_monitor.py -q` | 78 | 12 |
| `cd backend && python -m pytest tests/test_shadow_replay_batch.py -q` | 74 | 10 |
| `cd backend && python -m pytest tests/test_shadow_replay_signals.py -q` | 99 | 16 |
| `cd backend && python -m pytest tests/test_shadow_watch_historical_fetch.py -q` | 45 | 12 |
| `cd backend && python -m pytest tests/test_signal_quality.py -q` | 39 | 7 |
| `cd backend && python -m pytest tests/test_signal_validator.py -q` | 47 | 10 |
| `cd backend && python -m pytest tests/test_spike_exit_v3.py -q` | 14 | 6 |
| `cd backend && python -m pytest tests/test_stock_strategy_governance.py -q` | 55 | 10 |
| `cd backend && python -m pytest tests/test_stop_execution_study.py -q` | 47 | 8 |
| `cd backend && python -m pytest tests/test_strategy_registry_consistency.py -q` | 52 | 0 |
| `cd backend && python -m pytest tests/test_strategy_registry_report.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/test_trade_statistics_equivalence.py -q` | 26 | 5 |
| `cd backend && python -m pytest tests/test_trading_calendar.py -q` | 32 | 7 |
| `cd backend && python -m pytest tests/test_user_broker_status.py -q` | 24 | 0 |
| `cd backend && python -m pytest tests/test_v2_registry_gate.py -q` | 27 | 8 |
| `cd backend && python -m pytest tests/test_v2_scanner_integration.py -q` | 48 | 8 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_harness.py -q` | 13 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage1_ingest.py -q` | 11 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage2_frame.py -q` | 9 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage3_features.py -q` | 9 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage5_evaluator.py -q` | 18 | 10 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage5_strategy.py -q` | 6 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage6_filter.py -q` | 6 | 0 |
| `cd backend && python -m pytest tests/v2/fault_injection/test_fault_injection_stage7_risk.py -q` | 5 | 0 |
| `cd backend && python -m pytest tests/v2/integration/test_shadow_mode.py -q` | 19 | 4 |
| `cd backend && python -m pytest tests/v2/unit/config/test_loader.py -q` | 26 | 0 |
| `cd backend && python -m pytest tests/v2/unit/config/test_resolved.py -q` | 26 | 5 |
| `cd backend && python -m pytest tests/v2/unit/config/test_schema.py -q` | 7 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/stage4_features/test_open_positions.py -q` | 12 | 4 |
| `cd backend && python -m pytest tests/v2/unit/data/stage4_features/test_recent_losses.py -q` | 13 | 6 |
| `cd backend && python -m pytest tests/v2/unit/data/stage4_features/test_records.py -q` | 11 | 4 |
| `cd backend && python -m pytest tests/v2/unit/data/stage4_features/test_session_pnl.py -q` | 12 | 6 |
| `cd backend && python -m pytest tests/v2/unit/data/test_candles.py -q` | 36 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_atr.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_ci.py -q` | 9 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_ema.py -q` | 10 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_naming.py -q` | 7 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_pir.py -q` | 10 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_registry.py -q` | 26 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_session.py -q` | 16 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_features_vwap.py -q` | 7 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_gateway.py -q` | 19 | 6 |
| `cd backend && python -m pytest tests/v2/unit/data/test_gateway_impl.py -q` | 13 | 3 |
| `cd backend && python -m pytest tests/v2/unit/data/test_ingest.py -q` | 22 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_market_frame.py -q` | 3 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_market_frame_builder.py -q` | 23 | 0 |
| `cd backend && python -m pytest tests/v2/unit/data/test_trend.py -q` | 35 | 6 |
| `cd backend && python -m pytest tests/v2/unit/db/position_repositories/test_in_memory.py -q` | 17 | 7 |
| `cd backend && python -m pytest tests/v2/unit/db/position_repositories/test_sqlalchemy.py -q` | 16 | 6 |
| `cd backend && python -m pytest tests/v2/unit/db/test_positions.py -q` | 18 | 7 |
| `cd backend && python -m pytest tests/v2/unit/execution/test_execution_interface.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/execution/test_noop_executor.py -q` | 11 | 0 |
| `cd backend && python -m pytest tests/v2/unit/execution/test_paper_executor.py -q` | 31 | 8 |
| `cd backend && python -m pytest tests/v2/unit/exits/test_exit_interface.py -q` | 21 | 0 |
| `cd backend && python -m pytest tests/v2/unit/exits/test_exit_registry.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/filters/test_filter_interface.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/v2/unit/filters/test_filter_registry.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/filters/test_m4_exhaustion.py -q` | 57 | 12 |
| `cd backend && python -m pytest tests/v2/unit/filters/test_regime_ci.py -q` | 53 | 11 |
| `cd backend && python -m pytest tests/v2/unit/filters/test_trading_window.py -q` | 74 | 15 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_breakers.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_bus.py -q` | 4 | 0 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_dod_integration.py -q` | 3 | 0 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_events.py -q` | 10 | 0 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_logger.py -q` | 9 | 0 |
| `cd backend && python -m pytest tests/v2/unit/observability/test_sinks.py -q` | 6 | 0 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_break_even.py -q` | 176 | 30 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_hard_stop_loss.py -q` | 124 | 21 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_hard_stop_loss_v2.py -q` | 52 | 11 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_interface.py -q` | 17 | 6 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_manual_exit.py -q` | 72 | 11 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_partial_exit.py -q` | 132 | 20 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_policy_pipeline.py -q` | 10 | 6 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_policy_registry.py -q` | 10 | 4 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_session_close.py -q` | 78 | 16 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_spike_exit.py -q` | 94 | 14 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_target_exit.py -q` | 128 | 22 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_trailing_stop.py -q` | 205 | 32 |
| `cd backend && python -m pytest tests/v2/unit/positions/policies/test_volatility_exit.py -q` | 84 | 14 |
| `cd backend && python -m pytest tests/v2/unit/positions/test_manager.py -q` | 25 | 7 |
| `cd backend && python -m pytest tests/v2/unit/positions/test_position_excursion.py -q` | 21 | 4 |
| `cd backend && python -m pytest tests/v2/unit/positions/test_position_registry.py -q` | 12 | 6 |
| `cd backend && python -m pytest tests/v2/unit/positions/test_snapshot.py -q` | 8 | 2 |
| `cd backend && python -m pytest tests/v2/unit/positions/test_state_machine.py -q` | 11 | 5 |
| `cd backend && python -m pytest tests/v2/unit/registry/test_m14_dod_integration.py -q` | 7 | 0 |
| `cd backend && python -m pytest tests/v2/unit/risk/test_risk_rule_registry.py -q` | 16 | 4 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_engine.py -q` | 25 | 8 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_entry_plan_options.py -q` | 9 | 1 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_evaluate_risk_rules.py -q` | 15 | 6 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_option_delta_projection.py -q` | 16 | 4 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_option_greeks.py -q` | 35 | 3 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_option_selector.py -q` | 14 | 7 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_option_trade_quality.py -q` | 43 | 8 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_request.py -q` | 16 | 3 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_stock_option_selector.py -q` | 41 | 8 |
| `cd backend && python -m pytest tests/v2/unit/signal_engine/test_trade_plan.py -q` | 23 | 6 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_capabilities.py -q` | 15 | 0 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_ema_crossover.py -q` | 82 | 20 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_ema_crossover_v2.py -q` | 33 | 8 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_strategy_contract_verification.py -q` | 25 | 0 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_strategy_interface.py -q` | 13 | 0 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_strategy_registry.py -q` | 8 | 0 |
| `cd backend && python -m pytest tests/v2/unit/strategy/test_vwap_breakout.py -q` | 64 | 14 |
| `cd backend && python -m pytest tests/v2/unit/test_filter_evaluator.py -q` | 18 | 7 |
| `cd backend && python -m pytest tests/v2/unit/test_strategy_evaluator.py -q` | 20 | 0 |

### Selecting less than a file

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m pytest tests/test_db_migrations.py::test_name -q` | One test function | Iterating on a single failure | One test runs; the rest are not collected |
| `cd backend && python -m pytest tests/v2 -k "vwap and not slow" -q` | Every test whose name matches an expression | Working across files on one concept | Only matching tests run; the rest report as deselected |
| `cd backend && python -m pytest tests -q --collect-only` | List what would run, run nothing | Checking a selection before committing to it | The collected node ids |

### Flags

These come from pytest itself rather than from this repository — the only table in this document that is not discovered.

| Flag | Effect |
|---|---|
| `-q` | One character per test; the form CI uses |
| `-v` | One line per test with its node id |
| `-x` | Stop at the first failure |
| `--lf` | Re-run only what failed last time |
| `-s` | Do not capture stdout — needed to see `print` and `pdb` |
| `-p no:randomly` | Disable test-order randomisation, if installed |
| `-n auto` | Run in parallel across cores — requires `pytest-xdist` |
| `--durations=10` | Report the ten slowest tests |

### Coverage

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m pytest tests/operations/ --cov=operations --cov-report=term-missing --cov-fail-under=80 -q` | Coverage of `operations` with its 80% floor | Before pushing a change to that package | Exit 0 at or above 80%; **exit 1** below it |
| `bash backend/scripts/ci/check_operations_coverage.sh` | The same measurement, as CI runs it | Same, in one command | `OK` at or above 80% |
| `cd backend && python -m pytest installed. --cov=v2 --cov-report=term-missing --cov-fail-under=80 -q` | Coverage of `v2` with its 80% floor | Before pushing a change to that package | Exit 0 at or above 80%; **exit 1** below it |
| `bash backend/scripts/ci/check_v2_coverage.sh` | The same measurement, as CI runs it | Same, in one command | `OK` at or above 80% |

Measurement scope is `backend/.coveragerc`; the floor is the `--cov-fail-under` in the gate script, and nothing else enforces it.

## CI

`backend/scripts/ci/run_all.sh` is the gate — 13 checks, every one of them run even after an earlier one fails, with the failures summarised at the end. `.github/workflows/ci.yml` calls that script, so local and CI cannot disagree about what green means.

The table below is parsed out of `run_all.sh`'s own `run` lines. Adding a gate to that script adds a row here.

| # | Gate | Group | Command (from `backend/`) | What It Validates |
|---|---|---|---|---|
| 1 | `tests` | The test suite | `python -m pytest tests -q` | The whole test suite — every test in the tree. |
| 2 | `v2-isolation` | Structural contracts | `bash backend/scripts/ci/check_v2_isolation.sh` | CI grep-checks for V2 isolation rules. Exits non-zero on any violation. |
| 3 | `v2-importlinter` | Structural contracts | `bash backend/scripts/ci/check_v2_importlinter.sh` | CI wrapper for import-linter — Kickoff §4 task 36. |
| 4 | `v2-config-schema-sync` | Structural contracts | `bash backend/scripts/ci/check_v2_config_schema_sync.sh` | CI drift check — Spec 05 amendment Clause 3, Phase 1 kickoff amendment Clause 4. |
| 5 | `operations-isolation` | Structural contracts | `bash backend/scripts/ci/check_operations_isolation.sh` | CI entry point for the Phase 15 Operations boundary checks. |
| 6 | `research-engine` | Structural contracts | `bash backend/scripts/ci/check_research_engine.sh` | CI entry point for the Research Engine conformance rules — Review C-4. |
| 7 | `schema-evolution` | Structural contracts | `python backend/scripts/ci/check_schema_evolution.py` | Structural check — `db/migrations/` is the only schema-evolution mechanism. |
| 8 | `ai-auto-scan-isolation` | Structural contracts | `bash backend/scripts/ci/check_ai_auto_scan_isolation.sh` | CI entry point for the AI Auto Scan boundary checks. |
| 9 | `research-archive-integrity` | Governance | `bash backend/scripts/ci/check_research_archive_integrity.sh` | Research archive integrity check — RG-07 §3. |
| 10 | `signal-validator` | Governance | `bash backend/scripts/ci/check_signal_validator.sh` | CI entry point for the Signal Replay Validator boundary checks. |
| 11 | `developer-command-reference` | Generated documentation | `bash backend/scripts/ci/check_developer_command_reference.sh` | CI gate — the Developer Command Reference is regenerated and compared. |
| 12 | `v2-coverage` | Coverage floors | `bash backend/scripts/ci/check_v2_coverage.sh` | CI coverage gate — Kickoff §4 task 39, §6 DoD. |
| 13 | `operations-coverage` | Coverage floors | `bash backend/scripts/ci/check_operations_coverage.sh` | CI coverage gate for the Phase 15 Operations layer. |

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `bash backend/scripts/ci/run_all.sh` | Every gate — the whole thing | Before every push | `OK — every gate passed.`, or a summary naming each failed gate |
| `CI_SKIP="tests v2-coverage" bash backend/scripts/ci/run_all.sh` | Skip named gates while iterating locally | Local iteration only — the workflow never sets `CI_SKIP` | Skipped gates are listed separately in the summary |
| `PYTHON=python3 bash backend/scripts/ci/run_all.sh` | Choose the interpreter | When `backend/venv` is absent or the wrong one | The header line prints the interpreter and its version |

### Every CI script

| Command | Purpose |
|---|---|
| `bash backend/scripts/ci/check_ai_auto_scan_isolation.sh` | CI entry point for the AI Auto Scan boundary checks. |
| `bash backend/scripts/ci/check_developer_command_reference.sh` | CI gate — the Developer Command Reference is regenerated and compared. |
| `bash backend/scripts/ci/check_operations_coverage.sh` | CI coverage gate for the Phase 15 Operations layer. |
| `bash backend/scripts/ci/check_operations_isolation.sh` | CI entry point for the Phase 15 Operations boundary checks. |
| `bash backend/scripts/ci/check_research_archive_integrity.sh` | Research archive integrity check — RG-07 §3. |
| `bash backend/scripts/ci/check_research_engine.sh` | CI entry point for the Research Engine conformance rules — Review C-4. |
| `bash backend/scripts/ci/check_signal_validator.sh` | CI entry point for the Signal Replay Validator boundary checks. |
| `bash backend/scripts/ci/check_v2_config_schema_sync.sh` | CI drift check — Spec 05 amendment Clause 3, Phase 1 kickoff amendment Clause 4. |
| `bash backend/scripts/ci/check_v2_coverage.sh` | CI coverage gate — Kickoff §4 task 39, §6 DoD. |
| `bash backend/scripts/ci/check_v2_importlinter.sh` | CI wrapper for import-linter — Kickoff §4 task 36. |
| `bash backend/scripts/ci/check_v2_isolation.sh` | CI grep-checks for V2 isolation rules. Exits non-zero on any violation. |
| `bash backend/scripts/ci/run_all.sh` | The whole gate, in one command — Production Readiness Review C-5. |

### What the GitHub workflow runs

The workflow provisions an environment and calls the gate. These are its `run:` steps, in order.

| Job | Step | Command |
|---|---|---|
| backend | Bootstrap the CI database | `python backend/scripts/ci/bootstrap_ci_db.py` |
| backend | Verify the database is at the expected revision | `python backend/scripts/db_migrate.py status` |
| backend | Run the full gate | `bash backend/scripts/ci/run_all.sh` |
| frontend | Install | `npm ci` |
| frontend | Build | `npm run build` |

## Scheduler

The Capture Scheduler discharges the standing capture obligation automatically. `run` is the only verb that does anything; `status`, `config` and `render` are read-only, and `status` exits non-zero when the schedule is not being met, which makes it usable as a monitoring probe without anything parsing its output.

The Capture Scheduler — discharges the standing capture obligation (RG-04 §7, P-5) automatically. Closes N-1.

Source: [`backend/capture_scheduler/cli.py`](../backend/capture_scheduler/cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler run daily` | run a job now | Deliberate operator action — it writes. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler run session` | run a job now | Deliberate operator action — it writes. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler run weekly` | run a job now | Deliberate operator action — it writes. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler status` | what ran, whether the data is current, what is next | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler selftest` | check every precondition a run needs, and stop | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler config` | the effective configuration and its sources | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler render cron` | render a deployment artefact | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler render systemd` | render a deployment artefact | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler render github` | render a deployment artefact | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler render launchd` | render a deployment artefact | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler render env` | render a deployment artefact | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `run` | `job` | positional | daily = register contracts only; session = capture today's session, the only job with a same-day deadline (A-70); weekly = register and backfill the week as reconciliation |
| `status` | `--jobs-only` | optional | skip the corpus read. The job ledger is a file; the data currency half forks `capture_weekly.py --status` and needs a database, so this is the form to use when there isn't one. |
| `selftest` | `--job` | optional | which job to report the preconditions for; only the platform-broker line differs |
| `render` | `mode` | positional | one of `cron`, `systemd`, `github`, `launchd`, `env` |
| `render` | `--repo-root` | optional | the checkout path the artefact will run from (default: this checkout). Pass a placeholder to render an example for a host you are not on. |

Exit codes — the interface, in full:

| Exit Code | Meaning |
|---|---|
| `0` | the job ran and its step succeeded — or the kill switch is off, which is a deliberate state and not a failure. |
| `1` | the step failed, or a lock was held. An alert has already been raised. |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m capture_scheduler run daily     # register contracts
python -m capture_scheduler run session   # capture today's session
python -m capture_scheduler run weekly    # register and backfill
python -m capture_scheduler status        # jobs, data currency, what next
python -m capture_scheduler config        # the effective configuration
python -m capture_scheduler render cron | systemd | github | launchd | env
```

Ask the world one question, from the environment the scheduler's steps will run in.

Source: [`backend/capture_scheduler/probe.py`](../backend/capture_scheduler/probe.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler.probe` | Ask the world one question, from the environment the scheduler's steps will run in. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--trading-day` | optional | ask the exchange calendar whether this date is a trading session (default: today) |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m capture_scheduler.probe                        # is the DB up?
python -m capture_scheduler.probe --trading-day 2026-08-15
```

### Deployment renderers

Modes come from `capture_scheduler.deploy.MODES`; `env` is accepted alongside them.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler render cron` | Render the cron artefact for this checkout | When installing or re-installing the schedule on a host | The artefact on stdout — nothing is installed |
| `cd backend && python -m capture_scheduler render systemd` | Render the systemd artefact for this checkout | When installing or re-installing the schedule on a host | The artefact on stdout — nothing is installed |
| `cd backend && python -m capture_scheduler render github` | Render the github artefact for this checkout | When installing or re-installing the schedule on a host | The artefact on stdout — nothing is installed |
| `cd backend && python -m capture_scheduler render launchd` | Render the launchd artefact for this checkout | When installing or re-installing the schedule on a host | The artefact on stdout — nothing is installed |
| `cd backend && python -m capture_scheduler render env` | Render the env artefact for this checkout | When installing or re-installing the schedule on a host | The artefact on stdout — nothing is installed |
| `cd backend && python -m capture_scheduler render cron --repo-root /srv/ai_trade` | Render for a checkout path other than this one | Rendering on a laptop for a server | The same artefact with the given paths |

The artefacts already committed, for comparison with what `render` produces:

| Artefact | Kind |
|---|---|
| [`deploy/capture-scheduler/README.md`](../deploy/capture-scheduler/README.md) | md |
| [`deploy/capture-scheduler/capture-scheduler.env.example`](../deploy/capture-scheduler/capture-scheduler.env.example) | example |
| [`deploy/capture-scheduler/research-capture.crontab`](../deploy/capture-scheduler/research-capture.crontab) | crontab |
| [`deploy/capture-scheduler/research-capture.github-workflow.yml`](../deploy/capture-scheduler/research-capture.github-workflow.yml) | yml |
| [`deploy/capture-scheduler/research-capture.launchd`](../deploy/capture-scheduler/research-capture.launchd) | launchd |
| [`deploy/capture-scheduler/research-capture.systemd`](../deploy/capture-scheduler/research-capture.systemd) | systemd |

## Research Platform

A leaf that renders the programme's position and reconciles the corpus against itself. Every figure it prints is extracted from the document that owns it — it derives, it never re-declares.

_No CLI discovered for this area._

## Dataset Programme

Validation data as a programme-level asset: a ledger, an allocation policy and a lifecycle. The lifecycle verbs are the only way a slice changes state, and `spend` is terminal.

_No CLI discovered for this area._

## Platform Broker

The platform's own broker identity — not a user's. `status` and `audit` read stored state and make no broker call; `test`, `connect` and `refresh` reach the broker and say so by name.

The platform's broker identity — status and session control.

Source: [`backend/platform_broker/__main__.py`](../backend/platform_broker/__main__.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m platform_broker status` | what is configured, and is it usable | Any time — read-only. | Prints what is configured, and is it usable. Exit 0 on success. |
| `cd backend && python -m platform_broker test` | one real broker call, exit 0 if it worked | Any time — read-only. | Prints one real broker call, exit 0 if it worked. Exit 0 on success. |
| `cd backend && python -m platform_broker connect` | fresh TOTP login, persist the tokens | Deliberate operator action — it writes. | Prints fresh TOTP login, persist the tokens. Exit 0 on success. |
| `cd backend && python -m platform_broker refresh` | renew the JWT from the refresh token | Deliberate operator action — it writes. | Prints renew the JWT from the refresh token. Exit 0 on success. |
| `cd backend && python -m platform_broker logout` | drop the session, keep the credentials | Deliberate operator action — it writes. | Prints drop the session, keep the credentials. Exit 0 on success. |
| `cd backend && python -m platform_broker audit` | the recent lifecycle events | Any time — read-only. | Prints the recent lifecycle events. Exit 0 on success. |

Options accepted by every sub-command:

| Option | Kind | Meaning |
|---|---|---|
| `--connection-id` | optional | target a specific connection instead of the default |
| `--json` | optional | machine-readable output |
| `--limit` | optional | audit: how many events (default 25) |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m platform_broker status      # what is configured, and is it usable
python -m platform_broker test        # one real broker call, exit 0 if it worked
python -m platform_broker connect     # fresh TOTP login, persist the tokens
python -m platform_broker refresh     # renew the JWT from the refresh token
python -m platform_broker logout      # drop the session, keep the credentials
python -m platform_broker audit       # the recent lifecycle events
```

## Strategy Registry

A closed campaign's verdict reaches `strategy_registry` mechanically, not by memory. Edit the campaign record; never the row.

Print every strategy and its registration status.

Source: [`backend/scripts/strategy_registry_report.py`](../backend/scripts/strategy_registry_report.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/strategy_registry_report.py` | Print every strategy and its registration status. | Deliberate operator action — the bare form is the one that acts; run `--check` first. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/strategy_registry_report.py --json` | emit the audit as JSON | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/strategy_registry_report.py --check` | exit 1 when the audit reports an error | Any time — read-only. | Exit 1 if the committed artefact differs |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--json` | optional | emit the audit as JSON |
| `--check` | optional | exit 1 when the audit reports an error |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python scripts/strategy_registry_report.py            # table
python scripts/strategy_registry_report.py --json     # machine-readable
python scripts/strategy_registry_report.py --check    # exit 1 on an error
```

## Database

`backend/db/migrations/` is the only supported way to change the schema — `check_schema_evolution.py` fails the build on schema-mutating DDL anywhere else. Migrations run before `create_all`, so every migration must no-op when its table does not exist.

Schema migration CLI — the operator-facing half of Review finding H-5.

Source: [`backend/scripts/db_migrate.py`](../backend/scripts/db_migrate.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/db_migrate.py status` | one row per known migration | Any time — read-only. | Prints one row per known migration. Exit 0 on success. |
| `python backend/scripts/db_migrate.py upgrade` | apply every pending migration | Deliberate operator action — it writes. | Prints apply every pending migration. Exit 0 on success. |
| `python backend/scripts/db_migrate.py downgrade <version>` | reverse exactly one migration | Deliberate operator action — it writes. | Prints reverse exactly one migration. Exit 0 on success. |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `status` | `--group` | optional | one of `core`, `research` |
| `upgrade` | `--group` | optional | one of `core`, `research` — default `core` |
| `upgrade` | `--dry-run` | optional | report what would run; execute nothing |
| `downgrade` | `version` | positional | takes a value |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/scripts/db_migrate.py status
python backend/scripts/db_migrate.py upgrade [--dry-run] [--group core|research]
python backend/scripts/db_migrate.py downgrade <version>
```

### Known migrations

31 migrations — 31 `core`, 0 `research`. A pending `research` migration is expected and does not fail CI: the Research Workbench creates its tables on first request, not at boot.

| Version | Group | Description | Reversible |
|---|---|---|---|
| `0001` | core | scanner_sessions: quota columns + the (user_id, request_id) idempotency index | yes |
| `0002` | core | auto_paper_positions/logs: Phase-14 observational columns, equity nullability | no — the nullability relaxation cannot be reversed without knowing whether equity rows — which legitimately hold NULL in those four columns — have since been written. Re-tightening would fail or silently drop them. |
| `0003` | core | users.last_seen_at; collapse duplicate broker_sessions and index them uniquely | no — the collapse deletes superseded broker_sessions rows. They cannot be reconstructed, so a downgrade that recreated the index-free schema would restore the shape without the data and misrepresent the result. |
| `0004` | core | subscription/payment/usage columns, credit idempotency index, legacy column drops | no — `total_scans_used` and `free_scans_used` are dropped. Their values were superseded by per-subscription snapshots and cannot be recomputed from what remains, so a downgrade would recreate empty columns that read as data. |
| `0005` | core | rename ai_strategy_config → strategy_registry; add governance columns, backfill, drop capabilities | no — the reshape drops ten capability columns and `is_enabled`, and deletes retired strategy rows. Neither the dropped values nor the deleted rows survive anywhere, so a downgrade could restore the old shape but never the old contents — and a register that looks restored but is not is worse than one that plainly cannot be. |
| `0006` | core | ops_decisions/ops_audit_events/ops_failures: additive columns | yes |
| `0007` | core | ops_recommendations/ops_recommendation_events: additive columns | yes |
| `0008` | core | create config_snapshots and v2_events (Spec 04 + Spec 05 amendment Clause 5) | yes |
| `0009` | core | auto_paper_positions: trade_plan_id + correlation_id, so a position names the plan it came from | yes |
| `0010` | core | drop `orders` and `daily_stats` where empty; report and keep them where not | no — recreating an empty `orders` / `daily_stats` would restore a shape no code defines any more — the ORM models were deleted in Phase 3 — so the result would be two tables nothing can read, write or describe. Only provably-empty tables are dropped, so nothing is lost to reverse. |
| `0011` | core | drop `platform_settings`; its contents are columns on subscription_plans | no — no model defines `platform_settings` any more, so a downgrade could only recreate a table whose shape this codebase no longer knows. The values it held live on `subscription_plans` and are not lost. |
| `0012` | core | create platform_broker_connections + platform_broker_audit — the platform's own broker identity, separate from the user-scoped broker_connections | yes |
| `0013` | core | scanner_sessions + scanner_signals: the immutable SL-01 billability snapshot | yes |
| `0014` | core | scanner_sessions: contracts_received / after_strategy / after_filters — the per-stage contract funnel | yes |
| `0015` | core | replay_runs: spec_name / spec_version / spec_cell / batch_uid — which specification produced the run | yes |
| `0016` | core | replay_runs: evidence_class / evidence_level / production_validated — how a stored run is classified | yes |
| `0017` | core | replay_runs: validation_source — the authority that admitted the run, recorded at creation and never inferred | yes |
| `0018` | core | auto_paper_positions: user_id + broker_connection_id + manual_exit_requested_at, so a position names its owner, the broker connection it was opened on, and an operator-signalled exit | yes |
| `0019` | core | auto_paper_logs: user_id, so a decision log entry names the account it belongs to and the panel's read can scope by owner in the query | yes |
| `0020` | core | ai_auto_scan_sessions: risk_reward, so the reward-to-risk ratio a user selected is frozen onto the session with the other four configuration values instead of being re-derived per cycle | yes |
| `0021` | core | ai_auto_scan_sessions: last_candle_at plus the cycle-health counters, so a scan records which market bar it evaluated and how many of its cycles could not evaluate one at all | yes |
| `0022` | core | ai_auto_scan_evidence: the entry, stop, risk, ATR, delta, noise and achieved ATR multiple a geometry refusal was measured against, the stop model that produced them, and the strength blend decomposed — so a rejection records what it rejected and not only that it did | yes |
| `0023` | core | auto_paper_positions: the stop the entry plan placed, stored beside the stop protection has since been moved to — so a position adopted after a restart measures R against its own entry geometry rather than against whatever the trail had promoted | yes |
| `0024` | core | ai_auto_scan_evidence: whether a bar was evaluated live or recovered from an earlier blind cycle, and how many bars late — so the evaluation loss a rate limit causes stops being inferred from logs and becomes a column | yes |
| `0025` | core | strategy_registry: Campaign B's closure written onto the row that campaign_registry_sync used to reconcile at every boot, so retiring the boot-time document parse leaves no install claiming a closed campaign is still planned | no — the row records a campaign that closed REJECTED on 2026-08-04 under an immutable, checksummed archive (RG-07). Reverting it to PLANNED / PENDING would write a false statement about a closed record, and a code rollback does not need it: the corrected row is read identically by every version of the registry service |
| `0026` | core | drop the fifteen research_* tables with the packages that owned them (governance cleanup Step 2) | no — The dropped rows are observation data — replay trades, equity curves, behaviour events and their measured outcomes — and there is nothing left in the repository that could regenerate them: the Research Workbench, the Market Behaviour Study and the Research Platform were all removed in the same change. Recreating empty tables would restore the schema and not the observations, which is worse than an honest refusal because it looks like a rollback. Restore from `backups/governance-cleanup-2026-08-31/research_tables_pre_drop_2026-08-31.sql` instead. |
| `0027` | core | ai_auto_scan_quality_observations: every recovered row's signal_confidence moved from the strategy's 0-1 strength onto the 0-100 scale the live path publishes, so the two populations the collector exists to compare are on one scale | no — after the call-site fix that ships with it a recovered observation is natively 0-100, and nothing distinguishes a row this migration scaled from one the catch-up walk wrote correctly. Dividing by 100 would silently corrupt every observation recorded after the fix. A code rollback does not need it: the collector stores this column and interprets nothing, so every version reads the corrected rows the same |
| `0028` | core | capability_options + strategy_registry: offer the ratified runner ceiling as a selectable target model ('Target SL Trail'), so a governed scan can ask for the ATR-anchored target instead of a fixed reward-to-risk ratio | yes |
| `0029` | core | widen risk_reward to VARCHAR(32) on the four tables that store a reward-to-risk selection, so a named target model ('Target SL Trail') is stored whole rather than refused at the API or truncated in the database | no — narrowing back to VARCHAR(8) would truncate a stored 'Target SL Trail' to 'Target S' — a value no surface can render and no validator accepts. A code rollback does not need it: every prior version reads a wide column holding short strings correctly |
| `0030` | core | auto_paper_positions.peak_pnl_pct backfilled for every closed position that has a decision log, measured from that log's marks through the one excursion definition the close path now uses | no — the prior value is the column default `0.0` for every affected row, not a measurement. Writing it back would replace a correct peak with the absence of one and leave nothing able to distinguish an unmeasured row from a position that was genuinely never in profit. A code rollback does not need it: no policy, route or report reads this column, so every version of the application reads the corrected rows identically |
| `0031` | core | ai_auto_scan_evidence: whether the stored ATR multiple was a measurement of the plan or a constant of the stop model that placed it — so a day with no geometry refusals records why there were none | yes |

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/db_migrate.py downgrade 0031` | Reverse the newest core migration — ai_auto_scan_evidence: whether the stored ATR multiple was a… | Recovery only, and only if that migration declares a downgrade | One migration reversed, or a refusal if it declares itself irreversible |

## Maintenance Scripts

Everything under `backend/scripts/`, `backend/scripts/maintenance/` and `backend/tools/` that is not a CI gate, a stress harness or one of the packages with its own section above.

### `cd backend && python -m admin_cli`

TheTradeLogic — Admin CLI

Source: [`backend/admin_cli.py`](../backend/admin_cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m admin_cli create-super-admin` | Create the first (or additional) Super Admin | Deliberate operator action — it writes. | Prints create the first (or additional) Super Admin. Exit 0 on success. |
| `cd backend && python -m admin_cli list-admins` | List all admin accounts | Any time — read-only. | Prints list all admin accounts. Exit 0 on success. |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `create-super-admin` | `--email` | optional | takes a value |
| `create-super-admin` | `--password` | optional | takes a value |
| `create-super-admin` | `--name` | optional | takes a value |
| `create-super-admin` | `--force` | optional | Overwrite if the email exists |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m admin_cli create-super-admin
python -m admin_cli create-super-admin --email admin@site.com --password 'Str0ng!!Passw0rd' --name 'Ops Lead'
python -m admin_cli create-super-admin --email admin@site.com --password '...' --force
python -m admin_cli list-admins
```

### `cd backend && python -m data_audit`

Historical Market Data Audit — a read-only census of what the stored market data can and cannot answer.

Source: [`backend/data_audit/cli.py`](../backend/data_audit/cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m data_audit run` | run the whole audit and render the report | Deliberate operator action — it writes. | Prints run the whole audit and render the report. Exit 0 on success. |
| `cd backend && python -m data_audit matrix` | the feature availability matrix only | Any time — read-only. | Prints the feature availability matrix only. Exit 0 on success. |
| `cd backend && python -m data_audit digest` | print the run digest and nothing else | Any time — read-only. | Prints print the run digest and nothing else. Exit 0 on success. |

Options accepted by every sub-command:

| Option | Kind | Meaning |
|---|---|---|
| `--version` | optional | takes a value |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `run` | `--json` | optional | emit the machine-readable result instead of the report |
| `run` | `--no-write` | optional | do not write the run artefacts under backend/logs/data_audit/ |
| `run` | `--as-of` | optional | execution timestamp to stamp on the report; excluded from the digest, so supplying it makes two runs byte-identical |
| `matrix` | `--json` | optional | a switch; takes no value |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m data_audit run       # the whole audit → stdout + logs/data_audit/
python -m data_audit matrix    # the feature availability matrix only
python -m data_audit digest    # the digest alone, for a reproducibility check
```

### `cd backend && python -m generate_paper_trading_report`

Generate the end-of-session paper-trading report from already-recorded evidence. Read-only: it reads the attached paper outcomes and the original-signal replay, creates no trade, fetches no broker data and modifies no record.

Source: [`backend/generate_paper_trading_report/cli.py`](../backend/generate_paper_trading_report/cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m generate_paper_trading_report` | Generate the end-of-session paper-trading report from already-recorded evidence. Read-only: it reads the attached paper outcomes and the original-signal replay, creates no trade, fetches no broker data and modifies no record. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m generate_paper_trading_report --json` | Also print the full report document as JSON | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m generate_paper_trading_report --stdout` | Also print the rendered Markdown | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m generate_paper_trading_report --verbose` | Print the coverage, the per-trade sources and every divergence to the terminal as well as to the file | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--date` | optional | Session date, YYYY-MM-DD. Defaults to today in IST — the report names the date it used in its filename and its header, so a default can never be mistaken for another day |
| `--symbol` | optional | The index the report is about. Date and index together identify the population (default: NIFTY) |
| `--out-dir` | optional | Write the report here instead of docs/paper_trading_report/. The filename is still the session's date |
| `--markdown-out` | optional | Write the Markdown report to this exact file instead of naming it after the session date. An existing file is never overwritten — a timestamped sibling is written beside it |
| `--json` | optional | Also print the full report document as JSON |
| `--json-out` | optional | Also write the full report document as JSON to this path. The Markdown above is the report a person reads; this is the same document a console can read back |
| `--stdout` | optional | Also print the rendered Markdown |
| `--verbose` | optional | Print the coverage, the per-trade sources and every divergence to the terminal as well as to the file |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m generate_paper_trading_report
python -m generate_paper_trading_report --date 2026-08-17
python -m generate_paper_trading_report --symbol NIFTY
python -m generate_paper_trading_report --verbose
python -m shadow_mode attach-outcomes
python -m shadow_mode outcomes
python -m shadow_mode replay-signals
```

### `cd backend && python -m shadow_mode`

Shadow Mode — observe the live signal pipeline, trade nothing.

Source: [`backend/shadow_mode/cli.py`](../backend/shadow_mode/cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m shadow_mode observe` | Run one live observation and record it | Deliberate operator action — it writes. | Prints run one live observation and record it. Exit 0 on success. |
| `cd backend && python -m shadow_mode watch` | Observe once per closed bar until the session closes | Any time — read-only. | Prints observe once per closed bar until the session closes. Exit 0 on success. |
| `cd backend && python -m shadow_mode status` | What shadow mode is wired to | Any time — read-only. | Prints what shadow mode is wired to. Exit 0 on success. |
| `cd backend && python -m shadow_mode summary` | Aggregate the recorded evidence | Any time — read-only. | Prints aggregate the recorded evidence. Exit 0 on success. |
| `cd backend && python -m shadow_mode attach-outcomes` | Attach a simulated PAPER outcome to each shadow SIGNAL and record it | Deliberate operator action — it writes. | Prints attach a simulated PAPER outcome to each shadow SIGNAL and record it. Exit 0 on success. |
| `cd backend && python -m shadow_mode outcomes` | Aggregate the attached paper outcomes | Any time — read-only. | Prints aggregate the attached paper outcomes. Exit 0 on success. |
| `cd backend && python -m shadow_mode forensics --decisions <decisions> --candles <candles>` | Replay an exported session: signals, rejected crossovers, aggregates and data quality (read-only, underlying only) | Any time — read-only. | Prints replay an exported session: signals, rejected crossovers, aggregates and data quality (read-only, underlying only). Exit 0 on success. |
| `cd backend && python -m shadow_mode session-report --date <date>` | One session's post-close analytics: the day's largest move, the largest premium expansion, the largest missed opportunity, best and worst trade, capture efficiency, confirmation lag and every rejection with the gate state behind it (read-only) | Any time — read-only. | Prints one session's post-close analytics: the day's largest move, the largest premium expansion, the largest missed opportunity, best and worst trade, capture efficiency, confirmation lag and every rejection with the gate state behind it (read-only). Exit 0 on success. |
| `cd backend && python -m shadow_mode quality-evidence` | Every recorded signal-quality score joined to its outcome, duration, excursion and capture. Evidence only — proposes no threshold | Any time — read-only. | Prints every recorded signal-quality score joined to its outcome, duration, excursion and capture. Evidence only — proposes no threshold. Exit 0 on success. |
| `cd backend && python -m shadow_mode replay-signals --date <date>` | Replay the session's ORIGINAL signals — same minute, same contract, same premium — under the current exit model, walking each contract candle by candle. Generates no signal; read-only | Any time — read-only. | Prints replay the session's ORIGINAL signals — same minute, same contract, same premium — under the current exit model, walking each contract candle by candle. Generates no signal; read-only. Exit 0 on success. |
| `cd backend && python -m shadow_mode replay-batch --start <start> --end <end>` | Replay every trading session in a date range through replay-signals and pool the result: session, trade, signal-quality, filter and monthly tables. Generates no signal; read-only | Any time — read-only. | Prints replay every trading session in a date range through replay-signals and pool the result: session, trade, signal-quality, filter and monthly tables. Generates no signal; read-only. Exit 0 on success. |
| `cd backend && python -m shadow_mode stop-execution-study --start <start> --end <end>` | Classify and cost every replayed stop exit in a date range: exact, gap-through, close-based or delayed, with the slippage and the delay in bars behind each, a session summary and the P&L the model booked beside the P&L a fill at the stop would have produced. Measurement only; read-only, changes no parameter and proposes nothing | Any time — read-only. | Prints classify and cost every replayed stop exit in a date range: exact, gap-through, close-based or delayed, with the slippage and the delay in bars behind each, a session summary and the P&L the model booked beside the P&L a fill at the stop would have produced. Measurement only; read-only, changes no parameter and proposes nothing. Exit 0 on success. |
| `cd backend && python -m shadow_mode quality-calibration --start <start> --end <end>` | Measure whether the signal-quality score predicts anything: distribution, fixed-band analysis, Pearson and Spearman correlations, a seven-rung threshold simulation, classification metrics and a confidence assessment. Read-only; the evidence floor is declared before the measurement and no threshold is proposed | Any time — read-only. | Prints measure whether the signal-quality score predicts anything: distribution, fixed-band analysis, Pearson and Spearman correlations, a seven-rung threshold simulation, classification metrics and a confidence assessment. Read-only; the evidence floor is declared before the measurement and no threshold is proposed. Exit 0 on success. |
| `cd backend && python -m shadow_mode quality-monitor --start <start>` | Accumulate signal-quality evidence: measure every trading day in the range, keep one row per day in an evidence ledger, re-measure the last 5, 10 and 20 sessions and the whole corpus, and report which of five recommendations the evidence supports. Read-only against the platform; it changes no configuration, tunes no strategy and proposes nothing the declared evidence requirements do not support | Any time — read-only. | Prints accumulate signal-quality evidence: measure every trading day in the range, keep one row per day in an evidence ledger, re-measure the last 5, 10 and 20 sessions and the whole corpus, and report which of five recommendations the evidence supports. Read-only against the platform; it changes no configuration, tunes no strategy and proposes nothing the declared evidence requirements do not support. Exit 0 on success. |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `observe` | `--symbol` | optional | takes a value — default `NIFTY` |
| `observe` | `--instrument` | optional | takes a value — default `index_option` |
| `observe` | `--no-record` | optional | Print the decision without persisting evidence |
| `watch` | `--symbol` | optional | takes a value — default `NIFTY` |
| `watch` | `--instrument` | optional | takes a value — default `index_option` |
| `watch` | `--interval` | optional | Bar interval in seconds; the loop aligns to it |
| `watch` | `--max-observations` | optional | Stop after this many observations |
| `watch` | `--any-time` | optional | Do not wait for market hours (diagnostics only; outside a session the broker returns no candles) |
| `summary` | `--limit` | optional | takes a `int` — default `1000` |
| `attach-outcomes` | `--limit` | optional | Shadow signals to examine, newest first |
| `attach-outcomes` | `--dry-run` | optional | Classify and report without writing evidence |
| `attach-outcomes` | `--verbose` | optional | Include every resolved outcome document |
| `outcomes` | `--limit` | optional | takes a `int` — default `1000` |
| `forensics` | `--decisions` | required | JSON array of exported ops_decisions rows |
| `forensics` | `--candles` | required | JSON array of exported historical_candles rows |
| `forensics` | `--json` | optional | Emit the full report document instead of tables |
| `forensics` | `--out` | optional | Also write the full JSON report to this path |
| `session-report` | `--date` | required | Session date, YYYY-MM-DD. No default — a report for the wrong day reads exactly like one for the right day |
| `session-report` | `--symbol` | optional | The index the report is about. Date and index together identify the population |
| `session-report` | `--json` | optional | Emit the full report document instead of tables |
| `session-report` | `--out` | optional | Also write the full JSON report to this path |
| `quality-evidence` | `--limit` | optional | Evidence rows to examine, newest first |
| `quality-evidence` | `--json` | optional | Emit the full document instead of tables |
| `replay-signals` | `--date` | required | Session date, YYYY-MM-DD. No default — a replay of the wrong day reads exactly like one of the right day |
| `replay-signals` | `--symbol` | optional | The index whose stored signals are replayed. Date and index together identify the population |
| `replay-signals` | `--trace` | optional | Also print the bar-by-bar walk of every trade, with each stop promotion on the bar that caused it |
| `replay-signals` | `--json` | optional | Emit the full document instead of tables |
| `replay-signals` | `--out` | optional | Also write the full JSON document to this path |
| `replay-batch` | `--start` | required | First session date, YYYY-MM-DD (inclusive) |
| `replay-batch` | `--end` | required | Last session date, YYYY-MM-DD (inclusive). Weekends and recorded exchange holidays inside the range are skipped, never counted as empty sessions |
| `replay-batch` | `--symbol` | optional | The index whose stored signals are replayed. Date and index together identify the population |
| `replay-batch` | `--no-trades` | optional | Omit the per-trade table from the terminal output; the JSON and CSV still carry every trade |
| `replay-batch` | `--no-filters` | optional | Skip the refusal analysis, which reads the index bars of every session in the range |
| `replay-batch` | `--json` | optional | With no value, print the full document instead of the tables; with a path, write it there |
| `replay-batch` | `--csv` | optional | Write the five tables as CSV files into this directory, creating it if needed |
| `stop-execution-study` | `--start` | required | First session date, YYYY-MM-DD (inclusive) |
| `stop-execution-study` | `--end` | required | Last session date, YYYY-MM-DD (inclusive). Weekends and recorded exchange holidays inside the range are skipped |
| `stop-execution-study` | `--symbol` | optional | The index whose stored signals are replayed and then measured |
| `stop-execution-study` | `--no-trades` | optional | Omit the two per-trade tables from the terminal output; the JSON and CSV still carry every trade |
| `stop-execution-study` | `--json` | optional | With no value, print the full document instead of the tables; with a path, write it there |
| `stop-execution-study` | `--csv` | optional | Write the per-trade table as a CSV file into this directory, creating it if needed |
| `quality-calibration` | `--start` | required | First session date, YYYY-MM-DD (inclusive) |
| `quality-calibration` | `--end` | required | Last session date, YYYY-MM-DD (inclusive). Weekends and recorded exchange holidays inside the range are skipped |
| `quality-calibration` | `--symbol` | optional | The index whose stored signals are replayed and then measured |
| `quality-calibration` | `--json` | optional | With no value, print the full document instead of the tables; with a path, write it there |
| `quality-monitor` | `--start` | required | First session date, YYYY-MM-DD (inclusive) |
| `quality-monitor` | `--end` | optional | Last session date, YYYY-MM-DD (inclusive). Defaults to today. Weekends and recorded exchange holidays inside the range are skipped |
| `quality-monitor` | `--symbol` | optional | The index whose stored signals are replayed and then measured |
| `quality-monitor` | `--history` | optional | The evidence ledger to upsert into. Default: shadow_evidence/signal_quality_history.json at the repository root |
| `quality-monitor` | `--no-record` | optional | Measure and report without touching the ledger. The printed document is identical either way |
| `quality-monitor` | `--no-filters` | optional | Skip the refusal analysis, which reads the index bars of every session in the range. MISSED TRADES then reports blocked winners only |
| `quality-monitor` | `--top` | optional | How many rows the TOP TRADES and MISSED TRADES sections list. Default: the module's declared TOP_TRADES, resolved when the command runs so the parser does not import the replay stack |
| `quality-monitor` | `--json` | optional | With no value, print the full document instead of the report; with a path, write it there |
| `quality-monitor` | `--csv` | optional | Write the six tables as CSV files into this directory, creating it if needed |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m shadow_mode observe [--symbol NIFTY] [--instrument index_option] [--no-record]
python -m shadow_mode watch [--symbol NIFTY] [--interval 60] [--max-observations N]
python -m shadow_mode status
python -m shadow_mode summary [--limit 1000]
python -m shadow_mode attach-outcomes [--limit 500] [--dry-run]
python -m shadow_mode outcomes [--limit 1000]
python -m shadow_mode forensics --decisions D.json --candles C.json
python -m shadow_mode session-report --date 2026-08-13 [--symbol NIFTY]
python -m shadow_mode quality-evidence [--limit 2000]
python -m shadow_mode replay-signals --date 2026-08-13 [--symbol NIFTY]
python -m shadow_mode replay-batch --start 2026-08-01 --end 2026-08-31
python -m shadow_mode quality-calibration --start 2026-08-01 --end 2026-08-31
python -m shadow_mode quality-monitor --start 2026-08-01 [--symbol NIFTY]
```

### `cd backend && python -m signal_validator`

Replay stored candles through the production V2 Signal Engine and report every decision, trade and statistic. The platform's standard strategy validation tool.

Source: [`backend/signal_validator/cli.py`](../backend/signal_validator/cli.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m signal_validator run --symbol <symbol> --from <from> --to <to>` | replay a window and write the validation artefacts | Deliberate operator action — it writes. | Prints replay a window and write the validation artefacts. Exit 0 on success. |
| `cd backend && python -m signal_validator strategies` | list the strategies and frozen control arms that can be validated | Any time — read-only. | Prints list the strategies and frozen control arms that can be validated. Exit 0 on success. |
| `cd backend && python -m signal_validator coverage --symbol <symbol>` | what stored candles and contracts a symbol has | Any time — read-only. | Prints what stored candles and contracts a symbol has. Exit 0 on success. |
| `cd backend && python -m signal_validator verify --symbol <symbol> --from <from> --to <to>` | replay a window twice and compare the two reports | Any time — read-only. | Prints replay a window twice and compare the two reports. Exit 0 on success. |

Sub-command arguments:

| Sub-command | Argument | Kind | Meaning |
|---|---|---|---|
| `run` | `--symbol` | required | index symbol, e.g. NIFTY |
| `run` | `--from` | required | first session to replay |
| `run` | `--to` | required | last session to replay |
| `run` | `--strategy` | optional | strategy id, or 'auto' to let the engine choose (default: auto) |
| `run` | `--strategy-version` | optional | a frozen control arm to substitute, e.g. ema_crossover.v1. Omit to run the production implementation. |
| `run` | `--instrument` | optional | instrument type (default: index_option, which is what the live analyse route sends) |
| `run` | `--timeframe` | optional | analysis timeframe (default: 1m) |
| `run` | `--step` | optional | evaluate every Nth 1-minute bar (default: 1) |
| `run` | `--no-confirm` | optional | evaluate signals without opening virtual positions — decisions only, no trades and no statistics |
| `run` | `--threshold` | optional | confidence threshold for the informational billing column. Omit to resolve it from ai_config the way /analyse does. |
| `run` | `--label` | optional | free text recorded on the certificate, e.g. a campaign id |
| `run` | `--verify` | optional | a switch; takes no value |
| `run` | `--json` | optional | print the report as JSON instead of a summary |
| `run` | `--no-write` | optional | do not write artefacts to logs/validation/ |
| `coverage` | `--symbol` | required | takes a value |
| `verify` | `--symbol` | required | takes a value |
| `verify` | `--from` | required | takes a value |
| `verify` | `--to` | required | takes a value |
| `verify` | `--strategy` | optional | takes a value — default `auto` |
| `verify` | `--strategy-version` | optional | takes a value |
| `verify` | `--step` | optional | takes a `int` — default `1` |

### `python backend/scripts/analyze_identity_experiment.py`

Refusal rate per broker identity/transport experiment arm, read offline from a BROKER_HTTP log. Prints a table, never a verdict.

Source: [`backend/scripts/analyze_identity_experiment.py`](../backend/scripts/analyze_identity_experiment.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/analyze_identity_experiment.py` | Refusal rate per broker identity/transport experiment arm, read offline from a BROKER_HTTP log. Prints a table, never a verdict. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/analyze_identity_experiment.py --logical` | also report coordinated requests after their retry | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/analyze_identity_experiment.py --escalation` | print the refused-request ids and timestamps for an Angel One support ticket, as JSON | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/analyze_identity_experiment.py --json` | emit the report as JSON instead of a table | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--class` | optional | request class to count; a family name matches its members ('auth' matches auth_login, auth_token, auth_logout); '' for every class (default: candle) |
| `--since` | optional | ISO date, or date and time; ignore earlier rows |
| `--until` | optional | ISO date, or date and time; ignore later rows |
| `--logical` | optional | also report coordinated requests after their retry |
| `--escalation` | optional | print the refused-request ids and timestamps for an Angel One support ticket, as JSON |
| `--json` | optional | emit the report as JSON instead of a table |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/scripts/analyze_identity_experiment.py
python backend/scripts/analyze_identity_experiment.py logs/algo_trading.log
python backend/scripts/analyze_identity_experiment.py --class candle --json
python backend/scripts/analyze_identity_experiment.py --since 2026-08-28
```

### `python backend/scripts/audit_partial_candles.py`

Report candles stored before their minute closed. Never modifies the database.

Source: [`backend/scripts/audit_partial_candles.py`](../backend/scripts/audit_partial_candles.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/audit_partial_candles.py` | Report candles stored before their minute closed. Never modifies the database. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/audit_partial_candles.py --verify` | exit non-zero if any corrupt row remains; the check to run after a repair | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/audit_partial_candles.py --json` | machine-readable summary on stdout | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--export-dir` | optional | write the corrupt rows (JSON + CSV) and the repair SQL to this directory |
| `--suspect-window` | optional | upper bound, in seconds, of the review band reported beside the corrupt rows. Never repaired — a bar fetched just before its close and inserted just after lands here legitimately. |
| `--verify` | optional | exit non-zero if any corrupt row remains; the check to run after a repair |
| `--json` | optional | machine-readable summary on stdout |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/scripts/audit_partial_candles.py
python backend/scripts/audit_partial_candles.py --export-dir logs/partial_candles
python backend/scripts/audit_partial_candles.py --verify
```

### `python backend/scripts/calibrate_regime_bands.py`

Measure Market Intelligence regime band edges.

Source: [`backend/scripts/calibrate_regime_bands.py`](../backend/scripts/calibrate_regime_bands.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/calibrate_regime_bands.py` | Measure Market Intelligence regime band edges. | Deliberate operator action — the bare form is the one that acts; run `--check` first. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/calibrate_regime_bands.py --json` | emit the measurement as JSON | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/calibrate_regime_bands.py --check` | exit 1 if the committed bands have drifted | Any time — read-only. | Exit 1 if the committed artefact differs |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--json` | optional | emit the measurement as JSON |
| `--check` | optional | exit 1 if the committed bands have drifted |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python scripts/calibrate_regime_bands.py            # table
python scripts/calibrate_regime_bands.py --json     # machine-readable
python scripts/calibrate_regime_bands.py --check    # exit 1 if the
```

### `python backend/scripts/capture_weekly.py`

Weekly capture — RG-04 §7's standing obligation.

Source: [`backend/scripts/capture_weekly.py`](../backend/scripts/capture_weekly.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/capture_weekly.py` | Weekly capture — RG-04 §7's standing obligation. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/capture_weekly.py --status` | report freshness and exit non-zero if stale | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `python backend/scripts/capture_weekly.py --register-only` | snapshot the contract registry; no broker calls | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--status` | optional | report freshness and exit non-zero if stale |
| `--register-only` | optional | snapshot the contract registry; no broker calls |
| `--days` | optional | how many calendar days back to fetch candles. N covers today AND the N days before it — `--days 1` is yesterday and today, not yesterday alone (replay.backfill.backfill_symbol walks [today-N, today] inclusively). Asserted in tests/test_non_shrink_ingestion.py. |
| `--horizon` | optional | how far ahead to keep expiries registered BEYOND the front pair. The front pair is always registered — it is what the readiness report requires, and no number of days can express it for a monthly-only underlying. |
| `--max-calls` | optional | ceiling on option-backfill broker calls |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/scripts/capture_weekly.py --status
python backend/scripts/capture_weekly.py --register-only
python backend/scripts/capture_weekly.py --days 1 --max-calls 400   # today
python backend/scripts/capture_weekly.py --days 7
```

### `python backend/tools/generate_developer_command_reference.py`

Generate docs/DEVELOPER_COMMAND_REFERENCE.md from the repository. Discovery, not a cheat sheet.

Source: [`backend/tools/generate_developer_command_reference.py`](../backend/tools/generate_developer_command_reference.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/tools/generate_developer_command_reference.py` | Generate docs/DEVELOPER_COMMAND_REFERENCE.md from the repository. Discovery, not a cheat sheet. | Deliberate operator action — the bare form is the one that acts; run `--check` first. | Exit 0 on success, non-zero on failure. |
| `python backend/tools/generate_developer_command_reference.py --check` | verify the committed document matches; write nothing and exit 1 on any difference | Any time — read-only. | Exit 1 if the committed artefact differs |
| `python backend/tools/generate_developer_command_reference.py --stdout` | print the document instead of writing it | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--check` | optional | verify the committed document matches; write nothing and exit 1 on any difference |
| `--stdout` | optional | print the document instead of writing it |
| `--output` | optional | where to write (default: docs/DEVELOPER_COMMAND_REFERENCE.md) |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/tools/generate_developer_command_reference.py            # write
python backend/tools/generate_developer_command_reference.py --check    # verify
python backend/tools/generate_developer_command_reference.py --stdout   # print
```

The generator above is what wrote this page. Run it in the same change that adds or removes a command — the CI gate `developer-command-reference` regenerates the document and fails on any difference, so a command added without a regeneration is a red build rather than a quietly stale reference.

### Research harnesses

Runnable, but not routine. These are campaign instruments under `backend/experiments/`: they produce evidence under a protocol, and a formula transcribed into one of them must be declared in `experiments/conformance/registry.py` and proved numerically equal to production. Running one outside a campaign produces a number that no conclusion may cite.

#### `cd backend && python -m experiments.phase13.analyse`

Phase 13 analysis

Source: [`backend/experiments/phase13/analyse.py`](../backend/experiments/phase13/analyse.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m experiments.phase13.analyse` | Phase 13 analysis | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--results` | required | takes a value |
| `--out` | optional | takes a value |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m experiments.phase13.analyse --results results.json [--out summary.json]
```

#### `cd backend && python -m experiments.phase13.run`

Phase 13 experiment battery

Source: [`backend/experiments/phase13/run.py`](../backend/experiments/phase13/run.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m experiments.phase13.run` | Phase 13 experiment battery | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m experiments.phase13.run --skip-probes` | skip determinism + window-independence probes | Any time — read-only. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--out` | required | output JSON path |
| `--only` | optional | comma-separated experiment ids (default: all) |
| `--skip-probes` | optional | skip determinism + window-independence probes |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m experiments.phase13.run --out /path/to/results.json
```

#### `cd backend && python -m experiments.scanner_suitability`

Scanner Suitability Study — measure whether ema_crossover.v2 suits an on-demand scanner. Evidence collection only; changes no production behaviour.

Source: [`backend/experiments/scanner_suitability/__main__.py`](../backend/experiments/scanner_suitability/__main__.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m experiments.scanner_suitability` | Scanner Suitability Study — measure whether ema_crossover.v2 suits an on-demand scanner. Evidence collection only; changes no production behaviour. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m experiments.scanner_suitability --skip-delayed-entry` | omit §3b, the only section that reads the option store | Any time — read-only. | Exit 0 on success, non-zero on failure. |
| `cd backend && python -m experiments.scanner_suitability --no-charts` | write the statistics bundle without the figures | Deliberate operator action — it writes. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--validation-dir` | optional | where `signal_validator run` wrote its artefacts |
| `--out` | optional | directory for the statistics bundle and figures |
| `--skip-delayed-entry` | optional | omit §3b, the only section that reads the option store |
| `--no-charts` | optional | write the statistics bundle without the figures |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python -m experiments.scanner_suitability         --validation-dir backend/logs/validation         --out backend/logs/scanner_suitability
```

## Stress Tests

Harnesses that answer what a CI-sized test deliberately does not. None of them run in CI: they are slow by design and they are run when a concurrency or throughput claim needs evidence.

### `python backend/scripts/stress/scheduler_lock_stress.py`

Scheduler Lock — contention harness, v1 against v2.

Source: [`backend/scripts/stress/scheduler_lock_stress.py`](../backend/scripts/stress/scheduler_lock_stress.py)

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `python backend/scripts/stress/scheduler_lock_stress.py` | Scheduler Lock — contention harness, v1 against v2. | The script's default action — read the options below first. | Exit 0 on success, non-zero on failure. |

Options:

| Option | Kind | Meaning |
|---|---|---|
| `--sizes` | optional | takes a `int` |
| `--rounds` | optional | acquire/release cycles per process in the churn scenarios (default: 20) |
| `--impls` | optional | takes a value — default `v2, v1` |
| `--repeat` | optional | times to repeat every scenario. A race is a probability, not an event: one clean pass is weak evidence and repetition is the only way to make it less weak (default: 1) |
| `--json` | optional | takes a `Path` |
| `--skip-latency` | optional | a switch; takes no value |

Worked examples, quoted verbatim from the module's own docstring. They are the author's own lines, left exactly as written — several assume `backend/` as the working directory.

```bash
python backend/scripts/stress/scheduler_lock_stress.py
python backend/scripts/stress/scheduler_lock_stress.py --sizes 100 --rounds 40
python backend/scripts/stress/scheduler_lock_stress.py --json /tmp/stress.json
```

## Debugging

Read-only commands that answer *what is the system doing*, in the order they are usually needed.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler status` | what ran, whether the data is current, what is next | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m capture_scheduler config` | the effective configuration and its sources | Any time — read-only. | `0` the job ran and its step succeeded — or the kill switch is off,…; `1` the step failed, or a lock was held. An alert has already been… |
| `cd backend && python -m platform_broker status` | what is configured, and is it usable | Any time — read-only. | Prints what is configured, and is it usable. Exit 0 on success. |
| `cd backend && python -m platform_broker audit` | the recent lifecycle events | Any time — read-only. | Prints the recent lifecycle events. Exit 0 on success. |
| `cd backend && python -m shadow_mode status` | What shadow mode is wired to | Any time — read-only. | Prints what shadow mode is wired to. Exit 0 on success. |
| `cd backend && python -m signal_validator verify --symbol <symbol> --from <from> --to <to>` | replay a window twice and compare the two reports | Any time — read-only. | Prints replay a window twice and compare the two reports. Exit 0 on success. |
| `python backend/scripts/db_migrate.py status` | one row per known migration | Any time — read-only. | Prints one row per known migration. Exit 0 on success. |
| `python backend/scripts/strategy_registry_report.py` | Every strategy and its registration status | A strategy is refused and it is not clear why | The same audit `main.py` runs at startup, without booting it |

### State on disk

Paths the scheduler writes. `LOG_DIR` defaults to `logs/research/` under the repository root and is settable in the environment — `python -m capture_scheduler config` prints the effective value and where it came from.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m capture_scheduler config` | Every configuration value and its source | A schedule fired at an unexpected time | One row per variable, each tagged with its provenance |
| `cat logs/research/scheduler-state.json` | The scheduler's own record of what ran | `status` disagrees with what you expected | The last run of each step, its exit code and its alerts |
| `cat logs/research/alerts.log` | Alerts the scheduler raised | A job failed and nothing said so | One line per alert |
| `ls -l logs/research/` | Run locks and per-run logs | A run refuses to start because a lock is held | A lock file present means a run is in progress or died holding it |

## Deployment

In order. Every step is a command already documented above; this is the sequence, not a new set of tools.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `pip install -r backend/requirements.txt` | Install exactly what is declared | Every deploy — a clean install is the only thing that proves the declaration is complete | No resolution errors |
| `python backend/scripts/db_migrate.py upgrade --dry-run` | Show what the schema change would do | Before every deploy that touches `db/migrations/` | The pending migrations, applied to nothing |
| `python backend/scripts/db_migrate.py upgrade` | Apply pending core migrations before serving | Deploy, ahead of starting the application | The ledger advances; running it twice is a no-op |
| `python backend/scripts/db_migrate.py status` | Confirm the database is at the expected revision | Immediately after upgrading | No pending, failed or checksum-changed core migration |
| `bash backend/scripts/ci/run_all.sh` | The whole gate — 13 checks | Before tagging or shipping anything | `OK — every gate passed.` |
| `python backend/scripts/strategy_registry_report.py --check` | Gate the deployment on a consistent strategy register | After migrations, before starting the application | Exit 0 when consistent; **exit 1** on an error |
| `cd backend && python -m platform_broker status` | Confirm the platform has a usable broker identity | After deploy — every research and capture job depends on it | Exit 0 when healthy; **exit 1** when it could not serve a job |
| `cd backend && python -m capture_scheduler render cron --repo-root <deploy path>` | Render the cron unit for the host | When installing the schedule | The artefact on stdout, to be installed by hand |
| `cd backend && python -m capture_scheduler render systemd --repo-root <deploy path>` | Render the systemd unit for the host | When installing the schedule | The artefact on stdout, to be installed by hand |
| `cd backend && python -m capture_scheduler render github --repo-root <deploy path>` | Render the github unit for the host | When installing the schedule | The artefact on stdout, to be installed by hand |
| `cd backend && python -m capture_scheduler render launchd --repo-root <deploy path>` | Render the launchd unit for the host | When installing the schedule | The artefact on stdout, to be installed by hand |
| `cd backend && python -m capture_scheduler status` | Confirm the schedule is being met after installation | After installing the schedule, and as a monitoring probe | Exit 0 when on schedule; **exit 1** when overdue or failing |
| `cd frontend && npm ci && npm run build` | Build frontend (`vite build`) | Every deploy — neither frontend has a test suite, so the build is the gate | A production bundle, or a build error |
| `cd frontend-admin && npm ci && npm run build` | Build frontend-admin (`vite build`) | Every deploy — neither frontend has a test suite, so the build is the gate | A production bundle, or a build error |

## Emergency

Recovery, not routine. Every command here changes something or reaches a live system; read the row before pasting it.

| Command | Purpose | When to Use | Expected Result |
|---|---|---|---|
| `cd backend && python -m platform_broker connect` | fresh TOTP login, persist the tokens | Deliberate operator action — it writes. | Prints fresh TOTP login, persist the tokens. Exit 0 on success. |
| `cd backend && python -m platform_broker refresh` | renew the JWT from the refresh token | Deliberate operator action — it writes. | Prints renew the JWT from the refresh token. Exit 0 on success. |
| `cd backend && python -m platform_broker logout` | drop the session, keep the credentials | Deliberate operator action — it writes. | Prints drop the session, keep the credentials. Exit 0 on success. |
| `python backend/scripts/db_migrate.py downgrade <version>` | reverse exactly one migration | Deliberate operator action — it writes. | Prints reverse exactly one migration. Exit 0 on success. |
| `ls -l logs/research/` | Find a run lock left behind by a process that died holding it | A scheduled run refuses to start and no run is in progress | The lock file and its age |
| `cd backend && python -m capture_scheduler status` | Establish whether the lock belongs to a live run before touching it | Always, before removing anything under `logs/research/` | The last run of each step and whether one is overdue |
| `cd backend && python -m platform_broker test` | One real broker call — is the platform identity actually usable | Capture or backfill fails with an authentication error | Exit 0 and a latency figure, or the broker's error |
| `python backend/scripts/capture_weekly.py --status` | Has the standing capture obligation gone stale | After any scheduler outage | Exit non-zero when the capture has gone stale |
| `python backend/scripts/capture_weekly.py --register-only` | Register contracts now, without the expensive backfill | An expiry is approaching and the registry is behind — registration is the irreversible half | The instrument master snapshotted into the contract registry |

A capture that was missed is not uniformly recoverable: candles for an already-registered expiry can be backfilled, but an expiry that passed without being registered cannot be reconstructed at any price. Register first, backfill second.

## Release Checklist

Generated from the repository: every gate in `run_all.sh`, every `--check` a CLI offers, and the build of every frontend that declares one.

```bash
# 1. Dependencies install from the declaration alone
pip install -r backend/requirements.txt

# 2. The database is at the expected revision
python backend/scripts/db_migrate.py status

# 3. The whole gate — 13 checks
bash backend/scripts/ci/run_all.sh

# 4. Generated artefacts match their sources
python backend/scripts/calibrate_regime_bands.py --check
python backend/scripts/strategy_registry_report.py --check
python backend/tools/generate_developer_command_reference.py --check

# 5. The frontends build
(cd frontend && npm ci && npm run build)
(cd frontend-admin && npm ci && npm run build)
```

Item by item:

| Item | Command | Why |
|---|---|---|
| Dependencies install from `requirements.txt` alone | `pip install -r backend/requirements.txt` | A dependency present in a developer venv but undeclared reads as green locally and fails collection on a clean runner |
| Gate 1: `tests` | `python -m pytest tests -q` | The whole test suite — every test in the tree. |
| Gate 2: `v2-isolation` | `bash backend/scripts/ci/check_v2_isolation.sh` | CI grep-checks for V2 isolation rules. Exits non-zero on any violation. |
| Gate 3: `v2-importlinter` | `bash backend/scripts/ci/check_v2_importlinter.sh` | CI wrapper for import-linter — Kickoff §4 task 36. |
| Gate 4: `v2-config-schema-sync` | `bash backend/scripts/ci/check_v2_config_schema_sync.sh` | CI drift check — Spec 05 amendment Clause 3, Phase 1 kickoff amendment Clause 4. |
| Gate 5: `operations-isolation` | `bash backend/scripts/ci/check_operations_isolation.sh` | CI entry point for the Phase 15 Operations boundary checks. |
| Gate 6: `research-engine` | `bash backend/scripts/ci/check_research_engine.sh` | CI entry point for the Research Engine conformance rules — Review C-4. |
| Gate 7: `schema-evolution` | `python backend/scripts/ci/check_schema_evolution.py` | Structural check — `db/migrations/` is the only schema-evolution mechanism. |
| Gate 8: `ai-auto-scan-isolation` | `bash backend/scripts/ci/check_ai_auto_scan_isolation.sh` | CI entry point for the AI Auto Scan boundary checks. |
| Gate 9: `research-archive-integrity` | `bash backend/scripts/ci/check_research_archive_integrity.sh` | Research archive integrity check — RG-07 §3. |
| Gate 10: `signal-validator` | `bash backend/scripts/ci/check_signal_validator.sh` | CI entry point for the Signal Replay Validator boundary checks. |
| Gate 11: `developer-command-reference` | `bash backend/scripts/ci/check_developer_command_reference.sh` | CI gate — the Developer Command Reference is regenerated and compared. |
| Gate 12: `v2-coverage` | `bash backend/scripts/ci/check_v2_coverage.sh` | CI coverage gate — Kickoff §4 task 39, §6 DoD. |
| Gate 13: `operations-coverage` | `bash backend/scripts/ci/check_operations_coverage.sh` | CI coverage gate for the Phase 15 Operations layer. |
| `calibrate_regime_bands` matches its source | `python backend/scripts/calibrate_regime_bands.py --check` | exit 1 if the committed bands have drifted |
| `strategy_registry_report` matches its source | `python backend/scripts/strategy_registry_report.py --check` | exit 1 when the audit reports an error |
| `generate_developer_command_reference` matches its source | `python backend/tools/generate_developer_command_reference.py --check` | verify the committed document matches; write nothing and exit 1 on any difference |
| `frontend` builds | `cd frontend && npm run build` | Neither frontend has a test suite, so the build is the gate |
| `frontend-admin` builds | `cd frontend-admin && npm run build` | Neither frontend has a test suite, so the build is the gate |
