Skip to content

AppState Design Review — Criteria

Why this doc exists

The 2026-05-09 Architectural Foundations doc endorses a Redux/Vuex/MobX-shaped pattern ("single source of truth + pure reducers + selector-based subscriptions + memoized derived values") as the production-grade target for state management. That framing was imported from JS frontend practice.

The open question is whether that's the right target for a production-ready industrial machine desktop system, or whether it imports ceremony that doesn't earn its keep in this domain. A WPF desktop driving real hardware has different constraints from a browser SPA: long-lived sessions, hardware ownership semantics, audit obligations, hard threading boundaries, and operators who need the system to be debuggable in the field rather than time-travel-debuggable in a dev console.

Before doing the review, we lock the criteria. The review itself (a separate doc) will score the current AppState design against these criteria with concrete code references — same format as the 2026-05-09 audit. A criterion belongs on this list only if a real design choice could fail it.

Criteria

A. Correctness under the domain's real constraints

  1. Threading & ownership. Who is allowed to write each slice, from which thread, under what lock? A design that requires the reader to remember rules is worse than one that makes violations a compile error or a runtime assertion at the write boundary.
  2. Backpressure & bounded memory. At real machine rates, can a slow subscriber cause unbounded queueing, dropped events, or UI starvation? The store's contract on this must be explicit, not emergent from whatever the dispatcher happens to do.
  3. Consistency between related slices. When two fields must move together (e.g. WorkflowState + ActiveRecipe + StageCommandInFlight), can a subscriber ever observe a torn read? Atomic transitions must be expressible without subscriber-side reassembly.
  4. Crash & fault recovery. What is in-memory only, what is durable, what is reconstructable from device state on reconnect? The store should make that boundary visible at the type level, not as tribal knowledge.

B. Operational fitness

  1. Auditability. For an industrial machine, "why did the system command X at 14:03:22?" must be answerable from logs alone, not from re-running. The state-change record should be a first-class artifact, not a bolt-on.
  2. Debuggability of live systems — dual layer. Two distinct audiences:
    • 6a. Operator surface. A curated subset of state must be visible in the UI on the deployed machine. The operator cannot open logs or attach tools. If a state value drives an operator decision, it must be reachable from the touchscreen.
    • 6b. Engineer deep-dive. A trained service engineer with a laptop must be able to pull a self-contained snapshot of current state + recent history off the machine (log artifacts, state dump, or equivalent) without rebuilding or attaching a debugger. The mechanism should be appropriate to a fielded desktop app — not Redux DevTools, but not absent either.
  3. Equipment-state reporting (SEMI E10-aligned). The store must be able to project current machine state into the equipment-state vocabulary expected by a fab (productive / standby / engineering / scheduled-down / unscheduled-down / non-scheduled) without ambiguity or torn reads across the slices that determine each state. State transitions must be timestamped to a precision compatible with E10 reporting. This is a specific, regulated application of criterion 5 (auditability); both must hold.
  4. Determinism in tests. Pure state transitions should be unit-testable without spinning up a dispatcher, scheduler, dispatcher queue, or UI thread. If a test of "given state S, action A produces state S′" needs more than a function call, the design has taxed testability.

C. Cost of carrying the abstraction

  1. Marginal cost of a new field or slice. Adding one piece of state should touch ~1–2 files, not N. If it's N, the pattern is taxing every future change — including changes that have nothing to do with state management.
  2. Cognitive load for a new engineer. Someone unfamiliar must be able to trace "user clicks → state change → UI update" by reading code top-to-bottom, without learning a framework vocabulary (actions, reducers, selectors, middleware, thunks, sagas, effects) first.
  3. Earned abstraction. Every layer in the design — store, subscriber, dispatcher, action, reducer, selector, memoizer — must justify itself against a plain alternative (a typed property with INotifyPropertyChanged, or a typed event, or a Channel<T>). "It's how Redux does it" is not a justification.

D. Honest scope

  1. What the store is NOT. Not a message bus, not a workflow engine, not a persistence layer, not an event log. If those responsibilities have leaked into the store — or if the store has leaked into them — that is a finding regardless of code quality.

E. Craftsmanship and domain fit

Criteria A–D ask whether the design works. Section E asks whether the code is the kind of code a senior engineer would point to and say "yes, this is how it's done." A world-class result for an industrial-desktop system requires both. Each criterion below names a concrete failure mode that can be pointed at in a file — not abstract "elegance."

  1. Code reads as domain prose. Identifiers, types, and method names use the wafer-inspection / industrial-machine vocabulary, not generic state-management jargon. A reader from the fab side should recognize what each slice means, not just what it does mechanically. ActiveRun, Cassette, LoadedRecipe, LatestTagValues pass this. A hypothetical EntityStateContainer<T> would fail it. Scaffolding terms (PipelineCounters, OperationalCounters) are acceptable for instrumentation slices; simulator vocabulary in production state (SelectedSimulatorProfile) is a finding.

  2. Type design makes invalid states unrepresentable where the domain allows. Combinations the machine cannot physically be in should not be expressible in the type. If WorkflowState = Idle then ActiveRun is impossible — is that enforced by the type or only by convention? A record with two independently nullable fields lets a caller construct invalid pairs by with {}; a discriminated union over (WorkflowState, ActiveRun) does not. The verdict is per-pair: pick the level of strictness that matches what the domain can tolerate, and either enforce it at the type or document explicitly that the reducer is the enforcing site.

  3. API surface is small, intentional, and honestly named. Public surface exposes only what callers should use. Internal helpers stay internal. No Manager / Helper / Service suffixes that say nothing. No methods that exist for one caller and would confuse the next. Update, Subscribe, Current pass. RegisterTagsActiveProvider is the kind of name worth questioning — the name describes the mechanism (registering a callback), not the intent (supplying the gauge its data source).

  4. No accidental complexity, no dead surface, no speculative ceremony. No // TODO / // HACK / commented-out branches. No abstractions sitting unused waiting for a future caller. No "kept for back-compat with test fakes" surface that could be removed by editing the fakes. No two ways to do the same thing if the domain only has one. No null! workarounds that paper over a type's nullability semantics. What's not in the code is often the cleanest signal of quality.

Out of scope for this review

  • Performance (already audited under Phase 1 and Phase 2 measurement slices).
  • The view layer (MVVM, binding strategy) — separate concern, separate review.
  • Whether the store is the right place for any specific piece of state. That question is asked per-slice during normal development, not as a one-time audit.

What success looks like

A short follow-up document — 2026-05-??-appstate-design-review.md — that:

  • Scores the current AppState design row-by-row against criteria 1–16, with file/line evidence per row.
  • Identifies which criteria the current design passes on merit, which it passes by accident, and which it fails.
  • For each failure, proposes the smallest change that would address it — not a full pattern swap unless the failure is structural.
  • Does not invent abstractions to fix problems we don't have.

Open questions — resolved 2026-05-12

  • Regulatory framework: SEMI / semiconductor-fab. Equipment-state reporting (SEMI E10) and audit-record completeness are in scope. Added as criterion 7.
  • Field debugger audience: mixed — operator on a touchscreen + service engineer with a laptop. Criterion 6 split into 6a (operator-visible UI subset) and 6b (engineer-pullable snapshot artifacts).

Docs-first project memory for AI-assisted implementation.