Files
stonks-oracle/.kiro/steering/development-process.md
T
Celes Renata 751cce0509
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
feat: add documentation maintenance rules to steering, update migration number to 036
2026-05-01 04:29:52 +00:00

7.9 KiB

Development Process — Test-Develop-Debug

Local Environment

  • Ubuntu dev machine, Python 3.12, virtualenv at .venv/
  • Always use .venv/bin/python or activate with source .venv/bin/activate before running Python commands
  • Node.js 24 via nvm — always load nvm before running Node/npm/npx commands: export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" && nvm use 24
  • For tools not available in .venv/ (ruff, gh, etc.), install via pip or apt as needed
  • Frontend tests: load nvm first, then cd frontend && npx vitest --run
  • Python tests: .venv/bin/ruff check services/ then .venv/bin/python -m pytest tests/ -x --tb=short -q

Workflow

  1. Write or update tests for the target behavior
  2. Implement the minimal code to pass
  3. Debug failures, fix, re-run
  4. Commit and push — CI builds images automatically
  5. Deploy: helm upgrade --install stonks-oracle infra/helm/stonks-oracle -n stonks-oracle
  6. Restart changed services: kubectl rollout restart deployment/<name> -n stonks-oracle

Testing

  • Python: pytest with pytest-asyncio for async code, tests in tests/
  • Property-based tests: Hypothesis with @settings(max_examples=100), files prefixed test_pbt_*
  • Frontend: Vitest + MSW (Mock Service Worker) for deterministic API mocking, tests in frontend/src/test/
  • Run Python tests: python -m pytest tests/ -x --tb=short -q
  • Run frontend tests: cd frontend && npx vitest --run
  • Lint Python: .venv/bin/ruff check services/
  • Always run .venv/bin/ruff check services/ before committing Python changes — CI will reject the push if ruff fails
  • Ruff auto-fix: .venv/bin/ruff check --fix services/ (fixes import sorting and other auto-fixable issues)
  • Ruff is pinned to ruff==0.15.10 in requirements.txt — CI uses the same version
  • Ruff config: ruff.toml with known-first-party = ["services"] for consistent import sorting
  • Pre-existing test failures (not regressions): test_extractor_prompts.py, test_extractor_schemas.py, test_filings_adapter.py, test_ollama_client.py

CI/CD — Woodpecker CI (Gitea) → GitHub promotion

  • Woodpecker pipelines in .woodpecker/ — triggered by push to main on Gitea
  • Push to Gitea: git push gitea main
  • Gitea remote: http://admin:<password>@10.1.1.12:30300/admin/stonks-oracle.git
  • Pipeline stages: lint → pytest → frontend vitest → build all service images + dashboard + superset → push to Harbor
  • ArgoCD watches Gitea main and auto-syncs beta/paper/live stages
  • Do NOT push directly to GitHub — GitHub is the promotion target after CI passes
  • Once Woodpecker builds and tests pass, code is promoted to GitHub (git push origin main)
  • CI handles all image builds and pushes — do NOT manually docker push
  • Check Woodpecker CI status from the Gitea web UI or Woodpecker dashboard

Deploy

  • Full deploy/redeploy: bash ~/sources/kube/stonks-oracle/runmefirst.sh (from gremlin-1)
  • Full teardown: bash ~/sources/kube/stonks-oracle/runmelast.sh (from gremlin-1)
  • Quick Helm upgrade: helm upgrade --install stonks-oracle infra/helm/stonks-oracle -n stonks-oracle
  • Restart single service: kubectl rollout restart deployment/<name> -n stonks-oracle
  • Restart multiple: kubectl rollout restart deployment/aggregation deployment/query-api deployment/dashboard -n stonks-oracle
  • Check pods: kubectl get pods -n stonks-oracle
  • Check logs: kubectl logs deployment/<name> -n stonks-oracle --tail=30

Manually Triggering Ingestion

The scheduler runs on a cadence, but to trigger immediately:

# From a scheduler pod:
kubectl exec -n stonks-oracle <scheduler-pod> -- python -c "
import redis, json, asyncio, asyncpg
async def enqueue():
    pool = await asyncpg.create_pool(dsn='postgresql://stonks:<password>@postgresql-rw.postgresql-service.svc.cluster.local:5432/stonks')
    r = redis.from_url('redis://:<password>@redis-master.redis-service.svc.cluster.local:6379/0')
    rows = await pool.fetch('SELECT s.id AS source_id, s.source_type, s.config, c.id AS company_id, c.ticker FROM sources s JOIN companies c ON c.id = s.company_id WHERE s.active = TRUE AND c.active = TRUE')
    for row in rows:
        cfg = row['config'] if isinstance(row['config'], dict) else {}
        r.rpush('stonks:queue:ingestion', json.dumps({'source_id': str(row['source_id']), 'source_type': row['source_type'], 'ticker': row['ticker'], 'company_id': str(row['company_id']), 'config': cfg}))
    await pool.close()
asyncio.run(enqueue())
"

Ingestion jobs MUST include source_id, source_type, ticker, company_id, and config.

Git Conventions

  • Commit after each completed phase task
  • Commit message format: feat:, fix:, phase N: prefix
  • Always push to Gitea: git push gitea main
  • Do NOT push to GitHub (origin) directly — GitHub is the promotion target after CI passes
  • ArgoCD syncs from Gitea automatically

Code Style

  • Python 3.12, type hints everywhere
  • Pydantic for data validation
  • FastAPI for HTTP services
  • asyncio + asyncpg/aioredis for async I/O
  • Minimal dependencies, prefer stdlib where possible
  • Frontend: React 19, TypeScript strict mode, Tailwind CSS, TanStack Router/Query, Recharts for charts
  • UUID fields from asyncpg must be converted to str via _row_dict() helpers
  • asyncpg interval parameters must be Python timedelta objects, not SQL strings
  • Recharts v3 callbacks: do NOT add explicit type annotations on formatter/tickFormatter — let TypeScript infer

Common Pitfalls

  • asyncpg expects timedelta for $N::interval params, not strings like '7 days'
  • asyncpg expects UUID objects/strings for $N::uuid params — synthetic IDs like pattern:AAPL:earnings:7d will fail
  • The competitor_relationships table uses UUID company IDs — queries must join through companies to match by ticker
  • The dashboard Docker build uses TypeScript strict mode — unused imports that pass local diagnostics will fail in CI
  • Ingestion jobs require source_id from the sources table — don't just pass ticker

Documentation

  • Do NOT create large summary/success markdown files after each step
  • Keep notes short, concise, and organized under docs/notes/
  • If a note isn't useful for future reference, don't write it

Documentation Maintenance on Feature Changes

When implementing a feature or fix that introduces an impactful change, update the relevant documentation as part of the same commit or task. "Impactful" means any change that affects how someone installs, deploys, configures, operates, or understands the system. Specifically:

  • New database migrations: Update docs/architecture-data-pipeline.md or docs/api-reference.md if new tables, views, or endpoints are added. Update project-context.md steering file with the new migration number.
  • New API endpoints: Update docs/api-reference.md with the endpoint path, method, parameters, and response shape.
  • New services or service changes: Update docs/architecture-docker-compose.md and docs/docker-deployment.md if a new service is added or an existing service's configuration changes.
  • Helm chart changes: Update docs/helm-reference.md if new values, services, or config options are added.
  • New environment variables or secrets: Update docs/LOCAL_DEV_SETUP.md and the project-context steering file.
  • Install/deploy script changes: Update deploy-docker.sh, docs/docker-deployment.md, or the relevant runme scripts if the deploy process changes.
  • Frontend route or page additions: Update docs/api-reference.md (if it covers UI routes) and ensure the nav item is documented.
  • README.md: Update the top-level README.md when a major new capability is added (new signal layer, new dashboard section, new trading feature).
  • Steering files: Update .kiro/steering/project-context.md when migration numbers advance, new services are added, or key conventions change.

The goal is that someone reading the docs can always understand the current state of the system without reading the source code. When in doubt, update the doc.