diff --git a/CLAUDE.md b/CLAUDE.md index e96b96aa0..93e1be4d9 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -17,7 +17,7 @@ nix/ — all library and flake-module code aspect/ — children, normalize, provide policy/ — policy dispatch and effects entities/ — host.nix, home.nix entity kind definitions - diag/ — diagram generation (c4, mermaid, dot, fleet views) + diag/capture.nix — trace capture (graph/rendering moved to denful/den-diagram) nixModule/ — den.aspects, den.policies, den.lib option declarations modules/ — NixOS-module-style option declarations and batteries options.nix — den.hosts, den.homes, den.schema, den.classes, den.quirks @@ -129,6 +129,35 @@ New test files must be `git add`'d before nix can evaluate them. Use `--override - **den-debugging** (`.claude/skills/den-debugging.md`) — structured workflow for reproducing, isolating, and fixing bugs. Guides through: understand report → trace code path → write failing test → fix → validate. Includes an entry point table mapping symptoms to source files. +## Diagrams (den-diagram) + +Diagram rendering lives in a separate repo: [denful/den-diagram](https://github.com/denful/den-diagram). Den keeps only the capture layer (`nix/lib/diag/capture.nix`) which runs the fx pipeline with tracing handlers. + +**Capture** stays in den — `den.lib.capture.*`: + +- `capture`, `captureAll`, `captureWithPaths`, `captureWithPathsWith`, `captureFleet` + +**Rendering** lives in den-diagram — added as `inputs.den-diagram` in templates that need it: + +```nix +diagram = inputs.den-diagram.lib; + +# Two-step: capture in den, render in den-diagram +captured = den.lib.capture.captureWithPathsWith { + classes = [ "nixos" "homeManager" ]; + root = den.lib.resolveEntity "host" { inherit host; }; + ctx = { inherit host; }; +}; +g = diagram.context { + entries = captured.entries; + ctxTrace = captured.ctxTrace; + name = host.name; +}; +rendered = diagram.toMermaid g; +``` + +Templates using den-diagram: `diagram-demo`, `fleet-demo`. Den's core flake and CI have no den-diagram dependency. + ## Debugging and tracing For pipeline debugging, use `builtins.trace` temporarily to inspect values flowing through handlers: diff --git a/README.md b/README.md index 9a7e9a8f8..40e9843c9 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ **Write a feature once. Run it on every host, user, and platform you have — and share it with anyone, flake or not.** -Den turns Nix configuration into composable **features** instead of per-host piles of modules. A Den *aspect* is a plain function: give it context (your hosts and users) and it returns configuration for every Nix class it touches — `nixos`, `darwin`, `homeManager`, `hjem`, or a class you invent. +Den turns Nix configuration into composable **features** instead of per-host piles of modules. A Den _aspect_ is a plain function: give it context (your hosts and users) and it returns configuration for every Nix class it touches — `nixos`, `darwin`, `homeManager`, `hjem`, or a class you invent. ```nix # An aspect is a function of context that returns @@ -33,8 +33,8 @@ That one idea — **a feature as a function** — is what makes the rest possibl ## What Den makes possible - **One feature, everywhere, in one place.** Stop scattering a single concern across separate `nixos`, `darwin`, and `homeManager` files. An aspect holds all of it together. -- **Reuse across hosts, users — and across projects.** Share aspects between machines, between people, and between *flake and non-flake* setups, without forcing everyone to download each other's inputs. -- **No `mkIf` / `enable` clutter.** The shape of the context *is* the condition — a function that asks for `{ host, user }` simply doesn't run where there's no user. Conditionals disappear. +- **Reuse across hosts, users — and across projects.** Share aspects between machines, between people, and between _flake and non-flake_ setups, without forcing everyone to download each other's inputs. +- **No `mkIf` / `enable` clutter.** The shape of the context _is_ the condition — a function that asks for `{ host, user }` simply doesn't run where there's no user. Conditionals disappear. - **Hosts shape their users, users shape their hosts.** Cross-entity configuration flows both ways, without coupling them together. - **Add a capability in one line; remove it by deleting that line.** Hosts just pick the aspects they want. - **Bring your own classes and whole pipelines.** Custom Nix classes, machine fleets, MicroVM guests, terranix, standalone neovim — if you can walk it as data, Den can configure it. @@ -43,10 +43,10 @@ That one idea — **a feature as a function** — is what makes the rest possibl Four concepts, one job each: -- **Entity** — *what exists*: a host, user, or home. -- **Aspect** — *what it does*: a feature, spanning Nix classes. -- **Policy** — *how entities relate*: topology and routing between them. -- **Quirk** — *structured data aspects share*, without coupling. +- **Entity** — _what exists_: a host, user, or home. +- **Aspect** — _what it does_: a feature, spanning Nix classes. +- **Policy** — _how entities relate_: topology and routing between them. +- **Quirk** — _structured data aspects share_, without coupling. **Feature-first, not host-first.** Traditional setups start from hosts and push modules down; Den [flips that](https://den.denful.dev/explanation/core-principles/) — features are primary, hosts just select them. @@ -75,11 +75,13 @@ nix run github:denful/den ## [Documentation](https://den.denful.dev) **Start here** + - [From Zero to Den](https://den.denful.dev/guides/from-zero-to-den/) - [From Flake to Den](https://den.denful.dev/guides/from-flake-to-den/) - [Core Principles](https://den.denful.dev/explanation/core-principles/) **Go further** + - [Custom Nix Classes](https://den.denful.dev/guides/custom-classes/) - [Homes Integration](https://den.denful.dev/guides/home-manager/) - [Batteries](https://den.denful.dev/guides/batteries/) @@ -89,6 +91,7 @@ nix run github:denful/den - [Tests as Code Examples](https://den.denful.dev/tutorials/ci/) **Project** + - [Motivation](https://den.denful.dev/motivation/) - [Versioning](https://den.denful.dev/releases/) - [Community](https://den.denful.dev/community/) diff --git a/checkmate/modules/formatter.nix b/checkmate/modules/formatter.nix index ebbe71fbb..0cf87037f 100644 --- a/checkmate/modules/formatter.nix +++ b/checkmate/modules/formatter.nix @@ -6,6 +6,7 @@ "docs/*" "Justfile" "AGENT*.md" + "CLAUDE.md" "*.txt" "*.svg" "ci.bash" diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index ffa1b19e2..ae94f3549 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -178,7 +178,7 @@ export default defineConfig({ { label: 'den.policies', slug: 'reference/policies' }, { label: 'den.lib', slug: 'reference/lib' }, { label: 'den.lib', slug: 'reference/lib-deprecated', badge: { text: 'legacy', variant: 'note' } }, - { label: 'den.lib.diag', slug: 'reference/diag' }, + { label: 'den.lib.capture & den-diagram', slug: 'reference/diag' }, { label: 'flake.*', slug: 'reference/output' }, { label: 'Glossary', slug: 'reference/glossary' }, ], diff --git a/docs/src/content/docs/explanation/diagrams.mdx b/docs/src/content/docs/explanation/diagrams.mdx index d952688da..c86cac4cf 100644 --- a/docs/src/content/docs/explanation/diagrams.mdx +++ b/docs/src/content/docs/explanation/diagrams.mdx @@ -1,12 +1,13 @@ --- title: Diagrams -description: Visualizing aspect resolution graphs with den.lib.diag. +description: Visualizing aspect resolution graphs with den-diagram. --- import { Aside } from '@astrojs/starlight/components'; ## What it does diff --git a/docs/src/content/docs/guides/debug.md b/docs/src/content/docs/guides/debug.md index fe5e2408b..4a21ca42c 100644 --- a/docs/src/content/docs/guides/debug.md +++ b/docs/src/content/docs/guides/debug.md @@ -58,15 +58,21 @@ This returns a set of matching policies with their targets, routing type, and so ## Trace Aspect Includes -The resolution pipeline includes built-in tracing via the `diag` library. -Use `den.lib.diag.hostContext` to capture a full trace of which aspects are -included and how they resolve. See [Diagrams](/explanation/diagrams/) for details. +The resolution pipeline includes built-in tracing via `den.lib.capture` and the +[den-diagram](https://github.com/denful/den-diagram) library. Capture trace data +in den, then render with den-diagram. See [Diagrams](/explanation/diagrams/) for details. ```nix -# In a REPL: -diag = den.lib.diag -g = diag.hostContext { host = den.hosts.x86_64-linux.laptop; } -diag.toMermaid g # renders the full aspect graph +# In a REPL (with den-diagram available as `diagram`): +diagram = inputs.den-diagram.lib; +host = den.hosts.x86_64-linux.laptop; +captured = den.lib.capture.captureWithPathsWith { + classes = [ "nixos" "homeManager" ]; + root = den.lib.resolveEntity "host" { inherit host; }; + ctx = { inherit host; }; +}; +g = diagram.context { entries = captured.entries; name = host.name; }; +diagram.toMermaid g # renders the full aspect graph ``` ## Trace Context diff --git a/docs/src/content/docs/overview.mdx b/docs/src/content/docs/overview.mdx index 49d0e672f..c62f2107d 100644 --- a/docs/src/content/docs/overview.mdx +++ b/docs/src/content/docs/overview.mdx @@ -74,7 +74,7 @@ Option namespaces and library functions. - + diff --git a/docs/src/content/docs/reference/diag.mdx b/docs/src/content/docs/reference/diag.mdx index 62b30704a..89be65059 100644 --- a/docs/src/content/docs/reference/diag.mdx +++ b/docs/src/content/docs/reference/diag.mdx @@ -1,51 +1,52 @@ --- -title: den.lib.diag -description: Diagram library for visualizing aspect resolution graphs. +title: den.lib.capture & den-diagram +description: Trace capture and diagram rendering for aspect resolution graphs. --- import { Aside } from '@astrojs/starlight/components'; -The diagramming library provides a composable pipeline for rendering aspect-resolution -graphs: **trace capture** collects structured trace entries from resolved aspects, -**graph construction** builds a format-agnostic IR, **filters** prune and reshape the IR, -and **renderers** emit Mermaid, DOT, PlantUML, or JSON strings. +Diagram rendering is split across two packages: -See [Diagrams explanation](/explanation/diagrams/) for concepts and usage patterns. - -## Convenience wrappers - -High-level entry points that resolve an entity and return a graph IR in one call. - -### `hostContext { host, classes?, direction? }` - -Build a host-scoped graph. When `classes` is omitted, auto-discovers from `[ "nixos" "homeManager" "user" ]` plus each user's declared classes. Returns the graph IR plus `rootAspect`, `pathSets`, and `classes`. - -### `userContext { host, user, classes?, direction? }` - -Build a user-scoped graph. Defaults to `[ "homeManager" "user" ]` plus the user's own classes. - -### `homeContext { home, classes?, direction? }` +- **`den.lib.capture`** (in den) — runs the fx pipeline with tracing handlers, produces structured trace entries +- **`den-diagram`** (separate repo) — graph IR, filters, renderers. Pure library, depends only on `nixpkgs.lib` -Build a home-scoped graph. Defaults to `[ "homeManager" ]` plus the home's own classes. +See [Diagrams explanation](/explanation/diagrams/) for concepts and usage patterns. -### `context { root, name, classes, direction? }` +## Two-step pattern -Generic entry point for any entity kind. `root` is a resolved entity (from `den.lib.resolveEntity`), `name` is used as the graph root label, and `classes` lists the aspect classes to trace. `direction` defaults to `"LR"`. +Capture in den, render in den-diagram: ```nix -root = den.lib.resolveEntity "user" { inherit host user; }; -g = diag.context { inherit root; name = user.name; classes = [ "homeManager" ]; }; +diagram = inputs.den-diagram.lib; + +# 1. Capture +captured = den.lib.capture.captureWithPathsWith { + classes = [ "nixos" "homeManager" ]; + root = den.lib.resolveEntity "host" { inherit host; }; + ctx = { inherit host; }; +}; + +# 2. Build graph IR +g = diagram.context { + entries = captured.entries; + ctxTrace = captured.ctxTrace; + name = host.name; +}; + +# 3. Render +diagram.toMermaid g ``` ## Graph construction ### `graph.build { entries, rootName, ctxTrace?, direction? }` -Core IR builder. Transforms a list of structured trace entries into a graph record containing `nodes`, `edges`, `entityKinds`, `entityEdges`, `entityInstances`, `rootId`, `rootName`, and `direction`. This is the lowest-level constructor -- convenience wrappers call it internally. +Core IR builder. Transforms a list of structured trace entries into a graph record containing `nodes`, `edges`, `stages`, `stageEdges`, `rootId`, `rootName`, and `direction`. This is the lowest-level constructor -- convenience wrappers call it internally. ### `graph.ofHost args` @@ -57,7 +58,7 @@ Static namespace graph built from `den.aspects` declarations (no host resolution ## Filters -All filters are available on the `graph` attrset (e.g., `diag.graph.aspectsOnly`). Each takes a graph IR and returns a new graph IR. +All filters are available on the `graph` attrset (e.g., `diagram.graph.aspectsOnly`). Each takes a graph IR and returns a new graph IR. ### Predicate filters @@ -93,7 +94,7 @@ All filters are available on the `graph` attrset (e.g., `diag.graph.aspectsOnly` |---|---|---| | `foldWrappers` | `graph -> graph` | Remove wrapper/context nodes, rewire edges to their children | | `foldProviders` | `graph -> graph` | Collapse provider chains into single edges | -| `flattenEntityKinds` | `graph -> graph` | Remove entity-kind subgraph grouping | +| `flattenStages` | `graph -> graph` | Remove stage subgraph grouping | ### Composite filters @@ -101,7 +102,7 @@ All filters are available on the `graph` attrset (e.g., `diag.graph.aspectsOnly` |---|---|---| | `filterMeaningful` | `graph -> graph` | Drop anonymous and definition-stub nodes | | `filterUserAspects` | `graph -> graph` | `foldWrappers` composed with `filterMeaningful` | -| `simplified` | `graph -> graph` | `foldProviders` + `flattenEntityKinds` + `aspectsOnly` | +| `simplified` | `graph -> graph` | `foldProviders` + `flattenStages` + `aspectsOnly` | ### Structural @@ -136,7 +137,7 @@ Every renderer has two forms: `toFoo` uses the default theme and `toFooWith { th | `toSequenceMermaid` / `toSequenceMermaidWith` | Mermaid sequence | Stage sequence diagram | | `toSequenceMermaidExpanded` / `toSequenceMermaidExpandedWith` | Mermaid sequence | Expanded with per-aspect detail | | `toPolicySequenceMermaid` / `toPolicySequenceMermaidWith` | Mermaid sequence | Policy-level sequence | -| `toScopeEdgesMermaid` / `toScopeEdgesMermaidWith` | Mermaid | Scope topology edges | +| `toStageEdgesMermaid` / `toStageEdgesMermaidWith` | Mermaid | Stage topology edges | ### C4 renderers @@ -173,19 +174,6 @@ Every renderer has two forms: `toFoo` uses the default theme and `toFooWith { th | `toStateMermaid` / `toStateMermaidWith` | Mermaid state | State diagram | | `toJSON` | JSON | Graph IR serialized to JSON (no theme) | -### Fleet flow renderers - -These consume fleet capture data (from `captureFleet`) rather than a graph IR. The fleet capture walks the entire flake scope tree (flake → environment → host → user). - -| Renderer | Format | Notes | -|---|---|---| -| `toPipeFlowMermaid` / `toPipeFlowMermaidWith` | Mermaid flowchart | Cross-host quirk/pipe data flows with environment subgraphs | -| `toScopeTopologyMermaid` / `toScopeTopologyMermaidWith` | Mermaid flowchart | Fleet scope tree (fleet → environment → host → user) | -| `toAspectMatrixMermaid` / `toAspectMatrixMermaidWith` | Mermaid | Aspect coverage matrix -- which aspects land on which hosts | -| `toPolicyResolutionMapMermaid` / `toPolicyResolutionMapMermaidWith` | Mermaid | Policy entity resolution map | -| `toPipeSequenceMermaid` / `toPipeSequenceMermaidWith` | Mermaid sequence | Pipe emit -> collect flow per pipe | -| `toFleetDagMermaid` / `toFleetDagMermaidWith` | Mermaid flowchart | Fleet-wide DAG composing all hosts' aspect trees | - ### `renderers { theme?, mermaidConfig? }` Factory that returns an attrset of all pre-configured renderers. Each key is a `toFoo` function with the given theme/config baked in. Includes `toJSON`. @@ -201,7 +189,7 @@ Hard-coded github-light palette. No `pkgs` required -- the library is usable wit Build a theme from a [base16](https://github.com/tinted-theming/schemes) palette. Converts the YAML scheme to JSON via `yj` at build time. ```nix -theme = diag.themeFromBase16 { inherit pkgs; scheme = "catppuccin-mocha"; }; +theme = diagram.themeFromBase16 { inherit pkgs; scheme = "catppuccin-mocha"; }; ``` ### `themeFromPalette palette` @@ -218,9 +206,9 @@ A theme record contains: `palette`, `background`, `foreground`, `mutedForeground ## Fleet -### `fleet.of { hosts?, flakeName? }` +### `fleet.of { hosts, flakeName? }` -Build fleet-wide data from the host registry. `hosts` defaults to `den.hosts`; `flakeName` defaults to `"den flake"`. +Build fleet-wide data from a host registry. `hosts` is required (pass `den.hosts`); `flakeName` defaults to `"den flake"`. Returns: @@ -230,7 +218,7 @@ Returns: | `hosts` | `[{ name, description }]` | Host records (description is the system string) | | `users` | `[{ name }]` | Deduplicated user records across all hosts | | `relations` | `[{ from, to, label }]` | User-to-host edges labeled with class names | -| `providerSubAspects` | `[{ provider, subAspect, hostName }]` | Lazily evaluated provider sub-aspects per host | +| `providerSubAspects` | `[{ provider, subAspect, hostName }]` | Optional — callers pre-compute via `den.lib.capture` and pass in | ## Render context @@ -250,15 +238,15 @@ Returns: | `pumlSourceToSvg` | `base -> source -> derivation` -- PlantUML source to SVG | ```nix -rc = diag.renderContext { inherit pkgs; mermaidConfig = { layout = "elk"; }; }; +rc = diagram.renderContext { inherit pkgs; mermaidConfig = { layout = "elk"; }; }; rendered = rc.render.toMermaid myGraph; ``` ## Export helpers -Utilities for turning view definitions into derivations, packages, and write scripts. Available under `diag.export`. +Utilities for turning view definitions into derivations, packages, and write scripts. Available under `diagram.export`. -### `entityEntries { pkgs, rc, diag } { entity, name, dir, viewDefs, galleryDrv? }` +### `entityEntries { pkgs, rc } { entity, name, dir, viewDefs, galleryDrv? }` Generate all derivation entries (markdown + SVG) for a single entity. `entity` must be a pre-computed graph. @@ -288,11 +276,11 @@ View definitions describe what to compute from a graph IR and how to present it. ### `views.core rc` -Essential views shared by all entity kinds: aspect hierarchy, scope sequence, expanded scope sequence, policy sequence, provider tree, and IR JSON. +Essential views shared by all entity kinds: aspect hierarchy, stage sequence, expanded stage sequence, policy sequence, provider tree, and IR JSON. ### `views.extended rc` -Optional extra views: context hierarchy, simplified, scope topology, providers resolved, adapter impact, structural decisions, user-declared aspects, and class diff. +Optional extra views: context hierarchy, simplified, stage topology, providers resolved, adapter impact, structural decisions, and user-declared aspects. ### `views.host rc` / `views.user rc` / `views.home rc` @@ -300,7 +288,7 @@ Entity-specific view sets. Currently all return `views.core rc`. ### `views.fleet rc` -Fleet-wide views: aspect namespace (from `graph.ofNamespace`) and fleet provider matrix. +Fleet-wide views: aspect namespace, fleet C4 context (PlantUML), and fleet provider matrix. ### `views.classViews rc classes` @@ -308,7 +296,7 @@ Dynamic per-class views. Generates a class-slice view for each class name in the ## Trace capture -Low-level capture functions used internally by the convenience wrappers. Available at the top level of `den.lib.diag`. +Capture functions that run the fx pipeline with tracing handlers. Available at `den.lib.capture`. ### `capture class root` @@ -322,10 +310,6 @@ Capture entries for multiple classes, concatenated. Returns `{ entries, pathsByClass, ctxTrace }` -- entries plus per-class path sets needed by presence filters. -### `captureWithPathsWith { classes, root, ctx?, extraHandlers? }` - -Extended form accepting a scope context and additional trace handlers. - -### `captureFleet { class?, extraHandlers? }` +### `captureWithPathsWith { classes, root, extraHandlers? }` -Fleet-level capture. Runs the full pipeline from the flake root, walking the entire flake scope tree (flake → environment → host → user). `class` defaults to `"nixos"`. Returns a record with `entries`, `ctxTrace`, and post-pipeline scope/pipe-flow data (`scopeParent`, `scopeContexts`, `scopeEntityKind`, `scopedPipeEffects`, `scopedClassImports`, `pipeProducers`, `pipeConsumers`) consumed by the fleet flow renderers. +Extended form accepting additional trace handlers. diff --git a/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md b/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md deleted file mode 100644 index 18c00cd49..000000000 --- a/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md +++ /dev/null @@ -1,753 +0,0 @@ -# Diagram Identity Resolution Overhaul — Implementation Plan - -> **For agentic workers:** REQUIRED: Use superpowers-extended-cc:subagent-driven-development (if subagents available) or superpowers-extended-cc:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Fix the diagram system's broken trace handler so entity kinds, scope bounding, policy nodes, and sequence diagrams all work again. - -**Architecture:** The trace handler (`trace.nix`) needs three fixes: seed entityKind from `param.__entityKind`, populate `ctxTrace` for sequence diagrams, and add a `record-fired` handler for policy nodes. The graph builder (`graph.nix`) needs entity instance grouping. The mermaid renderer needs per-instance subgraphs. View names need updating from "stage" to "scope" vocabulary. - -**Tech Stack:** Pure Nix. Tests via nix-unit (`just ci`). Diagrams via mermaid-cli for SVG rendering. - -**Spec:** `docs/superpowers/specs/2026-05-07-diagram-identity-resolution-design.md` - ---- - -### Task 1: Fix entity kind seeding and ctxTrace population in trace handler - -**Goal:** Make `tracingHandler` produce entries with correct `entityKind` and populate `ctxTrace` for sequence diagrams. - -**Files:** -- Modify: `nix/lib/aspects/fx/trace.nix:102-148` (tracingHandler resolve-complete handler) -- Test: `templates/ci/modules/features/fx-trace.nix` - -**Acceptance Criteria:** -- [ ] Entity boundary aspects (those with `__entityKind`) produce entries with non-null `entityKind` -- [ ] Descendant aspects inherit `entityKind` via `deriveEntityKind` -- [ ] `ctxTrace` accumulates one entry per entity kind with `{ key, selfName, entityKind, ctxKeys }` -- [ ] ctxTrace entries are deduplicated by entity kind -- [ ] Existing trace tests still pass - -**Verify:** `nix develop -c just ci fx-trace` → summary line shows all pass - -**Steps:** - -- [ ] **Step 1: Fix entityKind and add ctxTrace in tracingHandler, then write test** - -Note: The test and implementation are done together because the ctxTrace assertions depend on the ctxTrace implementation. Write the implementation first, then the test. - -- [ ] **Step 2: Fix entityKind in tracingHandler** - -In `nix/lib/aspects/fx/trace.nix`, in the `tracingHandler` function's `resolve-complete` handler, change: - -```nix -# Old (line ~108): -entityKind = deriveEntityKind state; -``` - -to: - -```nix -entityKind = - let direct = param.__entityKind or null; - in if direct != null then direct else deriveEntityKind state; -``` - -- [ ] **Step 3: Add ctxTrace population** - -In the same handler, after computing `entityKind` and `name`, add ctxTrace logic. Add a `resolveEntityName` helper above the handler: - -```nix -resolveEntityName = ek: scopeCtx: - let entity = scopeCtx.${ek} or null; - in if entity != null && entity ? name then entity.name - else ek; -``` - -Then in the state update section, add: - -```nix -scope = state.currentScope; -scopeCtx = if scope == null then {} else ((state.scopeContexts or (_: {})) null).${scope} or {}; -isNewKind = !(builtins.any (e: e.key == entityKind) (state.ctxTrace or [])); -ctxEntry = { - key = entityKind; - selfName = resolveEntityName entityKind scopeCtx; - entityKind = entityKind; - ctxKeys = builtins.attrNames scopeCtx; -}; -``` - -And in the state merge: - -```nix -state = state // { - entries = (state.entries or []) ++ [ entry ]; -} // lib.optionalAttrs (entityKind != null && isNewKind) { - ctxTrace = (state.ctxTrace or []) ++ [ ctxEntry ]; -}; -``` - -- [ ] **Step 4: Write test for entityKind seeding and ctxTrace** - -Add to `templates/ci/modules/features/fx-trace.nix`: - -```nix -test-tracingHandler-entity-kind-seeded = denTest ( - { den, ... }: - let - entity = { - name = "host"; - __entityKind = "host"; - meta = { provider = []; }; - nixos = { a = 1; }; - includes = [ - { - name = "child"; - meta = { provider = []; }; - nixos = { b = 2; }; - includes = []; - } - ]; - }; - result = den.lib.aspects.fx.pipeline.mkPipeline { - class = "nixos"; - extraHandlers = den.lib.aspects.fx.trace.tracingHandler "nixos"; - extraState = { entries = []; ctxTrace = []; }; - } { - self = entity // { into = _: {}; provides = {}; }; - ctx = {}; - }; - hostEntry = lib.findFirst (e: e.name == "host") null result.state.entries; - childEntry = lib.findFirst (e: e.name == "child") null result.state.entries; - in { - expr = { - hostEntityKind = hostEntry.entityKind; - childEntityKind = childEntry.entityKind; - ctxTraceLength = builtins.length result.state.ctxTrace; - ctxTraceKey = (builtins.head result.state.ctxTrace).key; - }; - expected = { - hostEntityKind = "host"; - childEntityKind = "host"; - ctxTraceLength = 1; - ctxTraceKey = "host"; - }; - } -); -``` - -- [ ] **Step 5: Run tests, format, and commit** - -Run: `nix develop -c just fmt && nix develop -c just ci fx-trace` -Expected: All tests PASS including the new entityKind test. - -```bash -git add nix/lib/aspects/fx/trace.nix templates/ci/modules/features/fx-trace.nix -git commit -m "fix(diag): seed entityKind from param.__entityKind, populate ctxTrace" -``` - ---- - -### Task 2: Add record-fired handler for policy trace entries - -**Goal:** Capture fired policy names as trace entries so the graph builder can create policy dispatch nodes. - -**Files:** -- Modify: `nix/lib/aspects/fx/trace.nix` (add `record-fired` to tracingHandler) -- Test: `templates/ci/modules/features/fx-trace.nix` - -**Acceptance Criteria:** -- [ ] `tracingHandler` returns a handler set with both `resolve-complete` and `record-fired` -- [ ] Fired policies produce entries with `isPolicyDispatch = true`, `policyName`, `from`, `entityKind` -- [ ] `to` is `null` (inferred later in graph builder) -- [ ] Composing with `defaultHandlers` doesn't break `record-fired` resume (must be `null`) - -**Verify:** `nix develop -c just ci fx-trace` → all pass - -**Steps:** - -- [ ] **Step 1: Write test for record-fired handler** - -Add to `templates/ci/modules/features/fx-trace.nix`: - -```nix -# Test that tracingHandler's record-fired creates policy entries -test-tracingHandler-record-fired = denTest ( - { den, ... }: - let - fx = den.lib.fx; - comp = fx.send "record-fired" { - entityKind = "host"; - firedPolicies = { host-to-users = true; host-to-default = true; }; - }; - result = fx.handle { - handlers = den.lib.aspects.fx.pipeline.composeHandlers - (den.lib.aspects.fx.pipeline.defaultHandlers { class = "nixos"; ctx = {}; }) - (den.lib.aspects.fx.trace.tracingHandler "nixos"); - state = den.lib.aspects.fx.pipeline.defaultState // { - entries = []; ctxTrace = []; - }; - } comp; - policyEntries = builtins.filter (e: e.isPolicyDispatch or false) result.state.entries; - policyNames = lib.sort (a: b: a < b) (map (e: e.policyName) policyEntries); - in { - expr = { - count = builtins.length policyEntries; - names = policyNames; - fromKind = (builtins.head policyEntries).from; - toIsNull = (builtins.head policyEntries).to == null; - }; - expected = { - count = 2; - names = [ "host-to-default" "host-to-users" ]; - fromKind = "host"; - toIsNull = true; - }; - } -); -``` - -- [ ] **Step 2: Add record-fired handler to tracingHandler** - -In `nix/lib/aspects/fx/trace.nix`, change `tracingHandler` from returning a single-key handler set to a two-key handler set. Add `record-fired` alongside `resolve-complete`: - -```nix -tracingHandler = class: { - "resolve-complete" = { param, state }: /* ... existing handler ... */; - - "record-fired" = { param, state }: - let - firedNames = builtins.attrNames param.firedPolicies; - policyEntries = map (policyName: { - name = policyName; - class = ""; - parent = null; - provider = []; - excluded = false; - excludedFrom = null; - replacedBy = null; - isProvider = false; - handlers = []; - hasClass = false; - isParametric = false; - fnArgNames = []; - entityKind = param.entityKind; - isPolicyDispatch = true; - policyName = policyName; - from = param.entityKind; - to = null; - }) firedNames; - in { - resume = null; - state = state // { - entries = (state.entries or []) ++ policyEntries; - }; - }; -}; -``` - -- [ ] **Step 3: Run tests and commit** - -Run: `nix develop -c just fmt && nix develop -c just ci fx-trace` -Expected: All tests PASS. - -```bash -git add nix/lib/aspects/fx/trace.nix templates/ci/modules/features/fx-trace.nix -git commit -m "feat(diag): add record-fired handler to capture policy dispatch entries" -``` - ---- - -### Task 3: Add entity instance tracking to trace entries and graph IR - -**Goal:** Each trace entry carries an `entityInstance` field (e.g., `"host:laptop"`) so the graph builder can group nodes by specific entity instances. The graph IR gains an `entityInstances` list. - -**Files:** -- Modify: `nix/lib/aspects/fx/trace.nix:102-148` (add `entityInstance` to entry) -- Modify: `nix/lib/diag/graph.nix:23-51` (add `entityInstance` to `emptyNode` and `stubEntry`) -- Modify: `nix/lib/diag/graph.nix:100-493` (build `entityInstances` list in `buildGraph`, add `entityInstance` to `mkNode`) -- Modify: `nix/lib/diag/json.nix:41-51` (add `entityInstances` to serialized output) - -**Acceptance Criteria:** -- [ ] Trace entries include `entityInstance` field (e.g., `"host:laptop"` or `null`) -- [ ] `emptyNode` and `stubEntry` have `entityInstance = null` -- [ ] `buildGraph` output includes `entityInstances` list with `{ id, kind, name, label }` records -- [ ] `buildGraph` output retains `entityKinds` for sequence diagram compatibility -- [ ] Nodes without entity kind get `entityInstance = "flake"` when there are other entity instances in the graph - -**Verify:** `nix develop -c just ci fx-trace` → all pass; `nix build --override-input den . ./templates/diagram-demo#laptop-ir --no-link --print-out-paths` → IR JSON has `entityInstances` array with entries - -**Steps:** - -- [ ] **Step 1: Add entityInstance to trace entries** - -In `nix/lib/aspects/fx/trace.nix`, in the `tracingHandler`'s `resolve-complete` handler, compute `entityInstance` alongside `entityKind`: - -```nix -entityInstance = - if entityKind != null then - let - scope = state.currentScope; -scopeCtx = if scope == null then {} else ((state.scopeContexts or (_: {})) null).${scope} or {}; - eName = resolveEntityName entityKind scopeCtx; - in "${entityKind}:${eName}" - else null; -``` - -Add `inherit entityInstance;` to the entry record. - -Also add `entityInstance` to the `record-fired` policy entries, using the same derivation from state at that point. - -- [ ] **Step 2: Add entityInstance to graph.nix emptyNode/stubEntry** - -In `nix/lib/diag/graph.nix`, add to `emptyNode` (after `entityKind = null;`): - -```nix -entityInstance = null; -``` - -Add the same to `stubEntry`. - -- [ ] **Step 3: Add entityInstance to mkNode and build entityInstances list** - -In `buildGraph`, after computing `mkNode`, read `entityInstance` from the entry: - -```nix -mkNode = entry: - let - # ... existing code ... - in { - # ... existing fields ... - entityInstance = entry.entityInstance or null; - }; -``` - -After `finalNodes`, build `entityInstances`: - -```nix -# Assign "flake" instance to unscoped nodes when entity instances exist. -hasAnyInstances = builtins.any (n: n.entityInstance != null) finalNodes; -taggedNodes = if hasAnyInstances then - map (n: if n.entityInstance == null then n // { entityInstance = "flake"; } else n) finalNodes -else finalNodes; - -entityInstanceNames = lib.unique ( - builtins.filter (s: s != null) (map (n: n.entityInstance) taggedNodes) -); -entityInstances = map (inst: - let - parts = lib.splitString ":" inst; - kind = builtins.head parts; - name = if builtins.length parts > 1 then lib.concatStringsSep ":" (lib.tail parts) else inst; - in { - id = sanitize "ctx_${inst}"; - inherit kind name; - label = if inst == "flake" then "flake" else "${kind}: ${name}"; - } -) entityInstanceNames; -``` - -Update the return record: - -```nix -{ - inherit rootName direction; - rootId = sanitize rootName; - nodes = taggedNodes; # was: finalNodes - edges = /* ... unchanged ... */; - entityKinds = map mkEntityKind entityKindNames; # retained for sequence diagrams - inherit entityEdges entityInstances; -} -``` - -- [ ] **Step 4: Update json.nix to serialize entityInstances** - -In `nix/lib/diag/json.nix`, add `entityInstances` to the `toJSON` function's output record (line 49, after `entityEdges`): - -```nix -entityInstances = g.entityInstances or []; -``` - -- [ ] **Step 5: Also add entityInstance to record-fired entries** - -In `nix/lib/aspects/fx/trace.nix`, update the `record-fired` handler's policy entries to include `entityInstance`: - -```nix -scope = state.currentScope; -scopeCtx = if scope == null then {} else ((state.scopeContexts or (_: {})) null).${scope} or {}; -entityInstance = - if param.entityKind != null then - "${param.entityKind}:${resolveEntityName param.entityKind scopeCtx}" - else null; -``` - -Add `inherit entityInstance;` to each policy entry. - -- [ ] **Step 6: Run tests and verify IR output** - -Run: `nix develop -c just fmt && nix develop -c just ci fx-trace` -Then: `nix build --override-input den . ./templates/diagram-demo#laptop-ir --no-link --print-out-paths` and inspect the JSON for `entityInstances`. - -```bash -git add nix/lib/aspects/fx/trace.nix nix/lib/diag/graph.nix nix/lib/diag/json.nix -git commit -m "feat(diag): add entityInstance tracking to trace entries and graph IR" -``` - ---- - -### Task 4: Update mermaid renderer for entity instance subgraphs and policy bridges - -**Goal:** DAG diagrams group nodes into per-instance subgraphs (`host:laptop`, `user:alice`) with policy nodes as bridges between them. - -**Files:** -- Modify: `nix/lib/diag/mermaid.nix:70-275` (replace `entitySubgraph` with instance-based grouping, update policy edge rendering) - -**Acceptance Criteria:** -- [ ] DAG renders `subgraph` blocks per entity instance (e.g., `host: laptop`, `user: alice`) -- [ ] Unscoped nodes render in a `flake` subgraph -- [ ] Policy dispatch nodes render outside all subgraphs with bridge edges -- [ ] Cross-instance edges render at top level -- [ ] Flat views (no entity instances) still work correctly - -**Verify:** `nix build --override-input den . ./templates/diagram-demo#laptop-dag --no-link --print-out-paths` → DAG contains `subgraph` blocks with entity instance labels - -**Steps:** - -- [ ] **Step 1: Replace entitySubgraph with instanceSubgraph** - -In `nix/lib/diag/mermaid.nix`, replace the `entitySubgraph` function and related code. The key changes: - -Replace `hasEntityKinds` with: -```nix -hasEntityInstances = graph.entityInstances or [] != []; -``` - -Replace `entitySubgraph` with: -```nix -instanceSubgraph = inst: - let - instNodes = builtins.filter (n: - n.entityInstance == "${inst.kind}:${inst.name}" - && n.id != rootId - && !(n.isPolicyDispatch or false) - ) nodes; - instEdges = builtins.filter (e: - let - fromNode = nodeById.${e.from} or null; - toNode = nodeById.${e.to} or null; - fromInst = if fromNode != null then fromNode.entityInstance else null; - toInst = if toNode != null then toNode.entityInstance else null; - thisInst = "${inst.kind}:${inst.name}"; - in - fromNode != null - && fromInst == thisInst - && (toInst == null || toInst == thisInst) - && (e.style or "normal") != "policy" - ) edges; - in - lib.optional (instNodes != []) ( - " subgraph ${inst.id}[\"${inst.label}\"]\n" - + lib.concatMapStringsSep "\n" nodeDecl instNodes - + "\n" - + lib.concatMapStringsSep "\n" edgeDecl instEdges - + "\n end" - ); -``` - -- [ ] **Step 2: Update topLevelNodes and unmappedEdges for instances** - -```nix -topLevelNodes = - if hasEntityInstances then - builtins.filter (n: - n.entityInstance == null - && n.id != rootId - && !(n.isPolicyDispatch or false) - ) nodes - else - builtins.filter (n: n.id != rootId) nodes; - -policyNodes = builtins.filter (n: n.isPolicyDispatch or false) nodes; - -unmappedEdges = builtins.filter (e: - let - fromNode = nodeById.${e.from} or null; - toNode = nodeById.${e.to} or null; - fromInst = if fromNode != null then fromNode.entityInstance else null; - toInst = if toNode != null then toNode.entityInstance else null; - isCrossInst = fromInst != null && toInst != null && fromInst != toInst; - in - (fromNode != null && fromInst == null) - || (isCrossInst && (e.style or "normal") != "policy") -) edges; -``` - -- [ ] **Step 3: Update the diagram assembly** - -Replace the `hasEntityKinds` branch in the final `renderMermaid` call with `hasEntityInstances`: - -```nix -if hasEntityInstances then - lib.concatMap instanceSubgraph (graph.entityInstances or []) - ++ [ "" ] - ++ map nodeDecl policyNodes - ++ map edgeDecl (builtins.filter (e: (e.style or "normal") == "policy") edges) - ++ map edgeDecl unmappedEdges -else - map edgeDecl edges -``` - -Update `kindSuffix` to use `entityInstance` instead of `entityKind` for flat views. Update the subgraph style lines to iterate `entityInstances` instead of `entityKinds`. - -- [ ] **Step 4: Verify and commit** - -Run: `nix develop -c just fmt` -Then: `nix build --override-input den . ./templates/diagram-demo#laptop-dag --no-link --print-out-paths` and read the output. -Expected: Mermaid source contains `subgraph ctx_host_laptop["host: laptop"]` and similar blocks. - -```bash -git add nix/lib/diag/mermaid.nix -git commit -m "feat(diag): render entity instance subgraphs with policy bridges" -``` - ---- - -### Task 5: Investigate and fix anonymous nodes - -**Goal:** Determine what the `host/:3`, `user/:2`, `insecure-predicate/:1` nodes actually are, then either prune internal artifacts or enrich their labels. - -**Files:** -- Modify: `nix/lib/aspects/fx/trace.nix` (improve anon naming with entityKind now working) -- Possibly modify: `nix/lib/diag/filters/predicate.nix` or `fold.nix` (if pruning needed) - -**Acceptance Criteria:** -- [ ] Anonymous nodes that are entity resolution boundaries get `entityKind/resolve(ctxAspect)` names -- [ ] Anonymous nodes from policy-emitted includes get `policy:` prefix where possible -- [ ] Pure internal plumbing nodes (no class content, no children) are identified and documented -- [ ] `insecure-predicate/:1,2` nodes are explained and either named or pruned - -**Verify:** `nix build --override-input den . ./templates/diagram-demo#laptop-dag --no-link --print-out-paths` → no unexplained `:N` nodes - -**Steps:** - -- [ ] **Step 1: Verify entityKind disambiguation now works** - -After Task 1, rebuild the laptop DAG and check which anon nodes remain. Many should now have `entityKind/resolve(...)` names since entityKind is no longer null. - -Read the IR JSON and list remaining anonymous nodes. For each, check: -- Does it have class content (`hasClass`)? → real entity, needs a name -- Does it have children in the edge list? → structural node, needs a name -- Neither? → plumbing artifact, candidate for pruning - -- [ ] **Step 2: Investigate insecure-predicate/unfree-predicate anon children** - -Read the demo aspects that define `insecure-predicate` and `unfree-predicate` in `templates/diagram-demo/modules/aspects/den.nix`. Check if their `includes` contain anonymous functions (compile-conditional guards). The `:N` children are likely the guard branches. - -If they are conditional guard branches with no class content: these are structural artifacts of `compile-conditional`. They should be folded out in the `foldWrappers` filter or the existing `aspectsOnly` filter. - -If they have class content: they need names derived from their parent and role. - -- [ ] **Step 3: Improve naming for remaining anon nodes** - -In `nix/lib/aspects/fx/trace.nix`, after the existing entity-kind disambiguation block, add handling for policy-sourced aspects: - -```nix -else if isAnon && (param.__sourcePolicyName or null) != null then - "policy:${param.__sourcePolicyName}" -``` - -Verify `__sourcePolicyName` propagation: check `nix/lib/aspects/fx/policy/classify.nix` for where this is tagged. - -- [ ] **Step 4: Commit findings and fixes** - -Document which anon nodes were real vs artifacts. Commit the naming improvements. - -```bash -git add nix/lib/aspects/fx/trace.nix -git commit -m "fix(diag): improve anonymous node naming with entityKind disambiguation" -``` - ---- - -### Task 6: Rename views from "stage" to "scope" vocabulary - -**Goal:** Update view identifiers, titles, and internal function names from the removed "stage" concept to "scope". - -**Files:** -- Modify: `nix/lib/diag/views.nix:96-108,162-166` (view IDs and titles) -- Modify: `nix/lib/diag/sequence.nix:319-359` (rename `toStageEdgesMermaid` → `toScopeEdgesMermaid`) -- Modify: `nix/lib/diag/default.nix:208-210` (renderer spec key) -- Modify: `templates/diagram-demo/modules/diagrams.nix` (if it references stage-* views) - -**Acceptance Criteria:** -- [ ] `stage-seq` → `scope-seq`, `stage-seq-full` → `scope-seq-full`, `stage-edges` → `scope-edges` -- [ ] View titles updated: "Stage Sequence" → "Scope Sequence" etc. -- [ ] Internal function `toStageEdgesMermaid` → `toScopeEdgesMermaid` (and `With` variant) -- [ ] Renderer spec key in `default.nix` updated -- [ ] No remaining "stage" references in view/sequence code (except comments explaining the rename) - -**Verify:** `nix build --override-input den . ./templates/diagram-demo#laptop-scope-seq --no-link --print-out-paths` → builds successfully with "Scope Sequence" title - -**Steps:** - -- [ ] **Step 1: Rename in views.nix** - -```nix -# Line 96-108: rename view IDs and titles -view = "scope-seq"; -title = "Scope Sequence"; -altText = "Scope sequence"; - -view = "scope-seq-full"; -title = "Scope Sequence (expanded)"; -altText = "Scope sequence expanded"; - -# Line 162-166: extended views -view = "scope-edges"; -title = "Scope Topology"; -altText = "Scope edges"; -``` - -- [ ] **Step 2: Rename in sequence.nix** - -Rename the functions and their `With` variants: -- `toStageEdgesMermaidWith` → `toScopeEdgesMermaidWith` -- `toStageEdgesMermaid` → `toScopeEdgesMermaid` - -Update the export block at the bottom of the file. - -- [ ] **Step 3: Update default.nix renderer spec** - -In `nix/lib/diag/default.nix`, line 208-210: - -```nix -# Old: -toStageEdgesMermaid = { withFn = sequence.toStageEdgesMermaidWith; mc = true; }; -# New: -toScopeEdgesMermaid = { withFn = sequence.toScopeEdgesMermaidWith; mc = true; }; -``` - -- [ ] **Step 4: Update diagram-demo template if needed** - -Check `templates/diagram-demo/modules/diagrams.nix` for any references to `stage-seq`, `stage-seq-full`, or `stage-edges` view names and update them. - -- [ ] **Step 5: Run full CI and commit** - -Run: `nix develop -c just fmt && nix develop -c just ci` -Expected: All tests pass. The diagram packages now use `scope-seq` etc. - -Note: Also check `diagrams.nix` README text (writeText derivation) for hardcoded `stage-seq` strings in the output table and update them. - -Note: Package names change from `laptop-stage-seq` to `laptop-scope-seq`. This is acceptable since the branch hasn't shipped. - -```bash -git add nix/lib/diag/views.nix nix/lib/diag/sequence.nix nix/lib/diag/default.nix templates/diagram-demo/modules/diagrams.nix -git commit -m "refactor(diag): rename stage-* views to scope-* vocabulary" -``` - ---- - -### Task 7: Update filters for entityInstances and run full verification - -**Goal:** Ensure all graph filters handle the new `entityInstances` field, then do a full verification pass. - -**Files:** -- Modify: `nix/lib/diag/filters/fold.nix:178-185` (`flattenEntityKinds`) -- Modify: `nix/lib/diag/filters/reshape.nix:20-49` (`contextOnly`) -- Modify: `nix/lib/diag/filters/closure.nix:25-35` (`neighborhoodOf`) -- Modify: `nix/lib/diag/filters/predicate.nix:55-62` (if it zeros entityKinds) -- Modify: `nix/lib/diag/filters/diff.nix:80-85` (carry entityInstances) - -**Acceptance Criteria:** -- [ ] `flattenEntityKinds` also zeros `entityInstances` and nulls `entityInstance` on nodes -- [ ] `contextOnly` handles entity instances (or zeros them since it replaces all nodes) -- [ ] `neighborhoodOf` zeros `entityInstances` -- [ ] `diffGraphs` carries `entityInstances` from the `a` graph -- [ ] Predicate filters zero `entityInstances` -- [ ] All diagram packages build without errors -- [ ] Full CI passes - -**Verify:** `nix develop -c just ci` → all pass; `nix run --override-input den . ./templates/diagram-demo#write-diagrams` → regenerates all diagrams successfully - -**Steps:** - -- [ ] **Step 1: Update flattenEntityKinds** - -In `nix/lib/diag/filters/fold.nix`: - -```nix -flattenEntityKinds = graph: - graph // { - nodes = map (n: n // { entityKind = null; entityInstance = null; }) graph.nodes; - entityKinds = []; - entityEdges = []; - entityInstances = []; - }; -``` - -- [ ] **Step 2: Update contextOnly** - -In `nix/lib/diag/filters/reshape.nix`, `contextOnly` replaces all nodes, so add: - -```nix -entityInstances = []; -``` - -to the result attrset. - -- [ ] **Step 3: Update neighborhoodOf and closure filters** - -In `nix/lib/diag/filters/closure.nix`, `neighborhoodOf` already zeros `entityKinds` and `entityEdges`. Add: - -```nix -entityInstances = []; -``` - -- [ ] **Step 4: Update diffGraphs** - -In `nix/lib/diag/filters/diff.nix`, carry `entityInstances` from graph `a`: - -```nix -entityInstances = a.entityInstances or []; -``` - -- [ ] **Step 5: Update predicate filters** - -In `nix/lib/diag/filters/predicate.nix`, add `entityInstances = [];` where `entityKinds = [];` already appears. - -- [ ] **Step 6: Full CI and regenerate diagrams** - -Run: `nix develop -c just fmt && nix develop -c just ci` -Expected: All 753+ tests pass. - -Run: `nix run --override-input den . ./templates/diagram-demo#write-diagrams` -Expected: All diagrams regenerate without errors. - -Verify key outputs: -- `laptop-dag` has entity instance subgraphs -- `laptop-scope-seq` has entity kind participants -- `laptop-policy-seq` has policy dispatch nodes -- `home-alice-dag` has home entity instance subgraph - -```bash -git add nix/lib/diag/filters/ -git commit -m "fix(diag): update filters to handle entityInstances field" -``` - ---- - -## Task Dependencies - -``` -Task 1 (entityKind + ctxTrace) - ↓ -Task 2 (record-fired) - ↓ -Task 3 (entityInstance in trace + graph IR) - ↓ -Task 4 (mermaid renderer) Task 5 (anon nodes) Task 6 (view rename) - ↓ ↓ ↓ -Task 7 (filters + full verification) -``` - -Tasks 4, 5, and 6 can run in parallel after Task 3. Task 7 depends on all of them. diff --git a/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md.tasks.json b/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md.tasks.json deleted file mode 100644 index f4dcc089a..000000000 --- a/docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md.tasks.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "planPath": "docs/superpowers/plans/2026-05-07-diagram-identity-resolution.md", - "tasks": [ - { - "id": 2, - "subject": "Task 1: Fix entityKind seeding and ctxTrace population", - "status": "pending", - "description": "**Goal:** Make tracingHandler produce entries with correct entityKind and populate ctxTrace.\n\n**Files:**\n- Modify: nix/lib/aspects/fx/trace.nix\n- Test: templates/ci/modules/features/fx-trace.nix\n\n**Verify:** nix develop -c just ci fx-trace", - "metadata": { - "files": ["nix/lib/aspects/fx/trace.nix", "templates/ci/modules/features/fx-trace.nix"], - "verifyCommand": "nix develop -c just ci fx-trace", - "acceptanceCriteria": ["entityKind seeded from param.__entityKind", "ctxTrace populated with dedup", "existing tests pass"] - } - }, - { - "id": 3, - "subject": "Task 2: Add record-fired handler for policy trace entries", - "status": "pending", - "blockedBy": [2], - "description": "**Goal:** Capture fired policy names as trace entries for policy dispatch nodes.\n\n**Files:**\n- Modify: nix/lib/aspects/fx/trace.nix\n- Test: templates/ci/modules/features/fx-trace.nix\n\n**Verify:** nix develop -c just ci fx-trace", - "metadata": { - "files": ["nix/lib/aspects/fx/trace.nix", "templates/ci/modules/features/fx-trace.nix"], - "verifyCommand": "nix develop -c just ci fx-trace", - "acceptanceCriteria": ["record-fired handler added", "policy entries have isPolicyDispatch=true", "compose with defaultHandlers works"] - } - }, - { - "id": 4, - "subject": "Task 3: Add entityInstance tracking to trace and graph IR", - "status": "pending", - "blockedBy": [3], - "description": "**Goal:** Each trace entry carries entityInstance field, graph IR gains entityInstances list.\n\n**Files:**\n- Modify: nix/lib/aspects/fx/trace.nix\n- Modify: nix/lib/diag/graph.nix\n\n**Verify:** nix develop -c just ci fx-trace + build laptop-ir", - "metadata": { - "files": ["nix/lib/aspects/fx/trace.nix", "nix/lib/diag/graph.nix", "nix/lib/diag/json.nix"], - "verifyCommand": "nix develop -c just ci fx-trace", - "acceptanceCriteria": ["entityInstance on trace entries", "entityInstances in graph IR", "entityKinds retained", "json.nix serializes entityInstances"] - } - }, - { - "id": 5, - "subject": "Task 4: Update mermaid renderer for instance subgraphs", - "status": "pending", - "blockedBy": [4], - "description": "**Goal:** DAG diagrams group nodes into per-instance subgraphs with policy bridges.\n\n**Files:**\n- Modify: nix/lib/diag/mermaid.nix\n\n**Verify:** Build laptop-dag, check for subgraph blocks", - "metadata": { - "files": ["nix/lib/diag/mermaid.nix"], - "verifyCommand": "nix build --override-input den . ./templates/diagram-demo#laptop-dag --no-link --print-out-paths", - "acceptanceCriteria": ["per-instance subgraphs", "flake subgraph for unscoped", "policy bridges"] - } - }, - { - "id": 6, - "subject": "Task 5: Investigate and fix anonymous nodes", - "status": "pending", - "blockedBy": [4], - "description": "**Goal:** Determine what anon nodes are, then prune artifacts or enrich labels.\n\n**Files:**\n- Modify: nix/lib/aspects/fx/trace.nix\n\n**Verify:** Build laptop-dag, check for unexplained anon nodes", - "metadata": { - "files": ["nix/lib/aspects/fx/trace.nix"], - "verifyCommand": "nix build --override-input den . ./templates/diagram-demo#laptop-dag --no-link --print-out-paths", - "acceptanceCriteria": ["no unexplained anon nodes", "entityKind disambiguation works", "policy naming works"] - } - }, - { - "id": 7, - "subject": "Task 6: Rename views from stage to scope vocabulary", - "status": "pending", - "blockedBy": [4], - "description": "**Goal:** Update view IDs, titles, and function names from stage to scope.\n\n**Files:**\n- Modify: nix/lib/diag/views.nix, sequence.nix, default.nix, diagrams.nix\n\n**Verify:** Build laptop-scope-seq", - "metadata": { - "files": ["nix/lib/diag/views.nix", "nix/lib/diag/sequence.nix", "nix/lib/diag/default.nix", "templates/diagram-demo/modules/diagrams.nix"], - "verifyCommand": "nix build --override-input den . ./templates/diagram-demo#laptop-scope-seq --no-link --print-out-paths", - "acceptanceCriteria": ["view IDs renamed", "function names renamed", "titles updated"] - } - }, - { - "id": 8, - "subject": "Task 7: Update filters and full verification", - "status": "pending", - "blockedBy": [5, 6, 7], - "description": "**Goal:** Ensure all filters handle entityInstances, full CI + diagram regen passes.\n\n**Files:**\n- Modify: nix/lib/diag/filters/\n\n**Verify:** nix develop -c just ci + write-diagrams", - "metadata": { - "files": ["nix/lib/diag/filters/fold.nix", "nix/lib/diag/filters/reshape.nix", "nix/lib/diag/filters/closure.nix", "nix/lib/diag/filters/predicate.nix", "nix/lib/diag/filters/diff.nix"], - "verifyCommand": "nix develop -c just ci", - "acceptanceCriteria": ["filters handle entityInstances", "full CI passes", "all diagrams build"] - } - } - ], - "lastUpdated": "2026-05-07T00:00:00Z" -} diff --git a/docs/superpowers/plans/2026-05-07-hero-canvas-animation.md b/docs/superpowers/plans/2026-05-07-hero-canvas-animation.md deleted file mode 100644 index 28386d7d6..000000000 --- a/docs/superpowers/plans/2026-05-07-hero-canvas-animation.md +++ /dev/null @@ -1,283 +0,0 @@ -# Hero Canvas Animation Implementation Plan - -> **For agentic workers:** REQUIRED: Use superpowers-extended-cc:subagent-driven-development (if subagents available) or superpowers-extended-cc:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Replace the static hero image on the docs landing page with an animated canvas showing a branching topology. - -**Architecture:** Override Starlight's Hero.astro component to conditionally render a LogoAnimation.astro component on the index page. LogoAnimation is self-contained: canvas markup, scoped styles, and a client-side script with the topology animation algorithm adapted from ~/foo-wip.html. - -**Tech Stack:** Astro components, HTML Canvas 2D API, Catppuccin CSS custom properties, IntersectionObserver, MutationObserver - -**Spec:** `docs/superpowers/specs/2026-05-07-hero-canvas-animation-design.md` - ---- - -### Task 1: Create LogoAnimation.astro component - -**Goal:** Create the self-contained canvas animation component with topology algorithm, theme-aware colors, visibility pausing, and SPA cleanup. - -**Files:** -- Create: `docs/src/components/LogoAnimation.astro` -- Reference: `/home/sini/foo-wip.html` (source algorithm, outside repo) - -**Acceptance Criteria:** -- [ ] Canvas renders at 400×400 -- [ ] Animation cycles: grow topology → photon walk → rewind → regenerate (infinite loop) -- [ ] Colors read from `--sl-color-blue`, `--sl-color-purple`, `--sl-color-white` CSS custom properties -- [ ] Theme toggle (light/dark) updates colors via MutationObserver on `data-theme` -- [ ] IntersectionObserver pauses animation when canvas is off-screen -- [ ] `astro:before-swap` listener cleans up (cancelAnimationFrame, clearTimeouts, disconnect observers) -- [ ] No "den" text overlay -- [ ] Component wrapper has correct sizing styles for hero slot - -**Verify:** Visual inspection — component can be tested by temporarily importing it directly in index.mdx body. - -**Steps:** - -- [ ] **Step 1: Create LogoAnimation.astro with markup and scoped styles** - -```astro ---- -// No server-side logic needed ---- - -
- -
- - -``` - -- [ ] **Step 2: Add client-side script with color resolution and observers** - -Add a `