| 1 | # π€ CARLOS working conventions |
| 2 | |
| 3 | How the family builds. These conventions exist because each was learned the |
| 4 | hard way at least once; the incident is noted where it names the rule. |
| 5 | |
| 6 | ## Repo documents |
| 7 | |
| 8 | - **AGENTS.md** β "how we build and the decisions we've committed to." |
| 9 | Written before the code (factor V: the intent is the system β the code |
| 10 | should have to argue with something). Opens with the one rule. Contains: |
| 11 | settled decisions (dated, attributed, with rejected alternatives), |
| 12 | named laws, the workflow, and a "Deliberately not built yet" list with the |
| 13 | trigger for each deferral. One file, for every agent that reads the repo β |
| 14 | not one per vendor. Older repos still carry it as CLAUDE.md; rename it to |
| 15 | AGENTS.md the next time you touch one, rather than keeping two. |
| 16 | - **README.md** β user-facing: what it is, run it, honest trade-offs. |
| 17 | - **DESIGN.md** β the spec, what gets built. **PLAN.md** β why it is shaped |
| 18 | this way, the roadmap. |
| 19 | - **docs/** β design records and briefs, written *before* the build |
| 20 | ("Brainstormed with Paul; decisions recorded here"), each with a Status |
| 21 | line (`built` / `approved design β not yet built`), annotated in place |
| 22 | when built. The doc's decisions are binding: if one proves wrong in |
| 23 | practice, stop and flag it in the PR rather than redesigning. |
| 24 | |
| 25 | ## Git and PR playbook |
| 26 | |
| 27 | - **Worktree per session, always.** Multiple sessions (agents and humans) |
| 28 | are active at once; the main checkout's uncommitted state belongs to |
| 29 | another session. Never build in the main checkout β reading it is fine, |
| 30 | writing is not. |
| 31 | - **The cadence is: worktree β branch β regular pushed commits β |
| 32 | squash-merge.** Commit as you go and push every commit. The branch is |
| 33 | where work in progress lives; you do not wait to be asked to put it |
| 34 | there, and a session that ends with unpushed work has lost it as far as |
| 35 | everyone else is concerned. Keep the PR small and focused. |
| 36 | - **Squash-merge to main when all three hold: the work is done, a human has |
| 37 | approved it, and CI is green** (`gh pr merge --squash --delete-branch` |
| 38 | on GitHub; on amadan, where the branch is the pull request, |
| 39 | `amadan branch merge` makes a merge commit and `-squash` lands one |
| 40 | commit; never squash by hand there, or the branch shows as deleted). |
| 41 | Two of the three is not a merge β a green run on unapproved work waits, |
| 42 | and an approval over a red run waits. Never commit to main. Deploy only |
| 43 | merged main, and only when asked. |
| 44 | - **"CI green" binds once CI exists, and a new repo's early work is to |
| 45 | make it exist.** A repo with no CI has no green to wait for: the |
| 46 | condition is not waived, it simply has nothing to read yet, so the first |
| 47 | merges of a new project land on done + approved. Standing up CI is one |
| 48 | of the first work items, not a prerequisite nobody can satisfy β and it |
| 49 | is the gate you already run locally, kept as one definition rather than |
| 50 | a second list that drifts, so debugging CI is running it. Once the repo |
| 51 | has CI all three bind absolutely and the carve-out is spent. |
| 52 | - Commit subjects are area-prefixed prose that says why (`web: the front |
| 53 | door is a homepage β pitch left, sign-in right`; `auth: delete the |
| 54 | recovery passphrase, root and branch`). Comment the why, not the what. |
| 55 | - Every commit carries `Co-Authored-By: Claude <model> |
| 56 | <noreply@anthropic.com>`; the human is the author of record. |
| 57 | |
| 58 | ## Canaries |
| 59 | |
| 60 | - **Every session gets its own canary; review happens on a deployed canary, |
| 61 | never on localhost.** (Adopted after two sessions sharing a dev host |
| 62 | produced an evening outage.) Local stacks are for automated drives only. |
| 63 | - On the platform, a canary is a channel: `carlos canary` deploys to |
| 64 | `canary/<slug>` (a per-session dead-end rung, zero bake) and the canary |
| 65 | host serves it at |
| 66 | `<slug>-<app>.<sqid>.<domain>` β same rule, no box. Off-platform, |
| 67 | canaries multiplex onto the devbox: own port/state/unit/TLS at |
| 68 | `<branch>.<dev-host>`. Shared hosts (`prod`, the plain dev host) only ever |
| 69 | carry main builds β enforced by the deploy path, not memory. |
| 70 | - Report the complete clickable canary URL in **every** reply while |
| 71 | iterating, with a short list of what to test. The human clicks through |
| 72 | mid-flight; never make them scroll back for the URL. |
| 73 | - Tear your canary down when the branch merges. Never echo claim links or |
| 74 | secrets into the session β the prompt ledger publishes what gets typed. |
| 75 | |
| 76 | ## Testing |
| 77 | |
| 78 | The layers, in order: |
| 79 | |
| 80 | 1. **`go test ./...`, `go vet ./...`, `gofmt -l .` β all clean, always.** |
| 81 | Nothing lands untested; a bug fix carries its regression test. When |
| 82 | gating on a test run, never pipe it β a pipe eats the exit code (a red |
| 83 | suite once merged and deployed exactly this way). A guard belongs |
| 84 | *inside* the test binary, never in a shell step around it β a guard a |
| 85 | shell step can satisfy is a guard a shell step can be wrong about |
| 86 | (one browser-required check went silently unrun for weeks). |
| 87 | 2. **Prefer a parsing test to a browser for anything computable from |
| 88 | source.** Contrast ratios computed from the token file's declarations |
| 89 | (WCAG AA, both themes, no browser); network reachability rules parsed |
| 90 | from the served JS; performance fixes shipped with an `EXPLAIN QUERY |
| 91 | PLAN` assertion over the *exported production query string* so the |
| 92 | test cannot drift from the real SQL. When a rule needs an exception, |
| 93 | the allowlist entry pins the exact occurrence count, not just the |
| 94 | file β a new call site inside an exempted file must still fail. |
| 95 | 3. **JS logic DOM-free under `node --test`**, wired into `go test` via a |
| 96 | small `jstest_test.go`, so one command runs everything. `node --check` |
| 97 | alone is not enough β it parses as CommonJS and swallows ES-module errors |
| 98 | that crash Safari; check a `.mjs` copy and load it in a real engine. |
| 99 | 4. **uidump β dump, don't drive** (adopted by the platform console). An env-gated test (`UIDUMP_DIR` set, else |
| 100 | skipped) boots the real handler in-process over `httptest`, seeds a |
| 101 | fixture world rich enough that every state has a subject, signs in, |
| 102 | GETs every named screen, and writes each response body to disk with |
| 103 | asset paths rewritten relative so the files stand alone. An agent |
| 104 | then screenshots the static files directly with headless Chrome |
| 105 | (`--headless --screenshot=β¦`), re-shooting dark mode by stamping the |
| 106 | theme attribute and mobile at a narrow window. Near-zero cost, zero |
| 107 | flake, works on a display-less box; adding a screen is one map entry |
| 108 | β and **"add your screen to the dump" is a checklist line in every |
| 109 | plan**, or coverage rots. It renders HTML+CSS only and asserts |
| 110 | nothing itself: it is the visual-review gate, not the invariant gate. |
| 111 | 5. **Browser drives are for the residue** that needs a JS engine and |
| 112 | real layout geometry β enhancement-script behaviour, popover sizing, |
| 113 | overflow, theme resolution β env-gated, with assertions written as |
| 114 | the named bug class they caught. Playwright, or chromedp for |
| 115 | pure-Go. Passkey flows run in Chromium β the virtual authenticator |
| 116 | is a CDP feature; a separate passkey-free smoke runs in WebKit, |
| 117 | Safari's engine, because it breaks first. Loud on purpose: fail on |
| 118 | any console error, page error, or failed request, and junk-scan |
| 119 | every view for `undefined`, `null`, `[object Object]`, `NaN` β the |
| 120 | bug class that renders perfectly and says nothing (a stray `null` |
| 121 | *text node* included: native `append()` stringifies one). A skip is |
| 122 | not a pass: report executed-vs-skipped. |
| 123 | 6. **The merciless greps** (server-blind apps): end-to-end tests that grep |
| 124 | raw SQLite bytes *and* S3 objects for plaintext content and names. |
| 125 | 7. **After every deploy: the console check in WebKit** against the live |
| 126 | host (the keymail rule) β a JS syntax error takes the whole client down |
| 127 | and only a real engine sees it; WebKit specifically, because it is the |
| 128 | strictest engine and Safari users hit it first. |
| 129 | 8. **Security scan** (Aikido) before ending a work cycle; triage every |
| 130 | finding; never publish over an untriaged one. |
| 131 | |
| 132 | Simulation suites are the spec for convergence-critical engines (sync, |
| 133 | progression): any behaviour change changes the suite first. Golden vectors |
| 134 | pin multi-language implementations (see blueprint.md). |
| 135 | |
| 136 | ## Ending a session |
| 137 | |
| 138 | Every session ends through the committed `/end-session` gate. The shape: |
| 139 | |
| 140 | 1. Clean tree β nothing uncommitted, nothing unpushed, no orphan worktrees. |
| 141 | 2. Gates green: build, vet, gofmt, tests (unpiped), browser drives with |
| 142 | executed-vs-skipped counts. |
| 143 | 3. Security scan clean or triaged. |
| 144 | 4. Ledgers accrued (below), reviewed word-by-word for secrets before |
| 145 | publishing β never publish text you haven't read; when unsure, redact |
| 146 | with an annotated `β¦redacted: categoryβ§` and say what and why. |
| 147 | 5. Deploy state reported; canaries torn down or handed over. |
| 148 | 6. A handoff report: PRs, clickable canary URLs with what to verify, open |
| 149 | threads, and what was deliberately not done. |
| 150 | |
| 151 | ## Ledgers and transparency |
| 152 | |
| 153 | The family practices radical operational transparency on a public status |
| 154 | site: **prompt registry** (every human prompt behind the code, whole and in |
| 155 | order, credentials redacted in place), **carbon tally** (token spend β |
| 156 | COβe bands, plus the boxes' share), **contributors** (humans and AI models |
| 157 | on one metric, from git trailers), roadmap and milestones written in member |
| 158 | language β what changed for a member, never how the boxes are run. Ledger |
| 159 | updates ship exactly like code: commit β PR β deploy. No hostnames, no |
| 160 | account IDs, no third-party personal data in anything published. |
| 161 | |
| 162 | ## AI authorship (factor X) |
| 163 | |
| 164 | - Machine-authored text is marked **π€** wherever it surfaces β visible, |
| 165 | immediately before the heading or block it covers; a marker on a heading |
| 166 | cascades. Never hide the π€ in a comment. |
| 167 | - A person marker (π¨/π€/π§) certifies human text: an LLM must not rewrite, |
| 168 | rephrase, condense, or delete it. No marker means ambiguous β never claim |
| 169 | human authorship for unmarked text. |
| 170 | - Disclosure extends to product: AI features are opt-in, never required, |
| 171 | always disclosed, and every path works with the AI switched off β this is |
| 172 | a hard constraint, not an aspiration. Nothing assumes AI stays cheap. |
| 173 | - Uncompiled or unverifiable code is marked (`// UNVERIFIED: written without |
| 174 | an iOS toolchain`) and kept in separate commits. |
| 175 | |
| 176 | ## Working with the human |
| 177 | |
| 178 | Patterns visible across every prompt history, useful calibration for agents: |
| 179 | |
| 180 | - Directives are terse ("go", "merge", "Deploy!", "keep going until it's |
| 181 | done"). Long autonomous runs are normal; write the design doc, then build |
| 182 | until there's something live to click. |
| 183 | - Bug reports arrive as lived user experience from the live product, in |
| 184 | product language. Reproduce on a canary, fix, and ask "is this flow being |
| 185 | tested?" β because that question is coming. |
| 186 | - Product shape is given, parameters are left open ("allows edits for what, |
| 187 | 5 minutes? 2 minutes?") β make the judgement call and flag it. |
| 188 | - Sibling apps are the design vocabulary ("like Eleven's home server", |
| 189 | "Woodstar's shape, copied on purpose"). Check the sibling before |
| 190 | inventing. |
| 191 | - Calm UI: red is for danger only, no toasts, no nerdspeak in the default |
| 192 | flow, buttons stay where the thumb is, feedback is inline and honest. |
| 193 | |