┌─ ooknet-design/src/content/docs/ooknet-org/authoring/wikilinks.md ─────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/authoring/wikilinks.md b/oo ││ knet-design/src/content/docs/ooknet-org/authoring/wikilinks.md ││ deleted file mode 100644 ││ index b62c3c8..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/authoring/wikilinks.md ││ +++ /dev/null ││ @@ -1,44 +0,0 @@ ││ ---- ││ -title: "Wikilinks" ││ -order: 2 ││ ---- ││ - ││ -# Syntax ││ - ││ -| FORM | RESOLVES BY | ││ -| -------------------------------- | ------------------------------ | ││ -| `[[Style Sheet]]` | title, case-insensitive | ││ -| `[[graphs/entropy-bounds]]` | file id (path in collection) | ││ -| `[[Style Sheet\|see the sheet]]` | alias - target before the pipe | ││ - ││ -Live: [[Entropy Bounds in Sparse Graphs]] by title, ││ -[[ooknet-org/style-sheet]] by id, ││ -[[Entropy Bounds in Sparse Graphs|aliased to this]] with an alias. ││ - ││ -# Resolution ││ - ││ -The map is built once per build by scanning notes, KB, and docs ││ -frontmatter. Every published page in every collection is indexed under ││ -its id and its title, so the three collections cite each other with ││ -the same syntax. Targets are trimmed and matched case-insensitively. ││ - ││ -# Dead links ││ - ││ -An unresolved target renders soft with a dashed underline - like ││ -[[A NOTE THAT DOES NOT EXIST]] - rather than failing the build. Hover ││ -shows the unresolved target. This is deliberate: drafts reference ││ -future pages, and the wiki tradition is to show the gap. ││ - ││ -# Where they work ││ - ││ -Anywhere markdown renders: notes, KB, docs, and any prose the pipeline ││ -touches. They don't fire inside code fences or existing links. ││ - ││ -# Referenced by ││ - ││ -The graph runs both ways. Every resolved wikilink also feeds a ││ -REFERENCED BY index on the target page - the pages that cite this one, ││ -listed above the footer. Dead links, drafts, and self-references stay ││ -out of the index, and literal syntax in code doesn't count as a ││ -citation. Nothing to maintain: link a page and both directions appear ││ -at the next build. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...et-org/authoring/wikilinks.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/authoring/wikilinks.md b/ooknet-d ││ esign/src/content/docs/ooknet-org/authoring/ ││ wikilinks.md ││ deleted file mode 100644 ││ index b62c3c8..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/authoring/wikilinks.md ││ +++ /dev/null ││ @@ -1,44 +0,0 @@ ││ ---- ││ -title: "Wikilinks" ││ -order: 2 ││ ---- ││ - ││ -# Syntax ││ - ││ -| FORM | RESOLV ││ ES BY | ││ -| -------------------------------- | ------ ││ ------------------------ | ││ -| `[[Style Sheet]]` | title, ││ case-insensitive | ││ -| `[[graphs/entropy-bounds]]` | file i ││ d (path in collection) | ││ -| `[[Style Sheet\|see the sheet]]` | alias ││ - target before the pipe | ││ - ││ -Live: [[Entropy Bounds in Sparse Graphs]] b ││ y title, ││ -[[ooknet-org/style-sheet]] by id, ││ -[[Entropy Bounds in Sparse Graphs|aliased t ││ o this]] with an alias. ││ - ││ -# Resolution ││ - ││ -The map is built once per build by scanning ││ notes, KB, and docs ││ -frontmatter. Every published page in every ││ collection is indexed under ││ -its id and its title, so the three collecti ││ ons cite each other with ││ -the same syntax. Targets are trimmed and ma ││ tched case-insensitively. ││ - ││ -# Dead links ││ - ││ -An unresolved target renders soft with a da ││ shed underline - like ││ -[[A NOTE THAT DOES NOT EXIST]] - rather tha ││ n failing the build. Hover ││ -shows the unresolved target. This is delibe ││ rate: drafts reference ││ -future pages, and the wiki tradition is to ││ show the gap. ││ - ││ -# Where they work ││ - ││ -Anywhere markdown renders: notes, KB, docs, ││ and any prose the pipeline ││ -touches. They don't fire inside code fences ││ or existing links. ││ - ││ -# Referenced by ││ - ││ -The graph runs both ways. Every resolved wi ││ kilink also feeds a ││ -REFERENCED BY index on the target page - th ││ e pages that cite this one, ││ -listed above the footer. Dead links, drafts ││ , and self-references stay ││ -out of the index, and literal syntax in cod ││ e doesn't count as a ││ -citation. Nothing to maintain: link a page ││ and both directions appear ││ -at the next build. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.md ───┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.m ││ d b/ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.md ││ deleted file mode 100644 ││ index 7b93cf0..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/authoring/writing-content.md ││ +++ /dev/null ││ @@ -1,56 +0,0 @@ ││ ---- ││ -title: "Writing content" ││ -order: 1 ││ ---- ││ - ││ -# Three collections ││ - ││ -Written content lives in `src/content/` in three collections, each ││ -with its own register: ││ - ││ -| COLLECTION | WHAT GOES THERE | SPLIT BY | ││ -| ---------- | -------------------------------- | -------- | ││ -| `notes/` | personal writing, study notes | subject | ││ -| `kb/` | vendor docs and guides, credited | topic | ││ -| `docs/` | project documentation | project | ││ - ││ -The directory is the taxonomy: `notes/nix/flakes.md` files a note ││ -under the NIX subject, `docs/wowsim-stats/api.md` documents the ││ -wowsim-stats project. No frontmatter field repeats what the path ││ -already says. ││ - ││ -# Notes ││ - ││ -```yaml ││ -title: "ENTROPY BOUNDS IN SPARSE GRAPHS" ││ -published: "2026-04-19" ││ -status: "PEER-CHECKED [PASS]" ││ -tags: ["entropy", "sparse-graphs"] ││ -``` ││ - ││ -Only `title` and `published` are required. ││ - ││ -# Knowledge base ││ - ││ -KB entries are copies of external material and always carry the ││ -credit: ││ - ││ -```yaml ││ -title: "COLLECT DATA TO ANALYZE PERFORMANCE SCENARIOS" ││ -source: "MICROSOFT LEARN" ││ -url: "https://learn.microsoft.com/..." ││ -retrieved: "2026-04-25" ││ -tags: ["windows", "performance"] ││ -``` ││ - ││ -`source` is the vendor; `url` and `retrieved` say where and when the ││ -copy was taken. ││ - ││ -# Cross-references ││ - ││ -All three collections share one wikilink map - a note can cite a KB ││ -entry, a doc can cite a note. See [[Wikilinks]] for the syntax. Every ││ -resolved link also feeds the REFERENCED BY index on the target page. ││ - ││ -Set `draft: true` in any collection to keep a page out of the index, ││ -the search, and the wikilink map. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../authoring/writing-content.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/authoring/writing-content.md b/oo ││ knet-design/src/content/docs/ooknet-org/auth ││ oring/writing-content.md ││ deleted file mode 100644 ││ index 7b93cf0..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/authoring/writing-content.md ││ +++ /dev/null ││ @@ -1,56 +0,0 @@ ││ ---- ││ -title: "Writing content" ││ -order: 1 ││ ---- ││ - ││ -# Three collections ││ - ││ -Written content lives in `src/content/` in ││ three collections, each ││ -with its own register: ││ - ││ -| COLLECTION | WHAT GOES THERE ││ | SPLIT BY | ││ -| ---------- | ---------------------------- ││ ---- | -------- | ││ -| `notes/` | personal writing, study note ││ s | subject | ││ -| `kb/` | vendor docs and guides, cred ││ ited | topic | ││ -| `docs/` | project documentation ││ | project | ││ - ││ -The directory is the taxonomy: `notes/nix/f ││ lakes.md` files a note ││ -under the NIX subject, `docs/wowsim-stats/a ││ pi.md` documents the ││ -wowsim-stats project. No frontmatter field ││ repeats what the path ││ -already says. ││ - ││ -# Notes ││ - ││ -```yaml ││ -title: "ENTROPY BOUNDS IN SPARSE GRAPHS" ││ -published: "2026-04-19" ││ -status: "PEER-CHECKED [PASS]" ││ -tags: ["entropy", "sparse-graphs"] ││ -``` ││ - ││ -Only `title` and `published` are required. ││ - ││ -# Knowledge base ││ - ││ -KB entries are copies of external material ││ and always carry the ││ -credit: ││ - ││ -```yaml ││ -title: "COLLECT DATA TO ANALYZE PERFORMANCE ││ SCENARIOS" ││ -source: "MICROSOFT LEARN" ││ -url: "https://learn.microsoft.com/..." ││ -retrieved: "2026-04-25" ││ -tags: ["windows", "performance"] ││ -``` ││ - ││ -`source` is the vendor; `url` and `retrieve ││ d` say where and when the ││ -copy was taken. ││ - ││ -# Cross-references ││ - ││ -All three collections share one wikilink ma ││ p - a note can cite a KB ││ -entry, a doc can cite a note. See [[Wikilin ││ ks]] for the syntax. Every ││ -resolved link also feeds the REFERENCED BY ││ index on the target page. ││ - ││ -Set `draft: true` in any collection to keep ││ a page out of the index, ││ -the search, and the wikilink map. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/design/components.md ───────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/design/components.md b/ookn ││ et-design/src/content/docs/ooknet-org/design/components.md ││ deleted file mode 100644 ││ index d42413f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/design/components.md ││ +++ /dev/null ││ @@ -1,28 +0,0 @@ ││ ---- ││ -title: "Component library" ││ -order: 3 ││ ---- ││ - ││ -# Two kinds of component ││ - ││ -**Static** components are pure builders: a `.ts` function takes a ││ -width and returns a string, unit-tested, rendered at both frame widths ││ -through `Frame`. Tables, trees, timelines, gauges, calendars. ││ - ││ -**Interactive** components render once and wear ASCII chrome over ││ -native elements - a checkbox behind `[x]`, a range input over ││ -`├───█───┤`. They can't dual-render: duplicating an input duplicates ││ -its name and splits state across variants. ││ - ││ -# Conventions ││ - ││ -- one directory per component: `.astro`, `.ts`, optional `.scss` and `.test.ts` ││ -- every frame string passes `checkGrid` before it ships ││ -- motion is discrete and glyph-stepped, never a CSS fade ││ -- the live inventory renders on the [components page](/components) ││ - ││ -# Where to look first ││ - ││ -`Frame.astro` is the bridge between builders and the page. `Pre.astro` ││ -is the strict grid. Copy an existing component's directory and follow ││ -its shape. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...knet-org/design/components.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/design/components.md b/ooknet-des ││ ign/src/content/docs/ooknet-org/design/compo ││ nents.md ││ deleted file mode 100644 ││ index d42413f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/design/components.md ││ +++ /dev/null ││ @@ -1,28 +0,0 @@ ││ ---- ││ -title: "Component library" ││ -order: 3 ││ ---- ││ - ││ -# Two kinds of component ││ - ││ -**Static** components are pure builders: a ││ `.ts` function takes a ││ -width and returns a string, unit-tested, re ││ ndered at both frame widths ││ -through `Frame`. Tables, trees, timelines, ││ gauges, calendars. ││ - ││ -**Interactive** components render once and ││ wear ASCII chrome over ││ -native elements - a checkbox behind `[x]`, ││ a range input over ││ -`├───█───┤`. They can't dual-render: duplic ││ ating an input duplicates ││ -its name and splits state across variants. ││ - ││ -# Conventions ││ - ││ -- one directory per component: `.astro`, `. ││ ts`, optional `.scss` and `.test.ts` ││ -- every frame string passes `checkGrid` bef ││ ore it ships ││ -- motion is discrete and glyph-stepped, nev ││ er a CSS fade ││ -- the live inventory renders on the [compon ││ ents page](/components) ││ - ││ -# Where to look first ││ - ││ -`Frame.astro` is the bridge between builder ││ s and the page. `Pre.astro` ││ -is the strict grid. Copy an existing compon ││ ent's directory and follow ││ -its shape. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/design/frames.md ───────────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/design/frames.md b/ooknet-d ││ esign/src/content/docs/ooknet-org/design/frames.md ││ deleted file mode 100644 ││ index 71dad8f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/design/frames.md ││ +++ /dev/null ││ @@ -1,36 +0,0 @@ ││ ---- ││ -title: "Frames & the grid" ││ -order: 1 ││ ---- ││ - ││ -# The grid is the contract ││ - ││ -Every visual element on this site is text on a monospace grid. The ││ -frame is 86 cells wide on desktop and 48 on mobile; components render ││ -both variants at build time and CSS shows one per viewport. ││ - ││ -| CONSTANT | CELLS | WHERE | ││ -| -------------- | ----: | -------------------- | ││ -| FRAME_W | 86 | src/lib/config.ts | ││ -| MOBILE_FRAME_W | 48 | src/lib/config.ts | ││ - ││ -# checkGrid ││ - ││ -Nothing ships that would bend a border. Every frame passes through ││ -`checkGrid`, which fails the build if a line overflows its width or ││ -uses a glyph the frame font doesn't ship at one cell wide: ││ - ││ -```ts ││ -export function checkGrid(text: string, maxW: number, ctx: string): string { ││ - // throws on overflow or off-grid codepoints ││ -} ││ -``` ││ - ││ -The full glyph policy lives in [[Style Sheet]] - the doc that ││ -exercises every construct the pipeline renders. ││ - ││ -# Fonts ││ - ││ -Prose sets in IBM Plex Mono; frames set in Cascadia Mono, whose ││ -box-drawing glyphs are uniformly one cell. Never mix them on the same ││ -line of a frame. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...s/ooknet-org/design/frames.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/design/frames.md b/ooknet-design/ ││ src/content/docs/ooknet-org/design/frames.md ││ deleted file mode 100644 ││ index 71dad8f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/design/frames.md ││ +++ /dev/null ││ @@ -1,36 +0,0 @@ ││ ---- ││ -title: "Frames & the grid" ││ -order: 1 ││ ---- ││ - ││ -# The grid is the contract ││ - ││ -Every visual element on this site is text o ││ n a monospace grid. The ││ -frame is 86 cells wide on desktop and 48 on ││ mobile; components render ││ -both variants at build time and CSS shows o ││ ne per viewport. ││ - ││ -| CONSTANT | CELLS | WHERE ││ | ││ -| -------------- | ----: | ---------------- ││ ---- | ││ -| FRAME_W | 86 | src/lib/config.t ││ s | ││ -| MOBILE_FRAME_W | 48 | src/lib/config.t ││ s | ││ - ││ -# checkGrid ││ - ││ -Nothing ships that would bend a border. Eve ││ ry frame passes through ││ -`checkGrid`, which fails the build if a lin ││ e overflows its width or ││ -uses a glyph the frame font doesn't ship at ││ one cell wide: ││ - ││ -```ts ││ -export function checkGrid(text: string, max ││ W: number, ctx: string): string { ││ - // throws on overflow or off-grid codepoi ││ nts ││ -} ││ -``` ││ - ││ -The full glyph policy lives in [[Style Shee ││ t]] - the doc that ││ -exercises every construct the pipeline rend ││ ers. ││ - ││ -# Fonts ││ - ││ -Prose sets in IBM Plex Mono; frames set in ││ Cascadia Mono, whose ││ -box-drawing glyphs are uniformly one cell. ││ Never mix them on the same ││ -line of a frame. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/design/pipeline.md ─────────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/design/pipeline.md b/ooknet ││ -design/src/content/docs/ooknet-org/design/pipeline.md ││ deleted file mode 100644 ││ index b84668f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/design/pipeline.md ││ +++ /dev/null ││ @@ -1,30 +0,0 @@ ││ ---- ││ -title: "Markdown pipeline" ││ -order: 2 ││ ---- ││ - ││ -# One source, ASCII out ││ - ││ -Notes and docs are plain markdown - previewable in Obsidian and ││ -GitHub - transformed at build time by `rehype-ascii`: ││ - ││ -| CONSTRUCT | BECOMES | ││ -| -------------- | ---------------------------------------- | ││ -| fenced code | framed box with [ COPY ] | ││ -| table | ASCII table + sr-only semantic table | ││ -| [!NOTE] quote | labeled callout | ││ -| lone image | numbered figure | ││ -| diagram fence | verbatim ASCII art as a figure | ││ -| mermaid fence | generated diagram as a figure | ││ -| diff fence | framed diff, adds bold, deletions soft | ││ - ││ -# Diagrams ││ - ││ -A `mermaid` fence renders linear chains through the house layout and ││ -delegates branching graphs to mermaid-ascii. A `diagram` fence keeps ││ -hand-drawn art verbatim. Both take a `:: CAPTION` first line. ││ - ││ -# Wikilinks ││ - ││ -`[[target]]` resolves against notes and docs at build time - see ││ -[[Wikilinks]] for the syntax. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...ooknet-org/design/pipeline.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/design/pipeline.md b/ooknet-desig ││ n/src/content/docs/ooknet-org/design/pipelin ││ e.md ││ deleted file mode 100644 ││ index b84668f..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/design/pipeline.md ││ +++ /dev/null ││ @@ -1,30 +0,0 @@ ││ ---- ││ -title: "Markdown pipeline" ││ -order: 2 ││ ---- ││ - ││ -# One source, ASCII out ││ - ││ -Notes and docs are plain markdown - preview ││ able in Obsidian and ││ -GitHub - transformed at build time by `rehy ││ pe-ascii`: ││ - ││ -| CONSTRUCT | BECOMES ││ | ││ -| -------------- | ------------------------ ││ ---------------- | ││ -| fenced code | framed box with [ COPY ] ││ | ││ -| table | ASCII table + sr-only se ││ mantic table | ││ -| [!NOTE] quote | labeled callout ││ | ││ -| lone image | numbered figure ││ | ││ -| diagram fence | verbatim ASCII art as a ││ figure | ││ -| mermaid fence | generated diagram as a f ││ igure | ││ -| diff fence | framed diff, adds bold, ││ deletions soft | ││ - ││ -# Diagrams ││ - ││ -A `mermaid` fence renders linear chains thr ││ ough the house layout and ││ -delegates branching graphs to mermaid-ascii ││ . A `diagram` fence keeps ││ -hand-drawn art verbatim. Both take a `:: CA ││ PTION` first line. ││ - ││ -# Wikilinks ││ - ││ -`[[target]]` resolves against notes and doc ││ s at build time - see ││ -[[Wikilinks]] for the syntax. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/overview.md ────────────────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/overview.md b/ooknet-design ││ /src/content/docs/ooknet-org/overview.md ││ deleted file mode 100644 ││ index ff3744b..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/overview.md ││ +++ /dev/null ││ @@ -1,14 +0,0 @@ ││ ---- ││ -title: "Overview" ││ -order: 0 ││ ---- ││ - ││ -# ooknet.org ││ - ││ -This site: a text-mode knowledge base rendered entirely as ASCII on a ││ -strict monospace grid. Notes, a credited knowledge base, and project ││ -documentation, cross-linked by a build-time wiki graph. ││ - ││ -The design system behind it lives in these docs - start with ││ -[[ooknet-org/design/frames]] for how the grid works, or the ││ -[[Style Sheet]] to see every construct the pipeline renders. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...t/docs/ooknet-org/overview.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/overview.md b/ooknet-design/src/c ││ ontent/docs/ooknet-org/overview.md ││ deleted file mode 100644 ││ index ff3744b..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/overview.md ││ +++ /dev/null ││ @@ -1,14 +0,0 @@ ││ ---- ││ -title: "Overview" ││ -order: 0 ││ ---- ││ - ││ -# ooknet.org ││ - ││ -This site: a text-mode knowledge base rende ││ red entirely as ASCII on a ││ -strict monospace grid. Notes, a credited kn ││ owledge base, and project ││ -documentation, cross-linked by a build-time ││ wiki graph. ││ - ││ -The design system behind it lives in these ││ docs - start with ││ -[[ooknet-org/design/frames]] for how the gr ││ id works, or the ││ -[[Style Sheet]] to see every construct the ││ pipeline renders. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/docs/ooknet-org/style-sheet.md ─────────────────┐│ diff --git a/ooknet-design/src/content/docs/ooknet-org/style-sheet.md b/ooknet-des ││ ign/src/content/docs/ooknet-org/style-sheet.md ││ deleted file mode 100644 ││ index 9fd4c05..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet-org/style-sheet.md ││ +++ /dev/null ││ @@ -1,124 +0,0 @@ ││ ---- ││ -title: "Style sheet" ││ -description: "Exercises every markdown construct the ASCII pipeline renders." ││ -order: 99 ││ ---- ││ - ││ -# Purpose ││ - ││ -This sheet exercises every markdown construct the ASCII pipeline ││ -renders. If a change to the design system breaks layout, it should be ││ -visible on this page first. ││ - ││ -# Tables ││ - ││ -| GLYPH SET | CELLS | STATUS | ││ -| --------- | ----: | -------- | ││ -| single | 86 | ACTIVE | ││ -| double | 48 | RESERVED | ││ -| heavy | 86 | DROPPED | ││ - ││ -A wider table that must shrink and wrap on the narrow frame: ││ - ││ -| COMPONENT | RESPONSIBILITY | NOTES ││ | ││ -| --------- | ----------------------------------------- | ----------------------- ││ ----- | ││ -| Frame | renders a build function at both widths | static content only ││ | ││ -| TextField | input seated on an underscore placeholder | occupies exact cells ││ | ││ -| Select | native dropdown inside bracket chrome | arrow glyph must be on- ││ grid | ││ - ││ -# Callouts ││ - ││ -> [!NOTE] ││ -> The frame font must ship every glyph the renderer emits. Unknown ││ -> codepoints fall back to a wider face and break the cell grid. ││ - ││ -> [!WARNING] ││ -> Interactive elements cannot dual-render: duplicating an input ││ -> duplicates its name and splits radio state across variants. ││ - ││ -> A plain blockquote is unaffected by the callout transform and keeps ││ -> its italic rail styling. ││ - ││ -# Figures ││ - ││ - ││ - ││ -# Diagrams ││ - ││ -A `diagram` fence renders hand-drawn ASCII art as a numbered figure. ││ -The first line may set a caption with `::`. ││ - ││ -```diagram ││ -:: THE SAME BUILD FUNCTION AT BOTH WIDTHS ││ - ┌──────────────┐ ││ - │ build(width) │ ││ - └──────┬───────┘ ││ - ┌────────────┴────────────┐ ││ - ┌──────┴───────┐ ┌────────┴───────┐ ││ - │ FRAME_W = 86 │ │ MOBILE_W = 48 │ ││ - │ .frame-wide │ │ .frame-narrow │ ││ - └──────────────┘ └────────────────┘ ││ -``` ││ - ││ -A `mermaid` fence renders at build time - and previews live in ││ -Obsidian and GitHub, since it's plain mermaid. Linear chains lay out ││ -through the house renderer; chains that fit the frame run ││ -horizontally, longer ones stack on a vertical spine. ││ - ││ -```mermaid ││ -%% :: NOTE PIPELINE, GENERATED FROM MERMAID SOURCE ││ -graph LR ││ -md[markdown] -->|transforms| rh[rehype] ││ -rh -->|emits| fr[frames] ││ -fr -->|validates| vg[checkGrid] ││ -``` ││ - ││ -Branching graphs delegate to mermaid-ascii. Keep edge labels to ││ -chains - labels on branching edges can collide (an upstream ││ -mermaid-ascii issue): ││ - ││ -```mermaid ││ -%% :: MARKDOWN FANS OUT AND REJOINS ││ -graph TD ││ -md[markdown] --> t[tables] ││ -md --> d[diagrams] ││ -t --> pg[page] ││ -d --> pg ││ -``` ││ - ││ -# Code ││ - ││ -```ts ││ -export function pad(s: unknown, n: number): string { ││ - const str = String(s ?? ""); ││ - return str + " ".repeat(Math.max(0, n - len(str))); ││ -} ││ -``` ││ - ││ -A `diff` fence renders additions bold and deletions soft - the ││ -palette stays monochrome: ││ - ││ -```diff ││ -@@ -1,3 +1,4 @@ ││ - export function pad(s: unknown, n: number): string { ││ -- return String(s).padEnd(n); ││ -+ const str = String(s ?? ""); ││ -+ return str + " ".repeat(Math.max(0, n - len(str))); ││ - } ││ -``` ││ - ││ -# Wikilinks ││ - ││ -A wikilink resolves by title [[Entropy Bounds in Sparse Graphs]], ││ -by file id [[graphs/entropy-bounds]], or doc path ││ -[[ooknet-org/design/frames]], and takes an alias: ││ -[[Entropy Bounds in Sparse Graphs|the entropy note]]. Unresolved ││ -targets render dead: [[A NOTE THAT DOES NOT EXIST]]. ││ - ││ -# Lists ││ - ││ -- frames are strings, components are chrome ││ -- the grid is the contract ││ - 1. measure in cells ││ - 2. wrap at build time ││ - 3. never trust `ch` │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...ocs/ooknet-org/style-sheet.md ───┐│ diff --git a/ooknet-design/src/content/docs/ ││ ooknet-org/style-sheet.md b/ooknet-design/sr ││ c/content/docs/ooknet-org/style-sheet.md ││ deleted file mode 100644 ││ index 9fd4c05..0000000 ││ --- a/ooknet-design/src/content/docs/ooknet- ││ org/style-sheet.md ││ +++ /dev/null ││ @@ -1,124 +0,0 @@ ││ ---- ││ -title: "Style sheet" ││ -description: "Exercises every markdown cons ││ truct the ASCII pipeline renders." ││ -order: 99 ││ ---- ││ - ││ -# Purpose ││ - ││ -This sheet exercises every markdown constru ││ ct the ASCII pipeline ││ -renders. If a change to the design system b ││ reaks layout, it should be ││ -visible on this page first. ││ - ││ -# Tables ││ - ││ -| GLYPH SET | CELLS | STATUS | ││ -| --------- | ----: | -------- | ││ -| single | 86 | ACTIVE | ││ -| double | 48 | RESERVED | ││ -| heavy | 86 | DROPPED | ││ - ││ -A wider table that must shrink and wrap on ││ the narrow frame: ││ - ││ -| COMPONENT | RESPONSIBILITY ││ | NOTES ││ | ││ -| --------- | ----------------------------- ││ ------------ | ---------------------------- ││ | ││ -| Frame | renders a build function at b ││ oth widths | static content only ││ | ││ -| TextField | input seated on an underscore ││ placeholder | occupies exact cells ││ | ││ -| Select | native dropdown inside bracke ││ t chrome | arrow glyph must be on-grid ││ | ││ - ││ -# Callouts ││ - ││ -> [!NOTE] ││ -> The frame font must ship every glyph the ││ renderer emits. Unknown ││ -> codepoints fall back to a wider face and ││ break the cell grid. ││ - ││ -> [!WARNING] ││ -> Interactive elements cannot dual-render: ││ duplicating an input ││ -> duplicates its name and splits radio stat ││ e across variants. ││ - ││ -> A plain blockquote is unaffected by the c ││ allout transform and keeps ││ -> its italic rail styling. ││ - ││ -# Figures ││ - ││ - ││ - ││ -# Diagrams ││ - ││ -A `diagram` fence renders hand-drawn ASCII ││ art as a numbered figure. ││ -The first line may set a caption with `::`. ││ - ││ -```diagram ││ -:: THE SAME BUILD FUNCTION AT BOTH WIDTHS ││ - ┌──────────────┐ ││ - │ build(width) │ ││ - └──────┬───────┘ ││ - ┌────────────┴────────────┐ ││ - ┌──────┴───────┐ ┌────────┴────── ││ ─┐ ││ - │ FRAME_W = 86 │ │ MOBILE_W = 48 ││ │ ││ - │ .frame-wide │ │ .frame-narrow ││ │ ││ - └──────────────┘ └─────────────── ││ ─┘ ││ -``` ││ - ││ -A `mermaid` fence renders at build time - a ││ nd previews live in ││ -Obsidian and GitHub, since it's plain merma ││ id. Linear chains lay out ││ -through the house renderer; chains that fit ││ the frame run ││ -horizontally, longer ones stack on a vertic ││ al spine. ││ - ││ -```mermaid ││ -%% :: NOTE PIPELINE, GENERATED FROM MERMAID ││ SOURCE ││ -graph LR ││ -md[markdown] -->|transforms| rh[rehype] ││ -rh -->|emits| fr[frames] ││ -fr -->|validates| vg[checkGrid] ││ -``` ││ - ││ -Branching graphs delegate to mermaid-ascii. ││ Keep edge labels to ││ -chains - labels on branching edges can coll ││ ide (an upstream ││ -mermaid-ascii issue): ││ - ││ -```mermaid ││ -%% :: MARKDOWN FANS OUT AND REJOINS ││ -graph TD ││ -md[markdown] --> t[tables] ││ -md --> d[diagrams] ││ -t --> pg[page] ││ -d --> pg ││ -``` ││ - ││ -# Code ││ - ││ -```ts ││ -export function pad(s: unknown, n: number): ││ string { ││ - const str = String(s ?? ""); ││ - return str + " ".repeat(Math.max(0, n - l ││ en(str))); ││ -} ││ -``` ││ - ││ -A `diff` fence renders additions bold and d ││ eletions soft - the ││ -palette stays monochrome: ││ - ││ -```diff ││ -@@ -1,3 +1,4 @@ ││ - export function pad(s: unknown, n: number) ││ : string { ││ -- return String(s).padEnd(n); ││ -+ const str = String(s ?? ""); ││ -+ return str + " ".repeat(Math.max(0, n - ││ len(str))); ││ - } ││ -``` ││ - ││ -# Wikilinks ││ - ││ -A wikilink resolves by title [[Entropy Boun ││ ds in Sparse Graphs]], ││ -by file id [[graphs/entropy-bounds]], or do ││ c path ││ -[[ooknet-org/design/frames]], and takes an ││ alias: ││ -[[Entropy Bounds in Sparse Graphs|the entro ││ py note]]. Unresolved ││ -targets render dead: [[A NOTE THAT DOES NOT ││ EXIST]]. ││ - ││ -# Lists ││ - ││ -- frames are strings, components are chrome ││ -- the grid is the contract ││ - 1. measure in cells ││ - 2. wrap at build time ││ - 3. never trust `ch` │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/notes/ai/using-ai-to-troubleshoot.md ───────────┐│ diff --git a/ooknet-design/src/content/notes/ai/using-ai-to-troubleshoot.md b/ookn ││ et-design/src/content/notes/ai/using-ai-to-troubleshoot.md ││ deleted file mode 100644 ││ index c18eb2d..0000000 ││ --- a/ooknet-design/src/content/notes/ai/using-ai-to-troubleshoot.md ││ +++ /dev/null ││ @@ -1,181 +0,0 @@ ││ ---- ││ -title: "USING AI TO TROUBLESHOOT AND RESOLVE ISSUES" ││ -published: "2026-04-27" ││ -status: "DRAFT" ││ -tags: ["ai", "troubleshooting", "operations", "case-study"] ││ ---- ││ - ││ -In this guide I want to share my experience using AI to analyse, troubleshoot and ││ resolve issues. ││ - ││ -# intro ││ - ││ -For some issues the solution is clear, documented, and known. For example: ││ - ││ -> "I can't login to VPN" ││ - ││ -**Triage steps:** ││ - ││ -- Are they connected to the internet -> Connect to internet ││ -- Is their account locked? -> Unlock their account ││ -- Is their password expired -> Notify user their password has reset, walk through ││ steps on how to reset password ││ -- Is their DUO locked -> Confirm if they where the reason for the lock -> Unlock ││ DUO ││ -- Are they not accepting DUO prompt -> Ask them to open the DUO app and try again ││ -- Not receiving DUO prompt even with application open -> Verify their DUO details ││ are correct (Phone number and Device) ││ -- Still not receiving DUO Prompt -> Restart mobile device ││ -- Still not recieving DUO prompt -> Re-Enroll user in DUO ││ - ││ -Clear steps to follow, no ambiguity. ││ - ││ -Another example: ││ - ││ -> "My computer is running slow" ││ - ││ -This is where it can get hazy, often the best way to proceed is to start asking m ││ ore questions. ││ - ││ -"When is it slow?" ││ -"Is it always slow?" ││ -"What feels slow?" ││ - ││ -Often the user doesn't have clear answers to these questions, so we start going t ││ hrough common troubleshooting steps: ││ - ││ -- Restart the device ││ -- Check start-up programs and services ││ -- Check if they need OS/Driver updates ││ -- Look through processes running in task manager ││ -- Check storage space ││ - ││ -Sometimes the resolution looks clear, they have too many programs/browser tabs op ││ en at all times and they haven't restarted their device in 24 days. You ask the us ││ er to change behaviour and monitor over the next week to see if the issue persists ││ . ││ - ││ -But what if it all looks fine? You restarted the computer, they aren't opening 10 ││ 0s of tabs in Edge, they are fully up-to-date with their updates. How do you proce ││ ed? ││ - ││ -A common solution is the nuclear one, replace device/re-image. This is often the ││ simplest and sometimes least time consuming approach. But it's not ideal (in most ││ cases). Another approach would be to dig deeper, look into the logs of the device, ││ try to work out what the issue might be. This can be very time consuming, and oft ││ en lead to dead ends, ultimately landing you back to the nuclear option. In this d ││ ocument I want to provide my thoughts on how to best approach the alternative to t ││ he nuclear option, in an efficient manner utilising AI. ││ - ││ -# Using AI ││ - ││ -So you might decide you want to see what an LLM thinks, you prompt it with someth ││ ing like: ││ - ││ -> I have a user who reports their computer is running slow, any ideas? ││ - ││ -The LLM might respond with some generic troubleshooting steps we already know, or ││ ask you the same questions you asked the user (to no avail). ││ - ││ -Maybe it provides some additional steps to try, clear Office cache, run a repair ││ tool. It doesn't help, you go back to thinking of replacing the laptop. ││ - ││ -Maybe the issue is you didn't prompt it good enough? Maybe you need better word i ││ t better... ││ - ││ -> You are an expert IT system admin advising me on how best to approach a user wh ││ o is having trouble with a slow computer. ││ -> ││ -> User claims the computer doesn't always feel slow, just sometimes, then progra ││ ms start to crash and so they turn their computer off. ││ -> ││ -> Steps already taken: ││ -> - Restarted computer ││ -> - Updated Drivers and OS ││ -> - Checked RAM and CPU usage ││ -> ││ -> All seemed fine on my end, how do I proceed? ││ - ││ -You get back an emoji filled essay of things to try, most of which are not helpfu ││ l to you. The problem here is not how the prompt is written, it's that we gave it ││ nothing to work with. All we did was tee it up to write a "Top 10 Troubleshooting ││ steps for Windows 2026" blog post. ││ - ││ -What we need to do is give it the details, provide it with the context it needs t ││ o help find what the real issue is. ││ -# Event logs ││ - ││ -Event logs are Windows' central system for logging. Every time a service starts, ││ a driver loads, an app crashes, or a Windows component decides something is worth ││ noting - Windows writes an entry to the event log. ││ - ││ -Typically you read it through the Event Viewer (`eventvwr.msc`) accessible via ** ││ Start -> "Event Viewer"**. The left pane is a tree of log files. With the most com ││ mon "Channels" being: ││ - ││ -- **Application** - Anything an installed program logged. Crashes (Event ID 1000) ││ , hangs (1002), .NET errors, Office issues. ││ -- **System** - The operating system itself. Driver problems, service start/stop, ││ disk errors, unexpected shutdowns, hardware events. ││ -- **Security** - logons, logoffs, privilege use, account changes. ││ - ││ -Below those are the **Applications and Services Logs**, this is where Microsoft f ││ eatures and third-party software keep their own private logs. This is often where ││ the genuinely useful information lives. For example `Microsoft ? Windows ? Diagnos ││ tics-Performance ? Operational` for slow boot/shutdown investigations. ││ - ││ -Every entry has the same handful of fields: ││ - ││ -- **Level** - Critical, Error, Warning, Information, Verbose. On a healthy machin ││ e 99% of events are Information; filter those out first. ││ -- **Source** - What generated the entry (e.g. `Application Error`, `disk`, `Servi ││ ce Control Manager`, `Microsoft-Windows-Kernel-Power`). ││ -- **Event ID** - A number that uniquely identifies the _kind_ of event for that s ││ ource. `disk` Event ID 7 always means "the device has a bad block." Once you know ││ the (Source, Event ID) pair, you can look it up. ││ -- **Date/Time** - When it happened. Critical for correlating with what the user r ││ eported. ││ -- **Message** - A human-readable description, usually with the useful details (fi ││ le path, error code, process name, exit code). ││ - ││ -Two things make event logs especially well suited to handing to an LLM: ││ - ││ -1. **They're already structured.** Each entry is self-contained. Source, ID, mess ││ age, timestamp - this is exactly the shape an LLM needs to reason about without yo ││ u having to explain the surrounding context. ││ -2. **Event IDs are well-documented.** Microsoft docs, vendor KBs, and twenty year ││ s of forum posts mean the model has almost certainly seen the (Source, ID) pair yo ││ u're looking at and can tell you what it usually means and what tends to cause it. ││ - ││ -Typically the challenge with Event logs comes from its volume. On an average work ││ ing day a Windows machine might emit thousands of events. The skill here is the ab ││ ility to read through the noise. In the world of InfoSec, this often involves the ││ use of external tools such as Splunk and Log Parser Studio to interrogate the info ││ rmation you need for your investigation. For our purposes, these tools are not nee ││ ded, I want to propose an alternative utilising PowerShell, Excel, and Copilot. ││ - ││ -# Collecting Event logs ││ - ││ -Because all event logs are just .NET objects - exporting, manipulating, and filte ││ ring is easily done via PowerShell's builtin cmdlet `Get-WinEvent`. ││ - ││ -Example: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} ││ -``` ││ - ││ -This will return all level 1, 2 and 3 event logs from the "Application" channel. ││ From there we can pipe the output to `Select-Object` to get the fields we want: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} | Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, Message ││ -``` ││ - ││ -Then pipe that to an `Export-*` of our desired format, in this case, a CSV. The ` ││ @{...}` expression on `Message` flattens any embedded newlines into spaces, withou ││ t it, multi-line error messages would break the CSV across multiple rows: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} | Select-Object TimeCreated, Id, LevelDisplayName, ProviderName, ││ - @{N='Message'; E={ $_.Message -replace "`r`n",' ' }} | ││ -Export-Csv -NoTypeInformation -Path "$env:USERPROFILE\Desktop\events.csv" ││ -``` ││ - ││ -This will leave us with a `csv` file of all the "Application" channel logs from t ││ he past 24 hours containing the fields we specified (TimeCreated, Id, LevelDisplay ││ Name, ProviderName, Message). ││ - ││ -I won't get too deep into the PowerShell here. You can read more about the `Get-W ││ inEvent` and `Export-Csv` below: ││ - ││ -- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.diagno ││ stics/get-winevent?view=powershell-7.6 ││ -- https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.utilit ││ y/export-csv?view=powershell-7.6 ││ - ││ - ││ - ││ -# Using the Event logs with AI ││ - ││ -With the exported workbook, the easiest path would be to upload it to a Copilot c ││ hat and ask "any ideas what's going on here?" That works, but in my experience it ││ hands too much over to the LLM. You get an answer, you accept it (or you don't), a ││ nd the conversation is over. You've cut yourself out of the investigation, and you ││ 've lost the chance to spot anything the model glossed over. ││ - ││ -I now mostly use the Copilot agent directly inside Excel to keep the work collabo ││ rative. The workbook is open in front of you, Copilot edits it as you go, and you ││ can push back on what it suggests rather than just receive an answer. ││ - ││ -# Case study ││ - ││ -The following is a case study on a real world use of this technique. ││ - ││ -## The ticket ││ - ││ -**Title**: Laptop running slow, unable to complete any work ││ -**Description**: Laptop running slow. Unable to open and use outlook, teams or fi ││ les. Continuously freezing also. Unable to get any work done. ││ - ││ -## Steps taken ││ - ││ -1. Spoke to the user to get more information and learnt the following ││ - - This started to happen a couple weeks ago ││ - - No other useful information provided ││ -2. Checked disk space - Plenty of space ││ -3. Checked uptime - Had been rebooted recently ││ -4. Checked processes - Nothing stood out ││ -5. Check start-up software/services - Minimal ││ - ││ -When inspecting the computer, everything seemed to performing well. ││ -For deeper collection Microsoft's [[windows/collect-data-tss|TSS toolset]] ││ -is the heavier option; here I dumped some logs using the following script ││ - ││ - ││ - │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...i/using-ai-to-troubleshoot.md ───┐│ diff --git a/ooknet-design/src/content/notes ││ /ai/using-ai-to-troubleshoot.md b/ooknet-des ││ ign/src/content/notes/ai/using-ai-to-trouble ││ shoot.md ││ deleted file mode 100644 ││ index c18eb2d..0000000 ││ --- a/ooknet-design/src/content/notes/ai/usi ││ ng-ai-to-troubleshoot.md ││ +++ /dev/null ││ @@ -1,181 +0,0 @@ ││ ---- ││ -title: "USING AI TO TROUBLESHOOT AND RESOLV ││ E ISSUES" ││ -published: "2026-04-27" ││ -status: "DRAFT" ││ -tags: ["ai", "troubleshooting", "operations ││ ", "case-study"] ││ ---- ││ - ││ -In this guide I want to share my experience ││ using AI to analyse, troubleshoot and resol ││ ve issues. ││ - ││ -# intro ││ - ││ -For some issues the solution is clear, docu ││ mented, and known. For example: ││ - ││ -> "I can't login to VPN" ││ - ││ -**Triage steps:** ││ - ││ -- Are they connected to the internet -> Con ││ nect to internet ││ -- Is their account locked? -> Unlock their ││ account ││ -- Is their password expired -> Notify user ││ their password has reset, walk through steps ││ on how to reset password ││ -- Is their DUO locked -> Confirm if they wh ││ ere the reason for the lock -> Unlock DUO ││ -- Are they not accepting DUO prompt -> Ask ││ them to open the DUO app and try again ││ -- Not receiving DUO prompt even with applic ││ ation open -> Verify their DUO details are c ││ orrect (Phone number and Device) ││ -- Still not receiving DUO Prompt -> Restart ││ mobile device ││ -- Still not recieving DUO prompt -> Re-Enro ││ ll user in DUO ││ - ││ -Clear steps to follow, no ambiguity. ││ - ││ -Another example: ││ - ││ -> "My computer is running slow" ││ - ││ -This is where it can get hazy, often the be ││ st way to proceed is to start asking more qu ││ estions. ││ - ││ -"When is it slow?" ││ -"Is it always slow?" ││ -"What feels slow?" ││ - ││ -Often the user doesn't have clear answers t ││ o these questions, so we start going through ││ common troubleshooting steps: ││ - ││ -- Restart the device ││ -- Check start-up programs and services ││ -- Check if they need OS/Driver updates ││ -- Look through processes running in task ma ││ nager ││ -- Check storage space ││ - ││ -Sometimes the resolution looks clear, they ││ have too many programs/browser tabs open at ││ all times and they haven't restarted their d ││ evice in 24 days. You ask the user to change ││ behaviour and monitor over the next week to ││ see if the issue persists. ││ - ││ -But what if it all looks fine? You restarte ││ d the computer, they aren't opening 100s of ││ tabs in Edge, they are fully up-to-date with ││ their updates. How do you proceed? ││ - ││ -A common solution is the nuclear one, repla ││ ce device/re-image. This is often the simple ││ st and sometimes least time consuming approa ││ ch. But it's not ideal (in most cases). Anot ││ her approach would be to dig deeper, look in ││ to the logs of the device, try to work out w ││ hat the issue might be. This can be very tim ││ e consuming, and often lead to dead ends, ul ││ timately landing you back to the nuclear opt ││ ion. In this document I want to provide my t ││ houghts on how to best approach the alternat ││ ive to the nuclear option, in an efficient m ││ anner utilising AI. ││ - ││ -# Using AI ││ - ││ -So you might decide you want to see what an ││ LLM thinks, you prompt it with something li ││ ke: ││ - ││ -> I have a user who reports their computer ││ is running slow, any ideas? ││ - ││ -The LLM might respond with some generic tro ││ ubleshooting steps we already know, or ask y ││ ou the same questions you asked the user (to ││ no avail). ││ - ││ -Maybe it provides some additional steps to ││ try, clear Office cache, run a repair tool. ││ It doesn't help, you go back to thinking of ││ replacing the laptop. ││ - ││ -Maybe the issue is you didn't prompt it goo ││ d enough? Maybe you need better word it bett ││ er... ││ - ││ -> You are an expert IT system admin advisin ││ g me on how best to approach a user who is h ││ aving trouble with a slow computer. ││ -> ││ -> User claims the computer doesn't always ││ feel slow, just sometimes, then programs sta ││ rt to crash and so they turn their computer ││ off. ││ -> ││ -> Steps already taken: ││ -> - Restarted computer ││ -> - Updated Drivers and OS ││ -> - Checked RAM and CPU usage ││ -> ││ -> All seemed fine on my end, how do I proc ││ eed? ││ - ││ -You get back an emoji filled essay of thing ││ s to try, most of which are not helpful to y ││ ou. The problem here is not how the prompt i ││ s written, it's that we gave it nothing to w ││ ork with. All we did was tee it up to write ││ a "Top 10 Troubleshooting steps for Windows ││ 2026" blog post. ││ - ││ -What we need to do is give it the details, ││ provide it with the context it needs to help ││ find what the real issue is. ││ -# Event logs ││ - ││ -Event logs are Windows' central system for ││ logging. Every time a service starts, a driv ││ er loads, an app crashes, or a Windows compo ││ nent decides something is worth noting - Win ││ dows writes an entry to the event log. ││ - ││ -Typically you read it through the Event Vie ││ wer (`eventvwr.msc`) accessible via **Start ││ -> "Event Viewer"**. The left pane is a tree ││ of log files. With the most common "Channel ││ s" being: ││ - ││ -- **Application** - Anything an installed p ││ rogram logged. Crashes (Event ID 1000), hang ││ s (1002), .NET errors, Office issues. ││ -- **System** - The operating system itself. ││ Driver problems, service start/stop, disk e ││ rrors, unexpected shutdowns, hardware events ││ . ││ -- **Security** - logons, logoffs, privilege ││ use, account changes. ││ - ││ -Below those are the **Applications and Serv ││ ices Logs**, this is where Microsoft feature ││ s and third-party software keep their own pr ││ ivate logs. This is often where the genuinel ││ y useful information lives. For example `Mic ││ rosoft ? Windows ? Diagnostics-Performance ? ││ Operational` for slow boot/shutdown investi ││ gations. ││ - ││ -Every entry has the same handful of fields: ││ - ││ -- **Level** - Critical, Error, Warning, Inf ││ ormation, Verbose. On a healthy machine 99% ││ of events are Information; filter those out ││ first. ││ -- **Source** - What generated the entry (e. ││ g. `Application Error`, `disk`, `Service Con ││ trol Manager`, `Microsoft-Windows-Kernel-Pow ││ er`). ││ -- **Event ID** - A number that uniquely ide ││ ntifies the _kind_ of event for that source. ││ `disk` Event ID 7 always means "the device ││ has a bad block." Once you know the (Source, ││ Event ID) pair, you can look it up. ││ -- **Date/Time** - When it happened. Critica ││ l for correlating with what the user reporte ││ d. ││ -- **Message** - A human-readable descriptio ││ n, usually with the useful details (file pat ││ h, error code, process name, exit code). ││ - ││ -Two things make event logs especially well ││ suited to handing to an LLM: ││ - ││ -1. **They're already structured.** Each ent ││ ry is self-contained. Source, ID, message, t ││ imestamp - this is exactly the shape an LLM ││ needs to reason about without you having to ││ explain the surrounding context. ││ -2. **Event IDs are well-documented.** Micro ││ soft docs, vendor KBs, and twenty years of f ││ orum posts mean the model has almost certain ││ ly seen the (Source, ID) pair you're looking ││ at and can tell you what it usually means a ││ nd what tends to cause it. ││ - ││ -Typically the challenge with Event logs com ││ es from its volume. On an average working da ││ y a Windows machine might emit thousands of ││ events. The skill here is the ability to rea ││ d through the noise. In the world of InfoSec ││ , this often involves the use of external to ││ ols such as Splunk and Log Parser Studio to ││ interrogate the information you need for you ││ r investigation. For our purposes, these too ││ ls are not needed, I want to propose an alte ││ rnative utilising PowerShell, Excel, and Cop ││ ilot. ││ - ││ -# Collecting Event logs ││ - ││ -Because all event logs are just .NET object ││ s - exporting, manipulating, and filtering i ││ s easily done via PowerShell's builtin cmdle ││ t `Get-WinEvent`. ││ - ││ -Example: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} ││ -``` ││ - ││ -This will return all level 1, 2 and 3 event ││ logs from the "Application" channel. From t ││ here we can pipe the output to `Select-Objec ││ t` to get the fields we want: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} | Select-Object TimeCreated, Id, LevelDis ││ playName, ProviderName, Message ││ -``` ││ - ││ -Then pipe that to an `Export-*` of our desi ││ red format, in this case, a CSV. The `@{...} ││ ` expression on `Message` flattens any embed ││ ded newlines into spaces, without it, multi- ││ line error messages would break the CSV acro ││ ss multiple rows: ││ - ││ -``` powershell ││ -Get-WinEvent -FilterHashtable @{ ││ - LogName = 'Application' ││ - Level = 1,2,3 ││ - StartTime = (Get-Date).AddHours(-24) ││ -} | Select-Object TimeCreated, Id, LevelDis ││ playName, ProviderName, ││ - @{N='Message'; E={ $_.Message -replace ││ "`r`n",' ' }} | ││ -Export-Csv -NoTypeInformation -Path "$env:U ││ SERPROFILE\Desktop\events.csv" ││ -``` ││ - ││ -This will leave us with a `csv` file of all ││ the "Application" channel logs from the pas ││ t 24 hours containing the fields we specifie ││ d (TimeCreated, Id, LevelDisplayName, Provid ││ erName, Message). ││ - ││ -I won't get too deep into the PowerShell he ││ re. You can read more about the `Get-WinEven ││ t` and `Export-Csv` below: ││ - ││ -- https://learn.microsoft.com/en-us/powersh ││ ell/module/microsoft.powershell.diagnostics/ ││ get-winevent?view=powershell-7.6 ││ -- https://learn.microsoft.com/en-us/powersh ││ ell/module/microsoft.powershell.utility/expo ││ rt-csv?view=powershell-7.6 ││ - ││ - ││ - ││ -# Using the Event logs with AI ││ - ││ -With the exported workbook, the easiest pat ││ h would be to upload it to a Copilot chat an ││ d ask "any ideas what's going on here?" That ││ works, but in my experience it hands too mu ││ ch over to the LLM. You get an answer, you a ││ ccept it (or you don't), and the conversatio ││ n is over. You've cut yourself out of the in ││ vestigation, and you've lost the chance to s ││ pot anything the model glossed over. ││ - ││ -I now mostly use the Copilot agent directly ││ inside Excel to keep the work collaborative ││ . The workbook is open in front of you, Copi ││ lot edits it as you go, and you can push bac ││ k on what it suggests rather than just recei ││ ve an answer. ││ - ││ -# Case study ││ - ││ -The following is a case study on a real wor ││ ld use of this technique. ││ - ││ -## The ticket ││ - ││ -**Title**: Laptop running slow, unable to c ││ omplete any work ││ -**Description**: Laptop running slow. Unabl ││ e to open and use outlook, teams or files. C ││ ontinuously freezing also. Unable to get any ││ work done. ││ - ││ -## Steps taken ││ - ││ -1. Spoke to the user to get more informatio ││ n and learnt the following ││ - - This started to happen a couple weeks ag ││ o ││ - - No other useful information provided ││ -2. Checked disk space - Plenty of space ││ -3. Checked uptime - Had been rebooted recen ││ tly ││ -4. Checked processes - Nothing stood out ││ -5. Check start-up software/services - Minim ││ al ││ - ││ -When inspecting the computer, everything se ││ emed to performing well. ││ -For deeper collection Microsoft's [[windows ││ /collect-data-tss|TSS toolset]] ││ -is the heavier option; here I dumped some l ││ ogs using the following script ││ - ││ - ││ - │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/notes/graphs/entropy-bounds.md ─────────────────┐│ diff --git a/ooknet-design/src/content/notes/graphs/entropy-bounds.md b/ooknet-des ││ ign/src/content/notes/graphs/entropy-bounds.md ││ deleted file mode 100644 ││ index 8d7215b..0000000 ││ --- a/ooknet-design/src/content/notes/graphs/entropy-bounds.md ││ +++ /dev/null ││ @@ -1,59 +0,0 @@ ││ ---- ││ -title: "ENTROPY BOUNDS IN SPARSE GRAPHS" ││ -published: "2026-04-19" ││ -status: "PEER-CHECKED [PASS]" ││ -tags: ["entropy", "sparse-graphs", "spectral"] ││ -seeAlso: ││ - - "USING AI TO TROUBLESHOOT AND RESOLVE ISSUES" ││ ---- ││ - ││ -# Intro ││ - ││ -The following note refines an earlier bound on the Shannon entropy of sparse ││ -d-regular graphs. Where prior work (see [44]) admitted only asymptotic ││ -statements, we present a finite, constructive form. The argument proceeds by ││ -counting closed walks of length three and four, from which the spectral gap is ││ -recovered without recourse to the Hoffman trick. ││ - ││ -For the convenience of the reader unfamiliar with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The reader is invited to verify the ││ -inequality by hand; the calculation fits on one sheet. ││ - ││ -The curve admits a closed form in the regime d ? ?n; outside this range the ││ -bound becomes vacuous and one must defer to the operator-theoretic methods of ││ -Pembroke (forthcoming). A worked counter-example, due to a referee, appears in ││ -the appendix. ││ - ││ -# Explain? ││ - ││ -For the convenience of the reader unfamiliar with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The reader is invited to verify the ││ -inequality by hand; the calculation fits on one sheet. ││ - ││ -```nix ││ -# This is a test code ││ - ││ -{ ││ - someAttr = { ││ - value1 = 1; ││ - value2 = "string"; ││ - }; ││ -} ││ -``` ││ - ││ -The curve admits a closed form in the regime d ? ?n; outside this range the ││ -bound becomes vacuous and one must defer to the operator-theoretic methods of ││ -Pembroke (forthcoming). A worked counter-example, due to a referee, appears in ││ -the appendix. ││ - ││ -## The truth ││ - ││ -The following note refines an earlier bound on the Shannon entropy of sparse ││ -d-regular graphs. Where prior work (see [44]) admitted only asymptotic ││ -statements, we present a finite, constructive form. The argument proceeds by ││ -counting closed walks of length three and four, from which the spectral gap is ││ -recovered without recourse to the Hoffman trick. ││ - ││ -For the convenience of the reader unfamiliar with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The reader is invited to verify the ││ -inequality by hand; the calculation fits on one sheet. │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...otes/graphs/entropy-bounds.md ───┐│ diff --git a/ooknet-design/src/content/notes ││ /graphs/entropy-bounds.md b/ooknet-design/sr ││ c/content/notes/graphs/entropy-bounds.md ││ deleted file mode 100644 ││ index 8d7215b..0000000 ││ --- a/ooknet-design/src/content/notes/graphs ││ /entropy-bounds.md ││ +++ /dev/null ││ @@ -1,59 +0,0 @@ ││ ---- ││ -title: "ENTROPY BOUNDS IN SPARSE GRAPHS" ││ -published: "2026-04-19" ││ -status: "PEER-CHECKED [PASS]" ││ -tags: ["entropy", "sparse-graphs", "spectra ││ l"] ││ -seeAlso: ││ - - "USING AI TO TROUBLESHOOT AND RESOLVE I ││ SSUES" ││ ---- ││ - ││ -# Intro ││ - ││ -The following note refines an earlier bound ││ on the Shannon entropy of sparse ││ -d-regular graphs. Where prior work (see [44 ││ ]) admitted only asymptotic ││ -statements, we present a finite, constructi ││ ve form. The argument proceeds by ││ -counting closed walks of length three and f ││ our, from which the spectral gap is ││ -recovered without recourse to the Hoffman t ││ rick. ││ - ││ -For the convenience of the reader unfamilia ││ r with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The rea ││ der is invited to verify the ││ -inequality by hand; the calculation fits on ││ one sheet. ││ - ││ -The curve admits a closed form in the regim ││ e d ? ?n; outside this range the ││ -bound becomes vacuous and one must defer to ││ the operator-theoretic methods of ││ -Pembroke (forthcoming). A worked counter-ex ││ ample, due to a referee, appears in ││ -the appendix. ││ - ││ -# Explain? ││ - ││ -For the convenience of the reader unfamilia ││ r with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The rea ││ der is invited to verify the ││ -inequality by hand; the calculation fits on ││ one sheet. ││ - ││ -```nix ││ -# This is a test code ││ - ││ -{ ││ - someAttr = { ││ - value1 = 1; ││ - value2 = "string"; ││ - }; ││ -} ││ -``` ││ - ││ -The curve admits a closed form in the regim ││ e d ? ?n; outside this range the ││ -bound becomes vacuous and one must defer to ││ the operator-theoretic methods of ││ -Pembroke (forthcoming). A worked counter-ex ││ ample, due to a referee, appears in ││ -the appendix. ││ - ││ -## The truth ││ - ││ -The following note refines an earlier bound ││ on the Shannon entropy of sparse ││ -d-regular graphs. Where prior work (see [44 ││ ]) admitted only asymptotic ││ -statements, we present a finite, constructi ││ ve form. The argument proceeds by ││ -counting closed walks of length three and f ││ our, from which the spectral gap is ││ -recovered without recourse to the Hoffman t ││ rick. ││ - ││ -For the convenience of the reader unfamilia ││ r with the apparatus of ?2.1, a short ││ -worked example is reproduced below. The rea ││ der is invited to verify the ││ -inequality by hand; the calculation fits on ││ one sheet. │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/content/notes/nix/nixos-thoughts.md ────────────────────┐│ diff --git a/ooknet-design/src/content/notes/nix/nixos-thoughts.md b/ooknet-design ││ /src/content/notes/nix/nixos-thoughts.md ││ deleted file mode 100644 ││ index 7a00936..0000000 ││ --- a/ooknet-design/src/content/notes/nix/nixos-thoughts.md ││ +++ /dev/null ││ @@ -1,67 +0,0 @@ ││ ---- ││ -title: "NIXOS THOUGHTS" ││ -published: "2026-07-16" ││ -status: "DRAFT" ││ -tags: ["nixos", "nix", "guide"] ││ -draft: true ││ ---- ││ - ││ -# Intro ││ - ││ -NixOS is a mountain, but it's one clouded in fog. It's not until you're halfway u ││ p, wrapped in endless layers of abstraction, that you realise how far you are from ││ the top. Then something breaks. You look at the error log. Gibberish. ││ - ││ -It's at this point you realise you haven't been climbing the mountain at all. You ││ look down. A sherpa has been carrying you the whole way. ││ - ││ -I want to tell you how to climb it yourself. Slow, but steady. ││ - ││ -There's a pervasive perception in the NixOS community that the documentation is p ││ oor. The complaint is misaimed. ││ - ││ -The comparison people reach for is the ArchWiki. But Arch is just an OS. NixOS is ││ n't. It's a programming language, a library for building operating systems written ││ in that language, a package manager, and an OS assembled out of all of it. You ca ││ n't document something like that with a wiki, because most of what people want doc ││ umented isn't an OS. ││ - ││ -The pieces that exist are reasonable. The Nix manual covers the language, the Nix ││ pkgs manual covers the conventions, the NixOS manual covers the module system. Wha ││ t people usually mean when they say "the docs are bad" is that they couldn't find ││ a specific answer to a specific composition someone wrote inside Nixpkgs. That's t ││ he same thing as asking for every PyPI package to be documented in the Python manu ││ al. No language ecosystem works like that. ││ - ││ -What I think is actually happening: people jump in at the top of the abstraction ││ stack and never touch the roots. When something breaks they reach for higher-level ││ docs that only make sense if you already know what's underneath. It's like thinki ││ ng you understand JavaScript because you installed a WordPress plugin. You haven't ││ interacted with the language at all, you just imported a library. ││ - ││ -This guide builds the foundation. So when problems arise, and they will, you're p ││ repared to find solutions yourself. ││ - ││ -> [!NOTE] ││ -> This guide assumes a basic understanding of programming and command-line fundam ││ entals. If you don't have that and you still want to dive in: ││ -> ││ -> - You'll be able to use NixOS just fine. Follow a basic install tutorial and wr ││ ite a configuration. ││ -> - If you're persistent, you'll get into some of the more "advanced" features. ││ -> - A lack of documentation is almost never the reason you'll struggle to find so ││ lutions, despite what people say. ││ -> - At some point you WILL have to learn how the language works, and solve proble ││ ms no one else has had. ││ - ││ -Before we touch NixOS itself, we're going to walk through the layers it sits on: ││ - ││ -- The language basics ││ -- Derivations ││ -- The package manager and build system ││ -- Nixpkgs ││ -- Hydra ││ - ││ -Only once that's in place do we get to NixOS. ││ -# The language ││ - ││ -## The Basics ││ - ││ -Go to https://nixos.org/download/ and download the package manage for you current ││ OS. ││ - ││ -Verify the language is usable in your PATH ││ -```sh ││ -$ nix --version ││ -nix (Lix, like Nix) 2.94.1 ││ -``` ││ -Great, now hop into the repl ││ - ││ -```sh ││ -nix repl ││ - ││ -Type :? for help. ││ -nix-repl> ││ -``` ││ - ││ -The Nix language is actually very simple with very little in the way of "syntax" ││ - ││ - ││ - │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...t/notes/nix/nixos-thoughts.md ───┐│ diff --git a/ooknet-design/src/content/notes ││ /nix/nixos-thoughts.md b/ooknet-design/src/c ││ ontent/notes/nix/nixos-thoughts.md ││ deleted file mode 100644 ││ index 7a00936..0000000 ││ --- a/ooknet-design/src/content/notes/nix/ni ││ xos-thoughts.md ││ +++ /dev/null ││ @@ -1,67 +0,0 @@ ││ ---- ││ -title: "NIXOS THOUGHTS" ││ -published: "2026-07-16" ││ -status: "DRAFT" ││ -tags: ["nixos", "nix", "guide"] ││ -draft: true ││ ---- ││ - ││ -# Intro ││ - ││ -NixOS is a mountain, but it's one clouded i ││ n fog. It's not until you're halfway up, wra ││ pped in endless layers of abstraction, that ││ you realise how far you are from the top. Th ││ en something breaks. You look at the error l ││ og. Gibberish. ││ - ││ -It's at this point you realise you haven't ││ been climbing the mountain at all. You look ││ down. A sherpa has been carrying you the who ││ le way. ││ - ││ -I want to tell you how to climb it yourself ││ . Slow, but steady. ││ - ││ -There's a pervasive perception in the NixOS ││ community that the documentation is poor. T ││ he complaint is misaimed. ││ - ││ -The comparison people reach for is the Arch ││ Wiki. But Arch is just an OS. NixOS isn't. I ││ t's a programming language, a library for bu ││ ilding operating systems written in that lan ││ guage, a package manager, and an OS assemble ││ d out of all of it. You can't document somet ││ hing like that with a wiki, because most of ││ what people want documented isn't an OS. ││ - ││ -The pieces that exist are reasonable. The N ││ ix manual covers the language, the Nixpkgs m ││ anual covers the conventions, the NixOS manu ││ al covers the module system. What people usu ││ ally mean when they say "the docs are bad" i ││ s that they couldn't find a specific answer ││ to a specific composition someone wrote insi ││ de Nixpkgs. That's the same thing as asking ││ for every PyPI package to be documented in t ││ he Python manual. No language ecosystem work ││ s like that. ││ - ││ -What I think is actually happening: people ││ jump in at the top of the abstraction stack ││ and never touch the roots. When something br ││ eaks they reach for higher-level docs that o ││ nly make sense if you already know what's un ││ derneath. It's like thinking you understand ││ JavaScript because you installed a WordPress ││ plugin. You haven't interacted with the lan ││ guage at all, you just imported a library. ││ - ││ -This guide builds the foundation. So when p ││ roblems arise, and they will, you're prepare ││ d to find solutions yourself. ││ - ││ -> [!NOTE] ││ -> This guide assumes a basic understanding ││ of programming and command-line fundamentals ││ . If you don't have that and you still want ││ to dive in: ││ -> ││ -> - You'll be able to use NixOS just fine. ││ Follow a basic install tutorial and write a ││ configuration. ││ -> - If you're persistent, you'll get into s ││ ome of the more "advanced" features. ││ -> - A lack of documentation is almost never ││ the reason you'll struggle to find solution ││ s, despite what people say. ││ -> - At some point you WILL have to learn ho ││ w the language works, and solve problems no ││ one else has had. ││ - ││ -Before we touch NixOS itself, we're going t ││ o walk through the layers it sits on: ││ - ││ -- The language basics ││ -- Derivations ││ -- The package manager and build system ││ -- Nixpkgs ││ -- Hydra ││ - ││ -Only once that's in place do we get to NixO ││ S. ││ -# The language ││ - ││ -## The Basics ││ - ││ -Go to https://nixos.org/download/ and downl ││ oad the package manage for you current OS. ││ - ││ -Verify the language is usable in your PATH ││ -```sh ││ -$ nix --version ││ -nix (Lix, like Nix) 2.94.1 ││ -``` ││ -Great, now hop into the repl ││ - ││ -```sh ││ -nix repl ││ - ││ -Type :? for help. ││ -nix-repl> ││ -``` ││ - ││ -The Nix language is actually very simple wi ││ th very little in the way of "syntax" ││ - ││ - ││ - │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/git.config.ts ──────────────────────────────────────────┐│ diff --git a/ooknet-design/src/git.config.ts b/ooknet-design/src/git.config.ts ││ index e5811a7..c58a013 100644 ││ --- a/ooknet-design/src/git.config.ts ││ +++ b/ooknet-design/src/git.config.ts ││ @@ -16,4 +16,18 @@ export const REPOS: GitRepo[] = [ ││ desc: "personal website - a text-mode knowledge base and the ascii design sys ││ tem that renders it", ││ clone: "https://git.ooknet.org/ooks/ooknet.org.git", ││ }, ││ + { ││ + slug: "ooknet", ││ + name: "ooknet", ││ + path: at("ooknet", "../../ooknet"), ││ + desc: "my personal nixos infrastructure configuration", ││ + clone: "https://git.ooknet.org/ooks/ooknet.git", ││ + }, ││ + { ││ + slug: "wowsim-stats", ││ + name: "wowsim-stats", ││ + path: at("wowsim-stats", "../../wowsim-stats"), ││ + desc: "a nix pipeline that orchestrates mist of pandaria simulations at scale ││ , and a statically generated challenge mode leaderboard", ││ + clone: "https://git.ooknet.org/ooks/wowsim-stats.git", ││ + }, ││ ]; │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ooknet-design/src/git.config.ts ────┐│ diff --git a/ooknet-design/src/git.config.ts ││ b/ooknet-design/src/git.config.ts ││ index e5811a7..c58a013 100644 ││ --- a/ooknet-design/src/git.config.ts ││ +++ b/ooknet-design/src/git.config.ts ││ @@ -16,4 +16,18 @@ export const REPOS: GitRe ││ po[] = [ ││ desc: "personal website - a text-mode k ││ nowledge base and the ascii design system th ││ at renders it", ││ clone: "https://git.ooknet.org/ooks/ook ││ net.org.git", ││ }, ││ + { ││ + slug: "ooknet", ││ + name: "ooknet", ││ + path: at("ooknet", "../../ooknet"), ││ + desc: "my personal nixos infrastructure ││ configuration", ││ + clone: "https://git.ooknet.org/ooks/ook ││ net.git", ││ + }, ││ + { ││ + slug: "wowsim-stats", ││ + name: "wowsim-stats", ││ + path: at("wowsim-stats", "../../wowsim- ││ stats"), ││ + desc: "a nix pipeline that orchestrates ││ mist of pandaria simulations at scale, and ││ a statically generated challenge mode leader ││ board", ││ + clone: "https://git.ooknet.org/ooks/wow ││ sim-stats.git", ││ + }, ││ ]; │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/lib/wikilinks.test.ts ──────────────────────────────────┐│ diff --git a/ooknet-design/src/lib/wikilinks.test.ts b/ooknet-design/src/lib/wikil ││ inks.test.ts ││ index a8b7d90..34a53c1 100644 ││ --- a/ooknet-design/src/lib/wikilinks.test.ts ││ +++ b/ooknet-design/src/lib/wikilinks.test.ts ││ @@ -1,46 +1,67 @@ ││ -import { describe, expect, it } from "vitest"; ││ +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 { buildBacklinks, buildWikiMap, extractWikiTargets, resolveWikilink } from ││ "./wikilinks"; ││ import { wikiNodes } from "./remark-wikilinks"; ││ ││ -describe("buildWikiMap", () => { ││ - const map = buildWikiMap(); ││ +// fixture content dir - the graph tests must not depend on what the ││ +// real collections currently hold ││ +let dir: string; ││ +const write = (rel: string, fm: string, body = "") => { ││ + const p = join(dir, rel); ││ + mkdirSync(join(p, ".."), { recursive: true }); ││ + writeFileSync(p, `---\n${fm}\n---\n\n${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/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]]."); ││ +}); ││ +afterAll(() => rmSync(dir, { recursive: true, force: true })); ││ ││ +describe("buildWikiMap", () => { ││ it("indexes every collection by id and title", () => { ││ - expect(resolveWikilink("graphs/entropy-bounds", map)).toBe("/notes/graphs/ent ││ ropy-bounds/"); ││ - expect(resolveWikilink("Entropy Bounds in Sparse Graphs", map)).toBe("/notes/ ││ graphs/entropy-bounds/"); ││ - expect(resolveWikilink("windows/collect-data-tss", map)).toBe("/kb/windows/co ││ llect-data-tss/"); ││ - expect(resolveWikilink("Style Sheet", map)).toBe("/docs/ooknet-org/style-shee ││ t/"); ││ + 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("windows/tss", map)).toBe("/kb/windows/tss/"); ││ + expect(resolveWikilink("Docs Page", map)).toBe("/docs/site/page/"); ││ }); ││ ││ it("is case-insensitive and trims", () => { ││ - expect(resolveWikilink(" WIKILINKS ", map)).toBe("/docs/ooknet-org/authorin ││ g/wikilinks/"); ││ + const map = buildWikiMap(dir); ││ + expect(resolveWikilink(" TSS TOOLSET ", map)).toBe("/kb/windows/tss/"); ││ }); ││ ││ it("excludes drafts", () => { ││ - expect(resolveWikilink("nix/nixos-thoughts", map)).toBeNull(); ││ + expect(resolveWikilink("nix/draft", buildWikiMap(dir))).toBeNull(); ││ }); ││ ││ it("returns null for unknown targets", () => { ││ - expect(resolveWikilink("A NOTE THAT DOES NOT EXIST", map)).toBeNull(); ││ + expect(resolveWikilink("A NOTE THAT DOES NOT EXIST", buildWikiMap(dir))).toBe ││ Null(); ││ }); ││ }); ││ ││ describe("wikiNodes", () => { ││ - const map = new Map([["style sheet", "/docs/ooknet-org/style-sheet/"]]); ││ + const map = new Map([["docs page", "/docs/site/page/"]]); ││ ││ it("leaves plain text untouched", () => { ││ expect(wikiNodes("no links here", map)).toBeNull(); ││ }); ││ ││ it("splits text around a resolved link", () => { ││ - const nodes = wikiNodes("see [[Style Sheet]] for glyphs", map)!; ││ + 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/ooknet-org/style-sheet/"); ││ + expect(nodes[1].url).toBe("/docs/site/page/"); ││ }); ││ ││ it("uses the alias as the label", () => { ││ - const nodes = wikiNodes("[[Style Sheet|the sheet]]", map)!; ││ - expect((nodes[0].children as { value: string }[])[0].value).toBe("the sheet") ││ ; ││ + const nodes = wikiNodes("[[Docs Page|the page]]", map)!; ││ + expect((nodes[0].children as { value: string }[])[0].value).toBe("the page"); ││ }); ││ ││ it("renders unresolved targets as dead-link spans", () => { ││ @@ -62,33 +83,32 @@ describe("extractWikiTargets", () => { ││ }); ││ ││ describe("buildBacklinks", () => { ││ - const bl = buildBacklinks(); ││ - const urls = (target: string) => (bl.get(target) ?? []).map((r) => r.url); ││ - ││ it("inverts the graph across collections", () => { ││ - // the ai note cites the kb entry ││ - expect(urls("/kb/windows/collect-data-tss/")).toContain("/notes/ai/using-ai-t ││ o-troubleshoot/"); ││ - // the style-sheet doc cites the entropy note ││ - expect(urls("/notes/graphs/entropy-bounds/")).toContain("/docs/ooknet-org/sty ││ le-sheet/"); ││ + 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/"]); ││ }); ││ ││ it("labels sources with their collection", () => { ││ - const src = bl ││ - .get("/kb/windows/collect-data-tss/")! ││ - .find((r) => r.url === "/notes/ai/using-ai-to-troubleshoot/")!; ││ - expect(src.label).toBe("NOTES - USING AI TO TROUBLESHOOT AND RESOLVE ISSUES") ││ ; ││ + const src = buildBacklinks(dir).get("/kb/windows/tss/")![0]; ││ + expect(src.label).toBe("NOTES - FLAKES FIELD NOTES"); ││ }); ││ ││ - it("drops self-references and drafts", () => { ││ + 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/nixos-thoughts/"); ││ + expect(list.map((r) => r.url)).not.toContain("/notes/nix/draft/"); ││ } ││ + // the kb body only cites in a code span - it must not be a source ││ + const flakes = bl.get("/notes/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 bl.values()) { ││ + for (const list of buildBacklinks(dir).values()) { ││ const kinds = list.map((r) => order.findIndex((p) => r.url.startsWith(p))); ││ expect(kinds).toEqual([...kinds].sort()); ││ } │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...ign/src/lib/wikilinks.test.ts ───┐│ diff --git a/ooknet-design/src/lib/wikilinks ││ .test.ts b/ooknet-design/src/lib/wikilinks.t ││ est.ts ││ index a8b7d90..34a53c1 100644 ││ --- a/ooknet-design/src/lib/wikilinks.test.t ││ s ││ +++ b/ooknet-design/src/lib/wikilinks.test.t ││ s ││ @@ -1,46 +1,67 @@ ││ -import { describe, expect, it } from "vites ││ t"; ││ +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 { buildBacklinks, buildWikiMap, extr ││ actWikiTargets, resolveWikilink } from "./wi ││ kilinks"; ││ import { wikiNodes } from "./remark-wikilin ││ ks"; ││ ││ -describe("buildWikiMap", () => { ││ - const map = buildWikiMap(); ││ +// fixture content dir - the graph tests mu ││ st not depend on what the ││ +// real collections currently hold ││ +let dir: string; ││ +const write = (rel: string, fm: string, bod ││ y = "") => { ││ + const p = join(dir, rel); ││ + mkdirSync(join(p, ".."), { recursive: tru ││ e }); ││ + writeFileSync(p, `---\n${fm}\n---\n\n${bo ││ dy}`); ││ +}; ││ + ││ +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/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]]."); ││ +}); ││ +afterAll(() => rmSync(dir, { recursive: tru ││ e, force: true })); ││ ││ +describe("buildWikiMap", () => { ││ it("indexes every collection by id and ti ││ tle", () => { ││ - expect(resolveWikilink("graphs/entropy- ││ bounds", map)).toBe("/notes/graphs/entropy-b ││ ounds/"); ││ - expect(resolveWikilink("Entropy Bounds ││ in Sparse Graphs", map)).toBe("/notes/graphs ││ /entropy-bounds/"); ││ - expect(resolveWikilink("windows/collect ││ -data-tss", map)).toBe("/kb/windows/collect- ││ data-tss/"); ││ - expect(resolveWikilink("Style Sheet", m ││ ap)).toBe("/docs/ooknet-org/style-sheet/"); ││ + 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("windows/tss", m ││ ap)).toBe("/kb/windows/tss/"); ││ + expect(resolveWikilink("Docs Page", map ││ )).toBe("/docs/site/page/"); ││ }); ││ ││ it("is case-insensitive and trims", () => ││ { ││ - expect(resolveWikilink(" WIKILINKS ", ││ map)).toBe("/docs/ooknet-org/authoring/wiki ││ links/"); ││ + const map = buildWikiMap(dir); ││ + expect(resolveWikilink(" TSS TOOLSET ││ ", map)).toBe("/kb/windows/tss/"); ││ }); ││ ││ it("excludes drafts", () => { ││ - expect(resolveWikilink("nix/nixos-thoug ││ hts", map)).toBeNull(); ││ + expect(resolveWikilink("nix/draft", bui ││ ldWikiMap(dir))).toBeNull(); ││ }); ││ ││ it("returns null for unknown targets", () ││ => { ││ - expect(resolveWikilink("A NOTE THAT DOE ││ S NOT EXIST", map)).toBeNull(); ││ + expect(resolveWikilink("A NOTE THAT DOE ││ S NOT EXIST", buildWikiMap(dir))).toBeNull() ││ ; ││ }); ││ }); ││ ││ describe("wikiNodes", () => { ││ - const map = new Map([["style sheet", "/do ││ cs/ooknet-org/style-sheet/"]]); ││ + const map = new Map([["docs page", "/docs ││ /site/page/"]]); ││ ││ it("leaves plain text untouched", () => { ││ expect(wikiNodes("no links here", map)) ││ .toBeNull(); ││ }); ││ ││ it("splits text around a resolved link", ││ () => { ││ - const nodes = wikiNodes("see [[Style Sh ││ eet]] for glyphs", map)!; ││ + 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/ooknet ││ -org/style-sheet/"); ││ + expect(nodes[1].url).toBe("/docs/site/p ││ age/"); ││ }); ││ ││ it("uses the alias as the label", () => { ││ - const nodes = wikiNodes("[[Style Sheet| ││ the sheet]]", map)!; ││ - expect((nodes[0].children as { value: s ││ tring }[])[0].value).toBe("the sheet"); ││ + const nodes = wikiNodes("[[Docs Page|th ││ e page]]", map)!; ││ + expect((nodes[0].children as { value: s ││ tring }[])[0].value).toBe("the page"); ││ }); ││ ││ it("renders unresolved targets as dead-li ││ nk spans", () => { ││ @@ -62,33 +83,32 @@ describe("extractWikiTar ││ gets", () => { ││ }); ││ ││ describe("buildBacklinks", () => { ││ - const bl = buildBacklinks(); ││ - const urls = (target: string) => (bl.get( ││ target) ?? []).map((r) => r.url); ││ - ││ it("inverts the graph across collections" ││ , () => { ││ - // the ai note cites the kb entry ││ - expect(urls("/kb/windows/collect-data-t ││ ss/")).toContain("/notes/ai/using-ai-to-trou ││ bleshoot/"); ││ - // the style-sheet doc cites the entrop ││ y note ││ - expect(urls("/notes/graphs/entropy-boun ││ ds/")).toContain("/docs/ooknet-org/style-she ││ et/"); ││ + 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/"]); ││ }); ││ ││ it("labels sources with their collection" ││ , () => { ││ - const src = bl ││ - .get("/kb/windows/collect-data-tss/") ││ ! ││ - .find((r) => r.url === "/notes/ai/usi ││ ng-ai-to-troubleshoot/")!; ││ - expect(src.label).toBe("NOTES - USING A ││ I TO TROUBLESHOOT AND RESOLVE ISSUES"); ││ + const src = buildBacklinks(dir).get("/k ││ b/windows/tss/")![0]; ││ + expect(src.label).toBe("NOTES - FLAKES ││ FIELD NOTES"); ││ }); ││ ││ - it("drops self-references and drafts", () ││ => { ││ + 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/nixos-thoughts/"); ││ + expect(list.map((r) => r.url)).not.to ││ Contain("/notes/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/") ?? []; ││ + 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 bl.values()) { ││ + 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 ││ ()); ││ } │└──────────────────────────────────────────────┘
┌─ ooknet-design/src/pages/about.astro ──────────────────────────────────────┐│ diff --git a/ooknet-design/src/pages/about.astro b/ooknet-design/src/pages/about.a ││ stro ││ index 0c0eaca..23a1374 100644 ││ --- a/ooknet-design/src/pages/about.astro ││ +++ b/ooknet-design/src/pages/about.astro ││ @@ -3,7 +3,6 @@ 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 Prose from "../components/Prose/Prose.astro"; ││ import ArticleFooter from "../components/ArticleFooter/ArticleFooter.astro"; ││ import { kv, row2, rule, ruleD } from "../lib/ascii"; ││ ││ @@ -24,27 +23,11 @@ const buildColophon = (w: number) => ││ ].join("\n"); ││ --- ││ <ArticleLayout title="OOKNET KB - About" ││ - description="What OOKNET is: a personal knowledge base rendered as pure text on ││ a monospace grid - every border, chart, and frame is a real character."> ││ + description="OOKNET - a personal knowledge base rendered as pure text on a mono ││ space grid."> ││ <TopHeader /> ││ <Frame build={buildHeader} /> ││ - <Prose> ││ - <p> ││ - OOKNET is a personal knowledge base kept the way records used to ││ - be kept: as numbered technical sheets, filed under a taxonomy, ││ - printed on an endless roll of tractor-feed paper. Notes get a ││ - number, a status, and a place in the filing system; the archive ││ - grows from the bottom of the index. ││ - </p> ││ - <p> ││ - The site is also its own subject. Every frame on these pages is a ││ - string composed at build time on a strict monospace grid - no ││ - client-side rendering, no images of boxes, just characters. A ││ - validator walks every line at compile time and refuses to ship a ││ - bent border. The whole apparatus is documented on the style sheet ││ - at note N0 and demonstrated on the components page. ││ - </p> ││ - </Prose> ││ + <Pre>{" "}</Pre> ││ <Frame build={buildColophon} /> ││ <Pre>{" "}</Pre> ││ - <ArticleFooter page={4} /> ││ + <ArticleFooter /> ││ </ArticleLayout> │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...-design/src/pages/about.astro ───┐│ diff --git a/ooknet-design/src/pages/about.a ││ stro b/ooknet-design/src/pages/about.astro ││ index 0c0eaca..23a1374 100644 ││ --- a/ooknet-design/src/pages/about.astro ││ +++ b/ooknet-design/src/pages/about.astro ││ @@ -3,7 +3,6 @@ import ArticleLayout from ". ││ ./layouts/ArticleLayout/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 Prose from "../components/Prose/Pros ││ e.astro"; ││ import ArticleFooter from "../components/Ar ││ ticleFooter/ArticleFooter.astro"; ││ import { kv, row2, rule, ruleD } from "../l ││ ib/ascii"; ││ ││ @@ -24,27 +23,11 @@ const buildColophon = (w ││ : number) => ││ ].join("\n"); ││ --- ││ <ArticleLayout title="OOKNET KB - About" ││ - description="What OOKNET is: a personal k ││ nowledge base rendered as pure text on a mon ││ ospace grid - every border, chart, and frame ││ is a real character."> ││ + description="OOKNET - a personal knowledg ││ e base rendered as pure text on a monospace ││ grid."> ││ <TopHeader /> ││ <Frame build={buildHeader} /> ││ - <Prose> ││ - <p> ││ - OOKNET is a personal knowledge base k ││ ept the way records used to ││ - be kept: as numbered technical sheets ││ , filed under a taxonomy, ││ - printed on an endless roll of tractor ││ -feed paper. Notes get a ││ - number, a status, and a place in the ││ filing system; the archive ││ - grows from the bottom of the index. ││ - </p> ││ - <p> ││ - The site is also its own subject. Eve ││ ry frame on these pages is a ││ - string composed at build time on a st ││ rict monospace grid - no ││ - client-side rendering, no images of b ││ oxes, just characters. A ││ - validator walks every line at compile ││ time and refuses to ship a ││ - bent border. The whole apparatus is d ││ ocumented on the style sheet ││ - at note N0 and demonstrated on the co ││ mponents page. ││ - </p> ││ - </Prose> ││ + <Pre>{" "}</Pre> ││ <Frame build={buildColophon} /> ││ <Pre>{" "}</Pre> ││ - <ArticleFooter page={4} /> ││ + <ArticleFooter /> ││ </ArticleLayout> │└──────────────────────────────────────────────┘