634 lines
48 KiB
Text
634 lines
48 KiB
Text
|
|
let d = import "ontology/defaults/core.ncl" in
|
||
|
|
|
||
|
|
{
|
||
|
|
nodes = [
|
||
|
|
|
||
|
|
# ── Axioms (invariant = true) ─────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "config-driven-always",
|
||
|
|
name = "Config-Driven Always",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Axiom,
|
||
|
|
description = "Never hardcoded, always configuration. Runtime args > env > user config > infra config > defaults. TOML/YAML are output formats, never source of truth.",
|
||
|
|
invariant = true,
|
||
|
|
artifact_paths = ["config/", "schemas/config/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "type-safety-nickel",
|
||
|
|
name = "Type Safety via Nickel",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Axiom,
|
||
|
|
description = "Nickel schemas for all infrastructure configuration. TOML/YAML are generated output, never source of truth. Every config change is validated at the schema level before reaching runtime.",
|
||
|
|
invariant = true,
|
||
|
|
artifact_paths = ["schemas/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "solid-boundaries",
|
||
|
|
name = "SOLID Architecture Boundaries",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Axiom,
|
||
|
|
description = "6 hard boundaries with 6 layers of enforcement. Orchestrator ONLY does provider APIs + SSH. Auth decisions ONLY in Control Center. Secrets ONLY via Vault. Violations caught at compile-time, dev-time, pre-commit, CI, runtime, and audit.",
|
||
|
|
invariant = true,
|
||
|
|
artifact_paths = ["platform/crates/orchestrator/", "platform/crates/control-center/", "platform/secretumvault/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "provider-abstraction",
|
||
|
|
name = "Provider Abstraction",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Axiom,
|
||
|
|
description = "Providers as pluggable interface of 25 functions. New provider = implement interface, not change platform. Provider implementations are isolated in catalog/providers/.",
|
||
|
|
invariant = true,
|
||
|
|
artifact_paths = ["catalog/providers/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Tensions ──────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "nickel-complexity-vs-accessibility",
|
||
|
|
name = "Nickel Complexity vs Accessibility",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "Richer Nickel schemas provide better validation and safety but increase the barrier of entry for contributors. The balance: compose pattern over merge operator, progressive schema layers.",
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "monorepo-vs-split",
|
||
|
|
name = "Monorepo vs Split Repos",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "Everything in one repo simplifies development and cross-cutting changes but complicates CI, ownership boundaries, and workspace isolation.",
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "centralized-vs-scripted",
|
||
|
|
name = "Centralized Orchestration vs Scripts",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "Orchestrator provides audit trail, rollback, and state machine but adds complexity vs direct provider scripts. Solo mode mitigates by keeping full architecture with relaxed auth.",
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "extension-graph-vs-declarative-config",
|
||
|
|
name = "Extension Ontology vs Pure Config",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "The planned extension to embed ontology nodes in metadata.ncl makes extensions graph-aware but blurs the line between configuration and code. Simple extensions should remain pure config; only extensions with non-trivial platform relationships should declare ontology nodes. The resolution: ontology fields in metadata.ncl are optional and additive — the base metadata contract stays simple.",
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "nushell-vs-rust-boundary",
|
||
|
|
name = "Nushell Library Boundary vs Rust Platform",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "The Nushell CLI library (core/nulib/) grows to cover operations that could be in Rust platform crates. Moving logic to Rust improves type safety and testability; keeping it in Nushell keeps it operator-scriptable and writable without recompilation. Partial resolution via smart-interface-unification (ADR-029): Rust owns the Registry and all Tool dispatch semantics; Nushell owns orchestration sequences, the three-tier fallback probe chain, and the required legacy closure per operation. The boundary is now structural — operations cross it via tool-call, not via direct Nushell reimplementation.",
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Practices ─────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "nushell-cli-library",
|
||
|
|
name = "Nushell CLI Library",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "The primary user-facing command surface — 60+ Nushell modules in core/nulib/ covering all platform operations: server/cluster/taskserv/workspace lifecycle, batch ops, provider discovery, extension loading, orchestrator integration, AI tooling, secrets, observability. The CLI composes nulib modules; the library consumes platform APIs and extension metadata.",
|
||
|
|
artifact_paths = ["core/nulib/", "core/nulib/main_provisioning/", "core/nulib/lib_provisioning/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "extension-metadata-contract",
|
||
|
|
name = "Extension Metadata Contract",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Every extension (taskserv, provider, cluster) declares a metadata.ncl: name, version, category, description, typed dependencies, tags, and best_practices references. This contract is the machine-readable extension interface consumed by the CLI loader, schema validator, and orchestrator dependency resolver. Future: metadata.ncl will also declare ontology nodes, connecting extension capabilities to the platform DAG.",
|
||
|
|
artifact_paths = ["catalog/taskservs/*/metadata.ncl", "catalog/providers/*/", "catalog/clusters/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "taskserv-dependency-dag",
|
||
|
|
name = "Taskserv Dependency DAG",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Taskservs form a typed dependency DAG via their metadata.ncl `dependencies` field. Execution order: infrastructure → networking/storage → container_runtime → kubernetes/databases/development → applications. The orchestrator resolves this DAG before building the formula execution plan — no taskserv executes before its declared dependencies complete.",
|
||
|
|
artifact_paths = ["catalog/taskservs/", "platform/crates/orchestrator/src/formula.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "taskserv-pattern",
|
||
|
|
name = "TaskServ Pattern",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Extensions that serve infrastructure tasks — each taskserv declares dependencies, capabilities, and contracts. They consume workspace config and produce infrastructure operations. 10 built-in taskservs: infrastructure, networking, storage, container_runtime, kubernetes, databases, applications, development, cluster, misc.",
|
||
|
|
artifact_paths = ["catalog/taskservs/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "workspace-layer-resolution",
|
||
|
|
name = "Workspace Layer Resolution",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Three-tier priority system for workspace configuration composition: Core Layer (Priority 100, catalog/) → Workspace Templates (Priority 200, workspace/templates/) → Infrastructure-Specific (Priority 300, workspace/infra/{name}/). Higher priority overrides lower. Enables shared patterns to be extracted to templates while infrastructure-specific concerns stay local.",
|
||
|
|
artifact_paths = ["workspace/layers/", "workspace/templates/", "workspace/registry/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "workspace-as-implementation-plan",
|
||
|
|
name = "Workspace as Implementation Plan",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "A workspace IS the implementation plan. It declares the complete typed composition: which providers, which taskservs with their dependencies, which formula DAG nodes, which clusters. The platform validates the composition against schemas and resolves the execution plan from it — no imperative scripting. Workspaces make infrastructure intent explicit and auditable.",
|
||
|
|
artifact_paths = ["workspace/templates/", "examples/workspaces/", "schemas/config/workspace_config/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "workspace-contract",
|
||
|
|
name = "Workspace Contract",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Workspaces are typed compositions of ingredients in DAGs. Each workspace declares what it needs via Nickel schemas; the platform validates and provisions accordingly.",
|
||
|
|
artifact_paths = ["schemas/config/workspace_config/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "platform-dispatch",
|
||
|
|
name = "Platform Dispatch Model",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Orchestrator dispatches tasks via NATS work queues, maintains state machine per task, and provides rollback capability. All provider API calls flow through this single dispatch point.",
|
||
|
|
artifact_paths = ["platform/crates/orchestrator/src/workflow.rs", "platform/crates/orchestrator/src/batch.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "config-hierarchy",
|
||
|
|
name = "Configuration Hierarchy",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "5-level config precedence: Runtime args > env vars > user config > infra config > system defaults. Implemented via Nickel schema composition and Nushell config loading.",
|
||
|
|
artifact_paths = ["schemas/config/", "config/config.defaults.toml"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "formula-dag-execution",
|
||
|
|
name = "Formula DAG Execution",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Workspace taskserv execution modeled as typed DAGs. Formulas replace positional arrays: each node declares depends_on, on_error, parallel, max_retries. Orchestrator converts via formula.rs into DependencyGraph for topological parallel execution.",
|
||
|
|
artifact_paths = ["schemas/lib/formula.ncl", "platform/crates/orchestrator/src/formula.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "solo-mode",
|
||
|
|
name = "Solo Mode Architecture",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Full platform architecture with relaxed auth — solo_auth_middleware replaces JWT+Cedar, injecting a fixed admin session gated behind --mode solo runtime flag. Same binaries, same NATS subjects, same SurrealDB schema. SurrealDB uses embedded RocksDB, NATS runs as child process. Enables single-operator deployments and CI integration tests without external infrastructure.",
|
||
|
|
artifact_paths = ["platform/crates/control-center/src/middleware/", "platform/crates/orchestrator/src/nats.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Smart Interface Unification (ADR-029) ────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "provisioning-registry",
|
||
|
|
name = "Provisioning Core Registry",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "provisioning-core::Registry is the shared dispatch table for all 37 Tool implementations. Every surface (CLI binary, HTTP daemon, MCP server) must invoke operations through Registry::invoke — no surface may bypass it with direct tool instantiation. This structural constraint is the mechanism that makes semantic parity across surfaces a compile-time guarantee rather than a convention.",
|
||
|
|
artifact_paths = ["platform/crates/provisioning-core/src/", "platform/crates/provisioning-core/src/registry.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "smart-interface-unification",
|
||
|
|
name = "Smart Interface Unification",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Three-tier fallback chain in Nushell (platform/clients/fallback.nu::tool-call): tier 1 probes the HTTP daemon, tier 2 spawns provisioning-tool as a child process, tier 3 executes the caller-supplied Nushell legacy closure. The probe sequence runs at call time; no daemon state is managed by the operator. All three tiers dispatch through provisioning-core::Registry. Tier-3 closures are the offline-first guarantee and can only be retired per-operation after G3 passes for that operation.",
|
||
|
|
artifact_paths = [
|
||
|
|
"core/nulib/platform/clients/fallback.nu",
|
||
|
|
"core/nulib/platform/clients/daemon.nu",
|
||
|
|
"core/nulib/platform/clients/provisioning_tool.nu",
|
||
|
|
"platform/crates/provisioning-tool/",
|
||
|
|
"platform/crates/provisioning-daemon/",
|
||
|
|
"platform/scripts/start-provisioning-daemon.nu",
|
||
|
|
"justfiles/daemon.just",
|
||
|
|
],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "g3-contract-invariant",
|
||
|
|
name = "G3 Contract Invariant",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "The contract-tests crate asserts that CLI, HTTP daemon, and MCP server produce semantically equivalent payloads and identical error codes for every fixture tool. Five tests must pass: listing agreement, echo agreement, invalid-param error agreement, failing-tool error agreement, tools/list count agreement. normalise() defines what 'equivalent' means by stripping volatile fields. G3 is the CI mechanism that converts sync-irrenunciable into an architectural invariant.",
|
||
|
|
artifact_paths = ["platform/crates/contract-tests/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Reflection modes ──────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "mode-provisioning-assess",
|
||
|
|
name = "Mode: provisioning-assess",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "On+re reflection mode: assess system readiness across orchestrator, CLI, control center, schema coverage, and KCL migration dimensions.",
|
||
|
|
artifact_paths = ["reflection/modes/provisioning-assess.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "mode-provisioning-audit",
|
||
|
|
name = "Mode: provisioning-audit",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "On+re reflection mode: validate config, list and verify taskservs, check Nickel contracts, verify provider integrity, check axiom coherence.",
|
||
|
|
artifact_paths = ["reflection/modes/provisioning-audit.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "mode-provisioning-coverage",
|
||
|
|
name = "Mode: provisioning-coverage",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "On+re reflection mode: map extension ecosystem maturity — taskservs, providers, clusters — and identify coverage gaps.",
|
||
|
|
artifact_paths = ["reflection/modes/provisioning-coverage.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "mode-provisioning-validate-formula",
|
||
|
|
name = "Mode: provisioning-validate-formula",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "On+re reflection mode: cross-validate a workspace Formula DAG — typecheck, taskserv existence, metadata dep coverage, ConflictsWith detection, DAG acyclicity.",
|
||
|
|
artifact_paths = ["reflection/modes/provisioning-validate-formula.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "typedialog-web-ui",
|
||
|
|
name = "TypeDialog Schema-Driven Web UI",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Nickel schemas are the single source of truth for web form generation. TypeDialog reads contracts from `.typedialog/provisioning/schemas/` and generates validated HTML forms with no manual form code. Supports multi-user collaborative workflows (draft → review → approve), embedded in the control center dashboard. TUI is a fallback for SSH-only environments. Primary value: schema drift between form and config is structurally impossible.",
|
||
|
|
artifact_paths = [".typedialog/provisioning/", ".typedialog/provisioning/schemas/", ".typedialog/provisioning/form.toml"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ai-rag-surface",
|
||
|
|
name = "Schema-Aware AI and RAG Surface",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "AI config generation constrained by Nickel schemas — cannot produce invalid configs. RAG indexes Nickel schemas, docs, and past deployments as retrieval context. ai-service is the HTTP entry point (Cedar-gated); mcp-server exposes tool calling; rag crate manages vector store and embeddings. Cedar policy forbids AI from accessing secrets and requires human approval before any deployment.",
|
||
|
|
artifact_paths = ["platform/crates/ai-service/", "platform/crates/mcp-server/", "platform/crates/rag/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── DAG architecture (three-layer) ───────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "extension-capability-dag",
|
||
|
|
name = "Extension Capability DAG",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Extensions declare typed capability provides/requires/conflicts_with in metadata.ncl. Resolution maps required capabilities to concrete extensions before formula execution. Enables conflict detection and dependency ordering without hardcoded extension names.",
|
||
|
|
artifact_paths = ["catalog/taskservs/*/metadata.ncl", "schemas/lib/dag/contracts.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "workspace-composition-dag",
|
||
|
|
name = "Workspace Composition DAG",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Workspaces declare inter-formula dependencies as a typed DAG via dag.ncl. Formula execution ordering and health gates between formula groups are schema-validated by the WorkspaceComposition Nickel contract and enforced at runtime by WorkspaceComposition::into_workflow in formula.rs.",
|
||
|
|
artifact_paths = ["schemas/lib/dag/contracts.ncl", "platform/crates/orchestrator/src/formula.rs"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "capability-resolution",
|
||
|
|
name = "Capability Resolution",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "ResolutionPolicy maps required capabilities (from extension requires fields) to concrete extensions (from extension provides fields). Strict mode: all Required capabilities must resolve before execution proceeds. Optional gaps allowed only when resolution.allow_optional_gaps = true.",
|
||
|
|
artifact_paths = ["schemas/lib/dag/contracts.ncl", "schemas/config/dag/main.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "capability-granularity-vs-simplicity",
|
||
|
|
name = "Capability Granularity vs Simplicity",
|
||
|
|
pole = 'Spiral,
|
||
|
|
level = 'Tension,
|
||
|
|
description = "Fine-grained capabilities enable precise conflict detection but increase taxonomy maintenance burden. Initial set of 9 coarse capabilities (server-lifecycle, networking, storage, container-runtime, orchestration, database, application-deployment, dev-tooling, hypervisor) chosen to defer premature optimization.",
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "mode-provisioning-dag-integrity",
|
||
|
|
name = "Mode: provisioning-dag-integrity",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "On+re reflection mode: audit all taskservs for provides/requires/conflicts_with completeness, validate capability graph has no unresolved Required deps, check that ConflictsWith pairs don't coexist in any formula. Runs after B3 migration.",
|
||
|
|
artifact_paths = ["reflection/modes/provisioning-dag-integrity.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── CI pipelines ──────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ci-github-actions",
|
||
|
|
name = "CI: GitHub Actions",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "GitHub Actions pipelines: nushell-lint, nickel-typecheck, rust-ci. Enforce code quality and schema validity on every PR.",
|
||
|
|
artifact_paths = [".github/workflows/nushell-lint.yml", ".github/workflows/nickel-typecheck.yml", ".github/workflows/rust-ci.yml"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ci-woodpecker",
|
||
|
|
name = "CI: Woodpecker",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Woodpecker CI pipelines: ci.yml and ci-advanced.yml. Self-hosted CI for provisioning workloads.",
|
||
|
|
artifact_paths = [".woodpecker/ci.yml", ".woodpecker/ci-advanced.yml"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Examples ──────────────────────────────────────────────────────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "scenario-workspaces",
|
||
|
|
name = "Scenario: Workspaces",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Example workspace configurations demonstrating the workspace contract pattern, server composition, and formula DAGs.",
|
||
|
|
artifact_paths = ["examples/workspaces"],
|
||
|
|
},
|
||
|
|
|
||
|
|
# ── Platform service nodes (previously only in artifact_paths) ────────────
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "vault-service",
|
||
|
|
name = "Vault Service — Multi-Backend Secrets",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "SOLID boundary for all secret storage and retrieval. Three backends: Age (development, filesystem key), Cosmian KMS (production, HSM-backed), RustyVault (self-hosted). Provider credentials are leased, never stored raw. Secrets never transit NATS — only lease_id references are published to the event bus. Cedar policies in the control-center prevent any other service from calling vault endpoints directly.",
|
||
|
|
artifact_paths = ["platform/crates/vault-service/", "platform/crates/vault-service/src/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "extension-registry-service",
|
||
|
|
name = "Extension Registry — OCI Distribution Surface",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "OCI-compliant registry proxy that distributes provisioning extensions (taskservs, providers, cluster definitions) as OCI artifacts. Validates digests, caches manifests via LRU, emits extension lifecycle events to NATS. Enables versioned extension delivery without requiring a full OCI registry deployment — the proxy layer handles authentication and manifest caching transparently.",
|
||
|
|
artifact_paths = ["platform/crates/extension-registry/", "platform/crates/extension-registry/src/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ncl-sync-daemon",
|
||
|
|
name = "NCL Sync Daemon — Config Compilation Pipeline",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Daemon that watches Nickel (NCL) source files, compiles them to JSON on change, and keeps the config cache fresh for Nushell processes that cannot afford `nickel export` startup cost on every invocation. File-watching via the `notify` crate; optional NATS publication for cross-process cache invalidation. Resolves the tension between type-safe NCL source of truth and low-latency config reads at CLI time.",
|
||
|
|
artifact_paths = ["platform/crates/ncl-sync/", "platform/crates/ncl-sync/src/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "prvng-cli-daemon",
|
||
|
|
name = "prvng-cli — Zero-Cost CLI Query Daemon",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Unix-socket daemon (`~/.local/share/provisioning/cli.sock`) that answers command registry lookups with sub-millisecond latency. Eliminates the Nushell startup + `nickel export` cost from the bash wrapper's `_validate_command` path. File-watches `commands-registry.json` via the `notify` crate; auto-shuts after 60s idle; restarted by the bash wrapper on next invocation. The wrapper falls through to grep+JSON cache if the socket is absent.",
|
||
|
|
artifact_paths = ["platform/crates/prvng-cli/", "platform/crates/prvng-cli/src/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "platform-shared-infra",
|
||
|
|
name = "Platform Shared Infrastructure Crates",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Three workspace libraries consumed by all platform services: `platform-config` (centralized config loading with Nickel merge layer), `platform-nats` (shared NATS JetStream bridge with retry loop, explicit ACK, and subject prefixing under `provisioning.>`), and `platform-db` (SurrealDB connection pool supporting embedded RocksDB for solo mode, SurrealKV for dev, and WebSocket for multi-user production). These three crates are the only infrastructure primitives that cross service boundaries in the Rust layer.",
|
||
|
|
artifact_paths = ["platform/crates/platform-config/", "platform/crates/platform-nats/", "platform/crates/platform-db/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "platform-crate-naming",
|
||
|
|
name = "Platform Workspace Crate Naming Convention",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Four-rule naming convention applied to all workspace crates (ADR-030): (1) shared library crates use `platform-<name>` package name; (2) smart interface layer uses `provisioning-<name>`; (3) service binary package names are short — `provisioning-` prefix lives only in `[[bin]] name`; (4) ecosystem crates with many existing `use` callers use `platform-<name>` package name + `[lib] name = 'old_name'` to preserve Rust crate names without modifying call sites. The convention disambiguates workspace dep declarations and eliminates crates.io name collision risk for generic names.",
|
||
|
|
artifact_paths = ["platform/Cargo.toml", "platform/crates/", "platform/prov-ecosystem/crates/"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ops-contract-dual-mode",
|
||
|
|
name = "Ops Contract Dual-Mode — Pending Queue + Switchable Signer",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "NATS JetStream-based ops coordination with three subject namespaces (ops.pending, ops.cmd, ops.audit) and JWT-signed commands carrying scoped permissions, monotonic sequence, idempotency key, and expected_state_version. Signers (keeper-daemon for auto, keeper-cli for manual) are interchangeable subscribers to ops.pending — switching modes is operational (start/stop daemon), not architectural. ops-controller is the single per-workspace WorkQueue consumer of ops.cmd, persisting in-flight ops to SurrealDB before ack to survive restart. Multi-emitter coordination is delegated to JetStream stream order with optimistic concurrency on expected_state_version. ADR-037.",
|
||
|
|
artifact_paths = ["adrs/adr-037-ops-contract-dual-mode.ncl", "platform/crates/ops-keeper/", "platform/crates/ops-controller/", "schemas/lib/ops_contract.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "decentralized-governance-radicle",
|
||
|
|
name = "Decentralized Governance — Radicle Heartwood Substrate",
|
||
|
|
pole = 'Yin,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Radicle Heartwood as the substrate for governance, desired-state, and audit ledger across all workspaces. Three repos per workspace: policy-<workspace> (M-of-N delegation among operators), <workspace>-desired (M-of-N + CI keys), <workspace>-state (single delegate: ops-controller key). Keeper policy is declarative-only Nickel — parsed by Rust matcher, never evaluated as code. Audit mirror sidecar bridges NATS ops.audit subjects to <workspace>-state commits with jti idempotency. Operators may use any frontend (git, jj) over their local Radicle replica. Domain-level commands (governance delegations, governance signers) read local clones uniformly. ADR-038.",
|
||
|
|
artifact_paths = ["adrs/adr-038-radicle-decentralized-governance.ncl", "platform/crates/audit-mirror/", "schemas/lib/radicle.ncl", "schemas/lib/keeper_policy.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "ephemeral-build-infrastructure",
|
||
|
|
name = "Ephemeral Build Infrastructure — Golden Runners + S3-Backed zot",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Build pipeline using orchestrator-spawned ephemeral VMs from periodically-rebuilt golden images (buildkit-runner-golden). Per-build sizing resolved from .build-spec.ncl declaration, p95 historical, or language defaults. zot relocated from libre-daoshi to libre-wuji with S3-compatible backend (versioning + cross-region replication for DR). Multi-tenant zot layout serves /images, /cache, /sccache, /crates with JWT-scoped auth. BuildKit registry-cache and sccache S3-backend both terminate at zot for warm-cache cold-spawn. buildkit-launcher (Woodpecker plugin) is a thin bridge to orchestrator's VM lease semantics. ADR-039.",
|
||
|
|
artifact_paths = ["adrs/adr-039-build-infrastructure-ephemeral.ncl", "catalog/components/buildkit_runner.ncl", "platform/crates/buildkit-launcher/", "schemas/lib/build_spec.ncl"],
|
||
|
|
},
|
||
|
|
|
||
|
|
d.make_node {
|
||
|
|
id = "federated-integration-modes",
|
||
|
|
name = "Federated Integration Modes — OCI Domain Artifacts",
|
||
|
|
pole = 'Yang,
|
||
|
|
level = 'Practice,
|
||
|
|
description = "Cross-project integration via typed OCI domain artifacts. Each project declares an IntegrationMode in its own reflection/modes/ with domains_used pointing to versioned artifacts at reg.librecloud.online/domains/<id>:<semver>. Provisioning is a peer participant — not a plugin host; no filesystem coupling exists between participants. Cabling files at infra/<ws>/integrations/<mode-id>.ncl resolve domain context fields to concrete sources (sops, component, literal, env). Domains are OCI artifacts with mediaType application/vnd.ontoref.domain.v1 containing a typed Nickel contract. Initial catalog: secret-delivery 0.1.0, event-emission 0.1.0, result-reporting 0.1.0. prvng integration domain publish/pull/describe/verify manage the OCI surface. ADR-042.",
|
||
|
|
artifact_paths = ["adrs/adr-042-ecosystem-integration-modes.ncl", "schemas/lib/integration/", "catalog/domains/", "core/nulib/platform/oci/domain_client.nu", "core/nulib/cli/integration.nu"],
|
||
|
|
},
|
||
|
|
|
||
|
|
],
|
||
|
|
|
||
|
|
edges = [
|
||
|
|
|
||
|
|
# Axiom manifestations
|
||
|
|
{ from = "config-driven-always", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Nickel is the manifestation of config-driven — typed schemas enforce the axiom" },
|
||
|
|
{ from = "config-driven-always", to = "config-hierarchy", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "The 5-level hierarchy implements the config-driven axiom at runtime" },
|
||
|
|
{ from = "workspace-contract", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Workspaces are the user-facing manifestation of config-driven infrastructure" },
|
||
|
|
|
||
|
|
# Tension relationships
|
||
|
|
{ from = "type-safety-nickel", to = "nickel-complexity-vs-accessibility", kind = 'Contradicts, weight = 'Medium,
|
||
|
|
note = "The axiom generates the tension — stricter types vs contributor friction" },
|
||
|
|
{ from = "platform-dispatch", to = "centralized-vs-scripted", kind = 'Resolves, weight = 'High,
|
||
|
|
note = "The dispatch model is the resolution of centralized vs scripted" },
|
||
|
|
|
||
|
|
# Structural relationships
|
||
|
|
{ from = "solid-boundaries", to = "platform-dispatch", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "SOLID boundaries are validated by the dispatch model — only orchestrator touches providers" },
|
||
|
|
{ from = "solid-boundaries", to = "provider-abstraction", kind = 'Contains, weight = 'High,
|
||
|
|
note = "Provider abstraction is a corollary of SOLID boundaries" },
|
||
|
|
{ from = "solo-mode", to = "solid-boundaries", kind = 'ValidatedBy, weight = 'Medium,
|
||
|
|
note = "Solo mode preserves SOLID boundaries — auth bypass is the only runtime difference, not an architectural bypass" },
|
||
|
|
{ from = "solo-mode", to = "centralized-vs-scripted", kind = 'Resolves, weight = 'Medium,
|
||
|
|
note = "Solo mode proves single-operator usefulness without sacrificing the centralized architecture" },
|
||
|
|
{ from = "provider-abstraction", to = "taskserv-pattern", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "Providers and taskservs are complementary extension points" },
|
||
|
|
{ from = "taskserv-pattern", to = "workspace-contract", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "Taskservs consume what workspaces declare — the data flow direction" },
|
||
|
|
|
||
|
|
# TypeDialog and AI relationships
|
||
|
|
{ from = "typedialog-web-ui", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "TypeDialog is the user-facing manifestation of type-safety-nickel — schema contracts drive form generation" },
|
||
|
|
{ from = "typedialog-web-ui", to = "workspace-as-implementation-plan", kind = 'Complements, weight = 'High,
|
||
|
|
note = "TypeDialog is the authoring UI for workspace implementation plans" },
|
||
|
|
{ from = "ai-rag-surface", to = "type-safety-nickel", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "Nickel schemas constrain AI generation — AI cannot produce configs that fail schema validation" },
|
||
|
|
{ from = "ai-rag-surface", to = "solid-boundaries", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "Cedar policies enforce AI cannot read secrets or deploy without human approval" },
|
||
|
|
{ from = "ai-rag-surface", to = "typedialog-web-ui", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "AI fills TypeDialog forms; TypeDialog provides the schema context AI is constrained by" },
|
||
|
|
|
||
|
|
# Extension ecosystem relationships
|
||
|
|
{ from = "extension-metadata-contract", to = "taskserv-pattern", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "Metadata contracts formalize the taskserv interface — every taskserv must satisfy the metadata contract to be loaded" },
|
||
|
|
{ from = "extension-metadata-contract", to = "provider-abstraction", kind = 'ValidatedBy, weight = 'Medium,
|
||
|
|
note = "Provider extensions also declare metadata — the contract applies across all extension categories" },
|
||
|
|
{ from = "taskserv-dependency-dag", to = "taskserv-pattern", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "The dependency DAG is the runtime expression of taskserv relationship declarations" },
|
||
|
|
{ from = "taskserv-dependency-dag", to = "formula-dag-execution", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "Taskserv dependency graph feeds into the formula DAG execution plan — dependency resolution precedes formula construction" },
|
||
|
|
{ from = "extension-metadata-contract", to = "extension-graph-vs-declarative-config", kind = 'Contradicts, weight = 'Medium,
|
||
|
|
note = "Adding ontology fields to metadata.ncl is the planned evolution that generates this tension" },
|
||
|
|
|
||
|
|
# DAG architecture relationships
|
||
|
|
{ from = "extension-capability-dag", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Capability declarations are Nickel contracts — provides/requires/conflicts_with are schema-validated" },
|
||
|
|
{ from = "extension-capability-dag", to = "extension-metadata-contract", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Capability fields extend the extension-metadata-contract; backwards-compatible via defaults" },
|
||
|
|
{ from = "capability-resolution", to = "formula-dag-execution", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "Capability resolution produces the extension binding map consumed by formula execution" },
|
||
|
|
{ from = "workspace-composition-dag", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "dag.ncl is a typed Nickel declaration — no imperative composition scripts" },
|
||
|
|
{ from = "workspace-composition-dag", to = "formula-dag-execution", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "Composition DAG drives the inter-formula execution order and health gate injection" },
|
||
|
|
{ from = "extension-capability-dag", to = "capability-granularity-vs-simplicity", kind = 'ManifestsIn, weight = 'Medium,
|
||
|
|
note = "The capability taxonomy is the direct expression of this tension" },
|
||
|
|
{ from = "mode-provisioning-dag-integrity", to = "extension-capability-dag", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "Integrity mode is the verification pass for the capability DAG" },
|
||
|
|
|
||
|
|
# Workspace relationships
|
||
|
|
{ from = "workspace-as-implementation-plan", to = "workspace-contract", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Workspace-as-plan is the semantic interpretation; workspace contract is the schema mechanism that enforces it" },
|
||
|
|
{ from = "workspace-layer-resolution", to = "workspace-contract", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Layer resolution is the composition mechanism that realizes the workspace contract from template fragments" },
|
||
|
|
{ from = "workspace-as-implementation-plan", to = "formula-dag-execution", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "The workspace composition is the input from which the formula DAG execution plan is derived" },
|
||
|
|
{ from = "workspace-layer-resolution", to = "config-driven-always", kind = 'ManifestsIn, weight = 'Medium,
|
||
|
|
note = "Layer resolution is config-driven-always applied to workspace composition — no imperative override paths" },
|
||
|
|
|
||
|
|
# Nushell CLI library relationships
|
||
|
|
{ from = "nushell-cli-library", to = "workspace-as-implementation-plan", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "CLI library is the primary authoring surface for workspace definitions — workspace commands drive the full lifecycle" },
|
||
|
|
{ from = "nushell-cli-library", to = "centralized-vs-scripted", kind = 'Contradicts, weight = 'Medium,
|
||
|
|
note = "The CLI library's size generates the nushell-vs-rust-boundary tension — it could absorb orchestrator logic" },
|
||
|
|
{ from = "nushell-cli-library", to = "nushell-vs-rust-boundary", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "The library's growth is the direct expression of this tension" },
|
||
|
|
|
||
|
|
# Smart Interface Unification relationships (ADR-029)
|
||
|
|
{ from = "provisioning-registry", to = "solid-boundaries", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Registry::invoke is the single dispatch path — surfaces cannot bypass it; enforced by ADR-029 constraint registry-sole-dispatch-path" },
|
||
|
|
{ from = "provisioning-registry", to = "nushell-vs-rust-boundary", kind = 'Resolves, weight = 'High,
|
||
|
|
note = "Registry is the structural boundary: Rust owns dispatch semantics, Nushell owns orchestration sequences and the legacy closure" },
|
||
|
|
{ from = "smart-interface-unification", to = "nushell-cli-library", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "tool-call / tool-list in nulib/platform/clients/fallback.nu is the Nushell expression of the three-tier architecture" },
|
||
|
|
{ from = "smart-interface-unification", to = "provisioning-registry", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "All three tiers ultimately converge on Registry::invoke — the fallback chain selects the transport, not the dispatch logic" },
|
||
|
|
{ from = "smart-interface-unification", to = "ai-rag-surface", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "mcp-server is now Registry-backed — MCP tool calling and AI tool calling use the same dispatch path" },
|
||
|
|
{ from = "g3-contract-invariant", to = "provisioning-registry", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "G3 is the runtime evidence that Registry dispatch produces identical semantics across all three surfaces" },
|
||
|
|
{ from = "g3-contract-invariant", to = "smart-interface-unification", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "G3 is the CI gate that prevents surface drift — the invariant that makes sync-irrenunciable structural" },
|
||
|
|
{ from = "smart-interface-unification", to = "config-driven-always", kind = 'ManifestsIn, weight = 'Medium,
|
||
|
|
note = "Tier selection is driven by environment probes at call time, not by hardcoded configuration — the fallback chain is config-driven" },
|
||
|
|
|
||
|
|
# Platform service node relationships
|
||
|
|
{ from = "vault-service", to = "solid-boundaries", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Vault is the concrete implementation of the secrets SOLID boundary — Cedar policies enforce no other service touches vault endpoints" },
|
||
|
|
{ from = "vault-service", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Backend selection (Age/Cosmian/RustyVault) is purely configuration — same binary, different backend at runtime" },
|
||
|
|
|
||
|
|
{ from = "extension-registry-service", to = "provider-abstraction", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "Extension registry distributes the versioned artifacts that implement provider and taskserv extensions" },
|
||
|
|
{ from = "extension-registry-service", to = "extension-metadata-contract", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "Registry validates OCI artifact digests; the metadata.ncl contract inside each artifact is validated by the CLI loader on install" },
|
||
|
|
|
||
|
|
{ from = "ncl-sync-daemon", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "NCL sync makes NCL the live source of truth — JSON cache is always derived from NCL, never edited directly" },
|
||
|
|
{ from = "ncl-sync-daemon", to = "config-hierarchy", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "The JSON cache NCL sync maintains is the artifact consumed by the 5-level config hierarchy at runtime" },
|
||
|
|
|
||
|
|
{ from = "prvng-cli-daemon", to = "nushell-cli-library", kind = 'Complements, weight = 'High,
|
||
|
|
note = "prvng-cli eliminates the startup cost that made fast bash-wrapper validation infeasible — the CLI library benefits from sub-ms command lookup" },
|
||
|
|
{ from = "prvng-cli-daemon", to = "nushell-vs-rust-boundary", kind = 'Resolves, weight = 'Medium,
|
||
|
|
note = "Unix-socket daemon shifts command validation entirely to Rust, removing the Nu + nickel export cost from the hot path" },
|
||
|
|
|
||
|
|
{ from = "platform-shared-infra", to = "solid-boundaries", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "platform-config/nats/db are the only infra primitives crossing service boundaries — they are the shared substrate SOLID boundaries operate over" },
|
||
|
|
{ from = "platform-shared-infra", to = "solo-mode", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Solo mode is implemented entirely in platform-db (embedded RocksDB) and platform-nats (child process) — no solo-specific code in the business services" },
|
||
|
|
|
||
|
|
{ from = "platform-crate-naming", to = "monorepo-vs-split", kind = 'Resolves, weight = 'Medium,
|
||
|
|
note = "The naming convention makes the monorepo workspace unambiguous — dep declarations carry project affiliation without splitting into separate repos" },
|
||
|
|
{ from = "platform-crate-naming", to = "config-driven-always", kind = 'Complements, weight = 'Low,
|
||
|
|
note = "Consistent naming is a prerequisite for config-driven extension loading — ambiguous package names would make programmatic workspace dep discovery fragile" },
|
||
|
|
|
||
|
|
# Ops contract dual-mode relationships (ADR-037)
|
||
|
|
{ from = "ops-contract-dual-mode", to = "solid-boundaries", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "ops-controller is a new SOLID boundary — consumes from ops.cmd, applies via orchestrator API, writes to ops.audit and SurrealDB; never calls provider APIs or auth services directly" },
|
||
|
|
{ from = "ops-contract-dual-mode", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Mode switch (auto/manual/hybrid) is operational state, not configuration — keeper policy is declarative Nickel, ops contract is JWT scopes; no hardcoded mode constants" },
|
||
|
|
{ from = "ops-contract-dual-mode", to = "centralized-vs-scripted", kind = 'Resolves, weight = 'High,
|
||
|
|
note = "Pending queue + replaceable signer means automated CI and manual operator paths converge on the same contract — no fork between centralized and scripted" },
|
||
|
|
{ from = "ops-contract-dual-mode", to = "platform-shared-infra", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "ops contract uses platform-nats for JetStream and platform-db for SurrealDB persistence — built on the same shared substrate as orchestrator" },
|
||
|
|
|
||
|
|
# Decentralized governance relationships (ADR-038)
|
||
|
|
{ from = "decentralized-governance-radicle", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Radicle repos (policy/desired/state) are versioned configuration with cryptographic provenance — config-driven extends to governance itself" },
|
||
|
|
{ from = "decentralized-governance-radicle", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "keeper policy uses declarative-only Nickel schema — type-safe data, never executed as code" },
|
||
|
|
{ from = "decentralized-governance-radicle", to = "monorepo-vs-split", kind = 'Resolves, weight = 'Medium,
|
||
|
|
note = "Three-repo split per workspace separates governance/desired/audit by authority profile without compromising the source-code monorepo" },
|
||
|
|
{ from = "decentralized-governance-radicle", to = "ops-contract-dual-mode", kind = 'Complements, weight = 'High,
|
||
|
|
note = "ops contract handles transit (NATS); Radicle handles permanent record (audit ledger via mirror sidecar) and governance (delegations)" },
|
||
|
|
|
||
|
|
# Ephemeral build infrastructure relationships (ADR-039)
|
||
|
|
{ from = "ephemeral-build-infrastructure", to = "provider-abstraction", kind = 'FlowsTo, weight = 'High,
|
||
|
|
note = "buildkit_runner is a component spawned via orchestrator's VM lifecycle — same provider abstraction as taskservs" },
|
||
|
|
{ from = "ephemeral-build-infrastructure", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Per-build sizing resolved from .build-spec.ncl declaration, historical p95, or language defaults — no hardcoded sizing" },
|
||
|
|
{ from = "ephemeral-build-infrastructure", to = "ops-contract-dual-mode", kind = 'FlowsTo, weight = 'Medium,
|
||
|
|
note = "Build artifacts (image push to zot) trigger downstream deploy ops via ops.pending — build pipeline emits ops, does not apply them" },
|
||
|
|
{ from = "ephemeral-build-infrastructure", to = "extension-registry-service", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "zot multi-tenant /crates and /images coexist with extension-registry's OCI distribution — both are OCI-spec consumers of the same backing storage" },
|
||
|
|
|
||
|
|
# Federated integration modes relationships (ADR-042)
|
||
|
|
{ from = "federated-integration-modes", to = "type-safety-nickel", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Cabling contracts and domain contracts are Nickel NCL — type safety extends to integration context assembly" },
|
||
|
|
{ from = "federated-integration-modes", to = "solid-boundaries", kind = 'ValidatedBy, weight = 'High,
|
||
|
|
note = "No filesystem coupling between participants — each project is an autonomous peer consuming typed OCI artifacts" },
|
||
|
|
{ from = "federated-integration-modes", to = "ephemeral-build-infrastructure", kind = 'Complements, weight = 'Medium,
|
||
|
|
note = "Domain artifacts are stored in the same zot registry as build artifacts — OCI storage is shared substrate" },
|
||
|
|
{ from = "federated-integration-modes", to = "config-driven-always", kind = 'ManifestsIn, weight = 'High,
|
||
|
|
note = "Cabling files are declarative config — no imperative wiring; context assembly reads config, not code" },
|
||
|
|
|
||
|
|
],
|
||
|
|
}
|