loom-spec 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ <!doctype html>
2
+ <html lang="en" data-theme="light">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>loom-spec</title>
7
+ <script>
8
+ // Apply persisted theme before paint to avoid flash.
9
+ try {
10
+ const t = localStorage.getItem("loom-theme");
11
+ if (t === "light" || t === "dark") {
12
+ document.documentElement.setAttribute("data-theme", t);
13
+ } else if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
14
+ document.documentElement.setAttribute("data-theme", "dark");
15
+ }
16
+ } catch {}
17
+ </script>
18
+ <script type="module" crossorigin src="/assets/bundle.js"></script>
19
+ <link rel="stylesheet" crossorigin href="/assets/bundle.css">
20
+ </head>
21
+ <body>
22
+ <div id="root"></div>
23
+ </body>
24
+ </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loom-spec",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Node-based architecture spec that lives in your repo. AI-readable, AI-writable, git-diffable.",
5
5
  "type": "module",
6
6
  "author": "René Jesser",
@@ -47,7 +47,8 @@
47
47
  "./node-types-schema": "./schema/node-types.schema.json"
48
48
  },
49
49
  "scripts": {
50
- "build": "tsc -p tsconfig.json && vite build --config src/view/vite.config.ts",
50
+ "build": "tsc -p tsconfig.json && vite build --config src/view/vite.config.ts && vite build --config src/view/vite.config.export.ts",
51
+ "build:export": "vite build --config src/view/vite.config.export.ts",
51
52
  "dev": "concurrently -n server,view -c blue,magenta \"pnpm dev:server\" \"pnpm dev:view\"",
52
53
  "dev:server": "tsx src/cli/index.ts view --dev --root ../../examples/todo-app --port 7778",
53
54
  "dev:view": "vite --config src/view/vite.config.ts",
@@ -59,6 +59,38 @@ For any task that touches structure:
59
59
  `<name>.flow.json` instead of cramming it into an existing diagram.
60
60
  - Link from the overview with a `drill_down` reference if appropriate.
61
61
 
62
+ ### Tagging hygiene for exports
63
+
64
+ `loom-spec export-html` filters by node `tags` to produce scoped bundles
65
+ (public manual, ops runbook, internal overview). Tagging is the source
66
+ of truth for what ships where. Conventions:
67
+
68
+ - **`public`** — shows up in user-facing documentation. Default-off
69
+ (untagged nodes are *not* in public exports).
70
+ - **`internal`** — explicitly internal; can be used as `--exclude-tag`
71
+ for public exports, or as `--include-tag` for an internal-only bundle.
72
+ - **`ops`** — for operational runbooks (deploy paths, monitoring,
73
+ on-call docs).
74
+ - **`wip`** — work-in-progress; always exclude from any export.
75
+
76
+ When you set `tags: ["public"]` on a node, remember the cascade:
77
+
78
+ - Edges between two `public` nodes survive. Edges where one endpoint is
79
+ untagged are **dropped** in the public export. If the user-facing
80
+ flow depends on an "internal" node visually, either tag that node
81
+ `public` too or accept the dangling visualisation.
82
+ - A group with no `public` children disappears entirely.
83
+ - A `drill_down` chevron pointing at a diagram that has zero `public`
84
+ nodes is removed (the diagram doesn't ship).
85
+
86
+ **Security check before tagging `public`:** look at the node's
87
+ `code_refs[].path` and `description`. Tags filter nodes, not their
88
+ content. If a `code_ref` points at `src/server/admin/secrets.ts` or
89
+ the description names an internal system, that *text* ships in the
90
+ public export. Either remove the sensitive ref / rewrite the
91
+ description, or split the node into a public surface and an internal
92
+ implementation node and tag accordingly.
93
+
62
94
  ## Preferred tools (when the MCP server is wired up)
63
95
 
64
96
  If a `loom-spec` MCP server is registered with the host (e.g. via
@@ -78,6 +110,16 @@ re-reading + re-writing JSON on every mutation.
78
110
  If the MCP server is not available, edit the JSON files directly using the
79
111
  rules above — the format is stable and tools-agnostic by design.
80
112
 
113
+ For exporting the spec to a self-contained interactive HTML (for manuals,
114
+ docs sites, GitHub Pages, embed-in-Notion, etc.), use the CLI:
115
+
116
+ - `loom-spec export-html` (full export)
117
+ - `loom-spec export-html <bundle-name>` (from `.loom/exports.json`)
118
+ - `loom-spec export-html --include-tag public --no-timelines --out manual.html`
119
+ (ad-hoc filter)
120
+
121
+ See Example 7 for the full workflow.
122
+
81
123
  ---
82
124
 
83
125
  ## Examples
@@ -338,6 +380,67 @@ loom_validate()
338
380
  **Don't create a timeline for static structure** — that's what diagrams are
339
381
  for. A timeline of "the app boots, then runs forever" adds noise.
340
382
 
383
+ ### 7. User wants to publish architecture docs to a manual
384
+
385
+ > User: "We need to ship the checkout flow as an interactive diagram in
386
+ > our public user manual. Don't expose anything internal."
387
+
388
+ ```
389
+ # Step 1: Identify which nodes belong in the public manual.
390
+ loom_read_diagram("overview")
391
+ # → review nodes; confirm with the user if scope is unclear
392
+
393
+ # Step 2: Tag the public-facing surface. Skip anything that exposes
394
+ # internal services, security-sensitive paths, or work-in-progress.
395
+ loom_update_node({ diagram: "overview", id: "checkout-page",
396
+ patch: { tags: ["public"] } })
397
+ loom_update_node({ diagram: "overview", id: "checkout-api",
398
+ patch: { tags: ["public"] } })
399
+ loom_update_node({ diagram: "overview", id: "payments-service",
400
+ patch: { tags: ["public"] } })
401
+ # … but NOT fraud-screening, admin-tools, internal-billing, etc.
402
+
403
+ # Step 3: Verify the tag set covers a connected slice. Edges between
404
+ # two public nodes survive; edges to untagged neighbours get dropped
405
+ # in the export. If the export would have orphans, either tag the
406
+ # missing neighbour or accept that the link disappears.
407
+
408
+ # Step 4: Write a named bundle to .loom/exports.json so the export is
409
+ # reproducible. (No MCP tool for this yet — write the file directly.)
410
+ #
411
+ # .loom/exports.json
412
+ {
413
+ "exports": {
414
+ "user-manual": {
415
+ "include-tags": ["public"],
416
+ "exclude-tags": ["wip"],
417
+ "no-timelines": true,
418
+ "out": "docs/architecture.html"
419
+ }
420
+ }
421
+ }
422
+
423
+ # Step 5: Generate the HTML.
424
+ # (Shell, not MCP — agents can invoke via Bash tool or similar.)
425
+ $ loom-spec export-html user-manual
426
+
427
+ # Step 6: Sanity-check the output. Open docs/architecture.html in a
428
+ # browser and confirm: no internal node names visible, no surprising
429
+ # code_refs paths leaked in the inspector, the flow makes sense as a
430
+ # standalone visualisation.
431
+ ```
432
+
433
+ **When to use timelines vs not in an export:**
434
+
435
+ - Static manual / API reference → `--no-timelines` (the topology is the
436
+ story). Smaller file, less to digest.
437
+ - Onboarding / "how does the checkout actually run" → keep timelines so
438
+ the reader can hit Play.
439
+
440
+ **Don't auto-publish** — the export is intentional. A `git add` of the
441
+ generated `.html` belongs in the change that updates the architecture,
442
+ not in an automated commit triggered by every `.loom/` edit.
443
+
341
444
  ---
342
445
 
343
446
  ## Format reference
@@ -364,3 +467,11 @@ for. A timeline of "the app boots, then runs forever" adds noise.
364
467
  **flow**.
365
468
  - Don't leave `drill_down` pointing at a non-existent diagram id.
366
469
  - Don't `loom_delete_node` for code that was just removed — `mark_stale` it.
470
+ - Don't tag everything `public` "just in case" — the value of a tag is
471
+ that it means something. If `public` is on every node, scoped exports
472
+ stop being scoped.
473
+ - Don't manually edit the generated `.html` from `export-html` — re-run
474
+ the export instead. Edits to the generated file are lost on the next
475
+ run and obscure the source of truth.
476
+ - Don't add `loom-spec export-html` to `init` defaults or auto-run it
477
+ from a hook. Exports are intentional, not background.