provisioning-code/CHANGELOG.md

364 lines
28 KiB
Markdown
Raw Permalink Normal View History

# Provisioning Repository - Changes
---
## 2026-06-08 — provisioning-desktop: NATS auto-start + configurable launchers (ADR-049)
Refines ADR-048 by extending what the shell does on the local host.
**Generalized process supervision**: the daemon supervisor is extracted into a process-agnostic `ProcessSupervisor` driven by a `ProcessSpec` (spawn + health + restart-backoff + graceful SIGTERM→SIGKILL, port pre-check). The daemon (HTTP `/health`) and a local `nats-server` (TCP-connect health) are two specs on one shared path; `DaemonStatus`/`DaemonControl` are kept as aliases so existing code is untouched and the backoff tests are preserved.
**NATS auto-start (local-or-remote, like the daemon)**: a local `nats-server` is spawned and supervised **only when `nats.spawn` is set AND `nats.url` is loopback** (`-a <host> -p <port> -js`), started *before* the daemon and awaited (bounded) so the daemon and consumer bridge find it listening. The same port pre-check **adopts** an already-running external server instead of double-spawning. Remote `nats.url` stays connect-only — never spawned. Absent `nats-server` degrades to the REST poller, not a panic.
**Configurable launchers** (replaces tray `actions`): a unified `Launcher { kind, label, target, args, params, show_output, confirm }` declared **per-connection and globally**, merged in the tray. `kind=url` opens the OS browser (platform opener); `kind=command` runs a local program host-side via `tokio::process`, capturing stdout/stderr into a reusable bundled output window (`dist/output.html`); `kind=tool` invokes a daemon `/api/v1` tool (the former action). Legacy `[[connections.actions]]` TOML migrates transparently (serde `alias = "actions"`, defaulted `kind=tool`, `tool``target` alias).
**Security boundary**: command execution is **host-side only** — in the Rust tray handler, sourced from the local `settings.toml`, and deliberately **not** a Tauri command — so the external `/ui/` webview (no invoke) and remote connections cannot trigger host execution. Preserves the ADR-048 host-bridge boundary.
**Settings form**: the bundled settings window gains a Launchers editor (scope selector, kind-aware fields, JSON params validation); `save_settings` now carries the `AppHandle` and rebuilds the tray so edits appear immediately.
**Verified**: `cargo clippy -p provisioning-desktop --all-targets -- -D warnings` clean; `cargo test -p provisioning-desktop` → 34 passed (incl. legacy `actions`→launcher migration, NATS loopback-spawn gating, host/port parsing).
**Also — daemon UI SPA fix** (`provisioning-daemon/ui/templates/base.html`): the SPA router dropped per-page `<head>` `<script src>`/`<link>` on client-side navigation, so Cytoscape never loaded and the workspace DAG view rendered empty **in the desktop webview** (which is SPA-only) while a full browser load worked. `_spaNavigate` now loads a fetched page's `<head>` vendor assets (deduped by resolved URL) before running its inline scripts. Fixes the DAG view and any future `{% block head %}` vendor-lib page (e.g. `ontology.html`).
**ADR-049**: `provisioning-desktop local-host surface: generalized process supervision (daemon + NATS) and configurable launchers (url/command/tool)` (Accepted). 3 constraints — 1 `Hard` (`desktop-command-launcher-host-side-only`) + 2 `Soft` on `centralized-vs-scripted` / capability granularity (`desktop-nats-spawn-loopback-only`, `desktop-launchers-config-driven`). `ontology_check.verdict = 'Safe`. Engages `centralized-vs-scripted` (new core.ncl edge, `'Complements`): local affordances beside the orchestrator path, not through it.
**Files**: `platform/crates/provisioning-desktop/src/{supervisor,settings,connection,tray,commands,state,window,lib}.rs`, `dist/{output.html,settings.html}`, `capabilities/default.json`; `provisioning-daemon/ui/templates/base.html`; `.ontoref/adrs/adr-049-desktop-local-surface.ncl`, `.ontoref/ontology/core.ncl`.
---
## 2026-06-08 — provisioning-desktop: Tauri native shell for the daemon
### New crate `platform/crates/provisioning-desktop`
**Decision**: a Tauri 2 native shell (desktop + iOS) that **wraps the daemon's existing `/ui/` in a webview** rather than reimplementing a native UI — the daemon's web surface stays the single source of UI truth. It is a workspace member but does **not** depend on `provisioning-daemon`: it mirrors the small `DaemonEvent` JSON wire enum locally and reuses only `platform-nats`, so a GUI app never drags in Axum/tera/provisioning-core (also sidesteps the surrealdb rustls lockfile pin).
**Live status — durable JetStream consumer + REST fallback**: per-device durable pull consumer (`durable_name = desktop-<id>`, `DeliverPolicy::New` on first create, `RetentionPolicy::Limits` so each device keeps an independent cursor, delete-on-logout) on a new `DAEMON` stream. The stream was added to `platform-nats::ensure_provisioning_streams` (`provisioning.daemon.>`) in the same change — this also **fixed a latent bug** where `EventBus::publish` failed its JetStream ack on every tool invocation (no stream previously covered the subject). NATS-less remotes fall back to polling `GET /api/v1/state/recent` (baseline-on-first-poll mirrors `DeliverPolicy::New`, no notification flood).
**Multi-connection profiles**: exactly one supervised-local (spawn + health + restart-backoff + graceful SIGTERM→SIGKILL; port pre-check per ADR-027 to avoid double-spawning a CLI-started daemon) plus N connect-only-remote; live switch tears down tasks and re-points the webview.
**Auth + security**: reuses the daemon matrix (`solo`/`jwt`/`password`) — the shell **carries** tokens (keyring JWT, `/ui/` cookie) but never mints or decides them, honoring the ADR-014 auth boundary. **Remote profiles must be `https://`** (loopback exempt — never crosses a network, and aligns with iOS ATS), enforced at save + activate + load-fallback. The external `/ui/` window gets **no** invoke access; only the bundled settings window does.
**Tray**: Connections submenu (live switch + rebuild-on-switch), per-connection predefined Actions with `confirm` gating via a native OK/Cancel dialog, Workspaces (informational), daemon Start/Stop/Restart (local only), status tooltip.
**iOS-ready**: `[lib] crate-type = ["staticlib", "cdylib", "rlib"]` + thin `[[bin]]`; `run()` carries `#[cfg_attr(mobile, tauri::mobile_entry_point)]`; tray/settings-window are `#[cfg(desktop)]`; local profiles coerce to connect-only-remote on mobile. `cargo tauri ios init` is a local toolchain step (not run here).
**Verified**: `cargo clippy -p provisioning-desktop --all-targets -- -D warnings` clean (built on `/Volumes/Devel` target-dir); `cargo test -p provisioning-desktop` → 27 passed.
**ADR-048**: `provisioning-desktop: a Tauri native shell wrapping the daemon /ui/, a wire-decoupled workspace member with durable-consumer status` (Accepted). 5 constraints — 3 `Hard` (remote-https-only, external-window-no-invoke, auth-carried-never-minted) + 2 `Soft` on the `monorepo-vs-split` Spiral (wire-decoupled-from-daemon, daemon-stream-durable-per-device, direction-of-motion per ondaod). `ontology_check.verdict = 'Safe`. Sub-decisions folded in as constraints rather than standalone ADRs: lib/bin split is framework-mandated; HTTPS-loopback is localized validation.
**Ontology**: core.ncl Practice node `provisioning-desktop-shell` (edges to `smart-interface-unification` [Complements], `monorepo-vs-split` [Resolves], `platform-shared-infra` [ManifestsIn], `solid-boundaries` [ValidatedBy]; `adrs = ["adr-048"]`). State: no FSM transition — a new consumer surface, not an advance of a tracked dimension.
**Files**: `platform/crates/provisioning-desktop/` (new crate: api, auth, commands, connection, events, nats_bridge, notify, poller, settings, state, supervisor, tray, window, lib, main + dist/ SPA + tauri.conf.json + capabilities + icons), `platform/crates/platform-nats/src/streams.rs`, `platform/Cargo.toml`, `justfiles/desktop.just`, `.ontoref/ontology/core.ncl`.
---
## 2026-05-29 — catalog management as a multi-surface capability (ADR-047)
### ADR-047: Catalog management dispatched through the shared Registry
**Decision**: catalog management operations are provisioning-core Registry Tools dispatched exclusively through `Registry::invoke`, so CLI, WebUI, GraphQL, and MCP share identical semantics (no per-surface reimplementation). Per the nushell-vs-rust boundary synthesis (ADR-029) the Rust Tool owns dispatch while the Nushell resolver (`contract_resolver.nu`) is the tier-3 legacy closure, retired per-operation once G3 contract-test parity passes. 3 Hard constraints: `catalog-management-via-registry`, `catalog-tools-g3-parity`, `catalog-tier3-nushell-closure`.
**Implementation — first Tool `catalog_validate`** (read-shaped: evaluate a component manifest against the contract schema via `NclCache`, report its `requires.contract`): `platform/crates/provisioning-core/src/tools/catalog.rs`, registered in `with_all_tools`. **Verified**: `cargo check -p provisioning-core` Finished; G3 parity test `catalog_tool_agrees_across_three_tiers` (CLI≡HTTP≡MCP) green via a `ct_catalog` fixture in `contract-tests`.
### Full rename: extension-registry → catalog-registry (no compatibility aliases)
Complete cut-over with zero aliases — every `extension-registry`/`extension_registry`/`ExtensionRegistry`/`provisioning-extension-registry` identifier is now `catalog-registry`/`catalog_registry`/`CatalogRegistry`/`provisioning-catalog-registry`.
- **Plano A (crate)**: package name, lib name, directory (`git mv` in the `platform` submodule), workspace member, `provisioning-core` path dependency, `use` statements.
- **Plano B (service identity)**: binary name, `CatalogRegistry` type (service.rs/lib.rs/main.rs/handlers.rs), runtime service-key strings (`is_service_enabled`/`load_service_config_from_ncl`/observability/api_catalog/user-agent), control-center `ServiceType::CatalogRegistry` + `catalog_registry_url` + `check_catalog_registry`, platform-config `CatalogRegistryConfig`, `orchestrator/config_manager`, `core/nulib` health/autostart/startup/service_check/workspace strings, docker-compose service names (multi-user/solo + examples), nginx/prometheus, justfiles, scripts, typedialog forms/scripts.
- **Files renamed on disk** (12): `api-catalog-catalog-registry.json`, `schemas/platform/catalog-registry.ncl` + `configs/catalog-registry.{solo,multiuser,enterprise,cicd}.ncl` + `defaults/catalog-registry-defaults.ncl` + `templates/catalog-registry-config.ncl.j2`, `platform/templates/catalog-registry.ncl.j2`, `platform/typedialog_templates/catalog-registry.ncl.j2`, `platform/typedialog/forms/catalog-registry-form.toml`, `platform/typedialog/scripts/configure-catalog-registry.sh`.
**Verified**: `cargo check -p catalog-registry -p control-center -p platform-config -p orchestrator -p provisioning-core` Finished; `nickel export` clean for `catalog-registry.{solo,cicd}.ncl`, `nickel typecheck` clean for `{multiuser,enterprise}` (their export-time "missing url" is a pre-existing template-completeness issue, not the rename).
**Excluded**: `CHANGELOG.md` (historical record), `platform/archive/` (archived), `data/tasks/` (runtime artifacts), one draft SVG diagram label.
### Standalone "extension" vocabulary → catalog (Tool API surface)
The catalog-discovery Tools are renamed `extension_{list,show,search,capabilities}``catalog_{list,show,search,capabilities}` (structs `Catalog{List,Show,Search,Capabilities}`); `tools/extension.rs``tools/catalog_components.rs`; daemon UI `invoke_tool` call-sites updated. The `Environment.extensions_root` field → `catalog_root`, and the env var `PROVISIONING_EXTENSIONS_ROOT``PROVISIONING_CATALOG_PATH` — unifying the catalog-root identifier across the Nushell (env.nu) and Rust (daemon) boundary, which previously diverged. Touches provisioning-core, provisioning-daemon, provisioning-tool, control-center-ui, backup-manager, platform-config, mcp-server, the daemon NCL schema, and CLI help/workspace tooling. **This changes Tool names on the API surface** (HTTP `/api/v1/tools/catalog_*`, MCP `tools/call`, CLI) — no aliases.
**Ontology**: core.ncl Practice node `catalog-management-multi-surface` (edges to `provisioning-registry`, `g3-contract-invariant`, `catalog-contract-distribution`, `nushell-vs-rust-boundary`).
**Files**: `adrs/adr-047-catalog-management-multi-surface.ncl`, `platform/crates/provisioning-core/src/tools/catalog.rs`, `platform/crates/provisioning-core/src/{registry.rs,tools/mod.rs}`, `platform/crates/contract-tests/{src/lib.rs,tests/g3_contract.rs}`, `platform/crates/catalog-registry/` (renamed), `platform/Cargo.toml`, `.ontology/core.ncl`
---
## 2026-05-29 — catalog externalization + content-addressed contract (ADR-046)
### Catalog relocation: configurable root, no symlink
**Change**: the catalog (providers/components/taskservs/_clusters) no longer must live under `$PROVISIONING/catalog`. A new `PROVISIONING_CATALOG_PATH` env var (→ `paths.catalog` config → `$PROVISIONING/catalog` default) sets the root; all sub-paths derive from it. Resolved in `core/nulib/env.nu`; accessor `get-catalog-path` added; the loose `path join "catalog"` sites (components, clusters/taskservs run, OCI signing, integration) now derive from the root. Lets a catalog live anywhere — importable/downloadable, no provisioning checkout dependency.
### ADR-046: Catalog component contract distributed as content-addressed Domain artifact via OCI referrers
**Decision**: `schemas/catalog/manifest.ncl` (the component contract) is distributed as a content-addressed OCI Domain artifact, never vendored by copy. A catalog declares `requires.contract = { id, version, subject }` (semver intent); resolution pins it to an immutable `sha256` digest in `catalog.lock.ncl` (`DomainLockEntry` shape), and all automated loading reads the digest. Version→digest discovery uses the OCI Referrers API exclusively — each version is attached as a referrer to a stable anchor digest with an `org.opencontainers.image.version` annotation, so no mutable tags exist in the load path. Reuses the ADR-042 Domain-artifact primitives; component-owned config schemas stay vendored as `'SchemaLibrary`.
**Implementation**: `requires.contract` field + `ContractRef` type in `schemas/catalog/manifest.ncl`; resolver `core/nulib/platform/oci/contract_resolver.nu` (real semver matching incl. `^`/`~`/comma ranges, referrers discovery, pull-by-digest, lock writing, anchor + per-version attach publishing) reusing `domain_client.nu` credential machinery; validation wiring (`read-contract-decl`, `validate-manifest-against-contract`). Automated process: `core/nulib/platform/oci/contract_cli.nu` + `justfiles/catalog-contract.just` (recipes `contract-publish-anchor`/`-version`/`-resolve`/`-validate`).
**Verification**: full round-trip proven end-to-end against a local zot (referrers) — publish anchor → attach v1.0.0+v1.1.0 → discover → resolver selects 1.1.0 for `>=1.0.0, <2.0.0` → pull-by-digest → pulled `contract.ncl` typechecks → `catalog.lock.ncl` validates against `DomainLockEntry`. Production publish targets `reg.librecloud.online` (private — unreachable from dev sandbox).
### ADR-046 extension: push-registry as a pluggable-source platform setting
**Decision**: the OCI push-registry target is a platform setting, not a property of the active workspace. A command resolves endpoint + credentials at call time from a pluggable source — `sops | api | literal | env` — and materialises them as an env setting (`PROVISIONING_REGISTRY` + `PROVISIONING_PUSH_DOCKER_CONFIG`) that the contract resolver honours before the workspace credential flow. In override mode the credential **source** is the authorization gate (the Age key decrypting the sops file, or the api's own auth), replacing the workspace-scope check — which is what decouples the push target from CWD. Precedence: explicit flag → `registry.push.*` config → workspace `registry_provides` fallback.
**Implementation**: `core/nulib/platform/oci/push_registry.nu` (`resolve-push-registry`, `select-push-registry`; sops/api/literal/env sources reusing the ADR-042 cabling idiom); `acquire-docker-config` helper in `contract_resolver.nu` honouring the `PROVISIONING_PUSH_DOCKER_CONFIG` override across all four OCI functions; `select-registry` subcommand + `contract-use-registry` recipe. ADR-046 constraint `push-registry-pluggable-source` (Hard). **Verified**: literal/env/sops resolution (real Age-key sops decrypt) + export emission + docker-config materialisation.
### Catalog management as a cross-surface invariant
Catalog management is a domain capability that MUST be exposed uniformly across CLI, WebUI, GraphQL, and MCP, dispatching through `provisioning-core::Registry` per the G3 contract invariant (no per-surface reimplementation). The Nushell modules + just recipes are the current CLI/script surface; cross-surface parity (catalog operations as Registry Tools) is the target.
**Ontology**: core.ncl Practice nodes `catalog-contract-distribution` + `push-registry-selection` (9 edges); tensions `monorepo-vs-split` and `centralized-vs-scripted` updated with the ADR-046 synthesis. QA `provisioning-catalog` (what catalogs are, contents, distribution, cross-surface management) + mode `provisioning-catalog` (inspect/validate: resolve root, enumerate by kind, typecheck manifests, report contract lock).
**Files**: `adrs/adr-046-catalog-contract-content-addressed.ncl`, `schemas/catalog/manifest.ncl`, `core/nulib/platform/oci/{contract_resolver,push_registry,contract_cli}.nu`, `justfiles/catalog-contract.just`, `core/nulib/env.nu`, `core/nulib/platform/config/accessor/functions.nu`, `.ontology/core.ncl`, `.ontoref/reflection/qa.ncl`, `.ontoref/reflection/modes/provisioning-catalog.ncl`
---
## 2026-05-20 — foundation library relocation (ADR-045 revises ADR-043)
### ADR-045: Foundation library relocation — single-consumer provider traits fold into provisioning
**Decision**: `ontoref-compute` and `ontoref-dns` relocate into `provisioning/platform/crates/` as workspace members; `ontoref-auth` and `ontoref-ui` consolidate into one external `foundation` workspace; ADR-043's γ-posture two-consumer `locked` claim for `DnsProvider`/`ComputeProvider` is revised.
**Why ADR-043's premise lapsed**: ADR-043 declared both traits γ-posture locked on a two-consumer set (lian-build + orchestrator). lian-build subsequently issued its own ADR-009 (*no-provider-knowledge*, 2026-05-18): the libre-forge fleet control plane became operational, `fleet-daemon` took ownership of VM lifecycle, and lian-build removed all provider knowledge (deleted `orchestrator_client.rs`, the `daemon::compute` module, `GET /api/providers`, the ADR-005 catalog). lian-build is no longer a consumer — `DnsProvider`/`ComputeProvider` now have a single consumer, the provisioning orchestrator.
**Placement rule**: a library is foundation (its own repo/workspace) when plural independent consumers make sharing cheaper than duplication; it folds into a consumer's repo when a single consumer makes a standalone repo pure overhead. `auth`/`ui` (3 consumers: ontoref daemon, lian-build, mirador) stay split into `foundation`; `compute`/`dns` (1 consumer: orchestrator) fold into `provisioning`.
**Unaffected**: ADR-043's orchestrator-internal decisions — `coredns_client.rs` deletion, `GET /.panel/manifest`, `compute_provider: Option<Arc<dyn ComputeProvider>>` — and its three constraints remain in force. ADR-045 revises only the consumer-count and trait-home claims.
**Files**: `adrs/adr-045-foundation-library-relocation.ncl`
---
## 2026-05-12 — orchestrator γ-posture adoption (ontoref-foundation)
### ADR-043: Orchestrator adopts DnsProvider, ComputeProvider, federation manifest
**Decision**: Replace `coredns_client.rs` with `Arc<dyn DnsProvider>`; add `compute_provider: Option<Arc<dyn ComputeProvider>>` to AppState; expose `GET /.panel/manifest` at `src/handlers/federation.rs`.
**γ-posture verdicts confirmed by this ADR**:
- `DnsProvider`: two consumers (lian-build + orchestrator) → trait locked
- `ComputeProvider`: two consumers (lian-build + orchestrator) → lifecycle surface locked; HcloudClient retained for operations outside the trait surface (volumes, floating IPs, images) per γ PARTIAL semantics
**`/.panel/manifest` endpoint**: `OrchestratorManifest` (panel_id, version, connectors, panels, workflows) returned without auth. Superset of the three-field projection consumed by `FederationResolver` — serves both machine and human consumers.
**`compute_provider: Option<Arc<dyn ComputeProvider>>`**: Populated from `HetznerCloudProvider::from_env()` when `HCLOUD_TOKEN` is present; `None` → degraded mode (read-only fleet ops), consistent with ADR-042's no-hard-startup-failure contract.
**Files**: `adrs/adr-043-ontoref-foundation-orchestrator.ncl`
---
## 2026-05-12 — nickel branch consolidation
### ADR-025 Amendments: Eager function-body parse (2026-04-17)
**Decision**: Nushell parses `use` statements inside function bodies at module-load time. Subprocess boundary is the only true lazy-load mechanism.
**Constraints added/amended**:
- `bash-wrapper-has-no-runner-reference` — now permits `provisioning-cli.nu` as transitional fallback
- `universal-fallback-is-transitional` — 22 unmapped commands are explicit migration debt;
must be resolved before lazy-load architecture is considered complete
- `every-registry-command-has-thin-handler` — made directional (progress metric, not gate)
**Rejected approach**: Single-entry `provisioning-cli.nu` for hot paths — measured at 3.1s vs 0.080.15s
for thin handlers. All 15 dispatcher wrappers fire at module-load regardless of invoked command.
**Files**: `adrs/adr-025-unified-lazy-loading.ncl`
---
### Platform Services Documentation (2026-02-03)
**All 10 platform services documented** with endpoint catalogue and local setup guide.
**Services**:
| Service | Endpoints | Notes |
| ------- | --------- | ----- |
| vault | 8 | Transit encryption, dynamic secrets |
| registry | 6 | OCI extension distribution |
| control-center | 12 | RBAC, audit, compliance |
| rag | 5 | Vector search, document ingestion |
| ai-service | 7 | Model routing, DAG execution |
| mcp-server | 4 | AI-powered config tools |
| daemon | 3 | State sync, health aggregation |
| orchestrator | 8 | Workflow execution, checkpoints |
| detector | 4 | Anomaly detection, alerts |
| ui | 3 | Web control center frontend |
**Added**:
- `docs/src/operations/platform-services-inventory.md` — 50+ endpoints
- `docs/src/operations/local-services-setup.md` — build, config, troubleshooting
- `scripts/start-local-binaries.nu` — dependency-ordered service startup automation
---
### TypeDialog Migration (2026-01-09)
**`forminquire` fully replaced** by TOML-driven `typedialog` with TTY wrappers.
**New form registry** (`.typedialog/`):
- `core/forms/auth-login.toml`
- `core/forms/mfa-enroll.toml`
- `core/forms/setup-wizard.toml`
- `core/forms/infrastructure/server_delete_confirm.toml`
- `core/forms/infrastructure/cluster_delete_confirm.toml`
- `core/forms/infrastructure/taskserv_delete_confirm.toml`
- `core/forms/infrastructure/generic_delete_confirm.toml`
- `platform/forms/ai-service-form.toml` (with Nickel fragment composition)
- `platform/forms/control-center-form.toml`
- `platform/forms/extension-registry-form.toml`
**Documentation**: `.typedialog/README.md`, `platform/forms/README.md`, `platform/forms/fragments/README.md`
**Architecture doc**: `docs/src/architecture/config-loading-architecture.md`
---
### Nushell 0.110.0 Compatibility (2026-01-21)
- Fixed `try`/`catch` syntax across `.typedialog/platform/scripts/`, `bootstrap/install.nu`, example deploy scripts
- Updated `external.nu`, `paths.nu`, `export-toml.nu` for Nu 0.110.0 API changes
- Removed stale session reports from `.coder/`
- Relocated `nickel-installation-guide.md` into `docs/src/setup/`
---
## 2026-01-08 — Nickel IaC migration complete
**Repository**: provisioning (standalone, nickel branch)
**Changes**: Nickel IaC migration complete — Legacy KCL and config cleanup
---
## 📋 Summary
Complete migration to Nickel-based infrastructure-as-code with consolidated configuration strategy.
Legacy KCL schemas, deprecated config files, and redundant documentation removed.
New project structure with `.cargo/`, `.github/`, and schema-driven configuration system.
---
## 📁 Changes by Directory
### ✅ REMOVED (Legacy KCL Ecosystem)
- **config/** - Deprecated TOML configs (config.defaults.toml, kms.toml, plugins.toml, etc.)
- **config/cedar-policies/** - Legacy Cedar policies (moved to Nickel schemas)
- **config/templates/** - Old Jinja2 templates (replaced by Nickel generator/)
- **config/installer-examples/** - KCL-based examples
- **docs/src/** - Legacy documentation (full migration to provisioning/docs/src/)
- **kcl/** - Complete removal (all workspaces migrated to Nickel)
- **tools/kcl-packager.nu** - KCL packaging system
### ✅ ADDED (Nickel IaC & New Structure)
- **.cargo/** - Rust build configuration (clippy settings, rustfmt.toml)
- **.github/** - GitHub Actions CI/CD workflows
- **schemas/** - Nickel schema definitions (primary IaC format)
- main.ncl, provider-aws.ncl, provider-local.ncl, provider-upcloud.ncl
- Infrastructure, deployment, services, operations schemas
- **docs/src/architecture/adr/** - ADR updates for Nickel migration
- adr-010-configuration-format-strategy.md
- adr-011-nickel-migration.md
- adr-012-nushell-nickel-plugin-cli-wrapper.md
### 📝 UPDATED (Core System)
- **provisioning/docs/src/** - Comprehensive product documentation
- API reference, architecture, guides, operations, security, testing
- Nickel configuration guide with examples
- Migrated from legacy KCL documentation
- **core/** - Updated with Nickel integration
- Scripts, plugins, CLI updated for Nickel schema parsing
- **justfiles/** - Added ci.just for Nickel-aware CI/CD
- **README.md** - Complete restructure for Nickel-first approach
- **.gitignore** - Updated to ignore Nickel build artifacts
---
## 📊 Change Statistics
| Category | Removed | Added | Modified |
| ---------- | --------- | ------- | ---------- |
| Configuration | 50+ | 10+ | 3 |
| Documentation | 150+ | 200+ | 40+ |
| Infrastructure | 1 (kcl/) | - | - |
| Plugins | 1 | - | 5+ |
| Build System | 5 | 8+ | 3 |
| **Total** | **~220 files** | **~250 files** | **50+ files** |
## ⚠️ Breaking Changes
1. **KCL Sunset**: All KCL infrastructure code removed. Migrate workspaces using `nickel-kcl-bridge` or rewrite directly in Nickel.
2. **Config Format**: TOML configuration files moved to schema-driven Nickel system. Legacy config loading deprecated.
3. **Documentation**: Old KCL/legacy docs removed. Use `provisioning/docs/` for current product documentation.
4. **Plugin System**: Updated to Nickel-aware plugin API. Legacy Nushell plugins require recompilation.
## 🔧 Migration Path
```bash
# For existing workspaces:
provisioning workspace migrate --from-kcl <workspace-name>
# For custom configs:
nickel eval --format json <your-config.ncl> | jq '.'
```
## ✨ Key Features
- **Type-Safe**: Nickel schemas eliminate silent config errors
- **Composable**: Modular infrastructure definitions with lazy evaluation
- **Documented**: Schema validation built-in, IDE support via LSP
- **Validated**: All imports pre-checked, circular dependencies prevented
- **Bridge Available**: `nickel-kcl-bridge` for gradual KCL→Nickel migration
---
## 📝 Implementation Details
### Nickel Schema System
- **Three-tier architecture**: infrastructure, operations, deployment
- **Lazy evaluation**: Efficient resource binding and composition
- **Record merging**: Clean override patterns without duplication
- **Type validation**: LSP-aware with IDE auto-completion
- **Generator system**: Nickel-based dynamic configuration at runtime
### Documentation Reorganization
- **provisioning/docs/src/** (200+ files) - Customer-facing product docs
- **docs/src/** (20-30 files) - Architecture and development guidelines
- **.coder/** - Session files and implementation records
- Separation of concerns: Product docs isolated from session artifacts
### CI/CD Integration
- GitHub Actions workflows for Rust, Nickel, Nushell
- Automated schema validation pre-commit
- Cross-platform testing (Linux, macOS)
- Build artifact caching for fast iteration
---
## ⚠️ Compatibility Notes
**Breaking**: KCL workspaces require migration to Nickel. Use schema-aware tooling for validation.
**Migration support**: `nickel-kcl-bridge` tool and guides available in `provisioning/docs/src/development/`.
**Legacy configs**: Old TOML files no longer loaded. Migrate to Nickel schema format via CLI tool.
---
**Status**: Nickel migration complete. System is production-ready.
**Date**: 2026-01-08
**Branch**: nickel
---
*Last updated: 2026-05-12*