rename the project to khosra, initialise the module
Naming is free before a module is published, a URL is shared, or a binary is deployed; every day it waits costs more. Swept every form: module path, binary, cmd/ directory, KHOSRA_SITE, the feature-loop skill directory, and the prose in earlier ADRs — which describe this project under its old name, not a different project. Recorded as ADR-0030. go mod init lands here rather than with the first feature because the module path is what the rename is about. x/text and yaml.v3 are required but not yet imported, so both are indirect and no direct dependency is claimed yet.
This commit is contained in:
@@ -19,6 +19,8 @@
|
|||||||
"Bash(git diff:*)",
|
"Bash(git diff:*)",
|
||||||
"Bash(git log:*)",
|
"Bash(git log:*)",
|
||||||
"Bash(git show:*)",
|
"Bash(git show:*)",
|
||||||
|
"Bash(git add:*)",
|
||||||
|
"Bash(git commit:*)",
|
||||||
"Bash(rg:*)",
|
"Bash(rg:*)",
|
||||||
"Bash(wc:*)",
|
"Bash(wc:*)",
|
||||||
"Bash(curl -s http://localhost:*)",
|
"Bash(curl -s http://localhost:*)",
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
/atelier
|
/khosra
|
||||||
*.test
|
*.test
|
||||||
*.out
|
*.out
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# atelier — agent constitution
|
# khosra — agent constitution
|
||||||
|
|
||||||
`atelier` is a flat-file personal publishing engine in Go, built solo, one feature at a time.
|
`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.
|
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
|
A **personal publishing substrate**: a directory of Markdown becomes an owned, networked home for
|
||||||
@@ -74,7 +74,7 @@ service or a core-model change): stop, say so in a paragraph, propose the leaf,
|
|||||||
## 4. The loop (every request, no exceptions)
|
## 4. The loop (every request, no exceptions)
|
||||||
|
|
||||||
`Clarify → Plan → Implement → Verify → Document → Report`.
|
`Clarify → Plan → Implement → Verify → Document → Report`.
|
||||||
Procedure: `.claude/skills/atelier-feature-loop/SKILL.md`. The two gates people skip:
|
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,
|
**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,
|
batched, up front, each with a **bold** default so silence answers. Never about naming, formatting,
|
||||||
|
|||||||
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
# The atelier harness — human's guide
|
# The khosra harness — human's guide
|
||||||
|
|
||||||
Scaffolding that makes an agent build `atelier` the way you want: minimally, surgically, questions
|
Scaffolding that makes an agent build `khosra` the way you want: minimally, surgically, questions
|
||||||
before code, docs that stay true.
|
before code, docs that stay true.
|
||||||
|
|
||||||
`README.md` is a two-line signpost — human here, agent to `CLAUDE.md`. `CLAUDE.md` is the
|
`README.md` is a two-line signpost — human here, agent to `CLAUDE.md`. `CLAUDE.md` is the
|
||||||
@@ -107,7 +107,7 @@ imports `cmd`. One convenient sibling import is what turns a layered engine into
|
|||||||
it always looks locally reasonable — so it is a hard failure, not a review note.
|
it always looks locally reasonable — so it is a hard failure, not a review note.
|
||||||
|
|
||||||
**Feature locality is enforced, not hoped for.** A feature is one directory under `internal/ext/<name>/`
|
**Feature locality is enforced, not hoped for.** A feature is one directory under `internal/ext/<name>/`
|
||||||
plus one line in `cmd/atelier/wire.go`. `verify.sh` fails on a feature importing a sibling and on a
|
plus one line in `cmd/khosra/wire.go`. `verify.sh` fails on a feature importing a sibling and on a
|
||||||
feature package without a `doc.go` — the first because sibling imports make an agent's read set compound,
|
feature package without a `doc.go` — the first because sibling imports make an agent's read set compound,
|
||||||
the second because a four-line `doc.go` turns orientation into a fifteen-line read (ADR-0027).
|
the second because a four-line `doc.go` turns orientation into a fifteen-line read (ADR-0027).
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# atelier
|
# khosra
|
||||||
|
|
||||||
A flat-file personal publishing engine in Go. Point the binary at a directory of Markdown and it
|
A flat-file personal publishing engine in Go. Point the binary at a directory of Markdown and it
|
||||||
becomes an owned, networked home for fiction, webcomics, art, and essays, in English and Bengali.
|
becomes an owned, networked home for fiction, webcomics, art, and essays, in English and Bengali.
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ whatever builds it. What exists is `state.md`'s job. Never build a `[spec]` sect
|
|||||||
|
|
||||||
## The site root
|
## The site root
|
||||||
|
|
||||||
The engine is pointed at a site root outside this repository (`-site <dir>`, or `ATELIER_SITE`), with
|
The engine is pointed at a site root outside this repository (`-site <dir>`, or `KHOSRA_SITE`), with
|
||||||
its own git history (ADR-0011). Nothing in the engine repo is content. `templates/` in the site root
|
its own git history (ADR-0011). Nothing in the engine repo is content. `templates/` in the site root
|
||||||
overrides the defaults the binary embeds, so a bare root still renders.
|
overrides the defaults the binary embeds, so a bare root still renders.
|
||||||
|
|
||||||
|
|||||||
+3
-3
@@ -8,7 +8,7 @@ goes through an ADR, not a diff.
|
|||||||
- `net/http`, `html/template`, `log/slog`, `os`, `io/fs`, `embed`. Routing is `http.ServeMux` with Go
|
- `net/http`, `html/template`, `log/slog`, `os`, `io/fs`, `embed`. Routing is `http.ServeMux` with Go
|
||||||
1.22+ patterns — `GET /{section}/{slug}/` and `{path...}` for multi-segment slugs — so no router
|
1.22+ patterns — `GET /{section}/{slug}/` and `{path...}` for multi-segment slugs — so no router
|
||||||
library is needed. No web framework, ORM, or config library — flags plus environment variables, parsed in one place. The site root
|
library is needed. No web framework, ORM, or config library — flags plus environment variables, parsed in one place. The site root
|
||||||
(`-site`, `ATELIER_SITE`) is the only required setting; default templates are `embed`ded so a bare
|
(`-site`, `KHOSRA_SITE`) is the only required setting; default templates are `embed`ded so a bare
|
||||||
site root renders (ADR-0011).
|
site root renders (ADR-0011).
|
||||||
- New dependency = ADR + human approval + `scripts/allowed-deps.txt`. `verify.sh` enforces it.
|
- New dependency = ADR + human approval + `scripts/allowed-deps.txt`. `verify.sh` enforces it.
|
||||||
- Prefer 40 lines of obvious code over a dependency doing it in one call — unless the 40 lines would be
|
- Prefer 40 lines of obvious code over a dependency doing it in one call — unless the 40 lines would be
|
||||||
@@ -16,7 +16,7 @@ goes through an ADR, not a diff.
|
|||||||
|
|
||||||
## Package layout
|
## Package layout
|
||||||
```
|
```
|
||||||
cmd/atelier/ main, flag parsing, explicit wiring — the only place things are assembled
|
cmd/khosra/ main, flag parsing, explicit wiring — the only place things are assembled
|
||||||
internal/content/ bundles, frontmatter, slugs, queries — knows the disk, not HTTP
|
internal/content/ bundles, frontmatter, slugs, queries — knows the disk, not HTTP
|
||||||
internal/render/ markdown, transforms, templates — knows content, not HTTP
|
internal/render/ markdown, transforms, templates — knows content, not HTTP
|
||||||
internal/web/ handlers, routing, headers, caching — knows both, exposes neither
|
internal/web/ handlers, routing, headers, caching — knows both, exposes neither
|
||||||
@@ -26,7 +26,7 @@ Dependencies point inward. `internal/content` imports nothing from the others; `
|
|||||||
and is imported by nothing. A feature under `internal/ext/` may import `internal/content` and
|
and is imported by nothing. A feature under `internal/ext/` may import `internal/content` and
|
||||||
`internal/render`, and must not import `internal/web`, `cmd/`, or **another feature** — sibling imports
|
`internal/render`, and must not import `internal/web`, `cmd/`, or **another feature** — sibling imports
|
||||||
are what make an agent's read set compound (ADR-0027). Every `internal/ext/*` package carries a `doc.go`;
|
are what make an agent's read set compound (ADR-0027). Every `internal/ext/*` package carries a `doc.go`;
|
||||||
`verify.sh` fails without one. Routes reach `web` by assembly in `cmd/atelier/wire.go`, so `web` never
|
`verify.sh` fails without one. Routes reach `web` by assembly in `cmd/khosra/wire.go`, so `web` never
|
||||||
learns features exist. No `utils`, `helpers`, `common`, `shared`, `manager`, `base`,
|
learns features exist. No `utils`, `helpers`, `common`, `shared`, `manager`, `base`,
|
||||||
`impl`, `core` — a package name not describing a domain is a smell. Flat until a package exceeds
|
`impl`, `core` — a package name not describing a domain is a smell. Flat until a package exceeds
|
||||||
`FILE_LOC_WARN`; do not pre-partition, and a single-file package therefore warns at the same point it
|
`FILE_LOC_WARN`; do not pre-partition, and a single-file package therefore warns at the same point it
|
||||||
|
|||||||
+43
-2
@@ -116,7 +116,7 @@ Revisit if: the engine can no longer boot and serve correctly with every externa
|
|||||||
Date: 2026-07-28 · Status: accepted
|
Date: 2026-07-28 · Status: accepted
|
||||||
Decision: the engine is pointed at a **site root** — a directory outside this repository, versioned in
|
Decision: the engine is pointed at a **site root** — a directory outside this repository, versioned in
|
||||||
its own git repo — holding `content/`, `static/`, and optionally `templates/` overriding the defaults
|
its own git repo — holding `content/`, `static/`, and optionally `templates/` overriding the defaults
|
||||||
the binary embeds. Selected by `-site <dir>` or `ATELIER_SITE`. This repository contains engine source
|
the binary embeds. Selected by `-site <dir>` or `KHOSRA_SITE`. This repository contains engine source
|
||||||
only; no content, ever, not even an example.
|
only; no content, ever, not even an example.
|
||||||
Why: content is the database, and a database does not live in the application's repo. Mixing them
|
Why: content is the database, and a database does not live in the application's repo. Mixing them
|
||||||
interleaves "fixed a typo in a poem" with "extracted the resolver" in one history, makes every typo a
|
interleaves "fixed a typo in a poem" with "extracted the resolver" in one history, makes every typo a
|
||||||
@@ -298,7 +298,7 @@ Revisit if: it starts accumulating design decisions — then split it into a ref
|
|||||||
## ADR-0027 — Feature locality: one directory, no sibling imports, a mandatory `doc.go`
|
## ADR-0027 — Feature locality: one directory, no sibling imports, a mandatory `doc.go`
|
||||||
Date: 2026-07-30 · Status: accepted
|
Date: 2026-07-30 · Status: accepted
|
||||||
Decision: a feature is one directory under `internal/ext/<name>/` from its first use, plus one line in
|
Decision: a feature is one directory under `internal/ext/<name>/` from its first use, plus one line in
|
||||||
`cmd/atelier/wire.go` — the only file that knows every feature. No `internal/ext` package may import
|
`cmd/khosra/wire.go` — the only file that knows every feature. No `internal/ext` package may import
|
||||||
another. Every one carries a `doc.go` stating, in four lines: what it contributes, which cascade keys it
|
another. Every one carries a `doc.go` stating, in four lines: what it contributes, which cascade keys it
|
||||||
reads, which theme-contract fields it adds, and what it deliberately does not do. A feature may import
|
reads, which theme-contract fields it adds, and what it deliberately does not do. A feature may import
|
||||||
`internal/content` and `internal/render`; never `internal/web`, never `cmd/`, never a sibling.
|
`internal/content` and `internal/render`; never `internal/web`, never `cmd/`, never a sibling.
|
||||||
@@ -313,3 +313,44 @@ inward (earning it under the counters) or stay duplicated until the third use, w
|
|||||||
rather than a new one. Expensive — `wire.go` absorbs all the coupling on purpose and will look
|
rather than a new one. Expensive — `wire.go` absorbs all the coupling on purpose and will look
|
||||||
repetitive; and this buys cheap *features*, not cheap spine changes, which still need the core read.
|
repetitive; and this buys cheap *features*, not cheap spine changes, which still need the core read.
|
||||||
Revisit if: `wire.go` becomes hard to read, which means the registry from `extensions.md` is due.
|
Revisit if: `wire.go` becomes hard to read, which means the registry from `extensions.md` is due.
|
||||||
|
|
||||||
|
## ADR-0028 — Pagination lives in the path: `/{section}/page/2/`
|
||||||
|
Date: 2026-07-30 · Status: accepted
|
||||||
|
Decision: page two of a listing is `/{section}/page/2/`; page one is the bare listing URL and never
|
||||||
|
`/page/1/`, which permanently redirects to it. Tag listings compose the same way:
|
||||||
|
`/tags/{tag}/page/2/`. `page` is therefore a reserved segment inside a section.
|
||||||
|
Why: a query parameter is not a permalink — it reads as ephemeral, is dropped by careless sharing, and
|
||||||
|
splits caching by URL shape rather than by content. Putting it in the path keeps every listing page
|
||||||
|
linkable and cacheable on the same terms as a bundle.
|
||||||
|
Consequence: cheap — one more resolver case, and every paginated listing is a first-class URL. Expensive
|
||||||
|
— no bundle may be slugged `page` inside a paginated section, and changing the page size renumbers pages,
|
||||||
|
so page URLs are stable only while the size is.
|
||||||
|
Revisit if: never for the shape. Page size is a separate setting and changing it is a URL event.
|
||||||
|
|
||||||
|
## ADR-0029 — A bundle that fails to parse is skipped loudly, not fatal
|
||||||
|
Date: 2026-07-30 · Status: accepted
|
||||||
|
Decision: a bundle whose frontmatter or filename cannot be parsed is logged at error level with its path
|
||||||
|
and line, excluded from the site, and does not stop startup or any request. The `check` command reports
|
||||||
|
the same failures as errors, and that is where a non-zero exit belongs.
|
||||||
|
Why: `conventions.md` says startup failure is fatal and loud, and separately that request-time failure
|
||||||
|
degrades. A single mistyped colon in one post is not a startup failure — treating it as one means one
|
||||||
|
typo takes the whole site down, which is the worst possible failure mode for a personal site published
|
||||||
|
from a text editor. Fatal is right for a broken site root or an unreadable directory, not for one file.
|
||||||
|
Consequence: cheap — the site always serves whatever is valid, and the author sees the error in the log
|
||||||
|
and in `check`. Expensive — a silently absent page is a real failure mode, so the log line must name the
|
||||||
|
file and the reason plainly, and `check` must exist early enough to be the place you look.
|
||||||
|
Revisit if: skipped bundles start going unnoticed in practice — then `check` runs in CI, rather than the
|
||||||
|
engine becoming fatal.
|
||||||
|
|
||||||
|
## ADR-0030 — The project is named khosra
|
||||||
|
Date: 2026-07-30 · Status: accepted
|
||||||
|
Decision: the engine is `khosra`. Module path `khosra`, binary `khosra`, command directory
|
||||||
|
`cmd/khosra/`, environment variable `KHOSRA_SITE`, feature-loop skill `khosra-feature-loop`. The former
|
||||||
|
name `atelier` appears nowhere, including in earlier ADRs, which describe this project under its old
|
||||||
|
name rather than a different project.
|
||||||
|
Why: naming is the author's, and it is free now — before a module is published, a URL is shared, or a
|
||||||
|
binary is deployed. Every day it waits, it costs more.
|
||||||
|
Consequence: cheap now, and permanent-ish once the module path is fetched by anything. Expensive later —
|
||||||
|
a module rename after publication needs a redirect or a major-version bump, and a deployed binary name
|
||||||
|
appears in service files and container tags.
|
||||||
|
Revisit if: never. Renaming again costs strictly more than this did.
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ type Extension struct {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`cmd/atelier/wire.go` holds the only list of enabled extensions. Enabling or disabling one is a
|
`cmd/khosra/wire.go` holds the only list of enabled extensions. Enabling or disabling one is a
|
||||||
one-line diff and a rebuild. Removing one leaves no trace elsewhere — that property is the test of
|
one-line diff and a rebuild. Removing one leaves no trace elsewhere — that property is the test of
|
||||||
whether the contract is right.
|
whether the contract is right.
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -5,8 +5,12 @@ If this file disagrees with the code, the code is right and this file is a bug.
|
|||||||
|
|
||||||
## Inventory
|
## Inventory
|
||||||
|
|
||||||
No Go source, no `go.mod`. The harness is installed; the engine is unwritten. This repo holds engine
|
| File | Purpose | LOC |
|
||||||
source only — the site root is external and passed with `-site` (ADR-0011).
|
|---|---|---|
|
||||||
|
| `go.mod` | module `khosra`; `x/text` and `yaml.v3` required, not yet imported | 8 |
|
||||||
|
|
||||||
|
No Go source yet. This repo holds engine source only — the site root is external and passed with `-site`
|
||||||
|
(ADR-0011).
|
||||||
|
|
||||||
Dependencies: none.
|
Dependencies: none.
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
module khosra
|
||||||
|
|
||||||
|
go 1.26.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
golang.org/x/text v0.40.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||||
|
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
+1
-1
@@ -333,7 +333,7 @@ longfuncs=$(echo "$gofiles" | xargs awk -v m="$FUNC_LOC_WARN" '
|
|||||||
if [ -f Dockerfile ] && [ "${VERIFY_DOCKER:-0}" = "1" ]; then
|
if [ -f Dockerfile ] && [ "${VERIFY_DOCKER:-0}" = "1" ]; then
|
||||||
head_ "container (VERIFY_DOCKER=1)"
|
head_ "container (VERIFY_DOCKER=1)"
|
||||||
if command -v docker >/dev/null 2>&1; then
|
if command -v docker >/dev/null 2>&1; then
|
||||||
if docker build -q -t atelier:verify . >/tmp/docker.log 2>&1; then
|
if docker build -q -t khosra:verify . >/tmp/docker.log 2>&1; then
|
||||||
pass "docker build"
|
pass "docker build"
|
||||||
else
|
else
|
||||||
bad "docker build"; tail -20 /tmp/docker.log | sed 's/^/ /'
|
bad "docker build"; tail -20 /tmp/docker.log | sed 's/^/ /'
|
||||||
|
|||||||
Reference in New Issue
Block a user