flow5ctl 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. flow5ctl-0.1.0/.editorconfig +21 -0
  2. flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
  3. flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/feature_request.yml +28 -0
  5. flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/physics_issue.yml +47 -0
  6. flow5ctl-0.1.0/.github/ISSUE_TEMPLATE/platform_report.yml +54 -0
  7. flow5ctl-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +33 -0
  8. flow5ctl-0.1.0/.github/workflows/ci.yml +48 -0
  9. flow5ctl-0.1.0/.github/workflows/release.yaml +96 -0
  10. flow5ctl-0.1.0/.gitignore +60 -0
  11. flow5ctl-0.1.0/AGENTS.md +191 -0
  12. flow5ctl-0.1.0/CHANGELOG.md +348 -0
  13. flow5ctl-0.1.0/CLAUDE.md +79 -0
  14. flow5ctl-0.1.0/CODE_OF_CONDUCT.md +43 -0
  15. flow5ctl-0.1.0/CONTRIBUTING.md +147 -0
  16. flow5ctl-0.1.0/LICENSE +202 -0
  17. flow5ctl-0.1.0/PKG-INFO +348 -0
  18. flow5ctl-0.1.0/README.ja.md +268 -0
  19. flow5ctl-0.1.0/README.md +326 -0
  20. flow5ctl-0.1.0/SECURITY.md +44 -0
  21. flow5ctl-0.1.0/docs/ARCHITECTURE-ja.md +188 -0
  22. flow5ctl-0.1.0/docs/ARCHITECTURE.md +197 -0
  23. flow5ctl-0.1.0/docs/DESIGN-GUIDE.md +461 -0
  24. flow5ctl-0.1.0/docs/DOMAIN-MODEL.md +216 -0
  25. flow5ctl-0.1.0/docs/FLOW5-INTERFACE.md +918 -0
  26. flow5ctl-0.1.0/docs/MCP-TOOLS.md +363 -0
  27. flow5ctl-0.1.0/docs/MCP.md +140 -0
  28. flow5ctl-0.1.0/docs/RELEASING.md +134 -0
  29. flow5ctl-0.1.0/docs/ROADMAP.md +222 -0
  30. flow5ctl-0.1.0/docs/adr/0001-drive-flow5-via-the-xml-script-interface.md +54 -0
  31. flow5ctl-0.1.0/docs/adr/0002-one-core-two-frontends.md +46 -0
  32. flow5ctl-0.1.0/docs/adr/0003-file-based-project-state.md +51 -0
  33. flow5ctl-0.1.0/docs/adr/0004-summarise-results-not-raw-data.md +40 -0
  34. flow5ctl-0.1.0/docs/adr/0005-compute-reference-dimensions-ourselves.md +48 -0
  35. flow5ctl-0.1.0/docs/adr/0006-licensing-and-the-gpl-boundary.md +46 -0
  36. flow5ctl-0.1.0/docs/adr/0007-flow5-version-compatibility.md +42 -0
  37. flow5ctl-0.1.0/docs/adr/0008-python-and-distribution.md +50 -0
  38. flow5ctl-0.1.0/docs/adr/0009-two-pass-solver-invocation.md +51 -0
  39. flow5ctl-0.1.0/docs/adr/0010-treat-solver-output-as-hostile.md +56 -0
  40. flow5ctl-0.1.0/docs/architecture.drawio +96 -0
  41. flow5ctl-0.1.0/docs/architecture.png +0 -0
  42. flow5ctl-0.1.0/docs/architecture.svg +3 -0
  43. flow5ctl-0.1.0/docs/ja/DESIGN-GUIDE.md +461 -0
  44. flow5ctl-0.1.0/docs/ja/QUICKSTART.md +174 -0
  45. flow5ctl-0.1.0/docs/log/2026-09-03-feasibility-spike.md +159 -0
  46. flow5ctl-0.1.0/docs/log/2026-09-03-poc-verification.md +209 -0
  47. flow5ctl-0.1.0/docs/log/2026-09-04-induced-drag-against-avl.md +200 -0
  48. flow5ctl-0.1.0/docs/log/2026-09-04-induced-drag-and-the-mesh.md +123 -0
  49. flow5ctl-0.1.0/examples/cg-sweep.yaml +27 -0
  50. flow5ctl-0.1.0/examples/hpa.yaml +61 -0
  51. flow5ctl-0.1.0/examples/rc-glider.yaml +56 -0
  52. flow5ctl-0.1.0/poc/README.md +80 -0
  53. flow5ctl-0.1.0/poc/case_a_geometry.py +29 -0
  54. flow5ctl-0.1.0/poc/case_b_foil.py +51 -0
  55. flow5ctl-0.1.0/poc/case_c_bisect.py +63 -0
  56. flow5ctl-0.1.0/poc/case_c_viscous.py +54 -0
  57. flow5ctl-0.1.0/poc/case_d_twopass.py +65 -0
  58. flow5ctl-0.1.0/poc/case_e_polartypes.py +60 -0
  59. flow5ctl-0.1.0/poc/case_f_glider2pass.py +97 -0
  60. flow5ctl-0.1.0/poc/case_g_wide.py +64 -0
  61. flow5ctl-0.1.0/poc/case_h_hpa.py +95 -0
  62. flow5ctl-0.1.0/poc/case_i_inertia_body_multi.py +114 -0
  63. flow5ctl-0.1.0/poc/case_j_project.py +49 -0
  64. flow5ctl-0.1.0/poc/lib/f5.py +87 -0
  65. flow5ctl-0.1.0/poc/lib/gen.py +155 -0
  66. flow5ctl-0.1.0/poc/lib/parse.py +128 -0
  67. flow5ctl-0.1.0/poc/verify_platform.py +191 -0
  68. flow5ctl-0.1.0/pyproject.toml +74 -0
  69. flow5ctl-0.1.0/src/flow5ctl/__init__.py +35 -0
  70. flow5ctl-0.1.0/src/flow5ctl/__main__.py +4 -0
  71. flow5ctl-0.1.0/src/flow5ctl/advisor/__init__.py +0 -0
  72. flow5ctl-0.1.0/src/flow5ctl/advisor/dragbudget.py +155 -0
  73. flow5ctl-0.1.0/src/flow5ctl/advisor/guardrails.py +451 -0
  74. flow5ctl-0.1.0/src/flow5ctl/advisor/stability.py +81 -0
  75. flow5ctl-0.1.0/src/flow5ctl/advisor/structure.py +159 -0
  76. flow5ctl-0.1.0/src/flow5ctl/cli.py +771 -0
  77. flow5ctl-0.1.0/src/flow5ctl/errors.py +55 -0
  78. flow5ctl-0.1.0/src/flow5ctl/flow5/__init__.py +0 -0
  79. flow5ctl-0.1.0/src/flow5ctl/flow5/airfoils.py +175 -0
  80. flow5ctl-0.1.0/src/flow5ctl/flow5/foilpolar.py +121 -0
  81. flow5ctl-0.1.0/src/flow5ctl/flow5/markers.py +293 -0
  82. flow5ctl-0.1.0/src/flow5ctl/flow5/probe.py +141 -0
  83. flow5ctl-0.1.0/src/flow5ctl/flow5/results.py +309 -0
  84. flow5ctl-0.1.0/src/flow5ctl/flow5/runner.py +149 -0
  85. flow5ctl-0.1.0/src/flow5ctl/flow5/summary.py +427 -0
  86. flow5ctl-0.1.0/src/flow5ctl/flow5/xmlgen.py +422 -0
  87. flow5ctl-0.1.0/src/flow5ctl/geometry/__init__.py +0 -0
  88. flow5ctl-0.1.0/src/flow5ctl/geometry/derived.py +279 -0
  89. flow5ctl-0.1.0/src/flow5ctl/geometry/massprops.py +87 -0
  90. flow5ctl-0.1.0/src/flow5ctl/geometry/planform.py +254 -0
  91. flow5ctl-0.1.0/src/flow5ctl/mcp_server.py +615 -0
  92. flow5ctl-0.1.0/src/flow5ctl/model/__init__.py +0 -0
  93. flow5ctl-0.1.0/src/flow5ctl/model/design.py +280 -0
  94. flow5ctl-0.1.0/src/flow5ctl/model/presets.py +74 -0
  95. flow5ctl-0.1.0/src/flow5ctl/presets/custom.yaml +34 -0
  96. flow5ctl-0.1.0/src/flow5ctl/presets/hpa.yaml +53 -0
  97. flow5ctl-0.1.0/src/flow5ctl/presets/rc-glider.yaml +42 -0
  98. flow5ctl-0.1.0/src/flow5ctl/presets/uav.yaml +40 -0
  99. flow5ctl-0.1.0/src/flow5ctl/project/__init__.py +0 -0
  100. flow5ctl-0.1.0/src/flow5ctl/project/store.py +262 -0
  101. flow5ctl-0.1.0/src/flow5ctl/units.py +53 -0
  102. flow5ctl-0.1.0/src/flow5ctl/usecases/__init__.py +0 -0
  103. flow5ctl-0.1.0/src/flow5ctl/usecases/analyze.py +634 -0
  104. flow5ctl-0.1.0/src/flow5ctl/usecases/define.py +124 -0
  105. flow5ctl-0.1.0/src/flow5ctl/usecases/edit.py +320 -0
  106. flow5ctl-0.1.0/src/flow5ctl/usecases/ground.py +129 -0
  107. flow5ctl-0.1.0/src/flow5ctl/usecases/gui.py +50 -0
  108. flow5ctl-0.1.0/src/flow5ctl/usecases/plot.py +95 -0
  109. flow5ctl-0.1.0/src/flow5ctl/usecases/sweep.py +385 -0
  110. flow5ctl-0.1.0/src/flow5ctl/usecases/trim.py +371 -0
  111. flow5ctl-0.1.0/src/flow5ctl/viz/__init__.py +0 -0
  112. flow5ctl-0.1.0/src/flow5ctl/viz/charts.py +374 -0
  113. flow5ctl-0.1.0/src/flow5ctl/viz/palette.py +71 -0
  114. flow5ctl-0.1.0/tests/conftest.py +29 -0
  115. flow5ctl-0.1.0/tests/fixtures/README.md +27 -0
  116. flow5ctl-0.1.0/tests/fixtures/foilpolar_csv.csv +47 -0
  117. flow5ctl-0.1.0/tests/fixtures/foilpolar_xfoil.txt +48 -0
  118. flow5ctl-0.1.0/tests/fixtures/oppoint_strips.csv +67 -0
  119. flow5ctl-0.1.0/tests/fixtures/polar_t1_rectwing.csv +36 -0
  120. flow5ctl-0.1.0/tests/fixtures/polar_t2_single_point.csv +28 -0
  121. flow5ctl-0.1.0/tests/fixtures/polar_t5_anhedral.csv +35 -0
  122. flow5ctl-0.1.0/tests/fixtures/polar_t5_beta.csv +37 -0
  123. flow5ctl-0.1.0/tests/fixtures/polar_t5_finaft.csv +35 -0
  124. flow5ctl-0.1.0/tests/fixtures/polar_t5_finfwd.csv +35 -0
  125. flow5ctl-0.1.0/tests/fixtures/polar_t7_inf.csv +37 -0
  126. flow5ctl-0.1.0/tests/test_charts.py +192 -0
  127. flow5ctl-0.1.0/tests/test_edit.py +368 -0
  128. flow5ctl-0.1.0/tests/test_end_to_end.py +264 -0
  129. flow5ctl-0.1.0/tests/test_geometry.py +331 -0
  130. flow5ctl-0.1.0/tests/test_guardrails.py +771 -0
  131. flow5ctl-0.1.0/tests/test_mcp_server.py +206 -0
  132. flow5ctl-0.1.0/tests/test_mcp_stdio.py +154 -0
  133. flow5ctl-0.1.0/tests/test_platform.py +42 -0
  134. flow5ctl-0.1.0/tests/test_project.py +177 -0
  135. flow5ctl-0.1.0/tests/test_results_parser.py +225 -0
  136. flow5ctl-0.1.0/tests/test_summary.py +245 -0
  137. flow5ctl-0.1.0/tests/test_trim_sweep.py +479 -0
  138. flow5ctl-0.1.0/tests/test_units.py +38 -0
  139. flow5ctl-0.1.0/tests/test_xmlgen.py +335 -0
  140. flow5ctl-0.1.0/tools/check_docs.py +132 -0
  141. flow5ctl-0.1.0/tools/gen_architecture.py +144 -0
  142. flow5ctl-0.1.0/uv.lock +1531 -0
@@ -0,0 +1,21 @@
1
+ root = true
2
+
3
+ [*]
4
+ charset = utf-8
5
+ end_of_line = lf
6
+ insert_final_newline = true
7
+ trim_trailing_whitespace = true
8
+ indent_style = space
9
+ indent_size = 4
10
+
11
+ [*.{yml,yaml,json,toml}]
12
+ indent_size = 2
13
+
14
+ [*.md]
15
+ # trailing whitespace is significant for markdown line breaks
16
+ trim_trailing_whitespace = false
17
+
18
+ [*.{dat,csv,stl}]
19
+ # solver data files — leave exactly as produced
20
+ trim_trailing_whitespace = false
21
+ insert_final_newline = false
@@ -0,0 +1,43 @@
1
+ name: Bug report
2
+ description: flow5ctl crashed, failed, or did something unexpected
3
+ labels: ["bug"]
4
+ body:
5
+ - type: textarea
6
+ id: what-happened
7
+ attributes:
8
+ label: What happened?
9
+ validations:
10
+ required: true
11
+ - type: textarea
12
+ id: expected
13
+ attributes:
14
+ label: What did you expect?
15
+ validations:
16
+ required: true
17
+ - type: textarea
18
+ id: repro
19
+ attributes:
20
+ label: Reproduction
21
+ description: Commands or MCP calls, plus the smallest `design.yaml` that shows it.
22
+ validations:
23
+ required: true
24
+ - type: input
25
+ id: flow5-version
26
+ attributes:
27
+ label: flow5 version (`flow5 --version`)
28
+ placeholder: flow5 v7.57
29
+ validations:
30
+ required: true
31
+ - type: input
32
+ id: os
33
+ attributes:
34
+ label: Operating system
35
+ validations:
36
+ required: true
37
+ - type: dropdown
38
+ id: frontend
39
+ attributes:
40
+ label: How were you using it?
41
+ options: ["MCP (Claude Desktop or other)", "CLI", "Both", "Neither / library"]
42
+ validations:
43
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: flow5 itself (the solver)
4
+ url: https://github.com/techwinder/flow5/issues
5
+ about: Bugs in flow5 belong upstream. Please also tell us, so we can record it in the compatibility matrix.
6
+ - name: Discuss the design
7
+ url: https://github.com/97kuek/flow5ctl/blob/main/docs/ARCHITECTURE.md
8
+ about: The project is in its design phase. If a decision looks wrong, open a normal issue and say why.
@@ -0,0 +1,28 @@
1
+ name: Feature request
2
+ description: Something flow5ctl should be able to do
3
+ labels: ["enhancement"]
4
+ body:
5
+ - type: textarea
6
+ id: workflow
7
+ attributes:
8
+ label: What are you trying to do?
9
+ description: |
10
+ Describe the design task, not the feature. What is the loop you repeat by
11
+ hand today? That is what this project is trying to remove.
12
+ validations:
13
+ required: true
14
+ - type: textarea
15
+ id: proposal
16
+ attributes:
17
+ label: What would you like flow5ctl to do?
18
+ - type: dropdown
19
+ id: aircraft
20
+ attributes:
21
+ label: What do you design?
22
+ options:
23
+ - Human-powered aircraft (Birdman Rally / HPA)
24
+ - RC glider (F3B / F3F / F5J / DLG)
25
+ - Small UAV
26
+ - Other / several
27
+ validations:
28
+ required: true
@@ -0,0 +1,47 @@
1
+ name: Physics / wrong result
2
+ description: A number flow5ctl produced is aerodynamically wrong or misleading
3
+ labels: ["physics"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ **These are our most serious bugs.** People build aircraft from this, and some
9
+ of those aircraft carry a pilot. A plausible wrong number is worse than a crash.
10
+ - type: textarea
11
+ id: what
12
+ attributes:
13
+ label: What was reported, and what should it be?
14
+ placeholder: |
15
+ flow5ctl reported a static margin of 29.8 %.
16
+ Hand calculation from the neutral point gives 8.2 %.
17
+ validations:
18
+ required: true
19
+ - type: textarea
20
+ id: how-you-know
21
+ attributes:
22
+ label: How do you know the correct value?
23
+ description: Hand calculation, a second solver, wind-tunnel or flight data, published results.
24
+ validations:
25
+ required: true
26
+ - type: textarea
27
+ id: setup
28
+ attributes:
29
+ label: Design and analysis setup
30
+ description: Your `design.yaml` (or the smallest version that reproduces it) and the analysis conditions.
31
+ render: yaml
32
+ validations:
33
+ required: true
34
+ - type: input
35
+ id: flow5-version
36
+ attributes:
37
+ label: flow5 version
38
+ description: Output of `flow5 --version` — not the version shown by the app bundle.
39
+ placeholder: flow5 v7.57
40
+ validations:
41
+ required: true
42
+ - type: input
43
+ id: os
44
+ attributes:
45
+ label: Operating system
46
+ validations:
47
+ required: true
@@ -0,0 +1,54 @@
1
+ name: Platform report
2
+ description: Tell us whether flow5ctl works on your OS and flow5 version
3
+ labels: ["platform"]
4
+ body:
5
+ - type: markdown
6
+ attributes:
7
+ value: |
8
+ Everything in [docs/FLOW5-INTERFACE.md](../blob/main/docs/FLOW5-INTERFACE.md)
9
+ was verified on macOS with flow5 7.57. Linux and Windows are untested, and
10
+ reports are how we find out what we cannot check ourselves.
11
+
12
+ The quickest useful report is one command:
13
+
14
+ ```
15
+ python3 poc/verify_platform.py
16
+ ```
17
+
18
+ It runs flow5 a few times, checks each documented behaviour, and prints a
19
+ report to paste below. It needs only the standard library and an installed
20
+ flow5, and writes nothing outside a temporary directory.
21
+ - type: input
22
+ id: os
23
+ attributes:
24
+ label: Operating system and version
25
+ validations:
26
+ required: true
27
+ - type: input
28
+ id: flow5-version
29
+ attributes:
30
+ label: flow5 version (`flow5 --version`)
31
+ validations:
32
+ required: true
33
+ - type: input
34
+ id: install
35
+ attributes:
36
+ label: How did you install flow5?
37
+ placeholder: Homebrew cask / .deb / installer / built from source
38
+ - type: textarea
39
+ id: results
40
+ attributes:
41
+ label: Output of `python3 poc/verify_platform.py`
42
+ description: Paste it whole, including the header lines.
43
+ render: text
44
+ validations:
45
+ required: true
46
+ - type: textarea
47
+ id: differences
48
+ attributes:
49
+ label: Anything else worth knowing?
50
+ description: |
51
+ Where flow5 installed itself, whether it needed a display, and anything the
52
+ script did not cover. A `[ info ]` line saying this platform does NOT crash on
53
+ a combined script is especially interesting — it would mean flow5ctl's two-pass
54
+ design is unnecessary there.
@@ -0,0 +1,33 @@
1
+ ## What and why
2
+
3
+ <!-- What does this change, and what problem does it solve? Link the issue if there is one. -->
4
+
5
+ ## Evidence
6
+
7
+ <!-- Required for any claim about flow5's behaviour or about a physical result. -->
8
+
9
+ - [ ] I verified this by **running** it (say which flow5 version and OS)
10
+ - [ ] I verified this by **reading upstream source** (cite the file)
11
+ - [ ] Not applicable — this change makes no claim about flow5 or about physics
12
+
13
+ flow5 version used: <!-- `flow5 --version` output -->
14
+ OS: <!-- -->
15
+
16
+ ## Checklist
17
+
18
+ - [ ] No flow5 source code is copied into this repository
19
+ ([ADR-0006](../docs/adr/0006-licensing-and-the-gpl-boundary.md))
20
+ - [ ] No solver output or generated artifacts are committed (`poc/work/`, `build/`)
21
+ - [ ] Any new fact in `docs/FLOW5-INTERFACE.md` is marked **[run]** or **[src]** and cited
22
+ - [ ] If this changes an accepted decision, a new ADR supersedes the old one
23
+ - [ ] If this touches geometry or the result parser, golden tests were updated
24
+ deliberately — and the PR says why the old values were wrong
25
+ - [ ] Both front-ends are in step (MCP and CLI), with `--json` matching the MCP payload
26
+
27
+ ## Physics
28
+
29
+ <!-- Delete if this change cannot affect a number a user might act on. -->
30
+
31
+ - [ ] I have stated the limits that apply to any result this produces
32
+ - [ ] This does not let a stability figure come from a non-T7 polar
33
+ - [ ] This does not present a non-finite value as a result
@@ -0,0 +1,48 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ checks:
13
+ name: Repository checks
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v5
17
+ - uses: actions/setup-python@v6
18
+ with:
19
+ python-version: "3.12"
20
+
21
+ # Runs without flow5 installed.
22
+ - name: Docs, licence and artifact hygiene
23
+ run: python3 tools/check_docs.py
24
+
25
+ test:
26
+ name: Tests (Python ${{ matrix.python }})
27
+ runs-on: ubuntu-latest
28
+ strategy:
29
+ fail-fast: false
30
+ matrix:
31
+ python: ["3.11", "3.12", "3.13"]
32
+ steps:
33
+ - uses: actions/checkout@v5
34
+ - uses: astral-sh/setup-uv@v6
35
+ with:
36
+ enable-cache: true
37
+ - name: Install
38
+ run: uv sync --group dev --extra plot
39
+
40
+ - name: Lint
41
+ run: uv run ruff check src tests tools poc
42
+
43
+ # flow5 is not available on CI runners, so the solver-dependent tests are
44
+ # deselected. They run against a real flow5 on a developer machine — see
45
+ # poc/README.md. The rest of the suite must pass without it, which is what
46
+ # keeps the domain layer independent of the adapter (ADR-0002).
47
+ - name: Tests
48
+ run: uv run pytest -q -m "not needs_flow5"
@@ -0,0 +1,96 @@
1
+ name: Release
2
+
3
+ # Publishing is deliberately tag-triggered rather than automatic on main: a release
4
+ # is a decision, not a side effect of merging. See docs/RELEASING.md.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+ # Validate everything a release runs, without cutting a tag. The publish and
9
+ # release jobs below are guarded to tags, so a dispatch builds and checks only.
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ name: Build and check
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - uses: actions/checkout@v5
21
+ - uses: astral-sh/setup-uv@v6
22
+ with:
23
+ enable-cache: true
24
+
25
+ # The tag and the packaged version must agree. Getting this wrong publishes
26
+ # something no tag points at, and PyPI will not let the filename be reused.
27
+ - name: Tag matches the packaged version
28
+ if: startsWith(github.ref, 'refs/tags/')
29
+ run: |
30
+ tag="${GITHUB_REF_NAME#v}"
31
+ version="$(uv run --no-project --with hatchling python -c \
32
+ 'import tomllib,pathlib;print(tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"])')"
33
+ echo "tag=$tag pyproject=$version"
34
+ [ "$tag" = "$version" ] || { echo "::error::tag $tag does not match version $version"; exit 1; }
35
+
36
+ - name: Install
37
+ run: uv sync --group dev --extra plot
38
+
39
+ - name: Lint
40
+ run: uv run ruff check src tests tools poc
41
+
42
+ # flow5 is not on CI runners, so the solver tests are deselected here exactly
43
+ # as in ci.yml. A release must not be cut on a red tree.
44
+ - name: Tests
45
+ run: uv run pytest -q -m "not needs_flow5"
46
+
47
+ - name: Repository checks
48
+ run: python3 tools/check_docs.py
49
+
50
+ - name: Build
51
+ run: uv build
52
+
53
+ # Catches a malformed README before PyPI rejects the upload.
54
+ - name: Check metadata
55
+ run: uvx twine check dist/*
56
+
57
+ - uses: actions/upload-artifact@v5
58
+ with:
59
+ name: dist
60
+ path: dist/
61
+
62
+ publish:
63
+ name: Publish to PyPI
64
+ needs: build
65
+ if: startsWith(github.ref, 'refs/tags/')
66
+ runs-on: ubuntu-latest
67
+ environment: pypi
68
+ permissions:
69
+ id-token: write # PyPI trusted publishing; no API token is stored anywhere
70
+ steps:
71
+ - uses: actions/download-artifact@v5
72
+ with:
73
+ name: dist
74
+ path: dist/
75
+ - uses: pypa/gh-action-pypi-publish@release/v1
76
+
77
+ github_release:
78
+ name: GitHub release
79
+ needs: publish
80
+ if: startsWith(github.ref, 'refs/tags/')
81
+ runs-on: ubuntu-latest
82
+ permissions:
83
+ contents: write
84
+ steps:
85
+ - uses: actions/checkout@v5
86
+ - uses: actions/download-artifact@v5
87
+ with:
88
+ name: dist
89
+ path: dist/
90
+ - name: Create the release
91
+ env:
92
+ GH_TOKEN: ${{ github.token }}
93
+ run: |
94
+ gh release create "$GITHUB_REF_NAME" dist/* \
95
+ --title "$GITHUB_REF_NAME" \
96
+ --notes "See [CHANGELOG.md](https://github.com/${GITHUB_REPOSITORY}/blob/main/CHANGELOG.md)."
@@ -0,0 +1,60 @@
1
+ # ── flow5ctl generated artifacts ────────────────────────────────────
2
+ # design.yaml is the source of truth; everything below is regenerable.
3
+ build/
4
+ results/
5
+ .flow5ctl/
6
+ *.fl5
7
+ *.stl
8
+
9
+ # ── PoC verification workspace ──────────────────────────────────────
10
+ # poc/lib and poc/case_*.py are the reproducible harness and ARE tracked.
11
+ # Everything they produce is not.
12
+ poc/work/
13
+ poc/out/
14
+ poc/*_run.log
15
+
16
+ # ── Validation against real aircraft ────────────────────────────────
17
+ # Reconstructions of other teams' aircraft, and the logs comparing flow5ctl
18
+ # against their published data, stay out of the public repository. They are
19
+ # someone else's design work; publishing them is not ours to decide.
20
+ # Findings about flow5's own behaviour DO belong in docs/ — anonymised.
21
+ validation/
22
+ examples/*-real-*.yaml
23
+
24
+ # Real flow5 project files kept as references for what a practitioner's own model
25
+ # contains — mesh density, surface counts, airfoil choices. Reading one is how the
26
+ # Reynolds ladder default got fixed. They are someone's actual aircraft, so they
27
+ # stay local; what is learned from them goes into docs/ as a finding.
28
+ *.xfl
29
+ *.fl5
30
+
31
+ # Upstream flow5 source fetched for reference only.
32
+ # flow5 is GPL-3.0 and must never be committed here — see
33
+ # docs/adr/0006-licensing-and-the-gpl-boundary.md
34
+ poc/ref/
35
+
36
+ # ── Python ──────────────────────────────────────────────────────────
37
+ # uv.lock IS committed: it makes CI and a contributor's environment reproducible,
38
+ # and flow5ctl is installed as a tool more often than depended on as a library.
39
+ __pycache__/
40
+ *.py[cod]
41
+ *.egg-info/
42
+ .venv/
43
+ venv/
44
+ .env
45
+ dist/
46
+ build-py/
47
+ .pytest_cache/
48
+ .ruff_cache/
49
+ .mypy_cache/
50
+ .coverage
51
+ htmlcov/
52
+
53
+ # ── Editors / OS ────────────────────────────────────────────────────
54
+ .DS_Store
55
+ Thumbs.db
56
+ .idea/
57
+ .vscode/*
58
+ !.vscode/extensions.json
59
+ *.swp
60
+ *~
@@ -0,0 +1,191 @@
1
+ # AGENTS.md
2
+
3
+ Instructions for AI agents working **on** this repository.
4
+
5
+ > If you are an agent **using** flow5ctl to design an aircraft, this is the wrong
6
+ > file. Read [docs/DESIGN-GUIDE.md](docs/DESIGN-GUIDE.md) instead.
7
+
8
+ ## What this project is
9
+
10
+ `flow5ctl` lets AI agents design low-Reynolds-number aircraft by driving
11
+ [flow5](https://flow5.tech), a potential-flow solver, in its headless batch mode.
12
+ It ships as an MCP server and a CLI over one shared core.
13
+
14
+ The core, the CLI and the MCP server all work. See
15
+ [the roadmap](docs/ROADMAP.md) for what is left.
16
+
17
+ ## Layout
18
+
19
+ ```
20
+ src/flow5ctl/
21
+ model/ design.yaml schema, presets
22
+ geometry/ areas, span, MAC, mass properties ← no flow5 knowledge
23
+ advisor/ guardrails and thresholds ← no flow5 knowledge
24
+ viz/ chart rendering and the validated palette
25
+ flow5/ probe, xmlgen, runner, results, summary ← the ONLY flow5-aware package
26
+ usecases/ define, edit, analyze, trim, sweep, plot, gui ← the only orchestrators
27
+ presets/ *.yaml — data, so a new aircraft class needs no code
28
+ cli.py a thin adapter over the use cases
29
+ mcp_server.py the second thin adapter; no domain logic, no flow5 knowledge
30
+ poc/ the verification harness that produced the measured claims in docs/
31
+ examples/ worked designs and a study, used as documentation
32
+ tests/ golden values and real flow5 output; fixtures/ pins the parser traps
33
+ ```
34
+
35
+ The dependency rule is one way: `cli / mcp_server → usecases → geometry/advisor/model`,
36
+ with `flow5/` reachable only from `usecases/`. Nothing in `geometry/`, `advisor/` or
37
+ `model/` may import `flow5/`, so the aerodynamic model stays testable with flow5
38
+ absent — and CI relies on that.
39
+
40
+ The two front-ends must stay in step. A capability added to one and not the other is
41
+ an incomplete change ([ADR-0002](docs/adr/0002-one-core-two-frontends.md)).
42
+
43
+ ## Working on it
44
+
45
+ ```bash
46
+ uv sync --group dev --extra plot # the extra is what chart tests need
47
+ uv run pytest -q # everything, including the real flow5 runs
48
+ uv run pytest -q -m "not needs_flow5" # what CI runs
49
+ uv run ruff check src tests tools poc
50
+ python3 tools/check_docs.py
51
+ ```
52
+
53
+ The MCP server has two test layers, and they check different things:
54
+ `tests/test_mcp_server.py` runs it in process for the adapter's own contract, while
55
+ `tests/test_mcp_stdio.py` launches `flow5ctl mcp` as a subprocess and talks the
56
+ protocol — that is the only place a PNG surviving base64 transport, or a rejected
57
+ request coming back as an error result instead of killing the server, is actually
58
+ verified.
59
+
60
+ ## Read before you change anything
61
+
62
+ In this order:
63
+
64
+ 1. [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — the layering and why it is that way
65
+ 2. [docs/adr/](docs/adr/) — decisions already made, with their reasoning
66
+ 3. [docs/FLOW5-INTERFACE.md](docs/FLOW5-INTERFACE.md) — verified facts about flow5
67
+ 4. [docs/DOMAIN-MODEL.md](docs/DOMAIN-MODEL.md) — the vocabulary; use these words
68
+
69
+ ## Rules
70
+
71
+ ### Never invent facts about flow5
72
+
73
+ [docs/FLOW5-INTERFACE.md](docs/FLOW5-INTERFACE.md) marks every claim **[run]**
74
+ (verified by executing flow5) or **[src]** (read from a named upstream source file).
75
+ Anything not marked either way is not established.
76
+
77
+ If you need a fact that is not there:
78
+
79
+ - read it from the upstream source at [techwinder/flow5](https://github.com/techwinder/flow5), or
80
+ - verify it by running flow5, then
81
+ - add it to the reference with its marker and citation.
82
+
83
+ Do not guess an XML tag name. Unknown tags are silently ignored by flow5, so a
84
+ guess produces a subtly wrong analysis rather than an error.
85
+
86
+ ### Never copy flow5 source code into this repository
87
+
88
+ flow5 is GPL-3.0; flow5ctl is Apache-2.0. Read the source to learn the interface and
89
+ describe it in your own words with a citation. See
90
+ [ADR-0006](docs/adr/0006-licensing-and-the-gpl-boundary.md).
91
+
92
+ ### Physics correctness outranks everything
93
+
94
+ A crash is recoverable. A plausible wrong number that someone builds an aircraft
95
+ around is not. Specifically:
96
+
97
+ - Never let a code path produce stability results from a non-T7 polar. A T1 polar
98
+ will happily return an eigenvalue of `5.995e+51`.
99
+ - Never emit `PLANFORM` or `PROJECTED` reference dimensions
100
+ ([ADR-0005](docs/adr/0005-compute-reference-dimensions-ourselves.md)).
101
+ - Never report an L/D from an inviscid run without labelling it. Measured: an
102
+ inviscid run omitted 93 % of the drag at α=0°.
103
+ - Never mix the two viscous methods inside one comparison; they disagree by 10–25 %.
104
+ - Never report Dutch-roll or short-period figures from flow5 7.57 — they are wrong.
105
+ - Never present a non-finite cell (`inf`, `nan`) as a result.
106
+ - Never treat `Static margin` from a flow5 file as a fraction; it is a percentage.
107
+ - Never attribute an operating-point file by its directory — flow5 duplicates those
108
+ files across every polar's directory with the wrong contents.
109
+ - Never widen an α sweep past the airfoil's stall to "get more data".
110
+
111
+ If you are unsure whether something is physically sound, say so in the PR rather than
112
+ guessing.
113
+
114
+ ### Check both flow5's exit code and its stdout
115
+
116
+ `0` means nothing — flow5 exits 0 for a rejected script and for a run that failed
117
+ every operating point. Non-zero means it **crashed** (exit 139, SIGSEGV, observed
118
+ reproducibly). Success needs the stdout markers in
119
+ [docs/FLOW5-INTERFACE.md §6](docs/FLOW5-INTERFACE.md); crash detection needs the exit
120
+ code. Use both.
121
+
122
+ Scope marker matches carefully: `Made 0 valid analysis pairs (boat, polar) to run`
123
+ is printed on **every** run, so matching on `analysis pairs` alone reports failure on
124
+ every success.
125
+
126
+ ### Never emit both script sections in one file
127
+
128
+ A script containing both `<foil_analysis>` and `<Plane_analysis>` segfaults flow5.
129
+ Two invocations, always — [ADR-0009](docs/adr/0009-two-pass-solver-invocation.md).
130
+ The generator must make the single-file form impossible to express.
131
+
132
+ ### Never write a new output parser
133
+
134
+ flow5's output has at least seven traps that produce plausible wrong numbers rather
135
+ than errors ([docs/FLOW5-INTERFACE.md §5](docs/FLOW5-INTERFACE.md)). Extend
136
+ [poc/lib/parse.py](poc/lib/parse.py), keep its self-checks — especially validating
137
+ the row count against the file's own `Nbr. of data points` — and do not "simplify"
138
+ its oddities away. Every one of them is load-bearing
139
+ ([ADR-0010](docs/adr/0010-treat-solver-output-as-hostile.md)).
140
+
141
+ ### Respect the layering
142
+
143
+ `front-ends → use cases → domain → flow5 adapter`, one way only. Nothing in
144
+ `domain/` may import the adapter; the aerodynamic model must be testable with flow5
145
+ absent. Do not add flow5-specific knowledge outside the adapter, and do not add
146
+ domain logic to a front-end — see [ADR-0002](docs/adr/0002-one-core-two-frontends.md).
147
+
148
+ ### Keep the two front-ends in step
149
+
150
+ Every capability appears in both the MCP server and the CLI, and
151
+ `flow5ctl <verb> --json` emits exactly the MCP tool payload. A change to one without
152
+ the other is incomplete.
153
+
154
+ ### Changing a decision means writing an ADR
155
+
156
+ The ADRs record *why*. If you conclude one is wrong, add a new ADR that supersedes
157
+ it, with the new reasoning. Do not silently contradict an accepted decision in code.
158
+
159
+ ## Conventions
160
+
161
+ - Use the vocabulary in [docs/DOMAIN-MODEL.md](docs/DOMAIN-MODEL.md). Say *section*,
162
+ *analysis*, *operating point*, *design*. Do not introduce synonyms.
163
+ - SI internally, always. Unit conversion happens at the edges only.
164
+ - Angles are degrees in `design.yaml` and in every user-facing value, radians inside
165
+ geometry code. Name variables accordingly (`twist_deg`, `alpha_rad`).
166
+ - `design.yaml` is the source of truth; XML is a build artifact. Never hand-author
167
+ XML in a fixture unless it is deliberately testing the parser.
168
+ - Prose in documentation is English. User-facing Japanese lives in `README.ja.md`
169
+ and `docs/ja/`.
170
+
171
+ ## Testing
172
+
173
+ - Geometry has golden tests against hand-computed values. Changing geometry code
174
+ means changing them deliberately, never to make a failure go away.
175
+ - Solver-facing tests use recorded flow5 output where possible so the suite runs
176
+ without flow5 installed. Tests that need the real binary are marked and skipped
177
+ when it is absent.
178
+ - The spike design in [docs/log/2026-09-03-feasibility-spike.md](docs/log/2026-09-03-feasibility-spike.md)
179
+ is the canonical end-to-end fixture.
180
+ - [`poc/`](poc/) holds the verification harness that produced every measured claim in
181
+ the docs. If you change something the docs assert, re-run the relevant case and
182
+ update both. `poc/work/` and `poc/ref/` are gitignored — never commit solver output,
183
+ and never commit upstream flow5 source.
184
+
185
+ ## When you finish
186
+
187
+ - Say what you verified by running versus what you reasoned about.
188
+ - If you changed anything in `docs/FLOW5-INTERFACE.md`, state which flow5 version you
189
+ verified against.
190
+ - If a task turned out to be blocked, finish everything else and say plainly what you
191
+ left and why.