Date: 2026-05-22
Status: Design (v3)
Location: scope-engine/templates/nest-traits/
Depends on: gen-schema ref coercion spike (listOf coercion, custom coerce hooks, setOf)
Builds on: Schema-native redesign (traits as gen-schema instances)
schemaLib.ref "kind" supports string-to-instance coercion for scalar fields. Assigning host = "igloo" on a ref "host" field resolves to config.hosts.igloo via an apply function injected by mkRefBindingModules.
This breaks for listOf (ref "kind"). The apply function receives the entire list, checks builtins.isString val, gets false (it's a list), and passes it through unchanged. Individual string elements never get coerced. Same gap exists for nullOr (ref "kind") in the deferred path (the direct-mode nullOr test uses nullOr (ref instances), not nullOr (ref "kind")).
Consumers hit this in practice: nest-traits declares needs = listOf (ref "trait") and wants needs = [ "nginx" "firewall" ] to resolve each string to a trait instance.
Date: 2026-05-23
Status: Design approved
Location: scope-engine/templates/sql-schema/
Demonstrate the full gen ecosystem (gen-schema, gen-graph, scope-engine) via a complex infrastructure schema DSL — the SQL equivalent of nest-traits' CSS engine. 22 kinds modeling a multi-datacenter, multi-environment fleet. Two output modes:
A consistent terminology grounded in attribute grammar theory, spanning all five libraries and den v2.
The gen ecosystem implements a demand-driven Higher-Order Attribute Grammar (HOAG) evaluator over scope graphs. The vocabulary maps directly to AG theory as formalized by Knuth (1968), extended by Sloane (2010), and composed with Neron (2015) scope graphs and Arntzenius (2016) monotonic query combinators.
A demand-driven Higher-Order Attribute Grammar evaluator in pure Nix. No convergence loops, no flat pre-registration, no iteration. Tree expansion interleaved with attribute evaluation via lib.fix laziness. Every attribute evaluates exactly once — including on synthesized nodes.
Matches the semantics described by Vogt (1989), Sloane (2010), and Hedin (2003), adapted to Nix's eager-keys/lazy-values attrset semantics.
Status: Proven with working prototype (demos/hoag-hybrid-memo.nix). This spec defines the target architecture for gen-scope.
| let | |
| fix = f: let x = f x; in x; | |
| foldl' = builtins.foldl'; | |
| attrNames = builtins.attrNames; | |
| listToAttrs = builtins.listToAttrs; | |
| roots = { | |
| "host:igloo" = { | |
| id = "host:igloo"; | |
| type = "host"; |
Den v2 replaces the 37-handler fx-pipeline (~7,000 lines) with demand-driven attribute evaluation over a scope graph, built on five standalone, fully decoupled libraries. Tree expansion is interleaved with evaluation via Nix's native laziness — no convergence loops, no flat pre-registration, no iteration.
Supersedes: 2026-05-19-hoag-pipeline-architecture.md, 2026-05-19-scope-engine-design.md
The fx-pipeline (den v1) uses an algebraic effects trampoline with 37 sequential handlers, 25 mutable state fields, and nix-effects for stack safety. Implementation analysis revealed that the complexity serves the handler-chain architecture, not inherent problem complexity. A subsequent compiler-passes model was us manually scheduling what Nix's lazy evaluation does automatically.
| name | gen-select design hints |
|---|---|
| description | Planned selector algebra library for pattern matching over attributed graph positions. Design hints from nest-traits, SQL demo, den v2 brainstorming session 2026-05-24. |
| type | project |
gen-select: planned library for selector algebra over attributed graph positions.
Origin: Emerged from den v2 brainstorming when neededBy needed to express "inject wherever this pattern matches" rather than just literal aspect references. The nest-traits demo already has a working selector system in selectors.nix — gen-select extracts and generalizes this.
| { denTest, ... }: | |
| let | |
| mockMaidModule = | |
| { lib, ... }: | |
| { | |
| options.users.users = lib.mkOption { | |
| type = lib.types.attrsOf ( | |
| lib.types.submodule { | |
| options.maid = lib.mkOption { | |
| type = lib.types.submoduleWith { |