OOKNET                             [ /  search the index  ]  
──────────────────────────────────────────────────────────────────────────────────────
══════════════════════════════════════════════════════════════════════════════════════
OOKNET   [ /  search  ]  
────────────────────────────────────────────────
════════════════════════════════════════════════
 
HASH      27daccaea403
DATE      2026-07-18
SUBJECT   web: one kb, registers by dir.yml - notes/docs/reference decided by the
          tree, not the url
FILES     27 CHANGED
HASH      27daccaea403
DATE      2026-07-18
SUBJECT   web: one kb, registers by dir.yml -
          notes/docs/reference decided by the
          tree, not the url
FILES     27 CHANGED
 

diff --git a/ooknet-design/src/components/ArticleFooter/ArticleFooter.ts b/ooknet-
design/src/components/ArticleFooter/ArticleFooter.ts
index d2d98f2..69a9d39 100644
--- a/ooknet-design/src/components/ArticleFooter/ArticleFooter.ts
+++ b/ooknet-design/src/components/ArticleFooter/ArticleFooter.ts
@@ -1,9 +1,9 @@
-import { rule, row2 } from "../../lib/ascii";
+import { rule } from "../../lib/ascii";
 
 export interface Props {
   width?: number;
 }
 
 export function buildFooter(width: number): string {
-  return [rule(width), row2("OOKNET", "Knowledge as Infrastructure", width)].join
("\n");
+  return [rule(width), "OOKNET"].join("\n");
 }

diff --git a/ooknet-design/src/components/Ar
ticleFooter/ArticleFooter.ts b/ooknet-design
/src/components/ArticleFooter/ArticleFooter.
ts
index d2d98f2..69a9d39 100644
--- a/ooknet-design/src/components/ArticleFo
oter/ArticleFooter.ts
+++ b/ooknet-design/src/components/ArticleFo
oter/ArticleFooter.ts
@@ -1,9 +1,9 @@
-import { rule, row2 } from "../../lib/ascii
";
+import { rule } from "../../lib/ascii";
 
 export interface Props {
   width?: number;
 }
 
 export function buildFooter(width: number):
 string {
-  return [rule(width), row2("OOKNET", "Know
ledge as Infrastructure", width)].join("\n")
;
+  return [rule(width), "OOKNET"].join("\n")
;
 }
 

diff --git a/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.astro b/oo
knet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.astro
index fa8a091..0965eaf 100644
--- a/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.astro
+++ b/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.astro
@@ -1,6 +1,6 @@
 ---
 import Frame from "../Frame/Frame.astro";
 import { buildSeeAlso, type Props } from "./ArticleSeeAlso";
-const { fm } = Astro.props as Props;
+const { seeAlso = [] } = Astro.props as Props;
 ---
-{(fm.seeAlso?.length ?? 0) > 0 && <Frame build={(w) => buildSeeAlso(w, fm)} />}
+{seeAlso.length > 0 && <Frame build={(w) => buildSeeAlso(w, seeAlso)} />}

diff --git a/ooknet-design/src/components/Ar
ticleSeeAlso/ArticleSeeAlso.astro b/ooknet-d
esign/src/components/ArticleSeeAlso/ArticleS
eeAlso.astro
index fa8a091..0965eaf 100644
--- a/ooknet-design/src/components/ArticleSe
eAlso/ArticleSeeAlso.astro
+++ b/ooknet-design/src/components/ArticleSe
eAlso/ArticleSeeAlso.astro
@@ -1,6 +1,6 @@
 ---
 import Frame from "../Frame/Frame.astro";
 import { buildSeeAlso, type Props } from ".
/ArticleSeeAlso";
-const { fm } = Astro.props as Props;
+const { seeAlso = [] } = Astro.props as Pro
ps;
 ---
-{(fm.seeAlso?.length ?? 0) > 0 && <Frame bu
ild={(w) => buildSeeAlso(w, fm)} />}
+{seeAlso.length > 0 && <Frame build={(w) =>
 buildSeeAlso(w, seeAlso)} />}
 

diff --git a/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.ts b/ookne
t-design/src/components/ArticleSeeAlso/ArticleSeeAlso.ts
index 6aec6ea..06af4cd 100644
--- a/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.ts
+++ b/ooknet-design/src/components/ArticleSeeAlso/ArticleSeeAlso.ts
@@ -1,13 +1,11 @@
 import { kv, rule } from "../../lib/ascii";
-import type { NoteFrontmatter } from "../../lib/types";
 
 export interface Props {
   width?: number;
-  fm: NoteFrontmatter;
+  seeAlso?: string[];
 }
 
-export function buildSeeAlso(width: number, fm: NoteFrontmatter): string {
-  const refs = fm.seeAlso ?? [];
+export function buildSeeAlso(width: number, refs: string[]): string {
   const out: string[] = [rule(width)];
   out.push(kv("SEE ALSO", refs[0], 12, width));
   for (const s of refs.slice(1)) out.push(kv("", s, 12, width));

diff --git a/ooknet-design/src/components/Ar
ticleSeeAlso/ArticleSeeAlso.ts b/ooknet-desi
gn/src/components/ArticleSeeAlso/ArticleSeeA
lso.ts
index 6aec6ea..06af4cd 100644
--- a/ooknet-design/src/components/ArticleSe
eAlso/ArticleSeeAlso.ts
+++ b/ooknet-design/src/components/ArticleSe
eAlso/ArticleSeeAlso.ts
@@ -1,13 +1,11 @@
 import { kv, rule } from "../../lib/ascii";
-import type { NoteFrontmatter } from "../..
/lib/types";
 
 export interface Props {
   width?: number;
-  fm: NoteFrontmatter;
+  seeAlso?: string[];
 }
 
-export function buildSeeAlso(width: number,
 fm: NoteFrontmatter): string {
-  const refs = fm.seeAlso ?? [];
+export function buildSeeAlso(width: number,
 refs: string[]): string {
   const out: string[] = [rule(width)];
   out.push(kv("SEE ALSO", refs[0], 12, widt
h));
   for (const s of refs.slice(1)) out.push(k
v("", s, 12, width));
 

diff --git a/ooknet-design/src/components/Breadcrumbs/Breadcrumbs.ts b/ooknet-desi
gn/src/components/Breadcrumbs/Breadcrumbs.ts
index 1f40af1..067f94a 100644
--- a/ooknet-design/src/components/Breadcrumbs/Breadcrumbs.ts
+++ b/ooknet-design/src/components/Breadcrumbs/Breadcrumbs.ts
@@ -6,9 +6,7 @@ import { checkGrid, len } from "../../lib/ascii";
 export const CRUMB_LABEL = "[FILED UNDER]: ";
 
 const ROOTS: Record<string, string> = {
-  NOTES: "/notes/",
   KB: "/kb/",
-  DOCS: "/docs/",
   GIT: "/git/",
 };
 

diff --git a/ooknet-design/src/components/Br
eadcrumbs/Breadcrumbs.ts b/ooknet-design/src
/components/Breadcrumbs/Breadcrumbs.ts
index 1f40af1..067f94a 100644
--- a/ooknet-design/src/components/Breadcrum
bs/Breadcrumbs.ts
+++ b/ooknet-design/src/components/Breadcrum
bs/Breadcrumbs.ts
@@ -6,9 +6,7 @@ import { checkGrid, len } fr
om "../../lib/ascii";
 export const CRUMB_LABEL = "[FILED UNDER]: 
";
 
 const ROOTS: Record<string, string> = {
-  NOTES: "/notes/",
   KB: "/kb/",
-  DOCS: "/docs/",
   GIT: "/git/",
 };
 
 

diff --git a/ooknet-design/src/components/NavBar/NavBar.test.ts b/ooknet-design/sr
c/components/NavBar/NavBar.test.ts
index d895ccf..c2ef0fe 100644
--- a/ooknet-design/src/components/NavBar/NavBar.test.ts
+++ b/ooknet-design/src/components/NavBar/NavBar.test.ts
@@ -3,13 +3,14 @@ import { NAV_GAP, NAV_ITEMS, NAV_LANG_W, itemsWidth, navRows } f
rom "./NavBar";
 import { MOBILE_FRAME_W, FRAME_W } from "../../lib/config";
 
 describe("navRows", () => {
-  it("keeps all seven items on one row at desktop width", () => {
+  it("keeps every item on one row at desktop width", () => {
     const rows = navRows(NAV_ITEMS, FRAME_W, NAV_GAP, NAV_LANG_W + 1);
     expect(rows).toHaveLength(1);
   });
 
-  it("wraps onto rows at the narrow width", () => {
-    const rows = navRows(NAV_ITEMS, MOBILE_FRAME_W, 1);
+  it("wraps when items exceed the width", () => {
+    const many = [...NAV_ITEMS, ...NAV_ITEMS, ...NAV_ITEMS];
+    const rows = navRows(many, MOBILE_FRAME_W, 1);
     expect(rows.length).toBeGreaterThan(1);
     for (const row of rows) {
       expect(itemsWidth(row, 1)).toBeLessThanOrEqual(MOBILE_FRAME_W);
@@ -17,7 +18,8 @@ describe("navRows", () => {
   });
 
   it("preserves item order across rows", () => {
-    const rows = navRows(NAV_ITEMS, MOBILE_FRAME_W, 1);
-    expect(rows.flat().map((it) => it.label)).toEqual(NAV_ITEMS.map((it) => it.la
bel));
+    const many = [...NAV_ITEMS, ...NAV_ITEMS];
+    const rows = navRows(many, MOBILE_FRAME_W, 1);
+    expect(rows.flat().map((it) => it.label)).toEqual(many.map((it) => it.label))
;
   });
 });

diff --git a/ooknet-design/src/components/Na
vBar/NavBar.test.ts b/ooknet-design/src/comp
onents/NavBar/NavBar.test.ts
index d895ccf..c2ef0fe 100644
--- a/ooknet-design/src/components/NavBar/Na
vBar.test.ts
+++ b/ooknet-design/src/components/NavBar/Na
vBar.test.ts
@@ -3,13 +3,14 @@ import { NAV_GAP, NAV_ITEM
S, NAV_LANG_W, itemsWidth, navRows } from ".
/NavBar";
 import { MOBILE_FRAME_W, FRAME_W } from "..
/../lib/config";
 
 describe("navRows", () => {
-  it("keeps all seven items on one row at d
esktop width", () => {
+  it("keeps every item on one row at deskto
p width", () => {
     const rows = navRows(NAV_ITEMS, FRAME_W
, NAV_GAP, NAV_LANG_W + 1);
     expect(rows).toHaveLength(1);
   });
 
-  it("wraps onto rows at the narrow width",
 () => {
-    const rows = navRows(NAV_ITEMS, MOBILE_
FRAME_W, 1);
+  it("wraps when items exceed the width", (
) => {
+    const many = [...NAV_ITEMS, ...NAV_ITEM
S, ...NAV_ITEMS];
+    const rows = navRows(many, MOBILE_FRAME
_W, 1);
     expect(rows.length).toBeGreaterThan(1);
     for (const row of rows) {
       expect(itemsWidth(row, 1)).toBeLessTh
anOrEqual(MOBILE_FRAME_W);
@@ -17,7 +18,8 @@ describe("navRows", () => 
{
   });
 
   it("preserves item order across rows", ()
 => {
-    const rows = navRows(NAV_ITEMS, MOBILE_
FRAME_W, 1);
-    expect(rows.flat().map((it) => it.label
)).toEqual(NAV_ITEMS.map((it) => it.label));
+    const many = [...NAV_ITEMS, ...NAV_ITEM
S];
+    const rows = navRows(many, MOBILE_FRAME
_W, 1);
+    expect(rows.flat().map((it) => it.label
)).toEqual(many.map((it) => it.label));
   });
 });
 

diff --git a/ooknet-design/src/components/NavBar/NavBar.ts b/ooknet-design/src/com
ponents/NavBar/NavBar.ts
index e0c92fa..d37fe25 100644
--- a/ooknet-design/src/components/NavBar/NavBar.ts
+++ b/ooknet-design/src/components/NavBar/NavBar.ts
@@ -18,16 +18,13 @@ export interface Props {
 }
 
 export const NAV_ITEMS: NavItem[] = [
-  { label: "NOTES",     href: "/notes/" },
-  { label: "KB",        href: "/kb/" },
-  { label: "DOCS",      href: "/docs/" },
-  { label: "GIT",       href: "/git/" },
-  { label: "PROJECTS",  href: "/projects/" },
-  { label: "RESOURCES", href: "/resources/" },
-  { label: "ABOUT",     href: "/about/" },
+  { label: "KB",       href: "/kb/" },
+  { label: "GIT",      href: "/git/" },
+  { label: "PROJECTS", href: "/projects/" },
+  { label: "ABOUT",    href: "/about/" },
 ];
 
-export const NAV_GAP = 3;       // spaces between buttons - seven items fit one 8
6-cell row
+export const NAV_GAP = 4;       // spaces between buttons
 export const NAV_LANG_W = 2;    // visible width of "EN"
 
 /** Total width of all buttons + inter-button gaps. */

diff --git a/ooknet-design/src/components/Na
vBar/NavBar.ts b/ooknet-design/src/component
s/NavBar/NavBar.ts
index e0c92fa..d37fe25 100644
--- a/ooknet-design/src/components/NavBar/Na
vBar.ts
+++ b/ooknet-design/src/components/NavBar/Na
vBar.ts
@@ -18,16 +18,13 @@ export interface Props {
 }
 
 export const NAV_ITEMS: NavItem[] = [
-  { label: "NOTES",     href: "/notes/" },
-  { label: "KB",        href: "/kb/" },
-  { label: "DOCS",      href: "/docs/" },
-  { label: "GIT",       href: "/git/" },
-  { label: "PROJECTS",  href: "/projects/" 
},
-  { label: "RESOURCES", href: "/resources/"
 },
-  { label: "ABOUT",     href: "/about/" },
+  { label: "KB",       href: "/kb/" },
+  { label: "GIT",      href: "/git/" },
+  { label: "PROJECTS", href: "/projects/" }
,
+  { label: "ABOUT",    href: "/about/" },
 ];
 
-export const NAV_GAP = 3;       // spaces b
etween buttons - seven items fit one 86-cell
 row
+export const NAV_GAP = 4;       // spaces b
etween buttons
 export const NAV_LANG_W = 2;    // visible 
width of "EN"
 
 /** Total width of all buttons + inter-butt
on gaps. */
 

diff --git a/ooknet-design/src/content.config.ts b/ooknet-design/src/content.confi
g.ts
index d6e139b..1379305 100644
--- a/ooknet-design/src/content.config.ts
+++ b/ooknet-design/src/content.config.ts
@@ -1,46 +1,27 @@
 import { defineCollection, z } from "astro:content";
 import { glob } from "astro/loaders";
 
-// The directory is the taxonomy for every collection: notes split by
-// subject, kb by topic, docs by project. Frontmatter never repeats
-// what the path already says.
-
-// Personal writing and study notes. Only title and published are
-// required so a bare draft can't crash the content sync.
-const notes = defineCollection({
-  loader: glob({ pattern: "**/*.md", base: "./src/content/notes" }),
-  schema: z.object({
-    title: z.string(),
-    published: z.string(),                  // ISO date
-    status: z.string().default("DRAFT"),
-    tags: z.array(z.string()).default([]),
-    seeAlso: z.array(z.string()).optional(),
-    draft: z.boolean().default(false),
-  }),
-});
-
-// Copies of vendor documentation and guides, always credited.
+// One collection: the knowledge base. What a page IS - personal note,
+// project documentation, vendor reference - is declared by the nearest
+// dir.yml above it (see lib/registers.ts), not by where it lives in
+// the url. The schema stays permissive: only title is required, and
+// register-specific fields (published for notes, source/url/retrieved
+// for reference, order for docs) apply where the register wants them.
 const kb = defineCollection({
   loader: glob({ pattern: "**/*.md", base: "./src/content/kb" }),
   schema: z.object({
     title: z.string(),
-    source: z.string(),                     // vendor, e.g. "MICROSOFT LEARN"
-    url: z.string().optional(),
-    retrieved: z.string().optional(),       // ISO date the copy was taken
+    published: z.string().optional(),   // ISO date - notes
+    status: z.string().optional(),      // notes
     tags: z.array(z.string()).default([]),
+    seeAlso: z.array(z.string()).optional(),
+    description: z.string().optional(), // docs
+    order: z.number().optional(),       // docs
+    source: z.string().optional(),      // reference: vendor name
+    url: z.string().optional(),         // reference
+    retrieved: z.string().optional(),   // reference: ISO date
     draft: z.boolean().default(false),
   }),
 });
 
-// Project documentation. `order` sorts pages within a directory.
-const docs = defineCollection({
-  loader: glob({ pattern: "**/*.md", base: "./src/content/docs" }),
-  schema: z.object({
-    title: z.string(),
-    description: z.string().optional(),
-    order: z.number().optional(),
-    draft: z.boolean().default(false),
-  }),
-});
-
-export const collections = { notes, kb, docs };
+export const collections = { kb };

diff --git a/ooknet-design/src/content.confi
g.ts b/ooknet-design/src/content.config.ts
index d6e139b..1379305 100644
--- a/ooknet-design/src/content.config.ts
+++ b/ooknet-design/src/content.config.ts
@@ -1,46 +1,27 @@
 import { defineCollection, z } from "astro:
content";
 import { glob } from "astro/loaders";
 
-// The directory is the taxonomy for every 
collection: notes split by
-// subject, kb by topic, docs by project. F
rontmatter never repeats
-// what the path already says.
-
-// Personal writing and study notes. Only t
itle and published are
-// required so a bare draft can't crash the
 content sync.
-const notes = defineCollection({
-  loader: glob({ pattern: "**/*.md", base: 
"./src/content/notes" }),
-  schema: z.object({
-    title: z.string(),
-    published: z.string(),                 
 // ISO date
-    status: z.string().default("DRAFT"),
-    tags: z.array(z.string()).default([]),
-    seeAlso: z.array(z.string()).optional()
,
-    draft: z.boolean().default(false),
-  }),
-});
-
-// Copies of vendor documentation and guide
s, always credited.
+// One collection: the knowledge base. What
 a page IS - personal note,
+// project documentation, vendor reference 
- is declared by the nearest
+// dir.yml above it (see lib/registers.ts),
 not by where it lives in
+// the url. The schema stays permissive: on
ly title is required, and
+// register-specific fields (published for 
notes, source/url/retrieved
+// for reference, order for docs) apply whe
re the register wants them.
 const kb = defineCollection({
   loader: glob({ pattern: "**/*.md", base: 
"./src/content/kb" }),
   schema: z.object({
     title: z.string(),
-    source: z.string(),                    
 // vendor, e.g. "MICROSOFT LEARN"
-    url: z.string().optional(),
-    retrieved: z.string().optional(),      
 // ISO date the copy was taken
+    published: z.string().optional(),   // 
ISO date - notes
+    status: z.string().optional(),      // 
notes
     tags: z.array(z.string()).default([]),
+    seeAlso: z.array(z.string()).optional()
,
+    description: z.string().optional(), // 
docs
+    order: z.number().optional(),       // 
docs
+    source: z.string().optional(),      // 
reference: vendor name
+    url: z.string().optional(),         // 
reference
+    retrieved: z.string().optional(),   // 
reference: ISO date
     draft: z.boolean().default(false),
   }),
 });
 
-// Project documentation. `order` sorts pag
es within a directory.
-const docs = defineCollection({
-  loader: glob({ pattern: "**/*.md", base: 
"./src/content/docs" }),
-  schema: z.object({
-    title: z.string(),
-    description: z.string().optional(),
-    order: z.number().optional(),
-    draft: z.boolean().default(false),
-  }),
-});
-
-export const collections = { notes, kb, doc
s };
+export const collections = { kb };
 

diff --git a/ooknet-design/src/content/kb/windows/dir.yml b/ooknet-design/src/cont
ent/kb/windows/dir.yml
new file mode 100644
index 0000000..9a803d7
--- /dev/null
+++ b/ooknet-design/src/content/kb/windows/dir.yml
@@ -0,0 +1 @@
+register: reference

diff --git a/ooknet-design/src/content/kb/wi
ndows/dir.yml b/ooknet-design/src/content/kb
/windows/dir.yml
new file mode 100644
index 0000000..9a803d7
--- /dev/null
+++ b/ooknet-design/src/content/kb/windows/d
ir.yml
@@ -0,0 +1 @@
+register: reference
 

diff --git a/ooknet-design/src/lib/registers.test.ts b/ooknet-design/src/lib/regis
ters.test.ts
new file mode 100644
index 0000000..2ee02fe
--- /dev/null
+++ b/ooknet-design/src/lib/registers.test.ts
@@ -0,0 +1,40 @@
+import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
+import { join } from "node:path";
+import { tmpdir } from "node:os";
+import { afterAll, beforeAll, describe, expect, it } from "vitest";
+import { readManifests, registerOf } from "./registers";
+
+let dir: string;
+beforeAll(() => {
+  dir = mkdtempSync(join(tmpdir(), "reg-"));
+  mkdirSync(join(dir, "wowsim-stats/api"), { recursive: true });
+  mkdirSync(join(dir, "windows"), { recursive: true });
+  mkdirSync(join(dir, "nix"), { recursive: true });
+  writeFileSync(join(dir, "wowsim-stats/dir.yml"), 'register: docs\ntitle: "wowsi
m-stats"\n');
+  writeFileSync(join(dir, "windows/dir.yml"), "register: reference\n");
+});
+afterAll(() => rmSync(dir, { recursive: true, force: true }));
+
+describe("registers", () => {
+  it("resolves the nearest ancestor manifest", () => {
+    const m = readManifests(dir);
+    expect(registerOf("wowsim-stats/api/simulations", m)).toEqual({
+      register: "docs",
+      root: "wowsim-stats",
+      title: "wowsim-stats",
+    });
+    expect(registerOf("windows/tss", m).register).toBe("reference");
+  });
+
+  it("defaults unmarked subtrees to notes", () => {
+    const m = readManifests(dir);
+    expect(registerOf("nix/flakes", m)).toEqual({ register: "notes", root: null, 
title: null });
+    expect(registerOf("orphan", m).register).toBe("notes");
+  });
+
+  it("rejects unknown registers", () => {
+    writeFileSync(join(dir, "nix/dir.yml"), "register: blog\n");
+    expect(() => readManifests(dir)).toThrow(/register must be/);
+    rmSync(join(dir, "nix/dir.yml"));
+  });
+});

diff --git a/ooknet-design/src/lib/registers
.test.ts b/ooknet-design/src/lib/registers.t
est.ts
new file mode 100644
index 0000000..2ee02fe
--- /dev/null
+++ b/ooknet-design/src/lib/registers.test.t
s
@@ -0,0 +1,40 @@
+import { mkdtempSync, mkdirSync, rmSync, wr
iteFileSync } from "node:fs";
+import { join } from "node:path";
+import { tmpdir } from "node:os";
+import { afterAll, beforeAll, describe, exp
ect, it } from "vitest";
+import { readManifests, registerOf } from "
./registers";
+
+let dir: string;
+beforeAll(() => {
+  dir = mkdtempSync(join(tmpdir(), "reg-"))
;
+  mkdirSync(join(dir, "wowsim-stats/api"), 
{ recursive: true });
+  mkdirSync(join(dir, "windows"), { recursi
ve: true });
+  mkdirSync(join(dir, "nix"), { recursive: 
true });
+  writeFileSync(join(dir, "wowsim-stats/dir
.yml"), 'register: docs\ntitle: "wowsim-stat
s"\n');
+  writeFileSync(join(dir, "windows/dir.yml"
), "register: reference\n");
+});
+afterAll(() => rmSync(dir, { recursive: tru
e, force: true }));
+
+describe("registers", () => {
+  it("resolves the nearest ancestor manifes
t", () => {
+    const m = readManifests(dir);
+    expect(registerOf("wowsim-stats/api/sim
ulations", m)).toEqual({
+      register: "docs",
+      root: "wowsim-stats",
+      title: "wowsim-stats",
+    });
+    expect(registerOf("windows/tss", m).reg
ister).toBe("reference");
+  });
+
+  it("defaults unmarked subtrees to notes",
 () => {
+    const m = readManifests(dir);
+    expect(registerOf("nix/flakes", m)).toE
qual({ register: "notes", root: null, title:
 null });
+    expect(registerOf("orphan", m).register
).toBe("notes");
+  });
+
+  it("rejects unknown registers", () => {
+    writeFileSync(join(dir, "nix/dir.yml"),
 "register: blog\n");
+    expect(() => readManifests(dir)).toThro
w(/register must be/);
+    rmSync(join(dir, "nix/dir.yml"));
+  });
+});
 

diff --git a/ooknet-design/src/lib/registers.ts b/ooknet-design/src/lib/registers.
ts
new file mode 100644
index 0000000..0e277a5
--- /dev/null
+++ b/ooknet-design/src/lib/registers.ts
@@ -0,0 +1,72 @@
+// Directory registers: a dir.yml at any directory inside the kb tree
+// declares what its subtree is - personal notes, project docs, or
+// vendor reference copies. Resolution is nearest-ancestor; unmarked
+// content is notes, the natural resting state of a wiki.
+import { existsSync, readFileSync, readdirSync } from "node:fs";
+import { join } from "node:path";
+
+export type Register = "notes" | "docs" | "reference";
+
+export const DEFAULT_REGISTER: Register = "notes";
+
+export interface DirManifest {
+  register: Register;
+  /** Display name for the subtree, e.g. a project name. */
+  title?: string;
+}
+
+export interface RegisterInfo {
+  register: Register;
+  /** Manifest directory that decided the register; null when default. */
+  root: string | null;
+  title: string | null;
+}
+
+const REGISTERS: Register[] = ["notes", "docs", "reference"];
+
+// dir.yml is two known keys - a tiny parser beats a yaml dependency
+function parseManifest(text: string, at: string): DirManifest {
+  const field = (key: string): string | null => {
+    const m = text.match(new RegExp(`^${key}:\\s*["']?(.+?)["']?\\s*$`, "m"));
+    return m ? m[1] : null;
+  };
+  const register = field("register") as Register | null;
+  if (!register || !REGISTERS.includes(register)) {
+    throw new Error(`[kb] ${at}: register must be one of ${REGISTERS.join(", ")}`
);
+  }
+  return { register, title: field("title") ?? undefined };
+}
+
+/** Every dir.yml under the kb tree, keyed by directory id ("" root). */
+export function readManifests(contentDir = "src/content/kb"): Map<string, DirMani
fest> {
+  const out = new Map<string, DirManifest>();
+  if (!existsSync(contentDir)) return out;
+  const walk = (dir: string, prefix: string) => {
+    const manifest = join(dir, "dir.yml");
+    if (existsSync(manifest)) {
+      out.set(prefix, parseManifest(readFileSync(manifest, "utf8"), manifest));
+    }
+    for (const entry of readdirSync(dir, { withFileTypes: true })) {
+      if (entry.isDirectory()) {
+        walk(join(dir, entry.name), prefix ? `${prefix}/${entry.name}` : entry.na
me);
+      }
+    }
+  };
+  walk(contentDir, "");
+  return out;
+}
+
+/** Register of a page id, from the nearest ancestor manifest. */
+export function registerOf(id: string, manifests: Map<string, DirManifest>): Regi
sterInfo {
+  const segments = id.split("/").slice(0, -1);
+  for (let i = segments.length; i >= 0; i--) {
+    const dir = segments.slice(0, i).join("/");
+    const m = manifests.get(dir);
+    if (m) return { register: m.register, root: dir, title: m.title ?? null };
+  }
+  return { register: DEFAULT_REGISTER, root: null, title: null };
+}
+
+/** Display label for a register, as shown in chrome and search. */
+export const registerLabel = (r: Register): string =>
+  r === "notes" ? "NOTE" : r === "docs" ? "DOC" : "REF";

diff --git a/ooknet-design/src/lib/registers
.ts b/ooknet-design/src/lib/registers.ts
new file mode 100644
index 0000000..0e277a5
--- /dev/null
+++ b/ooknet-design/src/lib/registers.ts
@@ -0,0 +1,72 @@
+// Directory registers: a dir.yml at any di
rectory inside the kb tree
+// declares what its subtree is - personal 
notes, project docs, or
+// vendor reference copies. Resolution is n
earest-ancestor; unmarked
+// content is notes, the natural resting st
ate of a wiki.
+import { existsSync, readFileSync, readdirS
ync } from "node:fs";
+import { join } from "node:path";
+
+export type Register = "notes" | "docs" | "
reference";
+
+export const DEFAULT_REGISTER: Register = "
notes";
+
+export interface DirManifest {
+  register: Register;
+  /** Display name for the subtree, e.g. a 
project name. */
+  title?: string;
+}
+
+export interface RegisterInfo {
+  register: Register;
+  /** Manifest directory that decided the r
egister; null when default. */
+  root: string | null;
+  title: string | null;
+}
+
+const REGISTERS: Register[] = ["notes", "do
cs", "reference"];
+
+// dir.yml is two known keys - a tiny parse
r beats a yaml dependency
+function parseManifest(text: string, at: st
ring): DirManifest {
+  const field = (key: string): string | nul
l => {
+    const m = text.match(new RegExp(`^${key
}:\\s*["']?(.+?)["']?\\s*$`, "m"));
+    return m ? m[1] : null;
+  };
+  const register = field("register") as Reg
ister | null;
+  if (!register || !REGISTERS.includes(regi
ster)) {
+    throw new Error(`[kb] ${at}: register m
ust be one of ${REGISTERS.join(", ")}`);
+  }
+  return { register, title: field("title") 
?? undefined };
+}
+
+/** Every dir.yml under the kb tree, keyed 
by directory id ("" root). */
+export function readManifests(contentDir = 
"src/content/kb"): Map<string, DirManifest> 
{
+  const out = new Map<string, DirManifest>(
);
+  if (!existsSync(contentDir)) return out;
+  const walk = (dir: string, prefix: string
) => {
+    const manifest = join(dir, "dir.yml");
+    if (existsSync(manifest)) {
+      out.set(prefix, parseManifest(readFil
eSync(manifest, "utf8"), manifest));
+    }
+    for (const entry of readdirSync(dir, { 
withFileTypes: true })) {
+      if (entry.isDirectory()) {
+        walk(join(dir, entry.name), prefix 
? `${prefix}/${entry.name}` : entry.name);
+      }
+    }
+  };
+  walk(contentDir, "");
+  return out;
+}
+
+/** Register of a page id, from the nearest
 ancestor manifest. */
+export function registerOf(id: string, mani
fests: Map<string, DirManifest>): RegisterIn
fo {
+  const segments = id.split("/").slice(0, -
1);
+  for (let i = segments.length; i >= 0; i--
) {
+    const dir = segments.slice(0, i).join("
/");
+    const m = manifests.get(dir);
+    if (m) return { register: m.register, r
oot: dir, title: m.title ?? null };
+  }
+  return { register: DEFAULT_REGISTER, root
: null, title: null };
+}
+
+/** Display label for a register, as shown 
in chrome and search. */
+export const registerLabel = (r: Register):
 string =>
+  r === "notes" ? "NOTE" : r === "docs" ? "
DOC" : "REF";
 

diff --git a/ooknet-design/src/lib/types.ts b/ooknet-design/src/lib/types.ts
deleted file mode 100644
index 6d1a06d..0000000
--- a/ooknet-design/src/lib/types.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-// Mirrors the collection schemas in content.config.ts.
-
-export interface NoteFrontmatter {
-  title: string;
-  published: string;       // ISO date
-  status: string;
-  tags: string[];
-  seeAlso?: string[];
-  draft?: boolean;
-}
-
-export interface KbFrontmatter {
-  title: string;
-  source: string;          // vendor, e.g. "MICROSOFT LEARN"
-  url?: string;
-  retrieved?: string;      // ISO date
-  tags: string[];
-  draft?: boolean;
-}

diff --git a/ooknet-design/src/lib/types.ts 
b/ooknet-design/src/lib/types.ts
deleted file mode 100644
index 6d1a06d..0000000
--- a/ooknet-design/src/lib/types.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-// Mirrors the collection schemas in conten
t.config.ts.
-
-export interface NoteFrontmatter {
-  title: string;
-  published: string;       // ISO date
-  status: string;
-  tags: string[];
-  seeAlso?: string[];
-  draft?: boolean;
-}
-
-export interface KbFrontmatter {
-  title: string;
-  source: string;          // vendor, e.g. 
"MICROSOFT LEARN"
-  url?: string;
-  retrieved?: string;      // ISO date
-  tags: string[];
-  draft?: boolean;
-}
 

diff --git a/ooknet-design/src/lib/wikilinks.test.ts b/ooknet-design/src/lib/wikil
inks.test.ts
index 34a53c1..b024470 100644
--- a/ooknet-design/src/lib/wikilinks.test.ts
+++ b/ooknet-design/src/lib/wikilinks.test.ts
@@ -16,20 +16,22 @@ const write = (rel: string, fm: string, body = "") => {
 
 beforeAll(() => {
   dir = mkdtempSync(join(tmpdir(), "wiki-"));
-  write("notes/nix/flakes.md", 'title: "FLAKES FIELD NOTES"', "cites [[windows/ts
s]] and [[Docs Page]].");
-  write("notes/nix/draft.md", 'title: "DRAFT"\ndraft: true', "cites [[flakes fiel
d notes]].");
+  write("kb/nix/flakes.md", 'title: "FLAKES FIELD NOTES"', "cites [[windows/tss]]
 and [[Docs Page]].");
+  write("kb/nix/draft.md", 'title: "DRAFT"\ndraft: true', "cites [[flakes field n
otes]].");
   write("kb/windows/tss.md", 'title: "TSS TOOLSET"\nsource: "MS"', "literal `[[fl
akes field notes]]` only.");
-  write("docs/site/page.md", 'title: "Docs Page"', "self [[Docs Page]], live [[FL
AKES FIELD NOTES]], dead [[nope]].");
+  writeFileSync(join(dir, "kb/windows/dir.yml"), "register: reference\n");
+  write("kb/site/page.md", 'title: "Docs Page"', "self [[Docs Page]], live [[FLAK
ES FIELD NOTES]], dead [[nope]].");
+  writeFileSync(join(dir, "kb/site/dir.yml"), "register: docs\n");
 });
 afterAll(() => rmSync(dir, { recursive: true, force: true }));
 
 describe("buildWikiMap", () => {
   it("indexes every collection by id and title", () => {
     const map = buildWikiMap(dir);
-    expect(resolveWikilink("nix/flakes", map)).toBe("/notes/nix/flakes/");
-    expect(resolveWikilink("Flakes Field Notes", map)).toBe("/notes/nix/flakes/")
;
+    expect(resolveWikilink("nix/flakes", map)).toBe("/kb/nix/flakes/");
+    expect(resolveWikilink("Flakes Field Notes", map)).toBe("/kb/nix/flakes/");
     expect(resolveWikilink("windows/tss", map)).toBe("/kb/windows/tss/");
-    expect(resolveWikilink("Docs Page", map)).toBe("/docs/site/page/");
+    expect(resolveWikilink("Docs Page", map)).toBe("/kb/site/page/");
   });
 
   it("is case-insensitive and trims", () => {
@@ -47,7 +49,7 @@ describe("buildWikiMap", () => {
 });
 
 describe("wikiNodes", () => {
-  const map = new Map([["docs page", "/docs/site/page/"]]);
+  const map = new Map([["docs page", "/kb/site/page/"]]);
 
   it("leaves plain text untouched", () => {
     expect(wikiNodes("no links here", map)).toBeNull();
@@ -56,7 +58,7 @@ describe("wikiNodes", () => {
   it("splits text around a resolved link", () => {
     const nodes = wikiNodes("see [[Docs Page]] for glyphs", map)!;
     expect(nodes.map((n) => n.type)).toEqual(["text", "link", "text"]);
-    expect(nodes[1].url).toBe("/docs/site/page/");
+    expect(nodes[1].url).toBe("/kb/site/page/");
   });
 
   it("uses the alias as the label", () => {
@@ -83,34 +85,26 @@ describe("extractWikiTargets", () => {
 });
 
 describe("buildBacklinks", () => {
-  it("inverts the graph across collections", () => {
+  it("inverts the graph across the kb", () => {
     const bl = buildBacklinks(dir);
     const urls = (t: string) => (bl.get(t) ?? []).map((r) => r.url);
-    expect(urls("/kb/windows/tss/")).toEqual(["/notes/nix/flakes/"]);
-    expect(urls("/notes/nix/flakes/")).toEqual(["/docs/site/page/"]);
+    expect(urls("/kb/windows/tss/")).toEqual(["/kb/nix/flakes/"]);
+    expect(urls("/kb/nix/flakes/")).toEqual(["/kb/site/page/"]);
   });
 
-  it("labels sources with their collection", () => {
+  it("labels sources with their register", () => {
     const src = buildBacklinks(dir).get("/kb/windows/tss/")![0];
-    expect(src.label).toBe("NOTES - FLAKES FIELD NOTES");
+    expect(src.label).toBe("NOTE - FLAKES FIELD NOTES");
   });
 
   it("drops self-references, drafts, and code-span citations", () => {
     const bl = buildBacklinks(dir);
     for (const [target, list] of bl) {
       expect(list.map((r) => r.url)).not.toContain(target);
-      expect(list.map((r) => r.url)).not.toContain("/notes/nix/draft/");
+      expect(list.map((r) => r.url)).not.toContain("/kb/nix/draft/");
     }
-    // the kb body only cites in a code span - it must not be a source
-    const flakes = bl.get("/notes/nix/flakes/") ?? [];
+    // the reference body only cites in a code span - it must not be a source
+    const flakes = bl.get("/kb/nix/flakes/") ?? [];
     expect(flakes.map((r) => r.url)).not.toContain("/kb/windows/tss/");
   });
-
-  it("sorts sources notes, kb, docs", () => {
-    const order = ["/notes/", "/kb/", "/docs/"];
-    for (const list of buildBacklinks(dir).values()) {
-      const kinds = list.map((r) => order.findIndex((p) => r.url.startsWith(p)));
-      expect(kinds).toEqual([...kinds].sort());
-    }
-  });
 });

diff --git a/ooknet-design/src/lib/wikilinks
.test.ts b/ooknet-design/src/lib/wikilinks.t
est.ts
index 34a53c1..b024470 100644
--- a/ooknet-design/src/lib/wikilinks.test.t
s
+++ b/ooknet-design/src/lib/wikilinks.test.t
s
@@ -16,20 +16,22 @@ const write = (rel: stri
ng, fm: string, body = "") => {
 
 beforeAll(() => {
   dir = mkdtempSync(join(tmpdir(), "wiki-")
);
-  write("notes/nix/flakes.md", 'title: "FLA
KES FIELD NOTES"', "cites [[windows/tss]] an
d [[Docs Page]].");
-  write("notes/nix/draft.md", 'title: "DRAF
T"\ndraft: true', "cites [[flakes field note
s]].");
+  write("kb/nix/flakes.md", 'title: "FLAKES
 FIELD NOTES"', "cites [[windows/tss]] and [
[Docs Page]].");
+  write("kb/nix/draft.md", 'title: "DRAFT"\
ndraft: true', "cites [[flakes field notes]]
.");
   write("kb/windows/tss.md", 'title: "TSS T
OOLSET"\nsource: "MS"', "literal `[[flakes f
ield notes]]` only.");
-  write("docs/site/page.md", 'title: "Docs 
Page"', "self [[Docs Page]], live [[FLAKES F
IELD NOTES]], dead [[nope]].");
+  writeFileSync(join(dir, "kb/windows/dir.y
ml"), "register: reference\n");
+  write("kb/site/page.md", 'title: "Docs Pa
ge"', "self [[Docs Page]], live [[FLAKES FIE
LD NOTES]], dead [[nope]].");
+  writeFileSync(join(dir, "kb/site/dir.yml"
), "register: docs\n");
 });
 afterAll(() => rmSync(dir, { recursive: tru
e, force: true }));
 
 describe("buildWikiMap", () => {
   it("indexes every collection by id and ti
tle", () => {
     const map = buildWikiMap(dir);
-    expect(resolveWikilink("nix/flakes", ma
p)).toBe("/notes/nix/flakes/");
-    expect(resolveWikilink("Flakes Field No
tes", map)).toBe("/notes/nix/flakes/");
+    expect(resolveWikilink("nix/flakes", ma
p)).toBe("/kb/nix/flakes/");
+    expect(resolveWikilink("Flakes Field No
tes", map)).toBe("/kb/nix/flakes/");
     expect(resolveWikilink("windows/tss", m
ap)).toBe("/kb/windows/tss/");
-    expect(resolveWikilink("Docs Page", map
)).toBe("/docs/site/page/");
+    expect(resolveWikilink("Docs Page", map
)).toBe("/kb/site/page/");
   });
 
   it("is case-insensitive and trims", () =>
 {
@@ -47,7 +49,7 @@ describe("buildWikiMap", (
) => {
 });
 
 describe("wikiNodes", () => {
-  const map = new Map([["docs page", "/docs
/site/page/"]]);
+  const map = new Map([["docs page", "/kb/s
ite/page/"]]);
 
   it("leaves plain text untouched", () => {
     expect(wikiNodes("no links here", map))
.toBeNull();
@@ -56,7 +58,7 @@ describe("wikiNodes", () =
> {
   it("splits text around a resolved link", 
() => {
     const nodes = wikiNodes("see [[Docs Pag
e]] for glyphs", map)!;
     expect(nodes.map((n) => n.type)).toEqua
l(["text", "link", "text"]);
-    expect(nodes[1].url).toBe("/docs/site/p
age/");
+    expect(nodes[1].url).toBe("/kb/site/pag
e/");
   });
 
   it("uses the alias as the label", () => {
@@ -83,34 +85,26 @@ describe("extractWikiTar
gets", () => {
 });
 
 describe("buildBacklinks", () => {
-  it("inverts the graph across collections"
, () => {
+  it("inverts the graph across the kb", () 
=> {
     const bl = buildBacklinks(dir);
     const urls = (t: string) => (bl.get(t) 
?? []).map((r) => r.url);
-    expect(urls("/kb/windows/tss/")).toEqua
l(["/notes/nix/flakes/"]);
-    expect(urls("/notes/nix/flakes/")).toEq
ual(["/docs/site/page/"]);
+    expect(urls("/kb/windows/tss/")).toEqua
l(["/kb/nix/flakes/"]);
+    expect(urls("/kb/nix/flakes/")).toEqual
(["/kb/site/page/"]);
   });
 
-  it("labels sources with their collection"
, () => {
+  it("labels sources with their register", 
() => {
     const src = buildBacklinks(dir).get("/k
b/windows/tss/")![0];
-    expect(src.label).toBe("NOTES - FLAKES 
FIELD NOTES");
+    expect(src.label).toBe("NOTE - FLAKES F
IELD NOTES");
   });
 
   it("drops self-references, drafts, and co
de-span citations", () => {
     const bl = buildBacklinks(dir);
     for (const [target, list] of bl) {
       expect(list.map((r) => r.url)).not.to
Contain(target);
-      expect(list.map((r) => r.url)).not.to
Contain("/notes/nix/draft/");
+      expect(list.map((r) => r.url)).not.to
Contain("/kb/nix/draft/");
     }
-    // the kb body only cites in a code spa
n - it must not be a source
-    const flakes = bl.get("/notes/nix/flake
s/") ?? [];
+    // the reference body only cites in a c
ode span - it must not be a source
+    const flakes = bl.get("/kb/nix/flakes/"
) ?? [];
     expect(flakes.map((r) => r.url)).not.to
Contain("/kb/windows/tss/");
   });
-
-  it("sorts sources notes, kb, docs", () =>
 {
-    const order = ["/notes/", "/kb/", "/doc
s/"];
-    for (const list of buildBacklinks(dir).
values()) {
-      const kinds = list.map((r) => order.f
indIndex((p) => r.url.startsWith(p)));
-      expect(kinds).toEqual([...kinds].sort
());
-    }
-  });
 });
 

diff --git a/ooknet-design/src/lib/wikilinks.ts b/ooknet-design/src/lib/wikilinks.
ts
index 9aa1a76..22884f1 100644
--- a/ooknet-design/src/lib/wikilinks.ts
+++ b/ooknet-design/src/lib/wikilinks.ts
@@ -1,9 +1,9 @@
 // Wikilink resolution. The map is built by scanning content frontmatter
 // directly (remark plugins run outside astro:content), keyed by every
-// name an author might reach for: file id and title, across all three
-// collections.
+// name an author might reach for: file id and title.
 import { readdirSync, readFileSync } from "node:fs";
 import { join } from "node:path";
+import { readManifests, registerOf, registerLabel } from "./registers";
 
 export type WikiMap = Map<string, string>;
 
@@ -24,9 +24,6 @@ function mdFiles(dir: string, prefix = ""): string[] {
   return out;
 }
 
-const COLLECTIONS = ["notes", "kb", "docs"] as const;
-export type Collection = (typeof COLLECTIONS)[number];
-
 function scan(map: WikiMap, dir: string, urlFor: (id: string) => string) {
   let files: string[];
   try {
@@ -47,9 +44,7 @@ function scan(map: WikiMap, dir: string, urlFor: (id: string) =>
 string) {
 
 export function buildWikiMap(contentDir = "src/content"): WikiMap {
   const map: WikiMap = new Map();
-  for (const c of COLLECTIONS) {
-    scan(map, join(contentDir, c), (id) => `/${c}/${id}/`);
-  }
+  scan(map, join(contentDir, "kb"), (id) => `/kb/${id}/`);
   return map;
 }
 
@@ -110,23 +105,17 @@ function scanSources(
 }
 
 /** Target url ? pages whose body links to it. Dead links and
- *  self-references are dropped; sources sort notes, kb, docs. */
+ *  self-references are dropped; sources carry their register label. */
 export function buildBacklinks(contentDir = "src/content"): Map<string, Backlink[
]> {
   const map = buildWikiMap(contentDir);
+  const manifests = readManifests(join(contentDir, "kb"));
   const out = new Map<string, Backlink[]>();
-  for (const c of COLLECTIONS) {
-    scanSources(out, map, join(contentDir, c), (id) => `/${c}/${id}/`, (id, fm) =
> {
-      const title = fmField(fm, "title") ?? id;
-      return `${c.toUpperCase()} - ${title.toUpperCase()}`;
-    });
-  }
-  const group = (u: string) => COLLECTIONS.findIndex((c) => u.startsWith(`/${c}/`
));
+  scanSources(out, map, join(contentDir, "kb"), (id) => `/kb/${id}/`, (id, fm) =>
 {
+    const title = fmField(fm, "title") ?? id;
+    return `${registerLabel(registerOf(id, manifests).register)} - ${title.toUppe
rCase()}`;
+  });
   for (const list of out.values()) {
-    list.sort(
-      (a, b) =>
-        group(a.url) - group(b.url) ||
-        a.label.localeCompare(b.label, "en", { numeric: true }),
-    );
+    list.sort((a, b) => a.label.localeCompare(b.label, "en", { numeric: true }));
   }
   return out;
 }

diff --git a/ooknet-design/src/lib/wikilinks
.ts b/ooknet-design/src/lib/wikilinks.ts
index 9aa1a76..22884f1 100644
--- a/ooknet-design/src/lib/wikilinks.ts
+++ b/ooknet-design/src/lib/wikilinks.ts
@@ -1,9 +1,9 @@
 // Wikilink resolution. The map is built by
 scanning content frontmatter
 // directly (remark plugins run outside ast
ro:content), keyed by every
-// name an author might reach for: file id 
and title, across all three
-// collections.
+// name an author might reach for: file id 
and title.
 import { readdirSync, readFileSync } from "
node:fs";
 import { join } from "node:path";
+import { readManifests, registerOf, registe
rLabel } from "./registers";
 
 export type WikiMap = Map<string, string>;
 
@@ -24,9 +24,6 @@ function mdFiles(dir: stri
ng, prefix = ""): string[] {
   return out;
 }
 
-const COLLECTIONS = ["notes", "kb", "docs"]
 as const;
-export type Collection = (typeof COLLECTION
S)[number];
-
 function scan(map: WikiMap, dir: string, ur
lFor: (id: string) => string) {
   let files: string[];
   try {
@@ -47,9 +44,7 @@ function scan(map: WikiMap
, dir: string, urlFor: (id: string) => strin
g) {
 
 export function buildWikiMap(contentDir = "
src/content"): WikiMap {
   const map: WikiMap = new Map();
-  for (const c of COLLECTIONS) {
-    scan(map, join(contentDir, c), (id) => 
`/${c}/${id}/`);
-  }
+  scan(map, join(contentDir, "kb"), (id) =>
 `/kb/${id}/`);
   return map;
 }
 
@@ -110,23 +105,17 @@ function scanSources(
 }
 
 /** Target url ? pages whose body links to 
it. Dead links and
- *  self-references are dropped; sources so
rt notes, kb, docs. */
+ *  self-references are dropped; sources ca
rry their register label. */
 export function buildBacklinks(contentDir =
 "src/content"): Map<string, Backlink[]> {
   const map = buildWikiMap(contentDir);
+  const manifests = readManifests(join(cont
entDir, "kb"));
   const out = new Map<string, Backlink[]>()
;
-  for (const c of COLLECTIONS) {
-    scanSources(out, map, join(contentDir, 
c), (id) => `/${c}/${id}/`, (id, fm) => {
-      const title = fmField(fm, "title") ??
 id;
-      return `${c.toUpperCase()} - ${title.
toUpperCase()}`;
-    });
-  }
-  const group = (u: string) => COLLECTIONS.
findIndex((c) => u.startsWith(`/${c}/`));
+  scanSources(out, map, join(contentDir, "k
b"), (id) => `/kb/${id}/`, (id, fm) => {
+    const title = fmField(fm, "title") ?? i
d;
+    return `${registerLabel(registerOf(id, 
manifests).register)} - ${title.toUpperCase(
)}`;
+  });
   for (const list of out.values()) {
-    list.sort(
-      (a, b) =>
-        group(a.url) - group(b.url) ||
-        a.label.localeCompare(b.label, "en"
, { numeric: true }),
-    );
+    list.sort((a, b) => a.label.localeCompa
re(b.label, "en", { numeric: true }));
   }
   return out;
 }
 

diff --git a/ooknet-design/src/pages/about.astro b/ooknet-design/src/pages/about.a
stro
index 23a1374..1eabc0e 100644
--- a/ooknet-design/src/pages/about.astro
+++ b/ooknet-design/src/pages/about.astro
@@ -4,30 +4,16 @@ import TopHeader from "../components/TopHeader/TopHeader.astro";
 import Frame from "../components/Frame/Frame.astro";
 import Pre from "../components/Pre/Pre.astro";
 import ArticleFooter from "../components/ArticleFooter/ArticleFooter.astro";
-import { kv, row2, rule, ruleD } from "../lib/ascii";
+import { row2, ruleD } from "../lib/ascii";
 
 const buildHeader = (w: number) =>
   [row2("ABOUT THIS INSTALLATION", "OOKNET", w), ruleD(w)].join("\n");
 
-const buildColophon = (w: number) =>
-  [
-    row2("COLOPHON", "", w),
-    rule(w),
-    kv("OPERATOR",   "LIAM W",                          12, w),
-    kv("ENGINE",     "astro, rendered static",          12, w),
-    kv("FRAMES",     "strings, built at compile time",  12, w),
-    kv("BODY TYPE",  "IBM Plex Mono",                   12, w),
-    kv("FRAME TYPE", "Cascadia Mono",                   12, w),
-    kv("GRID",       "86 cells / 48 on paper tape",     12, w),
-    ruleD(w),
-  ].join("\n");
 ---
 <ArticleLayout title="OOKNET KB - About"
   description="OOKNET - a personal knowledge base rendered as pure text on a mono
space grid.">
   <TopHeader />
   <Frame build={buildHeader} />
   <Pre>{" "}</Pre>
-  <Frame build={buildColophon} />
-  <Pre>{" "}</Pre>
   <ArticleFooter />
 </ArticleLayout>

diff --git a/ooknet-design/src/pages/about.a
stro b/ooknet-design/src/pages/about.astro
index 23a1374..1eabc0e 100644
--- a/ooknet-design/src/pages/about.astro
+++ b/ooknet-design/src/pages/about.astro
@@ -4,30 +4,16 @@ import TopHeader from "../
components/TopHeader/TopHeader.astro";
 import Frame from "../components/Frame/Fram
e.astro";
 import Pre from "../components/Pre/Pre.astr
o";
 import ArticleFooter from "../components/Ar
ticleFooter/ArticleFooter.astro";
-import { kv, row2, rule, ruleD } from "../l
ib/ascii";
+import { row2, ruleD } from "../lib/ascii";
 
 const buildHeader = (w: number) =>
   [row2("ABOUT THIS INSTALLATION", "OOKNET"
, w), ruleD(w)].join("\n");
 
-const buildColophon = (w: number) =>
-  [
-    row2("COLOPHON", "", w),
-    rule(w),
-    kv("OPERATOR",   "LIAM W",             
             12, w),
-    kv("ENGINE",     "astro, rendered stati
c",          12, w),
-    kv("FRAMES",     "strings, built at com
pile time",  12, w),
-    kv("BODY TYPE",  "IBM Plex Mono",      
             12, w),
-    kv("FRAME TYPE", "Cascadia Mono",      
             12, w),
-    kv("GRID",       "86 cells / 48 on pape
r tape",     12, w),
-    ruleD(w),
-  ].join("\n");
 ---
 <ArticleLayout title="OOKNET KB - About"
   description="OOKNET - a personal knowledg
e base rendered as pure text on a monospace 
grid.">
   <TopHeader />
   <Frame build={buildHeader} />
   <Pre>{" "}</Pre>
-  <Frame build={buildColophon} />
-  <Pre>{" "}</Pre>
   <ArticleFooter />
 </ArticleLayout>
 

diff --git a/ooknet-design/src/pages/docs/[...slug].astro b/ooknet-design/src/page
s/docs/[...slug].astro
deleted file mode 100644
index 3565b79..0000000
--- a/ooknet-design/src/pages/docs/[...slug].astro
+++ /dev/null
@@ -1,102 +0,0 @@
----
-import { getCollection, render, type CollectionEntry } from "astro:content";
-import ArticleLayout from "../../layouts/ArticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/TopHeader/TopHeader.astro";
-import Frame from "../../components/Frame/Frame.astro";
-import Pre from "../../components/Pre/Pre.astro";
-import Breadcrumbs from "../../components/Breadcrumbs/Breadcrumbs.astro";
-import TableOfContents from "../../components/TableOfContents/TableOfContents.ast
ro";
-import Prose from "../../components/Prose/Prose.astro";
-import ArticlePagination from "../../components/ArticlePagination/ArticlePaginati
on.astro";
-import type { PageLink } from "../../components/ArticlePagination/ArticlePaginati
on";
-import DocsNav from "../../components/DocsNav/DocsNav.astro";
-import ArticleBacklinks from "../../components/ArticleBacklinks/ArticleBacklinks.
astro";
-import { box, center, len, rule, ruleD, row2, wrap } from "../../lib/ascii";
-import { published, topSegment } from "../../lib/notes";
-import { docsOrder, projectLabel, type DocEntry } from "../../lib/docs";
-import { buildBacklinks, type Backlink } from "../../lib/wikilinks";
-import { describeBody } from "../../lib/meta";
-
-export async function getStaticPaths() {
-  const docs = await getCollection("docs", published);
-  const backlinks = buildBacklinks();
-  const ordered = docsOrder(
-    docs.map((d) => ({ id: d.id, title: d.data.title, order: d.data.order })),
-  );
-  const byId = new Map(docs.map((d) => [d.id, d]));
-
-  // Pagination labels are titles, truncated to keep the buttons on the
-  // narrow frame.
-  const link = (id?: string): PageLink | null => {
-    if (!id) return null;
-    const title = byId.get(id)!.data.title.toUpperCase();
-    const note = len(title) > 18 ? [...title].slice(0, 15).join("") + "..." : tit
le;
-    return { href: `/docs/${id}/`, note };
-  };
-
-  // Docs are siloed by project: the index card and prev/next never
-  // cross into another project's pages.
-  return ordered.map((d) => {
-    const project = topSegment(d.id);
-    const projectDocs = ordered.filter((p) => topSegment(p.id) === project);
-    const i = projectDocs.findIndex((p) => p.id === d.id);
-    const strip = (id: string) => id.slice(project.length + 1) || id;
-    return {
-      params: { slug: d.id },
-      props: {
-        doc: byId.get(d.id)!,
-        prev: link(projectDocs[i - 1]?.id),
-        next: link(projectDocs[i + 1]?.id),
-        all: projectDocs.map((p) => ({ ...p, id: strip(p.id) })),
-        currentId: strip(d.id),
-        refs: backlinks.get(`/docs/${d.id}/`) ?? [],
-      },
-    };
-  });
-}
-
-interface Props {
-  doc: CollectionEntry<"docs">;
-  prev: PageLink | null;
-  next: PageLink | null;
-  all: DocEntry[];
-  currentId: string;
-  refs: Backlink[];
-}
-const { doc, prev, next, all, currentId, refs } = Astro.props as Props;
-const project = topSegment(doc.id);
-const { Content, headings } = await render(doc);
-const description =
-  doc.data.description ?? (describeBody(doc.body ?? "") || `OOKNET documentation 
- ${doc.data.title}.`);
-
-const buildTitleCard = (w: number) => {
-  const cardW = Math.min(46, w - 8);
-  const card = box({
-    width: cardW,
-    lines: ["OOKNET DOCS", ...wrap(doc.data.title.toUpperCase(), cardW - 4).split
("\n")],
-    align: "center",
-  });
-  return card.split("\n").map((line) => center(line, w)).join("\n");
-};
----
-<ArticleLayout title={`DOCS - ${doc.data.title}`} description={description} type=
"article">
-  <TopHeader />
-  <Frame build={(w) => [row2("OOKNET DOCUMENTATION", `PROJECT: ${projectLabel(pro
ject)}`, w), rule(w)].join("\n")} />
-  <Pre>{" "}</Pre>
-  <Frame build={buildTitleCard} />
-  <Pre>{" "}</Pre>
-  <Breadcrumbs filed={`/DOCS/${doc.id.toUpperCase()}`} />
-  <Frame build={(w) => ruleD(w)} />
-  <DocsNav docs={all} currentId={currentId} title={projectLabel(project)} base={`
/docs/${project}/`} />
-  {headings.length >= 3 && <TableOfContents headings={headings} />}
-  <Prose><Content /></Prose>
-  {refs.length > 0 && (
-    <Fragment>
-      <Frame build={(w) => rule(w)} />
-      <ArticleBacklinks refs={refs} />
-    </Fragment>
-  )}
-  <Pre>{" "}</Pre>
-  <ArticlePagination prev={prev} next={next} />
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>

diff --git a/ooknet-design/src/pages/docs/[.
..slug].astro b/ooknet-design/src/pages/docs
/[...slug].astro
deleted file mode 100644
index 3565b79..0000000
--- a/ooknet-design/src/pages/docs/[...slug]
.astro
+++ /dev/null
@@ -1,102 +0,0 @@
----
-import { getCollection, render, type Collec
tionEntry } from "astro:content";
-import ArticleLayout from "../../layouts/Ar
ticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/Top
Header/TopHeader.astro";
-import Frame from "../../components/Frame/F
rame.astro";
-import Pre from "../../components/Pre/Pre.a
stro";
-import Breadcrumbs from "../../components/B
readcrumbs/Breadcrumbs.astro";
-import TableOfContents from "../../componen
ts/TableOfContents/TableOfContents.astro";
-import Prose from "../../components/Prose/P
rose.astro";
-import ArticlePagination from "../../compon
ents/ArticlePagination/ArticlePagination.ast
ro";
-import type { PageLink } from "../../compon
ents/ArticlePagination/ArticlePagination";
-import DocsNav from "../../components/DocsN
av/DocsNav.astro";
-import ArticleBacklinks from "../../compone
nts/ArticleBacklinks/ArticleBacklinks.astro"
;
-import { box, center, len, rule, ruleD, row
2, wrap } from "../../lib/ascii";
-import { published, topSegment } from "../.
./lib/notes";
-import { docsOrder, projectLabel, type DocE
ntry } from "../../lib/docs";
-import { buildBacklinks, type Backlink } fr
om "../../lib/wikilinks";
-import { describeBody } from "../../lib/met
a";
-
-export async function getStaticPaths() {
-  const docs = await getCollection("docs", 
published);
-  const backlinks = buildBacklinks();
-  const ordered = docsOrder(
-    docs.map((d) => ({ id: d.id, title: d.d
ata.title, order: d.data.order })),
-  );
-  const byId = new Map(docs.map((d) => [d.i
d, d]));
-
-  // Pagination labels are titles, truncate
d to keep the buttons on the
-  // narrow frame.
-  const link = (id?: string): PageLink | nu
ll => {
-    if (!id) return null;
-    const title = byId.get(id)!.data.title.
toUpperCase();
-    const note = len(title) > 18 ? [...titl
e].slice(0, 15).join("") + "..." : title;
-    return { href: `/docs/${id}/`, note };
-  };
-
-  // Docs are siloed by project: the index 
card and prev/next never
-  // cross into another project's pages.
-  return ordered.map((d) => {
-    const project = topSegment(d.id);
-    const projectDocs = ordered.filter((p) 
=> topSegment(p.id) === project);
-    const i = projectDocs.findIndex((p) => 
p.id === d.id);
-    const strip = (id: string) => id.slice(
project.length + 1) || id;
-    return {
-      params: { slug: d.id },
-      props: {
-        doc: byId.get(d.id)!,
-        prev: link(projectDocs[i - 1]?.id),
-        next: link(projectDocs[i + 1]?.id),
-        all: projectDocs.map((p) => ({ ...p
, id: strip(p.id) })),
-        currentId: strip(d.id),
-        refs: backlinks.get(`/docs/${d.id}/
`) ?? [],
-      },
-    };
-  });
-}
-
-interface Props {
-  doc: CollectionEntry<"docs">;
-  prev: PageLink | null;
-  next: PageLink | null;
-  all: DocEntry[];
-  currentId: string;
-  refs: Backlink[];
-}
-const { doc, prev, next, all, currentId, re
fs } = Astro.props as Props;
-const project = topSegment(doc.id);
-const { Content, headings } = await render(
doc);
-const description =
-  doc.data.description ?? (describeBody(doc
.body ?? "") || `OOKNET documentation - ${do
c.data.title}.`);
-
-const buildTitleCard = (w: number) => {
-  const cardW = Math.min(46, w - 8);
-  const card = box({
-    width: cardW,
-    lines: ["OOKNET DOCS", ...wrap(doc.data
.title.toUpperCase(), cardW - 4).split("\n")
],
-    align: "center",
-  });
-  return card.split("\n").map((line) => cen
ter(line, w)).join("\n");
-};
----
-<ArticleLayout title={`DOCS - ${doc.data.ti
tle}`} description={description} type="artic
le">
-  <TopHeader />
-  <Frame build={(w) => [row2("OOKNET DOCUME
NTATION", `PROJECT: ${projectLabel(project)}
`, w), rule(w)].join("\n")} />
-  <Pre>{" "}</Pre>
-  <Frame build={buildTitleCard} />
-  <Pre>{" "}</Pre>
-  <Breadcrumbs filed={`/DOCS/${doc.id.toUpp
erCase()}`} />
-  <Frame build={(w) => ruleD(w)} />
-  <DocsNav docs={all} currentId={currentId}
 title={projectLabel(project)} base={`/docs/
${project}/`} />
-  {headings.length >= 3 && <TableOfContents
 headings={headings} />}
-  <Prose><Content /></Prose>
-  {refs.length > 0 && (
-    <Fragment>
-      <Frame build={(w) => rule(w)} />
-      <ArticleBacklinks refs={refs} />
-    </Fragment>
-  )}
-  <Pre>{" "}</Pre>
-  <ArticlePagination prev={prev} next={next
} />
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>
 

diff --git a/ooknet-design/src/pages/docs/index.astro b/ooknet-design/src/pages/do
cs/index.astro
deleted file mode 100644
index a0d429f..0000000
--- a/ooknet-design/src/pages/docs/index.astro
+++ /dev/null
@@ -1,27 +0,0 @@
----
-import { getCollection } from "astro:content";
-import ArticleLayout from "../../layouts/ArticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/TopHeader/TopHeader.astro";
-import Frame from "../../components/Frame/Frame.astro";
-import Pre from "../../components/Pre/Pre.astro";
-import Tree from "../../components/Tree/Tree.astro";
-import { rule, ruleD, row2 } from "../../lib/ascii";
-import { published } from "../../lib/notes";
-import { docsTree } from "../../lib/docs";
-
-const docs = (await getCollection("docs", published)).map((d) => ({
-  id: d.id,
-  title: d.data.title,
-  order: d.data.order,
-}));
-const tree = docsTree(docs);
----
-<ArticleLayout title="OOKNET KB - Docs"
-  description="OOKNET documentation - how the ASCII framework, the markdown pipel
ine, and the filing system work.">
-  <TopHeader />
-  <Frame build={(w) => [row2("DOCUMENTATION", `${docs.length} pages`, w), ruleD(w
)].join("\n")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={tree} />
-  <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>

diff --git a/ooknet-design/src/pages/docs/in
dex.astro b/ooknet-design/src/pages/docs/ind
ex.astro
deleted file mode 100644
index a0d429f..0000000
--- a/ooknet-design/src/pages/docs/index.ast
ro
+++ /dev/null
@@ -1,27 +0,0 @@
----
-import { getCollection } from "astro:conten
t";
-import ArticleLayout from "../../layouts/Ar
ticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/Top
Header/TopHeader.astro";
-import Frame from "../../components/Frame/F
rame.astro";
-import Pre from "../../components/Pre/Pre.a
stro";
-import Tree from "../../components/Tree/Tre
e.astro";
-import { rule, ruleD, row2 } from "../../li
b/ascii";
-import { published } from "../../lib/notes"
;
-import { docsTree } from "../../lib/docs";
-
-const docs = (await getCollection("docs", p
ublished)).map((d) => ({
-  id: d.id,
-  title: d.data.title,
-  order: d.data.order,
-}));
-const tree = docsTree(docs);
----
-<ArticleLayout title="OOKNET KB - Docs"
-  description="OOKNET documentation - how t
he ASCII framework, the markdown pipeline, a
nd the filing system work.">
-  <TopHeader />
-  <Frame build={(w) => [row2("DOCUMENTATION
", `${docs.length} pages`, w), ruleD(w)].joi
n("\n")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={tree} />
-  <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>
 

diff --git a/ooknet-design/src/pages/index.astro b/ooknet-design/src/pages/index.a
stro
index 9ee7dcf..0a9a77b 100644
--- a/ooknet-design/src/pages/index.astro
+++ b/ooknet-design/src/pages/index.astro
@@ -5,40 +5,24 @@ import TopHeader from "../components/TopHeader/TopHeader.astro";
 import Frame from "../components/Frame/Frame.astro";
 import Pre from "../components/Pre/Pre.astro";
 import Tree from "../components/Tree/Tree.astro";
-import { rule, ruleD, row2 } from "../lib/ascii";
+import ArticleFooter from "../components/ArticleFooter/ArticleFooter.astro";
+import { ruleD, row2 } from "../lib/ascii";
 import { published } from "../lib/notes";
 import { docsTree } from "../lib/docs";
 
-const section = (title: string, right: string) => (w: number) =>
-  [row2(title, right, w), ruleD(w)].join("\n");
-
-const notes = await getCollection("notes", published);
-const kb = await getCollection("kb", published);
-const docs = await getCollection("docs", published);
-
-const asTree = (entries: { id: string; data: { title: string; order?: number } }[
], base: string) =>
-  docsTree(entries.map((e) => ({ id: e.id, title: e.data.title, order: e.data.ord
er })), undefined, base);
+const entries = await getCollection("kb", published);
+const tree = docsTree(
+  entries.map((e) => ({ id: e.id, title: e.data.title, order: e.data.order })),
+  undefined,
+  "/kb/",
+);
 ---
 <ArticleLayout title="OOKNET KB - Index"
-  description="OOKNET archive - personal notes by subject, a credited knowledge b
ase, and project documentation, cross-linked.">
+  description="OOKNET - a text-mode knowledge base on a strict monospace grid.">
   <TopHeader />
-  <Frame build={(w) => [row2("INDEX OF THE ARCHIVE", `${notes.length + kb.length 
+ docs.length} PAGES`, w), ruleD(w)].join("\n")} />
-  <Pre>{" "}</Pre>
-
-  <Frame build={section("NOTES", "personal writing, by subject")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={asTree(notes, "/notes/")} />
+  <Frame build={(w) => [row2("INDEX OF THE ARCHIVE", `${entries.length} PAGES`, w
), ruleD(w)].join("\n")} />
   <Pre>{" "}</Pre>
-
-  <Frame build={section("KNOWLEDGE BASE", "vendor material, credited")} />
+  <Tree nodes={tree} />
   <Pre>{" "}</Pre>
-  <Tree nodes={asTree(kb, "/kb/")} />
-  <Pre>{" "}</Pre>
-
-  <Frame build={section("DOCS", "documentation, by project")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={asTree(docs, "/docs/")} />
-  <Pre>{" "}</Pre>
-
-  <Frame build={(w) => rule(w)} />
+  <ArticleFooter />
 </ArticleLayout>

diff --git a/ooknet-design/src/pages/index.a
stro b/ooknet-design/src/pages/index.astro
index 9ee7dcf..0a9a77b 100644
--- a/ooknet-design/src/pages/index.astro
+++ b/ooknet-design/src/pages/index.astro
@@ -5,40 +5,24 @@ import TopHeader from "../
components/TopHeader/TopHeader.astro";
 import Frame from "../components/Frame/Fram
e.astro";
 import Pre from "../components/Pre/Pre.astr
o";
 import Tree from "../components/Tree/Tree.a
stro";
-import { rule, ruleD, row2 } from "../lib/a
scii";
+import ArticleFooter from "../components/Ar
ticleFooter/ArticleFooter.astro";
+import { ruleD, row2 } from "../lib/ascii";
 import { published } from "../lib/notes";
 import { docsTree } from "../lib/docs";
 
-const section = (title: string, right: stri
ng) => (w: number) =>
-  [row2(title, right, w), ruleD(w)].join("\
n");
-
-const notes = await getCollection("notes", 
published);
-const kb = await getCollection("kb", publis
hed);
-const docs = await getCollection("docs", pu
blished);
-
-const asTree = (entries: { id: string; data
: { title: string; order?: number } }[], bas
e: string) =>
-  docsTree(entries.map((e) => ({ id: e.id, 
title: e.data.title, order: e.data.order }))
, undefined, base);
+const entries = await getCollection("kb", p
ublished);
+const tree = docsTree(
+  entries.map((e) => ({ id: e.id, title: e.
data.title, order: e.data.order })),
+  undefined,
+  "/kb/",
+);
 ---
 <ArticleLayout title="OOKNET KB - Index"
-  description="OOKNET archive - personal no
tes by subject, a credited knowledge base, a
nd project documentation, cross-linked.">
+  description="OOKNET - a text-mode knowled
ge base on a strict monospace grid.">
   <TopHeader />
-  <Frame build={(w) => [row2("INDEX OF THE 
ARCHIVE", `${notes.length + kb.length + docs
.length} PAGES`, w), ruleD(w)].join("\n")} /
>
-  <Pre>{" "}</Pre>
-
-  <Frame build={section("NOTES", "personal 
writing, by subject")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={asTree(notes, "/notes/")} />
+  <Frame build={(w) => [row2("INDEX OF THE 
ARCHIVE", `${entries.length} PAGES`, w), rul
eD(w)].join("\n")} />
   <Pre>{" "}</Pre>
-
-  <Frame build={section("KNOWLEDGE BASE", "
vendor material, credited")} />
+  <Tree nodes={tree} />
   <Pre>{" "}</Pre>
-  <Tree nodes={asTree(kb, "/kb/")} />
-  <Pre>{" "}</Pre>
-
-  <Frame build={section("DOCS", "documentat
ion, by project")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={asTree(docs, "/docs/")} />
-  <Pre>{" "}</Pre>
-
-  <Frame build={(w) => rule(w)} />
+  <ArticleFooter />
 </ArticleLayout>
 

diff --git a/ooknet-design/src/pages/kb/[...slug].astro b/ooknet-design/src/pages/
kb/[...slug].astro
index 4e2c5f5..fb50461 100644
--- a/ooknet-design/src/pages/kb/[...slug].astro
+++ b/ooknet-design/src/pages/kb/[...slug].astro
@@ -1,4 +1,7 @@
 ---
+// The one content route. What chrome a page gets - note card, project
+// docs with a scoped index, or vendor reference copy - is decided by
+// its register (nearest dir.yml; see lib/registers.ts).
 import { getCollection, render, type CollectionEntry } from "astro:content";
 import ArticleLayout from "../../layouts/ArticleLayout/ArticleLayout.astro";
 import TopHeader from "../../components/TopHeader/TopHeader.astro";
@@ -8,70 +11,144 @@ import ArticleMeta from "../../components/ArticleMeta/ArticleM
eta.astro";
 import type { MetaEntry } from "../../components/ArticleMeta/ArticleMeta";
 import TableOfContents from "../../components/TableOfContents/TableOfContents.ast
ro";
 import Breadcrumbs from "../../components/Breadcrumbs/Breadcrumbs.astro";
+import ArticleSeeAlso from "../../components/ArticleSeeAlso/ArticleSeeAlso.astro"
;
 import ArticleBacklinks from "../../components/ArticleBacklinks/ArticleBacklinks.
astro";
 import ArticlePagination from "../../components/ArticlePagination/ArticlePaginati
on.astro";
 import ArticleFooter from "../../components/ArticleFooter/ArticleFooter.astro";
+import DocsNav from "../../components/DocsNav/DocsNav.astro";
 import Prose from "../../components/Prose/Prose.astro";
 import Pre from "../../components/Pre/Pre.astro";
 import type { PageLink } from "../../components/ArticlePagination/ArticlePaginati
on";
-import { published } from "../../lib/notes";
-import { docsOrder } from "../../lib/docs";
+import { published, topSegment } from "../../lib/notes";
+import { docsOrder, type DocEntry } from "../../lib/docs";
+import { readManifests, registerOf, type RegisterInfo } from "../../lib/registers
";
 import { buildBacklinks, type Backlink } from "../../lib/wikilinks";
 import { describeBody } from "../../lib/meta";
 import { len } from "../../lib/ascii";
 
 export async function getStaticPaths() {
   const entries = await getCollection("kb", published);
+  const manifests = readManifests();
   const backlinks = buildBacklinks();
-  const ordered = docsOrder(entries.map((e) => ({ id: e.id, title: e.data.title }
)));
-  const byId = new Map(entries.map((e) => [e.id, e]));
-  const link = (id?: string): PageLink | null => {
-    if (!id) return null;
-    const t = byId.get(id)!.data.title;
+  const info = new Map(entries.map((e) => [e.id, registerOf(e.id, manifests)]));
+
+  const link = (e?: { id: string; title: string }): PageLink | null => {
+    if (!e) return null;
+    const t = e.title.toUpperCase();
     const note = len(t) > 18 ? [...t].slice(0, 15).join("") + "..." : t;
-    return { href: `/kb/${id}/`, note };
+    return { href: `/kb/${e.id}/`, note };
   };
-  return ordered.map((e, i) => ({
-    params: { slug: e.id },
-    props: {
-      entry: byId.get(e.id)!,
-      prev: link(ordered[i - 1]?.id),
-      next: link(ordered[i + 1]?.id),
-      refs: backlinks.get(`/kb/${e.id}/`) ?? [],
-    },
-  }));
+
+  // notes page in chronological order across the whole register;
+  // docs and reference page within their manifest root, reading order
+  const notes = entries
+    .filter((e) => info.get(e.id)!.register === "notes")
+    .map((e) => ({ id: e.id, title: e.data.title, date: e.data.published ?? "" })
)
+    .sort((a, b) => a.date.localeCompare(b.date) || a.id.localeCompare(b.id));
+
+  const rooted = new Map<string, DocEntry[]>();
+  for (const e of entries) {
+    const i = info.get(e.id)!;
+    if (i.register === "notes") continue;
+    const key = `${i.register}:${i.root ?? ""}`;
+    if (!rooted.has(key)) rooted.set(key, []);
+    rooted.get(key)!.push({ id: e.id, title: e.data.title, order: e.data.order })
;
+  }
+  for (const [k, list] of rooted) rooted.set(k, docsOrder(list));
+
+  return entries.map((entry) => {
+    const i = info.get(entry.id)!;
+    let prev: PageLink | null = null;
+    let next: PageLink | null = null;
+    let nav: DocEntry[] = [];
+    if (i.register === "notes") {
+      const at = notes.findIndex((n) => n.id === entry.id);
+      prev = link(notes[at - 1]);
+      next = link(notes[at + 1]);
+    } else {
+      const group = rooted.get(`${i.register}:${i.root ?? ""}`)!;
+      const at = group.findIndex((d) => d.id === entry.id);
+      prev = link(group[at - 1] && { id: group[at - 1].id, title: group[at - 1].t
itle });
+      next = link(group[at + 1] && { id: group[at + 1].id, title: group[at + 1].t
itle });
+      if (i.register === "docs") {
+        const strip = (id: string) =>
+          i.root ? id.slice(i.root.length + 1) || id : id;
+        nav = group.map((d) => ({ ...d, id: strip(d.id) }));
+      }
+    }
+    return {
+      params: { slug: entry.id },
+      props: {
+        entry,
+        info: i,
+        prev,
+        next,
+        nav,
+        currentId: i.root ? entry.id.slice(i.root.length + 1) || entry.id : entry
.id,
+        refs: backlinks.get(`/kb/${entry.id}/`) ?? [],
+      },
+    };
+  });
 }
 
 interface Props {
   entry: CollectionEntry<"kb">;
+  info: RegisterInfo;
   prev: PageLink | null;
   next: PageLink | null;
+  nav: DocEntry[];
+  currentId: string;
   refs: Backlink[];
 }
-const { entry, prev, next, refs } = Astro.props as Props;
+const { entry, info, prev, next, nav, currentId, refs } = Astro.props as Props;
 const { Content, headings } = await render(entry);
 const dirs = entry.id.split("/").slice(0, -1).join("/");
 const description =
-  describeBody(entry.body ?? "") || `${entry.data.title} - from ${entry.data.sour
ce}.`;
+  entry.data.description ??
+  (describeBody(entry.body ?? "") || `${entry.data.title} - ooknet kb.`);
 
-const meta: MetaEntry[] = [{ key: "SOURCE", value: entry.data.source }];
-if (entry.data.url) {
-  meta.push({
-    key: "URL",
-    value: entry.data.url.replace(/^https?:\/\//, ""),
-    href: entry.data.url,
-  });
+const subject = topSegment(entry.id);
+const rootTitle = (info.title ?? info.root ?? subject).toUpperCase();
+
+const meta: MetaEntry[] = [];
+const tags = entry.data.tags;
+if (info.register === "reference") {
+  if (entry.data.source) meta.push({ key: "SOURCE", value: entry.data.source });
+  if (entry.data.url) {
+    meta.push({
+      key: "URL",
+      value: entry.data.url.replace(/^https?:\/\//, ""),
+      href: entry.data.url,
+    });
+  }
+  if (entry.data.retrieved) meta.push({ key: "RETRIEVED", value: entry.data.retri
eved });
+} else {
+  if (entry.data.published) meta.push({ key: "PUBLISHED", value: entry.data.publi
shed });
+  if (entry.data.status) meta.push({ key: "STATUS", value: entry.data.status });
 }
-if (entry.data.retrieved) meta.push({ key: "RETRIEVED", value: entry.data.retriev
ed });
+
+const header =
+  info.register === "docs"
+    ? { left: "OOKNET KB", right: `DOCS: ${rootTitle}` }
+    : info.register === "reference"
+      ? { left: "OOKNET KB", right: `SOURCE: ${entry.data.source ?? rootTitle}` }
+      : { left: "OOKNET KB", right: `SUBJECT: ${subject.toUpperCase()}` };
+
+const cardLabel =
+  info.register === "docs" ? "DOCUMENTATION" : info.register === "reference" ? "R
EFERENCE COPY" : "NOTE";
 ---
 <ArticleLayout title={entry.data.title} description={description} type="article">
   <TopHeader />
-  <ArticleHeader left="OOKNET KNOWLEDGE BASE" right={`SOURCE: ${entry.data.source
}`} date={entry.data.retrieved} />
-  <DocumentTitle label="REFERENCE COPY" title={entry.data.title} />
+  <ArticleHeader left={header.left} right={header.right} date={entry.data.publish
ed ?? entry.data.retrieved} />
+  <DocumentTitle label={cardLabel} title={entry.data.title} />
   <Breadcrumbs filed={`/KB/${dirs.toUpperCase()}`} />
-  <ArticleMeta entries={meta} tags={entry.data.tags} />
+  {(meta.length > 0 || tags.length > 0) && <ArticleMeta entries={meta} tags={tags
} />}
+  {info.register === "docs" && nav.length > 1 && (
+    <DocsNav docs={nav} currentId={currentId} title={rootTitle} base={`/kb/${info
.root ? info.root + "/" : ""}`} />
+  )}
   {headings.length >= 3 && <TableOfContents headings={headings} />}
   <Prose><Content /></Prose>
+  <ArticleSeeAlso seeAlso={entry.data.seeAlso} />
   <ArticleBacklinks refs={refs} />
   <Pre>{" "}</Pre>
   <ArticlePagination prev={prev} next={next} />

diff --git a/ooknet-design/src/pages/kb/[...
slug].astro b/ooknet-design/src/pages/kb/[..
.slug].astro
index 4e2c5f5..fb50461 100644
--- a/ooknet-design/src/pages/kb/[...slug].a
stro
+++ b/ooknet-design/src/pages/kb/[...slug].a
stro
@@ -1,4 +1,7 @@
 ---
+// The one content route. What chrome a pag
e gets - note card, project
+// docs with a scoped index, or vendor refe
rence copy - is decided by
+// its register (nearest dir.yml; see lib/r
egisters.ts).
 import { getCollection, render, type Collec
tionEntry } from "astro:content";
 import ArticleLayout from "../../layouts/Ar
ticleLayout/ArticleLayout.astro";
 import TopHeader from "../../components/Top
Header/TopHeader.astro";
@@ -8,70 +11,144 @@ import ArticleMeta from 
"../../components/ArticleMeta/ArticleMeta.as
tro";
 import type { MetaEntry } from "../../compo
nents/ArticleMeta/ArticleMeta";
 import TableOfContents from "../../componen
ts/TableOfContents/TableOfContents.astro";
 import Breadcrumbs from "../../components/B
readcrumbs/Breadcrumbs.astro";
+import ArticleSeeAlso from "../../component
s/ArticleSeeAlso/ArticleSeeAlso.astro";
 import ArticleBacklinks from "../../compone
nts/ArticleBacklinks/ArticleBacklinks.astro"
;
 import ArticlePagination from "../../compon
ents/ArticlePagination/ArticlePagination.ast
ro";
 import ArticleFooter from "../../components
/ArticleFooter/ArticleFooter.astro";
+import DocsNav from "../../components/DocsN
av/DocsNav.astro";
 import Prose from "../../components/Prose/P
rose.astro";
 import Pre from "../../components/Pre/Pre.a
stro";
 import type { PageLink } from "../../compon
ents/ArticlePagination/ArticlePagination";
-import { published } from "../../lib/notes"
;
-import { docsOrder } from "../../lib/docs";
+import { published, topSegment } from "../.
./lib/notes";
+import { docsOrder, type DocEntry } from ".
./../lib/docs";
+import { readManifests, registerOf, type Re
gisterInfo } from "../../lib/registers";
 import { buildBacklinks, type Backlink } fr
om "../../lib/wikilinks";
 import { describeBody } from "../../lib/met
a";
 import { len } from "../../lib/ascii";
 
 export async function getStaticPaths() {
   const entries = await getCollection("kb",
 published);
+  const manifests = readManifests();
   const backlinks = buildBacklinks();
-  const ordered = docsOrder(entries.map((e)
 => ({ id: e.id, title: e.data.title })));
-  const byId = new Map(entries.map((e) => [
e.id, e]));
-  const link = (id?: string): PageLink | nu
ll => {
-    if (!id) return null;
-    const t = byId.get(id)!.data.title;
+  const info = new Map(entries.map((e) => [
e.id, registerOf(e.id, manifests)]));
+
+  const link = (e?: { id: string; title: st
ring }): PageLink | null => {
+    if (!e) return null;
+    const t = e.title.toUpperCase();
     const note = len(t) > 18 ? [...t].slice
(0, 15).join("") + "..." : t;
-    return { href: `/kb/${id}/`, note };
+    return { href: `/kb/${e.id}/`, note };
   };
-  return ordered.map((e, i) => ({
-    params: { slug: e.id },
-    props: {
-      entry: byId.get(e.id)!,
-      prev: link(ordered[i - 1]?.id),
-      next: link(ordered[i + 1]?.id),
-      refs: backlinks.get(`/kb/${e.id}/`) ?
? [],
-    },
-  }));
+
+  // notes page in chronological order acro
ss the whole register;
+  // docs and reference page within their m
anifest root, reading order
+  const notes = entries
+    .filter((e) => info.get(e.id)!.register
 === "notes")
+    .map((e) => ({ id: e.id, title: e.data.
title, date: e.data.published ?? "" }))
+    .sort((a, b) => a.date.localeCompare(b.
date) || a.id.localeCompare(b.id));
+
+  const rooted = new Map<string, DocEntry[]
>();
+  for (const e of entries) {
+    const i = info.get(e.id)!;
+    if (i.register === "notes") continue;
+    const key = `${i.register}:${i.root ?? 
""}`;
+    if (!rooted.has(key)) rooted.set(key, [
]);
+    rooted.get(key)!.push({ id: e.id, title
: e.data.title, order: e.data.order });
+  }
+  for (const [k, list] of rooted) rooted.se
t(k, docsOrder(list));
+
+  return entries.map((entry) => {
+    const i = info.get(entry.id)!;
+    let prev: PageLink | null = null;
+    let next: PageLink | null = null;
+    let nav: DocEntry[] = [];
+    if (i.register === "notes") {
+      const at = notes.findIndex((n) => n.i
d === entry.id);
+      prev = link(notes[at - 1]);
+      next = link(notes[at + 1]);
+    } else {
+      const group = rooted.get(`${i.registe
r}:${i.root ?? ""}`)!;
+      const at = group.findIndex((d) => d.i
d === entry.id);
+      prev = link(group[at - 1] && { id: gr
oup[at - 1].id, title: group[at - 1].title }
);
+      next = link(group[at + 1] && { id: gr
oup[at + 1].id, title: group[at + 1].title }
);
+      if (i.register === "docs") {
+        const strip = (id: string) =>
+          i.root ? id.slice(i.root.length +
 1) || id : id;
+        nav = group.map((d) => ({ ...d, id:
 strip(d.id) }));
+      }
+    }
+    return {
+      params: { slug: entry.id },
+      props: {
+        entry,
+        info: i,
+        prev,
+        next,
+        nav,
+        currentId: i.root ? entry.id.slice(
i.root.length + 1) || entry.id : entry.id,
+        refs: backlinks.get(`/kb/${entry.id
}/`) ?? [],
+      },
+    };
+  });
 }
 
 interface Props {
   entry: CollectionEntry<"kb">;
+  info: RegisterInfo;
   prev: PageLink | null;
   next: PageLink | null;
+  nav: DocEntry[];
+  currentId: string;
   refs: Backlink[];
 }
-const { entry, prev, next, refs } = Astro.p
rops as Props;
+const { entry, info, prev, next, nav, curre
ntId, refs } = Astro.props as Props;
 const { Content, headings } = await render(
entry);
 const dirs = entry.id.split("/").slice(0, -
1).join("/");
 const description =
-  describeBody(entry.body ?? "") || `${entr
y.data.title} - from ${entry.data.source}.`;
+  entry.data.description ??
+  (describeBody(entry.body ?? "") || `${ent
ry.data.title} - ooknet kb.`);
 
-const meta: MetaEntry[] = [{ key: "SOURCE",
 value: entry.data.source }];
-if (entry.data.url) {
-  meta.push({
-    key: "URL",
-    value: entry.data.url.replace(/^https?:
\/\//, ""),
-    href: entry.data.url,
-  });
+const subject = topSegment(entry.id);
+const rootTitle = (info.title ?? info.root 
?? subject).toUpperCase();
+
+const meta: MetaEntry[] = [];
+const tags = entry.data.tags;
+if (info.register === "reference") {
+  if (entry.data.source) meta.push({ key: "
SOURCE", value: entry.data.source });
+  if (entry.data.url) {
+    meta.push({
+      key: "URL",
+      value: entry.data.url.replace(/^https
?:\/\//, ""),
+      href: entry.data.url,
+    });
+  }
+  if (entry.data.retrieved) meta.push({ key
: "RETRIEVED", value: entry.data.retrieved }
);
+} else {
+  if (entry.data.published) meta.push({ key
: "PUBLISHED", value: entry.data.published }
);
+  if (entry.data.status) meta.push({ key: "
STATUS", value: entry.data.status });
 }
-if (entry.data.retrieved) meta.push({ key: 
"RETRIEVED", value: entry.data.retrieved });
+
+const header =
+  info.register === "docs"
+    ? { left: "OOKNET KB", right: `DOCS: ${
rootTitle}` }
+    : info.register === "reference"
+      ? { left: "OOKNET KB", right: `SOURCE
: ${entry.data.source ?? rootTitle}` }
+      : { left: "OOKNET KB", right: `SUBJEC
T: ${subject.toUpperCase()}` };
+
+const cardLabel =
+  info.register === "docs" ? "DOCUMENTATION
" : info.register === "reference" ? "REFEREN
CE COPY" : "NOTE";
 ---
 <ArticleLayout title={entry.data.title} des
cription={description} type="article">
   <TopHeader />
-  <ArticleHeader left="OOKNET KNOWLEDGE BAS
E" right={`SOURCE: ${entry.data.source}`} da
te={entry.data.retrieved} />
-  <DocumentTitle label="REFERENCE COPY" tit
le={entry.data.title} />
+  <ArticleHeader left={header.left} right={
header.right} date={entry.data.published ?? 
entry.data.retrieved} />
+  <DocumentTitle label={cardLabel} title={e
ntry.data.title} />
   <Breadcrumbs filed={`/KB/${dirs.toUpperCa
se()}`} />
-  <ArticleMeta entries={meta} tags={entry.d
ata.tags} />
+  {(meta.length > 0 || tags.length > 0) && 
<ArticleMeta entries={meta} tags={tags} />}
+  {info.register === "docs" && nav.length >
 1 && (
+    <DocsNav docs={nav} currentId={currentI
d} title={rootTitle} base={`/kb/${info.root 
? info.root + "/" : ""}`} />
+  )}
   {headings.length >= 3 && <TableOfContents
 headings={headings} />}
   <Prose><Content /></Prose>
+  <ArticleSeeAlso seeAlso={entry.data.seeAl
so} />
   <ArticleBacklinks refs={refs} />
   <Pre>{" "}</Pre>
   <ArticlePagination prev={prev} next={next
} />
 

diff --git a/ooknet-design/src/pages/kb/index.astro b/ooknet-design/src/pages/kb/i
ndex.astro
index 7f2d9ee..194f409 100644
--- a/ooknet-design/src/pages/kb/index.astro
+++ b/ooknet-design/src/pages/kb/index.astro
@@ -5,23 +5,36 @@ import TopHeader from "../../components/TopHeader/TopHeader.astr
o";
 import Frame from "../../components/Frame/Frame.astro";
 import Pre from "../../components/Pre/Pre.astro";
 import Tree from "../../components/Tree/Tree.astro";
-import { rule, ruleD, row2 } from "../../lib/ascii";
+import KeyValue from "../../components/KeyValue/KeyValue.astro";
+import ArticleFooter from "../../components/ArticleFooter/ArticleFooter.astro";
+import { ruleD, row2 } from "../../lib/ascii";
 import { published } from "../../lib/notes";
 import { docsTree } from "../../lib/docs";
+import { readManifests, registerOf, type Register } from "../../lib/registers";
 
 const entries = await getCollection("kb", published);
+const manifests = readManifests();
+const counts: Record<Register, number> = { notes: 0, docs: 0, reference: 0 };
+for (const e of entries) counts[registerOf(e.id, manifests).register]++;
+
 const tree = docsTree(
-  entries.map((e) => ({ id: e.id, title: e.data.title })),
+  entries.map((e) => ({ id: e.id, title: e.data.title, order: e.data.order })),
   undefined,
   "/kb/",
 );
 ---
 <ArticleLayout title="OOKNET KB - Knowledge base"
-  description="Vendor documentation and guides on file, credited to their sources
.">
+  description="The ooknet knowledge base - notes, documentation, and references, 
one tree.">
   <TopHeader />
   <Frame build={(w) => [row2("OOKNET KNOWLEDGE BASE", `${entries.length} ON FILE`
, w), ruleD(w)].join("\n")} />
   <Pre>{" "}</Pre>
+  <KeyValue entries={[
+    ["NOTES", `${counts.notes}`],
+    ["DOCS", `${counts.docs}`],
+    ["REFERENCE", `${counts.reference}`],
+  ]} />
+  <Pre>{" "}</Pre>
   <Tree nodes={tree} />
   <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
+  <ArticleFooter />
 </ArticleLayout>

diff --git a/ooknet-design/src/pages/kb/inde
x.astro b/ooknet-design/src/pages/kb/index.a
stro
index 7f2d9ee..194f409 100644
--- a/ooknet-design/src/pages/kb/index.astro
+++ b/ooknet-design/src/pages/kb/index.astro
@@ -5,23 +5,36 @@ import TopHeader from "../
../components/TopHeader/TopHeader.astro";
 import Frame from "../../components/Frame/F
rame.astro";
 import Pre from "../../components/Pre/Pre.a
stro";
 import Tree from "../../components/Tree/Tre
e.astro";
-import { rule, ruleD, row2 } from "../../li
b/ascii";
+import KeyValue from "../../components/KeyV
alue/KeyValue.astro";
+import ArticleFooter from "../../components
/ArticleFooter/ArticleFooter.astro";
+import { ruleD, row2 } from "../../lib/asci
i";
 import { published } from "../../lib/notes"
;
 import { docsTree } from "../../lib/docs";
+import { readManifests, registerOf, type Re
gister } from "../../lib/registers";
 
 const entries = await getCollection("kb", p
ublished);
+const manifests = readManifests();
+const counts: Record<Register, number> = { 
notes: 0, docs: 0, reference: 0 };
+for (const e of entries) counts[registerOf(
e.id, manifests).register]++;
+
 const tree = docsTree(
-  entries.map((e) => ({ id: e.id, title: e.
data.title })),
+  entries.map((e) => ({ id: e.id, title: e.
data.title, order: e.data.order })),
   undefined,
   "/kb/",
 );
 ---
 <ArticleLayout title="OOKNET KB - Knowledge
 base"
-  description="Vendor documentation and gui
des on file, credited to their sources.">
+  description="The ooknet knowledge base - 
notes, documentation, and references, one tr
ee.">
   <TopHeader />
   <Frame build={(w) => [row2("OOKNET KNOWLE
DGE BASE", `${entries.length} ON FILE`, w), 
ruleD(w)].join("\n")} />
   <Pre>{" "}</Pre>
+  <KeyValue entries={[
+    ["NOTES", `${counts.notes}`],
+    ["DOCS", `${counts.docs}`],
+    ["REFERENCE", `${counts.reference}`],
+  ]} />
+  <Pre>{" "}</Pre>
   <Tree nodes={tree} />
   <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
+  <ArticleFooter />
 </ArticleLayout>
 

diff --git a/ooknet-design/src/pages/notes/[...slug].astro b/ooknet-design/src/pag
es/notes/[...slug].astro
deleted file mode 100644
index d1ed2ba..0000000
--- a/ooknet-design/src/pages/notes/[...slug].astro
+++ /dev/null
@@ -1,74 +0,0 @@
----
-import { getCollection, render, type CollectionEntry } from "astro:content";
-import ArticleLayout from "../../layouts/ArticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/TopHeader/TopHeader.astro";
-import ArticleHeader from "../../components/ArticleHeader/ArticleHeader.astro";
-import DocumentTitle from "../../components/DocumentTitle/DocumentTitle.astro";
-import ArticleMeta from "../../components/ArticleMeta/ArticleMeta.astro";
-import TableOfContents from "../../components/TableOfContents/TableOfContents.ast
ro";
-import Breadcrumbs from "../../components/Breadcrumbs/Breadcrumbs.astro";
-import ArticleSeeAlso from "../../components/ArticleSeeAlso/ArticleSeeAlso.astro"
;
-import ArticleBacklinks from "../../components/ArticleBacklinks/ArticleBacklinks.
astro";
-import ArticlePagination from "../../components/ArticlePagination/ArticlePaginati
on.astro";
-import ArticleFooter from "../../components/ArticleFooter/ArticleFooter.astro";
-import Prose from "../../components/Prose/Prose.astro";
-import Pre from "../../components/Pre/Pre.astro";
-import type { PageLink } from "../../components/ArticlePagination/ArticlePaginati
on";
-import { published, byPublished, topSegment } from "../../lib/notes";
-import { buildBacklinks, type Backlink } from "../../lib/wikilinks";
-import { describeBody } from "../../lib/meta";
-import { len } from "../../lib/ascii";
-
-export async function getStaticPaths() {
-  const notes = (await getCollection("notes", published)).sort(byPublished);
-  const backlinks = buildBacklinks();
-  const link = (n?: (typeof notes)[number]): PageLink | null => {
-    if (!n) return null;
-    const t = n.data.title;
-    const note = len(t) > 18 ? [...t].slice(0, 15).join("") + "..." : t;
-    return { href: `/notes/${n.id}/`, note };
-  };
-  return notes.map((n, i) => ({
-    params: { slug: n.id },
-    props: {
-      note: n,
-      prev: link(notes[i - 1]),
-      next: link(notes[i + 1]),
-      refs: backlinks.get(`/notes/${n.id}/`) ?? [],
-    },
-  }));
-}
-
-interface Props {
-  note: CollectionEntry<"notes">;
-  prev: PageLink | null;
-  next: PageLink | null;
-  refs: Backlink[];
-}
-const { note, prev, next, refs } = Astro.props as Props;
-const { Content, headings } = await render(note);
-const subject = topSegment(note.id);
-const dirs = note.id.split("/").slice(0, -1).join("/");
-const description =
-  describeBody(note.body ?? "") || `Note filed under ${subject}.`;
----
-<ArticleLayout title={note.data.title} description={description} type="article">
-  <TopHeader />
-  <ArticleHeader left="OOKNET NOTES" right={`SUBJECT: ${subject.toUpperCase()}`} 
date={note.data.published} />
-  <DocumentTitle label="NOTE" title={note.data.title} />
-  <Breadcrumbs filed={`/NOTES/${dirs.toUpperCase()}`} />
-  <ArticleMeta
-    entries={[
-      { key: "PUBLISHED", value: note.data.published },
-      { key: "STATUS", value: note.data.status },
-    ]}
-    tags={note.data.tags}
-  />
-  {headings.length >= 3 && <TableOfContents headings={headings} />}
-  <Prose><Content /></Prose>
-  <ArticleSeeAlso fm={note.data} />
-  <ArticleBacklinks refs={refs} />
-  <Pre>{" "}</Pre>
-  <ArticlePagination prev={prev} next={next} />
-  <ArticleFooter />
-</ArticleLayout>

diff --git a/ooknet-design/src/pages/notes/[
...slug].astro b/ooknet-design/src/pages/not
es/[...slug].astro
deleted file mode 100644
index d1ed2ba..0000000
--- a/ooknet-design/src/pages/notes/[...slug
].astro
+++ /dev/null
@@ -1,74 +0,0 @@
----
-import { getCollection, render, type Collec
tionEntry } from "astro:content";
-import ArticleLayout from "../../layouts/Ar
ticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/Top
Header/TopHeader.astro";
-import ArticleHeader from "../../components
/ArticleHeader/ArticleHeader.astro";
-import DocumentTitle from "../../components
/DocumentTitle/DocumentTitle.astro";
-import ArticleMeta from "../../components/A
rticleMeta/ArticleMeta.astro";
-import TableOfContents from "../../componen
ts/TableOfContents/TableOfContents.astro";
-import Breadcrumbs from "../../components/B
readcrumbs/Breadcrumbs.astro";
-import ArticleSeeAlso from "../../component
s/ArticleSeeAlso/ArticleSeeAlso.astro";
-import ArticleBacklinks from "../../compone
nts/ArticleBacklinks/ArticleBacklinks.astro"
;
-import ArticlePagination from "../../compon
ents/ArticlePagination/ArticlePagination.ast
ro";
-import ArticleFooter from "../../components
/ArticleFooter/ArticleFooter.astro";
-import Prose from "../../components/Prose/P
rose.astro";
-import Pre from "../../components/Pre/Pre.a
stro";
-import type { PageLink } from "../../compon
ents/ArticlePagination/ArticlePagination";
-import { published, byPublished, topSegment
 } from "../../lib/notes";
-import { buildBacklinks, type Backlink } fr
om "../../lib/wikilinks";
-import { describeBody } from "../../lib/met
a";
-import { len } from "../../lib/ascii";
-
-export async function getStaticPaths() {
-  const notes = (await getCollection("notes
", published)).sort(byPublished);
-  const backlinks = buildBacklinks();
-  const link = (n?: (typeof notes)[number])
: PageLink | null => {
-    if (!n) return null;
-    const t = n.data.title;
-    const note = len(t) > 18 ? [...t].slice
(0, 15).join("") + "..." : t;
-    return { href: `/notes/${n.id}/`, note 
};
-  };
-  return notes.map((n, i) => ({
-    params: { slug: n.id },
-    props: {
-      note: n,
-      prev: link(notes[i - 1]),
-      next: link(notes[i + 1]),
-      refs: backlinks.get(`/notes/${n.id}/`
) ?? [],
-    },
-  }));
-}
-
-interface Props {
-  note: CollectionEntry<"notes">;
-  prev: PageLink | null;
-  next: PageLink | null;
-  refs: Backlink[];
-}
-const { note, prev, next, refs } = Astro.pr
ops as Props;
-const { Content, headings } = await render(
note);
-const subject = topSegment(note.id);
-const dirs = note.id.split("/").slice(0, -1
).join("/");
-const description =
-  describeBody(note.body ?? "") || `Note fi
led under ${subject}.`;
----
-<ArticleLayout title={note.data.title} desc
ription={description} type="article">
-  <TopHeader />
-  <ArticleHeader left="OOKNET NOTES" right=
{`SUBJECT: ${subject.toUpperCase()}`} date={
note.data.published} />
-  <DocumentTitle label="NOTE" title={note.d
ata.title} />
-  <Breadcrumbs filed={`/NOTES/${dirs.toUppe
rCase()}`} />
-  <ArticleMeta
-    entries={[
-      { key: "PUBLISHED", value: note.data.
published },
-      { key: "STATUS", value: note.data.sta
tus },
-    ]}
-    tags={note.data.tags}
-  />
-  {headings.length >= 3 && <TableOfContents
 headings={headings} />}
-  <Prose><Content /></Prose>
-  <ArticleSeeAlso fm={note.data} />
-  <ArticleBacklinks refs={refs} />
-  <Pre>{" "}</Pre>
-  <ArticlePagination prev={prev} next={next
} />
-  <ArticleFooter />
-</ArticleLayout>
 

diff --git a/ooknet-design/src/pages/notes/index.astro b/ooknet-design/src/pages/n
otes/index.astro
deleted file mode 100644
index 06e2867..0000000
--- a/ooknet-design/src/pages/notes/index.astro
+++ /dev/null
@@ -1,27 +0,0 @@
----
-import { getCollection } from "astro:content";
-import ArticleLayout from "../../layouts/ArticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/TopHeader/TopHeader.astro";
-import Frame from "../../components/Frame/Frame.astro";
-import Pre from "../../components/Pre/Pre.astro";
-import Tree from "../../components/Tree/Tree.astro";
-import { rule, ruleD, row2 } from "../../lib/ascii";
-import { published } from "../../lib/notes";
-import { docsTree } from "../../lib/docs";
-
-const notes = await getCollection("notes", published);
-const tree = docsTree(
-  notes.map((n) => ({ id: n.id, title: n.data.title })),
-  undefined,
-  "/notes/",
-);
----
-<ArticleLayout title="OOKNET KB - Notes"
-  description="Personal notes and study writing, filed by subject.">
-  <TopHeader />
-  <Frame build={(w) => [row2("OOKNET NOTES", `${notes.length} FILED`, w), ruleD(w
)].join("\n")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={tree} />
-  <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>

diff --git a/ooknet-design/src/pages/notes/i
ndex.astro b/ooknet-design/src/pages/notes/i
ndex.astro
deleted file mode 100644
index 06e2867..0000000
--- a/ooknet-design/src/pages/notes/index.as
tro
+++ /dev/null
@@ -1,27 +0,0 @@
----
-import { getCollection } from "astro:conten
t";
-import ArticleLayout from "../../layouts/Ar
ticleLayout/ArticleLayout.astro";
-import TopHeader from "../../components/Top
Header/TopHeader.astro";
-import Frame from "../../components/Frame/F
rame.astro";
-import Pre from "../../components/Pre/Pre.a
stro";
-import Tree from "../../components/Tree/Tre
e.astro";
-import { rule, ruleD, row2 } from "../../li
b/ascii";
-import { published } from "../../lib/notes"
;
-import { docsTree } from "../../lib/docs";
-
-const notes = await getCollection("notes", 
published);
-const tree = docsTree(
-  notes.map((n) => ({ id: n.id, title: n.da
ta.title })),
-  undefined,
-  "/notes/",
-);
----
-<ArticleLayout title="OOKNET KB - Notes"
-  description="Personal notes and study wri
ting, filed by subject.">
-  <TopHeader />
-  <Frame build={(w) => [row2("OOKNET NOTES"
, `${notes.length} FILED`, w), ruleD(w)].joi
n("\n")} />
-  <Pre>{" "}</Pre>
-  <Tree nodes={tree} />
-  <Pre>{" "}</Pre>
-  <Frame build={(w) => rule(w)} />
-</ArticleLayout>
 

diff --git a/ooknet-design/src/pages/projects.astro b/ooknet-design/src/pages/proj
ects.astro
index cf6d570..533705c 100644
--- a/ooknet-design/src/pages/projects.astro
+++ b/ooknet-design/src/pages/projects.astro
@@ -11,24 +11,21 @@ import { row2, ruleD } from "../lib/ascii";
 const buildHeader = (w: number) =>
   [row2("PROJECT LEDGER", "DEPT. OF OOKS", w), ruleD(w)].join("\n");
 
-const PROJECTS: { name: string; desc: string; git: string; docs: string; web?: st
ring }[] = [
+const PROJECTS: { name: string; desc: string; git: string; docs?: string; web?: s
tring }[] = [
   {
     name: "OOKNET",
     desc: "my personal nixos infrastructure configuration",
     git: "https://github.com/ooks-io/ooknet",
-    docs: "/docs/ooknet/overview/",
   },
   {
     name: "OOKNET.ORG",
     desc: "personal website - a text-mode knowledge base and the ascii design sys
tem that renders it, everything on a strict monospace grid",
     git: "https://github.com/ooks-io/ooknet.org",
-    docs: "/docs/ooknet-org/overview/",
   },
   {
     name: "WOWSIM-STATS",
     desc: "a nix pipeline that orchestrates mist of pandaria simulations at scale
, and a statically generated challenge mode leaderboard",
     git: "https://github.com/ooks-io/wowsim-stats",
-    docs: "/docs/wowsim-stats/overview/",
     web: "https://wowsimstats.com",
   },
 ];
@@ -42,7 +39,7 @@ const PROJECTS: { name: string; desc: string; git: string; docs:
 string; web?: s
     <Fragment>
       <Panel title={p.name}>
         <p class="project-desc">{p.desc}</p>
-        <Pre><Button label="GIT" href={p.git} />{"  "}<Button label="MORE" href={
p.docs} />{p.web && <Fragment>{"  "}<Button label="WEB" href={p.web} /></Fragment>
}</Pre>
+        <Pre><Button label="GIT" href={p.git} />{p.docs && <Fragment>{"  "}<Butto
n label="MORE" href={p.docs} /></Fragment>}{p.web && <Fragment>{"  "}<Button label
="WEB" href={p.web} /></Fragment>}</Pre>
       </Panel>
       <Pre>{" "}</Pre>
     </Fragment>

diff --git a/ooknet-design/src/pages/project
s.astro b/ooknet-design/src/pages/projects.a
stro
index cf6d570..533705c 100644
--- a/ooknet-design/src/pages/projects.astro
+++ b/ooknet-design/src/pages/projects.astro
@@ -11,24 +11,21 @@ import { row2, ruleD } f
rom "../lib/ascii";
 const buildHeader = (w: number) =>
   [row2("PROJECT LEDGER", "DEPT. OF OOKS", 
w), ruleD(w)].join("\n");
 
-const PROJECTS: { name: string; desc: strin
g; git: string; docs: string; web?: string }
[] = [
+const PROJECTS: { name: string; desc: strin
g; git: string; docs?: string; web?: string 
}[] = [
   {
     name: "OOKNET",
     desc: "my personal nixos infrastructure
 configuration",
     git: "https://github.com/ooks-io/ooknet
",
-    docs: "/docs/ooknet/overview/",
   },
   {
     name: "OOKNET.ORG",
     desc: "personal website - a text-mode k
nowledge base and the ascii design system th
at renders it, everything on a strict monosp
ace grid",
     git: "https://github.com/ooks-io/ooknet
.org",
-    docs: "/docs/ooknet-org/overview/",
   },
   {
     name: "WOWSIM-STATS",
     desc: "a nix pipeline that orchestrates
 mist of pandaria simulations at scale, and 
a statically generated challenge mode leader
board",
     git: "https://github.com/ooks-io/wowsim
-stats",
-    docs: "/docs/wowsim-stats/overview/",
     web: "https://wowsimstats.com",
   },
 ];
@@ -42,7 +39,7 @@ const PROJECTS: { name: st
ring; desc: string; git: string; docs: strin
g; web?: s
     <Fragment>
       <Panel title={p.name}>
         <p class="project-desc">{p.desc}</p
>
-        <Pre><Button label="GIT" href={p.gi
t} />{"  "}<Button label="MORE" href={p.docs
} />{p.web && <Fragment>{"  "}<Button label=
"WEB" href={p.web} /></Fragment>}</Pre>
+        <Pre><Button label="GIT" href={p.gi
t} />{p.docs && <Fragment>{"  "}<Button labe
l="MORE" href={p.docs} /></Fragment>}{p.web 
&& <Fragment>{"  "}<Button label="WEB" href=
{p.web} /></Fragment>}</Pre>
       </Panel>
       <Pre>{" "}</Pre>
     </Fragment>
 

diff --git a/ooknet-design/src/pages/resources.astro b/ooknet-design/src/pages/res
ources.astro
deleted file mode 100644
index cac1a60..0000000
--- a/ooknet-design/src/pages/resources.astro
+++ /dev/null
@@ -1,30 +0,0 @@
----
-import ArticleLayout from "../layouts/ArticleLayout/ArticleLayout.astro";
-import TopHeader from "../components/TopHeader/TopHeader.astro";
-import Frame from "../components/Frame/Frame.astro";
-import Pre from "../components/Pre/Pre.astro";
-import Badge from "../components/Badge/Badge.astro";
-import ArticleFooter from "../components/ArticleFooter/ArticleFooter.astro";
-import { pad, row2, ruleD } from "../lib/ascii";
-
-const buildSection = (title: string, right: string) => (w: number) =>
-  [row2(title, right, w), ruleD(w)].join("\n");
-
-const TOOLING: [string, string, string][] = [
-  ["FRAMEWORK", "ASTRO.BUILD", "https://astro.build"],
-  ["FRAME FONT", "CASCADIA-MONO", "https://github.com/microsoft/cascadia-code"],
-  ["BODY FONT", "IBM-PLEX-MONO", "https://github.com/IBM/plex"],
-  ["DIAGRAMS", "MERMAID-ASCII", "https://github.com/AlexanderGrooff/mermaid-ascii
"],
-];
----
-<ArticleLayout title="OOKNET KB - Resources"
-  description="External references filed by the OOKNET knowledge base - tools, pa
pers, and reading.">
-  <TopHeader />
-  <Frame build={buildSection("TOOLING", "EXTERNAL")} />
-  <Pre>{" "}</Pre>
-  {TOOLING.map(([label, name, href]) => (
-    <Pre>{pad(label, 12)}<Badge label={name} href={href} /></Pre>
-  ))}
-  <Pre>{" "}</Pre>
-  <ArticleFooter />
-</ArticleLayout>

diff --git a/ooknet-design/src/pages/resourc
es.astro b/ooknet-design/src/pages/resources
.astro
deleted file mode 100644
index cac1a60..0000000
--- a/ooknet-design/src/pages/resources.astr
o
+++ /dev/null
@@ -1,30 +0,0 @@
----
-import ArticleLayout from "../layouts/Artic
leLayout/ArticleLayout.astro";
-import TopHeader from "../components/TopHea
der/TopHeader.astro";
-import Frame from "../components/Frame/Fram
e.astro";
-import Pre from "../components/Pre/Pre.astr
o";
-import Badge from "../components/Badge/Badg
e.astro";
-import ArticleFooter from "../components/Ar
ticleFooter/ArticleFooter.astro";
-import { pad, row2, ruleD } from "../lib/as
cii";
-
-const buildSection = (title: string, right:
 string) => (w: number) =>
-  [row2(title, right, w), ruleD(w)].join("\
n");
-
-const TOOLING: [string, string, string][] =
 [
-  ["FRAMEWORK", "ASTRO.BUILD", "https://ast
ro.build"],
-  ["FRAME FONT", "CASCADIA-MONO", "https://
github.com/microsoft/cascadia-code"],
-  ["BODY FONT", "IBM-PLEX-MONO", "https://g
ithub.com/IBM/plex"],
-  ["DIAGRAMS", "MERMAID-ASCII", "https://gi
thub.com/AlexanderGrooff/mermaid-ascii"],
-];
----
-<ArticleLayout title="OOKNET KB - Resources
"
-  description="External references filed by
 the OOKNET knowledge base - tools, papers, 
and reading.">
-  <TopHeader />
-  <Frame build={buildSection("TOOLING", "EX
TERNAL")} />
-  <Pre>{" "}</Pre>
-  {TOOLING.map(([label, name, href]) => (
-    <Pre>{pad(label, 12)}<Badge label={name
} href={href} /></Pre>
-  ))}
-  <Pre>{" "}</Pre>
-  <ArticleFooter />
-</ArticleLayout>
 

diff --git a/ooknet-design/src/pages/rss.xml.ts b/ooknet-design/src/pages/rss.xml.
ts
index 9416347..1d69d49 100644
--- a/ooknet-design/src/pages/rss.xml.ts
+++ b/ooknet-design/src/pages/rss.xml.ts
@@ -1,22 +1,25 @@
-// Notes feed, newest first - personal writing only.
+// Personal writing only: kb pages whose register is notes, newest
+// first. Docs and reference copies stay out of the feed.
 import type { APIRoute } from "astro";
 import { getCollection } from "astro:content";
 import { published, topSegment } from "../lib/notes";
+import { readManifests, registerOf } from "../lib/registers";
 import { describeBody } from "../lib/meta";
 import { buildRss } from "../lib/feeds";
 
 export const GET: APIRoute = async ({ site }) => {
-  const notes = (await getCollection("notes", published)).sort((a, b) =>
-    b.data.published.localeCompare(a.data.published),
-  );
+  const manifests = readManifests();
+  const notes = (await getCollection("kb", published))
+    .filter((e) => registerOf(e.id, manifests).register === "notes" && e.data.pub
lished)
+    .sort((a, b) => b.data.published!.localeCompare(a.data.published!));
   const xml = buildRss({
     title: "OOKNET KB",
     site: site!.href,
     description: "OOKNET notes - personal writing, filed by subject.",
     items: notes.map((n) => ({
       title: n.data.title,
-      url: new URL(`/notes/${n.id}/`, site).href,
-      published: n.data.published,
+      url: new URL(`/kb/${n.id}/`, site).href,
+      published: n.data.published!,
       description: describeBody(n.body ?? "") || `Note filed under ${topSegment(n
.id)}.`,
     })),
   });

diff --git a/ooknet-design/src/pages/rss.xml
.ts b/ooknet-design/src/pages/rss.xml.ts
index 9416347..1d69d49 100644
--- a/ooknet-design/src/pages/rss.xml.ts
+++ b/ooknet-design/src/pages/rss.xml.ts
@@ -1,22 +1,25 @@
-// Notes feed, newest first - personal writ
ing only.
+// Personal writing only: kb pages whose re
gister is notes, newest
+// first. Docs and reference copies stay ou
t of the feed.
 import type { APIRoute } from "astro";
 import { getCollection } from "astro:conten
t";
 import { published, topSegment } from "../l
ib/notes";
+import { readManifests, registerOf } from "
../lib/registers";
 import { describeBody } from "../lib/meta";
 import { buildRss } from "../lib/feeds";
 
 export const GET: APIRoute = async ({ site 
}) => {
-  const notes = (await getCollection("notes
", published)).sort((a, b) =>
-    b.data.published.localeCompare(a.data.p
ublished),
-  );
+  const manifests = readManifests();
+  const notes = (await getCollection("kb", 
published))
+    .filter((e) => registerOf(e.id, manifes
ts).register === "notes" && e.data.published
)
+    .sort((a, b) => b.data.published!.local
eCompare(a.data.published!));
   const xml = buildRss({
     title: "OOKNET KB",
     site: site!.href,
     description: "OOKNET notes - personal w
riting, filed by subject.",
     items: notes.map((n) => ({
       title: n.data.title,
-      url: new URL(`/notes/${n.id}/`, site)
.href,
-      published: n.data.published,
+      url: new URL(`/kb/${n.id}/`, site).hr
ef,
+      published: n.data.published!,
       description: describeBody(n.body ?? "
") || `Note filed under ${topSegment(n.id)}.
`,
     })),
   });
 

diff --git a/ooknet-design/src/pages/search.json.ts b/ooknet-design/src/pages/sear
ch.json.ts
index 2b58afd..b3e52ad 100644
--- a/ooknet-design/src/pages/search.json.ts
+++ b/ooknet-design/src/pages/search.json.ts
@@ -1,41 +1,26 @@
 // Static index consumed by the masthead fuzzy search. `note` doubles
-// as the type marker shown next to results.
+// as the register marker shown next to results.
 import type { APIRoute } from "astro";
 import { getCollection } from "astro:content";
 import { published, topSegment } from "../lib/notes";
+import { readManifests, registerOf, registerLabel } from "../lib/registers";
 
 const dirsOf = (id: string) => id.split("/").slice(0, -1).join("/").toUpperCase()
;
 
 export const GET: APIRoute = async () => {
-  const notes = await getCollection("notes", published);
-  const kb = await getCollection("kb", published);
-  const docs = await getCollection("docs", published);
-  const data = [
-    ...notes.map((n) => ({
-      note: "NOTE",
-      title: n.data.title,
-      category: topSegment(n.id),
-      tags: n.data.tags,
-      filed: `/NOTES/${dirsOf(n.id)}`,
-      url: `/notes/${n.id}/`,
-    })),
-    ...kb.map((e) => ({
-      note: "KB",
+  const entries = await getCollection("kb", published);
+  const manifests = readManifests();
+  const data = entries.map((e) => {
+    const info = registerOf(e.id, manifests);
+    return {
+      note: registerLabel(info.register),
       title: e.data.title,
-      category: e.data.source.toLowerCase(),
+      category: (info.title ?? topSegment(e.id)).toLowerCase(),
       tags: e.data.tags,
       filed: `/KB/${dirsOf(e.id)}`,
       url: `/kb/${e.id}/`,
-    })),
-    ...docs.map((d) => ({
-      note: "DOC",
-      title: d.data.title,
-      category: topSegment(d.id),
-      tags: [],
-      filed: `/DOCS/${d.id.toUpperCase()}`,
-      url: `/docs/${d.id}/`,
-    })),
-  ];
+    };
+  });
   return new Response(JSON.stringify(data), {
     headers: { "Content-Type": "application/json" },
   });

diff --git a/ooknet-design/src/pages/search.
json.ts b/ooknet-design/src/pages/search.jso
n.ts
index 2b58afd..b3e52ad 100644
--- a/ooknet-design/src/pages/search.json.ts
+++ b/ooknet-design/src/pages/search.json.ts
@@ -1,41 +1,26 @@
 // Static index consumed by the masthead fu
zzy search. `note` doubles
-// as the type marker shown next to results
.
+// as the register marker shown next to res
ults.
 import type { APIRoute } from "astro";
 import { getCollection } from "astro:conten
t";
 import { published, topSegment } from "../l
ib/notes";
+import { readManifests, registerOf, registe
rLabel } from "../lib/registers";
 
 const dirsOf = (id: string) => id.split("/"
).slice(0, -1).join("/").toUpperCase();
 
 export const GET: APIRoute = async () => {
-  const notes = await getCollection("notes"
, published);
-  const kb = await getCollection("kb", publ
ished);
-  const docs = await getCollection("docs", 
published);
-  const data = [
-    ...notes.map((n) => ({
-      note: "NOTE",
-      title: n.data.title,
-      category: topSegment(n.id),
-      tags: n.data.tags,
-      filed: `/NOTES/${dirsOf(n.id)}`,
-      url: `/notes/${n.id}/`,
-    })),
-    ...kb.map((e) => ({
-      note: "KB",
+  const entries = await getCollection("kb",
 published);
+  const manifests = readManifests();
+  const data = entries.map((e) => {
+    const info = registerOf(e.id, manifests
);
+    return {
+      note: registerLabel(info.register),
       title: e.data.title,
-      category: e.data.source.toLowerCase()
,
+      category: (info.title ?? topSegment(e
.id)).toLowerCase(),
       tags: e.data.tags,
       filed: `/KB/${dirsOf(e.id)}`,
       url: `/kb/${e.id}/`,
-    })),
-    ...docs.map((d) => ({
-      note: "DOC",
-      title: d.data.title,
-      category: topSegment(d.id),
-      tags: [],
-      filed: `/DOCS/${d.id.toUpperCase()}`,
-      url: `/docs/${d.id}/`,
-    })),
-  ];
+    };
+  });
   return new Response(JSON.stringify(data),
 {
     headers: { "Content-Type": "application
/json" },
   });
 

diff --git a/ooknet-design/src/pages/sitemap.xml.ts b/ooknet-design/src/pages/site
map.xml.ts
index f47b99d..eed111d 100644
--- a/ooknet-design/src/pages/sitemap.xml.ts
+++ b/ooknet-design/src/pages/sitemap.xml.ts
@@ -3,18 +3,17 @@ import { getCollection } from "astro:content";
 import { published } from "../lib/notes";
 import { buildSitemap } from "../lib/feeds";
 
-const STATIC_PAGES = ["/", "/notes/", "/kb/", "/docs/", "/about/", "/projects/", 
"/resources/", "/components/", "/status/"];
+const STATIC_PAGES = ["/", "/kb/", "/git/", "/projects/", "/about/", "/components
/", "/status/"];
 
 export const GET: APIRoute = async ({ site }) => {
-  const notes = await getCollection("notes", published);
-  const kb = await getCollection("kb", published);
-  const docs = await getCollection("docs", published);
+  const entries = await getCollection("kb", published);
   const abs = (path: string) => new URL(path, site).href;
   const xml = buildSitemap([
     ...STATIC_PAGES.map((p) => ({ loc: abs(p) })),
-    ...notes.map((n) => ({ loc: abs(`/notes/${n.id}/`), lastmod: n.data.published
 })),
-    ...kb.map((e) => ({ loc: abs(`/kb/${e.id}/`), lastmod: e.data.retrieved })),
-    ...docs.map((d) => ({ loc: abs(`/docs/${d.id}/`) })),
+    ...entries.map((e) => ({
+      loc: abs(`/kb/${e.id}/`),
+      lastmod: e.data.published ?? e.data.retrieved,
+    })),
   ]);
   return new Response(xml, {
     headers: { "Content-Type": "application/xml; charset=utf-8" },

diff --git a/ooknet-design/src/pages/sitemap
.xml.ts b/ooknet-design/src/pages/sitemap.xm
l.ts
index f47b99d..eed111d 100644
--- a/ooknet-design/src/pages/sitemap.xml.ts
+++ b/ooknet-design/src/pages/sitemap.xml.ts
@@ -3,18 +3,17 @@ import { getCollection } f
rom "astro:content";
 import { published } from "../lib/notes";
 import { buildSitemap } from "../lib/feeds"
;
 
-const STATIC_PAGES = ["/", "/notes/", "/kb/
", "/docs/", "/about/", "/projects/", "/reso
urces/", "/components/", "/status/"];
+const STATIC_PAGES = ["/", "/kb/", "/git/",
 "/projects/", "/about/", "/components/", "/
status/"];
 
 export const GET: APIRoute = async ({ site 
}) => {
-  const notes = await getCollection("notes"
, published);
-  const kb = await getCollection("kb", publ
ished);
-  const docs = await getCollection("docs", 
published);
+  const entries = await getCollection("kb",
 published);
   const abs = (path: string) => new URL(pat
h, site).href;
   const xml = buildSitemap([
     ...STATIC_PAGES.map((p) => ({ loc: abs(
p) })),
-    ...notes.map((n) => ({ loc: abs(`/notes
/${n.id}/`), lastmod: n.data.published })),
-    ...kb.map((e) => ({ loc: abs(`/kb/${e.i
d}/`), lastmod: e.data.retrieved })),
-    ...docs.map((d) => ({ loc: abs(`/docs/$
{d.id}/`) })),
+    ...entries.map((e) => ({
+      loc: abs(`/kb/${e.id}/`),
+      lastmod: e.data.published ?? e.data.r
etrieved,
+    })),
   ]);
   return new Response(xml, {
     headers: { "Content-Type": "application
/xml; charset=utf-8" },
 

diff --git a/ooknet-design/src/pages/status.astro b/ooknet-design/src/pages/status
.astro
index 78e5f1c..547bc04 100644
--- a/ooknet-design/src/pages/status.astro
+++ b/ooknet-design/src/pages/status.astro
@@ -16,30 +16,36 @@ import BigNumber from "../components/BigNumber/BigNumber.astro
";
 import BarChart from "../components/BarChart/BarChart.astro";
 import Panel from "../components/Panel/Panel.astro";
 import { rule, ruleD, row2, pad } from "../lib/ascii";
-import { published, byPublished, topSegment } from "../lib/notes";
+import { published, topSegment } from "../lib/notes";
+import { readManifests, registerOf } from "../lib/registers";
 
 const section = (title: string, right: string) => (w: number) =>
   [row2(title, right, w), ruleD(w)].join("\n");
 const LABEL_W = 12;
 
-const notes = (await getCollection("notes", published)).sort(byPublished);
-const kb = await getCollection("kb", published);
-const docs = await getCollection("docs", published);
-const total = notes.length + kb.length + docs.length;
+const entries = await getCollection("kb", published);
+const manifests = readManifests();
+const reg = (e: { id: string }) => registerOf(e.id, manifests).register;
+const notes = entries
+  .filter((e) => reg(e) === "notes" && e.data.published)
+  .sort((a, b) => a.data.published!.localeCompare(b.data.published!));
+const docs = entries.filter((e) => reg(e) === "docs");
+const reference = entries.filter((e) => reg(e) === "reference");
+const total = entries.length;
 
 const words = notes.map((n) => (n.body ?? "").split(/\s+/).filter(Boolean).length
);
 const totalWords = words.reduce((a, b) => a + b, 0);
 
-const byDate = [...notes].sort((a, b) => (a.data.published < b.data.published ? 1
 : -1));
+const byDate = [...notes].reverse();
 const latestDate = byDate[0]?.data.published ?? null;
 const [year, month] = (latestDate ?? "").split("-").map(Number);
 const monthPrefix = latestDate ? `${year}-${String(month).padStart(2, "0")}` : ""
;
 const marks = byDate
-  .filter((n) => n.data.published.startsWith(monthPrefix))
+  .filter((n) => n.data.published!.startsWith(monthPrefix))
   .map((n) => Number(n.data.published.slice(8)));
 
 const tagFreq = new Map<string, number>();
-for (const entry of [...notes, ...kb]) {
+for (const entry of entries) {
   for (const t of entry.data.tags) tagFreq.set(t, (tagFreq.get(t) ?? 0) + 1);
 }
 const topTags = [...tagFreq.entries()]
@@ -48,10 +54,10 @@ const topTags = [...tagFreq.entries()]
   .map(([label, value]) => ({ label: label.toUpperCase(), value }));
 
 const log: TimelineEntry[] = byDate.map((n) => ({
-  date: n.data.published,
+  date: n.data.published!,
   title: `FILED /${topSegment(n.id).toUpperCase()}`,
   desc: n.data.title.toLowerCase(),
-  href: `/notes/${n.id}/`,
+  href: `/kb/${n.id}/`,
 }));
 ---
 <ArticleLayout title="OOKNET KB - Status"
@@ -65,8 +71,8 @@ const log: TimelineEntry[] = byDate.map((n) => ({
 
   <KeyValue entries={[
     ["NOTES", `${notes.length} FILED`],
-    ["KB", `${kb.length} ON FILE`],
     ["DOCS", `${docs.length} PAGES`],
+    ["REFERENCE", `${reference.length} ON FILE`],
     ...(notes.length > 0
       ? [["WORDS", `${totalWords} TOTAL / ${Math.round(totalWords / notes.length)
} AVG PER NOTE`] as [string, string]]
       : []),
@@ -85,7 +91,7 @@ const log: TimelineEntry[] = byDate.map((n) => ({
 
   {topTags.length > 0 && (
     <Fragment>
-      <Frame build={section("TAXONOMY", `tags across notes + kb, top ${topTags.le
ngth}`)} />
+      <Frame build={section("TAXONOMY", `tags across the kb, top ${topTags.length
}`)} />
       <Pre>{" "}</Pre>
       <BarChart items={topTags} />
       <Pre>{" "}</Pre>

diff --git a/ooknet-design/src/pages/status.
astro b/ooknet-design/src/pages/status.astro
index 78e5f1c..547bc04 100644
--- a/ooknet-design/src/pages/status.astro
+++ b/ooknet-design/src/pages/status.astro
@@ -16,30 +16,36 @@ import BigNumber from ".
./components/BigNumber/BigNumber.astro";
 import BarChart from "../components/BarChar
t/BarChart.astro";
 import Panel from "../components/Panel/Pane
l.astro";
 import { rule, ruleD, row2, pad } from "../
lib/ascii";
-import { published, byPublished, topSegment
 } from "../lib/notes";
+import { published, topSegment } from "../l
ib/notes";
+import { readManifests, registerOf } from "
../lib/registers";
 
 const section = (title: string, right: stri
ng) => (w: number) =>
   [row2(title, right, w), ruleD(w)].join("\
n");
 const LABEL_W = 12;
 
-const notes = (await getCollection("notes",
 published)).sort(byPublished);
-const kb = await getCollection("kb", publis
hed);
-const docs = await getCollection("docs", pu
blished);
-const total = notes.length + kb.length + do
cs.length;
+const entries = await getCollection("kb", p
ublished);
+const manifests = readManifests();
+const reg = (e: { id: string }) => register
Of(e.id, manifests).register;
+const notes = entries
+  .filter((e) => reg(e) === "notes" && e.da
ta.published)
+  .sort((a, b) => a.data.published!.localeC
ompare(b.data.published!));
+const docs = entries.filter((e) => reg(e) =
== "docs");
+const reference = entries.filter((e) => reg
(e) === "reference");
+const total = entries.length;
 
 const words = notes.map((n) => (n.body ?? "
").split(/\s+/).filter(Boolean).length);
 const totalWords = words.reduce((a, b) => a
 + b, 0);
 
-const byDate = [...notes].sort((a, b) => (a
.data.published < b.data.published ? 1 : -1)
);
+const byDate = [...notes].reverse();
 const latestDate = byDate[0]?.data.publishe
d ?? null;
 const [year, month] = (latestDate ?? "").sp
lit("-").map(Number);
 const monthPrefix = latestDate ? `${year}-$
{String(month).padStart(2, "0")}` : "";
 const marks = byDate
-  .filter((n) => n.data.published.startsWit
h(monthPrefix))
+  .filter((n) => n.data.published!.startsWi
th(monthPrefix))
   .map((n) => Number(n.data.published.slice
(8)));
 
 const tagFreq = new Map<string, number>();
-for (const entry of [...notes, ...kb]) {
+for (const entry of entries) {
   for (const t of entry.data.tags) tagFreq.
set(t, (tagFreq.get(t) ?? 0) + 1);
 }
 const topTags = [...tagFreq.entries()]
@@ -48,10 +54,10 @@ const topTags = [...tagF
req.entries()]
   .map(([label, value]) => ({ label: label.
toUpperCase(), value }));
 
 const log: TimelineEntry[] = byDate.map((n)
 => ({
-  date: n.data.published,
+  date: n.data.published!,
   title: `FILED /${topSegment(n.id).toUpper
Case()}`,
   desc: n.data.title.toLowerCase(),
-  href: `/notes/${n.id}/`,
+  href: `/kb/${n.id}/`,
 }));
 ---
 <ArticleLayout title="OOKNET KB - Status"
@@ -65,8 +71,8 @@ const log: TimelineEntry[]
 = byDate.map((n) => ({
 
   <KeyValue entries={[
     ["NOTES", `${notes.length} FILED`],
-    ["KB", `${kb.length} ON FILE`],
     ["DOCS", `${docs.length} PAGES`],
+    ["REFERENCE", `${reference.length} ON F
ILE`],
     ...(notes.length > 0
       ? [["WORDS", `${totalWords} TOTAL / $
{Math.round(totalWords / notes.length)} AVG 
PER NOTE`] as [string, string]]
       : []),
@@ -85,7 +91,7 @@ const log: TimelineEntry[]
 = byDate.map((n) => ({
 
   {topTags.length > 0 && (
     <Fragment>
-      <Frame build={section("TAXONOMY", `ta
gs across notes + kb, top ${topTags.length}`
)} />
+      <Frame build={section("TAXONOMY", `ta
gs across the kb, top ${topTags.length}`)} /
>
       <Pre>{" "}</Pre>
       <BarChart items={topTags} />
       <Pre>{" "}</Pre>
 
[ BACK TO LOG ]
──────────────────────────────────────────────────────────────────────────────────────
OOKNET
────────────────────────────────────────────────
OOKNET