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>
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'onthis.emitterinstead, 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.