Skip to content

📦 WP-145 — docs: ADR-005 — the fuse boundary and the battle-test contract

Engineering log · Phase 13 — Contributor admission, augmentation guard and keypoint parity, v0.6.0

🔗 Depends on none · Needed by none

⬅️ WP-144 · Roadmap · WP-146 ➡️

WP-145 — a boundary is cheaper to write than to discover

ADR-005 records an agreement between two repositories before either acts on it. No code moves here, and that is the point: every row in Phase 14 deletes a local implementation, and a deletion argued one call site at a time produces a boundary nobody ever stated and everybody assumed differently.

What the boundary actually divides is convention from policy. The first draft of the split was a list — fuse gets warping, letterboxing, the instance filter; this project keeps Targets, tiling, the recipe. A list settles the cases it enumerates and nothing else, and the cases that matter are the ones nobody thought of. The rule underneath it does settle them: fuse owns how a matrix is built, composed and sampled, and this project owns which numbers go into it. Read forward, that rule says a specification pushing an R1 Table S3 constant upstream is written wrong, and it says so without anyone having to have anticipated that specification.

Three capabilities argued their way to staying local, and the arguments are not interchangeable. Multi-image assemblies stay because they do not resample at all — mosaic pastes, mixup blends, copy-paste composites — so a package whose whole subject is collapsing repeated interpolation gains nothing by holding them. Polygon rings stay for an unrelated reason: they are ragged, clipping one at a canvas edge adds vertices, and every target the upstream package transports today is fixed-shape, so moving them would force a padded-tensor or list-of-tensors design onto the package to serve one consumer. Rotated boxes move, on the mirror of the ring argument — five numbers, fixed-shape, batch-leading, exactly the shape already transported, and oriented detection has its own literature rather than being a YOLO idiom. Three capabilities, three different reasons, and a list would have recorded only the verdicts.

The rings decision came with a condition that is worth more than the decision. Rings stay local because the composed matrix the upstream pipeline already returns is the same mapping it applied to the image, so applying that matrix to rings here makes the alignment invariant — a derived box equals a separately transported box on an unclipped instance — hold by construction rather than by agreement between two implementations. That holds only while the geometric run is a single fused segment. A pointwise barrier splitting it hands back one segment's matrix instead of the chain's, and the rings would then be warped by a partial transform with no error, no exception and no visible symptom beyond geometry that is quietly wrong. It is written into Phase 14 as a tier-C invariant for that reason: the failure mode is silence, and silence is what an invariant is for.

The battle-test contract is stated because neither side would otherwise hold it. This project's smoke tiers, goldens and frozen expectations are the real-project evidence the upstream package is tested against, and that evidence stays here. Written down, the exchange is symmetric — an engine with its own unit acceptance against four tasks, two datasets and published-number fidelity gates exercising it end to end. Unwritten, it is the thing each side assumes the other is doing.

Condition 1 of four is not this project's to satisfy, and is recorded as unconfirmed. A runtime dependency needs real SemVer releases on PyPI, because a git-URL requirement is legal in a dependency group and illegal in [project].dependencies — so a SHA pin cannot ship. That is an upstream commitment; this side can state the requirement and cannot meet it. An ADR that recorded it as settled would be recording a hope. The consequence of a refusal is bounded and worth saying: the augmentation stack stays local, which is where it is today, rather than the project becoming unpublishable.

The floor moved with the register. audit_docs_present.py ratchets on the decision count and asserts each ADR section by name, and both are deliberate edits rather than chores — the count catches a deleted last row that contiguity alone would find perfectly contiguous, and the name list is what makes a new ADR enforced rather than merely present. The floor had been left at D17 when D18 landed, so raising it to D19 here catches up one release of drift as well.