Chore: parametrize build_page.py and prove the page generalizes (not a spike) #31

Open
opened 2026-08-12 21:46:57 +00:00 by mathias · 0 comments
Owner

Rewritten 2026-08-13. This was filed as "Spike 3/3: generate the playback page from artifacts". Review killed that framing: scripts/spike-media/build_page.py already generates the page from (payload.json, video). There is no spike here — the question it claimed to ask is answered.

What is left is small, and one part of it is a real problem the original made worse.

1. The generator hardcodes one video

build_page.py:348:

TEMPLATE.replace("__TITLE__", html.escape("Jonas – genomgång av tillbyggnad"))

Title, speaker name and domain must come from the payload or the CLI. Until then "it generalizes" is untested by construction.

2. /bygge still has no versioned source of truth — and it cannot get one here

The original acceptance said "generator committed here, artifacts gitignored", while #28's comment claimed this issue closes the source-of-truth gap. Those contradict: a generator with no committed inputs is a source of truth for nothing.

Then a harder constraint turned up while committing the toolchain: mathias/tapir is a public repo. IMG_1233.sv.srt and payload.json are a verbatim recording of a named person discussing a client's building project — private third-party content. Committing them here publishes them. This is the same class of data #28 names as the reason uploaded transcripts cannot share the transcripts table, so it should not be waved through for the prototype's own artifacts.

DECISION NEEDED — blocks the "regenerate /bygge byte-identically" check, nothing else:

Option Cost
Private repo (or private submodule) for reference artifacts One repo; keeps tapir public and the artifacts versioned
SOPS-encrypted blob in this repo No new repo; git diff becomes useless on them
Accept no version control for the real artifacts; generalization proven only on synthetic fixtures Free; the /bygge regeneration check becomes a manual one-off

Recommendation: private repo. Do not commit the real transcript to a public repo to satisfy a checkbox. Until this is decided, the durable copy lives outside git at ~/dev/.rescue/bygge-toolchain-2026-08-13/private/, which is not a plan, it is a stay of execution.

3. The video tradeoff in the original was a false dilemma — cut

The original asked whether a smaller encode would lose "the detail on a wall". Parsed from the containers:

codec resolution fps size bitrate
IMG_1233.mov hvc1 720×1280 30 317 MiB ~11 Mbps
IMG_1233.web.mp4 avc1 720×1280 30 146 MiB ~5.1 Mbps

transcode.yaml uses scale='min(1280,iw)':-2, and iw is 720 — the filter is a no-op. It is HEVC→H.264 at identical resolution. The wall detail was never captured, and 5.1 Mbps is -preset veryfast on handheld constant-motion content, not a quality decision. The lever is the preset.

So measure instead of agonizing:

  • Re-encode at -preset slow -crf 28 and -crf 30, report MiB
  • Expected 20–45 MiB, i.e. 3–7× smaller with no perceptible loss at 720p
  • Correct #28 decision 5's storage math: a properly encoded hour is ~300–600 MB, not the ">2 GB" the original asserted by inheriting the bad encode

Not blocked on #32

Unlike #29 and #30 this costs an hour and produces no new artifacts to be sunk-cost about. Do it whenever.

Acceptance

  • caveman: me watch video and see what was decided at that moment, without writing page by hand
  • Title, speaker name and domain are not hardcoded anywhere in the generator or the prompt — grep proves it
  • Page generated from fixtures/fake.analys.json + any video renders correctly — generalization shown on the synthetic fixture, which needs no privacy decision
  • Re-encode measured at -preset slow -crf 28/-crf 30; numbers posted; #28 decision 5's storage estimate corrected
  • Red-first tests on cue→card timestamp mapping and the seek index
  • Visual result checked by eye, and said to be checked by eye
  • Artifact-custody decision above recorded in #28
  • (blocked on that decision) build_page.py payload.json IMG_1233.web.mp4 produces HTML whose embedded DATA object is byte-identical to the served page's, verified by diff

Refs

**Rewritten 2026-08-13.** This was filed as "Spike 3/3: generate the playback page from artifacts". Review killed that framing: `scripts/spike-media/build_page.py` **already** generates the page from `(payload.json, video)`. There is no spike here — the question it claimed to ask is answered. What is left is small, and one part of it is a real problem the original made worse. ## 1. The generator hardcodes one video `build_page.py:348`: ```python TEMPLATE.replace("__TITLE__", html.escape("Jonas – genomgång av tillbyggnad")) ``` Title, speaker name and domain must come from the payload or the CLI. Until then "it generalizes" is untested by construction. ## 2. `/bygge` still has no versioned source of truth — and it cannot get one here The original acceptance said *"generator committed here, artifacts gitignored"*, while #28's comment claimed this issue **closes** the source-of-truth gap. Those contradict: a generator with no committed inputs is a source of truth for nothing. Then a harder constraint turned up while committing the toolchain: **`mathias/tapir` is a public repo.** `IMG_1233.sv.srt` and `payload.json` are a verbatim recording of a named person discussing a client's building project — private third-party content. Committing them here publishes them. This is the same class of data #28 names as the reason uploaded transcripts cannot share the `transcripts` table, so it should not be waved through for the prototype's own artifacts. **DECISION NEEDED — blocks the "regenerate `/bygge` byte-identically" check, nothing else:** | Option | Cost | |---|---| | Private repo (or private submodule) for reference artifacts | One repo; keeps tapir public and the artifacts versioned | | SOPS-encrypted blob in this repo | No new repo; `git diff` becomes useless on them | | Accept no version control for the real artifacts; generalization proven **only** on synthetic fixtures | Free; the `/bygge` regeneration check becomes a manual one-off | Recommendation: private repo. Do **not** commit the real transcript to a public repo to satisfy a checkbox. Until this is decided, the durable copy lives outside git at `~/dev/.rescue/bygge-toolchain-2026-08-13/private/`, which is not a plan, it is a stay of execution. ## 3. The video tradeoff in the original was a false dilemma — cut The original asked whether a smaller encode would lose "the detail on a wall". Parsed from the containers: | | codec | resolution | fps | size | bitrate | |---|---|---|---|---|---| | `IMG_1233.mov` | `hvc1` | **720×1280** | 30 | 317 MiB | ~11 Mbps | | `IMG_1233.web.mp4` | `avc1` | **720×1280** | 30 | 146 MiB | ~5.1 Mbps | `transcode.yaml` uses `scale='min(1280,iw)':-2`, and `iw` is 720 — **the filter is a no-op.** It is HEVC→H.264 at identical resolution. The wall detail was never captured, and 5.1 Mbps is `-preset veryfast` on handheld constant-motion content, not a quality decision. The lever is the preset. So measure instead of agonizing: - Re-encode at `-preset slow -crf 28` and `-crf 30`, report MiB - Expected 20–45 MiB, i.e. 3–7× smaller with no perceptible loss at 720p - **Correct #28 decision 5's storage math**: a properly encoded hour is ~300–600 MB, not the ">2 GB" the original asserted by inheriting the bad encode ## Not blocked on #32 Unlike #29 and #30 this costs an hour and produces no new artifacts to be sunk-cost about. Do it whenever. ## Acceptance - [ ] `caveman: me watch video and see what was decided at that moment, without writing page by hand` - [ ] Title, speaker name and domain are **not hardcoded anywhere** in the generator or the prompt — grep proves it - [ ] Page generated from `fixtures/fake.analys.json` + any video renders correctly — generalization shown on the **synthetic** fixture, which needs no privacy decision - [ ] Re-encode measured at `-preset slow` `-crf 28`/`-crf 30`; numbers posted; #28 decision 5's storage estimate corrected - [ ] Red-first tests on cue→card timestamp mapping and the seek index - [ ] Visual result checked by eye, and **said** to be checked by eye - [ ] Artifact-custody decision above recorded in #28 - [ ] *(blocked on that decision)* `build_page.py payload.json IMG_1233.web.mp4` produces HTML whose embedded `DATA` object is **byte-identical** to the served page's, verified by `diff` ## Refs - `scripts/spike-media/` (committed 2026-08-13), #28 (Slice 5, decision 5), #29, #30, #32 - ADR-025 (honest state-aware status — the `warn` block is the same principle)
mathias changed title from Spike 3/3: generate the synced playback page from artifacts instead of hand-building it to Chore: parametrize build_page.py and prove the page generalizes (not a spike) 2026-08-13 09:39:29 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/tapir#31