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>
146 lines
9.5 KiB
Markdown
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.
|