Owner asked for the rules file change on 2026-11-12. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
6.7 KiB
portfolio-next
The next lucaswinther.info: a two-gate personal site (work / off the clock). Bun 1.3, TypeScript, Astro
(static) with React islands, one Docker container that polls a separate content repo and rebuilds (see docs/DECISIONS.md).
Sister repo with the same stack and conventions: commission-compass (Gitea, lucasw89/); copy its patterns before inventing new ones.
AGENTS.md is the single source of truth for agents here. CLAUDE.md and GEMINI.md only point here.
Deeper context on demand: docs/ARCHITECTURE.md, docs/DECISIONS.md, docs/TODO.md.
Commands
Verified in this repo (see docs/PERMISSIONS.md):
bun install --frozen-lockfile— install from the lockfilebun run typecheck—tsc --noEmitbun test— whole suite (bunfig.tomlroots it attest/)bun test test/x.test.ts— one file;bun test -t 'name'— tests matching a namebunx playwright test— e2e against a built fixture site on port 4378 (bun run e2e); asks firstbun src/cli.ts validate <dir>— check a content dir, list every problem (fixtures:fixtures/content)SOURCES_FIXTURES=fixtures/sources bun src/cli.ts build fixtures/content .tmp/site— render the static sitebun src/cli.ts preview .tmp/site [port]— serve a built site (default 4321)docker build -t portfolio-next:local . && scripts/smoke-test.sh portfolio-next:local— image builds and serves fixtures Bun is the runtime and package manager, never Node, npm, pnpm or yarn:bun <file>notnode,bun testnot jest/vitest,bun install/bun run <script>,bunxnotnpx. Bun loads.envitself; don't add dotenv. Prefer Bun built-ins (Bun.serve,Bun.file,Bun.$`…`,Bun.YAML). Exception: the site is Astro (built on Vite).bun init's "HTML imports, don't use vite" advice does not apply to it.
Workflow
Required skills: superpowers, impeccable. Check at session start; install if missing.
Specs → docs/specs/NNN-slug.md (permanent). Plans → docs/plans/NNN-slug.md while in progress;
git rm them before opening a merge request — the spec is the record.
(Overrides the superpowers default paths, e.g. docs/superpowers/specs/.)
Anything beyond a trivial fix: brainstorm → spec (sign-off) → plan → TDD → review. TDD means failing test first, watch it fail, minimal code, watch it pass, commit. Tick plan checkboxes as you go so an interrupted session is resumable. Trivial changes (typo, version bump, one-line config): say "trivial, skipping spec" and proceed.
Use impeccable for user-facing UI — craft for new, audit/polish on existing,
harden before shipping. Design context: PRODUCT.md, DESIGN.md.
Rules
- The chosen mockups are the visual source of truth:
mockups/nav-d.html(start page),mockups/work-k2.html(/work/, spec 009; built onmockups/tech-r3-c.html, which still sets the rest of the work space: panels, list, side sheet, project pages),mockups/personal-r2-b.html(off the clock). Match them; don't drift towards a generic template. Every other file inmockups/is a rejected or reference round. docs/inspiration.mdrecords the owner's taste in his own words. Read its principles before any visual change. Hard lines: no loading screens, nothing moves unless the visitor acts, no canvas/game feel, nothing that sells, no purple/blue "AI" gradients.- Every project carries an AI level:
ai-made|some-ai|by-hand| unknown. Never guess one; unknown stays unknown and is shown as such. - Never invent facts about the owner (jobs, dates, life events, opinions). Unknown facts stay out of
content, or go in as
draft: true; ask the owner. Real work history:extra_info.txt.
Architecture
src/core pure logic shared by build and browser · src/library content loading and validation ·
src/sources Ghost/Trakt/Hardcover clients and cache · src/site Astro pages, components, layouts, styles ·
src/runtime build, refresh (poll, rebuild, atomic swap), serve · src/cli.ts entry point ·
fixtures/ synthetic content and source JSON · e2e/, test/ · deploy/, scripts/ · archive/2021/ old site ·
mockups/ the approved visual source of truth.
Full picture: docs/ARCHITECTURE.md — read it before your first change in a session.
Content format: docs/CONTENT-CONTRACT.md. Deploying: docs/DEPLOY.md.
Non-obvious rules
src/corestays pure: no imports from othersrc/folders, no Node/Bun APIs, no I/O. The browser bundles it.- No network during Astro rendering. Only
buildSitefetches (once); pages read the cachedsources.json. - The real content lives in the separate
portfolio-next-contentrepo. Fixtures here are synthetic; never copy real content in. Tests setSOURCES_FIXTURES=fixtures/sourcesand never call Ghost, Trakt, Hardcover or Gitea. - Filter contract: set
data-aionly whereai !== null; elements without it are never hidden by the preference. Never writedisplay: … !importanton[data-ai](it defeats the hide rules). - Islands use
client:idlewhen they sit off-screen (sheet, chips);client:visiblewould never fire. - Nothing third-party loads before interaction, except the analytics script. Fonts are
@fontsource*. - Test imports use explicit
.tsextensions.
Boundaries
Never touch: .superpowers/ (local brainstorm sessions, gitignored), extra_info.txt (owner-maintained).
Never commit .env, secrets, or credentials.
Never force-push, rewrite pushed history, or commit directly to main.
Ask first: new runtime dependencies · deployment, containers or the owner's homelab config ·
anything destructive (rm -rf, git reset --hard) · work the approved plan does not cover.
If the task turns out much larger than the plan assumed: stop, report, re-plan.
Docs duties
Same commit as the code, not "later":
docs/ARCHITECTURE.md— when a module, boundary, or data flow changes.docs/DECISIONS.md— append-only; any choice a future maintainer would reverse-engineer.docs/TODO.md— anything noticed and deliberately not fixed. NoTODOcomments in source.docs/PERMISSIONS.md— together with.claude/settings.json,.codex/,.gemini/settings.json, in one commit.
Git
Branches: feat|fix|chore/<slug>. Commits: Conventional Commits. Small, each leaving tests green.
Merge to main locally, and only when the owner asks and approves. No pull requests.
Done means verified
Do not report success on work you have not run. Before saying done:
tests run in this session and seen passing · lint and typecheck clean · matches the approved spec ·
UI changes rendered and looked at (headless Chrome works: google-chrome --headless=new --no-sandbox --screenshot=out.png --window-size=1440,900 file://$PWD/page.html) · docs duties complete ·
git status clean and the diff is only what you intended.