Skip to content

feat(core): let each setting family own its own read and edit#18

Merged
nmrtist merged 1 commit into
mainfrom
feat/property-provider-seam
Jul 26, 2026
Merged

feat(core): let each setting family own its own read and edit#18
nmrtist merged 1 commit into
mainfrom
feat/property-provider-seam

Conversation

@nmrtist

@nmrtist nmrtist commented Jul 26, 2026

Copy link
Copy Markdown
Owner

The catalog could address only one kind of value: its planner took a contour
spec by type, so any setting stored elsewhere needed a second planner beside it.
This branch removes that limit and proves the result on three settings that live
in three different places.

What holds now

A setting family owns reading and editing one target. The service keeps
everything that must stay singular: the multi-target loop, the aggregate value,
the explicit skip list, and the atomic commit.

Adding a setting is a family module and one registration. If you find
yourself adding a branch to the service for a new setting, the boundary has been
crossed and something is wrong. A family whose values are not stored in the
document says so; it does not get a special case upstream.

Contour moved onto this boundary with no behaviour change, which its existing
tests pin.

What a user notices

Settings edited through the catalog now obey the rules their own surfaces
already had. Each of these was broken until this branch:

  • A paused recipe stages the change for Apply instead of recomputing
    immediately.
  • Dragging a value lands one undo entry, not one per frame.
  • A drag notch comes from the setting rather than from the width of its range,
    which had made one pixel worth 100 Hz.
  • A section counts the targets it can actually apply to.
  • A dataset offers only the processing axes the rest of the application shows,
    so a pseudo-2D recipe no longer accepts a write to an axis with no panel.
  • A locked plot keeps its read-only readouts and palette entries, and refuses
    edits with a reason that says how to unlock it.

Decisions worth not re-litigating

  • A skip carries a typed reason, so a caller can tell a value that was
    already set from a target the setting does not reach. Plan-time skips stay
    prose-only on purpose: none of them can be a same-value no-op, so the absence
    of a reason is itself the distinction.
  • Gaussian broadening excludes zero. At gb = 0 the window is exp(+π·lb·t)
    — an unbounded gain, not a window. A recipe that stored zero is repaired on
    load. The transform itself is unchanged.
  • Admission to the catalog is a capability, never a match on the dataset
    kind. A dataset that grows an addressable pipeline gains these settings by
    exposing the capability.

Deferred, with reasons

  • The image-encoding slice. SeriesEncoding::Image can be constructed, but
    no dataset adapter produces a figure from it, so cache invalidation, default
    policy and provenance have no path along which they could be observed.
    Validating it needs a rendering adapter, not a catalog entry.
  • Overflow hardening in the transform. A large broadening over a long
    acquisition can still overflow to infinity. Normalising the window to its peak
    would fix it without changing shape, but it changes absolute amplitude, so it
    belongs in a change that says so.

For whoever takes the next branch

  • crates/core/src/state/datasets.rs and crates/app/src/ui/object_inspector.rs
    are within three lines of the 800-line limit. Plan to split them before
    editing.
  • One commit reaches one store today, because a setting belongs to one scope.
    When a second store arrives, refuse a mixed request while planning — the
    executor is the wrong place to discover it.

Verification

cargo pr-check passes. Documentation for processing, layout and export, the UI
overview, the command palette and automation is updated in English and Simplified
Chinese, and npm run build in docs/ completes.

One existing assertion changed: a scoped-reset test asserted that one series was
updated, on a fixture whose contour already sat at its factory encoding. A reset
that finds nothing to do is now a reported skip, so the fixture moves the contour
off its default first and the test still proves the scoping it was written for.

The catalog could address only one kind of value: its planner took a contour
spec by type, so any setting stored elsewhere needed a second planner beside it.

A family now owns reading and editing one target. The service keeps what must
stay singular — the multi-target loop, the aggregate, the explicit skip list and
the atomic commit — so adding a setting is a family module and a registration,
and nothing in the service learns about it. Contour moved onto that boundary
unchanged.

Three settings prove it across scopes: figure tick-label size on the document,
line stroke width over a mixed selection, and apodization addressed by
processing step, offering the fields the chosen window actually has.

Settings edited through the catalog now obey the rules their surfaces already
had: a paused recipe stages the change for Apply, a drag lands one undo entry, a
section counts the targets it can apply to, a dataset offers only the processing
axes the rest of the app shows, and a locked plot keeps its read-only readouts
while refusing edits with a reason.

Two decisions to keep: a skip carries a typed reason, so a caller can tell a
value that was already set from a target the setting does not reach; and
Gaussian broadening excludes zero, which is an unbounded gain rather than a
window.
@nmrtist
nmrtist merged commit e4e9029 into main Jul 26, 2026
11 checks passed
@nmrtist
nmrtist deleted the feat/property-provider-seam branch July 26, 2026 05:04
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 26, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant