Site chrome unified into shared SiteHeader + SiteFooter + ThemeToggle components, and the docs/KB surface made fully open — DEC-0022's DOCS_ENABLED landing-only gate is removed so /knowledge ships publicly alongside the landing/board/graph

0038-shared-site-chrome-and-fully-open-kb

decision read as Explain confidence verified status active 2026-06-16 owner forward-deployed-engineer
Reversibility
two-way door

DEC-0038 — Shared site chrome (SiteHeader + SiteFooter + ThemeToggle) and a fully-open KB surface

Reversibility: two-way door — re-adding the docs gate = restore DOCS_ENABLED=false in vercel.json + the integrations: docsEnabled ? [...] : [] ternary; the shared-chrome components are a pure consolidation, revertible by inlining the header/footer markup back into each page. The durable parts are the standing rule (surfaces fully open by default, chrome single-sourced) and one-place header/footer.

Context

While iterating on the site, the operator gave a standing directive: "keep it all fully open. remove the gate. wire in the same header to graph. yes unify the footer. this should always be a rule unless there is a good reason otherwise. remember, atomic." This establishes a STANDING RULE for the surface layer: surfaces are fully open by default (no conditional hiding) and built atomically (one shared single-source component, never per-surface copies).

It follows directly from earlier same-session work: a shared SiteHeader.astro + ThemeToggle.astro were already extracted (landing + board). This decision completes the consolidation — it adds the graph to the shared header, adds a shared SiteFooter.astro, and removes the docs gate everywhere — and records the standing rule so future chrome-light pages inherit it by default.

The gate being removed is the DOCS_ENABLED landing-only gate from Ship the landing publicly behind a docs-gate flag, capture demand through two honest doors, into a list we own (which gated the Starlight docs/KB surface out of the public production build). Web ingestion — a keyless HttpConnector by default, Firecrawl wired as the premium path, and a first-class CLI web-ingest mode is unrelated web ingestion — the gate's real record is DEC-0022.

Options considered

1. Site chrome — per-page topbar/footer copies vs. one shared single-source component.

  • (a) Keep each surface's own .landing-topbar / .board-topbar / .graph-topbar (+ matching footers). Rejected: three copies of the same header/footer means every brand-bar fix, nav change, or theme-toggle tweak must be re-applied N times and can drift between surfaces — the opposite of single-source-of-truth, and against the operator's explicit "remember, atomic."
  • (b) Extract one shared SiteHeader.astro + SiteFooter.astro + ThemeToggle.astro; every surface renders them (chosen). SiteHeader.astro carries the wordmark + nav (Docs·Board·Graph·GitHub) + GitHub link + ThemeToggle, with optional cta/badge props for per-surface affordances; SiteFooter.astro carries a <slot> note + the same nav. The three chrome-light surfaces — LandingPage.astro, pages/board.astro, pages/graph.astro — render <SiteHeader/> + <SiteFooter/>; their per-page topbar/footer markup + dead consts were removed. Self-contained scoped styles, --ds-* tokens only, no hex.

2. The docs/KB surface — keep DEC-0022's landing-only gate vs. remove it (fully open).

  • (a) Keep DOCS_ENABLED=false in production so only the landing (+ capture endpoint) builds. Rejected per the standing directive — and because the board (Decouple the agentic board from DOCS_ENABLED — ship /board publicly in production while the /knowledge reading surface stays gated dark) and graph already expose KB-derived data publicly, so holding the reading surface back is now incongruent: the KB the board/graph project from is hidden while their projections are public.
  • (b) Remove the gate everywhere — fully open (chosen). astro.config.mjs ALWAYS includes the Starlight docs integration (was docsEnabled ? [...] : []); vercel.json's DOCS_ENABLED=false build env was deleted; every {docsEnabled && …} link/CTA conditional and the process.env.DOCS_ENABLED consts were removed across the three pages. Atom deep-links and the "Browse the knowledge base" CTA now always resolve.

3. The one exception — leak client demos publicly vs. keep a single justified gate.

Decision

Unify the site chrome into shared SiteHeader + SiteFooter + ThemeToggle components rendered by every chrome-light surface, and make the docs/KB surface fully open by removing DEC-0022's DOCS_ENABLED landing-only gate — keeping exactly one justified gate so client demos never ship publicly. This encodes the operator's standing rule: surfaces fully open by default, chrome single-sourced (atomic), deviation only with a good reason.

  1. Shared chrome (atomic SSOT). New packages/site/src/components/SiteHeader.astro (wordmark + nav Docs·Board·Graph·GitHub + GitHub + ThemeToggle, optional cta/badge props), SiteFooter.astro (slotted note + the same nav), and ThemeToggle.astro. LandingPage.astro, pages/board.astro, and pages/graph.astro now render <SiteHeader/> + <SiteFooter/>; their per-page topbar/footer markup + dead consts were removed. Scoped styles, --ds-* tokens only, no hex.
  2. Fully open (reversing DEC-0022). astro.config.mjs ALWAYS includes Starlight (the docsEnabled ternary is gone); vercel.json's DOCS_ENABLED=false build env is deleted (the file is now bare { "$schema": … }); every {docsEnabled && …} conditional and process.env.DOCS_ENABLED const is removed across the three pages. The full /knowledge reading surface ships.
  3. The one gate kept (good reason). pages/preview/[client].astro emits zero pages in production via if (!import.meta.env.DEV) return [] (was DOCS_ENABLED==='false'), so client landing demos stay dev-only.
  4. A real bug fixed en route. pages/graph.astro had two literal NUL bytes as edge-dedup separators (~line 117) that broke the Astro compiler's AST parse ("Bad control character in JSON"); replaced with a space (matching board.astro). The graph page now compiles and is wired into the shared chrome.

Rationale

Consequences

  • On the next Vercel deploy, the full /knowledge OKF reading surface becomes PUBLIC alongside the landing/board/graph — Dossier's institutional memory is now "work in the open," consistent with the board/graph already exposing KB-derived data. Reversible (restore DOCS_ENABLED=false + the ternary).
  • Header/footer are now edited in one place. Every surface — and future chrome-light pages — inherits the shared SiteHeader/SiteFooter/ThemeToggle by default, so the standing rule is self-enforcing for new surfaces.
  • The standing rule is now recorded, not just spoken. "Surfaces fully open by default, chrome single-sourced, deviate only with a good reason" is durable and citable for the next surface decision.
  • Open follow-ups (recorded honestly; NOT fixed in this turn):
    • src/lib/graph-island.ts has 4 ts(2339) Property 'transition' does not exist errors because d3-transition is imported by neither the file nor packages/site/package.json (only d3-force/selection/zoom/drag are deps). The graph renders, but zoom-button transitions won't animate at runtime. Fix = add d3-transition as a dep + import 'd3-transition'. This belongs to the graph work-stream (adding a dependency was out of scope this turn). Filed as Add the missing d3-transition dependency + import so graph-island's zoom transitions animate (and astro check reaches zero).
    • Dead CSS now unused: .landing-topbar* / .landing-footer* / .landing-wordmark (landing.css), .board-topbar* / .board-wordmark / .board-readonly / .board-footer (board.css), .graph-topbar* / .graph-wordmark / .graph-readonly / .graph-footer (graph.css) — safe to delete in a cleanup pass. (.landing-external is still used by the landing hero — keep it.) Filed as Prune the now-dead per-page topbar/footer CSS left behind after the shared-chrome consolidation (keep .landing-external).
    • A prod build should be run to confirm the now-public docs surface builds cleanly before/at deploy. Folded into the verification gate in Review (deploy-time confirmation), not a separate task.
  • Two-way vs. durable. Re-adding the gate = restore DOCS_ENABLED=false in vercel.json + the integrations: docsEnabled ? [...] : [] ternary; the shared-chrome components are a pure consolidation, revertible by inlining. The durable commitments are the standing rule and one-place header/footer.

Review

Promote toward a fuller verified once a real production Vercel build has confirmed the now-always-on Starlight docs surface builds cleanly and /knowledge publishes as expected on the next deploy (the one piece verified for dev but not yet for prod). Close the d3-transition follow-up (Add the missing d3-transition dependency + import so graph-island's zoom transitions animate (and astro check reaches zero)) so the graph's zoom transitions animate at runtime and astro check reaches zero errors, and run the dead-CSS prune (Prune the now-dead per-page topbar/footer CSS left behind after the shared-chrome consolidation (keep .landing-external)) so the removed per-page chrome leaves no orphan styles. Re-examine what of the public knowledge/ repo should stay open if any atom is ever judged not-for-public (the standing rule's "good reason" escape hatch, the same discipline as the kept /preview/* gate).