chore: bootstrap agent-ready docs and rules

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Lucas Winther
2026-10-23 18:25:46 +02:00
co-authored by Claude Opus 5.5
parent 10ce858b31
commit 80f6c97127
17 changed files with 486 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
{
"permissions": {
"allow": [
"Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git show:*)",
"Bash(git branch --list:*)", "Bash(git rev-parse:*)", "Bash(git add:*)", "Bash(git commit:*)",
"Bash(bun install --frozen-lockfile:*)", "Bash(bun test:*)", "Bash(bun run typecheck:*)",
"Bash(bun run build:*)", "Bash(bun run dev:*)", "Bash(bun run start:*)"
],
"ask": [
"Bash(git push:*)", "Bash(git merge:*)", "Bash(git rebase:*)", "Bash(git switch:*)",
"Bash(git restore:*)", "Bash(git commit --amend:*)", "Bash(rm:*)",
"Bash(bun add:*)", "Bash(bun remove:*)", "Bash(bun update:*)", "Bash(bun upgrade:*)",
"Bash(bunx:*)", "Bash(npx:*)", "Bash(docker:*)"
],
"deny": [
"Bash(git push --force:*)", "Bash(git push -f:*)", "Bash(git push --force-with-lease:*)",
"Bash(git reset --hard:*)", "Bash(git clean:*)", "Bash(git add -f:*)", "Bash(git add --force:*)",
"Bash(git commit --no-verify:*)", "Bash(git commit -n:*)",
"Bash(rm -r:*)", "Bash(rm -R:*)", "Bash(rm -rf:*)", "Bash(rm -fr:*)", "Bash(rm -Rf:*)",
"Bash(rm -fR:*)", "Bash(rm --recursive:*)", "Bash(sudo:*)",
"Read(./.env)", "Read(./.env.*)", "Read(./secrets/**)"
]
}
}
+2
View File
@@ -0,0 +1,2 @@
sandbox_mode = "workspace-write"
approval_policy = "on-request"
+20
View File
@@ -0,0 +1,20 @@
# Rendered from docs/PERMISSIONS.md. No allow rules: in Codex, allow means "run outside the sandbox".
# Deny
prefix_rule(pattern = ["git", "push", ["--force", "-f", "--force-with-lease"]], decision = "forbidden",
justification = "Force-push rewrites shared history.")
prefix_rule(pattern = ["git", "reset", "--hard"], decision = "forbidden", justification = "Discards work.")
prefix_rule(pattern = ["git", "clean"], decision = "forbidden", justification = "Deletes untracked files.")
prefix_rule(pattern = ["git", "add", ["-f", "--force"]], decision = "forbidden",
justification = "Stages ignored files, e.g. secrets.")
prefix_rule(pattern = ["git", "commit", ["--no-verify", "-n"]], decision = "forbidden", justification = "Skips hooks.")
prefix_rule(pattern = ["rm", ["-r", "-R", "-rf", "-fr", "-Rf", "-fR", "--recursive"]], decision = "forbidden",
justification = "Recursive delete.")
prefix_rule(pattern = ["sudo"], decision = "forbidden", justification = "No privilege escalation.")
# Ask
prefix_rule(pattern = ["git", ["push", "merge", "rebase", "switch", "restore"]], decision = "prompt")
prefix_rule(pattern = ["git", "commit", "--amend"], decision = "prompt")
prefix_rule(pattern = ["rm"], decision = "prompt")
prefix_rule(pattern = ["bun", ["add", "remove", "update", "upgrade"]], decision = "prompt")
prefix_rule(pattern = ["bunx"], decision = "prompt")
prefix_rule(pattern = ["npx"], decision = "prompt")
prefix_rule(pattern = ["docker"], decision = "prompt")
+21
View File
@@ -0,0 +1,21 @@
{
"tools": {
"allowed": [
"run_shell_command(git status)", "run_shell_command(git diff)", "run_shell_command(git log)",
"run_shell_command(git show)", "run_shell_command(git branch --list)", "run_shell_command(git rev-parse)",
"run_shell_command(git add)", "run_shell_command(git commit)",
"run_shell_command(bun install --frozen-lockfile)", "run_shell_command(bun test)",
"run_shell_command(bun run typecheck)", "run_shell_command(bun run build)",
"run_shell_command(bun run dev)", "run_shell_command(bun run start)"
],
"exclude": [
"run_shell_command(git push --force)", "run_shell_command(git push -f)",
"run_shell_command(git push --force-with-lease)", "run_shell_command(git reset --hard)",
"run_shell_command(git clean)", "run_shell_command(git add -f)", "run_shell_command(git add --force)",
"run_shell_command(git commit --no-verify)", "run_shell_command(git commit -n)",
"run_shell_command(rm -r)", "run_shell_command(rm -R)", "run_shell_command(rm -rf)",
"run_shell_command(rm -fr)", "run_shell_command(rm -Rf)", "run_shell_command(rm -fR)",
"run_shell_command(rm --recursive)", "run_shell_command(sudo)"
]
}
}
+2
View File
@@ -4,3 +4,5 @@ dist/
CLAUDE.local.md
.worktrees/
screenshots/
.superpowers/
.claude/settings.local.json
+112
View File
@@ -0,0 +1,112 @@
# EPIC: The Musical Timeline
Unofficial fan site: an interactive timeline of *EPIC: The Musical* songs. Currently 7 design-direction
mockups. Bun 1.3.14 (server, bundler, test runner), TypeScript, three.js (mockups 06/07 only), Docker.
`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
Install: `bun install --frozen-lockfile`
Dev: `bun run dev` (`PORT` env, default 3000)
Test: `bun test` | Single file: `bun test tests/data.test.ts` | Single case: `bun test -t "registry has 7"`
Typecheck: `bun run typecheck`
Build: `bun run build` | Run the build: `bun run start`
Docker: `docker build -t epic-timeline .` then `docker run --rm -p 3000:3000 epic-timeline`
Run the production bundle **from inside `dist/`**. Bun resolves bundled assets relative to the cwd:
```sh
bun run start # right: cd dist && bun server.js
bun dist/server.js # wrong: crashes with 'Bundled file "./index-….js" not found'
```
## Rules
- Every page shows the fan notice and the Jorge Rivera-Herrans credit: keep `<footer data-fan-credits>`
and the `../../shared/credits.css` link in the page's HTML, and call `mountCredits()` from
`src/shared/credits.ts`. Never write your own notice text.
- Mockup agents edit only their own `src/mockups/NN-slug/` folder. `src/data/`, `src/shared/`,
`src/server.ts`, `src/index.html` and `src/mockups/registry.ts` are the shared contract; change them
only when the plan says so.
- No two mockups may have byte-identical `style.css` — the build fails with
"Multiple files share the same output path". Give each one its own styles from the start.
- Sample data (`src/data/sample.ts`): lyrics are placeholders, never real lyrics. Any fact, story time
or Odyssey reference you can't confirm gets `// TODO(verify)`.
- Assets are bundled, never loaded from a CDN: fonts from `@fontsource/*`, three.js from npm.
YouTube videos are link cards (`createVideoCards`), not embeds.
- Adding a mockup route means editing `src/server.ts`, `src/mockups/registry.ts` and the cards in
`src/index.html` together; `tests/server.test.ts` checks they agree.
## 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.)
Anything beyond a trivial fix: brainstorm → spec (sign-off) → plan (sign-off) → TDD → review.
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`. No visual direction is chosen yet (see `DESIGN.md`).
Mockup rounds — any UI change beyond a trivial tweak, while brainstorming and before the spec:
- Round 1: about three genuinely different alternatives, each a clickable mockup with realistic
content, plus an index page giving each a one-line pitch and what to try.
- Then diverge (about three options on one open question) or converge (one combined mockup); small
choices can be toggles inside one mockup. Repeat until the user approves one mockup.
- Once `DESIGN.md` has a chosen direction, options vary structure, flow and behaviour within it —
never a new palette or font family.
- **This repo deviates from the usual throwaway setup:** mockups live in `src/mockups/NN-slug/`, are
committed, served by the app and smoke-tested.
- On approval, write `docs/design/NNN-slug.md` (the spec's number): each chosen idea, buildable without
the mockup, and what was deselected and why. The spec links it.
## Architecture
`src/server.ts` -> `Bun.serve` with HTML-import routes: `/` and `/mockups/NN-slug/`
`src/data/` -> `types.ts` (shapes), `sample.ts` (shared sample data), `query.ts` (helpers)
`src/shared/` -> credits, audio slot (direct MP3, empty state), YouTube link cards
`src/mockups/NN-slug/` -> one design direction each: `index.html`, `main.ts`, `style.css`
`tests/` -> data invariants, shared DOM modules (happy-dom), route smoke tests
Full picture: `docs/ARCHITECTURE.md` — read it before your first change in a session.
## Boundaries
Never touch: `dist/`, `node_modules/`.
Never commit `.env`, secrets, or credentials.
Never force-push or rewrite pushed history.
Ask first: new runtime dependencies · changes to `src/data/types.ts` or the shared modules ·
the Dockerfile · 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. No `TODO` comments in source
(`// TODO(verify)` in sample data is the exception).
- `docs/PERMISSIONS.md` — together with every tool permission file (`.claude/settings.json`,
`.codex/`, `.gemini/settings.json`), in one commit.
## Git
Remote `origin` is Gitea, not GitHub: `gh` does not apply. Commits: Conventional Commits (`feat:`,
`docs:`, `chore:`, `feat(mockups):`).
Branches: TODO(maintainer): confirm whether work goes on branches (suggest `feat|fix|chore/<slug>`) or straight to `main` as so far.
## Done means verified
Do not report success on work you have not run. Before saying done:
`bun test` and `bun run typecheck` run *in this session* and seen passing · `bun run build` succeeds ·
matches the approved spec · docs duties complete · `git status` clean and the diff is only what you intended.
UI work, in the app or a mockup: with `bun run start` running, take headless screenshots at 1440×900
(`google-chrome --headless --window-size=1440,900 --screenshot=screenshots/x.png <url>`, or Playwright)
of each screen you changed into `screenshots/` (gitignored) and look at them before saying done.
"It should work" is not verification.
+9
View File
@@ -0,0 +1,9 @@
# CLAUDE.md
**`AGENTS.md` is the single source of truth for this repository.** Read it now and follow it exactly.
@AGENTS.md
This file exists only so Claude Code finds the rules, and for Claude-specific configuration that
AGENTS.md cannot express. It contains no project rules of its own — add those to `AGENTS.md`.
If anything here ever conflicts with `AGENTS.md`, `AGENTS.md` wins.
+29
View File
@@ -0,0 +1,29 @@
# Design
## Status: visual direction open
No design system, palette or type scale is chosen yet. Seven mockups under `src/mockups/` each commit fully
to a different direction; the maintainer will pick one, or combine several, and this file then records the
chosen direction as tokens and rules. Until then, do not treat any single mockup's palette or fonts as the
house style.
## The candidate directions
| # | Folder | Direction |
|---|---|---|
| 1 | `01-editorial` | Editorial scroll: horizontal line, parchment and serif, motifs as thin threads |
| 2 | `02-vertical-story` | Vertical story: downward scroll with a sticky detail panel |
| 3 | `03-transit-map` | Transit map: motifs as metro lines, songs as stations |
| 4 | `04-greek-frieze` | Greek frieze: an unrolled black-figure vase frieze |
| 5 | `05-score` | Score / waveform: staff or waveform, parts as segments |
| 6 | `06-constellation` | Constellation (3D, three.js): songs as stars |
| 7 | `07-sea-voyage` | Sea voyage (3D, three.js): ship, sea, saga islands |
Details per direction: `docs/specs/001-timeline-mockups.md`.
## Constraints that hold for every direction
- Fonts are self-hosted from `@fontsource/*` (installed: Cormorant Garamond, Inter); nothing from a CDN.
- three.js only where a direction is 3D.
- Motif colours come from the data (`Motif.color` in `src/data/sample.ts`), so threads match across views.
- The fan-credits footer (`src/shared/credits.css`) is present on every page; a direction may restyle it
but not remove or hide it.
- Avoid generic, templated "AI-looking" UI; a direction that is just another layout in different colours
has failed.
+7
View File
@@ -0,0 +1,7 @@
# GEMINI.md
**`AGENTS.md` is the single source of truth for this repository.** Read `./AGENTS.md` before doing
anything else and follow it exactly.
This file exists only so Gemini CLI finds the rules. It contains no project rules of its own — add
those to `AGENTS.md`. If anything here ever conflicts with `AGENTS.md`, `AGENTS.md` wins.
+34
View File
@@ -0,0 +1,34 @@
# Product
## What it is
An interactive, **unofficial fan website** for *EPIC: The Musical* by **Jorge Rivera-Herrans**. Its core is
one long timeline of the songs. Recurring musical motifs and character themes are drawn as threads between
the songs where they appear, and the gaps between songs show how much in-story time passes.
Opening a song shows: its place in Homer's *Odyssey* (with a link to a public-domain translation), its
in-story time, its lyrics split into the song's parts with the singer of each part, the motifs in each part,
fun facts, related YouTube videos, and an audio player for a direct MP3 when one exists.
## Who it is for
TODO(maintainer): confirm. Working assumption from the spec: fans of EPIC who want to explore how motifs
recur across the songs and how the story maps onto the *Odyssey*. Desktop first (about 1440×900).
## Non-negotiables
- **Fan notice and credit on every page:** the site is unofficial, not affiliated with or endorsed by the
creator, and thanks Jorge Rivera-Herrans as the creator of EPIC. One shared snippet
(`src/shared/credits.ts`), never retyped.
- **Accuracy is visible:** content that isn't verified is marked as such (`// TODO(verify)` in data) and never
presented as fact-checked.
- **Lyrics:** mockups use placeholder lyric lines only. Whether and how real lyrics are shown is an open
copyright question, deferred by the maintainer.
- **Media:** videos are YouTube link cards (no embedded player); audio supports direct MP3 URLs. The app
works offline apart from following those links.
## Tone
Polished and smooth; may use 3D (three.js). It should feel like a crafted piece made by a fan, not a generic
template — see `DESIGN.md`.
## Current stage
Spec `docs/specs/001-timeline-mockups.md`: 7 design-direction mockups over shared sample data (Troy and
Cyclops sagas plus later callback songs). The maintainer picks or combines a direction; the real app gets
its own spec after that.
+6
View File
@@ -0,0 +1,6 @@
# EPIC: The Musical Timeline
An unofficial fan project: an interactive timeline of *EPIC: The Musical*. Not affiliated with or endorsed by
its creator, Jorge Rivera-Herrans. See `PRODUCT.md` for what it is.
Working on this repo (human or agent): start with [`AGENTS.md`](AGENTS.md) — commands, rules and workflow.
+48
View File
@@ -0,0 +1,48 @@
# Architecture
How `epic-the-musical-timeline` is built today. Update in the same commit as any structural change.
## Overview
A Bun server (`Bun.serve`) with HTML-import routes serves an index page and seven mockup pages. Bun's bundler
turns each HTML page and its imported TS/CSS into hashed assets. `bun run build` bundles the server and every
page into `dist/`; the Docker image runs that bundle. All pages read one typed sample dataset and mount one
shared credits snippet.
## Map
| Path | Responsibility |
|---|---|
| `src/server.ts` | `startServer(port)`: routes `/`, `/mockups/NN-slug/` (+ 302 from the no-slash form), 404 otherwise. Runs when `import.meta.main`; `PORT` env, default 3000. |
| `src/index.html`, `src/index.ts`, `src/index.css` | Index page: static cards linking each mockup, then `mountCredits()`. |
| `src/mockups/registry.ts` | `mockups: MockupInfo[]` — slug, number, name, pitch, what to try. |
| `src/mockups/NN-slug/` | One design direction: `index.html` (links `../../shared/credits.css`, has `<footer data-fan-credits>`), `main.ts`, `style.css`. |
| `src/data/types.ts` | `Saga`, `Song`, `Part`, `Motif`, `StoryTime`, `OdysseyRef`, `Video`. |
| `src/data/sample.ts` | `sagas`, `motifs`, `songs`. Placeholder lyrics; unverified fields `// TODO(verify)`. |
| `src/data/query.ts` | `songsWithMotif`, `motifsInSong`, `gapYears`, `sagaOf`. |
| `src/shared/credits.ts` | `FAN_NOTICE`, `mountCredits(target?)`. |
| `src/shared/audio-slot.ts` | `createAudioSlot(song)`: `<audio>` for `song.audio`, else an empty-state slot. |
| `src/shared/video-cards.ts` | `createVideoCards(song)`: YouTube link cards, no embeds. |
| `src/shared/credits.css` | Styles for credits, audio slot, video cards. |
| `tests/` | `data.test.ts` (dataset invariants), `shared.test.ts` (DOM modules under happy-dom), `server.test.ts` (every route 200s, index links every registry slug, each mockup has the credits hook). |
| `Dockerfile` | Two-stage `oven/bun:1.3`: install + build, then copy `dist/` and run from `WORKDIR /app/dist`. |
## Data flow
1. Request hits `Bun.serve`; an HTML-import route returns the bundled page (dev: bundled on demand with HMR
via `bun --hot`; production: prebuilt assets from `dist/`).
2. The page's `main.ts` imports `src/data/*` and `src/shared/*` (bundled in; no runtime API).
3. `main.ts` renders the direction and calls `mountCredits()` into `<footer data-fan-credits>`.
## External dependencies
None at runtime. Fonts (`@fontsource/*`) and three.js are bundled from npm. Outbound links only: YouTube
(video cards) and Project Gutenberg (Odyssey references) — the site works without them.
## Invariants
- Every page renders `FAN_NOTICE` via `mountCredits()`.
- `src/server.ts` routes, `src/mockups/registry.ts` and the cards in `src/index.html` list the same slugs
(`tests/server.test.ts`).
- Every motif id used by a part exists; every motif appears in at least one song (`tests/data.test.ts`).
- No two mockups ship byte-identical `style.css` (the build fails otherwise).
## Known rough edges
- The production bundle only runs with `dist/` as the cwd (`bun run start`, Docker `WORKDIR /app/dist`);
`bun dist/server.js` from the repo root crashes. See `docs/TODO.md`.
- Routes, registry and index cards are three hand-maintained lists. See `docs/TODO.md`.
+50
View File
@@ -0,0 +1,50 @@
# Decisions
Append-only. Never edit or delete an entry — supersede it with a new one that links back.
One entry per decision a future maintainer would otherwise have to reverse-engineer.
No index: `grep -n '^## ' docs/DECISIONS.md` lists every entry (date, title, line number).
## 2026-10-23 — Mockups are committed and served, not throwaway
**Status:** accepted
**Context:** The agent-repo playbook keeps mockups gitignored in `design/mockups/`, never wired into the app.
Spec 001 asks for 7 design-direction mockups that run in the Docker image for the maintainer to compare.
**Decision:** (maintainer decision) Mockups live in `src/mockups/NN-slug/`, are committed, served by the
Bun server and covered by route smoke tests. Mockup agents edit only their own folder.
**Alternatives:** Gitignored `design/mockups/` (can't ship in the image or be reviewed from the remote).
**Consequences:** Mockup code is in the bundle and the history; when a direction is chosen, the unused
mockups need an explicit removal decision (`docs/TODO.md`).
## 2026-10-23 — Production bundle runs from `dist/`
**Status:** accepted
**Context:** With Bun 1.3.14, a `--production` bundle of HTML imports resolves its assets relative to the
current working directory; `bun dist/server.js` from the repo root throws "Bundled file … not found".
**Decision:** `bun run start` is `cd dist && bun server.js`; the Docker runtime stage uses `WORKDIR /app/dist`.
**Alternatives:** Not recorded (e.g. `--asset-naming`/`naming` options were not explored).
**Consequences:** Any new run script or deploy target must set the cwd to `dist/`.
## 2026-10-23 — Bun only, self-contained assets
**Status:** accepted
**Context:** Spec 001 constraints.
**Decision:** Bun's own server, bundler and test runner with HTML imports; no Vite/Next. Fonts and three.js
bundled from npm, no CDN. YouTube as link cards, not embeds. three.js only in the 3D mockups (06, 07).
**Alternatives:** Not recorded.
**Consequences:** The image works offline apart from outbound links.
## 2026-10-23 — Placeholder lyrics, unverified facts marked
**Status:** accepted
**Context:** Lyrics recalled from memory risk inaccuracy, and their copyright question is open.
**Decision:** Sample data uses clearly marked placeholder lyric lines in the real data shape; any fact,
story time or Odyssey reference not confirmed carries `// TODO(verify)`.
**Alternatives:** Real lyrics (deferred by the maintainer).
**Consequences:** Nothing in `src/data/sample.ts` may be presented as fact-checked.
## 2026-10-23 — Adopt AGENTS.md as single source of truth
**Status:** accepted
**Context:** Agents had no durable project context and no consistent workflow between sessions.
**Decision:** `AGENTS.md` is canonical; `CLAUDE.md`/`GEMINI.md` are pointers. Specs are permanent in
`docs/specs/`. Plans live in `docs/plans/` only while work is in progress and are removed before the
merge request. Both override the superpowers default paths.
**Alternatives:** Per-tool rule files (drift, synchronisation debt); no rules file (agents re-explore
the repo every session — measurably slower and more tokens).
**Consequences:** Rule changes happen in one file; pointer files must stay thin. Plans are working
artefacts: kept after merge they go stale and contradict the code, so the spec is the record.
+58
View File
@@ -0,0 +1,58 @@
# Agent permissions
Source of truth for what AI coding agents may run here. Tool files are renderings of this page:
`.claude/settings.json`, `.codex/config.toml`, `.codex/rules/project.rules`, `.gemini/settings.json`.
Change this page and every tool file in the same commit.
## Verified commands
Run 2026-10-23 with Bun 1.3.14, each in a fresh shell. Only `worked` rows may appear in a tier, a tool file,
or `AGENTS.md` Commands.
| Command | Result |
|---|---|
| `bun install --frozen-lockfile` | worked (no changes) |
| `bun test` | worked — 32 pass, 0 fail, 3 files |
| `bun test tests/data.test.ts` | worked — 10 pass |
| `bun test -t "registry has 7"` | worked — 1 pass |
| `bun run typecheck` | worked (`tsc --noEmit`, exit 0) |
| `bun run build` | worked — `dist/server.js` + hashed page assets |
| `bun run dev` (`PORT=3113`) | worked — `/` and all mockup routes 200, no-slash form 302 |
| `bun run start` (`PORT=3112`) | worked — same route results as dev |
| `bun dist/server.js` (from repo root) | failed — `TypeError: Bundled file "./index-….js" not found`; assets resolve from cwd |
| `docker build -t epic-timeline .` | worked |
| `docker run --rm -p 3000:3000 epic-timeline` | worked — `/mockups/06-constellation/` 200 |
| `docker run -e PORT=8080 -p 8099:8080 epic-timeline` | worked — `/` and all 7 mockups 200, credits hook present (host port 8080 was taken on this machine) |
| `google-chrome --headless --window-size=1440,900 --screenshot=… <url>` | worked (no `chromium` binary on this machine) |
| `codex execpolicy check --rules .codex/rules/project.rules -- <cmd>` | worked — deny/ask rules match as intended |
| Lint / format | not run — no linter or formatter configured |
TODO(maintainer): confirm whether a linter/formatter should be added.
## Tiers
| Tier | Commands | Why |
|---|---|---|
| Allow | `git status`, `git diff`, `git log`, `git show`, `git branch --list`, `git rev-parse`, `git add`, `git commit`; `bun install --frozen-lockfile`, `bun test`, `bun run typecheck`, `bun run build`, `bun run dev`, `bun run start` | Inspection and the verified dev loop; no prompt needed. |
| Ask | `git push`, `git merge`, `git rebase`, `git switch`, `git restore`, `git commit --amend`, `rm` (any); `bun add`, `bun remove`, `bun update`, `bun upgrade`, `bunx`, `npx`; `docker` (any) | Leaves the machine, rewrites work, changes dependencies, downloads and runs code, or touches containers — a human decides. |
| Deny | `git push --force` / `-f` / `--force-with-lease`, `git reset --hard`, `git clean`, `git add -f` / `--force`, `git commit --no-verify` / `-n`, `rm -r` / `-R` / `-rf` / `-fr` / `-Rf` / `-fR` / `--recursive`, `sudo`; reading `.env`, `.env.*`, `secrets/**` | Destroys work, skips checks, or exposes secrets. |
`docker build`/`docker run` are verified but stay in Ask because they touch containers.
## Known gaps
- Allowlists save prompts; they are not a security boundary. Use the tool's sandbox for isolation.
- Prefix rules are bypassable: `git -C dir push`, flags placed later (`git push origin main --force`),
`+refspec` pushes. Claude Code's `:*` matches at a word boundary, so each flag cluster must be
listed — which is why `git clean` is denied whole and `rm` always asks.
- Allowed test, typecheck, and build commands run code from the repo; an agent that can edit
`package.json` or the tests can run anything through them (`bun run dev`/`start` run the server).
- Denying reads of `.env` does not stop a script that opens it itself, and
git can print staged or committed secrets (`git show HEAD:.env`). `git add -f` is denied so an agent
cannot stage an ignored secret; keep real secrets out of the working tree.
- Codex: loaded only once the user trusts the project; `approval_policy` here can loosen a stricter user
setting. No `allow` rules (allow = run outside the sandbox). In `workspace-write`, `.git/` is read-only, so
`git add`/`git commit` ask for escalation, and `bun install` needs network and asks too. Codex rules cannot
deny file reads. `--force-with-lease=<ref>`, `+refspec` and later-placed flags only hit the Ask rule.
- Gemini CLI: ignored until the folder is trusted. `tools.exclude` is deprecated but is the only
project-level deny. No Ask tier (anything not allowed asks). Cannot deny file reads.
- Cursor and Copilot: not configured — no sign the team uses them.
+43
View File
@@ -0,0 +1,43 @@
# TODO
Durable backlog. Agents append here instead of leaving `TODO` comments in source.
Small, specific, independently actionable. Remove only when actually done.
## Now
- [ ] Verify every `// TODO(verify)` field in `src/data/sample.ts` (48 at bootstrap: saga names, singers,
story times, Odyssey book/lines, motif descriptions, facts) before any content leaves mockup stage.
## Next
- [ ] After a direction is chosen: decide whether the unused mockups are deleted or kept, and record the
chosen direction in `DESIGN.md`.
- [ ] Add CI under `.gitea/workflows/` (none exists): `bun install --frozen-lockfile`, `bun test`,
`bun run typecheck`, `bun run build`; plus a check that `AGENTS.md`, `CLAUDE.md`, `GEMINI.md` and the
`docs/` paths exist and `AGENTS.md` is under 150 lines. Proposed, not added.
## Someday
- [ ] Make `bun dist/server.js` work from any cwd (Bun 1.3.14 resolves bundled assets relative to cwd;
try `--asset-naming` / `naming`, or re-check on a newer Bun). Today `bun run start` and Docker
work around it.
- [ ] Derive `src/server.ts` routes and `src/index.html` cards from `src/mockups/registry.ts` instead of
three hand-kept lists (tests catch drift today).
- [ ] Decide whether real lyrics may be shown (copyright question, deferred).
## 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. This is how the published numbers were produced, and it is
the only way to know your file is helping rather than costing 20% for nothing.
## Known issues
- [ ] `bun dist/server.js` from the repo root crashes: `TypeError: Bundled file "./index-….js" not found`.
Repro: `bun run build && bun dist/server.js`. Workaround: `bun run start`.
- [ ] Two mockups with byte-identical `style.css` fail `bun run build` with "Multiple files share the same
output path" (reproduced by copying `01-editorial/style.css` over `02-vertical-story/style.css`).
Identical `main.ts` files do not trigger it.
- [ ] `docs/plans/001-timeline-mockups.md` tells mockup agents to start `bun dist/server.js`, which hits
the issue above; use `bun run start` instead.
+11
View File
@@ -0,0 +1,11 @@
# Plans
Implementation plans from the superpowers writing-plans workflow. **Transient:** a plan lives only on
its feature branch and is removed with `git rm` before the merge request is opened. This README stays.
- Naming: `NNN-short-slug.md`, matching its spec number.
- Links back to its spec. Bite-sized checkbox tasks with exact file paths, test commands, and
commit checkpoints.
- Written for an engineer with zero context for this codebase — spell out the obvious.
- Tick checkboxes as you go so another agent can resume an interrupted session.
- Front matter: `status: draft | approved | in-progress`. There is no `done` — a finished plan is deleted.
+10
View File
@@ -0,0 +1,10 @@
# Specs
Approved specifications from the superpowers brainstorming workflow. Nothing non-trivial gets
implemented without one.
- Naming: `NNN-short-slug.md`, zero-padded, sequential.
- A spec states **what** and **why**, plus acceptance criteria. Not **how**.
- Front matter: `status: draft | approved | implemented | superseded`. No link to a plan — plans are
removed before merge.
- Never delete an implemented spec; it is the record of intent.