Skip to content

Commit 24ef9c1

Browse files
authored
Merge pull request #81 from SMI-Lab-Inha/dev
chore: 1.8.1 — governance scrubs + manifest pinning + claim tightening
2 parents 1ab2c73 + 7f0fdf8 commit 24ef9c1

11 files changed

Lines changed: 149 additions & 65 deletions

File tree

‎.github/ISSUE_TEMPLATE/code_review.yml‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@ body:
2929
attributes:
3030
label: Reviewer name / affiliation
3131
description: >-
32-
Who is filing this? Cite an organisation (e.g. "Frazer &
33-
Nash Consultancy") or a static-review tool name. The
34-
affiliation goes in the issue, not in the codebase — see
35-
``CONTRIBUTING.md`` on neutral provenance wording.
36-
placeholder: "e.g. Frazer & Nash Consultancy / GitHub Codex / SonarQube"
32+
Who is filing this? Name your organisation or the static-
33+
review tool you used. The affiliation goes in the issue,
34+
not in the codebase — see ``CONTRIBUTING.md`` on neutral
35+
provenance wording.
36+
placeholder: "engineering consultancy / static-analysis tool / individual reviewer"
3737
validations:
3838
required: true
3939

‎.github/workflows/validation.yml‎

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,23 @@
1717
# - ``workflow_dispatch`` — maintainers fire it from the Actions UI
1818
# before any tagged release (``docs/release_checklist.rst`` step 2
1919
# references this workflow).
20-
# - Weekly cron (Monday 09:00 UTC) — catches drift in upstream HEAD
21-
# (the manifest's per-repo SHA pins are still ``TBD`` as of 1.8.0;
22-
# once populated, the workflow becomes byte-reproducible).
20+
# - Weekly cron (Monday 09:00 UTC) — runs the same pinned snapshot
21+
# every Monday so a regression in pybmodes itself surfaces on a
22+
# stable baseline. To catch upstream-deck *drift* against newer
23+
# HEADs, bump the manifest SHAs through the
24+
# ``verify_external_data.py --update`` path and ship the bump as
25+
# its own commit + CHANGELOG entry under *Changed*.
2326
#
2427
# Reproducibility note
2528
# --------------------
2629
#
27-
# ``external/MANIFEST.toml`` pins each upstream-clone SHA + file
28-
# hashes, but the pins are currently the literal string ``TBD`` —
29-
# populating them is a maintainer action documented in
30-
# ``docs/release_checklist.rst`` step 2. Until then this workflow
31-
# clones the upstream HEAD of each repo; once the pins land, the
32-
# ``checkout`` step below switches to ``git checkout <sha>`` and
33-
# becomes reproducible byte-for-byte.
30+
# Each clone below checks out the SHA pinned in
31+
# ``external/MANIFEST.toml`` (1.8.1 maintainer action; bumped from
32+
# ``TBD`` in 1.8.0). The workflow is byte-reproducible from the
33+
# tagged commit — anyone reproducing the published 0.01 % tolerance
34+
# can re-run the same step set against the same upstream commits.
35+
# File-hash pins (the ``hashes`` tables in the manifest) remain
36+
# empty pending the ``verify_external_data.py --update`` rewrite.
3437

3538
name: Validation (external data)
3639

@@ -61,13 +64,16 @@ jobs:
6164
pip install -e ".[dev,plots,windio,notebook]"
6265
6366
# ---------------------------------------------------------------
64-
# Clone upstream external data into the expected layout.
67+
# Clone upstream external data into the expected layout, pinned
68+
# to the SHAs in ``external/MANIFEST.toml``.
6569
#
66-
# Each clone path matches ``relative_path`` in
67-
# ``external/MANIFEST.toml`` so the integration test suite
68-
# resolves the decks at the same paths it does locally. Shallow
69-
# clones (``--depth=1``) — the integration suite only reads the
70-
# tip of each upstream tree.
70+
# Each clone path matches ``relative_path`` in the manifest so
71+
# the integration test suite resolves the decks at the same
72+
# paths it does locally. We fetch the full git history (no
73+
# ``--depth=1``) so the explicit-SHA ``git checkout`` step
74+
# can resolve commits older than the shallow tip. Workflow run
75+
# time is dominated by the integration suite itself, not the
76+
# clones, so the extra fetch is cheap.
7177
#
7278
# BModes (CertTest 03 / 04 reference data) is NOT on GitHub; the
7379
# tests that depend on it skip cleanly when the data is absent
@@ -77,20 +83,33 @@ jobs:
7783
# code.
7884
# ---------------------------------------------------------------
7985

80-
- name: Clone OpenFAST r-test (NREL 5MW land / monopile / OC3 spar)
86+
- name: Clone OpenFAST r-test at manifest-pinned SHA
87+
# 1.8.1 manifest pin (NREL 5MW land / monopile / OC3 spar
88+
# regression decks). Full 40-char SHA matches what
89+
# ``scripts/verify_external_data.py`` compares ``git rev-parse
90+
# HEAD`` against — abbreviated SHAs in the manifest would
91+
# silently FAIL the verifier (static review on PR #81).
8192
run: |
82-
git clone --depth=1 https://github.com/OpenFAST/r-test.git \
93+
git clone https://github.com/OpenFAST/r-test.git \
8394
external/OpenFAST_files/r-test
95+
git -C external/OpenFAST_files/r-test \
96+
checkout dd5feaaaa500ba7283140107806300d551cff0a7
8497
85-
- name: Clone IEA-3.4-130-RWT
98+
- name: Clone IEA-3.4-130-RWT at manifest-pinned SHA
8699
run: |
87-
git clone --depth=1 https://github.com/IEAWindTask37/IEA-3.4-130-RWT.git \
100+
git clone https://github.com/IEAWindTask37/IEA-3.4-130-RWT.git \
88101
external/OpenFAST_files/IEA-3.4-130-RWT
102+
git -C external/OpenFAST_files/IEA-3.4-130-RWT \
103+
checkout 824f8d65196f9f84ee66df5e58e3a48a70a8f0c5
89104
90-
- name: Clone IEA-15-240-RWT
105+
- name: Clone IEA-15-240-RWT at manifest-pinned SHA
106+
# 1.8.1 manifest pin (v1.1 tower redesign — Allen 2020
107+
# free-decay reference).
91108
run: |
92-
git clone --depth=1 https://github.com/IEAWindTask37/IEA-15-240-RWT.git \
109+
git clone https://github.com/IEAWindTask37/IEA-15-240-RWT.git \
93110
external/OpenFAST_files/IEA-15-240-RWT
111+
git -C external/OpenFAST_files/IEA-15-240-RWT \
112+
checkout 86d51c8a1ee65be4f3686087a5c443c0b57e5cfb
94113
95114
# ---------------------------------------------------------------
96115
# Run the verifier + integration suite.
@@ -109,15 +128,15 @@ jobs:
109128
- name: Generate verifier report
110129
# The verifier script exits 0 on PASS + WARN and 1 on FAIL
111130
# (see ``scripts/verify_external_data.py`` lines 246-248).
112-
# WARN is the current TBD-pins state inherited from before
113-
# 1.8.0 — the workflow should NOT fail on WARN since the
114-
# maintainer-action to populate the manifest is out of scope
115-
# for the workflow itself. FAIL means a clone drifted from a
116-
# populated pinned SHA — that IS a release blocker and the
117-
# workflow should fail. So we run the verifier with its
118-
# natural exit-code semantics (no ``continue-on-error: true``)
119-
# — Codex P1 review on PR #79 caught the original
120-
# ``continue-on-error: true`` swallowing FAILs.
131+
# As of 1.8.1 every clone SHA is populated in the manifest,
132+
# so a healthy run produces PASS on every entry except
133+
# ``bmodes`` (which stays WARN because BModes is a NREL
134+
# download, not a GitHub clone — see the *Enforcement*
135+
# paragraph in VALIDATION.md). FAIL means a clone drifted
136+
# from its populated pinned SHA — release blocker. We use
137+
# the verifier's natural exit-code semantics with no
138+
# ``continue-on-error: true`` (PR #79 static-review pass
139+
# caught the original swallowing-of-FAILs).
121140
#
122141
# ``set -o pipefail`` ensures the verifier's exit code is
123142
# preserved through the ``tee`` pipe; without it ``tee``

‎CHANGELOG.md‎

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,48 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
1010

1111
(nothing yet)
1212

13+
## [1.8.1] — 2026-05-21
14+
15+
External-review follow-up patch on top of 1.8.0. No public-API
16+
change; no numerical change; no behaviour change. Three governance
17+
+ reproducibility items addressed:
18+
19+
### Changed
20+
21+
- **Scrubbed AI-assistant / tool-vendor attribution from tracked
22+
content** (eleven sites across ``CHANGELOG.md``,
23+
``.github/workflows/validation.yml``,
24+
``src/pybmodes/workflows/batch.py``,
25+
``tests/test_io_errors.py``, ``tests/test_workflows.py``, and
26+
``.github/ISSUE_TEMPLATE/code_review.yml``). The
27+
``CONTRIBUTING.md`` no-tool-attribution rule existed but the
28+
Phase 4 round had quietly re-introduced named references through
29+
the per-finding follow-up commits — a governance self-
30+
contradiction caught by the post-1.8.0 external review. All
31+
references now use neutral provenance wording ("static review",
32+
"static-review pass") plus the PR number for audit trail.
33+
- **Populated the external-data manifest's per-clone SHA pins.**
34+
``external/MANIFEST.toml`` carried ``sha = "TBD"`` for all eight
35+
upstream clones in 1.8.0 — ``validation.yml`` therefore cloned
36+
upstream HEAD with ``--depth=1``, making the workflow
37+
reproducible-as-process but not byte-reproducible. Every clone
38+
now has a real commit SHA (read from the maintainer's local
39+
``external/`` clones at the 1.8.1 release-prep snapshot), and
40+
``validation.yml`` checks out each pinned SHA explicitly after
41+
the clone. The workflow is now byte-reproducible from the tagged
42+
commit. File-hash pins (the manifest's ``hashes`` tables) remain
43+
empty pending the ``verify_external_data.py --update`` rewrite —
44+
that's a follow-up maintainer action separate from this patch.
45+
- **Tightened the validation-workflow coverage claim** in
46+
``README.md`` and ``VALIDATION.md``. The 1.8.0 text said the
47+
workflow validates "the IEA-Task-37 reference turbines" plural,
48+
but it only actually clones IEA-3.4-130-RWT and IEA-15-240-RWT;
49+
the broader claim implied IEA-10 / IEA-22 / WISDEM / MoorPy /
50+
RAFT were also exercised. Both files now name the three
51+
upstreams the workflow actually clones (OpenFAST r-test +
52+
IEA-3.4 + IEA-15) and call out the remaining manifest clones as
53+
reproducibility-pinned but not CI-exercised.
54+
1355
## [1.8.0] — 2026-05-21
1456

1557
Multi-phase architecture refactor + project-infrastructure round +
@@ -128,8 +170,8 @@ change. Four threads landed:
128170
``OutParseError``, ``SubDynParseError``, ``WindIOParseError``)
129171
let library callers catch parse errors precisely. Twenty-nine
130172
regression tests pin hashability and ``__eq__`` semantics
131-
(Codex caught the original ``@dataclass`` shadowing of
132-
``__hash__`` post-merge).
173+
(static review caught the original ``@dataclass`` shadowing of
174+
``__hash__`` post-merge — see PR #68 follow-up).
133175
- **Centralised numerical options dataclasses** (Phase 1 PR A1):
134176
:class:`pybmodes.options.SolverOptions`,
135177
:class:`pybmodes.options.FitOptions`,
@@ -232,8 +274,8 @@ Phase 4 — external-review hardening
232274
``run_patch`` keeps ``backup=False`` default), ``output_dir``
233275
(per-deck destination namespaces on the deck's *relative path
234276
under root* to avoid stem collisions between sibling sub-trees,
235-
fix from Codex P1 review). Addresses external-review finding
236-
P1-3.
277+
fix from PR #77 static-review pass). Addresses external-review
278+
finding P1-3.
237279
- **``run_windio`` ``on_skip`` policy** (Phase 4 PR D2, merged in
238280
#78). Three policies: ``"warn"`` (legacy permissive),
239281
``"fail-on-data"`` (**new default** — computational skips toggle
@@ -279,7 +321,7 @@ Phase 4 — external-review hardening
279321
``axhline``s) keep the original full-axis comb. Backward-compatible
280322
on ``rpm.min() == 0``. Regression test ``test_plot_campbell_blade_
281323
label_anchored_on_curve_for_positive_rpm`` pins the new behaviour.
282-
- **``pybmodes.io.ParseError`` hashability** (PR #68 Codex P2
324+
- **``pybmodes.io.ParseError`` hashability** (PR #68 static-review
283325
follow-up). The original ``@dataclass`` decorator on the
284326
``ParseError`` base shadowed ``__hash__`` to ``None``, breaking
285327
``set(exceptions)`` callers. Switched to ``@dataclass(eq=False)``

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
`pybmodes` is a pure-Python finite-element library for wind-turbine blade and tower modal analysis. It reads OpenFAST (ElastoDyn / SubDyn / HydroDyn / MoorDyn), BModes `.bmi`, and WISDEM / WindIO ontology YAML inputs; solves the coupled flap–lag–torsion–axial vibration modes with a 15-DOF Bernoulli-Euler beam element; and emits ElastoDyn-compatible mode-shape polynomials, MAC-tracked Campbell diagrams, and bundled Markdown / HTML / CSV reports.
99

10-
Validated against the BModes Fortran reference solver on six benchmark cases (NREL 5MW land + OC3 monopile + OC3 Hywind floating spar, IEA-3.4-130-RWT, BModes CertTest 03 / 04) to **better than 0.01 %** on every comparison. As of 1.8.0 the dedicated [Validation workflow](https://github.com/SMI-Lab-Inha/pyBModes/actions/workflows/validation.yml) (weekly cron + `workflow_dispatch`) enforces the strict tolerance in CI for every case whose external data is **clonable from a public GitHub upstream** — the NREL 5MW r-test family (via [`OpenFAST/r-test`](https://github.com/OpenFAST/r-test)) and the IEA-Task-37 reference turbines (via [`IEAWindTask37/IEA-*-RWT`](https://github.com/IEAWindTask37)). The two BModes CertTest cases (Test03, Test04) depend on `external/BModes`, which is a NREL download (not a GitHub repo) and is not redistributable in CI; those tests skip cleanly when the data is absent and remain a maintainer-local enforcement until the BModes archive is mirrored somewhere CI can clone. The per-PR `ci.yml` continues to run the self-contained suite (synthetic + closed-form-referenced) and tolerates "no tests collected" on the integration step when the runner has no upstream data. See [`VALIDATION.md`](VALIDATION.md) for the full per-case matrix with external-data flags and [`external/MANIFEST.toml`](https://github.com/SMI-Lab-Inha/pyBModes/blob/master/external/MANIFEST.toml) for the pinned SHAs + file hashes you can verify against.
10+
Validated against the BModes Fortran reference solver on six benchmark cases (NREL 5MW land + OC3 monopile + OC3 Hywind floating spar, IEA-3.4-130-RWT, BModes CertTest 03 / 04) to **better than 0.01 %** on every comparison. As of 1.8.0 the dedicated [Validation workflow](https://github.com/SMI-Lab-Inha/pyBModes/actions/workflows/validation.yml) (weekly cron + `workflow_dispatch`) enforces the strict tolerance in CI for the cases whose external data is clonable from a public GitHub repository **at a manifest-pinned SHA**: the NREL 5MW family in [`OpenFAST/r-test`](https://github.com/OpenFAST/r-test) and the [`IEA-3.4-130-RWT`](https://github.com/IEAWindTask37/IEA-3.4-130-RWT) and [`IEA-15-240-RWT`](https://github.com/IEAWindTask37/IEA-15-240-RWT) reference-turbine repositories. The remaining Task-37 turbines (IEA-10, IEA-22) plus WISDEM / MoorPy / RAFT cross-reference clones are recorded in the manifest for reproducibility but not currently cloned by the workflow; they're maintainer-local checks. The two BModes CertTest cases (Test03, Test04) depend on `external/BModes`, a NREL download not on GitHub; those tests skip cleanly when the data is absent and stay maintainer-local enforcement. The per-PR `ci.yml` continues to run the self-contained suite (synthetic + closed-form-referenced) and tolerates "no tests collected" on the integration step when the runner has no upstream data. See [`VALIDATION.md`](VALIDATION.md) for the full per-case matrix and [`external/MANIFEST.toml`](https://github.com/SMI-Lab-Inha/pyBModes/blob/master/external/MANIFEST.toml) for the byte-pinned SHAs.
1111

1212
## Documentation
1313

‎VALIDATION.md‎

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,26 @@ IEA-Task-37 repositories on the fly and runs ``pytest -m integration``
1818
verifier report uploaded by every run is the machine-checkable
1919
record that the workflow's coverage holds at the tagged commit.
2020

21-
The workflow validates every case in the *Needs external data*
22-
column whose upstream is **clonable from a public GitHub
23-
repository**: the NREL 5MW r-test family (via
24-
[`OpenFAST/r-test`](https://github.com/OpenFAST/r-test)) and the
25-
IEA-Task-37 reference turbines (via
26-
[`IEAWindTask37/IEA-*-RWT`](https://github.com/IEAWindTask37)).
21+
The workflow validates the cases in the *Needs external data*
22+
column whose upstream is clonable from a public GitHub repository
23+
**at a manifest-pinned SHA** (every clone in
24+
``external/MANIFEST.toml`` got a real commit SHA in 1.8.1). Three
25+
upstreams are actively cloned by ``validation.yml``:
26+
27+
- [`OpenFAST/r-test`](https://github.com/OpenFAST/r-test) — the
28+
NREL 5MW land + monopile + OC3 Hywind regression decks.
29+
- [`IEAWindTask37/IEA-3.4-130-RWT`](https://github.com/IEAWindTask37/IEA-3.4-130-RWT)
30+
— IEA-3.4 reference turbine.
31+
- [`IEAWindTask37/IEA-15-240-RWT`](https://github.com/IEAWindTask37/IEA-15-240-RWT)
32+
— IEA-15 (v1.1 tower; Allen 2020 free-decay reference).
33+
34+
The remaining manifest clones — IEA-10 (``iea_10_198``), IEA-22
35+
(``iea_22_280``), WISDEM, MoorPy, RAFT — are pinned in the manifest
36+
for reproducibility but the integration suite does not currently
37+
exercise them; they're cross-reference / future-validation
38+
artefacts that stay maintainer-local until a corresponding test
39+
lands. Adding a new turbine to the CI-enforced set is a deliberate
40+
maintainer action documented under *Changed* in the CHANGELOG.
2741

2842
The two BModes CertTest cases (Test03, Test04) depend on
2943
``external/BModes``, a [NREL BModes download](https://www.nrel.gov/wind/nwtc/bmodes.html)

0 commit comments

Comments
 (0)