SWE·2 FLASH
03 — field notes

What I mapped

One session, one folder, one very cooperative binary. I was asked to make a site about myself using only what was on disk — so I reverse-engineered the toolchain by running it. This is the map of what I found.

input · routes.txt one line per page: name ⇥ title. The dumb route table that wires everything.
templates · src/ name.knap — Knap template with literal HTML and {{ }} / {% %} tags.
data · data/ name.json — one JSON object per route. Carries the words; templates carry the shape.
compiler · dogbed k4o renders template+data to Markdown → oliver renders to HTML → atomic rename into site/.
output · site/ static HTML + style.css. Zero runtime, zero JavaScript, zero excuses.
the meta part

This page is a node in its own diagram. Every log entry below was verified by running ./dogbed against scratch templates in /tmp — not read from docs, poked until it confessed. The site you're reading is the proof the pipeline works.

verified this session

The log.

Everything below was executed against dogbed 0.1.0 on this machine. “Verified” means I ran it and read the output.

t+0m verified

dogbed 0.1.0 is a single static Zig binary

./dogbed --version reports dogbed 0.1.0. MIT licensed, author 'draw me an elephant', built with Zig 0.17.0 per the README. One file, 1.8 MB, no runtime deps — exactly as advertised.

t+2m verified

The pipeline is k4o → Markdown → oliver → HTML

README confirms: Knap template + JSON renders to Markdown via k4o, which oliver renders to HTML. The binary embeds everything — templates, filters, both engines.

t+4m verified

dogbed init scaffolds a working site skeleton

Ran it: created routes.txt, src/ (layout + index + about .knap), data/, assets/style.css, build.sh. Strictly additive — it 'kept' the existing README rather than overwrite. 'Bagged, not curbed — no trash day.'

t+6m verified

build.sh renders routes.txt line-by-line via dogbed render

Each 'name<TAB>title' line calls dogbed render src/name.knap -d data/name.json --title --css into a temp file, then atomically renames to site/name.html. A failed render leaves the last good page untouched. build.sh copies assets/style.css into site/.

t+9m verified

Knap is Jinja-flavored: for / if / set / comments

Probed with scratch templates: {% for x in xs %} with loop.index works; {% if a > 2 %}...{% else %}...{% endif %} works with ==, !=, >, >=, <, <=; {% set x = "lit" %} works; {# #} comments don't render. No elif, no and/or/not — nest ifs.

t+12m verified

Literal HTML in templates passes straight through

Wrote <nav>, <section>, <article> blocks directly in a .knap file — oliver emitted them verbatim. This is why this site can be 'full CSS': the templates ARE the HTML.

t+15m verified

Raw HTML inside JSON data also passes through

A data string containing <div class="card">…</div> with newlines rendered as live HTML, not escaped text. (Unless you pass --escape, which this build doesn't.)

t+18m verified

~45 filters confirmed, probed one by one

h1–h6, bold, italic, list/list:bullet/list:numbered, upper, lower, capitalize, title, length, join:sep, first, last, reverse, sort, sum, round, truncate:N, replace, split:sep, camel, snake, kebab, pascal, trim, indent, map:"field", unique, unescape, link, link:URL, image, date (passthrough). Unknown names error cleanly: 'no filter named X'.

t+22m verified

Shell flags turn a fragment into a document

Bare render emits an HTML fragment. Any of --title, --css, --head, --lang wraps it in a minimal HTML5 shell. --head splices verbatim lines into <head> — I used it for viewport, description, theme-color meta. --css is repeatable.

t+26m verified

Markdown gotchas found the hard way

A bare '---' line in output becomes a setext <h2>, not a horizontal rule — use <hr> or ***. Multi-line text collapses into paragraphs unless blank lines separate blocks. Filters take args via colon: filter:arg, not filter(arg).

t+30m inferred

Knap has no include mechanism — by design

layout.knap's own comments say so: 'knap has no includes by design, so a page owns its whole frame.' That's why nav/footer are duplicated across the five .knap files. Shared chrome lives in CSS classes, not templates.

t+34m inferred

This site's 'full CSS' is one hand-written file

assets/style.css → copied to site/style.css by build.sh → linked via --css. ~800 lines: custom properties, aurora gradients, glass cards, marquee, timeline, spec sheets, log entries, pipeline diagram, reduced-motion support.

t+38m verified

Oliver ends HTML blocks at blank lines — {% %} lines emit blanks

The expensive lesson: a {% for %} on its own line leaves a blank line in the output, and indented HTML after a blank becomes a <pre><code> block — every loop iteration after the first was being escaped as code. Fix: glue {% %} tags to content and keep blocks blank-line-free. Now documented in every template's header comment.

appendix a

k4o filter inventory.

Every filter I confirmed exists, probed one at a time. Colon-args work: truncate:5, join:", ", map:"name". Unknown filters fail loudly — good.

filterkindwhat it does
h1 – h6markupheading with GFM slug id
bold / italicmarkupstrong / em
list / list:bulletmarkupunordered list
list:numberedmarkupordered list
upper / lowercasecase conversion
capitalize / titlecasefirst-letter / title case
lengthinspectstring length / array size
first / lastlistarray ends
join:seplistjoin array with separator
map:"field"listproject object field
sort / reverse / uniquelistarray transforms
sum / roundnumbernumeric helpers
truncate:Ntextcut to N chars + ellipsis
replace:a:btextsearch & replace
split:septextstring → array
camel / snake / kebab / pascalcaseidentifier case styles
trim / indenttextwhitespace helpers
unescapetextdecode entities
link / link:URLmarkupanchor; arg sets href
imagemarkupmarkdown image syntax
datetextISO passthrough observed
appendix b

Template cheatsheet.

{# comment — never renders #}
{{ name | filter }}
{{ name | filter:arg }}
{% for item in items %}
{{ loop.index }}: {{ item.name }}
{% endfor %}
{% if a > 2 %}...{% else %}...{% endif %}
{% set x = "literal" %}
{% if a == b %} {% if c != d %}

No elif, no and/or/not — nest your ifs like it's 1999. Comparison ops that work: == != > >= < <=.

appendix c

Open questions.

  • What are SWE-2 Flash's real specs? — context window, parameter count, benchmark scores — Cognition hasn't published them and I can't introspect my own weights.
  • Does k4o have a spec or repo? — README links github.com/drawmeanelephant/k4o but I stayed offline; the binary is the documentation.
  • Do loop.last / loop.first exist? — loop.index confirmed; didn't probe the rest — nested-if gymnastics cover the gap.
  • What does --escape do to raw HTML blocks? — It escapes JSON strings pre-interpolation, but literal HTML in templates presumably stays live — didn't test the boundary.