website-htmx-rustelo/.ontoref/ontology/core.ncl
Jesús Pérez 3ccd15a14b init repo
2026-07-10 03:33:46 +01:00

139 lines
8.7 KiB
Text

let d = import "defaults/core.ncl" in
{
nodes = [
# ── Axioms (invariant = true) ─────────────────────────────────────────────
d.make_node {
id = "consume-not-fork",
name = "Consume, Don't Fork",
pole = 'Yang,
level = 'Axiom,
description = "This project is an implementation of Rustelo, never a fork of it. All customization flows through the framework's layered override system (Local > Feature > Template > Framework) and the plugin architecture — framework crates are consumed as dependencies, never modified in place.",
invariant = true,
artifact_paths = ["Cargo.toml", "crates/"],
},
d.make_node {
id = "config-driven-content",
name = "Config-Driven Content & Routing",
pole = 'Yang,
level = 'Axiom,
description = "Routes, menus, themes, content types and rendering profiles are declared in NCL/TOML under site/config — never hardcoded in Rust. Languages are discovered from configuration, not literals.",
invariant = true,
artifact_paths = ["site/config/", "site/config/routes/"],
},
d.make_node {
id = "bilingual-parity",
name = "Bilingual Parity (EN/ES)",
pole = 'Yang,
level = 'Axiom,
description = "Every public route and content object exists in both English and Spanish, with Fluent FTL keys symmetric across locales. The site never ships a route present in one language but missing in the other.",
invariant = true,
artifact_paths = ["site/content/", "site/i18n/locales/"],
},
# ── Tensions ──────────────────────────────────────────────────────────────
d.make_node {
id = "htmx-vs-hydration",
name = "HTMX SSR vs Leptos Hydration",
pole = 'Spiral,
level = 'Tension,
description = "The site selects the HTMX server-side rendering profile (ADR-006) for simplicity and crawlability, but Rustelo also supports Leptos WASM hydration. Synthesis (not collapse): rendering profile is a per-route configuration choice over compile-time-typed contracts, so the site can opt individual routes into hydration without abandoning the HTMX default.",
},
d.make_node {
id = "content-velocity-vs-parity",
name = "Content Velocity vs Bilingual Parity",
pole = 'Spiral,
level = 'Tension,
description = "Publishing EN content quickly conflicts with the parity axiom. Resolved by treating ES completion as a tracked publication blocker (validate-content mode) rather than relaxing the invariant — EN may be drafted ahead, but a route is not production-published until ES reaches parity.",
},
d.make_node {
id = "framework-vs-one-site",
name = "Framework Neutrality vs One-Site Convenience",
pole = 'Spiral,
level = 'Tension,
description = "code/ builds the shared rustelo-htmx-server binary consumed by multiple sites (the personal bilingual site and ontoref/outreach/site), yet site/config — routes, menus, theme, logo, languages — is consumed at compile time, so the binary IS one specific site. Synthesis (not collapse): the customization seam keeps brand, content, templates and env on the runtime side (per-consumer, no rebuild) while routes and identity stay build-baked; the direction of motion is from binary-is-one-site toward binary-serves-N-consumers-from-consumer-supplied-config, without pretending routing can be fully dynamic. Forbidden collapse: letting brand/content customization leak into crates/server/src (one-site capture) OR demanding every config be runtime-dynamic (losing the build-baked routing that is a real performance choice).",
},
# ── Practices ─────────────────────────────────────────────────────────────
d.make_node {
id = "htmx-rendering-profile",
name = "HTMX Rendering Profile",
pole = 'Yang,
level = 'Practice,
description = "Routes render server-side via Rustelo's HTMX profile and Minijinja templates, with per-template-block rendering for htmx fragment swaps (HX-Target → CSS id → block name). Vendored htmx assets verified by lockfile at startup.",
artifact_paths = ["site/config/routes/", "crates/pages_htmx/"],
},
d.make_node {
id = "bilingual-content-system",
name = "Bilingual Content System",
pole = 'Yang,
level = 'Practice,
description = "Per-language content directories and Fluent FTL bundles, with route configs declared per locale. Content integrity is checked by the validate-content reflection mode (route symmetry, content file completeness, FTL key coverage).",
artifact_paths = ["site/content/en/", "site/content/es/", "site/i18n/locales/"],
},
d.make_node {
id = "config-driven-routes",
name = "Config-Driven Route Surface",
pole = 'Yang,
level = 'Practice,
description = "Route table, menus and themes defined in NCL/TOML and resolved by Rustelo's custom routing engine at build time. No Leptos Router imports, no hardcoded paths.",
artifact_paths = ["site/config/routes/", "site/config/menus/"],
},
d.make_node {
id = "layered-customization",
name = "Layered Customization",
pole = 'Yin,
level = 'Practice,
description = "Site-specific look and behavior layered on top of framework defaults via local overrides and feature templates (UnoCSS theme, partials, page components), keeping the framework upgrade path intact.",
artifact_paths = ["site/config/", "uno.config.ts", "templates/"],
},
d.make_node {
id = "customization-seam",
name = "Customization Seam — Right Layer per Change",
pole = 'Yin,
level = 'Practice,
description = "Consumer customization spans four mechanisms: content + templates + brand-CSS + env are runtime/config (no rebuild); routes, menus, theme, logo and languages are build-baked config; shell, SEO and dispatch are shared Rust touched only for bugs or new framework capabilities. Brand and content changes must not touch crates/server/src. The seam is documented in .claude/CLAUDE.md and is the transition condition for the consumer-neutrality FSM dimension.",
artifact_paths = ["site/config/", "crates/pages_htmx/templates/", ".claude/CLAUDE.md"],
},
],
edges = [
# Axioms → Practices via ManifestsIn
{ from = "config-driven-content", to = "config-driven-routes", kind = 'ManifestsIn, weight = 'High },
{ from = "config-driven-content", to = "htmx-rendering-profile", kind = 'ManifestsIn, weight = 'Medium },
{ from = "bilingual-parity", to = "bilingual-content-system", kind = 'ManifestsIn, weight = 'High },
{ from = "consume-not-fork", to = "layered-customization", kind = 'ManifestsIn, weight = 'High },
# Tensions → resolving Practices
{ from = "htmx-vs-hydration", to = "htmx-rendering-profile", kind = 'Resolves, weight = 'High,
note = "Synthesis: the site defaults to HTMX SSR but the profile is a per-route config choice, so hydration stays available without forking the framework." },
{ from = "content-velocity-vs-parity", to = "bilingual-content-system", kind = 'Resolves, weight = 'High,
note = "ES completion is tracked as a publication blocker, preserving the parity invariant while allowing EN to draft ahead." },
# Inter-practice dependencies
{ from = "htmx-rendering-profile", to = "config-driven-routes", kind = 'DependsOn, weight = 'High },
{ from = "bilingual-content-system", to = "config-driven-routes", kind = 'DependsOn, weight = 'Medium },
{ from = "layered-customization", to = "htmx-rendering-profile", kind = 'Complements, weight = 'Medium },
# Framework-vs-one-site tension → the seam that holds it
{ from = "framework-vs-one-site", to = "customization-seam", kind = 'Resolves, weight = 'High,
note = "The seam holds the tension: per-consumer customization on the runtime side, identity and routes build-baked, neither pole collapsed." },
{ from = "consume-not-fork", to = "customization-seam", kind = 'Complements, weight = 'Medium,
note = "Customizing at the right layer is how a consumer avoids forking the framework." },
],
}