| new file mode 100644 |
| index 0000000..3636d66 |
| --- /dev/null |
| +++ b/AGENTS.md |
| @@ -0,0 +1,126 @@ |
| +# ๐ค Sheets |
| + |
| +A CARLOS spreadsheet. One instance per team. |
| + |
| +This file is binding. Where it disagrees with a general skill, this file |
| +wins. Where it disagrees with the design spec, the spec wins and this file |
| +is wrong โ fix it in the same commit. |
| + |
| +**The design:** `docs/superpowers/specs/2026-08-30-sheets-design.md`. Its |
| +ยง1 is the settled-decisions table: dated, attributed, with the rejected |
| +alternative beside each. Don't relitigate one in a drive-by. |
| + |
| +--- |
| + |
| +## The one rule |
| + |
| +> **A sheet's contents are sealed at rest. The bucket never holds a |
| +> plaintext cell.** |
| + |
| +The instance holds the key while it runs, so the server *can* read a sheet |
| +it has open. That is a deliberate softening, not an accident, and the spec's |
| +ยง2 names every leak it produces. What the rule forbids absolutely is a |
| +plaintext value reaching the replicated SQLite file or object storage. |
| + |
| +A test greps raw SQLite bytes and bucket objects for plaintext from the |
| +first commit. `plaintextExempt` starts empty; any entry is dated, justified, |
| +and listed in the README. |
| + |
| +**We never claim "we can't read your data."** A compromised live host can |
| +read any open sheet. The README says so and the UI never implies otherwise. |
| + |
| +--- |
| + |
| +## Review โ RULED 2026-08-30 by Paul |
| + |
| +**Every spec and every branch is reviewed by the highest-capability model |
| +available before it is approved or merged.** |
| + |
| +Not the model that happened to write it, and not whichever model the |
| +session started on. Cheaper models draft; the most capable one available |
| +reviews. A review by a weaker model than the author is not a review. |
| + |
| +Mechanically: |
| + |
| +- **Specs** โ a review pass before the spec is presented for human approval, |
| + and again after any material change to a settled decision. |
| +- **Branches** โ a review pass before the PR is opened, and again after any |
| + force-push that changes more than a rebase. |
| +- **Record which model reviewed**, in the PR body or the spec's status line. |
| + A review nobody can attribute is a review nobody can check. |
| + |
| +**The highest-capability model is discovered, not hardcoded.** At the time |
| +of writing that is Opus; naming a model here without saying how to re-derive |
| +it is how this rule goes stale silently. The check is: what is the most |
| +capable model this session can actually reach right now. If a session cannot |
| +reach one at least as capable as the author's, it says so and waits rather |
| +than passing a weaker review off as done. |
| + |
| +This is a floor, not a ceiling โ an extra pair of eyes, human or model, is |
| +always welcome on top. |
| + |
| +--- |
| + |
| +## Intent before code |
| + |
| +CARLOS's first principle: the design doc precedes the build, and its |
| +decisions are taken. If one proves wrong, **stop and flag it** rather than |
| +quietly redesigning around it. |
| + |
| +Keep the spec's "Deliberately not built yet" table honest. Every deferred |
| +thing has a written trigger. A thing deferred without a trigger is a thing |
| +forgotten. |
| + |
| +--- |
| + |
| +## Mechanical rules |
| + |
| +Rules here are enforced by tests, not by memory. If you find yourself |
| +relying on remembering one, make it a test instead. |
| + |
| +**Data** |
| +- Every query touching user-owned rows goes through `scope.Owned(g, uid)` โ |
| + reads *and* writes, inside transactions too. Scope `tx`, never the outer |
| + handle, or the one-connection writer pool deadlocks. |
| +- A row that isn't yours **404s, never 403s**. |
| +- Never bind a request body onto a GORM model. Explicit `map[string]any` |
| + plus a `.Select` allowlist. |
| +- Read the viewer with `auth.From(r)` or `sessions.Current(r)`. **Never |
| + `sessions.UserID`** โ it returns `(0, false)` for an email Subject, and |
| + dropping that `ok` scopes every query to uid 0. |
| +- Migrations are additive-only. New code over an old DB is always safe. |
| + Never delete data to update. |
| +- A float never touches an amount. The formula evaluator's IEEE arithmetic |
| + is a bounded exception that stops at the storage boundary. |
| + |
| +**Frontend** |
| +- No build step. Jspreadsheet CE is vendored as a static asset, not fetched |
| + from a CDN and not bundled. |
| +- Write the rastrillo **attribute** grammar (`rst-btn="primary compact"`), |
| + never the `class="rst-*"` vocabulary โ it is mid-migration and the classes |
| + are the losing spelling. |
| +- **Re-copy `static/tokens.css` on every rastrillo upgrade.** It is frozen |
| + at scaffold time while partials upgrade with the module. Adopt |
| + `rastrillo doctor` the day it lands and delete this step. |
| +- Don't build on `.rst-page-header::after` โ the rake line is retired in |
| + favour of `--rst-header-rule`. |
| +- 300-line JS module cap, test-enforced, ratchet-down only. The vendored |
| + Jspreadsheet blob is exempt and named as such; nothing we write is. |
| +- Every drag-only affordance has a menu equivalent. |
| + |
| +**Deploys** |
| +- One binary, `CGO_ENABLED=0`, cgo-free SQLite. A cgo driver sneaking in is |
| + a bug. |
| +- Review on a deployed canary, never localhost. |
| +- A 200 is not a deploy verification. Only `X-Carlos-Version` on the |
| + canonical host, after the watch, is proof. |
| +- A promote is not a deploy. Until the process cycles, the old binary |
| + serves. |
| + |
| +--- |
| + |
| +## Authorship |
| + |
| +๐ค / ๐จ markers, `Co-Authored-By: Claude โฆ` trailers, published prompt and |
| +carbon ledgers. AI is a guest with a name tag: opt-in, disclosed, and every |
| +path works with it switched off. |