ontoref/ontology/schemas/manifest.ncl
Jesús Pérez 401294de5d
Some checks failed
Nickel Type Check / Nickel Type Checking (push) Has been cancelled
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
feat: config surface, NCL contracts, override-layer mutation, on+re update
Config surface — per-project config introspection, coherence verification, and
  audited mutation without destroying NCL structure (ADR-008):

  - crates/ontoref-daemon/src/config.rs — typed DaemonNclConfig (parse-at-boundary
    pattern); all section structs derive ConfigFields + config_section(id, ncl_file)
    emitting inventory::submit!(ConfigFieldsEntry{...}) at link time
  - crates/ontoref-derive/src/lib.rs — #[derive(ConfigFields)] proc-macro; serde
    rename support; serde_rename_of() helper extracted to fix excessive_nesting
  - crates/ontoref-daemon/src/main.rs — 3-tuple bootstrap block (nickel_import_path,
    loaded_ncl_config: Option<DaemonNclConfig>, stdin_raw); apply_ui_config takes
    &UiConfig; NATS call site typed; resolve_asset_dir cfg(feature = "ui")
  - crates/ontoref-daemon/src/api.rs — config GET/PUT endpoints, quickref, coherence,
    cross-project comparison; index_section_fields() extracted (excessive_nesting)
  - crates/ontoref-daemon/src/config_coherence.rs — multi-consumer coherence;
    merge_meta_into_section() extracted; and() replaces unnecessary and_then

  NCL contracts for ontoref's own config:
  - .ontoref/contracts.ncl — LogConfig (LogLevel, LogRotation, PositiveInt) and
    DaemonConfig (Port, optional overrides); std.contract.from_validator throughout
  - .ontoref/config.ncl — log | C.LogConfig applied
  - .ontology/manifest.ncl — contracts_path, log/daemon contract refs, daemon section
    with DaemonRuntimeConfig consumer and 7 declared fields

  Protocol:
  - adrs/adr-008-ncl-first-config-validation-and-override-layer.ncl — NCL contracts
    as single validation gate; Rust structs are contract-trusted; override-layer
    mutation writes {section}.overrides.ncl + _overrides_meta, never touches source

  on+re update:
  - .ontology/core.ncl — config-surface node (28 practices); adr-lifecycle extended
    to adr-007 + adr-008; 6 new edges (ManifestsIn daemon, DependsOn ontology-crate,
    Complements api-catalog-surface/dag-formalized/self-describing/adopt-ontoref)
  - .ontology/state.ncl — protocol-maturity blocker and self-description-coverage
    catalyst updated for session 2026-03-26
  - README.md / CHANGELOG.md updated
2026-03-26 20:20:22 +00:00

265 lines
11 KiB
Plaintext

let content = import "content.ncl" in
let repo_kind_type = [|
'DevWorkspace,
'PublishedCrate,
'Service,
'Library,
'AgentResource,
'Mixed,
'PersonalOntology,
|] in
let consumer_type = [|
'Developer,
'Agent,
'EndUser,
'CI,
'Downstream,
|] in
let artifact_kind_type = [|
'RustDoc,
'JsonSchema,
'ContainerImage,
'CratePackage,
'StaticSite,
'NuPlugin,
'OntologyExport,
|] in
let audit_level_type = [|
'Quick,
'Standard,
'Strict,
|] in
# ── Operational layers ──────────────────────────────────────────────────────
# A layer is a named region of the repo with visibility rules per mode.
# The `committed` flag distinguishes product (true) from process (false).
let layer_type = {
id | String,
paths | Array String,
committed | Bool,
description | String | default = "",
} in
# ── Operational modes ───────────────────────────────────────────────────────
# A mode is an active perspective the developer/agent switches into.
# It determines which layers are visible and what audit level applies.
let op_mode_type = {
id | String,
description | String | default = "",
visible_layers | Array String,
audit_level | audit_level_type | default = 'Standard,
pre_activate | Array String | default = [],
post_activate | Array String | default = [],
} in
# ── Publication service ─────────────────────────────────────────────────────
# Where artifacts go and what operations surround the publish action.
let auth_method_type = [|
'SSH,
'Token,
'OIDC,
'None,
|] in
let service_scope_type = [|
'Public,
'PrivateNetwork,
'LocalRegistry,
'SelfHosted,
|] in
let publication_service_type = {
id | String,
artifact | artifact_kind_type,
scope | service_scope_type,
registry_url | String | default = "",
auth_method | auth_method_type | default = 'None,
pre_publish | Array String | default = [],
post_publish | Array String | default = [],
condition | String | default = "",
trigger | String,
} in
# ── Consumption modes (who consumes, what they need) ────────────────────────
let consumption_mode_type = {
consumer | consumer_type,
needs | Array artifact_kind_type,
audit_level | audit_level_type | default = 'Standard,
description | String | default = "",
} in
# ── Tool requirements ─────────────────────────────────────────────────────
# Declares what tools the project needs. install-tools.nu and sync audit
# consume this to verify availability or trigger installation.
let install_method_type = [|
'Builtin,
'Cargo,
'Npm,
'Brew,
'Pip,
'Manual,
|] in
let tool_requirement_type = {
name | String,
install_method | install_method_type | default = 'Builtin,
version | String | default = "",
required | Bool | default = true,
} in
# ── Justfile convention ──────────────────────────────────────────────────
# Declares expected justfile structure so sync audit can verify completeness.
let justfile_system_type = [| 'Import, 'Mod, 'Hybrid, 'Flat, 'None |] in
let justfile_convention_type = {
system | justfile_system_type | default = 'Mod,
required_modules | Array String | default = ["build", "test", "dev", "ci"],
required_recipes | Array String | default = ["default", "help"],
} in
# ── Config surface ──────────────────────────────────────────────────────
# Describes the project's configuration system: where the NCL config lives,
# how it is structured, and which consumers (Rust structs, Nushell scripts,
# CI pipelines, external tools) read each section.
#
# A field in a section is "unclaimed" only if no consumer declares it —
# not merely absent from the Rust struct. CI/CD scripts and external tooling
# are first-class consumers.
#
# Mutation uses an override layer: original NCL files are never modified.
# Changes are written to {section}.overrides.ncl and merged via & at the
# entry point. Comments, contracts, and formatting in originals are preserved.
let config_kind_type = [|
'NclMerge, # multiple .ncl files merged via & operator
'TypeDialog, # .typedialog/ structure with form.toml + validators + fragments
'SingleFile, # single monolithic .ncl file
|] in
let consumer_kind_type = [|
'RustStruct, # serde::Deserialize struct in a Rust crate
'NuScript, # Nushell script accessing $config.field paths
'CiPipeline, # CI pipeline (Woodpecker, GitHub Actions, etc.)
'External, # external tool or process reading config JSON
|] in
let config_consumer_type = {
# Identifier for this consumer (e.g. "vapora-backend", "deploy-script").
id | String,
kind | consumer_kind_type,
# Reference path: Rust fully-qualified type or script path.
# e.g. "vapora_backend::config::ServerConfig" or "scripts/deploy.nu"
ref | String | default = "",
# Fields this consumer reads. Empty means the consumer reads all fields,
# which is treated as claiming all NCL keys for orphan analysis.
fields | Array String | default = [],
} in
let config_section_type = {
# Section identifier, must match the top-level NCL key (e.g. "server").
id | String,
# Path to the NCL file for this section, relative to config_root.
file | String,
# Path to the NCL contract file that types this section. Relative to
# contracts_path (or project root if contracts_path is empty).
contract | String | default = "",
description | String | default = "",
# Why this section exists and why the current values were chosen.
# Consumed by the quickref generator to explain decisions, not just values.
rationale | String | default = "",
# When false: ontoref will only read, never write, this section.
mutable | Bool | default = true,
# All consumers of this section. A NCL field present in no consumer is
# flagged as unclaimed in the coherence report.
consumers | Array config_consumer_type | default = [],
} in
let config_surface_type = {
# Directory containing config NCL files, relative to project root.
# e.g. "config/", "site/config/", ".typedialog/provisioning/"
config_root | String,
# Main NCL file that merges all sections (entry point for nickel export).
entry_point | String | default = "config.ncl",
kind | config_kind_type | default = 'NclMerge,
# Directory containing NCL contract files. Relative to project root.
# Passed as NICKEL_IMPORT_PATH component when exporting.
contracts_path | String | default = "",
# Directory where ontoref writes {section}.overrides.ncl files.
# Defaults to config_root when empty.
overrides_dir | String | default = "",
sections | Array config_section_type | default = [],
} in
# ── Claude baseline ─────────────────────────────────────────────────────
# Declares expected .claude/ structure per project.
let claude_baseline_type = {
guidelines | Array String | default = ["bash", "nushell"],
session_hook | Bool | default = true,
stratum_commands | Bool | default = true,
} in
# ── Root manifest ───────────────────────────────────────────────────────────
let manifest_type = {
project | String,
repo_kind | repo_kind_type,
layers | Array layer_type | default = [],
operational_modes | Array op_mode_type | default = [],
consumption_modes | Array consumption_mode_type,
publication_services | Array publication_service_type | default = [],
tools | Array tool_requirement_type | default = [],
justfile | justfile_convention_type | default = {},
claude | claude_baseline_type | default = {},
default_audit | audit_level_type | default = 'Standard,
default_mode | String | default = "dev",
# Node ID this project maps to in the ontology DAG.
# Used by portfolio tooling to cross-reference publication cards.
ontology_node | String | default = "",
# Publishable content assets (logos, diagrams, web pages).
# Declares source paths and publication targets; consumed by publish modes
# and sync drift detection to verify assets exist and are deployed correctly.
content_assets | Array content.ContentAsset | default = [],
# Reusable NCL templates for mode steps, agent prompts, and publication cards.
# Each template is a parameterised NCL function at source_path.
templates | Array content.ContentTemplate | default = [],
# Configuration surface: where the project's NCL config lives, which
# consumers read each section, and mutation rules. Optional — projects
# without a structured config system omit this field.
config_surface | config_surface_type | optional,
} in
{
RepoKind = repo_kind_type,
ConsumerType = consumer_type,
ArtifactKind = artifact_kind_type,
AuditLevel = audit_level_type,
AuthMethod = auth_method_type,
ServiceScope = service_scope_type,
InstallMethod = install_method_type,
JustfileSystem = justfile_system_type,
Layer = layer_type,
OperationalMode = op_mode_type,
ConsumptionMode = consumption_mode_type,
PublicationService = publication_service_type,
ToolRequirement = tool_requirement_type,
JustfileConvention = justfile_convention_type,
ClaudeBaseline = claude_baseline_type,
ProjectManifest = manifest_type,
ConfigKind = config_kind_type,
ConsumerKind = consumer_kind_type,
ConfigConsumer = config_consumer_type,
ConfigSection = config_section_type,
ConfigSurface = config_surface_type,
}