| 2026-10-03 |
PT31 done — the lightbox keeps loading location batches when the reader navigates to the end of the loaded gallery. The reproduced fault: the batch loader was scroll-driven only while lightbox steps change only the hash and never scroll (measured scrollY 0), so the walk dead-ended at the last loaded slide — no a[rel=next] there (forward keys/wheel/swipe no-ops; the right-half zone click fell through to the close catcher). Fix (JS only, main.js → shipped main.min.js, +698 B raw / +154 B gzip): (1) prefetch ahead — a step that brings the walk within 25 loaded slides of the end triggers the next 5-page batch; (2) boundary affordance — while locations remain unloaded the last loaded slide gets a real forward arrow (same glyph and half-screen zone as any slide) whose press loads the batch and then follows the rewired href; a failed fetch leaves it pending and the next press retries; the true end stays arrow-less, forward presses there stay no-ops; no-JS untouched. Verified on a full build (builds/2026-10-03-pt31): blast radius = the two JS files only (every HTML page byte-identical to the PT30 build); chromium/CDP suite 23/23 + true-end stage (all 57 locations load; prefetch threshold 26 → no fetch, 25 → one batch; 40-step walk through the loaded-end boundary (0 stalls) and a second-boundary re-test (tail jump → prefetch → press crosses); held-fetch join loads exactly one batch; fail-then-retry; zone click and mobile swipe load-and-continue instead of closing; year page; no-JS), PT15 gesture harness 26/26, minify.sh --check clean, zero console errors. Commit cf572d5. |
| 2026-10-03 |
PT32 done — 2026/#p-26-10-DSCF4468(1) (Thessaloniki) was sideways; rotated 90° ccw to vertical — all four variants. Fix: fulls/ + thumbs/, JPG + AVIF — JPG via jpegtran -copy all (lossless; EXIF byte-identical; 1024×683→683×1024, 512×341→341×512), AVIF re-encoded through the resize.sh pipeline with the original metadata restored exactly (EXIF + the 12,255-byte XMP packet, md5 8a423194); all four decoded outputs visually verified. Committing surfaced a bug in the agent commit helper (mkcommit.sh) that silently CRLF-stripped the two large binary blobs (piped grep -q under set -o pipefail SIGPIPEs git for blobs over the pipe buffer → the check went false → text-normalise branch); helper fixed (temp-file dump + binary guard), bad commit 86b33ab dropped before any push, corrected rotation commit a7f1396 with all four committed blobs byte-verified against the worktree, and the parallel session’s PT31 board commit re-created on top as e66e01f. Push pending (owner). |
| 2026-10-03 |
PT31 opened — the lightbox must keep loading when the reader navigates to the end of the loaded gallery. Owner report: with the popup open, navigating right runs out of images although further location pages exist. Verified on the PT30 build (chromium/CDP; log pictrace-work/pt31-check-result.json): the last prerendered slide (p-26-11-DSCF4680(1)(1), end of Utrecht) has no a[rel=next]; ArrowRight there is a dead stop (no hash change, ±0 fetches) and the right-half click falls through to the close catcher (popup closes); page steps do not scroll (measured scrollY 0), so the scroll-driven loader never fires; a manual scroll to the bottom loads exactly one 5-page batch (+84 figures) after which the chain continues. Root cause: the loader is scroll-driven only (IntersectionObserver on the links nav + post-batch proximity check, main.js ~651-824) and the template omits the next arrow when the neighbour is outside the rendered set. Direction: expose the loader and drive it from the lightbox path — prefetch ahead on steps + a boundary arrow that loads-and-continues while unloaded pages remain; no-JS and the true end unchanged. Details: .hermes/plan/PT31-lightbox-load-ahead.md. |
| 2026-10-03 |
PT30 done — Helsinki 404 fixed + location stubs can no longer go missing silently. Root cause (verified): images/2025/41_Helsinki/ was committed (8383dcf) but ./mklocations.sh was never run again — the links nav enumerates locations from images/, so the nav and the 2025 quick-links already pointed at /2025/41_Helsinki/, while the page itself is generated from its _locations/ stub, which did not exist → live 404 (missing from the sitemap too). Fix: stub generated + automation so the step can’t be forgotten — mklocations.sh --check (missing/stale report, exit 1) and --expected (machine-readable list), write mode flags stubs not yet in git HEAD, resize.sh syncs at the end of a run, and the agent commit helper (mkcommit.sh) auto-includes stubs missing from HEAD. Compile-time generation was considered and rejected: the native Pages build (PT10, parked — vendor workflow, no custom plugins) cannot run repo scripts. Verified: full jekyll build (129 s) — 57 location + 3 year pages, /2025/41_Helsinki/ = 20 photos + full nav; index, Paris, London and 2025 pages byte-identical to the live site; resize.sh end-to-end run auto-created its stub; commit-guard self-test passed. Commit 2b135f4. |
| 2026-10-03 |
PT29 done — the agent-era commits are re-attributed. Owner report: the 2026-09-20…10-03 commits carried Hermes Agent <hermes-agent@local>. It came from explicit config, not a default: the repo-local .git/config and the sandbox clone’s helper githome/.gitconfig both had it written literally. Fix: the 98 contiguous tip commits rewritten (git filter-branch --env-filter, author + committer) to Alexander Dümont <alexander_duemont@web.de> — the identity of all pre-September history (GitHub accounts by email, so a-d attribution is unchanged); dates/trees/messages preserved, 437/437 commits pairwise-verified, boundary parent fcae42687 and older history untouched. Old tip 8383dcf kept at refs/original/…; new tip dc041b43; both configs + the helper’s fallback fixed. Force-push pending (owner). |
| 2026-10-03 |
PT28 retro-logged — the video gallery. Shipped 2026-09-27 without a card: video.sh turns a clip into gallery assets (animated AVIF + poster JPG under fulls//thumbs/) and needs no template change — iterator.html picks the non-.avif file and gallery_item.html already emits <source type="image/avif"> + the JPG <img>; Faro dolphin15 recut (7.7 s, 30 fps, loop; commits 894711b, 421afa1). Details: .hermes/plan/PT28-video-gallery.md. |
| 2026-09-24 |
PT27 done — deep links eagerly load instead of hopping. A #p-… anchor that is not prerendered sends the loader down the same sequential batch path as scrolling (arrow-chain rewiring intact) until the anchor exists, scrolls the tile into view and opens the slide. Settled empirically: Chromium does not re-evaluate :target for elements inserted after the fragment was set (the PT16 open question) — the handler re-asserts the hash via two location.replace calls (back-button history stays clean, history.length = 1). The location-page hop remains the fallback for anchors the manifest cannot resolve. Verified (built site, Chromium over HTTP): prerendered link → opens, no eager loading (76 slides); Namibia link → overlay in 0.6 s, hash kept, EXIF 7 items, arrows armed, close lands at the tile, loader continues 821 → 884; invalid basename → hop parity (/2025/36_Namibia/); cross-year anchor on a year page → settles at 462 slides, no navigation; zero console errors. min regenerated (11,976 B raw / 4,180 B gzip). Commit 3fb3c8e. Unpushed — owner push pending. |
| 2026-09-27 |
Backdrop moved out of images/ (owner report: images/bg-blurred.jpg conflicts with the folder used for scanning new location pictures). The concern is concrete, not cosmetic: resize.sh normal mode globs the drop folder as images/*.{jpg,jpeg,png,tiff,tif,webp} (resize.sh:395), so the backdrop was sitting exactly where new originals land - a bare ./resize.sh run would have queued it as a photo to import (a 96 px wide, 1.7 KB “travel photo”). Fix: git mv images/bg-blurred.jpg bg-blurred.jpg (project root, next to favicon.ico) + all live references: the preload link and the --bg body style in _layouts/default2.html (both /images/ -> /), the main.css comment, blur-bg.sh (output path + a header note on why it must stay out of images/), README.md, AGENTS.md (directory map + the baked-backdrop pitfall). The two images/bg-blurred mentions in this log’s PT21/PT12 entries are left as written - they are the historical record. No template enumerates the images/ root generically (every image_root path is {year}/{NN}_{Location}/-scoped, iterator.html filters on fulls/thumbs), so nothing else was affected. Verified: real jekyll build -> bg-blurred.jpg emitted at the site root and both references resolve; the old /images/bg-blurred.jpg path is absent from the build. |
| 2026-09-24 |
PT26 done — the DOM lost its structural classes. No more gallery-item / location / year / lightbox-container / nav-prev / nav-next in the shipped markup: CSS + JS target .gallery article, section[year], section[location], article > figure, a[rel=prev] / a[rel=next] (rel is the semantic form; aria-labels kept; the loader creates rewired arrows via setAttribute('rel','next')). Verified three ways: (1) applying the same replacement pass to the PT25 build reproduces all 60 pages byte-for-byte; (2) PT15 harness 26/26 — stubs updated, and the harness itself repaired (it had been broken since PT18’s zoom block joined the extracted snippet — a missing window.addEventListener stub); (3) browser matrix (Chromium over HTTP): left/right zone clicks, keyboard ←/→, wheel, centre-click close, hit-test map, open-state guards (article z 20011, gallery touch-action auto, content-visibility), loader 76 → 160 with boundary rewiring (fig[75] next → #p-26-10-…), mobile tap-close + swipe, no-JS :target + visible arrows, zero console errors. min regenerated (11,489 B raw / 4,042 B gzip). Commit 23f4a1e. Unpushed — owner push pending. |
| 2026-09-24 |
PT25 done — the tile <img> lost its width/height. The hardcoded 512×384 was inert (tiles are fixed 4:3 CSS boxes; the img fills them with object-fit: cover) and wrong for non-4:3 photos. alt="" deliberately kept — removing the attribute is the bad practice (screen readers fall back to filenames). Verified: 60/60 pages byte-identical after re-inserting the attributes (2,522 tile imgs); browser geometry identical to PT24 at 1280/800/375 px (page heights, tile counts, 60 tile rects each); every tile still carries alt=""; zero errors. A11y note: the tile link still has no accessible name (pre-existing; a photo-name alt/aria-label stays an open option at ≈0 gzip). Commit 48c07a5. Unpushed — owner push pending. |
| 2026-09-24 |
PT24 done — the tile links lost class="image". CSS derives the tile styling structurally (.gallery-item > a > picture > img; base + ≤899 px border rule); main.js binds the close-href wiring to the picture’s parent <a>; min regenerated (11,488 B raw / 4,047 B gzip). Verified: 60/60 built pages byte-identical after reconstructing the change (76 tile anchors on the index, 227 on Namibia; 2 CSS selectors/page); browser (Chromium over HTTP): tile img computed style unchanged (absolute · cover · 1px black), ≤899 px border flips white as before, close href = the pre-click hash after a tile click, real mouse click on a tile opens its slide, zero console errors. Commit dec1fa4. Unpushed — owner push pending. |
| 2026-09-24 |
PT23 done — the site ships minified JS. assets/js/main.js stays the editable source; minify.sh (terser 5.44.0, deterministic, --check drift guard, src-sha256 marker) writes assets/js/main.min.js; the layout loads the minified file (fallback to main.js if absent). Sizes: 28,719 → 11,470 B raw, 8,471 → 4,036 B gzip (−4,436 B per first visit). Verified: all 60 built pages byte-identical to the PT22 build once the script ref is reverted; browser smoke (Chromium over HTTP, real CDP) on the built site ran the minified bundle — lightbox open/next/close, EXIF 7 items, arrows armed, loader 76 → 160 slides, zero console errors. Note: main.min.js needs git add -f (/assets/ ignore) — documented in .clinerules. Commit 854d656. Unpushed — owner push pending. |
| 2026-09-24 |
PT22 done — per-page SEO/social meta. <meta name="description"> (site / location locname+year / year variants), self-referencing <link rel="canonical">, og:title/description/url/image/type and a twitter:card on all 60 pages; og:image = the page’s first preloaded thumbnail (JPG, absolute — scrapers don’t do AVIF). Hand-rolled Liquid; no plugin added (default Pages build). Verified: removing the new block reproduces the PT21 build byte-for-byte on 60/60 pages; canonical/descriptions/og:image content-checked (index, Namibia, Berlin, /2026/); index +714 B raw / +175 B gzip. Commit fae4646. Unpushed — owner push pending. |
| 2026-09-24 |
Owner batch opened: PT22–PT27 (meta tags · JS minification · class/attr trims · deep-link eager loading). Measured against the PT21 build before carding: class/attr trims (PT24–PT26) = −10.3 % raw but only −1.1 % gzip on the index (−9,375 B raw / −138 B gzip; Namibia page −27.5 KB raw / −287 B gzip) — done as requested, framed as markup hygiene, not transfer savings. The batch’s real lever: terser minification of main.js → 28,719 → 11,388 B raw, 8,471 → 3,955 B gzip (−4.5 KB per first visit) (PT23 implements). Side finding (owner decision): the inlined main.css costs 5.9 KB gzip on every page; minifying it saves ≈2.6 KB/page. PT25 answers the alt question: keep alt="" (removing the attribute makes screen readers read filenames). Plans (local): .hermes/plan/PT22…PT27. |
| 2026-09-20 |
PT21 done — the dark blurred backdrop now stays steady across prev/next steps (owner report: it “reset” with a distracting flash on every step). Root cause (measured, not guessed — per-frame opacity sampling + animationstart logs + compositor screencast frames on rebuilt fixtures): the backdrop is the open figure’s ::before, and two lbFade animations restarted whenever a new figure became :target — the pseudo’s own fade and the figure’s group-opacity fade, which fades the whole subtree (backdrop included), so removing just the pseudo’s animation does not help (proven empirically). The backdrop’s opacity ramped 0 → 1 over ~500 ms on every step (0.15 at 90 ms, 0.51 at 180 ms); step frames showed the corner jump 28 → 139 (page showing through the missing blur). Rejected alternative: one persistent per-page backdrop element — it fixes the reset (constant 27.6 across steps) but cannot sit between a tile and its slide: the raised item (z-index: 20011, PT12) paints above it, so the item’s tile shows undarkened (corner 28 → 152 while settled); hiding the raised tile instead exposes the light page backdrop (images/bg-blurred.jpg) — a white flash on open (255) and a lighter patch under the dark layer (~51 vs 28). The backdrop can only live inside the item’s stacking context, i.e. per-slide — so the fix stops it from fading instead. Fix (CSS only, assets/css/main.css): the fade-in moves off the figure and its ::before onto the slide image (.lightbox-container img { animation: lbFade 0.5s ease; }) — the backdrop is created already dark on each step (seamless swap), while the image keeps its fade-in on open and on steps; the X and the arrows keep their own fades. Verified on the real rebuild (file:// fixture, pinned tab, screencast): nav1 + nav2 = 27.4 in every frame (was: bright flash → 500 ms fade), animationstart on ::before per step: none, image fade intact; desktop open/close, arrows, X glyph, catcher close and mobile 390×844 (steady frames, centre hit = A.close) unchanged; built index differs from the previous build only in the inlined <style> block (+342 B = the main.css delta), rest byte-identical. Deliberate side effect: the backdrop’s 0.5 s fade-in on the first open becomes a fast dim (~2–3 frames); restoring it would need a few lines of JS — left as an option. Commit baf8324. Unpushed — owner push pending. |
| 2026-09-20 |
PT20 done — the Select quick-links now navigate consistently and every year got its own page (owner report: the location/year quick-links were inconsistent — sometimes /#p-…, sometimes /year/location/; the year links should not land on one location but show all locations of the year, lazily loaded like the index). Root cause: PT16 kept the legacy in-page anchors for targets present on the current page and added a data-page fallback (+ JS rewiring) for everything else; the year fallback pointed at the year’s first location page. Fix: the popover links are plain absolute links (locations → /{year}/{NN}_{Location}/, years → /{year}/); data-page and rewirePopoverLinks() are gone. Every year now has a page from _years/ stubs (mklocations.sh writes them; years collection) rendered by _layouts/year.html: only_year + limit_locations prerender the newest 5 of the year and a year-scoped location-links nav drives the existing batch loader (deep-link hop, boundary rewiring, journey re-init included). Multi-year locations now build their per-year locdir (a same-name location with different locnums in two years previously got a wrong fallback URL). Verified: real build — all 57 existing pages byte-identical outside the popover block, every location href equals its former data-page fallback, sitemap 57→60, scoped navs/preloads on /2024/ /2025/ /2026/; file:// fixtures — the real /2026/ loads all 15 locations (76 → 462 slides, boundary arrows both ways, injected Morocco map registered), the index loads everything as before, popover navigation + deep-link hop + no-JS fallback verified, zero console errors. Commit 98c53b3. Unpushed — owner push pending. The tile location/year labels (pure-CSS :target filter) intentionally keep their in-page anchors — open question for the owner (see the PT20 plan). |
| 2026-09-20 |
PT19 done — the X close button now paints above the slide’s blurred backdrop (owner report: the X sat behind the blur). Root cause: PT12’s stacking fix raises the whole slide item (z-index: 20011, backdrop included) above the page-level .close link (20005), and the link cannot simply be raised — it is the click-anywhere catcher and must stay below the raised item, or its full-viewport ::before zone swallows the arrows (the PT12 trap). Fix (CSS only, assets/css/main.css): the open slide paints the X itself (figure.lightbox-container:target::after, z-index: 20012 inside the slide’s stacking context, pointer-events: none), the .close link keeps its hit box + catcher role but loses its own visuals, and hover/focus feedback is mirrored from the link onto the painted glyph (:has()). Verified on the deployed page (A/B by style injection, pinned single tab) and on the rebuilt file:// fixture: X pixel max 51 → 255 (crisp above the 80 % backdrop); hit map and click contract unchanged (zones navigate, X + centre close); phone taps close (real CDP touch, click-target log A:312,78), swipes one step, no-JS click closes, zero errors; the built index differs from the live page only in the inlined <style> block (+1367 B = the main.css delta), rest byte-identical. Commit 42fd7ab. Unpushed — owner push pending. |
| 2026-09-20 |
PT18 done — pinch-zoom now resets when the highlight closes (owner report: zoom left over from the opened image stayed active on the gallery, where pinch is blocked, so it could not be undone). Mechanism (settled by probing the live page + the rebuilt fixture with real CDP touch): a brief viewport-meta flip (maximum-scale=1, restored after 120 ms) clamps the page scale back to 1 — but only when it lands before the close navigation runs; a hashchange-time flip is too late for tap closes, and a same-tick flip is coalesced away. Shipped (main.js, +35 lines): flip on the .close link’s click (capture) + a hashchange fallback for closes that bypass the catcher. Verified: 3 fresh-load tap-close rounds → scale 5→1 (meta restored, 0 errors); js-close → 1; reopen → pinch works again (5); swipes still single-step; unzoomed tap-close and desktop click-close unchanged; the exact block injected on the live page (https) → both close paths reset 5→1. Caveats: iOS ignores the meta (its users can pinch out anyway); no-JS phones unchanged. Commit 714c9b6. Unpushed — owner push pending; owner phone re-test pending. |
| 2026-09-20 |
PT11 follow-up (2) — the 18 sideways photos turned from the owner’s list (commit 9d7f793). Owner reviewed the galleries: rocktreff + bremen fine; 18 broken photos named (Dubrovnik 7, Kotor 3, Faro 2, and one each in CSD/Utrecht/Thessaloniki/Meteora/Perast/Mostar). Fixed by rotating the pixels 270 CW (the camera convention — proven against the Morocco re-derive: same-size SSIM 0.88 vs 0.35 for the alternative), rebuilding thumbs from the rotated fulls (512x768; the OM-1 file 512x683) and re-encoding the AVIFs. Fulls end up at 683x1024 / 768x1024 — the ceiling when recovering from derivatives; the originals would allow 1024x1536 re-derivation. Caveat: the OM-1 file (13_CSD/P7250131) has no tagged OM-1 portrait reference in the repo (all 239 tagged references are Rotate 270 CW from the Fujis) — worth one visual check. Remaining candidates (399 listed in .hermes/plan/PT11-images/jpg-orientation-affected.md) are presumed landscape. Unpushed — owner push pending. |
| 2026-09-20 |
PT17 done — tap-to-close on phones (owner report: the highlighted image could not be closed any more; only tap-to-advance existed). Root cause (probed on the live page with real touch events at 390×844): the ≤ 768 px block had given every tap to “next” — .nav-next::before covers the whole viewport with pointer-events: auto, the slide backdrop ::before was pointer-events: auto (“no tap-to-close on touch (as before)”) and .close::before (the click-anywhere catcher) sat in the display: none !important hide list — so every probe point, incl. the visible ×, hit-test-resolved to A.nav-next. Fix (CSS only, assets/css/main.css, ≤ 768 px block): the nav-next zone and the backdrop give up their pointer events and the catcher is no longer hidden, so taps close; swipes keep navigating (PT15 handlers are document-level; the 0×0 phone-arrow guard already treats them as “not the control”). Verified on the rebuilt page (jekyll build → file://, mobile emulation, real CDP touch): tap centre / tap × → #p; swipe left→next / right→prev, one step, slide stays open; pinch still zooms (1→5×, PT6 intact); hit map → A.close at every probe point; desktop 1280 px (zones, centre-close, ←/→, wheel) and tablet 800 px (half-screen taps) unchanged; no-JS tap closes; no page errors. Blast radius: the rest of the built index byte-identical to the live build, only the inlined <style> changed (+338 B). Commit 49d533b. Unpushed — owner push pending; owner phone re-test pending. Consequences: ≤ 768 px narrow desktop windows also tap-to-close (wheel/keys still navigate); phones without JS can now close but not advance (before: advance, never close). |
| 2026-09-20 |
PT11 follow-up — 2026 JPGs have no Orientation tag (owner-reported; Morocco fixed, commit b24d0e4). Owner: vertical photos render sideways — “it’s the jpg”. Root cause: the 2026-03-16 resize.sh version wrote derivatives from a PNG intermediate and copied back only a 9-tag EXIF whitelist (no Orientation) and never rotated pixels — camera JPEGs of portrait shots (landscape pixels + Rotate 270 CW) lost their rotation, while the owner’s darktable exports (already rotated) stayed correct; hence the mixed/”random” symptom within a collection. Pre-existing defect, not a recent commit: the affected files each have a single June commit and are byte-identical to the live site (the AVIF side of this defect was found and fixed earlier today — ecd2613/ab907c7 — but the JPGs were untouched). Scope: nearly every 2026 location except ca. half of 01_London (census + per-file table: .hermes/plan/PT11-images/jpg-orientation-affected.md); the sideways subset = the camera-sourced portraits; 2024/2025 unaffected. Fixed now: Morocco’s 7 such photos were re-derived from the rescued originals with the corrected pipeline (thumbs/fulls/AVIF now portrait, orientation normal, EXIF kept, verified). Still open: the other locations need their originals (no local rescue copy); DSCF6957-darktable1 stays suspect (source missing). resize.sh is correct since ecd2613. Unpushed. |
| 2026-09-20 |
PT6 follow-up fixed - pinch-zoom on the opened image (commit c29150f). Owner phone report: pinch on the highlighted image did nothing. Root cause (verified with two-finger touch events on the live page): PT6’s overview gating (touch-action: pan-x pan-y on .gallery) also restricts the lightbox - PT12’s slide merge nests the open slide inside the gallery, so the effective touch-action on the opened image was pan-x pan-y, which blocks pinch (the PT6 plan had avoided body for exactly this ancestor-walk reason; the merge moved the overlay under the restricted ancestor). Releasing the gallery’s touch-action while a slide is open (body:has(.lightbox-container:target) .gallery { touch-action: auto; }) zooms 1→4.9× in the same probe; closed, the overview keeps pan-x pan-y. Verified on the rebuilt page: pinch zooms, swipes left/right (PT15) and tap-to-next unchanged, overview pinch stays blocked. Note: Input.synthesizePinchGesture is a no-op probe in headless Chrome - manual two-finger touch sequences are the reliable verification method. Unpushed - owner push pending. |
| 2026-09-20 |
PT11 correction — EXIF/orientation incident (owner-reported) + new owner rule. The backfill encoded raw pixels of JPGs whose display orientation comes from an EXIF tag (232 files / 116 photos carry Rotate 270 CW: 2024×14, 2025×216, 2026×2 — list in .hermes/plan/PT11-images/orientation-affected.txt), and the metadata-strip dropped the tag → those AVIFs rendered landscape while the JPGs render portrait; the re-encoded 2026 AVIFs had also lost their metadata. Fix: resize.sh now (a) keeps ICC/EXIF/XMP in AVIF outputs (no --ignore-* flags), (b) auto-orients a non-normal source before encoding (rotation baked into pixels; copied tag normalised — no viewer can double-rotate), (c) normal mode auto-orients its JPG/AVIF intermediates. All 3814 AVIFs re-encoded (--backfill --force, 0 failures, 232× [baked orientation]). Verified: 0 dimension mismatches vs magick -auto-orient across the affected set, Exif present 232/232, display SSIM mean 0.983 (min 0.960), coverage 1907/1907. Owner rule — now in AGENTS.md + README.md: EXIF must be kept stable at all costs; never strip it, never skip the orientation step. Bytes: AVIF total 351→531 MB (+180 MB of metadata kept — the deliberate price of the rule; still far under the JPEG totals, e.g. 2025 fulls 271.9 vs 643 MB). Commits ecd2613, ab907c7. |
| 2026-09-20 |
PT15 follow-up fixed - phone swipe swallowed by the oversized arrow zones (commit bced596). Owner report: swiping on the phone did nothing. Root cause (verified with touch emulation on the live page): on ≤768 px .nav-next::before covers the whole viewport (pointer-events auto) and at every width the zones cover the left/right screen halves - so every touch starts on an a.nav-prev/a.nav-next element and PT15’s guard (event.target.closest('.nav-prev, .nav-next')) treated it as an arrow tap and never started tracking (hiding the zone made swipes work immediately). Fix (main.js, touchstart guard only): a touch keeps tap behaviour only when it starts inside the arrow’s own rendered box (getBoundingClientRect() - 0×0 on phones where the glyph is hidden); touches on the zones now track swipes. Taps are unchanged (no movement = no swipe action; the zone’s click still advances). Verification: PT15 harness 26/26 incl. three new zone cases; real touch events on the rebuilt page - mobile swipe left→next / right→prev, tap→next, two-finger start ignored; tablet width (800 px) swipe from the left zone navigates; desktop wheel/arrow clicks unchanged. Unpushed - owner push pending. |
| 2026-09-20 |
PT12 follow-up fixed — lightbox stacking after the slide merge (commit a6d226d). Owner report: the backdrop did not cover the full screen (items after the open slide painted above it) and the prev/next arrows could not be clicked — any click closed the lightbox. Root cause (verified in Chrome on the live page and the rebuilt fixture): the .gallery-item fade-in animation (animation: fadeIn 0.5s forwards) keeps a stacking context alive after it finishes, so the fixed overlay (z-index: 20010) was clamped inside its own <article> — later items and the page-level .close catcher (20005) painted above it. Fix (CSS only): (1) the item owning the open slide gets z-index: 20011; (2) while a slide is open, the slide and its item are pointer-events: none so clicks fall through to the .close catcher, with .nav-prev/.nav-next re-enabled; (3) the ≤899 px filter: brightness(1.1) joins the containment guard (filter: none) — a filter re-anchors fixed descendants and left the overlay anchored to its tile at narrow widths. Verified on the rebuilt page (file:// fixture): hit-test map (arrows/close/image), arrow-click navigation, click-anywhere-to-close, paint-order probe (backdrop above later items), no-JS arrow click, loader-injected slides, standalone location page, mobile emulation (viewport-centred geometry, tap map unchanged); geometry unchanged (723×542 desktop, 813×542 fixture). Payload: index +1.1 KB raw / +0.4 KB gzip (mostly comments). Unpushed — owner push pending. |
| 2026-09-20 |
PT11 done — image pipeline modernization. (1) resize.sh: parallel job pool (-j, default nproc; 208 encodes in 14 s, 2682 in 2 m 49 s) and new modes --backfill (missing AVIF from the published JPGs, thumbs first), --backfill --force, --coverage; the metadata-strip experiment was reverted the same day — see the EXIF/orientation correction entry below. (2) Backfill executed for 2024–2025 + London: 2936 AVIF (1468 photos) → coverage 1907/1907 (fulls+thumbs). (3) Quality sweep (20 photos × 4 quantizers, bytes + SSIM) + ground truth vs Morocco originals → quantizers stay 14/22 (SSIM ≈0.98 vs the JPGs; AVIF-from-JPG vs original 0.972 ≈ the JPG’s own 0.974); 2026 AVIFs re-encoded metadata-free (fulls 107→77 MB, thumbs 48→15 MB). Bytes: 2024 fulls 48.5→13.1 MB / thumbs 19.6→3.5 MB; 2025 fulls 643→194 MB / thumbs 241→48 MB. Commits 8b9470b, 47a7b1c, 722208a. Details in .hermes/plan/PT11-image-pipeline-modernization.md. srcset variants not done (gated by PT10, parked). |
| 2026-09-20 |
PT11 side-finding: 2024–2025 JPGs are q≈99 (not q95) — 643 MB of 2025 fulls alone. Site images 1259→1455 MB on disk (AVIF adds artifact/git bytes while cutting visitor downloads ~70–80 %), so the ≤1 GB deploy warning applies harder. Remaining lever: transcode 2024–25 JPEGs q99→~q90 (−~300 MB, SSIM ≥0.98) — irreversible-ish (git keeps the old blobs); owner decision, not executed. |
| 2026-09-20 |
PT14 push verified — owner force-pushed master (c0a35d6); origin branches redesign/namibia/multi-map deleted; remote tree matches local (2da1d517…); Pages rebuild for c0a35d6 succeeded; master in sync with origin/master; fresh clone check passed. Local .git 1.2 GB. GitHub repo-size figure shrinks over time (server gc is eventual — was ~1.5 GB at push time). |
| 2026-09-20 |
PT16 implemented — per-location pages + progressive loading. Every location gets a page at /{year}/{NN}_{Location}/ from front-matter-only stubs in _locations/ (regenerated by ./mklocations.sh); the index prerenders the newest 5 (prerender_locations) and lists all 56 in a location-links nav, which doubles as the id → page manifest. main.js fetches batches of 5 pages as the visitor nears the end, injects the galleries, rewires the prev/next chain at both batch boundaries, re-registers injected journey maps from their data-progress attributes and rewires popover links to the now-in-page anchors; a deep link to a non-prerendered photo hops to that photo’s page. Two fixes found on the way: main.css became a Jekyll page (its front matter lets every page inline it via site.pages; include_relative resolved against the rendering file and broke for collection documents) and the containment guard needed content-visibility: visible as well — content-visibility: auto implies containment, which re-anchored the fixed lightbox to its 320 px grid cell (caught by the geometry test, now a documented pitfall). Index 1.34 MB → 86.6 KB raw, 72.4 → 10.9 KB gzip; 56 pages + sitemap; build ~82 s (GH Pages limit 10 min). Verified on a file:// fixture: 76 → 306 slides, 2025 section created, boundary arrows rewired both ways, journey map registered + initialised, popover re-rewired, deep-link hop, no duplicate ids, zero JS errors; lightbox geometry 813×542 centred with and without JS; CV page height delta 0 px. Commits 3fe8a82, 91b36ad. Unpushed — owner push pending. |
| 2026-09-20 |
PT12 (a) landed — baked backdrop + content-visibility. images/bg-blurred.jpg (96 px, 1.7 KB, regenerated by ./blur-bg.sh) replaces the runtime backdrop-filter (pixel diff vs the live composite: mean 0.5/255, p99 10/255); .gallery-item gains content-visibility: auto with contain-intrinsic-size: auto calc(100vw/N) (N = column count — delta 0 px at every breakpoint, where a fixed placeholder inflated the page height by 62 %). Commit 4681a48; the :has() guard fix rides with PT16 (3fe8a82). |
| 2026-09-20 |
PT14 executed (local) — (1) Rescue first: 170 files (163 full-size Morocco sources + 7 staged P8010*) → IdeaProjects/pictrace-morocco-sources/, md5-verified (2,319.6 MB), then the 7 staged phantoms unstaged. (2) Prune: reflog expire + gc --prune=now removed the 2.22 GB cruft pack (+63 MB phantoms) → .git 3.6 → 1.5 GB. (3) Rewrite: git-filter-repo --path images/fulls/ --invert-paths --strip-blobs-bigger-than 2M — the pre-restructure originals (~347 MB, 240 versions) removed from all history; content trees preserved (state commit tree byte-identical, a157377a…; fsck clean); .git now 1.2 GB (single pack). Old→new commit hashes: .hermes/plan/PT14-audit/filter-repo/commit-map — the hashes previously listed on this board were remapped through it. (4) Local branches redesign/namibia/multi-map deleted (all merged; multi-map wip archived as PT14-audit/multi-map-wip.patch). (5) origin remote re-added (filter-repo removes it by design). Safety net: pre-rewrite bundle (1.57 GB, all refs) + WIP patches + untracked copies in IdeaProjects/pictrace-backups/. Notes: executed in place; a parallel session was committing during the operation (its PT12 blur-backdrop work is preserved — re-verified). Remaining (owner): git push --force-with-lease=refs/heads/master:d809adac857028b69d10316617ec98ba36e3ffef origin master + git push origin --delete redesign namibia multi-map (server-side gc afterwards is eventual). |
| 2026-09-20 |
PT16 opened — per-location pages + progressive single-page loading (owner-proposed: every location gets a page; the index prerenders N=5; a links block lists all pages in legacy order; JS inserts the next batch before the viewport reaches the end; lazy loading unchanged). Plan confirms the approach with must-solves: deep links into unloaded locations (the links block doubles as the id→page manifest), journey-map re-init for injected content (inline scripts do not run — move the data to data-* attributes), prev/next chain rewiring at batch boundaries, page generation via a Jekyll collection + generated stubs (no plugins on the default Pages build). Details: .hermes/plan/PT16-per-location-pages.md. |
| 2026-09-20 |
PT12 batch 2 done — fragment compaction: slide ids p-{yy}-{locnum}-{basename} (mean 32.8 → 21.4 chars), section anchors p-{yy} / p-{yy}-{locnum}, redundant nav-arrow class dropped; .clinerules documents the scheme. Verified: prev/next chain self-consistent, tile links match slide order, all in-page links resolve. 1.46 MB → 1.34 MB raw, gzip 72.4 KB; both batches together −20 % raw / −12.6 % gzip. Previously shared #p-… links break (fragments cannot redirect). Commit 69ad0b1. |
| 2026-09-20 |
PT12 batch 1 done — merge wrappers (each photo now has one root: the slide figure lives inside its gallery <article>; the iterator renders once with a precomputed id list, keeping the global prev/next chain identical), micro-trims (data-name removed, literal ❮/❯, shorter aria-labels), whitespace collapse in compress.html (−164 KB of inter-tag runs), lightbox CSS reworked (targeted figure is the fixed overlay; backdrop via figure::before; the shared .close link is the click-anywhere catcher; contain/backdrop-filter are neutralised while open because both re-anchor fixed descendants — verified empirically). Checks: chain byte-identical to the old build, all links resolve, lightbox geometry pixel-identical to the live site (desktop + mobile emulation), no-JS arrows visible, location/year filter unchanged. 1.67 MB → 1.46 MB raw, gzip 82.8 → 74.1 KB. Commit 1f1b98c. |
| 2026-09-20 |
PT13 set dormant — gated by the PT10 decision (native build stays): Jekyll-4 can’t run on the native builder (env pins Jekyll 3.10.0 / github-pages 232) and Astro/Eleventy can’t be built natively at all; any outcome requires reopening the deploy question first (custom build / prebuilt output / other host). If it reopens, decide PT10 + PT13 together. Plan note added: .hermes/plan/PT13-framework-spike.md. |
| 2026-09-20 |
PT10 parked — stay on the default Pages build (owner call: stick with the current solution). Findings: the site is already built by a vendor-owned Actions workflow (pages-build-deployment; run #131 success, 2m22s; build job = ghcr.io/actions/jekyll-build-pages:v1.0.13; env Jekyll 3.10.0 / github-pages 232 / ruby 3.3.4). Original rationale corrected: SEO plugins already whitelisted (jekyll-seo-tag 2.8.0, jekyll-sitemap 1.4.0); the 10-min limit is a deploy timeout that applies to custom workflows too; the run page already shows jobs + annotations. Revisit triggers: a non-whitelisted plugin or custom build actually wanted · vendor pipeline failing · 10 builds/h soft throttle hit (worst burst so far ≈5/h) · branch-deploy deprecation signals · PT13 keeps Jekyll and adds build needs. Side findings: deploy warns “artifact 1.03 GB exceeds the allowed size of 1 GB — deployment might fail” → weight reduction is the PT11 lever (pipeline-independent); Gemfile pins were behind the live env → aligned (commit 3e8ec49). Plan: .hermes/plan/PT10-github-actions-build.md. |
| 2026-09-20 |
PT15 done — lightbox gestures: horizontal trackpad/wheel (≥45 px cumulative, 220 ms quiet-lock absorbs inertia, ctrl/meta+wheel ignored) + one-finger swipe (≥50 px, 1.5× ratio, single-touch only — pinch/pan & touchcancel safe); both delegate to PT1’s .nav-prev/.nav-next. 24/24 stub-DOM harness tests (.hermes/plan/PT15-tests.js); owner phone test pending (rides with PT6). Commit 4d2e376. |
| 2026-09-20 |
PT9 done — exifr 7.1.3 (MIT) + FontAwesome 4.7.0 woff2 (SIL OFL 1.1) self-hosted (assets/js/vendor/, assets/fonts/) with license files; both verified byte-identical to upstream (sha256). License check: mirroring allowed (MIT/OFL); the EU GDPR risk (third-party CDN embedding) is removed by this change. Browser check: only first-party requests. Commit 81e7fc6. New assets/ files committed via git add -f (/assets/ ignore pending owner decision). |
| 2026-09-20 |
PT15 opened — lightbox gestures: horizontal wheel (PC) + one-finger swipe (mobile) for prev/next, reusing PT1’s delegation to the .nav-prev/.nav-next links. Details: .hermes/plan/PT15-lightbox-gestures.md. |
| 2026-09-20 |
PT8 done — dead CSS/HTML stripped (commented ::after block, unmatched #gallery-close rules), 24+0→24n+0 (no visual change), dead form attrs removed. Footer Design: credit + .gitignore /assets/ line flagged for owner decision. Commit 5a0e06b. |
| 2026-09-20 |
PT7 done — _site junk (root 3.7 GB + nested 33 MB) deleted, dead .travis.yml removed, build excludes added, /0/ ignores collapsed, git gc packed 6,913 loose objects (4.78 GiB → consolidated). Commit 0944396. |
| 2026-09-20 |
PT6 done — pinch-zoom is lightbox-only (user-scalable=no removed; touch-action: pan-x pan-y on the gallery; works on modern iOS; desktop/layout unaffected). Aria labels on prev/next/close; owner phone test pending. Commit fd5a57486ec1f1c7d1cd6022e0fc2de9b11dba96. |
| 2026-09-20 |
PT6 scope refined per owner: pinch-zoom is wanted on the opened photo only — overview stays fixed, big-screen layout scaling unchanged. Chosen mechanism: drop user-scalable=no, apply touch-action: pan-x pan-y to the gallery (works on modern iOS too; see .hermes/plan/PT6). |
| 2026-09-20 |
PT3 done — precomputed AVIF set replaces O(N·M) per-image scans (3,821-path equivalence test, 0 mismatches). Fix commit 5af1685c02ed16f54ae63c6eedcc4f71099b4063. |
| 2026-09-20 |
PT4 done — thumb preloads guarded (AVIF if present, else JPG; no 404 preloads). Fix commit aebc45b6327391fb85668502ed68ee2a63b0dc61. |
| 2026-09-20 |
PT5 done — resize.sh -d deletes only successfully processed originals (interrupted runs delete nothing; unknown flags warn). Fix commit 03a2c446377a9c0b9cf40745c4442c80b2ac7c4b. |
| 2026-09-20 |
Source review completed (templates, JS, CSS, scripts, assets, history). Findings + plans in .hermes/plan/2026-09-20-source-review.md; PT3–PT9 opened (quick wins), PT10–PT14 deferred (refactors / higher risk). |
| 2026-09-20 |
PT2 done — nav arrows hidden until the slide image is ready (img-ready + opacity fade; @media (scripting: none) keeps them visible without JS; links stay clickable while hidden). Fix commit 822ad561eeae1903566feb253587900e5e98ab98. |
| 2026-09-20 |
PT1 done — keyboard navigation via arrow keys (1f7d83e7b1a834a36e867cae6107ef47e3895f6f). Task details moved to .hermes/plan/ (one file per task). |
| 2026-09-20 |
AGENTS.md + KANBAN.md created; PT1/PT2 opened (P2). Lightbox stays hash/:target-based — enhancements go through main.js + arrow CSS, no re-architecture. |
| 2026-09-20 |
PT2 direction: fade arrows in on image load (opacity, links stay functional) as the primary approach; viewport-anchored arrows kept as fallback if the fade proves insufficient. |