Files
kontra/.pi/memory-vault/packs/kontra-day/40-actions/2026-09-08-kontra-phase-3-build-goth-app-complete-golang-gotchas.md
sam 0876512f1c feat: employer-showcase README with architecture map + publish to maps.lab; Mermaid + Archify project diagrams
- docs/kontra-architecture.mmd (mermaid source) + rendered PNG/SVG
- docs/kontra-architecture-map.html (interactive Archify, validated + visual-checked)
- docs/architecture.md (doc with embedded mermaid)
- README.md (showcase: stack, content model, design system, deploy, security)
- published docs/ to https://maps.lab.audasmedia.com.au/kontra_day/docs/

[secret-scan: skip - false positive on generated mermaid SVG path data]
2026-09-09 11:49:52 +10:00

3.3 KiB

type, id, title, status, source_of_truth, freshness, last_reviewed, tags
type id title status source_of_truth freshness last_reviewed tags
action action.kontra-day.kontra-phase-3-build-goth-app-complete-golang-gotchas Kontra Phase 3 build — GOTH app complete + golang gotchas active false current 2026-09-08
pack/kontra-day
agent-memory/action
kontra
golang
templ
goldmark
goth
build

Kontra Phase 3 build — GOTH app complete + golang gotchas

Kontra — Phase 3 build complete (verified local)

What was built (2026-09-08)

GOTH stack app at /home/sam/home_network/web_sites/kontra_day/app:

  • golang 1.26.2 (the "Go" in GOTH is golang, NOT Rust-like Go — vgo.mod uses require (...) blocks, package main, func main()).
  • templ v0.3.1020 — CLI at ~/go/bin/templ (installed via go install github.com/a-h/templ/cmd/templ@latest, path must be on PATH). .templ files in app/src/, generated with templ generate (writes *_templ.go), build with go build -o kontra-bin . from app/src/. Build script: app/build.sh.
  • goldmark v1.8.6 markdown + goccy/go-yaml v1.19.2.
  • Single static binary ~11.5MB. PORT and KONTRA_CONTENT and KONTRA_MEDIA env vars.

Key golang gotchas (learned the hard way)

  • [start:end] slices PANIC (bounds) if end > len — clamp first! Use helper firstArticles(a, n)/firstSubjects.
  • No ternary operator; templ {} interpolation doesn't support inline if/else { } expressions — use block-level if between elements, and templ.KV(" active", cond) for conditional classes.
  • //go:embed web keeps FULL path in the virtual FS → root = web/, files at web/assets/kontra.css. Serve with http.Handle("/web/", http.FileServerFS(web)). Route patterns need trailing-slash (/web/) plus / catch-all registered LAST.
  • Route specificity works but registers specific before /.
  • go-yaml lowers struct field names for keys by default — use lowercase YAML keys (name/tagline/defaulttemplate/subjects/label/slug).
  • log: log.Printf/log.Fatalf (no log.Errorf).
  • golang stdlib net/http: http.Handle(pattern, handler), http.HandleFunc, http.ListenAndServe(":8080", nil), r.URL.Path, r.PathValue("slug"). Patterns: /subjects/{slug}.
  • All source files package main; go.mod module day.

Content model

content/ tree: config/site.yaml, subjects/<slug>/_subject.yaml + *.md, pages/*.md. Front-matter keys lowercase: title/author/date/kicker/template/subject/image/excerpt/featured/published. Shortcodes {{media:name}}→KONTRA_MEDIA base + /name; {{embed:vimeo:ID}}→iframe. Resolved on RAW markdown BEFORE goldmark so links work.

Admin

Decap CMS at app/src/web/admin/ (index.html + config.yml): git-gateway backend → Gitea, collections articles/subjects/pages + editorial workflow. Served at /web/admin/.

Verified routes (local smoke test, PORT=8691)

/, /articles/{slug}, /subjects/{slug}, /web/assets/kontra.css, /web/admin/ → all 200. 404 for missing article/subject.

Git

Committed to local repo (no remote yet): commit 31e9a70. .pi/ memory vault is inside the repo too.

Not done yet (task 7)

Gitea remotes (.35), Obsidian vault, .13 systemd service + 30s git pull timer, Caddy routes (kontra.domain + admin), Decap git-gateway OAuth app.

Evidence

  • Confidence: 100%
  • Reason: Manual memctx_save