Repository navigation
fix(nodes): one fault order, add all and un-hide all can't double-run or stop early, dialogs show the current board - #153
Merged
Conversation
…he log X6 / R8 (#133). The log listed a board's own flags as bus-off, error-passive, degraded, overheat; the Cockpit as bus-off, fault, overheat, error-passive. - nodes/faults.ts owns the one order (NODE_FLAG_ORDER) and the helpers built on it: raisedNodeFlags, nodeFlagWords (Adopt evidence), nodeFlagChips (tile and Attention). - The event log walks NODE_FLAGS; the tile, the status boxes and the Adopt evidence read the same order. STATUS_FLAGS (events) is now a vocabulary by flag, with no order. - The PDU's Cockpit flag text reuses the log's tables instead of a second copy.
…ialogs show the current board #133 items R6, N4, R9 / R10 and C5. - R6: one busy flag for the whole Adopt dialog; done updates are functional. Main's MappingSequences ignores a second Add all / Un-hide all while the same one runs. - N4: Un-hide all tries every board; NodeBatchResult's refused case gained `failed`, and Hidden boards lists each board it couldn't un-hide. - R9: the reset review re-fetches only when the mapping it is built from changes, and drops an answer a newer fetch superseded. - R10: the Adopt dialog's count is the "New on the bus" button's; each request to open it starts it over on the board asked for. - C5: every model main hands out carries a rising rev; the renderer drops an older model or Cockpit snapshot than the one it holds.
mottihoresh
added a commit
that referenced
this pull request
Oct 17, 2026
…, one log of the physical bus (#154) Fix-track chunk 4 (#133), the new-boards and event-log items. Behaviour changes on purpose (decided 2026-10-16); nothing else does. Rebased on `fix/chunk-4` after #153 (one fault order); its changes are kept as they are. ## 1. D1 / X3 / R2: strays on every tab | | Today | After | |---|---|---| | Strays | The 5 s "UNIDENTIFIED" (or SECOND GATEWAY / UNKNOWN BOARD TYPE) warning counted on the Cockpit only. The Overview tile read "no nodes known", with no banner; Connected nodes had no row. | One `newState(entry, now, live)` in `node-model.ts`: `news` (adoptable), `identifying` (with `strayAt`), `stray`, or none. `isReportableNew` is built on it, and `attention.ts strayNew` reads it. `buildCockpitSnapshot` hands the same `strays` to `overviewSummary` and `nodesSummary`. The Cockpit, the Overview (tile and banner) and Connection (counts plus a stray row with chip `… · can’t be adopted`, no zone) all count it after 5 s. `NodesSummary.known` is the band's total, so a lone stray isn't hidden behind "No nodes reported". | `IDENTIFY_WAIT_MS` and `strayKind` moved into `node-model.ts`. `strayFlag` is `strayKind` in capitals. ## 2. S6: grace from coming online | | Today | After | |---|---|---| | Grace | Counted from `firstSeenAt`, which an absent roster row also sets. A board listed absent and coming online later was flagged at once. | `NodeRoster` keeps `NodeSeen.onlineSince`: set on a present row after an absent one (or the first present row), cleared when the board goes absent (delta or END). The model copies it onto new entries (`NodeModelEntry.onlineSince`), and `newState` counts the grace from it. | ## 3. X8 / C6: "from NODE_IDS.md" wording | | Today | After | |---|---|---| | A registry board waiting to be added | `nodeHealth` flag `NOT IN REGISTRY`, the `nodeFaultBoxes` box "Not in registry", log detail `in NODE_IDS.md` / `not in registry`. The Overview already said "from NODE_IDS.md". | One `newReason(entry)` in `node-model.ts`: `from NODE_IDS.md` / `not in NODE_IDS.md`. The flag is `FROM NODE_IDS.md` / `NOT IN NODE_IDS.md`. The box title is `From NODE_IDS.md` (a registry board's text no longer says it isn't listed). The log's "new on the bus" detail reads the same. | A probe test (`main/nodes/phase0-probes` "X8 / C6") scans every `.ts`/`.tsx` in `src` for `not in registry` (any case) and the old log literal `'in NODE_IDS.md'`, and fails on a hit. It was shown to fail when `// NOT IN REGISTRY` was appended to `views.ts`. **Left alone:** `AdoptDialog.tsx`'s queue label `<name> · in NODE_IDS.md` is the renderer's own wording for a registry board in the Adopt queue. It was out of scope (that dialog was another PR's file), so it isn't in the probe. ## 4. D8 / S5 / C4: the log is the physical bus | | Today | After | |---|---|---| | Boards outside your cockpit | Their offline / bus-off / flag rows were errors and warnings like your own boards' rows. A stray never got any row. | Every board on the bus is still logged. A row about a board outside your cockpit is at **info**, with the detail suffix `not in your cockpit` (one `row()` in the tracker). A stray gets one `new on the bus · <unidentified \| second Gateway \| unknown board type> · can’t be adopted` row at info once `newState` says `stray`. If it later identifies as adoptable, it gets its `new` row then. | | "N online" | Counted every board, with nothing to tell them apart. | Still counts the whole bus, now with `· K not in your cockpit` when some aren't yours. | | "New" | Re-derived in NodeHub (`isNew`: not known and an adoptable `kindOf`). Only checked on status rows, so a board identified only by telemetry got no row until its next roster row. | `announce()` reads `newState` from the model entry, built at most once per line, so it isn't re-derived. It is checked on status **and** telemetry lines: a PDU identified by its pages gets its row at those pages. | | "Seen new" record | Survived Forget, so Forget then re-plug logged nothing new. | `NodeEventTracker.cockpitChanged()` runs after every successful mapping change. It drops the record for boards now in your cockpit, so a forgotten board that returns is new again. | | Telemetry-only boards | No last seen, and no online / offline rows. | NodeHub's `busTransitions` (was `rosterTransitions`) uses `onBusIds`: the one `availabilityOf` rule over roster ∪ telemetry. It runs on roster changes and on page takes, so last seen and un-hide follow pages too. `pagesOnline()` gives a board online by its pages alone (`onlineByPages`) `online` / `offline · no telemetry` rows. If the roster later lists it, no second `online`. | | Degraded | A new fault id while still degraded logged another `degraded` row. | Once per episode: raised, then cleared. | ## Tests changed on purpose | Test | Was | Now | Why | |---|---|---|---| | T1 `the Overview does not count the same stray (X1 today)` | "no nodes known", no banner | Banner `1 node needs attention — 1 warning (NODE_ID 9)` | D1 | | T1 `the 5 s grace counts from firstSeenAt … (F26 today)` | Stray at once | No stray at first, a stray 5 s after `onlineSince` | S6 / F26 | | T3 `the Overview doesn’t count a stray (X1 today)` | `—` / "no nodes known" | Tile `1 / 1`, banner names NODE_ID 9 | D1 | | T4 `a board still identifying, or one that can’t be adopted, is not mentioned` | No status role at all | Notice still absent; the banner names the stray (fake clock at T0 so #9 is still identifying) | D1: the stray is a warning, not news | | T34 the agreement cases (header + 2 tests) | Overview / Connection without the stray | All three count it (`4 / 7`, Connection 2/2/2 with the stray's row; next push `6 / 7`, 5/1/0) | D1 | | `on-the-bus.test` link up | Connected nodes `known 2`, rows `[2]` | `known 3, warn 1`, rows `[2, 9]` | D1 (Connection strays-absent case) | | `overview-summary.test` `… without strays` | Band and items without strays | Same equality, `strays` passed; plus a new stray case | D1 | | events `lookup()` stub (and the #153 `faults.test` stub, call-site only) | `isNew` / `unmapped` / `inRegistry` | `outside` / `entry` (the model entry) | "New" from `newState` | | events: registry / not-in-registry details | `in NODE_IDS.md` / `not in registry` | `from NODE_IDS.md` / `not in NODE_IDS.md` | X8 | | events `reports a new node once per app run…` | `roster: 1 node online` | `… · 1 not in your cockpit` | D8 label | | events `a new node coming back in a later port session…` | `online [info]`, `offline · no heartbeat [error]` | `… · not in your cockpit [info]` | D8 | | events `degraded … a new fault id is a new row` | Second row for fault 9 | No row; after a clear, a new episode logs | Degraded once per episode | | `node-hub.seam` B33 | No row at the pages; one at the next status row | One row at the pages, none after | C4 | | main `phase0-probes` C4 | Same as B33 | Fixed; plus Forget → re-plug logs "new" again; plus the X8 source probe | C4 / X8 | | renderer `phase0-probes` D1, S6, C6 | Today's behaviour | The fixed behaviour | D1 / S6 / X8 | | `node-hub.test` S1 kind stored | `{ added, kind }` after PDU pages | Also `lastSeen` | Telemetry-only boards update last seen | | `node-status-wire.test` seen | `{ firstSeenAt, reportedAt }` | Includes `onlineSince` (reset on coming back) | S6 | | T22 log fixture | `detail: 'in NODE_IDS.md'` | `from NODE_IDS.md` | Fixture mirrors main's wording; the X8 probe covers it | **Inputs only, assertions unchanged:** `scenarios.newBoard` also sets `onlineSince` (its doc already said "online since firstSeenAt"), and the `adopt.test` / `cockpit-host.test` / `on-the-bus.test` stray fixtures set `onlineSince`. `nodesSummary` / `overviewSummary` gained the `strays` argument (`[]` in the old cases). The **NODE_ID 0 overview cases** (`takes / treats NODE_ID 0 like any other node`) are unchanged and still pass. Nothing here changes how the summaries treat a NODE_ID 0 entry, and the model has excluded node 0 since #344. ## New tests - `node-model.test`: `newState` (news / stray / identifying with `strayAt`, none off the bus or unplugged), `onlineSince` on the model, `newReason`, `strayKind`, `onlineByPages`. - `node-status-wire.test`: `onlineSince` cleared by absent row or END, reset on coming back. - `adopt.test`: grace from `onlineSince`. - `overview-summary.test` and `connection-summary.test`: a stray counted with its row. - events: outside rows at info and labelled; baseline count; stray announcements; stray → news; telemetry announce; `cockpitChanged`; `pagesOnline`. - New `main/nodes/new-boards.seam.test.ts`, through `makeServices()` over the fake serial port: - a stray on all three tabs after 5 s; - the grace from coming online; - the `FROM NODE_IDS.md` flag and log row; - outside rows at info and the roster label; - Forget → re-plug logs new again; - a pages-only PDU's last seen and its online / offline rows. ## How verified - `npm run lint`, `npm run lint:deps`, `npm run typecheck`, `npm test`: all green (1434 tests). - Not run on hardware. The chunk's hardware checklist is in #133. API guard: header note only. `NodeModelEntry` and `NodeSeen` gained optional `onlineSince`; there is no channel or intent change.
mottihoresh
added a commit
that referenced
this pull request
Oct 18, 2026
…of the physical bus, safe add all and un-hide all (#155) Chunk 4 of the fix track (#135): new boards, event log and cockpit actions. Closes #133. Item PRs squash-merged into `fix/chunk-4`: - #153 — one fault order (bus-off → fault → overheat → error-passive) in the Log and the Cockpit; Add all can't double-run (dialog busy flag + main re-entry guard); Un-hide all goes on past a failure and lists every board it couldn't un-hide; Reset preview refetches only when the mapping changes; Adopt opens on the board asked for and its count matches the button; an older node model or cockpit snapshot never replaces a newer one. - #154 — one `newState`: the unidentified warning counts on Cockpit, Overview and Connection after 5 s; the 5 s grace counts from when a board comes online (reset on each offline → online); "from NODE_IDS.md" wording everywhere (one `newReason`, probe test); the event log is the physical bus — boards outside your cockpit logged at info with "· not in your cockpit", a telemetry-only board gets its "new" row and online/offline rows, "new" is worked out once and cleared on Forget, degraded is logged once per episode. Tested on the rig before merge (checklist in #133). BEGIN_COMMIT_OVERRIDE fix(nodes): one fault order, add all and un-hide all can't double-run or stop early, dialogs show the current board fix(nodes): new boards counted on every tab, grace from coming online, one log of the physical bus END_COMMIT_OVERRIDE
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of fix-track chunk 4 (#133). It covers the items X6 / R8, R6, N4, R9 / R10 and C5, as decided on 2026-10-16. The new-board state and event-log items (D1 / X3 / R2, S6, X8 / C6, D8 / S5 / C4) are in a separate PR. Nothing else changes.
1. X6 / R8: one fault order
views.ts,panels.ts,adopt.ts) and the log's walk overSTATUS_FLAGS.nodes/faults.ts NODE_FLAG_ORDERand its helpers (raisedNodeFlags,nodeFlagWords,nodeFlagChips,NODE_FLAGS). The log (node-events.ts), the tile and Attention (views.ts), the status boxes (panels.ts) and the Adopt evidence (adopt.ts) all read them.events/status-flags.ts STATUS_FLAGSwas an ordered list.gateway-health.tslooks labels up by key and keeps its own documented Gateway order.The PDU flag text in
faults.tswas written out twice: once for the Cockpit and once for the log, in the same order. The Cockpit now reuses the log's tables, so its output is unchanged.2. R6: Add all can't double-run
donewas set from a stale closure.AdoptDialog. A ref refuses a second click before React re-renders. While Add all or Adopt runs, Add all, Later and Adopt / Add to cockpit are all disabled.doneupdates are functional.addAllInTurncould run again while one was running.mapping-sequence.ts MappingSequences(NodeHub and the component mock) ignores a second Add all, or a second Un-hide all, while the same one is running. It answers{ ok: true, done: [] }. The two sequences don't block each other.3. N4: Un-hide all goes on and reports
NodeBatchResultgainsfailed: every refusal, in order.nodeIdanderrorare still the first. Add all still stops at its first refusal, with one entry.set-err) lists each failure:Couldn’t un-hide #8 Right_Radar: <reason>. A single Un-hide still shows main's error alone. No new UI element.The API guard header (
ipc.compat.test-d.ts) records the change to the type.4. R9 / R10: the dialogs show the current board and count
nodes:modelpush (roster polls, PDU pages), with no stale guard.settings-view.ts resetPreviewKey: the mapping as the snapshot shows it (each board's name, console, PDU and source, the hidden ones, and the differing count). An answer that a newer fetch superseded is dropped.attention.newCount, the same field as the button.key) on the board asked for, as the latest push has it.5. C5: an older snapshot never wins
revon every model it pushes (NodeModel.rev, optional). Thenodes:getanswer carries the rev of the last push.nodes/store.tsdrops anodes:getanswer or anodes:modelpush that is older than the model it holds. It also drops acockpit:snapshotwhose rev is older than the one it holds.useNodesPulleduses its own pull only as a "data arrived" signal and writes nothing, so no change there. The mapping intents'modelanswers aren't read by the renderer. Everything the screens show comes from the snapshot, which is now rev-guarded.Tests changed on purpose
events.testraises and clears bus-off, error-passive (with TEC), overheatmapping-sequence.testunhideInTurn … stops at the first refusalfailedmapping-sequence.testAdd all refusalfailedfailed: [{ 7 }]node-hub.testAdd all / Un-hide allfailed; read-only un-hide stopped at #1failedadded; #2 is tried after #1lists every new board…New tests
nodes/faults.test.ts: a board with several flags. The log rows, the status boxes, the Adopt evidence and the tile chip all come out in the one order.nodes/mapping-sequence.test.ts:MappingSequencesignores re-entry, is free again after it finishes or after a refusal, and the two sequences are independent.nodes/mapping-sequences.seam.test.ts(viamakeServices, which gained the add-all and unhide intents):node-hub.test: every model pushed carries a rising rev, andnodes:getanswers the last push's rev.ipc-adapter.test: a pull answered behind a newer push is dropped, and so is an older Cockpit snapshot.How verified
npm run lint,npm run lint:deps,npm run typecheckandnpm testall pass (1409 tests).AdoptDialog;ResetDialog/CockpitSection;Cockpit.tsx;store.ts.STATUS_FLAGSwalk.