Skip to content

CT-001: SmartMount health pass - #211

Draft
Swissystem7 wants to merge 6 commits into
masterfrom
claude-team/CT-001-smartmount-health
Draft

Swissystem7 wants to merge 6 commits into
masterfrom
claude-team/CT-001-smartmount-health

Conversation

@Swissystem7

@Swissystem7 Swissystem7 commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

CT-001 health pass of SmartMount (owner: Goza, Claude Team, ASUS machine). No changes to .github/workflows, no secrets added, no force push. I did not touch test/coverage-pilot.test.js (#210).

Before starting I checked for open PRs. The only one was #210 (Copilot, tests only), which doesn't overlap.

The first session paused for quota after the install and docs commits. The second session (after the quota reset) did the module-by-module review of src/lib and the three bug fixes below.

Checklist: what changed

  • npm ci and npm audit work again
    • Committed package-lock.json. Without it, npm ci failed and npm audit failed with ENOLOCK. The project has no dependencies, so the lockfile has only the root entry.
    • package.json: added engines.node >=20 (CI uses 22).
  • Bug fixes in src/lib. Each one has a test that fails on master:
    1. control.js: a CUSTOM panel accepted any limit. calcOptimalAngle(500, 10, 'CUSTOM', 0, NaN) returned NaN, and moveToAngle gave steps: NaN. Infinity let the law ask for 235°. A negative cap pinned every angle to that negative value. Now only a finite limit ≥ 0 is used; anything else throws unknown panel type, like an unknown panel. No page passes a custom limit, so the pages don't change. → test/control-custom-limit.test.js
    2. power.js: pack-cannot-move-pulse could never be returned. sizeBattery chose pack-cannot-motor-rail whenever holdW > 0. holdW is the winding I²R and is positive even with a worm, so a worm build on a USB bank or 2S pack was told "coils left on drain it anyway". The power page has its own sentence for the move-pulse case ("idle is cheap thanks to the worm, but the move pulse is still 12 V"). The check now uses idle coils (coilsIdle > 0). → test/power-move-pulse.test.js, which also checks that every kind returned has text on power/index.html
    3. hil.js: validateBoardRunLog didn't recognize the firmware's real WiFi line. It matched only invented lines (WiFi connected, IP address). The .ino prints IP: + WiFi.localIP(), so a board that joined the network read as hasWifi: false. It now also accepts the real line. CRLF logs work too. → test/hil-board-log.test.js
  • Install test: test/install.test.js checks that the lockfile exists and matches package.json, that there are no npm dependencies, the engines.node floor, and (for Windows) that .gitattributes keeps eol=lf and the key files contain no CR.
  • Docs
    • ARCHITECTURE.md (new): the parts of the project, the data flow from config to firmware, JS and tests, how to run and test it, Windows notes, and CI.
    • README: fixed the stale "140+ host tests" (now about 500). Added an English "Development" section.
    • docs/TESTING.md now has rows for the new test files. Fixed a stale "no CI on purpose" comment in scripts/compile-firmware.sh.
  • Review coverage of src/lib: I read control, optics, torque, protocol, power, timing, gantt-chart, userValidation, sentryLoader, contact, feedback, bom, safety, hil, alts and case line by line. For fsm.js I read the firmware model and listed the functions in fsm-table.js; every cell of both is already pinned by fsm-matrix.test.js.
  • Secret scan (report only): I scanned the working tree and the full git history (all branches) for AWS, GitHub, OpenAI, Slack and Google keys, private keys and Sentry DSNs. Nothing found. The firmware WiFi credentials are placeholders, and history only ever had the placeholder. The Sentry and Codecov values are placeholders, and an existing test already guards them.

Verification (Windows 10, Node 24.19, npm 11.17)

npm ci          -> ok (before: failed, no lockfile)
npm test        -> tests 518, pass 518, fail 0   (master: 497/497)
npm audit       -> found 0 vulnerabilities (before: ENOLOCK)
npm run smoke   -> passed at 390px: 0 console errors, valid RTL, 0 overflow, 4 pages
npm run sync-params -> no diff
npm run pr-size -> 259 changed lines (budget 400)
firmware build  -> not run: arduino-cli is not installed on this machine

Not done

  • Firmware compile check (scripts/compile-firmware.sh needs arduino-cli and the esp32 core).
  • The inline scripts in the page HTML files were not part of this review; only src/lib was.

Needs a human decision

  • torque.verdict is not monotonic without a worm gear. factor: 1.0, selfLocking: false gives holds, but 1.01 gives marginal (src/lib/torque.js). This is deliberate ([dev-cycle] In src/lib/torque.js, the function 'verdict' does not handle the case  #36), and test/torque.test.js pins it. Which do you want: 1.0 → marginal, or every factor in [1,2) → holds without a worm?
  • The safety guidelines describe a telescope mount, not a TV mount. src/lib/safety.js (shown on spec/) talks about "altitude and azimuth axes", "releasing clutches", "optical payload" and "timing belts". Each entry also repeats the same sentence in three fields (mitigation/action/step). This is content, so I didn't rewrite it. Should someone rewrite it for an ESP32 + NEMA17 TV tilt mount?
  • The HIL log validator is still mostly invented. The firmware never prints a boot banner, a sensor-OK line, /status or a "holding position" line, so HIL-02 and HIL-03 can never match a real Serial log. Also, hasSensorInit is true for the failure line BH1750 top not found at 0x23. Should the validator check what the .ino really prints, or should the firmware print more?
  • Sentry loader: the CDN script (7.76.0) has no SRI integrity hash, and scrubEvent removes user and request.data but not request.url query strings or breadcrumbs. It's inert while the DSN is a placeholder.
  • src/lib/bom.js is CommonJS only (no browser global), unlike the other modules. No page loads it today.
  • scripts/smoke.js has a machine-specific Playwright path (D:\Cache\ms-playwright) and a fallback that uses HOME, which is often unset on Windows. Should Playwright become a pinned devDependency (that would end the zero-dependency rule)?
  • CI uses Node 22, and the factory machine uses Node 24. Should CI test a matrix? That would be a workflow change, which I wasn't allowed to make.

CT-014: E2E tests

Commit: 741df5b.

Why it lives under e2e/: the root package is deliberately dependency-free. The README says "no npm dependencies", and test/install.test.js (lockfile has only "") and test/pages.test.js (devDependencies undefined) enforce it. So @playwright/test is not added to the root package.json. The suite is its own package: e2e/package.json and e2e/package-lock.json (pinned @playwright/test 1.64.1), e2e/playwright.config.js, a zero-dependency static server e2e/serve.js (127.0.0.1 only, directory → index.html, unknown path → 404.html like Pages), and specs in e2e/tests/. The root gets only the script test:e2e. npm test is still node --test, the root lockfile is unchanged, and *.spec.js under e2e/ is outside the node --test default globs. The files of Copilot's #210 (test/coverage-pilot.test.js) and .github/workflows are untouched.

How to run:

npm --prefix e2e ci && npm --prefix e2e exec playwright install chromium   # once per machine
npm run test:e2e        # = npm --prefix e2e ci + playwright test (desktop + 360px mobile)

Flows covered (24 tests × 2 viewports = 48):

  1. Home: honest badge, panel limits rendered from control-params.js (OLED 40°, QLED 30°, LED 20°), CTA → engineering case, the "עוד" nav menu → API contract.
  2. All 12 pages: dir=rtl, lang=he, an h1, and no sideways scroll. The 404 page is served for an unknown path.
  3. Case: filter review findings by severity (aria-pressed, "2 מתוך 6"), then back to all.
  4. Dashboard: "בוהק חזק" → MOVE, ratio 6.00×, 15.1°. "כשל עליון (−1)" → HOLD, angle stays 15.1°. Manual mode unlocks the slider; −12° shows −11.9° because of whole-step rounding, as the README describes.
  5. Lab fault console: dead top sensor → FAIL + HOLD. deg=99 → HTTP 200, auto OFF, target and current angle clamp to 40.0° (OLED). WiFi cut → WiFi DOWN, firmware serial line, auto stays ON. Panel switch updates aria-pressed.
  6. Spec torque calculator: lower mass lowers the required torque, self-lock changes the unpowered result, and reset restores gear ratio 5.

Determinism: e2e/tests/fixtures.js aborts every request that is not to 127.0.0.1, and fails the test on any such request, page error, console error or 4xx/5xx response (except the browser's own /favicon.ico). Sentry stays inert because the DSN is the placeholder, so no CDN request is made. Assertions use roles, accessible names, ids and text, not CSS.

Results (Windows, Node 24, Chromium from Playwright 1.64.1): unit tests 518/518 pass before and after. E2E 48/48 pass, three runs in a row (one with CI=1). No E2E test exposed a bug in this repo.

Decisions for Aviran:

  • scripts/smoke.js / npm run smoke (390px, file://) is left as is. It overlaps with the new 360px suite, which goes further. Removing it would also change test/smoke-390.test.js, so I didn't.
  • The E2E suite does not run in CI. Adding a job means editing .github/workflows/validate.yml, which test/pages.test.js also pins, so I left that to you.

🤖 Generated with Claude Code

npm ci failed (no lockfile) and npm audit failed with ENOLOCK. The project
has no dependencies, so the lockfile is the root entry only. Adds
engines.node >=20, a test that pins the lockfile, zero deps and the LF
.gitattributes rule, and fixes a stale 'no CI' comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Nov 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

swissystem-maker and others added 5 commits November 3, 2026 03:05
Also updates the stale '140+ host tests' count (now ~500).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A CUSTOM panel accepted any limit: NaN or a string gave a NaN angle and
NaN step count, Infinity let the law ask for 235 degrees, and a negative
cap pinned every angle to it. Now only a finite limit >= 0 is used.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sizeBattery gated 'pack-cannot-motor-rail' on holdW, which is > 0 even
with a worm, so 'pack-cannot-move-pulse' (which power/ has text for) was
unreachable. Gate on idle coils instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
validateBoardRunLog only matched invented lines ('WiFi connected', 'IP
address'). The .ino prints 'IP: ' + localIP(), so a connected board read
as hasWifi=false.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The root package must stay dependency-free (test/install.test.js and
test/pages.test.js enforce it, README promises it), so the suite is an
isolated package under e2e/: its own package.json + lockfile with
@playwright/test, playwright.config.js, a zero-dependency static server
(e2e/serve.js, 127.0.0.1 only) and specs in e2e/tests/. Root gets only the
script npm run test:e2e; npm test is unchanged.

Desktop and 360px mobile, 24 tests x 2 viewports:
- home: badge, panel limits from control-params, CTA to the case, nav menu
- every page is Hebrew RTL with an h1 and no sideways scroll; 404 page
- case: filter review findings by severity
- dashboard: glare tilts, failed sensor holds, manual mode
- lab: dead sensor holds, deg=99 clamps to the OLED limit, WiFi cut, panel
- spec: torque calculator (mass, self-lock, reset)
Requests leaving 127.0.0.1, page errors, console errors and 4xx/5xx fail
the test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

2 participants