demarkus-library — Roadmap & Resume
Pick-up-where-we-left-off doc. Authoritative plan is
/plans/universe-library.md; the Reading Room
redesign lives in /plans/reading-room.md
(invariants locked in ADR 0005).
Build model: Opus 4.8 default, Fable 5 for load-bearing design.
Where we are (2026-06-13 — Reading Room fully LIVE in prod, R3 deployed)
main HEAD e8a136b (library). The whole Reading Room is deployed and
working in prod: R1 (Tufte pane) → R2 (trail engine) → the floor (pane zero)
→ R3 (previews, backlinks, graph pane), both transports (decision 16).
Cluster versions: library 0.1.7, broker 0.1.36, worlds server 0.17.18 —
ArgoCD Synced/Healthy (R3 deployed via PR #17 → v0.1.7, rev 0.1.7 confirmed on
the running pod). Next: floor enrichment / Phase 2. Detail in
journal 2026-06-13 +
journal 2026-06-12.
- Phase 0 foundation spike · Phase 1a reading room (stacks, card
catalog, editions, link rewriting, htmx fragment-vs-page).
- Phase 1b — library card: DONE. Broker confidential web clients (demarkus
117e2a6) · oauth client (PKCE S256, Basic auth §2.3.1 escaping) ·
server-side sessions + single-flight refresh · ctx threading on ports ·
broker MCP WorldGateway with per-reader pooled sessions (1 request per
read; fixed the live 429s) · login turnstile · composition-root swap
DEMARKUS_TRANSPORT=quic|broker · native TLS for local e2e (env at
~/.config/demarkus-library/dev.env, client id library-web-dev —
dedicated dev broker client; prod is library-web). E2e verified live.
- Multi-world floor + federation (fc4277c): (world, path) addressing,
/w/<world>/... routes, federated composite gateway — KS names via broker,
host[:port] via direct QUIC (DEMARKUS_FEDERATION default true). Read token
never leaves home host (test-pinned). Cross-world mark:// links clickable.
- Deploy + CI/release — DONE (PR #10, v0.1.0): goreleaser-pro, distroless
Dockerfile, helm chart (replicaCount 1 + Recreate — in-memory sessions),
hardened workflows. Module paths on latebit-io. Releases auto-cut on
merge to main (PaulHatch per-module semver). Deploy = ArgoCD watching
latebit-io/demarkus-knowledge-system-deploy; image+chart pins in
apps/<app>/applicationset.yaml, world list + webClients in
deployment.yaml.
- Rendering — DONE (PRs #11/#12): chroma classes-mode · GFM alerts ·
emoji · mermaid + KaTeX islands (lazy, degrade to source).
- R1 — Tufte pane: MERGED (PR #13), LIVE. 60ch column + 250px margin ·
pinned margin trust signals (status badge: metadata
status: axis →
frontmatter fallback → draft; tags; provenance; parsed-frontmatter
properties; mark:// + raw-source escape) · sidenote goldmark extension
(footnote→margin) · lookup-backed tag pages · /w/:world/raw/* · search
box removed · authoring contract published
(mark://soul.demarkus.io/.well-known/library/authoring.md).
- R2 — trail engine: MERGED (PR #14), LIVE (plans §"R2 build decisions"):
- Trail URL
/t/<pane>/~/<pane>?focus=i; ~ = separator; pure codec +
click algebra (truncate/append, dedup→focus, spine→focus). Cap 10, raw
slashes. Pane kinds: <world>/d/<path> (doc; trailing slash/empty =
listing), <world>/tags/<tag>, bare u = floor.
- Rendered-document cache (decision 9): port.DocumentCache + LRU.
Focused-live: every click costs exactly 1 world read.
- Canvas: spines / body-only parent / focused Tufte pane; tombstone spine
for unreadable unfocused panes; trailizeLinks rewrites /w/ hrefs to
post-click trail URLs + active-link breadcrumb. Spec published
(
.well-known/library/trails.md).
- Codec note:
paneAddrFromParts(world,kind,value) (trail.go) is the
single source of truth for pane semantics; parsePaneChunk (/t/) and
paneAddrFromRoute (/w/) both call it (PR #16). Share semantics, not
syntax — don't re-duplicate.
- The floor — universe view (pane zero): MERGED (PR #15), LIVE (plans
§"Universe-view research" + 2 addenda):
- demarkus prereqs:
mark_worlds broker tool (#182) lists authorized
worlds; LOOKUP match-all * (b8aedb7) returns the whole catalog in
importance order. World must run server ≥ 0.17.18 or it tombstones.
WorldGateway.Worlds(): broker parses mark_worlds; QUIC returns the home
world; federated prefers Names→Hosts.
service.Floor/FloorCached: worlds × Lookup("*"); failed world →
tombstone (dimmed, never dropped); ErrUnauthorized → re-login.
- Web: pane kind
u; / → /t/u; SSR SVG floorSVG (worlds as circles
r∝satellite count, top-10 docs on orbit r∝importance, stroke=status,
every node a plain ). ADR 0003's canvas island stays UNSPENT.
- PR #16 fix: parser rejected the world-root listing chunk (
<world>/d/);
fixed + consolidated the two parsers.
- R3 — previews + backlinks + graph pane: MERGED (PR #17),
v0.1.7, LIVE
(plans §"R3 build decisions"; verified live in QUIC):
- Edge source = render-time observed-links map (the load-bearing
decision).
rewriteLinks already resolves every link to (world,path);
capturing edges is free and library-internal → transport-symmetric
(no broker graph store, so decision 16 holds). Rejected: broker store
(broker-only), library-side crawl (over-built). Cold state fills as the
room is read; durable hub-sourced topology (addendum 2) is the upgrade
layered in front later — the service edge methods are the seam.
- Architecture:
domain.Ref + domain.Neighborhood; edge store is an
internal service.linkGraph (fwd+reverse maps, mirrors floorCache — no
new port/adapter/wiring). Inbound port gains
RecordLinks/Backlinks/Neighborhood. rewriteLinks returns
(html, []Ref), rewriteHref returns (route, Ref, isDoc); web
resolves+records, core owns the store.
- Hover previews:
previewize wraps in-app doc anchors with a
preview-host/preview-card popover + htmx (mouseenter delay:300ms once →
/w/:world/preview/* fragment). Pure htmx+CSS, no JS. One component
for body links AND backlinks. Snippet = first paragraph, from
ReadCached (budget-safe). mouseenter overrides click so hx-boost nav
still fires.
- Backlinks "referenced by" block in shared doc-meta (trail URLs on the
canvas, /w/ permalinks single-doc). Empty ⇒ absent.
- Graph pane: kind
<world>/g/<path> via shared paneAddrFromParts;
route /w/:world/g/*; SSR SVG graph.go (center + backlinks left /
outbound right, each node continues the trail). Canvas island still
unspent. Margin "graph" affordance opens it.
docs/trail-format.md + in-universe trails.md (v3) updated with the g
kind (decision 14).
- Repo
pre-commit.sh added (from demarkus, single-module): fmt/vet →
golangci-lint (pinned to CI v2.9.0) → build → test. Run before
wrapping a phase/task.
- Broker-mode R3 smoke still pending (interactive OAuth); parity is
architectural (edge source is library-internal, no broker-only path).
How to resume
cd ~/latebit/demarkus-library && ./pre-commit.sh # fmt/vet/lint/build/test
go build -o /tmp/dl ./cmd/demarkus-library
# quic mode (no auth): open /t/u (floor), a doc, hover a link, click "graph".
PORT=8080 DEMARKUS_INSECURE=true DEMARKUS_HOST=soul.demarkus.io \
DEMARKUS_AUTH=<token from .mcp.json> /tmp/dl
# broker mode: source ~/.config/demarkus-library/dev.env && /tmp/dl
# OAuth on broker.knowledge.demarkus.io. Port held? lsof -ti :8443.
# cluster (GKE): kubectl ctx gke_knowledge-49722_...; creds expire mid-session
# → ! gcloud auth login. ArgoCD ns argocd; check operationState, not just
# sync.status (stale on a retrying op).
File map (hexagonal — ADR 0002)
internal/core/{domain,port,service} — entities (Document, Floor,
WorldInfo, Ref, Neighborhood), ports; ReadingService =
Read/Browse/History/Search/Tag/Raw + *Cached + Floor/FloorCached +
RecordLinks/Backlinks/Neighborhood; WorldGateway adds Worlds();
DocumentCache; service/floor.go (catalog parse), service/graph.go
(linkGraph observed-links store)
internal/adapter/inbound/web — Echo routes (/t/* canvas incl. u/g,
/w/:world/... incl. tags/raw/g/preview), trail.go (codec + click
algebra + paneAddrFromParts, incl. g), trail_links.go, links.go
(rewriteLinks → edges), trail_handlers.go (+ floorPaneView + graphPaneView),
floor.go + graph.go (SSR SVG), preview.go (cards + previewize +
backlinkLinks), templates (+ preview/preview-link), static/
internal/adapter/outbound/{world,broker,federated,oauth,markdown,cache} —
direct QUIC, MCP pool (Worlds parses mark_worlds), shape-router, broker
OAuth, goldmark, LRU cache
cmd/demarkus-library — composition root, AppConfig, brokerAuth shim
docs/ — authoring-contract.md + trail-format.md (sources of the
in-universe /.well-known/library/ docs; keep both in sync)
pre-commit.sh — the phase/task gate (golangci-lint pinned to CI v2.9.0)
Next sessions — queued work
1. Floor enrichment (pairs with R3's edge-source seam)
Hub-sourced topology (plans addendum 2 — hubs are the primary map: durable,
versioned, transport-symmetric) layered in front of the observed-links map;
portal nodes for externally-linked hosts in quic mode; world-view zoom level.
Hub discovery convention still open (manifest self-declaration vs DEMARKUS_HUB
env — lean manifest + override). Graph-pane aggregation/cap for high-degree
hubs (a doc with ~40 outbound links crowds the right arc).
2. Phase 2 — restricted collections (per /plans/universe-library.md)
Per-world/per-path authz surfaced in the reading room. Depends on broker-side
authz semantics — re-read the plan + broker authz.go first.
Maybe / observed
- Broker-mode R3 smoke (
dev.env): hover preview, backlinks, graph pane
behind the turnstile — last unrun acceptance check.
- Deploy CI papercut:
apps-immutable-check runs the guard via
... | tee, so the pipe masks the script's exit — the check can NEVER fail
the build, only posts a PR comment. Add set -o pipefail / drop the tee.
- Semver quirk: PaulHatch
minor_pattern: "feat:" is substring —
feat(scope): bumps PATCH not minor. Fix via regex /^feat(\(.+\))?:/.
- Broker rate-limit drop (60/30 → 30/15) on
fix/broker-rate-limit-down —
confirm merged/synced.
- Visual polish by eyeball (pane widths/gaps, spine legibility, dark mode,
canvas scroll; floor at many-worlds scale + label crowding; preview-card
placement near pane edges; graph pane at high node counts).
Standing deferred
- Find + fix publishers that wrote metadata fences into bodies (R1's friendly
frontmatter reduces harm; source fix still open).
- Shared session store (or sticky sessions) → replicaCount > 1 + RollingUpdate
- PDB — the R2 doc cache, single-slot floor cache, and R3 observed-links
graph are all per-pod (fine at replicaCount 1; revisit together — per-pod
graph means backlinks differ across replicas).
- Adopt demarkus's strict
.golangci.yml (18 issues to clean first).
- Phase 3 cataloging desk (writes, CodeMirror island) · Phase 4 AI librarian
(SSE; joins the canvas as a pane, trail = its context) · Phase 5 public face.
History (condensed — details in journals + ADRs)
- 2026-06-10: Phases 0 + 1a built + merged (PRs #1–2).
- 2026-06-11: Phase 1b planned + shipped (PRs #3–#9); floor + federation;
frontmatter strip. Gotchas in debugging.md.
- 2026-06-12: v0.1.0 deploy/CI (#10) · latebit-io migration · v0.1.1/v0.1.2
rendering (#11/#12) · ADR 0005 · R1 (#13) · R2 (#14)
(journal).
- 2026-06-13: universe-view research · demarkus mark_worlds (#182) +
LOOKUP match-all (
b8aedb7) · floor merged (#15) · cluster rollout
of R1+R2+floor (server 0.17.18 / broker 0.1.36 / library → 0.1.6) · trail
root-listing fix + parser consolidation (#16) · R3 merged + deployed
(#17, v0.1.7) — observed-links edge source, hover previews, backlinks
margin, graph pane; repo pre-commit.sh added (golangci-lint pinned to CI
v2.9.0). Reading Room fully live in prod (library 0.1.7)
(journal).
Git / process notes
- Work on
feature/* / fix/* branches. Never auto-commit — Fritz
commits and opens PRs; CodeRabbit reviews via .coderabbit.yaml. Releases
auto-cut on merge.
- Run
./pre-commit.sh before wrapping a phase/task (fmt/vet/lint/build/
test; golangci-lint pinned to CI's v2.9.0).
- Deploy = ArgoCD on
latebit-io/demarkus-knowledge-system-deploy. Bump chart
targetRevision + image.tag together in apps/<app>/applicationset.yaml.
Library/broker are Deployments (clean roll); worlds are StatefulSets.
- PR scopes: domain, port, service, web, world, markdown, cmd, view, deps, ci,
broker, auth, session, oauth, federated, deploy, docker, helm.
⭐ CURRENT STATUS (2026-06-14) — supersedes "Where we are" above
The Reading Room is feature-complete and fully deployed. Cluster (all
ArgoCD Synced/Healthy): library 0.1.10, broker 0.1.37, worlds server 0.17.19,
agent 0.12.43.
Shipped + verified since the stale section above:
- R3 (PR #17, library 0.1.7): hover previews, backlinks margin, graph pane.
- Floor enrichment: hub-graph topology → cross-world edges + portal nodes.
- library hub-topology reader (0.1.8) reading
<hub>/graph.md; DEMARKUS_HUB
chart wiring (0.1.9); host↔name join via broker mark_worlds address
column (broker 0.1.37 + library 0.1.10).
- agent publishes
/graph.md (--publish-graph, client 0.12.42/43).
- server token-ownership fix (0.17.19): token Secrets created by a
pre-install bootstrap Job, not chart-templated (ArgoCD was pruning/
corrupting the runtime-mutated tokens.toml — see debugging.md).
- External federation verified: floor portals to public worlds reachable
from the cluster (
docs.demarkus.io cloud + soul.demarkus.io once its
residential ingress was opened). docs in debugging.md.
Full detail: journal 2026-06-13
(latest entries run into 2026-06-14) + debugging.md.
Next phases (queued)
- World-view zoom — deferred floor-enrichment piece: one world's catalog
as dir clusters + aggregate bubbles → listing panes (plan §Universe-view
research, zoom level 2).
- Phase 2 — restricted collections — per-world/path authz in the room
(re-read /plans/universe-library.md + broker authz.go).
- Standing deferred — shared session store → replicaCount>1 (R2 cache,
floor cache, R3 link graph all per-pod); strict
.golangci.yml; deploy CI
papercuts (apps-immutable-check tee-masks exit; PaulHatch feat(scope):
bumps patch not minor).
Loose end
Demo links in world-a/index.md (mark://root/index.md + mark:// soul.demarkus.io/index.md) added to exercise floor edges/portals — keep as a
live example or strip (then restart the agent to re-publish graph.md).
Process
feature/* branches; Fritz commits, never the agent; ./pre-commit.sh
gate (golangci-lint pinned CI v2.9.0); CodeRabbit per PR; releases auto-cut on
merge; deploy = bump apps/<app>/applicationset.yaml targetRevision+image.tag
(ArgoCD). knowledge MCP joined for root/world-a writes.
Update (2026-06-14, later) — world-view zoom SHIPPED
Cluster now library 0.1.11 (others unchanged). Next-phases item 1
(world-view zoom) is DONE + deployed: PR #21, reader-only, floor world node
zooms to <world>/u/ (catalog as dir clusters + "+N more" → listing panes) +
doc-margin "map" affordance. In-universe trails.md → v4 with the <world>/u/
chunk. Loose end resolved: demo links in world-a/index.md KEPT (legit
related-links footer; floor edges stay live). Remaining queued phases: Phase 2
(restricted collections) and standing deferred. Only open item on this phase:
human browser smoke of the world map at library.knowledge.demarkus.io.
Phase 2 plan recorded (2026-06-14)
Detailed Phase 2 plan written:
/plans/phase-2-restricted-collections.md
— sub-world read-authz tier (collections), broker-led, library projects.
Settles D1–D4 (rules in broker WorldConfig; thread read identity; per-collection
locked-vs-hidden; path globs) with recommendations; broker/library work split,
acceptance, build order. Important, NOT urgent — scheduled later. Authz today is
world-level only (reads tokenless/open within an allowed world), so this is a
real broker security change, not just a library projection.
Phase 3 plan recorded (2026-06-14) — flagged important
Detailed Phase 3 plan written:
/plans/phase-3-cataloging-desk.md
— the WRITE side: create/edit docs in markdown source, a projection of the
broker's mark_publish/mark_append protocol. Settles D1–D5 (htmx textarea +
live server preview first, CodeMirror island only on escalation; editing is a
focused-pane mode NOT a trail chunk; metadata as separate form fields — no ---
fence; expected_version + merge flow; write authz coupled with Phase 2).
First write path through the library (ends the read-only invariant). Library is
the bulk; broker work only if write-collections (D5) are in scope. Fritz: Phase
3 is important — likely ahead of Phase 2 in priority.
Phase 3 STARTED — edit slice live (2026-06-14)
Cluster now library 0.1.12. Phase 3 (cataloging desk) is underway: the first
write slice — edit an existing document — is merged (PR #22) + deployed
(ArgoCD Synced/Healthy). The room is now reader+writer. Margin "edit" → htmx
textarea + live server preview → publish via mark_publish (metadata out of band,
expected_version conflict guard) → 303 back to the doc. Auth-gated; no
broker/worlds/agent change. Plan: /plans/phase-3-cataloging-desk.md. Remaining
Phase 3 slices: create-new document, mark_append affordance, conflict
merge-candidate flow, write-collections authz (couples with Phase 2). Open:
human browser write-smoke at library.knowledge.demarkus.io.
Next session candidate — R4 reader mode (2026-06-14)
Fritz wants a reader mode: a focus overlay that centers the document you
want to read over the dimmed trail (context kept behind, one click/Back to
return). Plan: /plans/reader-mode.md.
It's a lens over the trail (NOT a new pane), URL-state (?reader=<i>, ADR 0003
shareable + Back-closes + no client state), web-layer only — no port/service/
read/broker/deploy change. Settle before building: persist-on-navigate (recommend
yes), Esc-to-close, margin shown/collapsible. Phase 3 (cataloging desk: edit +
create + append) is live at library 0.1.14 — a clean pause point; reader mode is
the next nice-to-have. Remaining Phase 3 slices (world-map "new", conflict merge,
write-collections authz) still queued but lower priority.
R4 reader mode BUILT (2026-06-15, branch feature/reader-mode)
The focus overlay is built + locally verified (NOT yet merged). Web-only lens
over the trail: ?reader=<paneIndex> renders the focused document centered
over the dimmed canvas (context preserved behind), ✕/backdrop/Esc close to the
bare trail. Decisions settled (Fritz, all matched recommendations): persist-on-
navigate, standalone ?reader=, ✕+backdrop+Esc (vanilla in islands.js, not
_hyperscript), margin shown. Load-bearing: a valid ?reader= also takes Focus,
so the overlay reuses the focused pane's already-fetched doc (no 2nd read; ADR
0005 d9 holds). No port/service/domain/broker/deploy change. Gate green; live
QUIC smoke passed. Detail: plans/reader-mode.md
SETTLED+BUILT appendix + journal 2026-06-15.
Next: Fritz + CodeRabbit → merge (library 0.1.15) → deploy bump (reader-only).
R4 reader mode COMPLETE — deployed + lens fix (2026-06-15)
Cluster now library 0.1.16. R4 shipped in two PRs:
- PR #25 (0.1.15) — reader overlay built (
?reader=<paneIndex>); merged with
a CodeRabbit DRY fix (trailBasePath extracted).
- PR #26 (0.1.16) — lens-not-navigation fix. The original "reader implies
focus" decision was wrong: opening reader on a non-focused pane re-focused it,
collapsing the canvas behind. Fixed by decoupling reader from focus — the
overlay reuses the reader pane's already-fetched doc (every pane is read in
the loop; cached if unfocused),
trailReaderURL preserves a non-default
focus, close keeps it. The overlay is now a pure lens; the canvas behind never
relayouts. Both reader-only deploys (no broker/worlds/agent change). ArgoCD
Synced/Healthy on 0.1.16; Fritz confirmed live. Detail: journal 2026-06-15.
Remaining queued: Phase 2 (restricted collections) + Phase 3 leftover slices
(world-map "new" for empty worlds, conflict merge-candidate flow,
write-collections authz) + standing deferred.
Update (2026-06-18) — Phase 3 leftover slices SHIPPED + strict golangci; releases out to v0.4.0
All merged to main; releases auto-cut. Cluster currently runs library
0.3.0 (apps/demarkus-library/applicationset.yaml targetRevision+tag 0.3.0;
broker 0.2.0, worlds 0.17.19, agent 0.12.43) — so 0.3.1 and 0.4.0 are released
but NOT yet deployed (bump the applicationset to roll them).
Releases since 0.1.16 (intermediate fixes: 0.1.17 floor TTL-cache #27 · 0.1.18
no-store #28 · 0.1.19 empty-floor zero-world #29 · 0.1.20 signed-in email nav
#30 · 0.1.21 markdown H1-title + ci #31/#32):
- World-map "new" affordance — SHIPPED (PR #33,
v0.2.0, d2ad556):
create-doc entry from the floor for empty/unreadable worlds; degrade on
unreadable catalog. Clears the queued Phase 3 "world-map new" slice.
Deployed (cluster ≥ 0.3.0 supersedes).
- Conflict merge-candidate flow — SHIPPED (PR #34,
v0.3.0, 633a9ab):
cataloging-desk write path reviews the broker's mark_publish merged
candidate (on_conflict=merge) before republish. Clears the queued Phase 3
"conflict merge-candidate flow" slice. Deployed — current cluster (0.3.0).
- Strict
.golangci.yml — SHIPPED (PR #35, v0.3.1, 895aac1/ac13ca0):
adopted demarkus's strict config (revive/gocritic/modernize/…), findings
fixed. Clears the standing-deferred golangci item. Released, not yet
deployed.
Phase 3 remaining: write-collections authz only (couples with Phase 2).
Also shipped, NOT a tracked phase — login knowledge-graph backdrop (PR #36,
v0.4.0, be8fdc0): login.html redesigned to a B&W dark theme with a faint
full-page SVG knowledge-graph backdrop (jittered-grid network, degree-sized hub
nodes, center-fade opacity for text legibility; decorative, aria-hidden; ADR
0003 no-JS form behavior unchanged). Incidental visual polish — see journal
2026-06-18. Released, not yet deployed.
Deploy action queued: bump apps/demarkus-library/applicationset.yaml
targetRevision+image.tag 0.3.0 → 0.4.0 to roll golangci + login backdrop.
Still next (unchanged)
- Phase 2 — restricted collections (
/plans/phase-2-restricted-collections.md)
— couples with the last Phase 3 slice (write-collections authz).
- Standing deferred — shared session store → replicaCount>1 (R2/floor/R3
caches per-pod); deploy CI papercuts (apps-immutable-check tee-masks exit;
PaulHatch
feat(scope): bumps PATCH not MINOR); publishers writing ---
metadata fences into bodies.
- Phase 4 AI librarian (SSE pane) · Phase 5 public face.
Decision (2026-06-18) — write-collections authz (Phase 3 D5) DROPPED
Fritz: not important. World-level write access is acceptable — if an
identity may write to a world, it may write to ANY path in that world. The
coarse grant (world AllowConfig + the broker's per-world write token, op
["publish"], path globs) is the intended model, not a stopgap.
Consequences:
- Phase 3 (cataloging desk) is COMPLETE. Edit (#22) · create (#23) ·
append (#24) · world-map "new" (#33) · conflict-merge (#34) all shipped; D5
was the only open slice and it's now a non-goal. No further write-authz work.
- The library keeps gating the edit/new affordance on writability and degrading
to "not permitted" on a broker reject — that's just honoring the world-level
token, no per-path tier.
- Decouples Phase 2 from Phase 3. Phase 2 (restricted READ collections)
stands on its own if/when wanted; it no longer has a write-side twin to
co-design. Phase 3 plan D5 + its "coordinate with Phase 2" coupling are void.
Phase 3 plan (/plans/phase-3-cataloging-desk.md) D5 should be read as
superseded by this note.
Phase 5 anonymous-read question SETTLED — plan recorded (2026-06-18)
Plan: /plans/phase-5-public-face.md
— settles the universe-library open question ("does the broker expose any
anonymous-readable surface"). Recommendation: yes, but a narrow, default-closed,
operator+author opt-in public carve-out — NOT a relaxation of the org-scoped
default.
Load-bearing finding (grounded in broker ratelimit.go/mcp_tools_read.go):
the world read layer is already identity-blind — reads dispatch with an empty
bearer (tokens.toml is write-only), so requireAuth at the broker edge is the
only thing making org reads private. Anonymous-public is therefore a narrow
fail-closed bypass for explicitly-public resources, the inverse end of Phase
2's read-authz axis — build them on one model or a public world × restricted
subtree will leak.
Decisions: D1★ tier yes/default-closed · D2 operator-eligible ∧ author-marked
(visibility:public) · D3 one evaluator shared with Phase 2 (public: true rule
flag) · D4 dedicated fail-closed anonymous read path (not requireAuth-bypassed
gateway) · D5 follow-a-link only, NO anonymous catalog/list/worlds in v1 · D6
session-less library render + OG + bluemonday · D7 ipRateLimit + cacheable +
residential worlds stay behind the broker. Settle D1/D2 with Fritz before
building. Cross-repo (broker leads). Prefer Phase 2 first or joint.
Direction (2026-06-18) — DEFER forward phases; focus usage + robustness + code quality
Fritz: pause net-new feature phases. The reading room is feature-complete and
deployed; next priorities are (1) get real usage, (2) make the system robust,
(3) raise architecture/code quality before continuing the phase ladder.
Deferred (plans recorded, not scheduled): Phase 2 restricted collections ·
Phase 5 public face / anonymous read · Phase 4 AI librarian. Write-collections
authz already dropped (world-level write is fine). These resume after the
robustness bar is met.
Now (robustness / quality agenda — pick up here):
- Observability for usage — without metrics/structured logs/error tracking
there's no "usage" signal and no way to know it's robust. Likely prerequisite
to everything else.
- Resilience / no SPOF — the standing-deferred shared session store →
replicaCount>1 (in-memory sessions + per-pod R2 doc cache / floor cache / R3
link graph are all single-pod today; HA needs them shared or sticky). This is
the biggest reliability gap.
- Architecture + code-quality audit — verify hexagonal adherence (ADR 0002),
ADR conformance, error-handling/degradation paths, test coverage gaps, the
deploy CI papercuts (apps-immutable-check tee-masks exit; PaulHatch
feat(scope): bumps PATCH not MINOR).
Starting point under discussion: an architecture + code-quality audit to set the
bar, paired with observability so usage is measurable.
Architecture + code-quality audit DONE (2026-06-18)
Full report: /demarkus-library/audit-2026-06-18.md
(5 parallel agents: architecture, concurrency, error-handling, security, tests/CI).
Verdict: strong foundation, clear hardening gap. Hexagon clean (core imports
zero adapters — verified), go vet/go test -race clean, XSS closed, OAuth/PKCE/
session correct, .mcp.json git-ignored (NOT a committed secret — earlier worry
void), coverage high (service 93 / web 90 / markdown 92.6 / broker 87.3%). Work is
production-hardening + HA, not rearchitecting.
Robustness backlog (detail in the audit), do in order:
- Tier 1 hardening (small, first): CSRF on POST routes (CRITICAL — none
today, only SameSite=Lax) · HTTP server timeouts + ctx into QUIC fetch · body-size
limit on writes · post-turnstile
ErrUnauthorized→re-login (dead-ends on a 401
today).
- Tier 2 leaks/staleness: inflight lock leak (
manager.go:139) · worldMapCache
has no TTL/eviction (永 stale) · cache invalidation on write · linkGraph unbounded
· broker errors matched by substring (typed status instead) + tests.
- Tier 3 arch (optional): split
ReadingService god-interface (port.go) into
Reader/Graph/Map/Editor · move listing-vs-doc rule into a core Open dispatch.
- Tier 4 HA (big, when scaling >1 replica): shared session + pending store
(Store iface is the seam) · shared linkGraph · cross-pod cache invalidation.
- Quick-win tests: mapWriteError branches · Append tool-error · config fail-fast.
Standing-deferred CI papercuts RESOLVED/MOOT: the apps-immutable-check
tee-mask is NOT in this repo (likely the broker repo); the PaulHatch feat(scope):
semver bug is already fixed (release.yml:73 anchored regex). Removed from the
library's deferred list.
Recommended start: Tier 1 — small, high-value, directly serves "robust enough
for real usage." CSRF first.
Tier 1 hardening BUILT (2026-06-18) — branch feature/tier1-hardening
All four Tier-1 items done, gate-green (0 lint, build, all tests + 6 new), NOT
merged (Fritz commits). Detail: journal 2026-06-18.
- CSRF (the CRITICAL gap) — Echo v5 CSRF (Sec-Fetch-Site + token fallback),
web.CSRFMiddleware, {{ csrf }} renderer injection, tokens in all forms +
htmx preview header. Tested.
- HTTP timeouts — ReadHeaderTimeout/IdleTimeout via BeforeServeFunc (both
plain + TLS), ContextTimeout 30s handler bound. WriteTimeout left unset (future
SSE).
- Body limit — 2 MiB on write POSTs.
- Post-turnstile re-login — presentError ErrUnauthorized clears the session +
re-logins (HX-Redirect for htmx); QUIC mode keeps plain 401. Tested.
Deferred follow-up: thread ctx into the QUIC fetch adapter (world.go ignores
it) — ContextTimeout bounds the response but won't abort the QUIC goroutine.
Next: Tier 2 (leaks/staleness — inflight lock leak, worldMapCache TTL, cache
invalidation on write, linkGraph cap, typed broker errors) then Tier 3 / Tier 4.
Tier 2 hardening BUILT (2026-06-18) — branch feature/tier2-hardening
Leaks/staleness/eviction + the quick-win tests. Gate-green, race-clean, NOT
merged. Branched off main (independent of the Tier 1 PR #37; no file overlap).
- inflight lock leak —
manager.go dead-grant path now dropLocks (was
leaking a mutex per revoked-token session). Test asserts no leak.
- worldMapCache TTL + invalidate — per-world timestamped entries; mirror
floorTTL; WorldMapCached rebuilds when stale (was serving the first-ever
assembly forever).
- cache invalidation on write —
Publish/Append now invalidateTopology
(floor + world map) so an edit shows at once, not at TTL; a merge candidate
(nothing written) leaves caches intact.
- linkGraph bounded —
maxLinkGraphSources 5000 with LRU-by-last-observed
eviction (was unbounded for a long-lived pod).
- broker error tests —
mapWriteError/mapToolError branch coverage +
Append tool-error path (the substring-brittleness the audit flagged). NOTE:
the typed-status broker change stays a cross-repo follow-up; library-side this
is test coverage, not a behavior change.
- config fail-fast tests —
getEnvAsBoolStrict/getEnvAsDuration error
paths + NewAppConfig rejects a bad strict switch (was 0% covered).
Remaining: Tier 3 (split ReadingService god-interface; core Open dispatch
for listing-vs-doc) — optional; Tier 4 (HA: shared session/pending store,
shared linkGraph, cross-pod invalidation) — the big one, when scaling >1 replica.
Tier 1 + Tier 2 MERGED; Tier 3 BUILT (2026-06-18)
- PR #37 MERGED — Tier 1 (CSRF, timeouts, body limit, re-login) + Tier 2
(inflight leak, worldMap TTL, write-invalidation, linkGraph bound, broker/config
tests) + the CodeRabbit fixes, all folded into one PR, now on
main.
- Tier 3 BUILT — branch
feature/tier3-interface-split, gate-green, NOT
merged. The two architecture refinements:
- Split the
ReadingService god-interface (port.go) into Reader /
GraphService / MapService / Editor, composed as ReadingService. The
concrete *service.ReadingService satisfies all four; a narrower consumer
(preview-only handler, Phase 4 librarian) can depend on one slice. Pure
organization — no behavior change; the web handler still uses the composite.
- Core
Open/OpenCached own the listing-vs-document read dispatch; the
rule itself is domain.IsListingPath (single definition). Doc + readPane
call Open/OpenCached instead of re-deciding by trailing slash; the
margin/edge-source presentation checks route through domain.IsListingPath
too, so the rule lives in exactly one place.
Robustness push status: Tiers 1–3 done (T1/T2 merged, T3 in review). Only
Tier 4 (HA / replicaCount>1) remains — the big one: shared session + pending
store (the Store interface is the seam), shared linkGraph, cross-pod cache
invalidation. Do it when actually scaling past one replica; single-replica +
Recreate is correct until then.
Deployed — library 0.5.1 LIVE (2026-06-19)
Cluster bumped 0.3.0 → 0.5.1 (deploy repo apps/demarkus-library/applicationset.yaml
targetRevision + image.tag, branch bump/demarkus-library-0.5.1, merged).
ArgoCD verified: app Synced, revision=0.5.1, operationState phase=Succeeded
("all tasks run"); pod ...c5cfcb554-gxg74 running image :0.5.1, ready.
Single replica (Recreate), as intended pre-HA.
Now live: golangci (0.3.1) · login knowledge-graph backdrop (0.4.0) · Tier 1+2
robustness hardening (0.5.0) · Tier 3 interface split + Open dispatch (0.5.1).
Reader-only deploy — broker 0.2.0 / worlds 0.17.19 / agent 0.12.43 unchanged.
Robustness push complete + deployed (Tiers 1–3). Tier 4 (HA) still deferred until
1 replica is needed. Next per direction: get usage → observability.
Deployed — library 0.5.2 LIVE (2026-06-19)
Cluster 0.5.1 → 0.5.2 (sign-in page restyled to the reading room's Tufte
palette — paper+ink, Charter serif, light-dark() adaptive; knowledge-graph
backdrop recolored to faint ink; CSS only). ArgoCD verified: Synced, rev 0.5.2,
operationState Succeeded, Healthy; pod ...78c68d5c7f-cl2gn on image :0.5.2,
ready. Reader-only (no broker/worlds/agent change), single replica.
Gotcha: after merging the deploy bump, the ApplicationSet git-generator hadn't
regenerated the Application yet (live target still 0.5.1) — a hard refresh
(kubectl annotate ... argocd.argoproj.io/refresh=hard) made it pick up 0.5.2
immediately instead of waiting for the ~3min appset requeue.
Universe overlay MERGED (2026-06-22) — PR #47
The universe floor now has the on-demand overlay treatment (parity with the §4
graph and §5 world-map overlays). The floor's "view as map" link summons a
full-viewport lens (/u?overlay=1 → bare floorSVG fragment, lazy htmx into
#universe-overlay); hx-boost="false" on the trigger with a JS-off fallback to
inline ?view=map. Landing-only reach; worlds-as-cards stays the default.
Two things rode along in the same PR:
- Scalable floor layout —
floorSVG went from single unbounded rows to
centered grids (~√N landscape columns) for both systems and portals, so a lone
world centers and a large universe wraps + grows downward into the scrollable
overlay instead of sliding off one edge.
- Overlay accessibility parity — a shared
showOverlay/hideOverlay focus
helper across all three overlays (focus into panel on open, restore on close),
and role="dialog"/aria-modal/aria-labelledby on each panel (the map
overlay gained these; graph + universe aligned). Closed the CodeRabbit nitpicks
on PR #47 and the pre-existing gaps it flagged.
Decision recorded: ADR 0006 §6
(also the first written form of the umbrella ADR 0006; §0–§5 indexed there).
Web-layer only, reader-only — no broker/worlds/agent/deploy change; not yet
deployed (bump the applicationset to roll it).
Open follow-up: satellite density at very large N — each system still draws
its orbiting top-docs, so the grid gets busy per-cell at tens of worlds. Cap
satellites per world or show them only on zoom-in (same family as graph-pane
aggregation for high-degree hubs).
Deployed — library 0.13.0 LIVE (2026-06-23)
Cluster rolled to library 0.13.0 (deploy repo apps/demarkus-library/applicationset.yaml targetRevision + image.tag 0.12.0 → 0.13.0, branch bump/demarkus-library-0.13.0, merged to deploy main). ArgoCD verified: app Synced, revision=0.13.0, operationState phase=Succeeded, Healthy; pod demarkus-library-5c7b7c76f8-m8mc2 Running on image :0.13.0, ready. Single replica (Recreate), pre-HA.
Now live:
- OKF doc-meta grouping (0.13.0, PR #48) — the document margin's metadata is split into an OKF group (type, tags, timestamp — the Open Knowledge Format frontmatter fields) plus a complete metadata block that surfaces every other out-of-band catalog key (importance, etag, content-hash, publisher-custom) instead of dropping all but a curated handful. The OKF
type field is newly threaded through domain.Document and both view models (page + paneVM); otherMeta() sorts the catch-all and skips already-surfaced keys. Web/core only, reader-only deploy.
- Universe overlay (0.12.0, PR #47) — already on the cluster (pin was 0.12.0), now documented in the deploy comment trail: the universe floor's on-demand full-viewport "view as map" overlay + scalable centered-grid layout + shared focus/ARIA across the graph/map/universe overlays.
Reader-only roll — broker 0.2.0 / worlds 0.17.19 / agent 0.12.43 unchanged.
Stale-version note: the "⭐ CURRENT STATUS (2026-06-14)" and "Where we are" sections above still cite library 0.5.2 — out of date. The deployed line moved 0.5.2 → 0.6.0 (palette) → 0.7.0 (dock) → 0.8.0 (worlds-only floor + reference map) → 0.9.0 (rich index) → 0.10.x (graph/map overlays) → 0.11.0 (overlay usability) → 0.12.0 (universe overlay) → 0.13.0 (OKF doc-meta). Treat this entry as the current deployed status.
Deployed — floor portal cleanup: library 0.13.2 + agent 0.13.1 LIVE (2026-06-23)
Fixed the universe floor showing phantom/duplicate federated portals
(127.0.0.1:6401-6403, localhost, localhost:6309, plus soul.demarkus.io
beside soul.demarkus.io:6309 etc., and a second root portal). Root cause: the
durable hub graph (root/graph.md, published by the federation agent) had
accumulated dev-crawl link targets and addressed hosts in mixed port-less/ported
forms; the floor renders every distinct host as a portal.
Shipped across two repos + three releases, all deployed (ArgoCD Synced/Succeeded/
Healthy):
- demarkus-agent 0.13.1 (demarkus
fix/fedcrawl-filter-loopback-portals, PR
#209) — recordEdges now normalizes each edge target's host via ParseMarkURL
(port-stable) and drops loopback/localhost/unspecified targets; discoverServers
skips queuing them. Narrow filter by design — private + cluster-internal hosts
are KEPT (real federated/cluster worlds use those). CodeRabbit round: hardened
isLoopbackHost for unbracketed IPv6+port (::1:6309) + IPv6 test coverage.
Cleans graph.md at the source on its next crawl (confirmed: 0 loopback
mentions, port variants collapsed after the re-crawl).
- demarkus-library 0.13.1 (
fix/floor-filter-loopback-portals) — defense-in-
depth portalLabel/isLocalHost in service/hub.go: collapses port-variant
portals (hostKey-canonical, default port elided) and drops loopback/unspecified/
link-local/localhost + unmatched cluster-internal portals. Private IPs KEPT (LAN
federation). Makes the floor clean regardless of a dirty graph.
- demarkus-library 0.13.2 (
fix/floor-match-world-by-name-port) — hostName
now joins an authorized world referenced by name+port (mark://root:6309, the
port-normalized form of a curated mark://root/... link from
world-a/index.md) to the real world, instead of doubling it as its own root
portal. This second root was a regression introduced by the agent's
port-normalization (before, the port-less mark://root matched the authorized
name directly). Render-time join — applies immediately, no re-crawl needed.
Result on the live floor: worlds root + world-a; real external portals
soul.demarkus.io, docs.demarkus.io, hub.demarkus.io only. No loopback/local
clutter, no port-variant or name+port duplicates.
Cluster now: library 0.13.2 / agent 0.13.1 (broker / worlds / etc. unchanged).
Gotcha (reusable): "world identity" in the hub graph has THREE forms that must
all join to one authorized world — the name (root), the dial address
(root.root.svc.cluster.local:6309, from mark_worlds), and a name+port
(root:6309, from a port-normalized name link). host2name only covers the dial
address; authorized[name] only the bare name; the name+port case needs an
explicit port-strip-and-recheck. A world that is the hub is never crawled (not
seeded), so it appears in graph.md ONLY via inbound name links — making the
name+port form its only representation there.
Phase 4 plan recorded — AI librarian NEXT (2026-07-02)
Fritz: work on the AI librarian now, overriding the 06-18 defer direction.
Detailed plan written:
/plans/phase-4-ai-librarian.md
— embedded assistant as a canvas pane (kind a), streamed answer + visible
tool-call trace over SSE (ADR 0003's earmarked island), no-JS full-page
degrade. Load-bearing D1 supersedes the universe-library sketch: tools are
the library's own Reader/Graph/Map ports (transport-symmetric, reader-identity
via ctx, read-only — no Editor slice), NOT broker MCP. Loop in
service/librarian.go behind a new outbound port.Assistant; adapter =
official Go SDK, manual loop, claude-opus-4-8 + adaptive thinking + prompt
caching; key via env/Secret, feature dark when unset; caps day one. Settle the
4 open decisions (presentation, model tier, trail-context depth, trace
verbosity) with Fritz, then build order starts with the SSE spike.
Amendment (same day): Phase 4 adopts latebit-io/nib (nib/agent +
nib/ai/llm) instead of a hand-built loop/port — supersedes the plan's D2/D3;
multi-provider (Anthropic + OpenAI-compatible/local) via nib's llmconfig. See
the plan's addendum.
Phase 4 — AI librarian: steps 1–3 MERGED (2026-07-03, PR #51 → v0.14.0)
The reading room is now reader+writer+asker. Everything through the UX
pass is on main; release v0.14.0 auto-cut. NOT yet deployed.
Shipped: nib-backed librarian (internal/adapter/librarian — per-conversation
nib Agent, MaxTurns 15, LRU conversation store, 4 read-only port tools) ·
port.Librarian + domain events · pane kind a (transcript through the
document pipeline; citations are trail-continuing links) · CSRF'd ask form
(htmx SSE live exchange via one-shot ask tokens — the question never rides a
GET URL; no-JS runs synchronously + PRG) · mark:// scheme allowed through
bluemonday (pre-existing bug: in-body cross-world links were losing hrefs) ·
nav Librarian door (trail-appending) · chat-scroll transcript · attention
marker + click engagement (ADR 0003 concessions recorded, incl. the SSE
island) · ?slow= soak kept as the transport diagnostic.
Feature-dark by design: the librarian enables only when nib llmconfig
resolves a provider (LLM_API_KEY / llm.json). Deploying v0.14.0 is safe
(dark without a key) but enabling it in the cluster still needs chart work:
an LLM-key Secret + env wiring in the helm chart — NOT done yet.
Next: deploy bump 0.13.2 → 0.14.0 (dark) or chart+Secret first (live) ·
cluster soak (/a/stream?slow=45 through the GKE ingress) · broker-mode
smoke · step 4: trail-context injection (decision open: titles + focused
body recommended) · nib packaging fix upstream (tag agent/ai modules, drop
the consumer-side replace).
Phase 4 build order COMPLETE — trail context merged (2026-07-03, PR #52 → v0.15.0)
Step 4 on main: each ask carries the reader's view ( —
every pane labeled with cached titles, focus marked, focused document's text
from the cached render, 8KB rune-safe cap). Injected via nib TransformContext
on the model transcript only — History/pane stay clean; cleared at run end.
Ask form gains hidden focus (idx = librarian pane for link algebra; focus =
whose text rides). CodeRabbit round: document-derived text is defanged
against </reader-context> wrapper-breakout (case-insensitive neutralizer).
Verified live: "what does this document decide?" answered from the focused
ADR, cited as a trail link.
All five build-order steps of the Phase 4 plan are done and merged
(v0.13.x spike → v0.14.0 pane+UX → v0.15.0 context). Remaining around the
phase, unchanged: deploy decision (dark bump 0.13.2→0.15.0 vs helm
chart + LLM-key Secret first, then one roll) · cluster soak
(/a/stream?slow=45 through GKE ingress) · broker-mode smoke · nib packaging
fix upstream (tag agent/ai modules, drop the consumer-side replace) ·
independent-pane-scroll design experiment (parked).
Deployed — library 0.15.1 LIVE, PHASE 4 COMPLETE (2026-07-03)
Fritz confirmed in prod: the librarian answers with streaming tokens
through the turnstile at library.knowledge.demarkus.io. That one interaction
closed the last three checks at once — broker-mode smoke (tool reads with the
reader's bearer), GKE-ingress SSE (progressive tokens = no buffering;
X-Accel-Buffering honored), and MiniMax-M3 live with the four tools.
Phase 4 — the AI librarian — is COMPLETE and in production. The plan's
exit criterion ("returns the doc with its catalog reasoning shown") is met:
asks stream a visible tool-call trace and answers cite documents as
trail-continuing links, grounded in the reader's own trail context. Built on
nib (agent kit + multi-provider llmconfig: fugu in dev, MiniMax-M3 in prod,
zero code difference); read-only by design; feature-dark without a key.
Cluster: library 0.15.1, broker 0.2.0, worlds 0.17.19, agent 0.13.1.
Remaining queued (unchanged priorities):
- nib packaging fix upstream (tag agent/ai modules → drop our replace pin)
- observability (the pre-Phase-4 direction: usage signal + robustness)
- Tier 4 HA (now also: conversation store is per-pod)
- independent-pane-scroll design experiment (parked)
- deferred phases: Phase 2 restricted collections · Phase 5 public face
UX round MERGED — v0.16.0 (2026-07-04); cluster still on 0.15.1
PR #55 (feature/library-ux) on main, release v0.16.0 auto-cut, CI green.
Reader-side only: hover/settle scroll fix (islands.js afterSettle gating +
horizontal-only recenter) · preview cards anchor-positioned (per-pair inline
anchor names, position:fixed escapes pane clips, flip fallbacks) · graph arc
cap 18/side with "+N more" · floor satellites scale with world count ·
doc-meta metadata fold · metadata lens (?meta=<pane>, head "meta"
affordance) · pane-scroll experiment (DEMARKUS_PANE_SCROLL, default
off: fixed-viewport room, panes scroll internally, marginless wide columns)
· librarian nib-keystore fallback (DEMARKUS_LLM_KEYSTORE, default true).
Detail + gotchas: journal 2026-07-03-ux.
Deploy action queued: bump apps/demarkus-library/applicationset.yaml
0.15.1 → 0.16.0 (safe: experiment flags dark by default; cluster env key
preempts the keystore path). Decide separately whether to set
DEMARKUS_PANE_SCROLL=true in prod once the experiment verdict lands —
if it wins, it's ADR territory (margins/dock/mobile).
Experiment verdict: it wins. Recorded as
ADR 0007; built on the
working tree (Fritz commits):
DEMARKUS_PANE_SCROLL default true — the flag is the opt-out back to
the page-scroll room, kept until that room is retired.
- Status chip in the pane head (
paneVM.HeadStatus, pane-scroll room
only): the at-rest trust signal, since the margin badge waits behind the
?meta= lens.
- Tests: config default-on/opt-out; chip presence in the lens test.
Open per ADR consequences: mobile (46ch columns pan horizontally — neither
room designed for phones) · retire page-scroll margin CSS with the flag ·
Firefox card-clip degrade until anchor positioning ships · sidenotes hidden
in-room (live in reader overlay).
Cluster rolled 0.15.1 → 0.17.0 (deploy branch bump/demarkus-library-0.17.0,
merged; appset regenerated without the hard-refresh nudge this time). ArgoCD
Synced/Healthy/Succeeded; pod on image :0.17.0, ready; librarian enabled
(MiniMax-M3 — Secret env key resolves first, nib-keystore fallback inert);
/health 200. Reader-only roll (broker 0.2.0 / worlds / agent unchanged),
single replica pre-HA.
Now live: the whole UX round (v0.16.0 — hover/settle fix, anchor-positioned
preview cards, graph/floor density caps, metadata fold + ?meta= lens) and
ADR 0007's pane-scroll room as the default (v0.17.0 — fixed-viewport
canvas, per-pane scrolling, wide marginless columns, head status chip;
DEMARKUS_PANE_SCROLL=false is the opt-out).
Remaining human check: browser walk of the room at
library.knowledge.demarkus.io (authed) — chip, meta lens, hover cards
through the turnstile.
Observability DEFERRED again (2026-07-04)
Recommended as next (blind on usage/cost; librarian spends tokens); Fritz:
defer. Design sketch agreed for when it's picked up — prom client library
only, no cluster infra: /metrics on a separate internal port (not in
Service/ingress; port-forward to read; GKE managed collection or Prometheus
scrapes it later via pod annotation, zero code change). Metric set: HTTP by
route-class/status + latency (Echo middleware) · world reads per transport
(WorldGateway decorator) · cache hit/miss (nil-safe metrics iface in
service) · librarian asks/outcomes/duration/turns (the money surface).
Rejected: expvar (conversion cost later); Grafana in-cluster (not wanted).
Where it stands (2026-08-18): cluster on 0.23.2
The living status had stopped at 2026-07-04. Catching it up, newest first. Deployed version in the knowledge system is 0.23.2; the deploy repo pins it in apps/demarkus-library/applicationset.yaml.
- 0.23.2 (#66, 2026-08-18): pinned editions are no longer edge sources. A version path renders as an ordinary document, so every edition a reader opened was recorded in the observed-links map and showed up in the margin as a bare
v1, v12, v26. domain.IsVersionPath now sits beside IsListingPath and is excluded at both renderers; an edition still renders in full. Deployed same day via deploy repo #126, which also cleared the accumulated entries because the map is per-pod and in-memory. Detail in debugging, and the design question it exposed in debt.
- 0.23.1 (#65, 2026-08-11): sign-in restored against the spec-correct broker. OAuth endpoints are discovered through the RFC 9728 chain: the gateway's PRM names the issuer, RFC 8414 is fetched there, the issuer echo is validated and an https downgrade rejected, with a single-origin fallback for older brokers. Also fixed logout revocation, which had been silently 404ing against the gateway.
- 0.23.0 (#64, 2026-07-19): operator theming and branding.
- 0.22.0 (2026-07-15): the floor reads the enriched graph again. The agent's six-column
/graph.md had broken federation edge parsing; the parser was updated and rel-typed edges now render dashed with their predicate. This is the export-format regression the floor is known for, and the reason any change to that format gets checked here first.
- 0.21.x (2026-07-05 to 07): editing and librarian polish. Side-by-side editor with a live preview pane, edit flow returning to the trail, the librarian's
open adopting the mdoutline convention, and the broker adapter forcing full-body fetches so a large document does not arrive as an outline.
Checked and needing no change: the ADR 0005 identity form (demarkus /graph.md rows dropping the default port). The floor's hostKey appends the default port before matching, and host2name is built through the same function, so both spellings collapse to one key. Verified 2026-08-18 against a full identity-form export before the agent republished.
Direction (2026-09-09): layout locked, focus polish + branding
Fritz: the room layout is right as it is. Next work is polish and branding capability, not layout or new phases. Cluster past 0.23.2 with #69 (worldmap labeled nodes), #70 (directory aggregates), #71 (htmx 4.0.0) unrecorded here.
Branding today (0.23.0, docs/theming.md): DEMARKUS_BRAND, DEMARKUS_LOGO, DEMARKUS_THEME_CSS with :root tokens; Helm ConfigMap mount; assets read at startup.
Branding v2 candidates: favicon and touch icon; OG and social meta; tagline, footer text and links; custom font files with @font-face; login turnstile background and copy; per-world branding; hot reload of assets; example theme pack.
Polish candidates: pane widths and gaps, spine legibility, dark-mode pass; floor label crowding at many-worlds scale and graph-pane cap for high-degree hubs; preview-card placement near pane edges; canvas scroll edge cases; review of worldmap nodes and directory aggregates at scale.
Suggested order: branding v2 slice first, then a screenshot-driven polish pass per room surface.
Branding v2 BUILT (2026-09-09, branch feat/branding-v2, uncommitted)
Manifest (DEMARKUS_BRANDING), per-world name/logo/css, terms.universe rename, DEMARKUS_TERM_UNIVERSE, Helm manifestKey/universeTerm. Detail in journal 2026-09-09. Still open from the branding v2 list: favicon, OG meta, footer, font files, login background, hot reload, example theme pack.
Branding v3 BUILT on feat/branding-v2 (2026-09-09)
Static overlay, in-world per-world branding, branding desk. ADR 0008 decides the layers. Branch holds v2 + v3 together by request: fewer, larger releases. Remaining from the branding list: favicon, OG meta, footer, font files, login background, example theme pack, admin claim from the broker.
Queued next: handler decomposition and signature cleanup (2026-09-09)
Plan recorded at /demarkus-library/plans/handler-decomposition.md. Pays off the structural debt the standards sweep found: ReadingHandler serving seven surfaces, and six-to-twelve parameter signatures on the ports and the SVG builders. Sequence starts with extracting the shared nav chrome, then splits the branding desk out as the test of the seam. Steps 4 and 5 (port parameter structs, builder options) take their own branch because they touch four packages and every fake.
Branding feature complete on feat/branding-v2 (2026-09-09)
Added design tokens (manifest, per world, in-world; validated names and values), a default and overridable favicon with /favicon.ico answered, and a copyable example pack under docs/branding-example/ that a test loads so it cannot drift. Branding list now clears except: Open Graph and social meta (folded into Phase 5 public face), hot reload of assets, a /theme preview page, and the broker admin claim.
Branding authorization settled: a write token is the gate (2026-09-09)
No admin claim and no email allowlist. Whoever the world lets write may brand it, which is a broker session with write scope or a configured token in QUIC mode; branding documents are ordinary documents, so the world's own write authorization is already the correct gate. Recorded as an amendment to ADR 0008 and in the in-world branding plan. The branding list now clears except Open Graph and social meta (Phase 5), hot reload of assets, a /theme preview page, and linking the desk from the world map in QUIC mode.
Branding v2+v3 MERGED (2026-09-09, PR #72 → main 2a51078)
Manifest with design tokens, universe term, static overlay, per-world file and in-world branding, branding desk, favicon, example pack, plus the repo standards (CLAUDE.md, SessionStart bootstrap, Go review rules). Review round addressed: logo validation at both boundaries with nosniff and a sandboxed CSP, variable-length fences, partial-save reporting, Helm key validation. Next: cut a release and deploy, then the queued handler decomposition.
Handler decomposition steps 1-3 MERGED (2026-09-10, PR 73 → main e5dcedf)
ReadingHandler no longer serves seven surfaces. Six handlers now, one per family (reading with the trail canvas, editing, graph and map, palette, librarian, branding), each over the slice of the port it drives; Room assembles them and RoomRoutes composes six per-family route functions. Shared nav lives in chrome.go, built once per request and embedded by each view model. Routes, URLs and the trail format untouched, verified by diffing the registration set against main.
Next on this thread: plan steps 4 and 5 (parameter structs on the write and lookup ports, options values for the SVG builders), plus the third cluster the split exposed — the web adapter's own pane builders. See the plan and debt.
Hub-world room branding + page CSP BUILT (2026-09-23, branch feat/branding-hub, uncommitted)
The hub world's /.well-known/library/ documents brand the room (name, logo, favicon, stylesheet), ahead of the files; the desk on the hub takes a favicon. Every page carries a CSP (no inline/eval script, same-origin fonts/@import/XHR); document stylesheets are checked at ingestion for other-origin references. Resolver keeps the last good brand across failed reads. ADR 0008 amended. Left: terms from documents.
Librarian polish BUILT (2026-09-26, branch feat/librarian-polish, uncommitted)
Retrieval (lookup/body match/versions/links fallback), flat-cost exchanges, stop/reset/budget/usage log, linked steps + sources, streamed render, starters, ask-about-this + a, hub persona, opt-in eval. ADR 0010. Gates green; verified live on fugu. Open: demarkus client upgrade (QUIC body match), Tier 4 HA for conversation state.