Files
noteman-slicer/docs/adr/0001-slicer-owns-image-processing-bundle-is-the-only-channel.md
T
Esa Kataja efc920fdc5 Add design docs: glossary, ADR 0001, and project scaffolding
Records the outcome of the design review that reworked the original
handoff notes:

- CONTEXT.md — ubiquitous language for the slicer (slice, cut, discard,
  content rectangle, trim, song scale, bundle, project, jump source)
- docs/adr/0001 — the slicer owns all image processing and the bundle is
  the only channel to noteman; noteman's sharp pipeline goes away
- pyproject renamed to noteman-slicer
- .gitignore keeps scores and bundles out of the repo (copyright, size)
2026-07-28 21:33:14 +03:00

49 lines
2.8 KiB
Markdown

# The slicer owns all image processing; the bundle is the only channel to noteman
The slicer was originally scoped to share the work with noteman: it would cut the
pages, upload them to `POST /api/admin/songs/:id/slices`, and noteman's sharp
pipeline would trim, downscale and convert ink to alpha on arrival. We decided
instead that the slicer produces finished, display-ready artifacts and hands them
over as a single zip; noteman does no image manipulation beyond what plain HTML
and CSS do at runtime, and there is no API between the two tools.
## Considered options
**Split the work (rejected).** Slicer cuts, noteman normalises. This was the
existing shape and needed no changes on either side. It failed on inspection:
noteman's `normalizeSlice` calls sharp's `removeAlpha()` before its luminance
pass, and `removeAlpha()` discards the alpha channel rather than compositing it
over white — so any transparency the slicer produced would flatten to black ink.
A polygon-masked slice would have arrived as a solid black rectangle. More
fundamentally, the split put trim, downscale and encoding rules in two repos and
two languages, where they must agree exactly and nothing checks that they do.
**Slicer produces final pixels, noteman stores them (chosen).**
**Direct POST with a bypass flag (rejected).** Keeps the API but adds a
content-sniff or flag to skip normalisation. Preserves the two-language
duplication it was meant to remove, and needs an admin token in a local script.
## Consequences
Several rules that read as arbitrary in the slicer's code are only explicable
from here:
- The slicer owns **trim, staff-height normalisation, the 1920px ceiling, right
padding, levels and the ink→alpha conversion.** Nothing downstream can rescue
a faint scan or rehabilitate a bad crop, so the slicer's output is final.
- **Scale is normalised on staff height, not width**, and derived once per song.
A per-slice factor would change note size line to line down a continuous
scroll; a width-based factor breaks the moment a slice comes from a different
source (a rescanned page, or a re-engraved replacement system).
- **noteman must delete its sharp pipeline** and accept a bundle instead. Until
that lands, the slicer produces artifacts nothing consumes.
- **The bundle is a versioned contract**, not an internal format. Two separately
maintained repos now agree on: the marker enum, slice ordering, and the shape
of `song.json`. That coupling is deliberately kept as small as possible —
which is why every jump source stores its target slice explicitly rather than
relying on an unwritten "exactly one Coda per song" resolution rule.
- **The split is reversible only in one direction.** Re-adding server-side
processing means re-deriving all of the above; going further the other way
(more processing in the slicer) is free.