/invariants at the arc boundary: 1, 3 and 5 held and were checked rather than assumed; 2, 4, 6, 7, 8 and 9 are not yet applicable, with the pre-freeze core figure recorded as 619. Invariant 2 turned up a load-bearing default nobody had written down: goldmark omits raw HTML unless WithUnsafe is set, verified, and that is the only reason authored Markdown cannot inject script today. The obvious move when a shortcode needs to emit HTML removes it, so it is now a latent item triggered by queue entry 12 rather than a surprise. The other two latent items are triaged as the arc-close rule requires: the counter-correctness gap is accepted with a reason, the untrusted-boundary gate is scheduled to Arc 3.
77 lines
4.7 KiB
Markdown
77 lines
4.7 KiB
Markdown
# State
|
|
|
|
**Verified against:** `8a6857b` 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* | Accepted at the Arc 1 boundary: the coupling gate makes forgetting them impossible, which is the real failure mode, and checking the values needs code to count | 3rd transform (queue 12) |
|
|
| No mechanical gate on the untrusted boundary (ADR-0003) | Scheduled to Arc 3: nothing untrusted is read yet | The comment path — a test that untrusted input reaches no shortcode or template evaluation |
|
|
| Raw HTML in Markdown is currently omitted only because goldmark's default omits it | Verified at the Arc 1 boundary, and it is what keeps invariant 2 intact for authored content. `html.WithUnsafe()` is the obvious move when a shortcode needs to emit HTML, and it silently turns authored Markdown into an injection path | Shortcodes (queue 12) — if unsafe rendering is enabled, the trusted/untrusted split must be real code, not a default |
|
|
|
|
## 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.
|
|
|
|
- **Arc 1 — the spine.** 619 core lines, 3 dependencies, 4 queue entries. Taught: `os.Root` makes the
|
|
path guard a property of the type, so the latent item that shipped with the harness died instead of
|
|
being implemented; and running the gates against real code found six defects *in the gates* — an
|
|
allowlist parser that rejected its own documented format, two advisories that fired only on correct
|
|
code, a coupling gate that demanded explanations for permission edits, an untidy `go.mod` hiding a
|
|
direct dependency, and a nesting check off by one level. Made unnecessary: a hand-rolled traversal
|
|
cleaner, and a second routing branch — the resolver arrived by counter at exactly the right moment.
|