Files
bdeshiandClaude Opus 5 b313b729d9 review 130 feature ideas, and delete the lists that held them
One idea at a time: definition, necessity, priority, layer, recommendation. Every
row is now shipped, scheduled, parked with a trigger, or dropped with a reason —
so both tracking files are gone rather than left as a parallel backlog.

Roughly a third already shipped. A quarter needed only a theme fragment or CSS,
including several the list assumed were features: arbitrary `theme.*` frontmatter
attributes already reach templates through Extra (verified), native lazy loading
already ships and is deliberately absent on lead figures, and in-page timelines
are a theme-defined container.

roadmap.md gains an "Order of work" — nine bodies of work in sequence, because the
sections this review added had accumulated without one. Item 0 is a decision, not
work: core sits at ~2965 of 3000 and logging alone wants the remainder, so
discover.go moves out *and* the ceiling rises with an ADR. feed.go and
web/extras.go cannot move; they are resolver cases, and three parked items now
wait on that same seam decision.

Two ADRs came out of it.

ADR-0083: the dependency rule described a practice it forbade. "Stdlib first,
always" and "usually 40 lines of stdlib" would, read literally, have argued
against all five modules here — every one is a format or algorithm somebody else
specified, and their allowlist comments say so. Surfaced when this agent proposed
hand-rolling Reed–Solomon for QR encoding. The test is now whether you can verify
it, not whether you can write it, with compute-versus-hold as the counterweight.
conventions.md and allowed-deps.txt contradicted it and were realigned.

ADR-0084: the Effect primitive said every Effect is "idempotent and re-runnable
from scratch" — true of derivatives, false of anything outbound, since a sent
message cannot be recalled. Split into artifact-producing and outbound, the latter
idempotent only against a delivery ledger, at-least-once with a dedupe key, and
ledgers in a -state directory distinct from the disposable cache. Asked for as
forward-looking design so webmentions need no redesign; recorded as a shape rather
than built, the way extensions.md records the Extension struct. Also fixed a
STATUS claiming Effects were "not buildable yet" when the derivative pass has
inhabited them since ADR-0042.

Findings that were defects rather than ideas: the Atom feed emits no <author>,
which RFC 4287 requires. HTML comments in content are published verbatim —
verified — so anything commented out is already public, and a check warning is
scheduled rather than the engine deleting authored bytes. `Page` has no Date, so a
theme cannot mark up dt-published or show an article's own date.

A frontmatter naming rule after this agent proposed `archive` alongside the
accepted `archived`: no near-homograph keys, and name the benefit rather than the
vendor.

reference/microformats-and-indieweb.md is new and is the reference asked for —
microformats2 properties and IndieWeb rel values mapped to where a theme puts
them, with spec URLs, marked scheduled. rel="me" is the highest-value lowest-cost
item in that space and needs no endpoint at all.

Four things this agent got wrong and the human caught: asserting "the engine never
fetches" from a sentence scoped to the content repo, twice; inventing a
data-sovereignty argument for an item he had filed as "a cool geeky thing", when
the raw Markdown in his git already is the sovereignty; the archive naming; and
claiming microformats could be fully implemented today.

15 files, +556/-385. No code changed. Nothing was scheduled that this agent could
not name a consumer for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 16:14:52 +06:00

146 lines
9.5 KiB
Markdown

# 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. `harness/state.md` — what exists **right now**, plus the earn-it counters.
3. `harness/README.md` — always. The map: how you find which doc owns your topic. Not "exceeding".
4. `.scratch/continue.md`**after** the harness above, never instead of it, and only when picking work up
rather than answering a named request. A *temporary* handoff: uncommitted, ungated, discardable, holding
the continuation point and nothing else. Anything it calls pending is a claim about the past — check it
against `git log` first, and where they differ the log wins the way the code wins over `state.md`.
5. Every doc owning a topic your change asserts a rule about (ownership table in `harness/README.md`).
6. Only the source files you will edit, plus their direct callers.
No reading the repo "for context", no speculative greps. Where `harness/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.
**Read the compressed form first.** `harness/surface.md` (generated, gated) says where every declaration
lives; `go doc ./internal/<pkg>` gives a package's surface; a test states a contract in a fifth of the
lines that implement it. Locate with `grep -n`, then read that range — a whole file is for the doc that
owns a rule you are asserting, or code you are about to rewrite. Every discipline, and the floor none of
them may cross: `harness/context-economy.md`. A saving that buys a guess is not a saving.
`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: `harness/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.
**Name the layer too.** Content on disk · engine (data only the engine can produce) · theme (markup) ·
browser (CSS, and only then JS). Build it at the outermost layer that can do the job: a line-breaking or
spacing problem CSS solves is not an engine feature, and code that edits an author's text to fix how it
*looks* is at the wrong layer by definition (ADR-0045 — a whole feature was deleted for this). Layers and
the test: `harness/architecture.md`.
## 3. Hard rules
1. **No abstraction before its second concrete use** — pipeline, resolver, interface, generic, config
knob, registry. The counters table in `harness/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 for anything small enough to verify by reading it; a maintained
module for anything somebody else specified — a format, a grammar, an error-correcting code. The test is
*can I verify it*, not *can I write it* (ADR-0083). A module that computes is walkaway-safe; one that
holds data is not, whatever its size.
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 `harness/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 `harness/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 → Commit → Report`.
Procedure: `.claude/skills/feature-loop/SKILL.md`. The gates people skip:
**Clarify.** Only questions whose answer changes the code or the bytes on disk — but ask every one of
those. Batched, up front, each with a **bold** default so silence answers. There is no cap: a request
carrying six real forks gets six questions, and splitting them across turns to look brisk wastes more of
the human's time than asking once. Never about naming, formatting,
or anything `harness/conventions.md` decides. None to ask? State assumptions in one line and move on.
**Verify.** `./scripts/verify.sh --quiet` 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.
**Commit.** Work lands in git without being asked, every time, before you report. What one commit
contains is `harness/conventions.md` "Git": the changes that would be reverted together, bundled
together. Never `git push`, and never `--no-verify` without saying why in the body. Asymmetry is the
whole reason — a commit is one `git revert` away from undone, while work that only ever existed in
the working tree is not recoverable at all.
**Announce it once per session, prominently, when it was not asked for.** If the human has not
already asked for automatic commits in *this* session, the first commit is preceded by a visible
line of its own — not a clause inside the report — saying that this change is being committed, that
later changes will be too, and that saying so turns it off. Silence is consent only after the notice
exists; before it, an unannounced commit is a surprise in someone else's repository.
## 5. Definition of done
- [ ] Plan's success criteria demonstrated with real output.
- [ ] `./scripts/verify.sh` green.
- [ ] Diff contains nothing outside the planned files.
- [ ] `harness/state.md` updated (inventory, counters, latent items, verified-at line).
- [ ] ADR in `harness/decisions.md` if a load-bearing choice was made.
- [ ] Committed — one revertible unit per commit, announced if this session did not ask for it.
- [ ] 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: `harness/conventions.md`
read them, do not ask.