Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
89 commits
Select commit Hold shift + click to select a range
49ca193
chore(modernization): add plan, verification-gate tracker, and progre…
dnplkndll Jun 14, 2026
ee63b11
docs(modernization): add Engine track (review, tests, DDMRP, Rust pilot)
dnplkndll Jun 15, 2026
8f2ec3e
docs(engine): E1 code review — ~12 concrete bugs found + Rust evidence
dnplkndll Jun 15, 2026
15d30b7
fix(forecast): bound weight[] SMAPE index, fix divergent initializer
dnplkndll Jun 15, 2026
5c3270d
fix(utils): adopt owned PyObject refs, make initialize() GIL-safe
dnplkndll Jun 15, 2026
23b6a23
ci(modernization): non-blocking Debug+ASan engine job
dnplkndll Jun 15, 2026
1a5aa03
fix(solver): drop refcount leak in OperatorDelete::create
dnplkndll Jun 15, 2026
6a71127
fix(model): undefined behaviour in EntityIterator copy constructor
dnplkndll Jun 15, 2026
85fa814
fix(utils): correct inverted double bound in JSON getLong/getInt
dnplkndll Jun 15, 2026
e043790
fix(model): iterator-after-erase in Buffer::setMaximumCalendar/setMax…
dnplkndll Jun 15, 2026
24bf7f1
fix(auth): reject inactive users on JWT/token authentication
dnplkndll Jun 15, 2026
a8361b3
test(engine): add 10 pegging regression scenarios (E2)
dnplkndll Jun 15, 2026
d84dc17
fix(model): UB decrementing begin() in Calendar::EventIterator::opera…
dnplkndll Jun 15, 2026
c452697
test(engine): make pegging_4/5/7 golden-free smoke tests
dnplkndll Jun 15, 2026
22af229
test(engine): drop pegging_4/5/7 (Release crash), gate to >=9
dnplkndll Jun 15, 2026
3b390d6
ci(engine): make ASan job blocking; golden suite is ASan-clean
dnplkndll Jun 15, 2026
2dc4c4d
fix(model): null-guard dynamic_cast in Buffer::followPegging (review H4)
dnplkndll Jun 15, 2026
e3a2398
test(engine): add golden-free structural-invariant scenarios (E2)
dnplkndll Jun 15, 2026
aa06ceb
feat(api): Phase 0 — OpenAPI schema + JSON output endpoints
dnplkndll Jun 15, 2026
70b27ae
test(api): Phase 0 output-endpoint parity + schema tests
dnplkndll Jun 15, 2026
3ee2386
fix(api): schema introspection guard + robust Phase 0 tests
dnplkndll Jun 15, 2026
ca60cc0
chore(modernization): activate Phase 0 REST gates
dnplkndll Jun 15, 2026
e9432a3
feat(auth): shared JWT + scenario helpers (Phase 0 Increment 2, stage 1)
dnplkndll Jun 15, 2026
b68ae82
feat(auth): ASGI scenario routing + authenticated websockets (Phase 0…
dnplkndll Jun 15, 2026
67a8bc9
fix(asgi): import cleanly without the embedded engine module
dnplkndll Jun 15, 2026
2564667
test(asgi): query-string WS carrier + diagnostic close codes
dnplkndll Jun 15, 2026
37ff283
test(asgi): isolate TokenMiddleware to diagnose WS carrier auth
dnplkndll Jun 15, 2026
aade996
test(asgi): self-contained probe to isolate decode_jwt per carrier
dnplkndll Jun 15, 2026
2655976
feat(api): generate + typecheck TS client in CI (Phase 0 ts-client gate)
dnplkndll Jun 15, 2026
80b3366
test(asgi): clarify WS carrier tests scope (extract+decode; e2e via h…
dnplkndll Jun 15, 2026
4c8fd26
fix(solver): stop double-counting a_penalty/a_cost across capacity re…
dnplkndll Jun 16, 2026
79734ae
ci: run ts-client gate on PR events too (head_ref), not just push
dnplkndll Jun 16, 2026
b9c5881
test(engine): restore pegging_4/5/7; macOS-only crash, Linux-clean
dnplkndll Jun 16, 2026
9f8be2b
feat(ws): live task-progress over websockets (Phase 1A-1)
dnplkndll Jun 16, 2026
147a3f4
test(ws): relay test injects auth scope (avoid flaky threaded get_user)
dnplkndll Jun 16, 2026
849fe9e
test(ws): relay test uses a zero-DB active-user stand-in
dnplkndll Jun 16, 2026
0b53cf8
feat(frontend): scaffold Next.js /frontend + Execute screen (Phase 1A-3)
dnplkndll Jun 16, 2026
f653953
feat(ws): live log-tail over ws/tasks/<id>/log/ (Phase 1A-2)
dnplkndll Jun 16, 2026
ecd1818
feat(frontend): forecast data layer + editable pivot grid (Phase 1B-1)
dnplkndll Jun 16, 2026
66dcec3
feat(forecast): enriched output + override save -> re-net (Phase 1B-2)
dnplkndll Jun 16, 2026
d0ad66b
feat(api): /api/token/ - session-to-JWT for the same-origin SPA
dnplkndll Jun 16, 2026
9162957
test(e2e): compose + Playwright harness; screens verified end-to-end
dnplkndll Jun 16, 2026
711f494
feat(forecast): bulk edit + outlier highlighting (Phase 1B-3a)
dnplkndll Jun 16, 2026
7f13067
feat(forecast): Recharts series chart + a11y axe gate (Phase 1B-3b/c)
dnplkndll Jun 16, 2026
9d71270
test(e2e): engine overlay + live-progress verified end-to-end
dnplkndll Jun 16, 2026
cac4c79
deploy(helm): Helm chart + ARC image build for frepple-staging
dnplkndll Jun 16, 2026
da20187
deploy(helm): trust https origin + forwarded-proto for TLS ingress
dnplkndll Jun 16, 2026
1072f76
fix(frontend): send CSRF header on the runplan launch POST
dnplkndll Jun 16, 2026
4a59e6c
docs(deploy): record frepple-staging review env + chart README
dnplkndll Jun 16, 2026
7ccc546
feat(frontend): planning-console redesign + auth-aware Run plan
dnplkndll Jun 16, 2026
574fa8f
fix(api): harden auth/output review findings + cover inactive-user path
dnplkndll Jun 16, 2026
47e0ce3
refactor(frontend): typed auth errors + shared authedFetch; harden la…
dnplkndll Jun 16, 2026
c25e917
test(frontend): cover parseStatus, decodeClaims, and the CSRF parser
dnplkndll Jun 16, 2026
76b80a1
deploy(helm): harden staging runtime posture (DEBUG off, real secret/…
dnplkndll Jun 16, 2026
e330fb8
chore(modernization): tighten gate checks + repro-script guards
dnplkndll Jun 16, 2026
1a02222
feat(api): generalize ForecastJSONStreamView -> PivotJSONStreamView
dnplkndll Jun 16, 2026
4a18819
feat(frontend): Inventory/Buffer screen (Phase 3, screen #1)
dnplkndll Jun 16, 2026
e27a3b2
test(api): inventory endpoint is now enriched (PivotJSONStreamView)
dnplkndll Jun 16, 2026
17cf0d0
feat(api): enrich demand + resource output endpoints (PivotJSONStream…
dnplkndll Jun 17, 2026
d515e36
feat(frontend): Demand + Resource screens (Phase 3) via generic Pivot…
dnplkndll Jun 17, 2026
d7ee4f5
ci(deploy): cache the engine C++ build layer (Dockerfile reorder + bu…
dnplkndll Jun 17, 2026
b183c13
feat(engine): Rust/PyO3 pilot on the json number kernel (Engine track…
dnplkndll Jun 17, 2026
9447403
feat(engine): Rust port of MovingAverage forecast method (E4 slice 2)
dnplkndll Jun 17, 2026
f22df66
feat(engine): Rust port of SingleExponential forecast method (E4 phas…
dnplkndll Jun 17, 2026
d860800
feat(engine): Rust port of DoubleExponential forecast method (E4 phas…
dnplkndll Jun 17, 2026
2e783e3
feat(engine): Rust port of Croston forecast method (E4 phase 5)
dnplkndll Jun 17, 2026
0ee653d
feat(engine): Rust port of Seasonal forecast method (E4 phase 6) — me…
dnplkndll Jun 17, 2026
dd65ccc
feat(engine): Rust forecast C ABI + engine-integration foundation (E4…
dnplkndll Jun 17, 2026
598392b
ci(e2e): compose-based Playwright E2E guardrail with engine warmup ga…
dnplkndll Jun 17, 2026
5970f21
feat(web): read-only Demand Pegging Gantt screen (Phase 3-D1) (#3)
dnplkndll Jun 17, 2026
024e492
style(web): refine the planning-console shell (#5)
dnplkndll Jun 17, 2026
ca07fda
feat(web): drag-to-reschedule on the pegging Gantt (Phase 3-D2) (#6)
dnplkndll Jun 17, 2026
de78964
feat(engine): flag-gated Rust forecast link + golden-parity gate (Pha…
dnplkndll Jun 17, 2026
1c2cfb2
feat(engine): wire Seasonal to Rust — 5/5 forecast methods (Phase 7) …
dnplkndll Jun 17, 2026
17b132f
spike(engine): optimisation solver evaluation (good_lp + microlp) (#9)
dnplkndll Jun 17, 2026
2fa6268
feat(web): pegging downstream highlight + in-place re-plan loop (Phas…
dnplkndll Jun 17, 2026
443653b
feat(web): Problems/Constraints + Orders screens, with inline order C…
dnplkndll Jun 17, 2026
466eaef
feat(deploy): build staging engine image with Rust forecast (phase 7)…
dnplkndll Jun 17, 2026
d15905f
docs(modernization): record Rust forecast flip to staging (E4 phase 7…
dnplkndll Jun 18, 2026
45a7d1e
feat(engine): UBSan sanitizer baseline + first UB fix (E1) (#14)
dnplkndll Jun 18, 2026
5074c50
feat(engine): UBSan gate blocking + clang-tidy baseline (E2 slice 1) …
dnplkndll Jun 18, 2026
b9c95fe
docs(engine): E1 engine review report — debt/TODO triage + risk hotsp…
dnplkndll Jun 18, 2026
d6a80f1
test(engine): confirm pegging alternate/routing output is env-order-u…
dnplkndll Jun 19, 2026
d256376
test(engine): structural-invariant assertion oracle (E2) (#18)
dnplkndll Jun 20, 2026
e7143b0
test(engine): structural-invariant sweep over 11 models (E2) (#19)
dnplkndll Jun 20, 2026
1fc1473
test(engine): stress scenario with solve-time + memory regression gat…
dnplkndll Jun 20, 2026
409af33
fix(engine): deterministic operationplan ordering + pegging golden co…
dnplkndll Jun 22, 2026
a0e33a8
docs(plan): roadmap review — progress dashboard + status tags (#22)
dnplkndll Jun 22, 2026
6d58f1f
feat(api): typed OpenAPI client consumed by the SPA (Phase 0 finish) …
dnplkndll Jul 7, 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
28 changes: 28 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Engine track E1 - static-analysis baseline. The point is bug-finding (the
# "rewrite oracle"), not style: clang-analyzer-* (the path-sensitive analyzer -
# null derefs, leaks, uninitialised reads) + the high-signal bugprone-* checks.
# Style/readability/modernize families are deliberately OFF to keep the signal
# high on a large, mature C++23 codebase. Two bugprone checks are also off as
# high-volume/low-signal here: unhandled-self-assignment (fires on every operator=
# lacking a self-check - defensive, not a bug) and exception-escape (legacy
# destructors). The gate is advisory (engine-clang-tidy.yml) and reports counts;
# it tightens to "no NEW findings" once the baseline is triaged.
Checks: >
-*,
clang-analyzer-*,
bugprone-*,
-clang-analyzer-optin.*,
-clang-analyzer-cplusplus.NewDeleteLeaks,
-bugprone-easily-swappable-parameters,
-bugprone-narrowing-conversions,
-bugprone-branch-clone,
-bugprone-reserved-identifier,
-bugprone-implicit-widening-of-multiplication-result,
-bugprone-unhandled-self-assignment,
-bugprone-exception-escape

# Engine headers are ours; third-party (Python.h, xerces) is excluded by the
# regex so we only flag frePPLe code.
HeaderFilterRegex: 'frepple/.*\.h$'
WarningsAsErrors: ''
FormatStyle: none
17 changes: 17 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Keep the E2E build context lean and host-artifact-free.
.git
venv
build
logs
node_modules
frontend/node_modules
frontend/.next
# Rust build dirs are host-arch artifacts; a leaked target/ shadows the
# in-image cargo build (CMake sees the OUTPUT present and skips it), linking
# a wrong-arch/index-less staticlib. Force a clean cargo build inside the image.
**/target
**/__pycache__
*.pyc
bin/frepple
bin/libfrepple.*
.DS_Store
77 changes: 77 additions & 0 deletions .github/workflows/deploy-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: Build staging images

# Builds the modernization images on the ledoent x86 ARC runners and pushes them
# to GHCR, where the Helm chart (deploy/helm/frepple) pulls them for the
# frepple-staging review env on the hetzner-ledo cluster.
#
# ghcr.io/ledoent/frepple-app:<sha> (engine + Django; e2e/Dockerfile.engine)
# ghcr.io/ledoent/frepple-frontend:<sha> (Next.js SPA; e2e/Dockerfile.frontend)
#
# The runner is x86 (matching the cluster) and has a docker:dind sidecar, so a
# plain `docker build` produces native amd64 images without buildx/QEMU emulation.

on:
workflow_dispatch:
push:
branches:
- "modernization"
paths:
- "e2e/Dockerfile.engine"
- "e2e/Dockerfile.frontend"
- "e2e/entrypoint.sh"
- "src/**"
- "freppledb/**"
- "frontend/**"
- ".github/workflows/deploy-staging.yml"

permissions:
contents: read
packages: write

env:
REGISTRY: ghcr.io/ledoent

jobs:
build:
name: Build & push ${{ matrix.image }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- image: frepple-app
dockerfile: e2e/Dockerfile.engine
# Phase 7: staging runs the Rust forecast methods (proven 5/5
# byte-parity in the forecast-phase7 gate). Default-OFF elsewhere.
rust: "ON"
- image: frepple-frontend
dockerfile: e2e/Dockerfile.frontend
steps:
- uses: actions/checkout@v4

- name: Set up Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# buildx + a GHCR registry cache: the engine's C++ build layer is restored
# from :buildcache, so a Python/frontend-only change skips the ~4-min
# recompile (the dind runner has no persistent local layer cache).
- name: Build & push
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
push: true
build-args: |
FREPPLE_RUST_FORECAST=${{ matrix.rust || 'OFF' }}
tags: |
${{ env.REGISTRY }}/${{ matrix.image }}:${{ github.sha }}
${{ env.REGISTRY }}/${{ matrix.image }}:latest
cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ matrix.image }}:buildcache
cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ matrix.image }}:buildcache,mode=max
83 changes: 83 additions & 0 deletions .github/workflows/engine-asan.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
name: Engine ASan

# Debug build with AddressSanitizer over the C++ engine golden-test suite.
# Catches memory errors (buffer overflows, use-after-free) that the Release CI
# can't see. Deterministically flags the weight[] out-of-bounds read via the
# test/forecast_11 long-history scenario.
#
# BLOCKING: the golden suite is ASan-clean (the 8 crashes rooted in the
# Calendar::EventIterator operator-- UB are fixed). A new memory error now fails CI.
#
# Path-filtered so doc/CI/gate-only commits don't trigger the heavy build.

on:
push:
branches:
- "modernization"
- "modernization/**"
paths:
- "src/**"
- "include/**"
- "test/**"
- "CMakeLists.txt"
- ".github/workflows/engine-asan.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
build-asan:
name: Debug + ASan engine tests
runs-on: ubuntu-24.04
# Blocking: the golden suite is ASan-clean (the 8 crashes from the
# Calendar::EventIterator UB are fixed). Future memory regressions fail CI.
steps:
- name: Checking out source code
uses: actions/checkout@v6
with:
submodules: recursive

- name: Set up Nodes
uses: actions/setup-node@v6

- name: Install packages
run: |
sudo apt update
sudo apt install --no-upgrade libxerces-c-dev openssl libssl-dev libpq5 libpq-dev postgresql-client python3 python3-dev python3-venv python3-setuptools
sudo npm -g install pnpm@latest-10
pnpm install --frozen-lockfile

- name: Build (Debug + AddressSanitizer)
# CMAKE_BUILD_TYPE=Debug injects -fsanitize=address (CMakeLists.txt:88).
run: |
git submodule update --remote --checkout --force
grunt
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=Debug
cmake --build ${{github.workspace}}/build --config Debug -- -j 4
. ${{github.workspace}}/venv/bin/activate
echo "PATH=$PATH" >> $GITHUB_ENV
echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV

- name: Run engine golden tests under ASan
working-directory: ${{github.workspace}}
# detect_leaks=0: focus on memory-error detection (buffer overflows /
# use-after-free). The forecast_11 scenario forces the weight[] index
# above MAXBUCKETS, so a pre-fix binary aborts here with a
# global-buffer-overflow; post-fix it runs clean.
run: |
export FREPPLE_DATE_STYLE="day-month-year"
export ASAN_OPTIONS="detect_leaks=0:halt_on_error=1:abort_on_error=1"
# Exclude the stress scenario (test/stress_1): it solves ~24k operationplans,
# which under a Debug+ASan build is ~1000x slower (allocation-heavy at scale,
# ~24 min) than Release. It is a perf/scale gate, not a memory-safety one - it
# runs in the optimised ubuntu24 (Release) suite instead.
./test/runtest.py -e stress_1

- name: Keep logs
uses: actions/upload-artifact@v6
if: always()
with:
name: asan_logs
path: logs
retention-days: 3
99 changes: 99 additions & 0 deletions .github/workflows/engine-clang-tidy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: Engine clang-tidy

# Static-analysis baseline over the C++ engine (Engine track E1). The check set
# (.clang-tidy) is the bug-finders only - clang-analyzer-* + high-signal
# bugprone-* - not style. clang-tidy is parse-only (no codegen), so this is
# lighter than the sanitizer Debug builds.
#
# ADVISORY: reports the finding count + breakdown in the job step-summary but
# never fails the job (the baseline isn't zero on a large legacy codebase). It
# tightens to "no NEW findings on changed files" (clang-tidy-diff) once the
# baseline is triaged - tracked as E2. See tools/modernization/ubsan-baseline.md
# for the sibling sanitizer gates.
#
# Path-filtered so doc/gate-only commits don't trigger it.

on:
push:
branches:
- "modernization"
- "modernization/**"
paths:
- "src/**"
- "include/**"
- ".clang-tidy"
- ".github/workflows/engine-clang-tidy.yml"
pull_request:
paths:
- "src/**"
- "include/**"
- ".clang-tidy"
- ".github/workflows/engine-clang-tidy.yml"
workflow_dispatch:

permissions:
contents: read

jobs:
clang-tidy:
name: clang-tidy (advisory)
runs-on: ubuntu-24.04
steps:
- name: Checking out source code
uses: actions/checkout@v6
with:
submodules: recursive

- name: Install packages
run: |
sudo apt update
sudo apt install --no-upgrade libxerces-c-dev openssl libssl-dev libpq-dev python3 python3-dev python3-venv clang-tidy
clang-tidy --version

- name: Configure (export compile_commands.json)
# No build needed - clang-tidy parses TUs from the compile DB. Configure
# still generates config.h (referenced by the engine headers).
run: |
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

- name: Run clang-tidy over the engine (advisory)
# Parallel over the src/ TUs, each writing to its OWN log so concurrent
# writes can't interleave; then concatenate. Uses only `clang-tidy`
# (run-clang-tidy isn't always on PATH). Never fails the job - baseline
# capture only.
run: |
mkdir -p .tidy-logs
find src -name '*.cpp' -print0 \
| xargs -0 -P"$(nproc)" -I{} sh -c \
'clang-tidy -p build "$1" > ".tidy-logs/$(echo "$1" | tr "/." "__").log" 2>/dev/null || true' _ {}
cat .tidy-logs/*.log > clang-tidy.out 2>/dev/null || true
echo "clang-tidy run complete over $(find src -name '*.cpp' | wc -l) TUs"

- name: Summarise findings
if: always()
# A header finding is reported once per including TU; the warning line
# (path:line:col + check) is byte-identical across them, so `sort -u`
# collapses them to DISTINCT findings. The raw line count is shown too.
run: |
tidy='\[(clang-analyzer|bugprone)[a-zA-Z.-]+\]'
distinct=$(grep -hE ": warning: .*${tidy}" clang-tidy.out 2>/dev/null | sort -u | wc -l | tr -d ' ')
raw=$(grep -hcE ": warning: .*${tidy}" clang-tidy.out 2>/dev/null || echo 0)
{
echo "## clang-tidy baseline"
echo "distinct findings: **${distinct}** (raw lines incl. header re-includes: ${raw})"
echo ''
echo 'By check (distinct):'
echo '```'
grep -hE ": warning: .*${tidy}" clang-tidy.out 2>/dev/null | sort -u \
| grep -oE "${tidy}" | sort | uniq -c | sort -rn \
|| echo "(no findings)"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"

- name: Keep report
uses: actions/upload-artifact@v6
if: always()
with:
name: clang-tidy-report
path: clang-tidy.out
retention-days: 7
Loading
Loading