diff --git a/REUSE.md b/REUSE.md index 1b9e0dff..977cf189 100644 --- a/REUSE.md +++ b/REUSE.md @@ -172,6 +172,7 @@ | Workspace installer | scripts/install_workspace.py | Lays down the workspace-root `CLAUDE.md` (canonical source: `documentation/runbooks/workspace-CLAUDE.md`) and the `InstructionsLoaded` hook from `workspace/hooks/`. **Unlike `install_skills.py` the targets are LIVE CONFIG, not disposable**: timestamped backup before every write, `settings.json` MERGED (owns exactly one key, `hooks.InstructionsLoaded`), a diverged CLAUDE.md reported not silently resolved, an unparseable settings.json REFUSED. Idempotent — a second run writes nothing. | | Rule-firing report | scripts/rules_report.py | Reads the `InstructionsLoaded` hook log and cross-references every `.claude/rules/*.md` in the workspace. **The column that matters is the empty one** — a rule that has never fired is mis-globbed or dead (built-but-never-wired, applied to instructions). Prints each silent rule's `paths:` so "wrong glob" is distinguishable from "quiet month". Caveat: the log only covers since the hook was armed. | | Gate script | scripts/site_gates.py | Byte-level mechanical gates (BOM, emoji codepoint ranges, nav/footer diff, analytics, banned tokens, cache-bust); run `python scripts/site_gates.py` after ANY website change; non-zero exit on failure. | +| Hero clip builder | scripts/make_hero_clip.py | Assembles the home page's laptop clip from the operator's raw app recordings: scale+crop each to the screen's 1.626:1, crossfade, encode mp4 (h264) + webm (VP9) + a webp poster. **Runs on DooPlex** - Windows has no ffmpeg. NOT part of any build, gate or deploy: the site has no build step, so the output is committed like any other picture. Measured 2026-10-12: GIF is 11x the mp4 for the same seconds and dithers to 256 colours, hence mp4+webm. The frame it plays under is `website/assets/hero-laptop-frame.webp`, whose screen is cut out; the hole's position is in the CSS as percentages. | | Fetch-validate-install (shell) | scripts/felhom-host-install.sh `step_agent_install` (~L1108) | `fetch_raw` to mktemp → syntax-check (`bash -n`) → `install -m0755 -o root -g root` → only then activate; guarded-mkfs wrapper installed BEFORE the sudoers that references it (ordering is the safety property). All mutations through `run()` (dry-run aware). | | Install-profile gate (shell) | scripts/felhom-host-install.sh `--mode appliance\|byo` (GL-2, v1.10.0) | Mandatory-flag profile (no default), refusals at argv time BEFORE any prompt/step, risky step gated at its CALL SITE (one auditable place — never a branch inside the step), mode persisted to state.json + resume-mismatch refusal, `FELHOM_INSTALL_STATE_DIR` override for harness isolation. Harness: scripts/hostinstall-mode-harness.sh (static refusal matrix + grep-invariants + PVE dry-transcript tier; red-proofs run against a mutated scratch copy). | | Disclosure↔uninstall parity (shell) | scripts/felhom-host-install.sh `_uninstall_statement` + harness GL4-D (v1.11.0) | Every host artifact the byo disclosure names must be removed OR explicitly listed KEPT by `run_uninstall`; the harness greps the parity (token list). New install-time artifact ⇒ add its removal + disclosure line + parity token in the SAME commit. Drive data rule: plain `umount` only, never `-l`/`-f`, never any format op under /mnt/felhom-drives. | diff --git a/scripts/CHANGELOG.md b/scripts/CHANGELOG.md index 27c418ea..9cc9d0db 100644 --- a/scripts/CHANGELOG.md +++ b/scripts/CHANGELOG.md @@ -1,3 +1,23 @@ +## 2026-10-12 (evening) — make_hero_clip.py: the home page's laptop clip + +- `scripts/make_hero_clip.py` assembles the hero clip from the operator's raw app recordings: + `NN-appname.ext` in, scaled and centre-cropped to the laptop screen's 1.626:1, crossfaded, out as + **mp4 (h264) + webm (VP9) + a webp poster**. Runs on DooPlex; Windows has no ffmpeg. It is not part + of any build, gate or deploy — the site has no build step, so its output is committed like any + other picture. +- **Why mp4+webm and not a GIF**, measured on DooPlex 2026-10-12 with six crossfaded scenes: + + | | 11.4s @ 904×556 | 11.4s @ 1130×696 | + |---|---:|---:| + | mp4 (h264 crf30) | **331 KB** | 775 KB | + | webm (VP9 crf44) | 411 KB | 1 196 KB | + | animated webp | — | 2 551 KB | + | GIF | **2 676 KB for 6s** | — | + + A GIF is ~11× the mp4 for the same seconds, and dithers to 256 colours on a dark UI. +- Proven end to end against six placeholder scenes built from the repo's own app screenshots: + 21s loop → mp4 480 KB, webm 609 KB, poster 83 KB. + ## 2026-10-12 (afternoon) — gate 22 also refuses an unscoped bare element rule - `site_gates.py` gate 22 gains a second half: a rule in `site.css` whose selector starts with a bare diff --git a/scripts/make_hero_clip.py b/scripts/make_hero_clip.py new file mode 100644 index 00000000..6112e269 --- /dev/null +++ b/scripts/make_hero_clip.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""make_hero_clip.py — assemble the home page's laptop clip from raw app recordings. + + python3 scripts/make_hero_clip.py [--lang hu|en] [--seconds 4] [--dry-run] + +WHAT IT IS FOR. The hero on / and /en/ is a laptop whose SCREEN is a short, silent, looping clip of +real apps running on a real box. The operator records the raw clips; this turns them into the two +files the page loads, and into nothing else — it is NOT part of any build, gate or deploy. The site +has no build step: the output is committed like any other picture. + +WHERE IT RUNS. On DooPlex, which has ffmpeg (7.1.5, libx264 + libvpx-vp9). Windows has no ffmpeg. + +INPUT. One file per scene in , named `NN-appname.` so the order is the file order: + + 01-jellyfin.mp4 02-tandoor.mkv 03-onlyoffice.webm 04-immich.mp4 ... + +Any container ffmpeg reads. Audio is dropped. Each clip is scaled to cover the laptop's screen and +centre-cropped to 1.626:1 — record at 16:10 (1440x900 is ideal) and almost nothing is lost. + +OUTPUT (in , which is normally website/assets): + + hero-clip-.mp4 h264, the one every browser plays + hero-clip-.webm VP9, preferred where offered + hero-poster-.webp the first frame, which the