Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
3019be0
Merge pull request #294 from w7-mgfcode/main
w7-mgfcode May 25, 2026
97c13f8
docs(docs): add forecast intelligence planning docs (#295)
w7-mgfcode May 26, 2026
8ca0831
Merge pull request #296 from w7-mgfcode/docs/forecast-intelligence-prps
w7-mgfcode May 26, 2026
40d536c
feat(data,repo): add local demo tooling + seeder window fix (#297)
w7-mgfcode May 26, 2026
1f36c74
fix(data): address review feedback on seed_registry_from_jobs (#297)
w7-mgfcode May 26, 2026
26a105a
Merge pull request #298 from w7-mgfcode/chore/local-demo-enrichment-t…
w7-mgfcode May 26, 2026
4cbcdf4
feat(forecast): add feature frame v2 (#299)
w7-mgfcode May 26, 2026
f2bf7c8
Merge pull request #300 from w7-mgfcode/feat/forecast-feature-frame-v2
w7-mgfcode May 26, 2026
0e091c7
docs(prp): refresh prp36 after feature frame v2 (#295)
w7-mgfcode May 26, 2026
1437bc6
Merge pull request #301 from w7-mgfcode/docs/refresh-prp36-after-prp35
w7-mgfcode May 26, 2026
a12c374
feat(forecast): add model zoo and backtesting comparison (#302)
w7-mgfcode May 26, 2026
d9bd3ae
fix(forecast): address PR #303 review feedback (#302)
w7-mgfcode May 26, 2026
0e2ad9e
Merge pull request #303 from w7-mgfcode/feat/forecast-model-zoo-and-b…
w7-mgfcode May 26, 2026
6b5292d
docs(forecast): refresh prp37 after model zoo contracts (#295)
w7-mgfcode May 26, 2026
c0aea20
Merge pull request #304 from w7-mgfcode/docs/refresh-prp37-after-prp36
w7-mgfcode May 26, 2026
d92e2ca
feat(ui): add interactive forecast intelligence UI (#305)
w7-mgfcode May 26, 2026
5e86b6f
Merge pull request #306 from w7-mgfcode/feat/forecast-interactive-ui
w7-mgfcode May 26, 2026
f6f2613
fix(ui): rename duplicate trainFamily binding in forecast page (#307)
w7-mgfcode May 26, 2026
48cddf3
Merge pull request #308 from w7-mgfcode/fix/forecast-train-family-dup…
w7-mgfcode May 26, 2026
9c4bb91
feat(api,ui): showcase pipeline richer data and v2 foundation (#309)
w7-mgfcode May 26, 2026
3e771c9
Merge pull request #310 from w7-mgfcode/feat/showcase-38-data-modelin…
w7-mgfcode May 26, 2026
72823a9
docs(docs): add rich showcase planning artifacts (#313)
w7-mgfcode May 26, 2026
90a2c36
Merge pull request #314 from w7-mgfcode/docs/showcase-prp-39-40-plann…
w7-mgfcode May 26, 2026
14041ca
feat(api,ui): showcase pipeline — decision + portfolio lifecycle (#316)
w7-mgfcode May 26, 2026
babb6b3
Merge pull request #318 from w7-mgfcode/feat/showcase-39-decision-por…
w7-mgfcode May 26, 2026
39a7f21
feat(api,ui): showcase planning + knowledge lifecycle (#315)
w7-mgfcode May 26, 2026
015b47b
Merge pull request #317 from w7-mgfcode/feat/showcase-40-planning-kno…
w7-mgfcode May 26, 2026
9a610e2
docs(docs): refresh initial 41 after prp 39 40 (#313)
w7-mgfcode May 26, 2026
2615176
Merge pull request #319 from w7-mgfcode/docs/initial-41-drift-patch
w7-mgfcode May 26, 2026
72a954e
fix(data): make phase2 enrichment idempotent (#312)
w7-mgfcode May 26, 2026
608d5a9
test(data): relax phase2 idempotency assertion for sparse CI DB (#312)
w7-mgfcode May 26, 2026
b3ba1f4
Merge pull request #320 from w7-mgfcode/fix/seeder-phase2-enrichment-…
w7-mgfcode May 26, 2026
e5349af
docs(docs): add prp 41 showcase agent ops polish (#321)
w7-mgfcode May 26, 2026
58d593a
Merge pull request #322 from w7-mgfcode/docs/add-prp-41-showcase-agen…
w7-mgfcode May 26, 2026
b6f3e4d
feat(api,ui): showcase pipeline agent ops final polish (#321)
w7-mgfcode May 26, 2026
bd07d2c
chore(repo): apply ruff format to test_e2e_demo.py (#321)
w7-mgfcode May 26, 2026
511ff41
Merge pull request #323 from w7-mgfcode/feat/showcase-41-agent-ops-po…
w7-mgfcode May 26, 2026
b2caef9
fix(api): repair showcase safer promote cascade (#324)
w7-mgfcode May 31, 2026
fc70571
fix(api): address review feedback on showcase safer promote cascade (…
w7-mgfcode May 31, 2026
a838b20
docs(docs): add showcase manual demo guide (#324)
w7-mgfcode May 31, 2026
6dd1708
Merge pull request #325 from w7-mgfcode/fix/showcase-safer-promote-ar…
w7-mgfcode May 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ FORECAST_DEFAULT_HORIZON=14
FORECAST_MAX_HORIZON=90
FORECAST_MODEL_ARTIFACTS_DIR=./artifacts/models
FORECAST_ENABLE_LIGHTGBM=false
# FORECAST_ENABLE_XGBOOST defaults to false (opt-in; install ml-xgboost extra)
# FORECAST_ENABLE_RANDOM_FOREST=false # PRP-36 optional model — pure sklearn, no extra needed

# RAG Configuration
# Embedding Provider: "openai" or "ollama"
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,5 @@ artifacts/
HANDOFF.md

# Local CI / dogfood logs and screenshots (per-session, never committed)
.ci-logs/
.ci-logs/
docs/manual_hun/
147 changes: 147 additions & 0 deletions PR-BODY-DRAFT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# feat(forecast): add feature frame v2

Tracking issue: **#299** (under Forecast Intelligence roadmap epic **#295**).
PRP: `PRPs/PRP-35-forecast-intelligence-A-feature-frame-v2.md`.

## Summary

Lands the **V2 feature-frame contract** as an **additive, opt-in** surface
alongside the frozen V1 contract:

- **Shared layer** — V2 column manifest (38 default / 53 max columns across 11
`FeatureGroup`s), `V2HistoricalSidecar` / `V2FutureSidecar` data carriers,
and `build_historical_feature_rows_v2` / `build_future_feature_rows_v2`
pure row builders. `app/shared/feature_frames/` stays leaf-level.
- **Training path** — `POST /forecasting/train` accepts optional
`feature_frame_version: int = 1` and `feature_groups: list[str] | None = None`.
V2 bundles persist `feature_frame_version`, `feature_columns`,
`feature_groups`, `feature_safety_classes`, and `feature_pinned_constants`
in bundle metadata.
- **Scenarios path** — `POST /scenarios/simulate` reads `feature_frame_version`
from the loaded bundle metadata and dispatches V1 vs V2 future-frame
assembly transparently.
- **LOAD-BEARING leakage specs** — three new specs land alongside the V1
spec; never to be weakened:
- `app/shared/feature_frames/tests/test_leakage_v2.py`
- `app/features/forecasting/tests/test_regression_features_v2_leakage.py`
- `app/features/scenarios/tests/test_future_frame_v2_leakage.py`

## V1 compatibility (back-compat invariant)

- Every V1 export keeps its current signature, return type, and behaviour.
- The load-bearing V1 leakage spec
(`app/shared/feature_frames/tests/test_leakage.py`) and 22 sibling V1
contract tests remain green **without modification**.
- V1 bundles trained before this PR load, predict, scenario-simulate, and
backtest unchanged.
- `feature_frame_version=1` is the default everywhere; legacy bundles that
predate the metadata field are treated as V1 via
`bundle.metadata.get("feature_frame_version", 1)`.
- `feature_frame_version` lives on `TrainRequest`, **not** on
`ModelConfigBase` — adding it to the config would mutate every existing V1
`config_hash()` and orphan registry rows / aliases. Persisted to bundle
metadata instead.

## V2 opt-in behaviour

- A `TrainRequest` with `feature_frame_version=2` (optionally `feature_groups=[…]`)
triggers the V2 path; otherwise V1 runs unchanged.
- Validator gates:
- V1 + `feature_groups` supplied → 422.
- V2 + unknown `FeatureGroup` name → 422.
- Default V2 groups: `TARGET_HISTORY`, `CALENDAR`, `ROLLING`, `TREND`,
`PRICE_PROMO`, `LIFECYCLE` (38 columns). Phase-2 sidecar groups
(`INVENTORY`, `REPLENISHMENT`, `RETURNS`, `EXOGENOUS_WEATHER`,
`EXOGENOUS_MACRO`) are off by default so the MVP stays green on smaller
seeded DBs (max 53 columns when all enabled).
- Pinned V2 constants: `EXOGENOUS_LAGS_V2=(1,7,14,28,56,364)`,
`ROLLING_WINDOWS_V2=(7,28,90)`, `TREND_WINDOWS_V2=(30,90)`,
`HISTORY_TAIL_DAYS_V2=400`.

## Validation

All four mandatory gates green locally on `Python 3.12`:

```
✅ uv run ruff check . All checks passed
✅ uv run ruff format --check . 327 files already formatted
✅ uv run mypy app/ 0 PRP-35 errors (3 pre-existing xgboost noise on dev)
✅ uv run pyright app/ 0 PRP-35 errors (8 pre-existing optional-extra noise on dev)
✅ uv run pytest -m "not integration" 1480 passed, 12 skipped, 264 deselected
```

40 V2 leakage tests across 3 LOAD-BEARING files all green; 23 V1 contract /
leakage tests byte-stable.

The 3 mypy + 8 pyright pre-existing errors stem from optional `lightgbm` /
`xgboost` extras and are unrelated to PRP-35; CI runs `--all-extras` and won't
see them.

## No Alembic migration

V2 reads only existing tables (`inventory_snapshot_daily`,
`replenishment_event`, `sales_returns`, `exogenous_signal`, `promotion`,
`product`) and writes nothing to the DB. `alembic heads` unchanged at
`c1d2e3f40512`.

## Deferred: V2 backtesting dispatch — tracked in #299

PRP-35 lands V2 **training + scenarios + shared builders**. **Backtesting V2
dispatch is deferred** and explicitly tracked in the
"Deferred follow-up: V2 backtesting dispatch" section of **#299**.

PRP-35 Task 13 reads *"READ `feature_frame_version` from the fitted bundle
BEFORE the fold loop"*, but
`app/features/backtesting/service.py:_run_model_backtest` trains fresh per
fold from `BacktestConfig.model_config_main` and **does not load a fitted
bundle**. The correct opt-in surface is a request-time field on
`BacktestConfig` itself — a re-design Task 13 did not spec.

**This PR does NOT claim completion of PRP-35 Tasks 13 or 18.** V1
backtesting is unchanged; a V2-trained bundle still trains and
scenario-simulates correctly. Only `/backtesting/run` remains V1-only until
the follow-up under #299 lands. Integration tests (PRP-35 Tasks 15 + 16) and
the PHASE/3 + PHASE/4 doc edits (Task 21) are also deferred there.

## qwen3 stash status

The session's `stash@{0}` ("local qwen3 rag demo changes before prp-35",
`app/features/rag/models.py` +7/-2) is **not applied, not popped, not
dropped**. The decision on it (write a real
`INITIAL-rag-embedding-provider-pluggability.md` doc vs. add to
`.git/info/exclude`) is carryover work, untouched by this PR.

## Files changed

```
M app/features/forecasting/routes.py (+2)
M app/features/forecasting/schemas.py (+70)
M app/features/forecasting/service.py (+318)
M app/features/scenarios/feature_frame.py (+193)
M app/features/scenarios/service.py (+25)
M app/shared/feature_frames/__init__.py (+79)
M docs/optional-features/10-baseforecaster-feature-contract.md (+40)
A app/shared/feature_frames/contract_v2.py
A app/shared/feature_frames/rows_v2.py
A app/shared/feature_frames/sidecar.py
A app/shared/feature_frames/tests/test_contract_v2.py
A app/shared/feature_frames/tests/test_leakage_v2.py
A app/features/forecasting/v2_loaders.py
A app/features/forecasting/tests/test_regression_features_v2_leakage.py
A app/features/scenarios/tests/test_future_frame_v2_leakage.py
A examples/forecasting/feature_frame_v2_preview.py
A PR-BODY-DRAFT.md
```

## Test plan

- [ ] CI green on all five gates (ruff / mypy / pyright / pytest / migration-check).
- [ ] Verify `/forecasting/train` accepts `feature_frame_version=2` with default groups.
- [ ] Verify `/forecasting/train` accepts `feature_frame_version=2` with opt-in
Phase-2 group (e.g. `INVENTORY`) on a seeded DB carrying inventory rows.
- [ ] Verify `/scenarios/simulate` against a V2-trained bundle produces a
`model_exogenous` re-forecast (V2 future-frame assembly via bundle metadata).
- [ ] Verify a V1 bundle trained before this PR still loads, predicts, and
scenario-simulates unchanged.
- [ ] Verify `/backtesting/run` against a V2-trained bundle remains V1-only
(no V2 dispatch on the fold loop) — documented deferral above.
Loading
Loading