| 1 | # Instructions for AI agents working on paulca.com |
| 2 | |
| 3 | This is Paul Campbell's personal site: a deliberately plain, default-browser-styles |
| 4 | static site served by GitHub Pages. You (the agent) make changes on Paul's behalf. |
| 5 | Follow the recipes below exactly. |
| 6 | |
| 7 | ## How the repo works |
| 8 | |
| 9 | - The code lives on amadan: `origin` → |
| 10 | [amadan.net/paulca/paulca.com](https://amadan.net/paulca/paulca.com). GitHub |
| 11 | is kept as a second remote named `github`, purely as a deploy target for |
| 12 | GitHub Pages. See "Recipe: commit and push" — you push to both. |
| 13 | - `src/` — the source. All edits happen here. |
| 14 | - `docs/` — the built site, generated by Eleventy and **committed to git**. |
| 15 | GitHub Pages serves the `docs/` directory of `main` as-is. There is no build |
| 16 | on push: if you don't build locally and commit `docs/`, nothing changes on |
| 17 | the live site. |
| 18 | - `src/blog/*.md` — blog posts, one Markdown file each. The filename is the |
| 19 | URL slug: `src/blog/my-post.md` → `https://paulca.com/blog/my-post/`. |
| 20 | - `src/ledger/*.md` — the prompt & carbon ledger at `/ai.html`, one small |
| 21 | Markdown file per entry. `src/_data/ledger.json` holds only the prose |
| 22 | equivalent for the running total. |
| 23 | - `src/_includes/base.njk` and `src/_includes/post.njk` — the only two HTML |
| 24 | templates. `post.njk` wraps every blog post. |
| 25 | - `src/index.njk` — homepage. The Writing list fills itself from `src/blog/`; |
| 26 | you never need to edit the homepage when adding a post. |
| 27 | - `src/blog-text.njk` and the RSS plugin (configured in `eleventy.config.js`) |
| 28 | generate a plain-Markdown `/blog/<slug>.text` twin for every post and the |
| 29 | feed at `/feed.xml`. Both are automatic — adding a post needs no extra |
| 30 | work, and you never edit these. |
| 31 | - `src/ai.njk` — the ledger page template. The table and running total are |
| 32 | generated from the files in `src/ledger/`; you never edit this file to add |
| 33 | an entry. |
| 34 | - `src/faq/*.md` — the FAQ at `/faq/`, one Markdown file per question. |
| 35 | `src/faq.njk` is the page template that assembles them in filename order; |
| 36 | you never edit it to add a question. |
| 37 | - `src/posts/`, `src/microblog/`, `src/assets/`, `src/activity_pub/` — frozen |
| 38 | archives of the old site, copied into `docs/` unchanged. Never edit these. |
| 39 | |
| 40 | ## Rules |
| 41 | |
| 42 | 1. **No styling.** No CSS files, no `<style>` blocks, no new inline styles, no |
| 43 | classes, no JavaScript. Semantic HTML and Markdown only. The single |
| 44 | `max-width:80ch` inline style on `<article>` in `post.njk` (a line-length |
| 45 | cap for readability) is the only styling on the site; leave it alone. |
| 46 | 2. **Never edit anything in `docs/` by hand.** Change `src/`, then rebuild. |
| 47 | 3. **Post Paul's words verbatim.** When Paul gives you text for a blog post, |
| 48 | do not rewrite, correct, or embellish it. |
| 49 | 4. **The ledger is append-only.** Add a new file under `src/ledger/`; never |
| 50 | edit or delete the existing entry files. |
| 51 | 5. **Every session that changes the site also adds a ledger entry**, in the |
| 52 | same push as the change. This applies to every change you make, with any |
| 53 | tool: a model editing files with its own edit tool is AI work, full stop. |
| 54 | There is no "hand-edit" exemption for your own actions — the only changes |
| 55 | that skip the ledger are ones Paul makes himself, in his own editor, |
| 56 | without prompting any model. |
| 57 | 6. **Always rebuild before committing**, and commit `src/` and `docs/` |
| 58 | together. |
| 59 | |
| 60 | ## Recipe: add a blog post |
| 61 | |
| 62 | 1. Start up to date, on `main`: |
| 63 | |
| 64 | git checkout main |
| 65 | git pull |
| 66 | |
| 67 | Pick a `<slug>` from the post title: lowercase, words separated by |
| 68 | hyphens, no punctuation (e.g. "My Great Post!" → `my-great-post`). |
| 69 | |
| 70 | 2. Create `src/blog/<slug>.md`: |
| 71 | |
| 72 | --- |
| 73 | title: The post title exactly as Paul gave it |
| 74 | date: 2026-07-25 |
| 75 | --- |
| 76 | The body of the post, in Markdown, verbatim. |
| 77 | |
| 78 | The `date` is today's date in `YYYY-MM-DD` format. Links are written as |
| 79 | `[text](https://example.com)`. |
| 80 | |
| 81 | 3. Build and check (first time on a machine, run `npm install` once): |
| 82 | |
| 83 | npm run build |
| 84 | grep "<h1>" docs/blog/<slug>/index.html |
| 85 | |
| 86 | The grep must print the post title. To preview in a browser: |
| 87 | `npm run serve`, then open http://localhost:8080/. |
| 88 | |
| 89 | 4. Add the ledger entry (see recipe below) and rebuild. |
| 90 | |
| 91 | 5. Commit and push (see recipe below). |
| 92 | |
| 93 | ## Recipe: make a tweak |
| 94 | |
| 95 | Same flow as a post, with an edit instead of a new file: pull `main`, edit |
| 96 | the relevant file under `src/`, run `npm run build`, add a ledger entry, |
| 97 | then commit and push. |
| 98 | |
| 99 | ## Recipe: update the prompt & carbon ledger |
| 100 | |
| 101 | Each entry is one small Markdown file in `src/ledger/`. Create a new file — |
| 102 | never edit an existing one — named `YYYY-MM-DD-NN-slug.md`: today's date, |
| 103 | then `NN`, a two-digit number one higher than the highest already used for |
| 104 | that date (run `ls src/ledger/` to check; use `01` if the date is new), then |
| 105 | two or three words about the change. Example: `2026-07-25-08-model-column.md`. |
| 106 | Get today's date by running `date +%F` — never guess it from memory; models |
| 107 | frequently don't know what day it is. |
| 108 | |
| 109 | --- |
| 110 | model: GPT-OSS (RTX 3090) |
| 111 | wh: 4 |
| 112 | co2_g: 1.6 |
| 113 | comparison: A few minutes of a smartphone charging |
| 114 | prompt: >- |
| 115 | “The instruction Paul gave, quoted verbatim.” |
| 116 | --- |
| 117 | One or two sentences describing what you did, in Markdown. |
| 118 | |
| 119 | - `model`: the model doing the work — that is, you, the model reading this |
| 120 | file right now. If the latest entries name a different model, do **not** |
| 121 | copy them: write your own name and where you run, e.g. |
| 122 | `Qwen3-Coder 30B (Ollama)` or `GPT-OSS (RTX 3090)`. |
| 123 | - The arithmetic, for a local open-source model on the home GPU: |
| 124 | `wh = (number of prompts Paul sent this session) × 2`, and |
| 125 | `co2_g = wh × 0.4`. Round to one decimal at most. |
| 126 | - `comparison` is a fresh everyday-energy equivalent for that amount of |
| 127 | energy. Vary it; don't reuse a comparison from an existing entry. |
| 128 | - The `prompt` text must stay indented under `prompt: >-` exactly as in the |
| 129 | example. Quotes inside it need no escaping. |
| 130 | - The running total on the page computes itself. The `totals_comparison` |
| 131 | string in `src/_data/ledger.json` is the prose equivalent for the *total*; |
| 132 | update it when the total has drifted well past what it describes. |
| 133 | |
| 134 | Then rebuild and confirm your entry made it into the page: |
| 135 | |
| 136 | npm run build |
| 137 | grep "the comparison text from your new entry" docs/ai.html |
| 138 | |
| 139 | ## Recipe: add an FAQ entry |
| 140 | |
| 141 | Each question is one Markdown file in `src/faq/`, and the page at `/faq/` |
| 142 | assembles them in filename order. Create a new file — never renumber or edit |
| 143 | existing ones unless Paul asks — named `NN-slug.md`: `NN` is a two-digit |
| 144 | number one higher than the highest already in `src/faq/` (run `ls src/faq/` |
| 145 | to check), and the slug is two or three words from the question. Example: |
| 146 | `06-what-model-runs-this.md`. |
| 147 | |
| 148 | --- |
| 149 | question: >- |
| 150 | The question, phrased the way a reader would ask it? |
| 151 | --- |
| 152 | The answer, in Markdown. Keep it short and factual, write about Paul in |
| 153 | the third person, and only state things that are true of this site. |
| 154 | |
| 155 | The `question` text must stay indented under `question: >-` exactly as in |
| 156 | the example. Then rebuild and confirm the question shows up: |
| 157 | |
| 158 | npm run build |
| 159 | grep "the question text" docs/faq/index.html |
| 160 | |
| 161 | An FAQ change is a site change like any other: add a ledger entry, then |
| 162 | commit and push as usual. |
| 163 | |
| 164 | ## Recipe: commit and push |
| 165 | |
| 166 | Work directly on `main` — no branches, no pull requests. Paul is the only |
| 167 | human in the repo and reviews the live site. |
| 168 | |
| 169 | git add -A |
| 170 | git commit -m "Add blog post: <title>" # or a one-line description of the tweak |
| 171 | git push origin main |
| 172 | git push github main |
| 173 | |
| 174 | **Push to both remotes, every time.** `origin` is amadan |
| 175 | (`amadan.net/paulca/paulca.com`) — the home of the code. `github` is a mirror |
| 176 | that exists only because GitHub Pages serves the live site from it. If you |
| 177 | push to `origin` alone, your change is safely stored and the live site does |
| 178 | not move. Both pushes, or the deploy didn't happen. |
| 179 | |
| 180 | Use `git add -A` exactly as written — never stage individual files. If your |
| 181 | commit doesn't include the changed files under `docs/`, the live site will |
| 182 | not change, no matter what you changed under `src/`. After committing, run |
| 183 | `git show --stat HEAD` and check that `docs/` files are in it. |
| 184 | |
| 185 | Then verify the live site (GitHub Pages takes a minute or two to deploy): |
| 186 | |
| 187 | curl -sL https://paulca.com/blog/<slug>/ | grep "<h1>" |
| 188 | |
| 189 | If the push is rejected because the remote has newer commits, run |
| 190 | `git pull --rebase`, run `npm run build` again, and push again (to both |
| 191 | remotes). |
| 192 | |
| 193 | ## Checklist before pushing |
| 194 | |
| 195 | - [ ] `npm run build` ran without errors after the last edit to `src/` |
| 196 | - [ ] The change shows up in `docs/` |
| 197 | - [ ] No new CSS, classes, or JavaScript anywhere |
| 198 | - [ ] `src/ledger/` has a new entry file for this session, and its row shows |
| 199 | up in `docs/ai.html` |
| 200 | - [ ] Nothing under `docs/` was edited by hand; nothing under the archive |
| 201 | directories was touched |
| 202 | - [ ] Pushed to **both** `origin` (amadan) and `github` (Pages deploy) |
| 203 | |