Files
portfolio-next/docs/TODO.md
T
Lucas WintherandClaude Opus 5.5 5491230807 docs(agents): work-k2 is the visual truth for /work/
Owner asked for the rules file change on 2026-11-12.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:56:29 +01:00

12 KiB
Raw Blame History

TODO

Durable backlog. Agents append here instead of leaving TODO comments in source. Small, specific, independently actionable. Remove only when actually done.

Before go-live (owner)

  • Confirm the 13 old projects' dates: they are conventional (2021-01-07…19) and visible on /everything/.
  • Fill in project years where known (the field is optional; unknown ones are simply not shown).
  • Set the AI level of the AU projects and un-draft them (content repo); unknown stays unknown.
  • Write the intro and "now" lines (content repo); nothing is invented meanwhile.
  • Write personal_intro in site.yml (content repo): the lede on the start page's right half and /off-the-clock/. Until then there is none (the mockup's "A wall of what's caught me lately…" line is no longer hard-coded).
  • Check the Junkwraith comic item's line by eye before un-drafting it (content repo items/junkwraith.md; transcribed from a Bluesky share).
  • Add the comic images (media/) and the CV PDF (site.links.cv, a filename under media/).
  • Verify the Ghost, Trakt and Hardcover modules against the live services. Done 2026-11-11: the live /status reports ghost, trakt, hardcover and listenbrainz all ok, and the strip shows a book, a film with its rating and the week's track. Not yet seen live: a Hardcover book with page progress.
  • Make the Trakt profile public (the build uses no OAuth). If it must stay private, add OAuth in src/sources/trakt.ts.
  • DNS cutover and the 2021 site's move to old.lucaswinther.info, per docs/DEPLOY.md (done 2026-11-10: the config repo's portfolio stack serves the 2021 site there).
  • Redirect the old blog URLs (blog.lucaswinther.info/<slug>/) to /work/writing/<slug>/ (DEPLOY step 7), and decide what stays on the blog host. Post pages link "Read it on the blog →" to the Ghost URL, so that link must change or stop redirecting once the blog redirects.
  • Old report links (lucaswinther.info/documents/<name>.pdf, e.g. from a CV or LinkedIn) break at the DNS cutover: the reports now live at /media/reports/ (lowercase names) and in the archive at old.lucaswinther.info/documents/. Redirect one way or the other if anything still points there. Done 2026-11-10: the config repo's nginx-proxy.yml sends /documents/ on the apex to old.lucaswinther.info with a 302.
  • Check the gate palette against the approved mockups/nav-d.html once more on the live site (left gate light, work dark: kept on purpose, owner to confirm).
  • Reword the 2021 portfolio project summary if wanted ("The 2021 version of this portfolio…", derived from its info.json).

Next

  • Work through docs/audits/2026-11-11-audit.md (24 findings from the 2026-11-11 audit; each row carries its own status).
  • Start page: the owner feels the personal half lag on hover (2026-11-10). Findings so far: his browser gets ~70–80 fps on a 144 Hz monitor even on a blank page (testufo: 70; Firefox layout.frame_rate is -1), so the cap is his machine, possibly load from other programs; recheck with nothing else running. The grain's live filter was a real cost and is now a tile (DECISIONS). Still open: the lean animates flex-grow, a layout animation repainting both halves every frame; a transform/clip-path lean would run on the compositor. Hover lab (local only): .tmp/real-site/hover-lab.html switches off each effect and shows fps; baseline .tmp/real-site/fps.html.
  • The strip's Listening cell replaced the newest pinned song (owner: "for now, then we can look at alternatives later"). The pinned song's note ("why ↘") now has no spot in the strip; look at alternatives.
  • Revisit the start page's "or wander through everything at once" pill (and its G shortcut). Hidden for now on the owner's request: SHOW_EVERYTHING_PILL in src/site/pages/index.astro. Its CSS, the seam positioning in src/site/scripts/gate.ts and the seam check in e2e/gate.spec.ts are kept; /everything/ is still reachable from /simple.
  • Personal wall: a chip filter is not reflected in the URL, so a filtered view can't be shared.
  • Link previews: a site-wide preview image for pages without a picture of their own (the start page, /work/, posts, the wall, issues). A design call for the owner (spec 007 left it out); until then those pages share a plain summary card with no image.
  • Feeds: no visible link to /feed.xml, /work/feed.xml or /off-the-clock/feed.xml yet, only the rel="alternate" links in each head (spec 007). Where a visible one would go, if anywhere, is the owner's design call.
  • Film soundtrack, alternative design: mockup B (the soundtrack as a record tucked behind the poster, slides out on hover; mockups/shots/spec-002-r1-cards.png). The owner kept it as an idea for later or as an alternative to the tray.
  • Single-cover comic, alternative design: mockup G (ink-framed cover beside the text). J (cover leaning on the paper with the one-liner) "could be interesting but doesn't work yet". I (cover plus two panels) only with real, different page images — today's panel strip already does that when an item has three images.
  • Media images ship as-is: the build copies media/ verbatim and cards use plain <img> (no astro:assets). Resize/convert at build (e.g. sharp, a new runtime dependency: ask the owner) or keep resizing by hand in the content repo.
  • Spec 002 review minors: the tray's "plays from … when you press play" note is wrong without JavaScript (it's a link out then); focus drops to <body> when EmbedOnClick swaps in the iframe; interestFor should prefer an interest matching its pattern over the primary (a film listed [music, film-tv] files the Trakt card under music); Music.astro embed label uses video?.alt though the embed plays entry.link; .panels.cover shares the global .cover class name.
  • Spec 003 review minors: on phones a flipped film card keeps a 40% poster where others get 38% (.film-grid.flip outranks the ≤620px rule); sameFilm doesn't fall back to the slug when the history has a trakt id but the rating entry lacks one; the last film's date is the UTC day (a film logged just after midnight in Copenhagen shows the day before).
  • A film's soundtrack as an audio file in media/ (only a url makes a tray today).
  • Personal wall: Bandcamp page URLs link out instead of embedding (only EmbeddedPlayer URLs embed); consider an optional embed field.

Review follow-ups (deferred minors, by area)

Library and CLI

  • src/library/load.ts: a numeric title reports "required"; bad issue filenames give duplicate errors; invalid interests.yml gives noisy unknown-interest errors.

  • src/core/ai.ts: isVisible has no default branch for un-parsed preference values (fails closed); no isAiLevel test. Sources

  • src/sources/cache.ts: a failed cache write is swallowed silently and the cached shape is unchecked; a corrupt sources.json reads as "not fetched" in src/site/data.ts.

  • src/sources/index.ts: the timeout wrapper overwrites a caller's init.signal; no test for a body-read stall.

  • src/sources/trakt.ts: season/episode ratings are labelled as the show; thin 401/404 tests for Trakt and Hardcover; Hardcover author may be ''.

  • src/site/data.ts: "today" is UTC; the dev-server memo never refreshes. Runtime and deploy

  • scripts/archive-old-site.sh: curl stderr is hidden for individual files.

  • Dockerfile chowns node_modules in a duplicate layer; the healthcheck returns 200 even when a source has lastError.

  • src/runtime/: a failed sources-only rebuild is untested; sources.json is written before Astro with no rollback.

  • test/runtime: the token-stripping test checks the buildEnv helper, not a real spawn; the serve test's page check is weak. Site (work)

  • The start page's collage note clips at ~821–1056px.

  • Sheet: the scrollbar shifts when it opens (scrollbar-gutter).

  • Colour-only hovers in the personal CSS are not all under (hover: hover).

  • Filter boxes ignore data-show; "Latest writing" considers only the newest 3; Showcase.astro branches over the showcase ids by hand (Astro needs static island imports). Site (personal, issues, everything)

  • Personal wall: year dividers ignore the chip filter and the AI filter; dense-grid order differs from DOM order; own-audio path is untested (no audio fixture); a chip-filtered summary does not follow "show all".

  • Preference: the storage key is duplicated in the inline pre-paint script in Base.astro, and PAINT_SWITCH in scripts/pref-switch.ts mirrors paintSwitch (both on purpose, no imports there; a unit test keeps the painters in step). The pressed-state flicker until hydration is gone (spec 006: the switch is plain HTML painted before first paint). Tests

  • test/site/fixture-sites.ts: the fixture build child has no timeout. On 2026-11-11, with the machine at load ~15, one bun test run hung in bun src/cli.ts build fixtures/sparse (idle, 5 minutes) until the 300 s beforeAll limit failed the run; three reruns of the build and two of the suite passed in 12 s. Not reproduced, cause unknown.

  • e2e: helpers hard-code localhost:4378; the third-party check is not re-run after the play click; "nothing animates" uses a getAnimations snapshot; the no-JS featured count is hard-coded (now 6); sparse specs lack positive controls; the YouTube exemption matches by substring; bun test can contend with an overlapping Playwright server.

  • Missing automated tests: sheet history, keys and focus; filter toggling on /everything/; dead-anchor and live-region regressions; a date-independent issue test; the CLI build path; the CSS filter contract and pre-paint script.

  • Gemini allow run_shell_command(bun test) may not match argument forms (cosmetic).

  • Plan 009 asked for axe checks of /work/ and /work/learning/ in e2e/a11y.spec.ts, but the repo has no axe dependency (adding one needs the owner's yes). The learning page was added to the existing alt-text and reduced-motion checks instead.

Recurring

  • Maintain AGENTS.md:
    • Add a rule only in response to a real failure. When an agent repeatedly gets something wrong, that is a gap; add one line, ideally with a code example. Do not pre-empt hypothetical mistakes.
    • Update in the same PR as the convention change. A stale rules file is worse than none — it actively misleads.
    • Prune quarterly. Delete rules whose convention has changed or whose mistake no longer occurs.
    • Optional, cheap validation: run the same task twice in clean sessions, with and without the file, and compare steps and token use.

Known issues

  • A project file named learning.md would claim /work/learning/, the course page (spec 009); the loader does not reserve that id the way RESERVED_PERSONAL_IDS guards issues off the clock.
  • RowingStroke showcase at 390 px: the chart's "one stroke · force at the handle" caption and the "scrub the curve" hint overlap (seen while checking spec 009; the showcase itself is unchanged).
  • Placeholders in the mockups are inconsistent between rounds (band name, instrument, university, years). Resolve with real data when building.
  • mockups/tech-r3-c.html: pointer effects (tilt, scrub, peer hover) were only checked by reading the code, never in a real browser.
  • mockups/tech-r4-b.html: the hover preview is enabled by viewport width (≥900px), not (hover: hover); don't copy that into the real build.
  • /everything/ filter counts are totals; they don't narrow with the other filters or the "projects made" preference (the "N of M things" line does). /everything/ and the issue pages have no preference switch of their own.
  • Issue pages: a lead entry that can't share its row stretches to the full 9 columns, so a single drawing can become a very large spread. Revisit with real art.
  • Contrast below WCAG AA in the mockup palettes (impeccable audit, Task 12): small metadata text in work --dim #625c54 on --bg (2.8:1), personal --dim #a8947f (2.4:1) and --mute #7d6a5c (4.2:1) on cream, /everything/ #786a62 (3.5:1), gate --t-mute (4.1:1) and --t-acc labels (3.3:1). Left as approved; needs the owner's call on lightening those tokens.
  • Start page: the leaned-away half still clips its content at 821–1056px while the other side is hovered or focused (the resting layout fits).