Skip to content
Merged
Show file tree
Hide file tree
Changes from 60 commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
f901466
pid-designer: fix the three bugs that made the canvas feel broken
Carlsaurus Sep 9, 2026
37926a5
pid-designer: give every component a config, in feed-twin's parameter…
Carlsaurus Sep 9, 2026
1de8d1f
pid-designer: real fluids, configurable lines, QD pairing
Carlsaurus Sep 9, 2026
6609139
pid-designer: instruments clip to what they measure
Carlsaurus Sep 9, 2026
4e5dc8c
pid-designer: a checks panel for what is wrong with the feed system
Carlsaurus Sep 9, 2026
a2b9f5d
pid-designer: pages, so the rocket and the GSE are one diagram
Carlsaurus Sep 9, 2026
f8face5
pid-designer: section boxes for grouping a skid or a panel
Carlsaurus Sep 9, 2026
3a36e79
pid-designer: square pipe routing, with a middle segment you can move
Carlsaurus Sep 9, 2026
34a1076
pid-designer: document what a drawing now carries
Carlsaurus Sep 9, 2026
439ae85
pid-designer: check that a tag names exactly one thing
Carlsaurus Sep 9, 2026
bca9901
pid-designer: K-bottles, dewars, and vents read off the drawing
Carlsaurus Sep 9, 2026
b949999
pid-designer: drop three exports nothing calls
Carlsaurus Sep 9, 2026
d6e64a4
Stamp the achieved design point; delete a module that never ran
Carlsaurus Sep 9, 2026
69bc957
pid-designer: ports have an identity, not just a count
Carlsaurus Sep 9, 2026
b221000
Flag Layer-2 pressure curves left stale by a Layer-1 re-run
Carlsaurus Sep 9, 2026
54d2474
pid-designer: a resized section box stays resized
Carlsaurus Sep 9, 2026
b8128cf
Make the pressurisation path configurable and its state per-config
Carlsaurus Sep 9, 2026
a395df3
pid-designer: document ports and vents
Carlsaurus Sep 9, 2026
b4b5c4b
pid-designer: strip the UI back to what a propulsion engineer needs
Carlsaurus Sep 9, 2026
7f41cd6
pid-designer: fix the port wiring, the paint tool, and the checkout f…
Carlsaurus Sep 9, 2026
6c1e96b
pid-designer: line segments and the fitting tally
Carlsaurus Sep 9, 2026
29670a7
pid-designer: how a line's loss is known
Carlsaurus Sep 9, 2026
05482d4
pid-designer: draw the flow path, so a tally can be checked
Carlsaurus Sep 9, 2026
4c61011
pid-designer: a junction needs the tool, not a click
Carlsaurus Sep 9, 2026
96654f3
pid-designer: Clear asks first, and takes only the page you are on
Carlsaurus Sep 9, 2026
bac8d7c
pid-designer: manifold geometry, one-port transducers, rotated tag dr…
Carlsaurus Sep 9, 2026
96e8a6e
Junctions appear when you branch, and go without taking the pipe
Carlsaurus Sep 9, 2026
b9699ed
Open a drawing and you can see it; click a junction and you get it
Carlsaurus Sep 9, 2026
a9224ea
Take no longer moves you, and the line panel says which numbers count
Carlsaurus Sep 9, 2026
2c87305
Drop a transducer on a line and it taps the line
Carlsaurus Sep 9, 2026
4a16e81
Merge pid-designer branching, framing and checkout fixes
Carlsaurus Sep 10, 2026
56e1c7d
Lines leave a port the way the port points, and green means inert
Carlsaurus Sep 10, 2026
9633362
Turning a symbol turns its ports, not just its picture
Carlsaurus Sep 10, 2026
ae656c0
Lettering stays on the thing it labels, and tags stop being see-through
Carlsaurus Sep 10, 2026
499d462
Tell ReactFlow the ports moved when a symbol is turned
Carlsaurus Sep 10, 2026
c6d3cc3
Four on a turned rotary valve: the vent, the P, the NC, and QD ports
Carlsaurus Sep 10, 2026
9892ad0
Dropping a symbol lines its ports up with the ones already drawn
Carlsaurus Sep 10, 2026
a1e7116
Rebuild the fittings panel around what somebody is trying to say
Carlsaurus Sep 11, 2026
b63c48f
Fittings screw into each other, and the drawing works out how far
Carlsaurus Sep 11, 2026
55c8e8b
A swage depth comes off the part, not out of this app
Carlsaurus Sep 11, 2026
81f5e0e
Saving a symbol no longer rewrites where its numbers came from
Carlsaurus Sep 12, 2026
03b91b0
The regulator dialog now feeds the solver, and the sheet checks its o…
Carlsaurus Sep 12, 2026
0fc256e
A sheet you can copy on, print, and find your way around
Carlsaurus Sep 12, 2026
276275e
The exported sheet is framed on what is drawn
Carlsaurus Sep 12, 2026
7fb315a
Locally, the checkout is never in the way
Carlsaurus Sep 12, 2026
73c79ff
pid-designer overhaul plan: slimmer dialogs, numbers on symbols, cent…
Carlsaurus Sep 12, 2026
a62b8ec
Phase 0: the header goes, the regulator's words stay put, a grid step…
Carlsaurus Sep 12, 2026
c80202b
Phase 1: dialogs ask what a person on the stand knows; symbols show t…
Carlsaurus Sep 12, 2026
db5334d
Phase 3: a line asks what a tape and a scale know
Carlsaurus Sep 12, 2026
263a494
Bring feed-twin, the physics library and the docs into the tree
Carlsaurus Sep 12, 2026
e34e176
Merge remote-tracking branch 'origin/main' into fix/stale-design-meta…
Carlsaurus Sep 12, 2026
d56fb75
Merge origin/main into fix/stale-design-metadata
Carlsaurus Sep 12, 2026
18a2a1a
A line is drawn, measured and dimensioned instead of described
Carlsaurus Sep 12, 2026
83d9d78
A number field you can type a number into
Carlsaurus Sep 12, 2026
1627f1c
Stop a render error from blanking the whole app
Carlsaurus Sep 8, 2026
52f5f0a
EngineDesign overnight sweep: solver window, config-driven stability,…
Carlsaurus Sep 12, 2026
db221a0
The local checkout is the app's choice, not the hostname's
Carlsaurus Sep 12, 2026
b843d6c
A hundred and thirty lines of the twin's API were a second copy
Carlsaurus Sep 12, 2026
8b58632
The physics core's gates run on the version CI installs
Carlsaurus Sep 12, 2026
5903aec
Merge origin/main: the auth gate order, and feed-twin with it
Carlsaurus Sep 12, 2026
6487b64
Give the twin's test suite a budget it can finish in
Carlsaurus Sep 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
18 changes: 15 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,20 @@ landing
docs
scripts/setup-test

# lib/ is excluded wholesale EXCEPT the two shared cores every design-tool
# image builds on -- the Python one the APIs install, and the TypeScript one the
# frontends compile. The negations must come after the exclusion.
# lib/ is excluded wholesale EXCEPT the shared cores that root-context images
# install or compile: the Python design core the APIs install, the TypeScript UI
# the frontends compile, and the feed system physics feed-twin's API is built on.
# The negations must come after the exclusion.
#
# Adding a package under lib/ WITHOUT a negation here is a silent trap: the
# `COPY lib/<pkg>/ ...` in its Dockerfile fails with "not found", but only
# inside a real docker build -- every local test passes, because nothing else
# consults this file. If you add a lib/ package that an image needs, add it here
# in the same commit.
lib
!lib/stardesign
!lib/stardesign-ui
!lib/feedtwin

# star-openrocket and pid-designer now build from the repo root too, so their
# sources have to reach the context -- but only their own image copies them, and
Expand All @@ -41,6 +49,10 @@ venv
**/__pycache__
**/*.pyc
**/.pytest_cache
# setuptools metadata from a host `pip install -e`. The image regenerates it
# when it installs the package, so copying the host's in only risks a stale
# SOURCES.txt shadowing the real one.
**/*.egg-info
**/.pio
**/build

Expand Down
254 changes: 254 additions & 0 deletions .github/workflows/feed-twin-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
name: feed-twin CI

'on':
push:
paths:
- 'feed-twin/**'
# The physics core. Every number this app produces comes from it, so a
# change there has to run these gates -- including the ones in the app.
- 'lib/feedtwin/**'
# EngineDesign's dependency set: the engine-design-compat job below proves
# the physics core can be installed alongside it, so a change to what
# EngineDesign pins has to re-run that proof.
- 'EngineDesign/requirements-base.txt'
- '.github/workflows/feed-twin-ci.yml'
pull_request:
paths:
- 'feed-twin/**'
- 'lib/feedtwin/**'
- 'EngineDesign/requirements-base.txt'
- '.github/workflows/feed-twin-ci.yml'
workflow_dispatch:

jobs:
# The physics core, on its own. Deliberately a separate job from the backend:
# the whole premise of ADR-0001 is that this package stands up with no web
# stack installed, and the only way to keep that true is to prove it in an
# environment that has never seen FastAPI.
library:
name: Physics core (lint, types, tests)
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
# 3.12, matching Dockerfile.api's python:3.12-slim -- the library is
# exercised here on the version it actually ships on. The backend job
# below runs 3.11, which is the floor in pyproject.toml, so between
# them both ends of the supported range are covered.
python-version: '3.12'
cache: 'pip'
cache-dependency-path: lib/feedtwin/pyproject.toml

- name: Install the physics core
# Editable, so mypy and the PropsSI scan see the tree rather than a
# copy in site-packages. Note that nothing here installs fastapi --
# test_package_carries_no_web_framework depends on that being true.
run: pip install -e "lib/feedtwin[dev]"

- name: Format (black --check)
# No working-directory: black finds the repo root by walking up to .git
# and reads [tool.black] from the root pyproject.toml, keeping this
# package on the same style as the rest of the repo.
run: black --check lib/feedtwin

- name: Types (mypy --strict)
# working-directory matters: mypy reads its target from [tool.mypy] in
# lib/feedtwin/pyproject.toml, and discovers that file relative to the
# working directory. Run from the repo root it finds the root
# pyproject.toml, which configures only black, and exits with
# "Missing target module, package, files, or command".
working-directory: lib/feedtwin
run: mypy

# Two gates, and the second is the reason this phase exists.
#
# test_package.py proves `import feedtwin` works and the physics stack
# resolved -- Phase 00's exit criterion, half of it.
#
# test_property_call_discipline.py fails the build on any PropsSI call in
# library code. That is not style policing: PropsSI rebuilds its backend
# per call and is ~1300x slower than a reused AbstractState (184.5 us vs
# 0.14 us, measured). A stiff transient evaluates properties millions of
# times, and nothing about the slow spelling looks wrong in review.
- name: Tests
run: python -m pytest lib/feedtwin/tests -q

backend:
name: Backend (imports + tests)
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: feed-twin/requirements.txt

- name: Install dependencies
working-directory: feed-twin
# The physics core is installed from a path, not from requirements.txt
# -- the path that reaches it differs between a checkout and a Docker
# build context. Same arrangement as lib/stardesign in the other apps.
run: |
pip install -r requirements.txt
pip install -e ../lib/feedtwin

- name: Verify imports
working-directory: feed-twin
run: python3 -c "import backend.main; print('backend imports OK')"

- name: Install test dependencies
working-directory: feed-twin
# httpx: starlette's TestClient is built on it, and the design-tool
# import tests drive it through an httpx.MockTransport.
# anyio: those tests are async, and it carries the pytest plugin that
# runs them. It arrives transitively with httpx today -- named here
# anyway, because a transitive dependency that disappears would turn an
# async test gate into a silent no-op rather than a failure.
run: pip install pytest httpx anyio black mypy

# The app gets the same lint and type gates as the library. It is a small
# shell today, which is exactly why holding the line is cheap -- the time
# to discover the backend was never type-checked is not the first time it
# grows a solve endpoint.
- name: Format (black --check)
working-directory: feed-twin
run: black --check backend tests

- name: Types (mypy --strict)
working-directory: feed-twin
# --ignore-missing-imports, unlike the library job: the app's own
# per-module overrides would be config for one dependency, and FastAPI
# ships its own types anyway.
run: mypy backend --strict --ignore-missing-imports

# Proves the library imports in the *app's* environment too, which is
# built from a different requirements file than the library job's. Phase
# 00 claims both environments; both are checked.
- name: Run tests
working-directory: feed-twin
run: python -m pytest tests -q

# Phase 00's other exit criterion: the physics core has to be installable
# alongside EngineDesign, because Layer X will import it in that process
# (ADR-0001). Nothing makes EngineDesign depend on feedtwin yet -- that
# happens in Phase 04, when feed_loss.py starts delegating -- so this proves
# the two dependency sets can coexist *before* anything is riding on it.
#
# The realistic failure is a numpy ceiling: EngineDesign pins numba, numba
# constrains numpy's upper bound, and feedtwin wants numpy >= 1.26. A resolver
# conflict discovered in Phase 04 would be a bad surprise; discovered here it
# is a version bump.
engine-design-compat:
name: Coexists with EngineDesign
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: '3.11'
cache: 'pip'
cache-dependency-path: |
EngineDesign/requirements-base.txt
lib/feedtwin/pyproject.toml

# requirements-base, not requirements.txt: the latter adds rocketcea,
# which builds NASA CEA from Fortran source and is only needed to
# regenerate the CEA cache. Nothing about dependency resolution needs it.
- name: Install EngineDesign, then the physics core on top
run: |
pip install -r EngineDesign/requirements-base.txt
pip install -e lib/feedtwin

# pip resolves happily and then warns about incompatibilities rather than
# failing, so check explicitly instead of trusting the exit code above.
- name: No broken dependencies
run: pip check

# working-directory: EngineDesign because `engine` is a source package in
# that tree, not an installed distribution -- it is only importable with
# EngineDesign/ on sys.path, which is how its own CI runs too.
- name: Both import in one process
working-directory: EngineDesign
run: |
python3 -c "
import feedtwin, engine, numpy, scipy
print('feedtwin', feedtwin.__version__)
print('numpy ', numpy.__version__)
print('scipy ', scipy.__version__)
"

frontend:
name: Frontend (TypeScript build)
runs-on: ubuntu-latest
timeout-minutes: 10

steps:
- uses: actions/checkout@v4

- uses: actions/setup-node@v4
with:
node-version: '20.x'
cache: 'npm'
cache-dependency-path: feed-twin/frontend/package-lock.json

- name: Install dependencies
working-directory: feed-twin/frontend
run: npm ci || npm install

# `npm run build` is `tsc -b && vite build`, so this is the type gate as
# well as the build gate.
- name: TypeScript build
working-directory: feed-twin/frontend
run: npm run build

# Only the pure logic is tested, and deliberately: `src/lib/series.ts` is
# the one part of this frontend that can be *wrong* rather than merely
# ugly. Rendering assertions on a layout that is still moving would cost
# more to maintain than they catch.
- name: Unit tests
working-directory: feed-twin/frontend
run: npm test

# Docker image build. Proves the images build against the current tree, on a
# PR rather than after the merge -- publish-apps.yml only runs on push to
# main, so without this a Dockerfile that no longer matches the tree breaks
# the deploy image instead of the PR. That is exactly how
# EngineDesign/Dockerfile.api broke main when PR #37 deleted engine/native.
#
# push: false, so this publishes nothing and needs no GHCR login.
docker-image:
name: Docker image (${{ matrix.name }})
runs-on: ubuntu-latest
timeout-minutes: 30

strategy:
fail-fast: false
matrix:
include:
- { name: feed-twin-api, context: ., dockerfile: ./feed-twin/Dockerfile.api }
- { name: feed-twin-frontend, context: ., dockerfile: ./feed-twin/frontend/Dockerfile }

steps:
- uses: actions/checkout@v4

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

- name: Build (no push)
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: false
cache-from: type=gha,scope=${{ matrix.name }}
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,10 @@ daq-server/BACKEND_REVIEW.md
# macOS
.DS_Store
**/.DS_Store

# Build trees, per-app user data, engine run output: never source.
build/
**/build/
.userdata/
**/.userdata/
EngineDesign/engine/output/
Loading
Loading