Commit Graph

236 Commits

Author SHA1 Message Date
Deeman
8d1dbace0f fix(analytics): add _conn module-level override for test patching
Some checks failed
CI / test-cli (push) Successful in 11s
CI / test-sqlmesh (push) Successful in 12s
CI / test-web (push) Failing after 12s
CI / tag (push) Has been skipped
Tests monkeypatch analytics._conn to inject a temp DuckDB connection.
The attribute didn't exist; fetch_analytics now uses it when set,
bypassing the _db_path / threading.local path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 02:00:11 +01:00
Deeman
cddcd4463e docs: update CI/CD references from GitLab to Gitea
Some checks failed
CI / test-cli (push) Successful in 11s
CI / test-sqlmesh (push) Successful in 13s
CI / test-web (push) Failing after 12s
CI / tag (push) Has been skipped
Replace .gitlab/.gitlab-ci.yml with .gitea/workflows/ci.yaml, update
CI_JOB_TOKEN → github.token, CI_PIPELINE_IID → github.run_number, and
update setup instructions to point to git.padelnomics.io deploy keys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 01:58:09 +01:00
Deeman
efb5a165e7 fix(billing): add missing hook infrastructure (_billing_hooks, on_billing_event, _fire_hooks)
Some checks failed
CI / test-cli (push) Successful in 11s
CI / test-sqlmesh (push) Successful in 13s
CI / tag (push) Has been cancelled
CI / test-web (push) Has been cancelled
Tests expected a billing event hook system that was never implemented.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 01:57:48 +01:00
Deeman
d58fa67238 fix(tests): update test assertions to match refactored function signatures
Some checks failed
CI / test-cli (push) Successful in 11s
CI / test-sqlmesh (push) Successful in 13s
CI / test-web (push) Failing after 11s
CI / tag (push) Has been skipped
- Pass url_template and landing_subdir to extract_cot_year (signature changed to support both COT variants)
- Update secrets test assertion from 'ESC connection successful' to 'SOPS decryption successful'

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-28 01:56:13 +01:00
Deeman
66d484955d fix: correct Gitea repo name materia → beanflows
Some checks failed
CI / test-cli (push) Failing after 57s
CI / test-sqlmesh (push) Successful in 13s
CI / test-web (push) Failing after 11s
CI / tag (push) Has been skipped
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:19:18 +01:00
Deeman
2e928de156 chore: migrate from GitLab to self-hosted Gitea
Some checks failed
CI / test-cli (push) Has been cancelled
CI / test-sqlmesh (push) Has been cancelled
CI / test-web (push) Has been cancelled
CI / tag (push) Has been cancelled
Update bootstrap_supervisor.sh and setup_server.sh to use
git.padelnomics.io:2222 instead of gitlab.com.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 18:05:54 +01:00
Deeman
1de5d69906 fix(supervisor): use sqlmesh plan prod --auto-apply instead of run
'run' requires the prod environment to already exist and defaults to
dev_<username> on first run. 'plan --auto-apply' initializes prod if
missing and applies pending changes — fully self-healing.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:49:38 +01:00
Deeman
dd07b0218b fix(infra): chown -R APP_DIR so service user owns full tree
Without -R, a manual uv sync or git operation run as root would create
files under the app dir owned by root, breaking uv for the service user
(Permission denied on .venv/bin/python3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 15:23:20 +01:00
Deeman
a5d2a61cfb fix(billing): add missing helper functions and fix upsert_subscription signature
- Add upsert_billing_customer / get_billing_customer (billing_customers table)
- Add record_transaction (idempotent on provider_transaction_id)
- Fix upsert_subscription: remove provider_customer_id param, key by
  provider_subscription_id instead of user_id (allows multi-sub)
- Update webhook handler to call upsert_billing_customer separately

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 14:43:14 +01:00
Deeman
3faa29d8e5 fix(ci): move .gitlab-ci.yml to repo root so GitLab picks it up
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 14:26:37 +01:00
Deeman
2d79627ca9 fix(infra): change host port to 5001 to avoid conflict with padelnomics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 14:12:45 +01:00
Deeman
33c8b4edbd update kyes 2026-02-27 13:51:56 +01:00
Deeman
5e22f2e1ae update secrets 2026-02-27 13:30:53 +01:00
Deeman
37b48d8f1c chore: use git remote for copier _src_path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 11:03:18 +01:00
Deeman
dee0600ee8 chore: delete stale web/ deployment files (now at repo root)
Removes: web/Dockerfile, web/docker-compose.yml, web/docker-compose.prod.yml,
web/deploy.sh, web/litestream.yml, web/router/, web/.copier-answers.yml,
web/.env.example — all superseded by root-level counterparts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 10:26:26 +01:00
Deeman
3a8dd6ba00 refactor: add .copier-answers.yml at root + feature flags + .env.example
.copier-answers.yml (new, at repo root):
- Points to local template path (was GitLab remote)
- _commit: v0.19.0 (enables copier update)
- Reflects actual feature set: enable_cms, enable_daas, not directory/i18n/leads

web/src/beanflows/core.py:
- Added ENABLE_CMS/ENABLE_DAAS/ENABLE_DIRECTORY/ENABLE_LEADS/BUSINESS_MODEL
  to Config class (mirrors copier.yml questions for runtime feature gating)

.env.example (new, at repo root):
- Moved from web/.env.example; updated DUCKDB_PATH/SERVING_DUCKDB_PATH
  to root-relative paths (local.duckdb, analytics.duckdb)

.gitignore:
- Added web/src/beanflows/static/css/output.css (previously only in web/.gitignore)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 10:25:28 +01:00
Deeman
c4cb263f18 refactor: move deployment files from web/ to repo root
Moves Dockerfile, docker-compose.yml, docker-compose.prod.yml, deploy.sh,
litestream.yml, and router/ from web/ to the monorepo root so copier update
can manage them from the template.

Dockerfile updated for monorepo layout:
- CSS build stage: COPY web/src/ ./web/src/ (Tailwind input path updated)
- Python build stage: copies root uv.lock + web/pyproject.toml separately,
  runs `uv sync --package beanflows` (not full workspace sync)
- Runtime CSS copy path updated to web/src/beanflows/static/css/output.css

deploy.sh: fixed sops path ($APP_DIR/.env.prod.sops, was $APP_DIR/../)

supervisor.py:
- web_code_changed(): Dockerfile path is now root-level (was web/Dockerfile)
- tick(): deploy script is now ./deploy.sh (was ./web/deploy.sh)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 10:24:52 +01:00
Deeman
128c177401 feat(ci): add Gitea Actions workflow
Mirrors the existing GitLab CI: three parallel test jobs (cli, sqlmesh,
web) gated by a tag job that creates v<run_number> on master. Supervisor
polls for new tags to deploy — no SSH keys or deploy credentials in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 08:20:28 +01:00
Deeman
7c5235ff39 feat(admin): flat sidebar + horizontal subnav navigation
Replace grouped section labels + 9 individual links with 5 flat
section-level items (Dashboard, Manage, Content, Engagement, System)
and a horizontal tab strip for multi-page sections. Active state
derived via _section_map dict — no JS required.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 07:53:07 +01:00
Deeman
2b2a7274ca fix(ci): always run tests, remove needs:[] so tag waits for passing tests
Two bugs in the previous CI:
- needs: [] on the tag job bypassed stage ordering — tags were created before
  tests finished, defeating the entire pull-based deploy safety guarantee
- changes: rules meant a push to infra/ or docs would skip all tests but still
  create a tag

Now matches the padelnomics pattern: all three test jobs always run on master
and MRs, tag job runs after the test stage completes (stage ordering, no needs).
Also use uv sync --all-packages consistently across all jobs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 07:47:02 +01:00
Deeman
54dbb296dd fix(secrets): add secrets-updatekeys-prod target, use --input-type dotenv
sops updatekeys doesn't inherit --input-type from context, so calling it bare
on .env.prod.sops causes "Error unmarshalling input json" (guesses JSON from
the .sops extension). Explicit --input-type dotenv fixes it.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 07:40:03 +01:00
Deeman
7d3263a39c chore: add server age key 2026-02-27 07:37:36 +01:00
Deeman
fd164ca66a chore: add server age key 2026-02-27 07:31:56 +01:00
Deeman
a3ce707a5b fix(infra): fix setup_server.sh summary — correct bootstrap command + sops format
- Detect server IP at runtime (hostname -I) and print real ssh command
- Replace misleading >- yaml block + '+' notation with correct comma-separated
  age key format: age: <dev-key>,<server-key>
- Label next steps as "(run from your workstation)"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 07:31:14 +01:00
Deeman
d14b45f7d6 fix(infra): guard SSH config write, add ROTATE_KEYS for key rotation
setup_server.sh is now fully idempotent on re-runs:
- deploy key generation was already guarded; SSH config write was not
- SSH config now only written if it doesn't exist (content never changes)
- ROTATE_KEYS=1 deletes the old keypair before generation, prints new
  public key to add to GitLab

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 07:12:09 +01:00
Deeman
c778903264 merge(infra): consolidate tool installs in setup, strip bootstrap to essentials
Merges worktree-sops-supervisor-docs → master.

Summary of changes:
- setup_server.sh: now installs all tools (git, curl, age, sops, rclone, uv) —
  single source of truth for server provisioning
- bootstrap_supervisor.sh: stripped to ~45 lines — zero tool installs, only
  clone/fetch + decrypt + uv sync + systemd enable
- readme.md: updated descriptions to reflect new responsibilities

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-27 06:57:09 +01:00
Deeman
cf65fa16b6 refactor(infra): consolidate tool installs in setup, strip bootstrap to essentials
- setup_server.sh: add git/curl/ca-certificates apt install, add uv install
  as service user, fix SSH config write (root + chown vs sudo heredoc), remove
  noise log lines after set -e makes them redundant
- bootstrap_supervisor.sh: remove all tool installs (apt, uv, sops, age) —
  setup_server.sh is now the single source of truth; strip to ~45 lines:
  age-key check, clone/fetch, tag checkout, decrypt, uv sync, systemd enable
- readme.md: update step 1 and step 3 descriptions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 22:25:31 +01:00
Deeman
0317cb885f feat(infra): use beanflows_service for supervisor
- materia-supervisor.service: User=root → User=beanflows_service,
  add PATH so uv (~/.local/bin) is found without a login shell
- setup_server.sh: full rewrite — creates beanflows_service (nologin),
  generates SSH deploy key + age keypair as service user at XDG path
  (~/.config/sops/age/keys.txt), installs age/sops/rclone as root,
  prints both public keys + numbered next-step instructions
- bootstrap_supervisor.sh: full rewrite — removes GITLAB_READ_TOKEN
  requirement, clones via SSH as service user, installs uv as service
  user, decrypts with SOPS auto-discovery, uv sync as service user,
  systemctl as root
- web/deploy.sh: remove self-contained sops/age install + keypair
  generation; replace with simple sops check (exit if missing) and
  SOPS auto-discovery decrypt (no explicit key file needed)
- infra/readme.md: update architecture diagram for beanflows_service
  paths, update setup steps to match new scripts

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 21:33:31 +01:00
Deeman
b27f06d811 chore: remove stale ralph-loop session file
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:28:31 +01:00
Deeman
ba3994a63b chore: add secrets-encrypt-dev/prod targets to match template 2026-02-26 20:27:35 +01:00
Deeman
6d4921d1a6 chore: align Makefile with padelnomics (pinned tailwind version, help target, dev target, .PHONY) 2026-02-26 20:23:31 +01:00
Deeman
c469f585eb docs: add PROJECT.md with backlog (retry/backoff for ICE + yfinance) 2026-02-26 20:08:12 +01:00
Deeman
8f97c6b0c9 fix(positioning): prevent canvas collapse on type/range toggle
- Lock #positioning-canvas min-height to current offsetHeight before each
  HTMX swap, release it in htmx:afterSwap — prevents flash-to-zero during
  Chart.js initialization in the new content
- Add CSS min-height:200px fallback on all canvas containers so they never
  fully collapse even before JS runs
- Extract _swapCanvas() helper to deduplicate setRange/setType logic

Root cause of visual collapse: cot_positioning_combined table missing
(needs sqlmesh plan prod + export_serving to materialize).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 20:04:19 +01:00
Deeman
d3c9d95386 fix(analytics): wrap max_date in ANY_VALUE() in get_weather_stress_latest
DuckDB requires all selected columns to be aggregate expressions when there
is no GROUP BY. latest.max_date is a scalar CTE value but still needs
ANY_VALUE() wrapping to satisfy the binder.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 19:50:27 +01:00
Deeman
6461c58957 fix(web): fix Chart.js sizing after HTMX swaps on all dashboard pages
Two-part fix for charts going tiny on range changes (especially 3m) and
staying broken after subsequent navigations:

1. dashboard_base.html: global htmx:beforeSwap handler destroys any Chart.js
   instances in the swap target before HTMX replaces the DOM. Without this,
   the old chart's ResizeObserver remains attached to the parent container and
   interferes with the newly created chart instance's dimension calculations.

2. All chart pages (positioning, supply, warehouse, weather): afterSwap handler
   now wraps chart resize in requestAnimationFrame, ensuring the browser has
   completed layout before Chart.js measures container dimensions. MA toggle
   state is also restored inside the rAF callback after resize.

Root cause: chart init scripts run synchronously during innerHTML swap, before
browser layout is complete. Fast server responses (e.g. 3m = small dataset)
gave even less time for layout, making the timing issue reproducible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 15:16:13 +01:00
Deeman
411aea3954 merge: SOPS migration + Python supervisor + docs (3 repos) 2026-02-26 12:15:35 +01:00
Deeman
518b50d0f5 docs(claude+infra): expand CLAUDE.md + infra/readme.md for full architecture
CLAUDE.md additions:
- List all 6 extractor packages + extract_core
- Full data flow with all sources + dual-DuckDB
- Foundation-as-ontology: dim_commodity conforms cross-source identifiers
- Two-DuckDB architecture explanation (why not serving.duckdb)
- Extraction pattern: one-package-per-source, state SQLite, adding new source
- Supervisor: croniter scheduling, topological waves, tag-based deploy
- CI/CD: pull-based via git tags, no SSH
- Secrets management: SOPS+age section, file table, server key workflow
- uv workspace management section
- Remove Pulumi ESC references; update env vars table

infra/readme.md:
- Update architecture diagram (add analytics.duckdb, age-key.txt)
- Rewrite setup flow: setup_server.sh → add key to SOPS → bootstrap
- Secrets management section with file table
- Deploy model: pull-based (no SSH/CI credentials)
- Monitoring: add supervisor status + extraction state DB query

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:04:55 +01:00
Deeman
95f881827e feat(infra): replace Pulumi ESC with SOPS in bootstrap + setup scripts
- bootstrap_supervisor.sh: remove esc CLI + PULUMI_ACCESS_TOKEN; install
  sops+age; check age keypair exists; decrypt .env.prod.sops → .env;
  checkout latest release tag; use uv sync --all-packages
- setup_server.sh: add age keypair generation at /opt/materia/age-key.txt;
  install age binary; print public key with .sops.yaml instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 12:03:11 +01:00
Deeman
5d7d53a260 feat(supervisor): port Python supervisor from padelnomics + workflows.toml
Port padelnomics' schedule-aware Python supervisor to materia:
- src/materia/supervisor.py — croniter scheduling, topological wave
  execution (parallel independent workflows), tag-based git pull + deploy,
  status CLI subcommand
- infra/supervisor/workflows.toml — workflow registry (psd daily, cot
  weekly, prices daily, ice daily, weather daily)
- infra/supervisor/materia-supervisor.service — updated ExecStart to Python
  supervisor, added SUPERVISOR_GIT_PULL=1

Adaptations from padelnomics:
- Uses extract_core.state.open_state_db (not padelnomics_extract.utils)
- uv run sqlmesh -p transform/sqlmesh_materia run
- uv run materia pipeline run export_serving
- web/deploy.sh path (materia's deploy.sh is under web/)
- Removed proxy_mode (not used in materia)

Also: add croniter dependency to src/materia, delete old supervisor.sh.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:59:55 +01:00
Deeman
64687d192c merge: CFTC COT combined (futures+options) report — extractor, transform, web toggle 2026-02-26 11:29:20 +01:00
Deeman
0326e5c83d feat(web): add F+O Combined toggle to positioning dashboard
- analytics.py: add _cot_table() helper; add combined=False param to
  get_cot_positioning_time_series(), get_cot_positioning_latest(),
  get_cot_index_trend(); add get_cot_options_delta() for MM net delta
  between combined and futures-only
- dashboard/routes.py: read ?type=fut|combined param; pass combined flag
  to analytics calls; conditionally fetch options_delta when combined
- api/routes.py: add ?type= param to /positioning and /positioning/latest
  endpoints; returned JSON includes type field
- positioning.html: add report type pill group (Futures / F+O Combined)
  with setType() JS; setRange() and popstate now preserve the type param
- positioning_canvas.html: sync type pills on HTMX swap; show Opt Δ badge
  on MM Net card when combined+options_delta available; conditional chart
  title and subtitle reflect which report variant is shown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 11:25:05 +01:00
Deeman
b884bc2b4a feat(cot): add combined (futures+options) COT extractor and transform models
- extract/cftc_cot: refactor extract_cot_year() to accept url_template and
  landing_subdir params; add _extract_cot() shared loop; add extract_cot_combined()
  entry point using com_disagg_txt_{year}.zip → landing/cot_combined/
- pyproject.toml: add extract_cot_combined script entry point
- macros/__init__.py: add @cot_combined_glob() for cot_combined/**/*.csv.gzip
- fct_cot_positioning.sql: union cot_glob and cot_combined_glob in src CTE;
  add report_type column (FutOnly_or_Combined) to cast_and_clean + deduplicated;
  include FutOnly_or_Combined in hkey to avoid key collisions; add report_type to grain
- obt_cot_positioning.sql: add report_type = 'FutOnly' filter to preserve
  existing serving behavior
- obt_cot_positioning_combined.sql: new serving model filtered to report_type =
  'Combined'; identical analytics (COT index, net %, windows) on combined data
- pipelines.py: register extract_cot_combined; add to extract_all meta-pipeline

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 11:24:56 +01:00
Deeman
520da2c920 feat(ci): switch to pull-based deploy via git tags
Replace push-based SSH deploy (deploy:web stage with SSH credentials +
individual env var injection) with tag-based pull deploy:

- Add `tag` stage: creates v${CI_PIPELINE_IID} tag using CI_JOB_TOKEN
- Remove all SSH variables (SSH_PRIVATE_KEY, SSH_KNOWN_HOSTS, DEPLOY_USER,
  DEPLOY_HOST) and all individual secret variables from CI
- Zero deploy secrets in CI — only CI_JOB_TOKEN (built-in) needed

Deployment is now handled by the on-server supervisor (src/materia/supervisor.py)
which polls for new v* tags every 60s and runs web/deploy.sh automatically.
Secrets live in .env.prod.sops (git-committed, age-encrypted), decrypted at
deploy time by deploy.sh — never stored in GitLab CI variables.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 11:10:06 +01:00
Deeman
4c7e520804 fix(deploy): add analytics.duckdb bind-mount to docker-compose.prod.yml
App containers need access to the serving DuckDB populated by the
pipeline supervisor. Bind-mounts /data/materia/analytics.duckdb as
read-only and sets SERVING_DUCKDB_PATH in container environment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:59:33 +01:00
Deeman
f253e39c2c feat(deploy): port padelnomics deploy.sh improvements to web/deploy.sh
- Auto-install sops + age binaries to web/bin/ if not present
- Generate age keypair at repo root age-key.txt if missing (prints public
  key with instructions to add to .sops.yaml, then exits)
- Decrypt .env.prod.sops → web/.env at deploy time (no CI secrets needed)
- Backup SQLite DB before migration (timestamped, keeps last 3)
- Rollback on health check failure: dump logs + restore DB backup
- Reset nginx router to current slot before --wait to avoid upstream errors
- Remove web/scripts/deploy.sh (duplicate)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:59:07 +01:00
Deeman
643c0b2db9 feat(secrets): update core.py dotenv to load from repo root .env
Load .env from repo root first (created by `make secrets-decrypt-dev`),
falling back to web/.env for legacy setups. Also fixes import sort order
and removes unused httpx import.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:47:34 +01:00
Deeman
6d716a83ae feat(secrets): rewrite secrets.py for SOPS, update cli.py
secrets.py: replace Pulumi ESC (esc CLI) with SOPS decrypt. Reads
.env.prod.sops via `sops --decrypt`, parses dotenv output. Same public
API: get_secret(), list_secrets(), test_connection().

cli.py: update secrets subcommand help text and test command messaging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:44:25 +01:00
Deeman
9d0e6843f4 feat(secrets): add SOPS+age secret management infrastructure
- .sops.yaml: creation rules matching .env.{dev,prod}.sops (dotenv format)
- .env.dev.sops: encrypted dev defaults (blank API keys, local paths)
- .env.prod.sops: encrypted prod template (placeholder values to fill in)
- Makefile: root Makefile with secrets-decrypt-dev/prod, secrets-edit-dev/prod, css-build/watch
- .gitignore: add age-key.txt

Dev workflow: make secrets-decrypt-dev → .env (repo root) → web app picks it up.
Server: deploy.sh will auto-decrypt .env.prod.sops on each deploy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-26 10:36:14 +01:00
Deeman
b25b8780a7 docs: update inventory with ICE options research findings
- yfinance confirmed not viable (OPRA only, KC=F not covered)
- CFTC COT combined report is the free immediate path (URL change only)
- ICE Report Center settlement data viable with WebICE login automation
- Barchart OnDemand has correct coverage but requires paid subscription
- All OpenBB providers, Polygon.io, Nasdaq Data Link confirmed no KC=F coverage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 10:16:50 +01:00
Deeman
70415e23b8 docs: add data sources inventory
Documents all 7 ingested sources (CFTC COT, Yahoo Finance KC=F, ICE stocks×3,
USDA PSD, Open-Meteo ERA5) plus planned sources (ICE options, COT combined,
World Bank Pink Sheet, FAO crop calendar). Matches padelnomics inventory format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-26 09:57:46 +01:00