Commit Graph
9 Commits
Author SHA1 Message Date
Claude Opus 5andbdeshi 77c658bf98 move the feature catalogue out of docs, and replace the queue with a plan
Two structural changes, both about what an agent may pull into context.

exploration.md catalogues engine features nobody has asked for. That is storage,
not working material, so it moves to ideas/ where nothing sweeps it and it is
opened only when named — the same rule the other parked material already
follows. Six references repointed; the ideas gates then demanded an index line
and a status, and both were supplied rather than exempted.

build-queue.md was 516 lines, nearly all of it entries 0-23 finished months of
work ago, with the plan buried at the top. It becomes .scratch/continue.md at
49: where the code is, what is planned, and the findings worth carrying that no
doc owns — chiefly that silent damage to prose is this engine's recurring
failure mode, and that three defects this arc were invisible to curl.

Docs and HARNESS point at the new names. No rule, gate or threshold changed.
2026-08-02 00:57:55 +06:00
Claude Opus 5andbdeshi b574800adb delete the widows feature; line breaking is CSS
The human asked whether widow prevention belonged in the backend at all. It did
not, and it broke two rules already written down: the theme contract says the
engine decides nothing about how something looks, and ADR-0034 says authored body
text is the author's — while this inserted U+00A0 into that text.

The practical harm follows from the layer error rather than from a coding mistake.
The engine cannot see the line box, so joining the last two words is a guess that
can overflow a narrow viewport, and a reader copying the paragraph gets a
non-breaking space in their clipboard. `text-wrap: pretty` and `text-wrap: balance`
in the reference stylesheet know the line box and need no bytes in the content.

108 lines of engine deleted for one CSS declaration. The typographer stays: turning
`--` into an en dash is a text transformation no stylesheet can express, which is
exactly the distinction the new layer test draws.

Also worth recording: this took the Extensions counter from 3 back to 2. A threshold
reached by a feature that should not have existed was never a threshold.
2026-07-31 12:47:03 +06:00
Claude Opus 5andbdeshi 04ae2ff475 prevent widows, as the second feature package
The last two words of a paragraph or heading are joined by a non-breaking space,
so one word never falls alone onto its own line. Deferred from entry 11 for the
right reason: over rendered HTML this cannot tell prose from an escaped code span,
so it had to wait for a tree transform.

The interesting failure is worth keeping: written against goldmark alone it passed
six tests, and did nothing in the real engine. The typographer splits a text run
wherever it looks for a substitution, so a paragraph ending "hand." arrives as two
text nodes and the last of them holds no space at all. A version that inspects
only the last child therefore finds nothing to join. It now takes the whole
trailing run of text nodes, stopping at a line break or any markup, and there is a
regression test that builds both extensions together — the only configuration that
would have caught it.

The joined text becomes a String node, which carries its own bytes: a segment is
an offset into bytes every node shares, so editing the source in place is not
possible. That path still escapes, and a test says so, since otherwise this
transform would be an injection route.

PhaseMarkup is now empty in extensions.md, and honestly so: everything expected
there turned out to belong either earlier or later.
2026-07-31 02:12:08 +06:00
Claude Opus 5andbdeshi 2041c43b70 include another file from the bundle, one level deep
{{< include file="notes.md" >}} renders a file from the bundle as Markdown in
place. The included file is converted by the same goldmark instance that is
rendering the page — handed to the transformer in Extend — so its configuration
can never drift from the page's.

Three properties, each tested:

A name containing ".." is refused. os.Root would stop a path leaving the site
root, but path.Join collapses ".." long before the filesystem sees it, so without
this an include could read a template or a stray dotfile from the site root and
publish it. Verified the test fails without the guard: it took three levels of
".." from content/pages/d to reach the root, and the first version of the test
used two, so it passed either way and proved nothing.

An include inside an included file renders nothing and logs. The nested parse is
marked, so one level is all there is and a file including itself is a log line
rather than a stack overflow (ADR-0038, ADR-0029).

A gallery inside an included file still resolves, because the nested parse carries
the same Origin.

The node gained `content` for output a feature produced itself, plus `isContent`
so a failed include renders nothing instead of falling through to a fragment
lookup and complaining about a template that was never meant to exist.
2026-07-31 02:00:39 +06:00
Claude Opus 5andbdeshi f196e3420d add the gallery shortcode and the seam it needed
A feature now learns which bundle is rendering: render.Bundle puts an Origin —
the bundle's directory plus the rooted fs.FS — on the parse context, and
render.OriginFrom reads it back. Available while parsing, not while rendering,
which decides where a feature does its filesystem work: goldmark hands the
context to a block parser and not to a node renderer, so gallery gathers its
filenames at parse time and carries them on the node.

Reads stay inside the site root because Origin passes the fs.FS rather than a
path to join (ADR-0031).

Fragment{Args, Items} lands with it (ADR-0037), so figure's template now reads
.Args.src. Authored arguments and engine-gathered items stay in separate fields:
a src argument beside a src the engine found would otherwise silently pick one.

A gallery is pictures beside the bundle, in filename order, skipping
subdirectories and anything a browser cannot show. Filename order is what makes
the sparse numeric-prefix convention work without numbers in URLs (ADR-0016).

New latent row: the reference theme's images carry no width/height and a
gallery's carry no alt, which is below the output floor conventions.md states.
Nothing can supply either yet — dimensions need the image read, and a filename is
not alt text. Queue 13 computes dimensions and brings structured items with it.
2026-07-30 10:39:37 +06:00
Claude Opus 5andbdeshi c16bf4bd9d add shortcodes as the first internal/ext feature
A call is `{{< name key="value" >}}` alone on a line, parsed by a goldmark block
parser into an AST node and rendered by executing a theme template of that name
(ADR-0036). `figure` ships; `include` and `gallery` need the including bundle's
directory, which the parser does not carry yet, so they wait.

The layering did the design work here. internal/render may not import
internal/ext, so render.New takes a callback that receives a Partial and returns
Markdown extensions, and cmd/khosra/wire.go holds the only list of enabled
features. Empty that list and the engine still builds and serves — which is the
property extensions.md says the contract should have.

Raw HTML stays disabled. An author's text reaches a page only as arguments that
html/template escapes in context, which the real binary shows: a hostile alt
becomes &lt;script&gt; and src="javascript:…" becomes #ZgotmplZ. Getting
contextual escaping from the standard library rather than writing it is the whole
reason a fragment renders this instead of the feature.

parseSet became variadic so the fragment set reuses it rather than growing a
second copy of the overlay logic; `Partial` takes map[string]string after the
advisory correctly flagged `any` as generality nothing had asked for.
2026-07-30 10:29:00 +06:00
Claude Opus 5andbdeshi 9d817dcadf 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.
2026-07-30 01:14:01 +06:00
Claude Opus 5andbdeshi 99f8c730b0 harness: withdraw five pre-code ADRs to deferred ideas
Twenty-seven ADRs existed before a line of Go. Five specified the shape of
unbuilt mechanisms — cache validity, declared types, the settings cascade,
taxonomies, extras — which breaks the rule against abstraction before a
second concrete use, in prose where the counters cannot see it. They move to
ideas/deferred-decisions.md as recorded intent and return shaped by whatever
implements them.

Citations retargeted throughout; where one was decoration the rule now stands
on its own reasoning. Type declarations and the cascade drop to [spec] with
the MVP behaviour stated instead, so the first prompts have less to build.
conventions.md names http.ServeMux as the router, closing a hole that invited
hand-rolling a path splitter. The ADR gate now checks a number is registered
in the log rather than headed by an entry, so withdrawals resolve and invented
numbers still fail. Two architecture invariants corrected: identity no longer
implies a required language suffix, and the duplicated permalink clause is gone.
2026-07-30 01:01:38 +06:00
bdeshiandClaude Opus 5 02268f9121 init
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 00:34:18 +06:00