GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

museum.md



MEMORY

memory/project-C--Dev--GEM--robco-local-backup/museum.md

sha256 177bb8f4a4849225 · 7224 bytes · original held in the private archive

--- name: museum description: "The archive museum — generator at museum/generate.mjs, regenerated by sync.ps1 as a non-blocking ritual; design constraints and conventions" metadata: node_type: memory type: project originSessionId: 798ebe8e-3057-4afc-a464-abd28677d015 modified: 2026-07-20T18:12:51.814Z --- Built 2026-07-19. A generated, browsable view of THIS private archive lives at `museum/`. One generator, `museum/generate.mjs` (Node built-ins only), walks the archive once into a single manifest and renders every view from it. **Hard constraints (settled by two external reviews, do not relitigate):** - Standalone, NOT embedded in the app — the archive is private; anything the app serves crosses the privacy boundary. - Opened as a LOCAL FILE by double-click, so `fetch()` is unavailable. All DATA (search corpus, manifest) is written INTO the page as inline JS, never fetched. - No npm deps / framework / CDN / bundler / SW. Playwright (from the app repo's node_modules) is used at GENERATION time ONLY, to screenshot the prototypes, cached by content hash in `museum/.cache/thumbs` so unchanged ones never re-render. **CSS interpretation (a deliberate, documented reading):** the app's real stylesheets are concatenated in their canonical `<link>` order (the mobile sheet LAST) into `museum/site/assets/robco.css` and referenced by a relative `<link>`, not inlined per-page. A local sibling `<link>` loads fine under `file://` — the same subresource mechanism the required in-place `<img src>` gallery depends on; inlining the whole sheet into every generated page was rejected as pure waste. The ONE museum-specific CSS block loads last and includes a **scroll reset** (`body[data-museum]` overrides) because the app's `.glass-frame`/`body` are `overflow:hidden` fixed-viewport — a museum page must scroll as a document or everything below the fold is unreachable. **Conventions worth keeping:** - `graveyard/<slug>/record.md` — structural encoding of ruled-out ideas so the generator can show "abandoned, and why" (a script can't infer it from prose). Front-matter `original:` points at the real in-archive decision record to inline verbatim — no later summaries; that's the failure mode. - `museum/accounts/<version>.md` — the ONLY hand-written content; one per release, currently unapproved (`draft: true`) pending owner-written accounts. Unapproved renders the account plus ONE quiet provenance line at the foot — it does NOT shout DRAFT. Missing → "ACCOUNT NOT FILED" (never invented). Owner-facing drafting notes live in non-rendered front matter (`owner_note:`), never in the body. Rooms are discovered by version-shaped folder names, never hardcoded. - Stamp on every page = app commit + hash of the archive input tree; NO wall clock, so it regenerates byte-identically when nothing changed (verified idempotent). - Growth is plain SVG from real data, but **one series per panel, one axis per panel, own units, from zero** — never two units on one frame. The earlier chart normalised checks and code lines to finish at the same height against a checks-only axis; that was numerically wrong, not just badly labelled. **Code lines is the hero series, NOT check count** — count is inventory, not confidence, and the app retired its test-count bookkeeping on 2026-07-20. The checks series is a CLOSED record that visibly ends where the `Tests:` marker was stripped from `[Unreleased]`; never re-promote it. Points are keyed off DATE, never off the presence of a Tests marker (keying off the marker silently dropped the newest point when the marker went away). - **The chart plots the IN-FLIGHT point, not just releases.** When nothing has shipped for a while, a releases-only chart reads as a museum that stopped updating. The `dev` working state is its own point: dashed segment, hollow ring, labelled IN FLIGHT, amber row in the table. Never merged into the release line — that would fabricate a release. ⚠ **This broke once already and is the canonical local example of an INERT mechanism** (design doc §5): the Unreleased handling was written and correct, but `if (!tm) continue` gated it on a `Tests:` marker, so when that marker was stripped on 2026-07-20 the whole point vanished — date, files, lines included. It looked healthy; the owner caught it by eye. - **`growthCurrency()` is the recurrence guard.** It compares the newest charted point against the pinned app ref's commit date; on disagreement the lobby AND growth page render a generated NOT CURRENT notice. Test it by simulating the regression, not by pinning MUSEUM_APP_REF to a tag (old refs have their own `[Unreleased]`, so that passes for the wrong reason). - Do NOT compute a fresh test count to "fix" the checks series ending where it does. That would rebuild in the archive the bookkeeping obligation the app deliberately deleted. The ending IS the record. - Claims are LITERAL. The lobby states the things the build actually checks and names what it does NOT check. Never "DATA INTEGRITY — clean". One of those checks is a real internal-link check (`checkInternalLinks`) that resolves every generated href/src on disk — it skips inline `<script>` bodies, or JS building markup as strings scans as broken links. - Keypad nav is `<a href>` + `aria-current`, never `<button onclick>`/`role=tablist` — these are separate-page links. Zero JS in navigation. - Search corpus is SHARDED into `assets/search-N.js`, loaded on first keystroke, weight stated on the page, so search.html stays small instead of shipping the whole corpus. `<script src>` works under `file://` where `fetch()` does not — that is the mechanism that makes this possible. The shards are TRANSPORT, not a search partition: all load before any query runs, so a result set is never silently partial. - Sparse rooms (a version with only one artifact) render the artifact as a seated module on an empty 6-slot bay + a coverage notice. ⚠ Wording is load-bearing: the material "is present in the archive", NEVER "was never created". **Wiring:** `sync.ps1` runs `node museum/generate.mjs` after `Sync-Memory` as a RITUAL that can never fail the sync (mirrors `backup-nudge.js` fail-safe DNA; the generator itself exits 0 on any error and leaves the previous museum intact via atomic temp-build + swap). `museum/site/` IS COMMITTED as of 2026-07-20 — it was gitignored as "regenerable output", but the owner reads this archive through GitHub on a phone, so output living on one disk defeated the archive's whole purpose. The museum step runs BEFORE sync.ps1's `git add -A`, so a refresh rides along in the same sync commit and GitHub never goes stale. Only `.cache/` and `.build-tmp/` stay ignored. Entry point is `museum/site/index.html` — NOT `museum/index.html`, which never existed and is the path to correct if anyone reports the museum "missing". Note: `backup-nudge.js` lives in the read-only app repo, so the pattern was extended into `sync.ps1`, not that file. See [[planning-additive-only]] (the additive-only rename hazard is surfaced by the generator's duplicate-content detector) and [[backup-memory-topology]].
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).