Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f757276
ADD: rule that a sup.run() call must live in the tool's own src/
Jules-GP Aug 18, 2026
b430922
ADD: split AREG into AREG_CBCT and AREG_IOS with a shared dependency-…
Jules-GP Aug 18, 2026
63c0538
FIX: split a concatenated jaw and timepoint token so patient keys and…
Jules-GP Aug 18, 2026
7216a3d
FIX: restore ALI_IOS's missing import, torch helpers, monai dependenc…
Jules-GP Aug 19, 2026
e22737c
ADD: AREG_IOSCBCT registering an intraoral scan onto a CBCT through t…
Jules-GP Aug 19, 2026
e319145
ADD: pyflakes to the repository checks to catch undefined names
Jules-GP Aug 19, 2026
83932dd
CLEAN: replace em-dashes with plain dashes across markdown and Python…
Jules-GP Aug 19, 2026
4abd9bc
FIX: point AREG error messages at each split tool's own data endpoint
Jules-GP Aug 19, 2026
a98d6dd
UPDATE: Crown_Seg install hint, now that pytorch3d ships as a prebuil…
Jules-GP Aug 19, 2026
6d24de9
CLEAN: drop four imports no code in these modules uses
Jules-GP Aug 19, 2026
cd8bbed
FIX: ASO reads split_scan_extension from scans, not through the CBCT …
Jules-GP Aug 19, 2026
88753b2
FIX: AREG_IOS asked the supervisor for 'ALI', renamed to ALI_IOS by t…
Jules-GP Aug 19, 2026
638f928
ADD: describe.py collects require() tool names, not only sup.run()
Jules-GP Aug 19, 2026
7ade429
FIX: split AREG's stranded test file into the three tools it became
Jules-GP Aug 19, 2026
fa7ae70
FIX: move ALI's stranded IOS tests into ALI_IOS and update the CBCT s…
Jules-GP Aug 19, 2026
4bbdef2
FIX: CI discovers tools inside grouping folders and runs on Node 24
Jules-GP Aug 19, 2026
d64ab72
FIX: stub the engine import probe so Crown_Seg's tests do not depend …
Jules-GP Aug 19, 2026
0b02fa1
CLEAN: drop NOTE prefixes, keeping what they warned about
Jules-GP Aug 19, 2026
444bac9
FIX: refuse a fully-automated CBCT run with no segmentation weights n…
Jules-GP Aug 19, 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
27 changes: 18 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,20 @@ jobs:
outputs:
tools: ${{ steps.list.outputs.tools }}
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v5
- id: list
run: |
tools=$(find tools -mindepth 2 -maxdepth 2 -name pyproject.toml -printf '%h\n' \
| xargs -r -n1 basename | sort | jq -R . | jq -sc .)
# Depth 2 AND 3, and keyed on [tool.sadt], which is the same rule
# the server's registry and the deployment Dockerfile use. Depth 2
# alone stopped matching when ALI and AREG became grouping folders:
# CI went on testing six tools and silently dropped the five the
# splits produced -- which is where the stale tests were found, months
# later, by hand. [tool.sadt] also drops _template, which CI was
# testing and no server ever serves.
tools=$(find tools -mindepth 2 -maxdepth 3 -name pyproject.toml -printf '%h\n' \
| while read -r dir; do
grep -q '^\[tool.sadt\]' "$dir/pyproject.toml" && echo "$dir"
done | sed 's|^tools/||' | sort | jq -R . | jq -sc .)
echo "tools=$tools" >> "$GITHUB_OUTPUT"
echo "matrix: $tools"

Expand All @@ -37,8 +46,8 @@ jobs:
matrix:
tool: ${{ fromJson(needs.discover.outputs.tools) }}
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true

Expand Down Expand Up @@ -67,8 +76,8 @@ jobs:
scripts:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
# --no-project: scripts/ is deliberately not a package. There is no
Expand All @@ -84,8 +93,8 @@ jobs:
testkit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v5
- uses: actions/checkout@v5
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
- name: Build the template it runs against
Expand Down
159 changes: 108 additions & 51 deletions CONTRIBUTING.md

Large diffs are not rendered by default.

24 changes: 12 additions & 12 deletions PROVENANCE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Where each tool's algorithm came from. Upstream history is deliberately **not**
grafted into this repository: it is a single history covering sixteen unrelated
modules, and merging it would make neither history readable. This table is the
record instead, and it matters more here than commit history does it is what
record instead, and it matters more here than commit history does -- it is what
tells you whether a result came from upstream code or from something we changed.

Upstream is
Expand All @@ -13,13 +13,13 @@ information in full, including the pins kept and the changes made.

| Tool | Upstream path | Upstream commit | Ported | Algorithm modified |
|---|---|---|---|---|
| [Surg_Mov_Pred](tools/Surg_Mov_Pred/) | `SurgMovPred_CLI/SurgMovPred_CLI.py` | `d7702ae` (2026-06-24) | 2026-08-12 | no repackaging only. Model load order sorted for reproducibility, both result tables returned instead of one; predictions bit-identical to the pre-port implementation. |
| [AMASSS](tools/AMASSS/) | `AMASSS_CLI/` | `21a62a8` (2026-05-22) | 2026-08-12 | no repackaging only. Pinned to the deployed stack (torch 2.8.0+cu128, nnunetv2 2.8.1) rather than upstream's declared torch 2.2.0 / nnunetv2 2.8.0; masks bit-identical to the pre-port implementation, within nnUNet's own CUDA nondeterminism. |
| [ASO](tools/ASO/) | `ASO/`, `ASO_CBCT/{PRE,SEMI}_ASO_CBCT/`, `ASO_IOS/{PRE,SEMI}_ASO_IOS/` | **unrecorded** see below | 2026-08-13 | no repackaging only. Nothing is pinned upstream; pinned to the imaging stack the sibling tools lock. Fully-automated CBCT reaches the landmark tool through the supervisor at the point it always ran, so the order is unchanged. Driven end to end through a real supervisor on the real bundle and a real card; not yet diffed numerically against the pre-port ASO. |
| [ALI](tools/ALI/) | `ALI_CBCT/`, `ALI_CBCT_utils/`, `ALI_IOS/`, `ALI_IOS_utils/` | **unrecorded** see below | 2026-08-13 | no repackaging only. Pinned to the deployed stack (torch 2.8.0+cu128, monai 1.6.0, itk 5.4.7). One visible behaviour change: an unlabelled IOS mesh is refused naming `Crown_Seg` instead of being segmented in-process. CBCT landmarks **bit-identical** to the pre-port implementation on a real scan (0.0000 mm across 16/16 run pairs, both sides deterministic); the IOS half is unvalidated, pytorch3d needing a CUDA toolkit. Mucogingival (a third IOS network, mandible only) added from the server's unmerged `AREG` branch, where it had been written against the in-process ALI. IOS crown networks validated on a real mesh; MG's own predictions are not, for want of a lower arch. |
| [AREG](tools/AREG/) | `AREG/` and its CLI modules, by way of the server's unmerged `AREG` branch | **unrecorded** see below | 2026-08-14 | no repackaging only. Pinned to the deployed stack, plus `itk-elastix` which no sibling needs. Drives four tools (AMASSS, ASO, Crown_Seg, ALI) through the supervisor, where the in-process version used `registry.TOOLS`. CBCT engine validated end to end against a known transform; the IOS engine and any comparison with the pre-port implementation are **not**. |
| [Crown_Seg](tools/Crown_Seg/) | (written against `shapeaxi` directly) | shapeaxi 2.0.2 | 2026-08-12 | no the network is untouched and its raw output is bit-identical. Carries a two-line workaround for a shapeaxi 2.0.x bug that breaks the tool upstream and downstream alike. |
| [Batch_Dental_Seg](tools/Batch_Dental_Seg/) | `BATCHDENTALSEG/BATCHDENTALSEGLib/SegmentationWidget.py` | `6df3fab` (2026-08-05) | 2026-08-12 | no repackaging only. Same stack as AMASSS (torch 2.8.0+cu128, nnunetv2 2.8.1); labels compared against the pre-port implementation. |
| [Surg_Mov_Pred](tools/Surg_Mov_Pred/) | `SurgMovPred_CLI/SurgMovPred_CLI.py` | `d7702ae` (2026-06-24) | 2026-08-12 | no -- repackaging only. Model load order sorted for reproducibility, both result tables returned instead of one; predictions bit-identical to the pre-port implementation. |
| [AMASSS](tools/AMASSS/) | `AMASSS_CLI/` | `21a62a8` (2026-05-22) | 2026-08-12 | no -- repackaging only. Pinned to the deployed stack (torch 2.8.0+cu128, nnunetv2 2.8.1) rather than upstream's declared torch 2.2.0 / nnunetv2 2.8.0; masks bit-identical to the pre-port implementation, within nnUNet's own CUDA nondeterminism. |
| [ASO](tools/ASO/) | `ASO/`, `ASO_CBCT/{PRE,SEMI}_ASO_CBCT/`, `ASO_IOS/{PRE,SEMI}_ASO_IOS/` | **unrecorded** -- see below | 2026-08-13 | no -- repackaging only. Nothing is pinned upstream; pinned to the imaging stack the sibling tools lock. Fully-automated CBCT reaches the landmark tool through the supervisor at the point it always ran, so the order is unchanged. Driven end to end through a real supervisor on the real bundle and a real card; not yet diffed numerically against the pre-port ASO. |
| [ALI](tools/ALI/) | `ALI_CBCT/`, `ALI_CBCT_utils/`, `ALI_IOS/`, `ALI_IOS_utils/` | **unrecorded** -- see below | 2026-08-13 | no -- repackaging only. Pinned to the deployed stack (torch 2.8.0+cu128, monai 1.6.0, itk 5.4.7). One visible behaviour change: an unlabelled IOS mesh is refused naming `Crown_Seg` instead of being segmented in-process. CBCT landmarks **bit-identical** to the pre-port implementation on a real scan (0.0000 mm across 16/16 run pairs, both sides deterministic); the IOS half is unvalidated, pytorch3d needing a CUDA toolkit. Mucogingival (a third IOS network, mandible only) added from the server's unmerged `AREG` branch, where it had been written against the in-process ALI. IOS crown networks validated on a real mesh; MG's own predictions are not, for want of a lower arch. |
| [AREG](tools/AREG/) | `AREG/` and its CLI modules, by way of the server's unmerged `AREG` branch | **unrecorded** -- see below | 2026-08-14 | no -- repackaging only. Pinned to the deployed stack, plus `itk-elastix` which no sibling needs. Drives four tools (AMASSS, ASO, Crown_Seg, ALI) through the supervisor, where the in-process version used `registry.TOOLS`. CBCT engine validated end to end against a known transform; the IOS engine and any comparison with the pre-port implementation are **not**. |
| [Crown_Seg](tools/Crown_Seg/) | -- (written against `shapeaxi` directly) | shapeaxi 2.0.2 | 2026-08-12 | no -- the network is untouched and its raw output is bit-identical. Carries a two-line workaround for a shapeaxi 2.0.x bug that breaks the tool upstream and downstream alike. |
| [Batch_Dental_Seg](tools/Batch_Dental_Seg/) | `BATCHDENTALSEG/BATCHDENTALSEGLib/SegmentationWidget.py` | `6df3fab` (2026-08-05) | 2026-08-12 | no -- repackaging only. Same stack as AMASSS (torch 2.8.0+cu128, nnunetv2 2.8.1); labels compared against the pre-port implementation. |

A row is filled in by the PR that migrates the tool, in the same commit that
adds the package. "Algorithm modified" is `no` for a pure repackaging and
Expand All @@ -33,16 +33,16 @@ The server-side source is preserved at the `archive/AREG` tag in

**ALI's, ASO's and AREG's upstream commits are unrecorded, and that is a gap,
not a style.** Both server-side ports landed with no upstream revision in the commit
message and none in the tree ALI as `ADD ALI & CrownSeg` (`a0ed474`,
2026-07-31) so which upstream commit each algorithm came from cannot be
message and none in the tree -- ALI as `ADD ALI & CrownSeg` (`a0ed474`,
2026-07-31) -- so which upstream commit each algorithm came from cannot be
recovered from either repository. The per-module mappings are exact and are in
[tools/ALI/README.md](tools/ALI/README.md) and
[tools/ASO/README.md](tools/ASO/README.md); only the revisions are missing, and
they need filling in by whoever made those ports. Until then, "no repackaging
they need filling in by whoever made those ports. Until then, "no -- repackaging
only" is a claim about code that cannot be pointed at.

`tools/_template/` has no row: it is the reference package the others are copied
from, not a port.

**Every tool is now in this table.** AREG was the last, and it was the one this
document used to say was deliberately absent it is migrated as of 2026-08-14.
document used to say was deliberately absent -- it is migrated as of 2026-08-14.
24 changes: 12 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,20 +63,20 @@ The server runs it out of process, one interpreter per tool:
/tools/<name>/.venv/bin/python /opt/sadt/runner.py --job /jobs/<uuid>/job.json
```

`runner.py` ships with the server and is injected by absolute path never
installed into a tool venv so runner and server are always the same version
`runner.py` ships with the server and is injected by absolute path -- never
installed into a tool venv -- so runner and server are always the same version
and there is no cross-repo skew to manage. That is also why there is no shared
`sadt-core` package: adding one would put a version of *ours* inside every tool
venv, and it would solve a problem that does not exist.

A tool that needs another tool **mid-run** declares `*, sup` and is handed a
supervisor; the call re-enters the same runner with the sibling's interpreter.
`ASO` and `AREG` are the two that do. Everything the server has to hold up on
its side of that and everything else it took over when the tools stopped
doing it is in [docs/SERVER_CONTRACT.md](docs/SERVER_CONTRACT.md).
its side of that -- and everything else it took over when the tools stopped
doing it -- is in [docs/SERVER_CONTRACT.md](docs/SERVER_CONTRACT.md).

The full set of rules annotations, defaults, batch inputs, where output may be
written is in [CONTRIBUTING.md](CONTRIBUTING.md). `tools/_template/` is a
The full set of rules -- annotations, defaults, batch inputs, where output may be
written -- is in [CONTRIBUTING.md](CONTRIBUTING.md). `tools/_template/` is a
working example of all of them.

## Layout
Expand All @@ -103,7 +103,7 @@ thing without importing it. See [testkit/README.md](testkit/README.md).
## Scripts

`scripts/describe.py` emits the JSON schema the server publishes for a tool,
read from `run()`'s signature so the schema cannot drift from the code. It
read from `run()`'s signature -- so the schema cannot drift from the code. It
runs with the tool's own interpreter, because importing a tool needs the tool's
dependencies:

Expand All @@ -125,7 +125,7 @@ $ tools/_template/.venv/bin/python scripts/describe.py tools/_template
```

An argument annotated `Literal[...]` publishes its options as `choices`, so the
client can render a picker without a second declaration to keep in step
client can render a picker without a second declaration to keep in step --
`list[Literal[...]]` for several-of, a bare `Literal[...]` for exactly-one.

It exits 2 on anything it cannot represent rather than emitting a schema that is
Expand Down Expand Up @@ -163,12 +163,12 @@ out/
The second command is the whole point: ASO needs landmarks mid-run, so it is
given a **supervisor**, and `sup.run("ALI_CBCT", ...)` re-enters this same
script with that tool's interpreter. Chaining and nesting are the same
recursion `AREG → ASO → ALI_CBCT` is three levels of it with no special case.
recursion -- `AREG → ASO → ALI_CBCT` is three levels of it with no special case.

**Developer convenience, not the deployment path.** In production the server's
`execution/runner.py` does this, and a tool cannot tell the two apart: five
members, duck-typed, nothing shared. It is still the shortest readable
reference for what a supervisor has to be and the place to reproduce a
reference for what a supervisor has to be -- and the place to reproduce a
chaining bug without standing a server up.

## Getting started
Expand All @@ -184,6 +184,6 @@ uv run pytest # runs run() end to end
## Provenance

[PROVENANCE.md](PROVENANCE.md) records, for every tool, the upstream path and
commit it was ported from and whether the algorithm was modified. That table
not this repository's commit history is what tells you six months from now
commit it was ported from and whether the algorithm was modified. That table --
not this repository's commit history -- is what tells you six months from now
whether a result came from upstream code or from something we changed.
Loading