# khosra — agent constitution `khosra` is a flat-file personal publishing engine in Go, built solo, one feature at a time. You implement; the human owns scope and taste. This file overrides your defaults. A **personal publishing substrate**: a directory of Markdown becomes an owned, networked home for fiction, webcomics, art, and essays, in English and Bengali. Values: data sovereignty, minimalism as aesthetic, comprehensibility by one person. --- ## 1. Read order (do not skip, do not exceed) 1. This file. 2. `docs/state.md` — what exists **right now**, plus the earn-it counters. 3. `docs/README.md` — always. The map: how you find which doc owns your topic. Not "exceeding". 4. Every doc owning a topic your change asserts a rule about (ownership table in `docs/README.md`). 5. Only the source files you will edit, plus their direct callers. No reading the repo "for context", no speculative greps. Where `docs/state.md` and the code disagree, the code wins — say so, fix the doc in Document. **The ceiling has a floor.** "Read less" governs breadth, never the doc that owns what you are writing. Before stating a rule, contract, threshold, or gate, read its owning doc; if it already says it, amend there instead of restating elsewhere. `ideas/` and `reference/` are out of context by default, indexes included. Open one only when the human names it. Never sweep, never list, never cite unasked. Storage, not background. ## 2. The primitives — everything reduces to one **Bundle · Stage · Query · View · Interaction · Effect** (+ bundle-as-program). Definitions and STATUS: `docs/architecture.md`. Name the primitive before writing code. If it reduces to none it is a **trunk** (wants a permanent service or a core-model change): stop, say so in a paragraph, propose the leaf, wait. ## 3. Hard rules 1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config knob, registry. The counters table in `docs/state.md` holds every threshold and is the only place they are written down: read them, increment them, never anticipate them. 2. **No new dependency** without an ADR and human approval. Allowlist: `scripts/allowed-deps.txt`. Stdlib first, always. 3. **Surgical diffs.** Only the lines the feature needs. No renames, no reformatting beyond `gofmt`, no "while I was in there". Spotted something bad? Latent list in `docs/state.md`. 4. **The untrusted boundary is absolute.** Anything not from the site root (comments, webmentions, form input) never reaches shortcode or template evaluation. Crossing it needs a plan callout. 5. **Permalinks are permanent.** A published URL never changes meaning; renames add aliases and permanent redirects. The path shape is decided (ADR-0008) and written in `docs/content-model.md` — read it before emitting a URL, and never invent a second shape. 6. **No speculative anything**: no unused parameters, no `interface{}` for flexibility, no "we might want to" comments, no one-field options structs, no plugin registry before its counter is due, no cache until requests feel slow, no concurrency until profiled, no `utils`/`helpers`/`common`/`manager`/`base` packages, ever. 7. **Delete before you add.** If removing code buys the feature, do that. 8. **Nothing ships without the doc that describes it, and nothing ships still describing what you replaced.** Code carries its `state.md` update; harness changes (this file, `scripts/`, `.claude/`) carry their `HARNESS.md` update; both are gated. Changed a rule, value, name or path? Grep the repo for the old form and every doc that names the concept, and fix them in this change — `verify.sh` catches dangling paths and ADR numbers, never a superseded sentence. And never assert a mechanism that does not exist yet: if a doc says a gate rejects something, run it and watch it reject, or do not write the sentence. `./scripts/verify.sh --list` names every gate there actually is. The harness evolves: when a rule here proves wrong, fix the rule *and* its description rather than working around it. 9. **Surface conflicts; never resolve them silently.** If a request contradicts a decision already recorded — an ADR, an architecture invariant, the permalink shape, the untrusted boundary, a budget, a frozen contract — say so before writing code: quote the line, name the file, and give the two paths (comply, or change the decision on purpose). A request that merely exceeds a convention is not a conflict; a request that reverses a deliberate choice always is. Kinds and procedure: the Clarify step in `SKILL.md`. Silently doing what was asked is the failure mode this rule exists to prevent — the human cannot audit a conflict they were never shown. ## 4. The loop (every request, no exceptions) `Clarify → Plan → Implement → Verify → Document → Report`. Procedure: `.claude/skills/khosra-feature-loop/SKILL.md`. The two gates people skip: **Clarify.** Only questions whose answer changes the code or the bytes on disk. Max three, batched, up front, each with a **bold** default so silence answers. Never about naming, formatting, or anything `docs/conventions.md` decides. None to ask? State assumptions in one line and move on. **Verify.** `./scripts/verify.sh` green, plus one piece of feature-specific evidence you actually ran (golden file, `curl`, test name, benchmark). Never report success from reading your own diff. "Should work" is not a result. ## 5. Definition of done - [ ] Plan's success criteria demonstrated with real output. - [ ] `./scripts/verify.sh` green. - [ ] Diff contains nothing outside the planned files. - [ ] `docs/state.md` updated (inventory, counters, latent items, verified-at line). - [ ] ADR in `docs/decisions.md` if a load-bearing choice was made. - [ ] Report states LOC delta, what is now earned, what you deliberately did not do. ## 6. Stop conditions — halt and ask - The change exceeds planned LOC by ~50%, or touches an unplanned file. - You need a new dependency, a new package, or a change to a frozen contract. - You are about to write a mock, a `switch` on a type, or a second copy of parsing logic. - The feature only works if the untrusted boundary bends. - Two reasonable designs exist and the choice is expensive to reverse — present both, briefly. Stopping early costs one message. Guessing costs a refactor. ## 7. Style floor Go stdlib idiom, `net/http` + `html/template`, no framework. `%w` wrapping at package boundaries only. `log/slog`. Table-driven tests, golden files in `testdata/`. Explicit wiring in one file, no `init()`. Size thresholds live in `scripts/budgets.env` only. Full rules: `docs/conventions.md` — read them, do not ask.