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 MCPWorldGatewaywith per-reader pooled sessions (1 request per read; fixed the live 429s) · login turnstile · composition-root swapDEMARKUS_TRANSPORT=quic|broker· native TLS for local e2e (env at~/.config/demarkus-library/dev.env, client idlibrary-web-dev— dedicated dev broker client; prod islibrary-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_FEDERATIONdefault 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 inapps/<app>/applicationset.yaml, world list + webClients indeployment.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>, bareu= 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/) andpaneAddrFromRoute(/w/) both call it (PR #16). Share semantics, not syntax — don't re-duplicate.
- Trail URL
- The floor — universe view (pane zero): MERGED (PR #15), LIVE (plans
§"Universe-view research" + 2 addenda):
- demarkus prereqs:
mark_worldsbroker 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.
- demarkus prereqs:
- 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).
rewriteLinksalready 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 internalservice.linkGraph(fwd+reverse maps, mirrors floorCache — no new port/adapter/wiring). Inbound port gainsRecordLinks/Backlinks/Neighborhood.rewriteLinksreturns(html, []Ref),rewriteHrefreturns(route, Ref, isDoc); web resolves+records, core owns the store. - Hover previews:
previewizewraps 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, fromReadCached(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 sharedpaneAddrFromParts; 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 thegkind (decision 14).- Repo
pre-commit.shadded (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).
- Edge source = render-time observed-links map (the load-bearing
decision).
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 cachecmd/demarkus-library— composition root, AppConfig, brokerAuth shimdocs/— 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-checkruns the guard via... | tee, so the pipe masks the script's exit — the check can NEVER fail the build, only posts a PR comment. Addset -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; repopre-commit.shadded (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.shbefore 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 charttargetRevision+image.tagtogether inapps/<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_HUBchart wiring (0.1.9); host↔name join via brokermark_worldsaddresscolumn (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).
- library hub-topology reader (0.1.8) reading
- External federation verified: floor portals to public worlds reachable
from the cluster (
docs.demarkus.iocloud +soul.demarkus.ioonce 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; PaulHatchfeat(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 (trailBasePathextracted). - 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),
trailReaderURLpreserves 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'smark_publishmerged 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
ReadingServicegod-interface (port.go) into Reader/Graph/Map/Editor · move listing-vs-doc rule into a coreOpendispatch. - 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.godead-grant path nowdropLocks (was leaking a mutex per revoked-token session). Test asserts no leak. - worldMapCache TTL + invalidate — per-world timestamped entries; mirror
floorTTL;WorldMapCachedrebuilds when stale (was serving the first-ever assembly forever). - cache invalidation on write —
Publish/AppendnowinvalidateTopology(floor + world map) so an edit shows at once, not at TTL; a merge candidate (nothing written) leaves caches intact. - linkGraph bounded —
maxLinkGraphSources5000 with LRU-by-last-observed eviction (was unbounded for a long-lived pod). - broker error tests —
mapWriteError/mapToolErrorbranch 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/getEnvAsDurationerror 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
ReadingServicegod-interface (port.go) intoReader/GraphService/MapService/Editor, composed asReadingService. The concrete*service.ReadingServicesatisfies 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/OpenCachedown the listing-vs-document read dispatch; the rule itself isdomain.IsListingPath(single definition).Doc+readPanecall Open/OpenCached instead of re-deciding by trailing slash; the margin/edge-source presentation checks route throughdomain.IsListingPathtoo, so the rule lives in exactly one place.
- Split the
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 —
floorSVGwent 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/hideOverlayfocus helper across all three overlays (focus into panel on open, restore on close), androle="dialog"/aria-modal/aria-labelledbyon 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
typefield is newly threaded throughdomain.Documentand 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) —recordEdgesnow normalizes each edge target's host viaParseMarkURL(port-stable) and drops loopback/localhost/unspecified targets;discoverServersskips queuing them. Narrow filter by design — private + cluster-internal hosts are KEPT (real federated/cluster worlds use those). CodeRabbit round: hardenedisLoopbackHostfor unbracketed IPv6+port (::1:6309) + IPv6 test coverage. Cleansgraph.mdat 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- depthportalLabel/isLocalHostinservice/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) —hostNamenow joins an authorized world referenced by name+port (mark://root:6309, the port-normalized form of a curatedmark://root/...link fromworld-a/index.md) to the real world, instead of doubling it as its ownrootportal. This secondrootwas a regression introduced by the agent's port-normalization (before, the port-lessmark://rootmatched 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).
ADR 0007 — pane-scroll room is now the DEFAULT (2026-07-04, uncommitted)
Experiment verdict: it wins. Recorded as ADR 0007; built on the working tree (Fritz commits):
DEMARKUS_PANE_SCROLLdefault 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).
Deployed — library 0.17.0 LIVE, pane-scroll room default in prod (2026-07-04)
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.IsVersionPathnow sits besideIsListingPathand 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.mdhad 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
openadopting 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.