Files
EVOLV/.claude/refactor/OPEN_QUESTIONS.md
znetsixe 91e4255ef5 Add refactor planning docs (.claude/refactor/)
Platform-wide refactor plan: README, CONVENTIONS, CONTRACTS,
MODULE_SPLIT, TASKS, OPEN_QUESTIONS. Source of truth for the
phased refactor across all 12 submodules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 18:22:35 +02:00

3.3 KiB

Open questions

Things deferred. Append, don't rewrite history. Add a date when you add or resolve an entry. Anyone (human or agent) discovering an unclear decision during refactor work writes it here rather than guessing.

Format:

## YYYY-MM-DD — Short title

**Context:** what we're trying to do
**Question:** what's unresolved
**Default chosen:** what we did meanwhile
**Decision needed by:** which phase or task

2026-05-10 — External Port-0 topic naming — RESOLVED

Decision (2026-05-10): Use canonical names (set.* / cmd.* / data.* / child.* / query.* / evt.*) from Phase 1 onwards. Each commands/index.js declares the canonical name as the topic and lists legacy names in aliases. Aliases log a one-time deprecation warning. Phase 7 shrinks to: remove aliases after one release cycle.

The full prefix glossary (with what each does and why) is now in CONTRACTS.md §1. See it before naming a topic.


2026-05-10 — Parent EVOLV repo development branch lineage — RESOLVED

Decision (2026-05-10): Rebase parent development onto origin/main before the refactor proceeds. Done at the start of Phase 1.


2026-05-10 — generalFunctions deprecated paths — RESOLVED

Decision (2026-05-10): Tracked as Phase 8.5 in TASKS.md. Cleanup runs after promotion to main. The list of paths to remove is captured there so it isn't lost.


2026-05-10 — Two child-storage shapes — RESOLVED

Decision (2026-05-10): Registry-as-truth, with named getters that read clearly in code. domain.machines keeps working — it's a getter that returns the rotatingMachine slice of this.child. Same for domain.stations, domain.machineGroups, etc. Domain code reads naturally; the registry is the source of truth underneath.

Named getters are declared by the domain subclass in configure():

configure() {
  Object.defineProperty(this, 'machines',
    { get: () => this.child?.machine?.centrifugal ?? {} });
}

(BaseDomain provides a helper for this pattern.)


2026-05-10 — Async vs sync tick() — RESOLVED with redesign

Decision (2026-05-10): Default is event-driven. Ticks are opt-in.

BaseNodeAdapter exposes two timers:

  • static tickInterval = null — opt-in periodic tick. Default null = no tick. Domain emits 'output-changed' on this.emitter instead, and BaseNodeAdapter subscribes to that event to push outputs.
  • static statusInterval = 1000 — always-on status badge poll. Required because Node-RED's editor refresh expects a heartbeat. Set to 0 only in headless test environments.

When opting into ticks:

  • Document why in a one-line comment above static tickInterval = ... (e.g. "needs delta-time for predicted volume integrator").
  • A node should opt in only when truly time-driven. Examples that need it: pumpingStation (predicted volume integrates over time), measurement (when simulator is enabled — ticks the random walk).
  • Examples that DO NOT need it: MGC (recomputes on pressure events), rotatingMachine (recomputes on measurement events + state changes).

tick() is treated as fire-and-forget (no await). A node that needs serialisation uses LatestWinsGate internally.

See CONTRACTS.md §2 for the BaseNodeAdapter shape.