Skip to content

fix: characterization-pass follow-up defects (#149, #150, #155, #156)#201

Merged
mairas merged 4 commits into
mainfrom
fix/characterization-followup-defects
Jul 4, 2026
Merged

fix: characterization-pass follow-up defects (#149, #150, #155, #156)#201
mairas merged 4 commits into
mainfrom
fix/characterization-followup-defects

Conversation

@mairas

@mairas mairas commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

What

Fixes four correctness defects surfaced by the #27 characterization pass (PR #147/#146). Each fix intentionally re-pins the characterization test that previously asserted the buggy behavior.

Not included: #148

#148 (prev/next navigation inverted) was requested but is already fixed on main by c5b858c4 (2026-07-04), which corrected both cycling methods and their navigateTo* siblings, did the caller audit, and re-pinned the direction assertions. That commit recorded Closes #184, leaving #148 open as a stale duplicate — it should just be closed.

Verification

  • npm run lint — clean.
  • ng test on the three affected specs — 61/61 pass. Each updated/new assertion is a genuine regression catch (fails on the pre-fix code).
  • npm run build:dev — clean.

CI runs the full matrix (lint, vitest 20/22/24, build 20/22/24).

🤖 Generated with Claude Code

@mairas

mairas commented Jul 4, 2026

Copy link
Copy Markdown
Contributor Author

Code review — multi-persona

Scope: 6 files, core services (dashboard, data, signalk-requests) + specs, diffed against main.
Intent: four correctness fixes from the #27 characterization pass (#149, #150, #155, #156), each re-pinning the test that formerly asserted the buggy behavior.
Reviewers (6): correctness, testing, maintainability, project-standards (always-on) + reliability (timeout/error paths) + adversarial (delete index arithmetic).

Findings are confidence-gated (≥0.60) and deduped; cross-reviewer agreement noted. All in-scope findings were addressed in the pushed commits; two pre-existing/out-of-scope items were filed as issues.

P2 — High

# File Issue Reviewer(s) Conf Resolution
1 dashboard.service.ts:196 delete() with an out-of-range/negative itemIndex shifted (or, for a negative index at active 0, set -1) the active index for a delete that removed nothing — a regression vs the old safe no-op; -1 then crashes loadDashboard. adversarial (correctness concur) 0.68 Fixed — bounds guard at top of delete() matching duplicate()/navigateTo(); test added.
2 dashboard.service.spec.ts The < boundary that is the #149 fix was unpinned — no test deletes the active, non-last dashboard, so a <<= regression survived the suite. testing 0.82 Fixed — added active-non-last delete test (+ out-of-range test).

P2 — High (pre-existing, out of scope → filed)

# File Issue Reviewer(s) Conf Resolution
3 dashboard.component.ts:235 Deep-link/reload to an invalid /page/:id (out-of-range, or fractional now that #149 rejects non-integers) leaves activeDashboard null; loadDashboard has no null guard → undefined.configuration TypeError / blank page. Fractional already crashed pre-#149 via dashboards()[1.5]. adversarial 0.66 Filed #205 — pre-existing; component-level, outside this PR's 3-service scope.

P3 — Moderate

# File Issue Reviewer(s) Conf Resolution
4 data.service.ts:718 #155 now resets all source registrations for a path. Correct when the whole path is silent, but on redundant-sensor setups one source's timeout nulls a still-live sibling (+ default), flashing it blank and reverting alarm state until the next delta. correctness + reliability ~0.80 Filed #206 — the all-reset is #155's requested fix and correct for the common case; source-scoping is the follow-up.
5 data.service.spec.ts:341 Test comment narrated the old implementation ("the old first-match lookup…") — violates the repo's comment policy (comments state WHY, never what changed). maintainability + project-standards ~0.80 Fixed — trimmed to the invariant.
6 dashboard.service.spec.ts:96 #150 clone test asserted only configuration array identity; a shallow [...arr] clone sharing nested widget objects would still pass. testing 0.85 Fixed — added nested-widget identity assertion.
7 signalk-requests.service.ts:139 Server-error branch: only 500-with-message was tested; 502/504 and the no-message path (detail = description) were uncovered. testing (reliability/standards/correctness concur) ~0.80 Fixed — parameterized 500/502/504 + added a no-message case.

Not actioned (advisory)

  • serverErrorStatusCodes repeats 500/502/504 already in deltaStatusCodes/handledStatusCodes — a three-way sync cost (maintainability, 0.60). Acceptable for three codes; reviewer agreed no change needed now.

Coverage

Verdict

Ready to merge once CI is green. All P2/P3 in-scope findings are fixed in the pushed commits (66/66 targeted specs pass, lint + dev build clean); the two pre-existing items are tracked in #205 and #206.

Review by 6 independent persona agents; synthesis deduped and confidence-gated.

mairas and others added 4 commits July 5, 2026 01:50
timeoutPathObservable matched a single registration with
_pathRegister.find(item => item.path == path) — loose == and first-match
by path only. When the same path was registered under multiple $sources,
only the first-registered subject received the timeout reset; other-source
registrations kept their stale value. Switch to _pathRegisterByPath.get()
(strict keyed lookup) and reset every registration for the path.

Closes #155

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDu7zsKECA9SvBR5DLdUex
…ding

deltaStatusCodes carried descriptions for 500/502/504, but handledStatusCodes
omitted them, so those responses fell to the "Unknown Request Status Code"
error toast — which then printed the very description it claimed not to know.
Add them to the handled set and surface them as an error toast carrying their
description plus any server message.

Also fix the statusCode 200 wording: "The request was successfully." →
"The request was successful."

Closes #156

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDu7zsKECA9SvBR5DLdUex
Blank-dashboard seeding spread DefaultDashboard entries shallowly, so a new
dashboard's configuration array (and its widget objects) were the same objects
as the module-level DefaultDashboard constant; any in-place widget mutation
corrupted the shared default for every later blank creation. Deep-clone the
default (same defect class as the already-fixed #35/#100).

Closes #150

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDu7zsKECA9SvBR5DLdUex
…actional

delete() only clamped an active index that overflowed the end; when a
lower-indexed dashboard was removed, activeDashboard silently pointed at a
different dashboard. Follow the active dashboard to its new index when a lower
one is deleted, then clamp.

Guard delete() against an out-of-range itemIndex (matching duplicate()/
navigateTo()/setActiveDashboardIndex()): without it the new remap shifts —
or, for a negative index with active 0, sets a negative — active index for a
delete that removes nothing.

Fold in two siblings from the same characterization pass:
- setActiveDashboardIndex accepted fractional indexes (Number('1.5') passed
  the bounds check), so a URL id like '1.5' set a non-integer active index;
  require an integer.
- Two console.error prefixes in duplicate() read "Dashboard Service]" with no
  opening bracket.

Closes #149

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VDu7zsKECA9SvBR5DLdUex
@mairas mairas force-pushed the fix/characterization-followup-defects branch from f852d36 to 504bbfe Compare July 4, 2026 22:50
@mairas mairas merged commit e2a6515 into main Jul 4, 2026
9 of 12 checks passed
@mairas mairas deleted the fix/characterization-followup-defects branch July 4, 2026 23:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[PCS-05] Default-config singletons are mutated by reference instead of cloned

1 participant