0Packaging + CI
Readysrc/plumber/, hatchling, published asab-plumber- Nix + uv + direnv
- ruff + pytest on Python 3.9 and 3.12
Copy maply's setup. Nothing to decide.
The plan breaks into 10 components. Six can go to tickets now. Four are blocked by two gaps in the decisions, and each gap needs one short grilling session.
Numbers like #5 point to tickets on the Discovery map.
An arrow means "the next one is built on this one." Dotted lines are support work.
flowchart LR
P[1 · protocols] --> R[3 · resolver]
P --> C[2 · config]
R --> B[4 · builder / run]
C --> B
B --> LS[5a · LocalSequential]
B --> MP[5b · LocalMultiprocess]
MP --> CK[6 · checkpoints]
LS --> CK
R --> CH[7 · check]
CK --> CLI[8 · CLI]
CH --> CLI
PK[0 · packaging + CI] -.-> P
SH[9 · shim example + docs] -.-> R
src/plumber/, hatchling, published as ab-plumberCopy maply's setup. Nothing to decide.
PhaseModule[Payload]DatasetRef[Payload]Result[Payload]The shapes are locked. But what DatasetRef is used for is unclear. See Gap A.
./plumber.yaml, or --config PATH--params key=value over the file#4's YAML example has no output: block, because #14 added output later. Fix: give it the same shape as input (path + params).
"pkg.mod:attr" → callable (:attr is required)sys.pathname defaults to the path; duplicate names stop with an errorstrategy.execute()Depends on who reads the input. See Gap A.
ProcessPoolExecutorpartitionable: false → join the pieces, run once, split againcheckpoint: true → <dir>/<name>.parquet (GeoParquet)--from loads the previous phase's file; stops with an error if it's missingoutput runs only if the run reaches the last phaseinspect.signature) → runtime probereport.jsonrun() calls it first, as a preflightThe runtime probe has no clear source of test data. See Gap B.
plumber run with --strategy --worker-count --chunk-size --params --from --to --configplumber check with --configplumber.examples.adapter_shimTwo decisions on the map don't fit together:
#4 / #14: input = the user's function → returns a GeoDataFrame #5: execute(phases, data: DatasetRef, ...) # "LocalSequential loads the DatasetRef"
If the user's function does the read, execute never needs a DatasetRef to load. The same question applies to Result.ref and the user's output function. The fix touches components 1, 2, 4 and 5a.
#6 says "a tiny synthetic GeoDataFrame." But a real phase like dissolve(by="zone_id") crashes on made-up data that has no zone_id column. That gives a false FAIL.
Ready for tickets now: 0 · 3 · 5b · 6 · 8 · 9
Waiting on two grilling tickets: 1 · 2 · 4 · 5a (Gap A) and 7 (Gap B)
Next step: open both gaps as grilling tickets on the map, and grill Gap A first, since it blocks the most components. Neither gap is in the plan.md draft yet, so both go into its Deferred list as open questions.