Fleet changelogs · dev.ecs0.net
rdmpw3265m-changelog-20260903-1626-appindex-and-ecs0-rootcause

rdmpw3265m changelog — App Index published, dev.ecs0.net root cause found

[2026-09-03 16:26:54 EDT · rdmpw3265m] · session start ~15:38 EDT, end 2026-09-03 16:26:54 EDT

1. Published https://eastcoastscience.net/apps — "App Index"

Rich: "I forget what a lot of names from older projects do." The page answers that from measured state, not recall. 56 projects under rdmsm4x:~/dev/apps, one row each, expanding on click, with a side rail filtering by last-worked-on, repo age, status, build architecture, outstanding work, function tag and language.

Collected on rdmsm4x, all measured:

Shipped as part of the site build, not dropped into dist/ — build.js does fs.rmSync(DIST_DIR), so a dist-only page would have vanished on the next build. New files src/templates/apps.html + src/data/apps_inventory.json; scripts/build.js gained section 10; layout.html gained the nav link. Build went 27 → 28 pages.

Commit 5b80161, pushed to fleet and backup (all three refs identical). Deployed via wrangler pages deploy. Theme: inherits the site's design tokens, so the existing header control drives System / Light / Dark with no palette of its own.

Two silent-wrongness defects found and fixed while building it

  1. GitHub column came back empty for all 56. The collector read ~/dev/FLEET-PROJECT-POINTERS.md, which does not exist at ~/dev on rdmsm4x (it lives at ~/dev/fleet/ops/git/). Zero was read as "no repos" rather than "lookup broken". Now reads each repo's own git remotes — ground truth — 56/56 resolved.
  2. ISSUES.md matcher accepted a bare OPEN line, so continuation lines inside paragraphs counted as tickets. Tightened to require a real item marker. Verified in both directions: exactly 5 projects lose exactly 1 phantom each (196 → 191); no project lost a real ticket.

Verification (against reality, not the build log)

node --check on build.js; build emits 28 pages; the embedded inventory JSON parses back to 56 apps; rendered in Chrome light and dark, zero console errors; facet / combined-facet / search / reset paths all return expected sets (e.g. Credentials ∩ Universal2 = 3, correctly dropping the unbuilt PasswordScope). Trap hit and caught: the first browser check showed a stale page — Chrome had cached it; curl proved the file on disk was current. A screenshot is not proof the bytes are fresh.

Live check note: curl https://eastcoastscience.net/apps returns HTTP 200 — but that is the Cloudflare Access sign-in page, not the page. Verified instead through an Access-authenticated browser: 56 apps, 56 rows, correct build stamp. HTTP 200 was the wrong measurement.

2. dev.ecs0.net — root cause found, PR #4 filed for agy

Rich reported it "still broken/not loading". It is not an outage.

Origin is healthy: curl -H 'Host: dev.ecs0.net' http://127.0.0.1:8788/ returns 200 for /, /index.html, /issues/; port 8788 open; gateway, wiki and tunnel containers all up 17h. The public 302 to Cloudflare Access is the documented masker, not the fault.

Actual fault: the shell renders and every content pane is blank because the page's single inline <script> does not compile — SyntaxError: Invalid or unexpected token at :702:222663. At that offset the script holds a JSON string containing a harvested HTML document with a literal, unescaped </script>. The HTML parser ends the script there, leaving an unterminated string literal; the router never starts.

Cause: scripts/build_site.py:571-575 interpolates seven json.dumps() blobs straight into inline <script> tags. json.dumps escapes for JSON, not for HTML script context. Fix given: json.dumps(obj).replace("</", "<\\/") behind one helper. Carried by the agy doc agy/hss-network-070326-…-docs.html; any ingested external HTML can do the same.

PR #4 on richhdoty/dev-ecs0-net adds the dated ISSUES.md entry only — no generator change, left for agy per Rich. The PR states the both-directions check: a fix that drops or truncates the offending record clears the error by deleting content, which is worse than the bug.

Same defect class was pre-empted in the App Index build the same day — build.js injects its inventory with a function replacement and .replace(/<\//g, '<\\/').

Files changed