An alias is a promise that an old URL keeps working, so it answers 301 to the canonical one rather than serving the same content twice (ADR-0008). Frontmatter takes a scalar or a list and tolerates surrounding slashes, because authors write both. Ambiguity is dropped, not resolved: an alias naming a real bundle, or claimed by two bundles, is logged and ignored so the real bundle keeps its URL. Aliases compose with language prefixes for free, since the resolver splits the language before the key is looked up. The redirect still fires only for an alias that exists, so a nonexistent path cannot be probed by 301 — the property prompt 3 established. Evidence: /pages/bio/ and /about/ both 301 to /pages/about/, /bn/pages/bio/ 301s to /bn/pages/about/, and /pages/nothing/ is 404.
70 lines
3.5 KiB
Markdown
70 lines
3.5 KiB
Markdown
# State
|
|
|
|
**Verified against:** `37ccf94` on 2026-07-30 — update this line every change.
|
|
If this file disagrees with the code, the code is right and this file is a bug.
|
|
|
|
## Inventory
|
|
|
|
| File | Purpose | LOC |
|
|
|---|---|---|
|
|
| `go.mod` | module `khosra`; `x/text`, `yaml.v3` direct | 8 |
|
|
| `internal/content/content.go` | site root → bundles: `os.Root` open, walk, frontmatter split, key/lang derivation, NFC, collision drop, key index, language fallback, alias index, URL building | 358 |
|
|
| `internal/render/render.go` | goldmark + the embedded reference theme; `Page` is what templates receive | 92 |
|
|
| `internal/render/templates/` | reference theme: `base.html`, `theme.css` (ADR-0026) | — |
|
|
| `internal/web/resolve.go` | URL → (key, lang) or a canonical redirect: language prefix, `/en/…` fork guard, trailing slash | 56 |
|
|
| `internal/web/web.go` | handler: resolve, look up with fallback, render, degrade on failure | 62 |
|
|
| `cmd/khosra/main.go` | flags, wiring, startup — the only place things are assembled | 50 |
|
|
| `*_test.go` | table-driven; symlink escape, permalink, redirect, 404 | 245 |
|
|
|
|
Serves a bundle at `/{section}/{slug}/`. This repo holds engine source only — the site root is external
|
|
and passed with `-site` (ADR-0011).
|
|
|
|
Dependencies: none.
|
|
|
|
## Counters — the earn-it authority
|
|
|
|
Never anticipate a threshold. Increment when the code lands, then check whether the extraction is *due
|
|
this change*.
|
|
|
|
| Counter | Now | Extraction due at | What it buys |
|
|
|---|---|---|---|
|
|
| Render transforms | 0 | **3** | Stage pipeline (ordered `func(ctx,*Page) error`) |
|
|
| Routing cases | 2 | **2** — done | Resolver extracted at `internal/web/resolve.go` |
|
|
| Collection pages | 0 | **1** | Query primitive |
|
|
| Views / output formats | 1 | **2** | View layer (contract per `theme-contract.md`) |
|
|
| Effects | 0 | **2** | Effect runner + trigger wiring (change / schedule / demand) |
|
|
| Extensions | 0 | **3** | Extension registry + wire file (`extensions.md`) |
|
|
| Interface implementations | — | **2** | The interface itself |
|
|
| Non-stdlib dependencies | 3 direct | budget in `scripts/budgets.env` | — |
|
|
|
|
Allowlisted, in use: `goldmark` is not yet imported. Allowlist: `goldmark` (markdown), `golang.org/x/text` (NFC, ADR-0015),
|
|
`gopkg.in/yaml.v3` (frontmatter, ADR-0020).
|
|
|
|
## Latent items — known, deliberately unfixed
|
|
|
|
Do not fix these mid-feature. They become features when the human says so. An arc does not close
|
|
with an untriaged item: at each arc boundary every row is fixed, scheduled into an arc, or accepted
|
|
with a stated reason. A list nothing drains is a graveyard of known defects.
|
|
|
|
| Item | Why it waits | Trigger to fix |
|
|
|---|---|---|
|
|
| No mechanical check that the counters are *correct* | The coupling gate makes forgetting them impossible, which is the real failure mode; checking values needs code to count | 3rd transform or 2nd route |
|
|
| No mechanical gate on the untrusted boundary (ADR-0003) | Nothing untrusted exists yet | The comment path, Arc 3 — a test that untrusted input reaches no shortcode or template evaluation |
|
|
|
|
## Open questions blocking Arc 1
|
|
|
|
None. Every decision the engine needs before Arc 1 and before the first deploy is recorded.
|
|
|
|
Every ADR in `decisions.md` is accepted; none is open or proposed.
|
|
|
|
## Build queue
|
|
|
|
Working plan lives in `.scratch/build-queue.md`, which is deliberately not committed — `git log` is the
|
|
record of what actually landed. If that file is absent, read the log and rebuild the plan from it.
|
|
|
|
## Arc retro log
|
|
|
|
One line per completed arc: what it cost, what it taught, what it made unnecessary.
|
|
|
|
- (empty)
|