P9 follow-up: expand wiki template + add Home/Archive template

WIKI_TEMPLATE.md — extend the canonical per-node page from 9 to 14 sections:
  + Header band (commit hash + regen date)
  + Code map (flowchart TB w/ subgraphs over concern modules)
  + Child registration (mirrors ChildRouter declarations)
  + Data model — getOutput() (abstract schema + optional concrete sample)
  + Debug recipes (symptom → first thing to check)
  + AUTOGEN markers around topic-contract + data-model schema so the
    Phase 9 regen script can rewrite in place.
  + 'Picking a visual' table: Mermaid is default, plots/SVG/screenshots
    allowed where they serve the data.
  + Archive banner snippet.

WIKI_HOME_TEMPLATE.md (new) — Home.md + Archive.md templates:
  - Platform-wide Mermaid graph of 11 active nodes, S88-coloured.
  - Navigation table grouped by S88 level.
  - Standards-pointer table to .claude/rules + .claude/refactor docs.
  - Live refactor-status table for returning visitors.
  - Archive index template with archival-date column.

No wiki pages written yet — next step is one worked example
(pumpingStation) before any change to the Gitea wiki repo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene De Ren
2026-05-11 14:25:34 +02:00
parent 0a890fd0d7
commit e03a7a51b7
2 changed files with 396 additions and 122 deletions

View File

@@ -0,0 +1,141 @@
# Platform wiki home — `Home.md` template
The landing page for the EVOLV Gitea wiki. Same visual-first rules as `WIKI_TEMPLATE.md`: diagrams lead, tables annotate, ≤ 60 words per paragraph.
`Home.md` answers three questions for a first-time visitor:
1. **What is this platform?** One paragraph.
2. **What nodes exist and how do they relate?** One platform-wide Mermaid graph + a navigation table.
3. **Where do I find the conventions?** A pointer table to the rule files in `.claude/`.
Plus a live refactor-status table so a returning visitor knows what changed since they last looked.
## Template — copy the block below as the seed for `Home.md`
```
<!-- BEGIN TEMPLATE — Home.md -->
# EVOLV — Wastewater treatment plant automation
> **Reflects code as of `<git short hash>` · regenerated `<YYYY-MM-DD>` via `npm run wiki:home`**
EVOLV is a Node-RED node library for wastewater plant automation, developed by the R&D team at Waterschap Brabantse Delta. Nodes follow the ISA-88 (S88) batch control standard. The library exposes 11 active nodes spanning four S88 levels: from Process Cell down to Control Module, plus one utility node for dashboard integration.
## Platform overview
~~~mermaid
flowchart TB
subgraph PC["Process Cell"]
ps[pumpingStation]:::pc
end
subgraph UN["Unit"]
mgc[machineGroupControl]:::unit
vgc[valveGroupControl]:::unit
reactor[reactor]:::unit
settler[settler]:::unit
monster[monster]:::unit
end
subgraph EM["Equipment"]
rm[rotatingMachine]:::equip
v[valve]:::equip
diff[diffuser]:::equip
end
subgraph CM["Control Module"]
meas[measurement]:::ctrl
end
subgraph UT["Utility"]
dash[dashboardAPI]:::neutral
end
ps --> mgc
ps --> vgc
mgc --> rm
vgc --> v
reactor --> diff
meas -.data.-> rm
meas -.data.-> v
meas -.data.-> reactor
meas -.data.-> settler
classDef pc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
classDef neutral fill:#dddddd,color:#000
~~~
S88 colours: Process Cell `#0c99d9`, Unit `#50a8d9`, Equipment `#86bbdd`, Control Module `#a9daee`. Solid arrow = parent/child relationship. Dashed arrow = data flow (`measurement` feeds many node types).
## Live nodes
| S88 | Node | One-liner | Wiki |
|---|---|---|---|
| 🟦 Process Cell | **pumpingStation** | Manages a wet-well basin, hands demand to one or more group controllers. | [→](pumpingStation) |
| 🔷 Unit | **machineGroupControl** | Load-sharing across a group of rotatingMachines. | [→](machineGroupControl) |
| 🔷 Unit | **valveGroupControl** | Coordinated valve control across a group of valves. | [→](valveGroupControl) |
| 🔷 Unit | **reactor** | Bioreactor — couples diffuser + measurements + kinetics. | [→](reactor) |
| 🔷 Unit | **settler** | Settler / clarifier modelling. | [→](settler) |
| 🔷 Unit | **monster** | Composite-sample sensor surrogate. | [→](monster) |
| 🟦 Equipment | **rotatingMachine** | Single pump / compressor — curves, state machine, prediction. | [→](rotatingMachine) |
| 🟦 Equipment | **valve** | Single valve actuator with FSM. | [→](valve) |
| 🟦 Equipment | **diffuser** | Aeration diffuser, gas-side modelling. | [→](diffuser) |
| 🔹 Control Module | **measurement** | Sensor signal-conditioning, scaling, calibration. | [→](measurement) |
| ⚪ Utility | **dashboardAPI** | Bridge between FlowFuse dashboard widgets and EVOLV. | [→](dashboardAPI) |
## Standards & conventions
| Document | What it covers | Where |
|---|---|---|
| Node architecture (3-tier) | entry → nodeClass → specificClass | `.claude/rules/node-architecture.md` |
| Flow layout (Node-RED tabs) | Tab boundaries, lanes, S88 colours, link channels | `.claude/rules/node-red-flow-layout.md` |
| Topic naming (`set.` / `cmd.` / `evt.`) | Canonical input + output topics | `.claude/refactor/CONTRACTS.md` §1 |
| Wiki page shape | Per-node page template | `.claude/refactor/WIKI_TEMPLATE.md` |
| Wiki home shape | This page's template | `.claude/refactor/WIKI_HOME_TEMPLATE.md` |
| generalFunctions stability rules | What's safe to change | `.claude/rules/general-functions.md` |
## Refactor status
| Tier | What | Status |
|---|---|---|
| 1 | Add infra in generalFunctions (additive only) | ✅ done |
| 2 | Pilot: pumpingStation | ✅ done |
| 3 | Convert measurement, MGC, rotatingMachine | ✅ done |
| 4 | Convert valve, VGC, reactor, settler, monster, diffuser | ✅ done |
| 4* | dashboardAPI | ⏸️ out of scope (no `generalFunctions` dep) |
| 5 | Canonical topic names + alias deprecation | 🟡 partial |
| 6 | development → main promotion | ⏳ pending Docker E2E |
| 7 | Wiki refactor (this work) | 🟡 in progress |
## Archive
Pre-refactor pages live under `Archive/`. See [Archive index](Archive).
<!-- END TEMPLATE -->
```
## Notes for the maintainer
- `npm run wiki:home` (not yet built) re-renders the platform Mermaid block if any node's `softwareType` registration changes. Until then, the diagram is hand-maintained.
- Refactor-status rows flip as tiers land. Anyone landing a tier updates the table in the same PR.
- The "Live nodes" table is hand-maintained but small — bulk changes happen only when a node is added or retired.
- The Mermaid graph above mirrors what's in `.claude/rules/node-red-flow-layout.md` §10.1 (lane convention). If the rule changes, mirror it here.
## Archive index — `Archive.md` template
A separate page that lists every archived page with its archival date and the era it describes.
```
<!-- BEGIN TEMPLATE — Archive.md -->
# Archive — pre-refactor wiki pages
Pages kept for historical reference. **Do not update them.** Corrections go on the current page; if you find a meaningful inaccuracy in the archived page, leave it and add a note to the *current* page explaining what changed.
| Page | Era | Archived on |
|---|---|---|
| [pumpingStation (pre-refactor)](Archive/pumpingStation-pre-refactor) | Pre-Tier-2 (May 2026) | 2026-05-11 |
| [rotatingMachine (pre-refactor)](Archive/rotatingMachine-pre-refactor) | Pre-Tier-3 (May 2026) | 2026-05-11 |
| ... | ... | ... |
Each archived page carries the standard banner at its top (see `WIKI_TEMPLATE.md` → Archive banner).
<!-- END TEMPLATE -->
```

View File

@@ -1,48 +1,83 @@
# Wiki page template — every node uses this shape
This is the canonical structure for every node's gitea wiki landing
page. Visual-first, scannable, ≤ 60 words of unbroken prose anywhere.
Canonical structure for every node's Gitea wiki landing page. **Visual-first**, scannable, ≤ 60 words per paragraph anywhere on the page.
## Why this shape
The platform now has 12 nodes with the same architectural skeleton
(BaseDomain + BaseNodeAdapter + ChildRouter + commands registry).
The wiki should mirror that uniformity: a reader can flip between
nodes and find the same sections in the same order.
The platform has 12 nodes that all share the same architectural skeleton (BaseDomain + BaseNodeAdapter + ChildRouter + commands registry). The wiki should mirror that uniformity: a reader flips between nodes and finds the same 14 sections in the same order. Diagrams lead. Tables annotate. Prose only fills gaps.
Mermaid blocks lead. Tables annotate. Prose only fills gaps.
## Picking a visual
## Template — copy this whole file as the seed for each node's wiki
The default is Mermaid (Gitea renders it natively). It's the right tool for graph-shaped things — neighbours, lifecycles, state machines, file maps. But Mermaid doesn't render data: when a section is about *what a curve looks like* or *what the predicted vs measured signal does over time*, use:
| Need | Tool | Where the artifact lives |
|---|---|---|
| Graph (nodes + edges, hierarchy, state) | Mermaid `flowchart` / `sequenceDiagram` / `stateDiagram-v2` | inline in the wiki page |
| XY data (pump curves, prediction trace, drift over time) | Generated PNG/SVG via a small `npm run wiki:plots` script | committed under `wiki/_partial-plots/<NodeName>/*.svg` |
| Table of facts / config / topics | Markdown table | inline |
| Screenshot (dashboard, editor form) | PNG ≤ 200 KB | `wiki/_partial-screenshots/<NodeName>/*.png` |
| ASCII layout (when Mermaid is overkill) | code block | inline |
Lead with the visual that serves the section. Don't gate it on "is this Mermaid".
## Section list
Sections 19 and 1114 are mandatory for every node. Section 10 (State chart) is mandatory for stateful nodes (`rotatingMachine`, `valve`, `pumpingStation`, …) and skipped for pure aggregators (`measurement`, `dashboardAPI`).
| # | Section | Visual lead | Auto-gen? |
|---|---|---|---|
| 0 | Header band (git hash + regen date) | — | yes |
| 1 | What this node is | — (single paragraph) | no |
| 2 | Position in the platform | Mermaid `flowchart LR` | no |
| 3 | Capability matrix | table | no |
| 4 | Code map | Mermaid `flowchart TB` w/ subgraphs | no |
| 5 | Topic contract | table | **yes** (`wiki:contract`) |
| 6 | Child registration | Mermaid + table | no |
| 7 | Lifecycle | Mermaid `sequenceDiagram` | no |
| 8 | Data model — `getOutput()` | table + concrete sample | **yes** (`wiki:datamodel`) |
| 9 | Configuration — form ↔ config | Mermaid `flowchart TB` | no |
| 10 | State chart (stateful only) | Mermaid `stateDiagram-v2` | no |
| 11 | Examples | table + screenshots | no |
| 12 | Debug recipes | table | no |
| 13 | When NOT to use this node | bullets | no |
| 14 | Known limitations | table | no |
## Template — copy the block below as the seed for each node's wiki
(The block uses standard markdown syntax. The outer fence below is for visual delimitation in this README only; when seeding a new wiki page, copy the *content* between the `BEGIN TEMPLATE` / `END TEMPLATE` markers verbatim.)
```
<!-- BEGIN TEMPLATE — wiki/<NodeName>.md -->
```markdown
# <Node name>
> One-paragraph "what is this node". Cap 60 words. Plain English.
> Example: "rotatingMachine models a single pump or compressor —
> takes pressure measurements and predicts flow + power from supplier
> curves. State machine drives startup/shutdown sequences."
> **Reflects code as of `<git short hash>` · regenerated `<YYYY-MM-DD>` via `npm run wiki:all`**
> If this banner is stale, the page may be out of date. Treat as informative, not authoritative.
## 1. Position in the platform
## 1. What this node is
```mermaid
One paragraph, ≤ 60 words. Plain English. State the *role*, not the *implementation*.
> Example: "**rotatingMachine** models a single pump or compressor. It takes pressure measurements from upstream and downstream, predicts the resulting flow + power from supplier-provided characteristic curves, and drives a state machine for startup/shutdown sequences. Used as a child of `machineGroupControl` when grouped, or directly under a `pumpingStation`."
## 2. Position in the platform
~~~mermaid
flowchart LR
parent[<Parent type — e.g. machineGroupControl>] -->|set.demand| this[<This node>]
parent[machineGroupControl<br/>Unit]:::unit -->|set.demand| this[rotatingMachine<br/>Equipment]:::equip
this -->|evt.state-change| parent
sensor[<measurement>] -->|data.flow| this
sensor_up[measurement up]:::ctrl -->|data.pressure| this
sensor_dn[measurement down]:::ctrl -->|data.pressure| this
this -->|child.register| parent
classDef proc fill:#0c99d9,color:#fff
classDef unit fill:#50a8d9
classDef equip fill:#86bbdd
classDef ctrl fill:#a9daee
class this <classname-by-S88-level>
```
classDef unit fill:#50a8d9,color:#000
classDef equip fill:#86bbdd,color:#000
classDef ctrl fill:#a9daee,color:#000
~~~
The Mermaid block shows the typical neighbours and the data direction.
Pick the right `classDef` for the S88 level (Process Cell / Unit /
Equipment / Control Module). Match the colour codes from
`.claude/rules/node-red-flow-layout.md`.
S88 colours are mandatory. Map: Process Cell `#0c99d9`, Unit `#50a8d9`, Equipment `#86bbdd`, Control Module `#a9daee`. Source of truth: `.claude/rules/node-red-flow-layout.md`.
## 2. Capability matrix
## 3. Capability matrix
| Capability | Status | Notes |
|---|---|---|
@@ -50,54 +85,141 @@ Equipment / Control Module). Match the colour codes from
| Receives manual setpoint | ✅ | Topic `set.setpoint` |
| Auto-start on demand from parent | ✅ | |
| Self-calibrating | ❌ | Calibration is operator-triggered (`cmd.calibrate`) |
| Supports multi-parent registration | ⚠️ | Possible but not fully tested — see CONTRACT.md notes |
| Supports multi-parent registration | ⚠️ | Possible but not fully tested — see CONTRACT.md |
Keep this table to ≤ 10 rows. For longer feature inventories, link out.
Cap at 10 rows. Longer inventories link out.
## 3. Topic contract
## 4. Code map
> Auto-generated from `src/commands/index.js`. Do NOT hand-edit.
> Re-run `npm run wiki:contract` after any change to commands.
~~~mermaid
flowchart TB
subgraph nodeRED["nodeClass.js — adapter (BaseNodeAdapter)"]
nc["buildDomainConfig()<br/>static DomainClass, commands"]
end
subgraph domain["specificClass.js — orchestrator (BaseDomain)"]
sc["Machine.configure()<br/>declares ChildRouter rules"]
end
subgraph concerns["src/ concern modules"]
curves["curves/<br/>characteristic curve loader"]
prediction["prediction/<br/>flow + power predictor"]
drift["drift/<br/>prediction-vs-measured assessor"]
flow["flow/<br/>aggregation + smoothing"]
state["state/<br/>FSM transitions"]
io["io/<br/>output formatting helpers"]
display["display/<br/>status badge composition"]
end
nc --> sc
sc --> concerns
~~~
| Module | Owns | Read first if you're changing… |
|---|---|---|
| `curves/` | Supplier characteristic curves, interpolation | Curve fitting, asset selection |
| `prediction/` | Flow + power predictors | Predicted output values |
| `drift/` | Quality of prediction vs measurement | Health status / alarms |
| `flow/` | Aggregation, smoothing | Flow reporting |
| `state/` | FSM (off → idle → operational → …) | Startup / shutdown behaviour |
Update this section when you rename or split a directory.
## 5. Topic contract
> **Auto-generated** from `src/commands/index.js`. Do NOT hand-edit between the markers. Re-run `npm run wiki:contract`.
<!-- BEGIN AUTOGEN: topic-contract -->
| Canonical topic | Aliases | Payload | Effect |
|---|---|---|---|
| `set.mode` | `setMode` | `string` (`auto` \| `manual` \| `maintenance`) | Switches operating mode. |
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) | `{source: string}` | Triggers startup sequence (state machine). |
| ... | ... | ... | ... |
| `set.mode` | `setMode` | `string` (`auto`\|`manual`\|`maintenance`) | Switches operating mode. |
| `cmd.startup` | `execSequence` (with `payload.action='startup'`) | `{source: string}` | Triggers startup sequence. |
## 4. Lifecycle — what one tick (or event) does
<!-- END AUTOGEN: topic-contract -->
```mermaid
## 6. Child registration
What children this node accepts and what it does with each event the child can emit. Mirrors the `ChildRouter` declarations in `specificClass.js` → `configure()`.
~~~mermaid
flowchart LR
subgraph kids["accepted children (softwareType)"]
m_up["measurement<br/>type=pressure<br/>position=upstream"]:::ctrl
m_dn["measurement<br/>type=pressure<br/>position=downstream"]:::ctrl
end
m_up -->|data.pressure| handler1[pressure handler<br/>updates measurements/upstream]
m_dn -->|data.pressure| handler2[pressure handler<br/>updates measurements/downstream]
handler1 --> recompute[prediction.recompute]
handler2 --> recompute
recompute --> emit[emitter.emit 'output-changed']
classDef ctrl fill:#a9daee,color:#000
~~~
| softwareType | filter | wired to | side-effect |
|---|---|---|---|
| `measurement` | `type=pressure, position=upstream` | `pressureHandlers.onUpstream` | prediction recomputes |
| `measurement` | `type=pressure, position=downstream` | `pressureHandlers.onDownstream` | prediction recomputes |
## 7. Lifecycle — what one event (or tick) does
~~~mermaid
sequenceDiagram
participant parent
participant node as this node
participant sensor as measurement child
participant out as Port-0 output
sensor->>node: data.flow (12 m³/h, downstream)
node->>node: route → measurementHandlers
node->>node: drift assess → predictionHealth update
node->>node: getOutput composes snapshot
sensor->>node: data.pressure (3.4 bar, upstream)
node->>node: ChildRouterpressure handler
node->>node: prediction recomputes
node->>node: drift assesses prediction vs measured
node->>node: getOutput() composes snapshot
node->>out: msg{topic, payload, [process|influx]}
parent->>node: set.demand (15 m³/h)
node->>node: control.dispatch
node->>parent: child.register (handshake)
```
node->>node: state.handleInput → maybe transition
~~~
Keep the diagram to one screen. If you have multiple distinct flows
(idle vs running vs error), pick the most common one and link out
to the others.
One screen max. For multiple distinct flows (idle vs running vs error), pick the most common and link out to the rest.
## 5. Configuration — editor form & where each field lands
## 8. Data model — `getOutput()`
```mermaid
What lands on Port 0. Composed in domain `getOutput()`, then delta-compressed by `outputUtils.formatMsg`.
**Abstract schema** (always include):
<!-- BEGIN AUTOGEN: datamodel-schema -->
| Key | Type | Unit | Source |
|---|---|---|---|
| `<type>.<variant>.<position>.<childId>` | number | per `UnitPolicy.output(type)` | MeasurementContainer |
| `state` | string | — | `state/` |
| `predictionHealth.level` | 03 | — | `drift/` |
| `predictionHealth.flags` | string[] | — | `drift/` |
<!-- END AUTOGEN: datamodel-schema -->
**Concrete sample** (include only when the *shape* is hard to grok from the schema — e.g. nested objects, sparse keys, or unit conventions a newcomer would get wrong):
~~~json
{
"flow.measured.downstream.default": 12.4,
"pressure.measured.upstream.default": 3.4,
"power.measured.atequipment.default": 18.2,
"state": "operational",
"predictionHealth": { "level": 1, "flags": ["pressure_init_warming"], "message": "warmup phase", "source": "rotatingMachine#pump-A" }
}
~~~
Concrete samples must come from a known-good test run — never made-up values. Regenerate when concern modules change shape.
## 9. Configuration — editor form ↔ config keys
~~~mermaid
flowchart TB
subgraph editor["Node-RED editor form"]
f1[Mode]
f2[Demand]
f3[Threshold percent]
f1[Mode dropdown]
f2[Demand input]
f3[Threshold %]
end
subgraph config["Domain config"]
subgraph config["Domain config slice"]
c1[control.mode]
c2[control.targets.demand]
c3[safety.thresholdPercent]
@@ -105,28 +227,17 @@ flowchart TB
f1 --> c1
f2 --> c2
f3 --> c3
```
~~~
| Form field | Config key | Default | Range |
|---|---|---|---|
| Mode | `control.mode` | `auto` | enum |
| Demand | `control.targets.demand` | `0` | ≥ 0 |
| Threshold % | `safety.thresholdPercent` | `95` | 0100 |
| Form field | Config key | Default | Range | Where used |
|---|---|---|---|---|
| Mode | `control.mode` | `auto` | enum | `control/strategies.js` |
| Demand | `control.targets.demand` | `0` | ≥ 0 | `dispatch/` |
| Threshold % | `safety.thresholdPercent` | `95` | 0100 | `safety/guards.js` |
## 6. Examples
## 10. State chart (stateful nodes only)
| Tier | File | What it shows |
|---|---|---|
| Basic | `examples/01-Basic.json` | Inject + dashboard, no parent |
| Integration | `examples/02-Integration.json` | Wired to `<parent>` + 1 measurement child |
| Dashboard | `examples/03-Dashboard.json` | Live FlowFuse charts of flow / power / state |
Include one screenshot per tier where helpful — keep them under 200 KB
each. Link to a runnable docker compose snippet under `examples/README.md`.
## 7. State chart (only for stateful nodes)
```mermaid
~~~mermaid
stateDiagram-v2
[*] --> off
off --> idle: cmd.startup
@@ -136,78 +247,100 @@ stateDiagram-v2
coolingdown --> off: cooldown_time elapsed
operational --> emergencystop: cmd.estop
emergencystop --> off: cmd.reset
```
~~~
Skip this section for stateless / pure-aggregator nodes (e.g. measurement,
dashboardAPI).
Skip this section for stateless nodes (`measurement`, `dashboardAPI`).
## 8. When you would NOT use this node
## 11. Examples
- "Use rotatingMachine for a SINGLE pump. For groups of 2+ pumps with
load sharing, use machineGroupControl as the parent."
- "Use measurement only for sensor signal conditioning. For setpoint
injection, use the dashboard or a parent's command topic."
| Tier | File | What it shows | Mandatory? |
|---|---|---|---|
| Basic | `examples/01-Basic.json` | Inject + dashboard, no parent | ✅ |
| Integration | `examples/02-Integration.json` | Wired to `<parent>` + 1 child | ✅ if has parent |
| Dashboard | `examples/03-Dashboard.json` | Live FlowFuse charts | ⭕ optional |
Two or three bullets, each one sentence. Forces explicit non-goals.
One screenshot per tier where helpful. PNG ≤ 200 KB under `wiki/_partial-screenshots/<NodeName>/`. Docker compose snippet under `examples/README.md`.
## 9. Known limitations / current issues
## 12. Debug recipes
How to diagnose the common failure modes. One table row per recipe.
| Symptom | First thing to check | Where to look |
|---|---|---|
| Status badge stuck on `⚠ no input` | Did the measurement child register? Watch Port 2. | Editor debug tap on Port 2 |
| `flow.measured.downstream` not updating | Confirm the child's emitted topic matches the `ChildRouter` filter. | `specificClass.js` → `configure()` |
| Prediction `level=3` | Run `enableLog: 'debug'` *temporarily*; look for drift evaluator output. | container log |
> Never ship `enableLog: 'debug'` in a demo — fills the container log within seconds and obscures real errors. Use only for live debugging.
## 13. When you would NOT use this node
Two or three bullets, one sentence each. Forces explicit non-goals.
- Use rotatingMachine for a **single** pump. For groups of 2+ pumps with load sharing, use `machineGroupControl` as the parent.
- Don't use rotatingMachine to model a passive non-return valve — use `valve` (no curve, no FSM-driven motor).
## 14. Known limitations / current issues
| # | Issue | Tracked in |
|---|---|---|
| 1 | Drift confidence drops to 0 when pressure is missing for > 30 s | `OPEN_QUESTIONS.md` 2026-05-10 |
| 2 | Multi-parent teardown ordering | `OPEN_QUESTIONS.md` 2026-05-10 |
| 1 | Drift confidence drops to 0 when pressure missing > 30 s | `.claude/refactor/OPEN_QUESTIONS.md` |
| 2 | Multi-parent teardown ordering | Gitea issue #42 |
Link to repo issues when they exist. Keep this table living — it's the
contract with the user about what "works".
Link to repo issues when they exist. Keep this table living — it's the contract with the user about what "works".
<!-- END TEMPLATE -->
```
## Hard rules for editors
1. The Mermaid platform-position block is **section 1**, before any
prose. Diagrams first.
2. Each section opens with a diagram or table; prose annotates the
visual, not the other way round.
3. No more than **60 words of unbroken prose** anywhere on a page.
4. The topic contract is **auto-generated** from `src/commands/index.js`.
Don't hand-write it.
5. Use `flowchart LR` for node connections, `sequenceDiagram` for
tick-to-output flow, `stateDiagram-v2` for state machines.
6. Skip `classDiagram` (we don't expose classes to users) and `gantt`
(no schedules in node docs).
1. Section 2 (Position in the platform) appears **before any prose**. Diagrams lead.
2. Every section opens with a diagram, table, or chart. Prose annotates the visual; never the other way round.
3. **Max 60 words per paragraph.** A paragraph longer than that splits into bullets or moves into a table.
4. The topic contract (section 5) and data-model schema (section 8) are **auto-generated** between the `BEGIN AUTOGEN` / `END AUTOGEN` markers. Don't hand-edit between markers.
5. Mermaid is the default for graph structures. Use generated SVG/PNG for XY data (curves, time series). Use tables for facts.
6. Skip `classDiagram` (we don't expose classes to users) and `gantt` (no schedules in node docs).
7. **Concrete sample payloads must come from a known-good test run.** Made-up numbers rot silently.
8. S88 colour codes are non-negotiable in section 2. Match the palette in `.claude/rules/node-red-flow-layout.md`.
## Auto-generation notes (Phase 9 follow-up)
## Archive banner — paste at the top of every archived page
The "Topic contract" table in section 3 should be generated by a small
`npm run wiki:contract` script, per node. Implementation:
```js
// scripts/generate-contract.js
const commands = require('../src/commands');
const rows = commands.map((c) => ({
topic: c.topic,
aliases: (c.aliases || []).join(', ') || '—',
payload: describeSchema(c.payloadSchema),
effect: c.description || '— (handler-only)',
}));
console.log(toMarkdownTable(rows));
```
> **⚠️ ARCHIVED — pre-refactor (Tier 14, 2026-05)**
>
> This page describes the architecture before the platform refactor.
> The current page is **[<NodeName>](../<NodeName>)**.
>
> Kept for historical reference only. **Do not update.**
```
`describeSchema` walks the lightweight schema (`{type, properties}`)
and produces a one-line readable form. Wire each node's `package.json`:
Archived pages move to `Archive/<NodeName>-pre-refactor.md` in the Gitea wiki repo. After moving, the page is read-only — corrections go on the current page, not the archive.
## Auto-generation — Phase 9 follow-up
Two scripts per node, wired in `package.json`:
```json
"scripts": {
"wiki:contract": "node scripts/generate-contract.js > wiki/_partial-topics.md"
"wiki:contract": "node scripts/generate-contract.js > wiki/_partial-topics.md",
"wiki:datamodel": "node scripts/generate-datamodel.js > wiki/_partial-datamodel.md",
"wiki:all": "npm run wiki:contract && npm run wiki:datamodel"
}
```
Then a thin wiki page `<Node>.md` includes `_partial-topics.md` (or
the section is regenerated in-place between markers).
- **`generate-contract.js`** walks `src/commands/index.js`, emits one table row per descriptor between the topic-contract markers.
- **`generate-datamodel.js`** instantiates the domain with the default config, calls `getOutput()`, emits the abstract schema between the datamodel-schema markers. If `wiki/sample-output.fixture.json` exists, the concrete-sample block below the markers is also overwritten.
- `describeSchema` walks the lightweight `{type, properties}` schema and produces a one-line readable form.
## What lives in the gitea wiki vs the repo
## What lives where
- `wiki/` directory in the repo (where present): canonical, version-
controlled.
- gitea wiki UI: cosmetic mirror — re-rendered from `wiki/` on push.
- Auto-generated partials (`_partial-*.md`): committed under `wiki/`
but generated; never hand-edited.
| Artifact | Location | Hand-edited? |
|---|---|---|
| Canonical page source | `wiki/<NodeName>.md` in the node's repo | Yes (except inside AUTOGEN markers) |
| Auto-generated partials | written inline between AUTOGEN markers | No — generated |
| Plots | `wiki/_partial-plots/<NodeName>/*.svg` | No — generated |
| Screenshots | `wiki/_partial-screenshots/<NodeName>/*.png` | Yes (committed) |
| Gitea wiki UI | mirror — re-rendered from `wiki/` on push | No |
| Archived pre-refactor pages | `Archive/<NodeName>-pre-refactor.md` in the wiki repo | No (read-only after archival) |
The Gitea wiki repo is separate from each node's source repo. The `wiki/` directory in each node's repo is canonical; a `wiki-sync` workflow (not yet built) mirrors it into the Gitea wiki repo on each push to `development` / `main`.