Skip to content

Implement system maintenance metadata refresh console#197

Merged
limsiokuan merged 25 commits into
mainfrom
feature/196-maintenance-console
Jun 24, 2026
Merged

Implement system maintenance metadata refresh console#197
limsiokuan merged 25 commits into
mainfrom
feature/196-maintenance-console

Conversation

@evanlow

@evanlow evanlow commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Summary

Implements the first full slice of #196: System Maintenance: Market Metadata Refresh & Data Hygiene.

This PR adds a metadata-only maintenance subsystem for safely refreshing market universe metadata and exposing it through CLI, API, and a dashboard page.

What changed

  • Added web/maintenance/ package:
    • task/result models
    • validators
    • JSON + Markdown report writer
    • maintenance runner
    • S&P 500 / STI / HSI source adapters
    • CLI entrypoint via python -m web.maintenance
  • Added Maintenance web page:
    • /maintenance
    • dry-run buttons
    • apply constituents refresh button
    • market events refresh button
    • metadata validation button
  • Added Maintenance API:
    • GET /api/maintenance/status
    • POST /api/maintenance/run
    • GET /api/maintenance/reports
    • GET /api/maintenance/reports/<report_id>
  • Routed legacy scripts through the new runner:
    • scripts/refresh_sp500_constituents.py
    • deployment_scripts/refresh_hsi_constituents.py
  • Added tests for:
    • validation behavior
    • dry-run preserving existing files
    • apply backup/replacement behavior
    • validation failure preserving existing files
    • market-events dry-run safety
    • page/API smoke loading
  • Added docs:
    • docs/MAINTENANCE.md

Safety / Prime Directive notes

  • This feature is metadata-only.
  • It does not place orders.
  • It does not modify strategy behavior.
  • It does not start/stop strategies.
  • It does not modify autonomous trading configuration.
  • Apply mode writes only validated constituent metadata, backups, reports, and existing market-event rows via data.market_events.
  • Dry-run remains the default for the CLI.

Validation behavior

Constituent refreshes validate before replacement:

  • required columns
  • minimum row counts
  • duplicate symbols
  • symbol format
  • suspicious count changes

Apply mode backs up existing files first and invalidates relevant screener caches after successful replacement.

Testing

I added tests/test_maintenance.py for the new module.

I could not run the test suite from this chat environment because the repository is only accessible through the GitHub connector here, not as a local checkout with dependencies installed. Please run:

pytest tests/test_maintenance.py
pytest

Notes / follow-up candidates

  • The page route is available at /maintenance; if desired, a follow-up can add it directly into the navbar under Monitoring.
  • First implementation keeps market-events dry-run metadata-only; event refresh apply mode delegates to the existing market-events service.

Closes #196

@limsiokuan limsiokuan marked this pull request as ready for review June 24, 2026 09:39
@limsiokuan limsiokuan requested a review from Copilot June 24, 2026 09:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a safety-first “System Maintenance” subsystem to refresh market metadata (index constituents + market events) with a dry-run/apply workflow, validation, backups, and JSON/Markdown reporting, exposed via a new web console, API endpoints, and CLI entrypoint (first slice of Issue #196).

Changes:

  • Introduces web/maintenance/ runner, task/report models, validators, and source adapters for S&P 500 / STI / HSI, plus report persistence.
  • Adds the /maintenance dashboard page and /api/maintenance/* endpoints to run tasks and view status/reports.
  • Routes legacy refresh scripts through the new runner; adds docs and a dedicated test module.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
web/templates/maintenance/index.html New Maintenance console UI with buttons for dry-run/apply and status/report panels.
web/routes/maintenance.py Registers the server-rendered /maintenance page route.
web/routes/api_maintenance.py Adds maintenance status/run/report API endpoints for the UI/CLI.
web/maintenance/init.py Introduces the maintenance package.
web/maintenance/main.py CLI entrypoint (python -m web.maintenance) for running/validating tasks.
web/maintenance/tasks.py Task/result/report datamodels for serialized output and report generation.
web/maintenance/validators.py Constituent validation rules (required columns, counts, duplicates, symbol formats, change thresholds).
web/maintenance/runner.py Orchestrates tasks, performs dry-run/apply behavior, backups, atomic writes, cache invalidation, and report writing.
web/maintenance/reports.py Writes/reads JSON + Markdown reports; lists recent report summaries.
web/maintenance/sources/init.py Source adapter package marker.
web/maintenance/sources/sp500.py Fetches/normalizes S&P 500 constituents from Wikipedia.
web/maintenance/sources/sti.py Fetches/normalizes STI constituents from Wikipedia with resilient column detection.
web/maintenance/sources/hsi.py Fetches/normalizes HSI constituents from Wikipedia via requests + read_html parsing.
web/init.py Registers maintenance page and API blueprints in the Flask app factory.
tests/test_maintenance.py Adds unit/integration tests for validation and safe dry-run/apply behaviors and basic page/API loading.
scripts/refresh_sp500_constituents.py Replaces legacy script implementation with a runner-based compatibility wrapper.
deployment_scripts/refresh_hsi_constituents.py Replaces legacy script implementation with a runner-based compatibility wrapper.
docs/MAINTENANCE.md Documents safety boundaries, UI/CLI usage, validation rules, reports, and cadence.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread web/maintenance/tasks.py
Comment on lines +105 to +110
def finalize(self, *, started_monotonic: float, now_monotonic: float) -> None:
self.finished_at = utc_now_iso()
self.duration_seconds = round(max(0.0, now_monotonic - started_monotonic), 3)
self.warnings = [w for r in self.results for w in r.warnings]
self.errors = [e for r in self.results for e in r.errors]
if any(r.status == STATUS_FAILED for r in self.results):
Comment thread web/maintenance/runner.py Outdated
Comment on lines +56 to +61
dry_run = bool(payload.get("dry_run", True))
days_ahead = _bounded_int(payload.get("days_ahead"), default=28, minimum=1, maximum=90)
event_symbols = _csv_or_list(payload.get("event_symbols"))
include_portfolio = bool(payload.get("include_portfolio_symbols", True))
if include_portfolio:
event_symbols.extend(_portfolio_symbols())
Comment thread web/routes/api_maintenance.py Outdated
Comment thread web/maintenance/runner.py
Comment on lines +124 to +131
path = self.data_dir / str(cfg["filename"])
row_count = len(_read_csv_rows(path)) if path.exists() else 0
mtime = datetime.fromtimestamp(path.stat().st_mtime, timezone.utc).isoformat() if path.exists() else None
validation = validate_constituent_rows(
_read_csv_rows(path),
market=str(cfg["market"]),
before_count=row_count,
) if path.exists() else ValidationResult(status=STATUS_FAILED, errors=["File not found"])
Comment thread web/maintenance/runner.py Outdated
Comment thread web/maintenance/runner.py Outdated
Comment on lines +110 to +113
else:
result = MaintenanceTaskResult(task=task_name, status=STATUS_FAILED, dry_run=dry_run)
result.errors.append(f"Unknown maintenance task: {task_name}")
result.finished_at = _utc_now().isoformat()
Copilot AI added 4 commits June 24, 2026 09:57
- tasks.py: Include validation.errors in finalize() error aggregation
- runner.py: Check status==failed before total_errors in _run_market_events_task
- runner.py: Read CSV rows once in get_status() to avoid redundant I/O
- runner.py: Use microseconds in backup dir timestamp to avoid collisions
- runner.py: Call result.finish() for unknown tasks instead of setting finished_at directly
- api_maintenance.py: Only expand portfolio symbols when market_events task is requested
- api_maintenance.py: Remove raw exception string from 500 response
Copilot AI requested a review from limsiokuan June 24, 2026 10:05
@limsiokuan limsiokuan merged commit c45250b into main Jun 24, 2026
@limsiokuan limsiokuan deleted the feature/196-maintenance-console branch June 24, 2026 10:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

System Maintenance: Market Metadata Refresh & Data Hygiene

4 participants