From 546a9749e0f149690f1939b5cbf89e54d3685fcd Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 18 Jul 2026 15:58:58 +0000 Subject: [PATCH 1/9] docs(site): propose multilevel nav IA redesign (Lume nav plugin) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full proposal for restructuring the docs-site sidebar from the flat 120-link navSections array to a five-lane (Start/Learn/Build/Reference/ Concepts) multilevel nav driven by the Lume nav plugin, keeping the nine capability pillars contiguous and moving the 27 how-to recipes into per-pillar how-to/ subfolders. Includes the complete annotated target arborescence, nav-plugin implementation plan, storyline, cross-reference migration slices, rewrite list, rejected alternatives, and open questions. Proposal only — no content moved or rewritten yet. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01UVD6TpugadnV8aQw6mcGV6 --- docs/site/_plan/10-nav-ia-redesign.md | 469 ++++++++++++++++++++++++++ 1 file changed, 469 insertions(+) create mode 100644 docs/site/_plan/10-nav-ia-redesign.md diff --git a/docs/site/_plan/10-nav-ia-redesign.md b/docs/site/_plan/10-nav-ia-redesign.md new file mode 100644 index 000000000..ffa333db6 --- /dev/null +++ b/docs/site/_plan/10-nav-ia-redesign.md @@ -0,0 +1,469 @@ +# 10 — Multilevel Nav & IA Redesign (Lume nav plugin) — Proposal + +**Status: PROPOSAL — awaiting maintainer approval. No content moved or rewritten yet.** + +Provenance: produced 2026-07-18 by a supervised multi-agent design run (4 repo/market audits → +3 independent full IA proposals → 3 adversarial judges → synthesis), with every count re-verified +against the live repo. Judges: reader-experience → P2, migration/link-integrity → P2, +state-of-the-art/technical-fit → P3. Winning spine = P2 (journey lanes) with two structural grafts +from P1 (capability-native) and P3 (Diátaxis-separation). + +The problem being solved: today's sidebar is a hand-rolled flat `navSections` array in `_data.ts` — +13 sections, ~120 always-visible links, 37 duplicate listings (the same page in 2–3 sections), +13 pillar guide pages orphaned from nav, and no hierarchy/collapse. The Lume nav plugin derives the +tree from page URLs, so the folder layout must become the IA. + +## 1. Decision summary + +**Shape chosen: lifecycle-lane spine, with the pillars kept whole and the recipes moved home.** +Five curated top-level lanes — **Start · Learn · Build · Reference · Concepts** — render as visual +dividers over folder-derived `nav.menu()` subtrees. This lands dead-center of the 6–9-group SOTA +band (today: 13 flat sections, ~120 always-visible links), keeps Learn (tutorials) as the second +lane so a newcomer's "build one thing" path is prominent, and uses plain-English/intent labels that +are *not* the Diátaxis quadrant names (satisfying the locked "Diátaxis demoted from front door" +decision in `08-decisions-locked.md`). + +**Per-capabilities: KEPT and strengthened.** All nine pillars stay top-level and **contiguous +inside one Build lane**, fixing the journey-first proposal's own worst defect — a Build/Operate +split that fractured the capability set ("where is Observability?"). There is no Operate lane; +deploy/observe concerns are expressed as recipes inside the Orchestration and Observability pillars +plus cross-refs, not by relocating whole pillars. The locked "docs-v4 Capability-Hub IA" (pillars as +the mental model) is preserved, so this design carries **no top-level-IA ratification risk**. + +**The one graft that changes URLs: recipes move into their pillar.** The 27 flat `/how-to/` recipes +were the sidebar's least navigable asset — a decontextualized global list. Two of three judges ruled +a flat recipe list unacceptable. We adopt the namespaced **`//how-to//`** subfolder +(plugin-native real node, collision-proof, depth-3 because pillars stay top-level). Each recipe now +sits next to the guide it serves, in one expand. `queue-kv-cron` and `choose-a-queue-provider` +re-home from Background Processing to Data & Persistence (better semantic fit next to the +`kv-queues-cron` guide). + +**Cost of that graft, stated honestly:** 28 URL moves (27 recipes + `capabilities/agent-tooling`). +This is a deliberate reader-value-over-migration-minimalism decision. The cost is bounded and +build-gated: `check-internal-links.ts` (deno.json task) scans every href in built HTML, so no broken +link can merge; the Lume `redirects` plugin reduces 28 shims to 28 front-matter lines with +auto-`unlisted`. Reference (crown jewels), tutorials, explanation, and all nine pillar-guide URLs +stay byte-stable. + +**Reference stays a single flat locked lane** (universal SOTA pattern; 32 units, `mcp` now +registered) — never scattered into pillars. **Cross-listing dies entirely** (37 duplicate nav +listings removed); discovery re-lands in pillar-hub card-grids via the existing `xref` system. + +## 2. Full target arborescence + +Legend: **[KEEP]** URL+home unchanged · **[MOVE from X]** file moves, ships redirect · **[NEW]** new +file · **[RETIRE→catalog]** kept as nav-hidden catalog · **[HIDE]** on disk, `nav_hide`, +search/link-reachable only · **[LOCKED]** reference URL, byte-stable · **[REGISTER]** exists on +disk, add to data/xref. Lane dividers (`━━`) are non-interactive headers (depth L0). Collapsible +depth below a lane: L1 pillar/track/unit → L2 leaf/how-to-node → L3 recipe/chapter/subpage. +**Max collapsible depth = 3.** + +``` +/ Home (not a nav item) [KEEP] + +━━ START ━━ (curated flat list — locked plain-English front door) + /why/ Why NetScript [KEEP] + /quickstart/ Quickstart (Aspire hero path) [KEEP] (de-cross-listed: drop 2nd listing under Orchestration) + /quickstart/aspire/ Aspire quickstart [NEW] (hero CTA landing; TS AppHost, not .NET; --no-aspire opt-out lives in /explanation/aspire/) + /concepts/ Core concepts [KEEP] (de-cross-listed: drop 2nd listing under Observability; reprose — §6) + /cli-reference/ CLI reference (task cheat-sheet) [KEEP] (NOT moved; reprose to defer symbols to /reference/cli/commands/) + /glossary/ Glossary [KEEP] + /how-to/ All how-to recipes (catalog) [RETIRE→catalog] children moved out; nav_hide; linked from Start + pillar hubs + +━━ LEARN ━━ nav.menu("/tutorials/", "nav_hide!=true", "order basename") + /tutorials/ Tutorials (index) [KEEP] L1 + /tutorials/live-dashboard/ 01–06 Live dashboard [KEEP] L2 → chapters L3 (drop Web "Quickstart→index" dup) + /tutorials/chat/ 01–06 Durable chat [KEEP] L2 → L3 (drop AI dup) + /tutorials/workspace/ 01–06 Workspace [KEEP] L2 → L3 (drop Identity "02-auth" mid-chapter anchor) + /tutorials/storefront/ 01–07 Storefront [KEEP] L2 → L3 (drop Services/Data/Durable mid-chapter anchors) + /tutorials/erp-sync/ 01–05 ERP sync [KEEP] L2 → L3 (drop BG "03-polyglot-transform" anchor) + /tutorials/eis-chat/** (5 shims) → /tutorials/chat/* [HIDE] nav_hide (URL preservation only) + +━━ BUILD ━━ 9 contiguous pillar nav.menu() roots, curated order (the locked capability spine) + within each pillar: guide leaves (L2), then a how-to/ node (L2) whose recipes are L3 + + /web-layer/ Web Layer [KEEP] L1 (rewrite → card-grid hub, §6) + /web-layer/server/ builders/ route/ query/ form/ defer-streaming-ui/ + interactive/ vite/ error/ testing/ examples/ [KEEP] L2 (12 guide leaves) + /web-layer/fresh-ui/ Fresh UI & design [KEEP] L2 (was nav-orphan → gains home) + /web-layer/how-to/ (recipes) [NEW] L2 index + …/customize-fresh-ui/ [MOVE from /how-to/customize-fresh-ui/] L3 + …/build-a-desktop-frontend/ [MOVE from /how-to/build-a-desktop-frontend/] L3 + …/build-a-server-validated-form/ [MOVE from /how-to/build-a-server-validated-form/] L3 + + /services-sdk/ Services & SDK [KEEP] L1 (rewrite hub) + /services-sdk/services/ Services & contracts [KEEP] L2 (was orphan → home) + /services-sdk/sdk/ Typed SDK & client [KEEP] L2 (was orphan → home) + /services-sdk/how-to/ [NEW] L2 + …/add-a-service/ …/discover-services/ …/expose-openapi-scalar/ [MOVE from /how-to/*] L3 (3) + + /background-processing/ Background jobs [KEEP] L1 (rewrite hub; "workers" taught inside) + /background-processing/workers/ Background jobs (guide) [KEEP] L2 (was orphan → home) + /background-processing/polyglot-tasks/ Polyglot tasks [KEEP] L2 (was orphan → home) + /background-processing/how-to/ [NEW] L2 + …/tune-worker-runtime/ …/run-a-polyglot-task/ + …/add-a-task-runtime-adapter/ …/restrict-worker-task-permissions/ [MOVE from /how-to/*] L3 (4) + + /durable-workflows/ Durable workflows [KEEP] L1 (rewrite hub; "sagas" taught inside) + /durable-workflows/sagas/ streams/ triggers/ [KEEP] L2 (all 3 were orphans → homes) + /durable-workflows/how-to/ [NEW] L2 + …/build-a-validated-ingestion-queue/ …/publish-a-durable-stream/ [MOVE from /how-to/*] L3 (2) + + /ai/ AI & Agents [KEEP] L1 (rewrite hub) + /ai/mcp/ durable-chat/ chat-ui/ [KEEP] L2 + /ai/engine/ AI engine (guide) [KEEP] L2 (reprose: demote from "Reference:", §6) + /ai/agent-tooling/ Agent tooling [MOVE from /capabilities/agent-tooling/] L2 (was orphan in all-shim dir) + /ai/how-to/ [NEW] L2 + …/build-a-durable-chat/ [MOVE from /how-to/build-a-durable-chat/] L3 (UNKEYED — see §5) + + /data-persistence/ Data & Persistence [KEEP] L1 (rewrite hub) + /data-persistence/database/ kv-queues-cron/ [KEEP] L2 (both were orphans → homes) + /data-persistence/how-to/ [NEW] L2 + …/database-migration/ …/use-a-second-database/ [MOVE from /how-to/*] L3 + …/queue-kv-cron/ …/choose-a-queue-provider/ [MOVE from /how-to/*, RE-HOMED from Background Processing] L3 (4 total) + + /identity-access/ Identity & Access [KEEP] L1 (rewrite hub) + /identity-access/auth/ Authentication [KEEP] L2 (was orphan → home) + /identity-access/better-auth-plugins/ [KEEP] L2 + /identity-access/how-to/ [NEW] L2 + …/add-authentication/ [MOVE from /how-to/add-authentication/] L3 + + /orchestration-runtime/ Orchestration & Runtime (Aspire home) [KEEP] L1 (rewrite hub) + /orchestration-runtime/cli-scaffold/ [KEEP] L2 + /orchestration-runtime/runtime-config/ [KEEP] L2 (was orphan → home) + /orchestration-runtime/how-to/ [NEW] L2 + …/deploy/ …/deploy-local-aspire/ …/deploy-deno-deploy/ + …/roll-out-runtime-overrides/ …/graceful-shutdown/ + …/add-a-plugin/ …/author-a-plugin/ …/deno-lsp-code-intelligence/ [MOVE from /how-to/*] L3 (8) + (deploy-deno-deploy is UNKEYED — see §5) + + /observability/ Observability [KEEP] L1 (rewrite hub) + /observability/telemetry/ Telemetry & logging [KEEP] L2 (was orphan → home) + /observability/how-to/ [NEW] L2 + …/add-opentelemetry/ [MOVE from /how-to/add-opentelemetry/] L3 + +━━ REFERENCE ━━ nav.menu("/reference/", "", "order basename") — 32 units, URLs LOCKED byte-stable + /reference/ Reference (index) [KEEP][LOCKED] L1 + …31 registered units (ai, aspire, auth, auth-better-auth, auth-kv-oauth, auth-workos, + cli, config, contracts, cron, database, fresh, fresh-ui, kv, logger, plugin, + plugin-ai, plugin-ai-core, plugin-auth, plugin-auth-core, prisma-adapter-mysql, + queue, runtime-config, sagas, sdk, service, streams, telemetry, triggers, + watchers, workers)… [KEEP][LOCKED] L2 (single listing; drop the 2nd pillar-section copy) + /reference/mcp/ @netscript/mcp [REGISTER][LOCKED] L2 (exists on disk; ADD to referenceUnits + REFERENCE_UNITS + ref:mcp) + /reference/cli/commands/ CLI commands [KEEP][LOCKED] L3 + /reference/ai/skills/ AI skills [KEEP][LOCKED] L3 + /reference/telemetry/convention/ Telemetry convention [KEEP][LOCKED] L3 (was orphan → auto-surfaces via nav.menu) + +━━ CONCEPTS ━━ nav.menu("/explanation/", "nav_hide!=true", "order basename") — plain-English label (Diátaxis demoted) + /explanation/ How NetScript works (index) [KEEP] L1 + /explanation/architecture/ contracts/ plugin-system/ auth-model/ + durability-model/ observability/ aspire/ [KEEP] L2 (7 essays) + +━━ (not in any nav lane) ━━ + /capabilities/*.md (16 existing shims → pillar dirs) [KEEP as shims] nav_hide + + 28 NEW redirect entries at every [MOVE] source (27 recipes + agent-tooling) [NEW] auto-unlisted via redirects plugin +``` + +**Verified totals:** 28 pages MOVE · 2 NEW pages (`/quickstart/aspire/`, optional — §8) · +9 NEW `how-to/index.md` · 15 pages surface in nav for the first time (13 orphan guides + +`telemetry/convention` + `mcp`) · **37 duplicate listings removed** · **0 reference URLs changed**. + +## 3. Sidebar & nav plugin implementation + +**Plugin registration (`_config.ts`).** Add `import nav from "lume/plugins/nav.ts"; site.use(nav());` +and `import redirects from "lume/plugins/redirects.ts"; site.use(redirects());`. Registration order +relative to `base_path` is irrelevant for `nav` — it is a template-data provider emitting *source* +(pre-base) URLs, and `base_path()` still runs last and rewrites emitted `href`s. `redirects` must +run before `base_path` (it emits pages). `code_highlight`, `pagefind`, `aiTooling` untouched. + +**What replaces `navSections`.** Delete the ~120-entry `navSections` array, the +`NavItem`/`NavSection` types, **and** the `referenceUnits` array (all were nav-only inputs; +reference is now folder-derived). Replace with a compact `navLanes` spine — the *only* +hand-maintained nav data (~35 lines vs ~120): + +```ts +export interface NavLane { + label: string; subtitle: string; icon?: string; + kind: "flat" | "menu"; + items?: string[]; // flat: curated hrefs (Start) + roots?: string[]; // menu: nav.menu roots, in curated order (Build owns the 9-pillar sequence) +} +export const navLanes: NavLane[] = [ + { label: "Start", subtitle: "Get running in minutes", kind: "flat", + items: ["/why/","/quickstart/","/quickstart/aspire/","/concepts/","/cli-reference/","/glossary/","/how-to/"] }, + { label: "Learn", subtitle: "Build one thing end to end", kind: "menu", roots: ["/tutorials/"] }, + { label: "Build", subtitle: "Add a capability to your app", kind: "menu", + roots: ["/web-layer/","/services-sdk/","/background-processing/","/durable-workflows/", + "/ai/","/data-persistence/","/identity-access/","/orchestration-runtime/","/observability/"] }, + { label: "Reference", subtitle: "Every symbol, generated", kind: "menu", roots: ["/reference/"] }, + { label: "Concepts", subtitle: "How and why it works", kind: "menu", roots: ["/explanation/"] }, +]; +``` + +The nine-pillar curated order lives in `Build.roots` — this is what neutralizes decision D-E1's +concern (a *global* `nav.menu("/")` inverting designed order): top-level lane order and pillar +sequence stay curated data, and `nav.menu` only builds single-home subtrees it can order correctly +with `order` front matter. + +**Front-matter conventions (the real content sweep):** + +- `order: ` on every index + leaf a `nav.menu` root touches (~113 non-shim pages; none exist + today). Sort string everywhere: `"order basename"` (verified valid multi-field grammar). + Pillar/track/how-to **index pages get `order: 0`** so the hub sorts first. Tutorial chapters need + no `order` — their `NN-` basename sorts under the `basename` tiebreak. Recipes get an + `order: 100+` band so they always cluster after guides inside a pillar. +- `nav_hide: true` filtered out via the `"nav_hide!=true"` query (verified: negation valid, + undefined passes). The Lume `redirects` plugin auto-sets `unlisted: true` on moved-from URLs, + which also excludes them — so the 28 new shims need no manual flag; only the 5 + `tutorials/eis-chat/*` shims (which live under a rendered root) and the retired `/how-to/` + catalog need explicit `nav_hide`. +- `nav_title: ` only where the sidebar label must differ from the page H1 (today's curated + labels like "The Fresh page model" diverge from H1s). Template reads + `node.data.nav_title || node.data.title`. +- Every folder already has `index.md` (verified) → every section node carries `data`, so every + `` is clickable and `order`-sort is hazard-free (no page-less intermediate nodes). + +**`base.vto` (replace lines 47–72).** Loop `navLanes`: + +- Render each lane divider: + `` + (subtitles are a kept SOTA affordance — Stripe/Lume pattern). +- `kind:"flat"` → curated ``s; active check `page.data.url == href` (drop today's double + `|> url` dance — source==source, since base_path rewrites the emitted href). +- `kind:"menu"` → for each root, feed `nav.menu(root, "nav_hide!=true", "order basename").children` + to a recursive **`_includes/menu_item.vto`** partial (the plugin's documented recursion — required + because Build reaches depth 3 at pillar→how-to→recipe and Reference/Learn reach + unit/track→subpage). Each folder node is `
`; + leaves are ``. Per-lane icons come + from `navLanes`; per-page icons are dropped (nav data carries none). + +**Collapse:** default-collapsed siblings, auto-expand the active path via `url.startsWith`. Native +`
/`, zero JS. A reader sees ~5 lane headers + 9 pillar summaries + one open +subtree — strictly fewer on-screen elements than today's 120. + +**Breadcrumb (`breadcrumb.vto`).** Replace the `navSections` scan with `nav.breadcrumb(url)` +(root→page array), prefixed by the owning lane label via a small `root→lane` lookup (so +`/web-layer/query/` reads *Build › Web Layer › Data loading & cache*). Guard `{{ if crumb.data }}`. +Behavior change accepted: one deterministic trail replaces "last matching section." + +**Next/prev (`nextPrev.vto`).** Keep front-matter-driven for tutorials (per-chapter editorial +control; it never touched `navSections`, cannot break). For Reference (32 units, unmaintainable by +hand) auto-derive with the **correct** signature (all three source proposals misused it; verified: +`nav.nextPage(url, basePath, query, sort)`, 2nd arg is basePath): +`nav.nextPage(url, "/reference/", "", "order basename")` / `nav.previousPage(...)`. + +**Untouched chrome (verified nav-agnostic):** `