Four cross-linked documents under docs/mbse/, structured after Jon Holt's Systems Engineering Demystified (2nd ed.): Framework = Ontology + Viewpoints, producing a Model made of Views. - framework.md — the Ontology (SE meta-concepts + Oikos's domain ontology) and an 11-entry Viewpoint catalog (two repeating: Component, Ontology). - README.md — the Model's 9 concern-based Views (mission, requirements, functional/physical architecture, interfaces, behavior, V&V, risk, roadmap). - components.md — 8 per-component Views going one layer deeper into each running part of the system's own internal structure. - ontology.md — 4 Views on the domain ontology itself: entity type hierarchy (split into 9 digestible per-domain diagrams), full relationship catalog, lifecycle state machines with their requires: gates, and concrete population. Grounded in direct verification against source (grep/read), not just existing docs — every finding is graded verified vs. per-research-pass. Surfaced several real, previously undocumented findings along the way: the policy kill-switch (global.auto_act/never_auto_act) is checked only by dead code and an unstarted actuator package, so it doesn't gate the live run path; internal/actuator and internal/learning are compiled but never started by any process; the relationship catalog grew from 34 to 47 types since ADR-0014; and task has no registered lifecycle_defs entry despite having a documented, code-enforced state machine. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
445 lines
20 KiB
Markdown
445 lines
20 KiB
Markdown
# Oikos — Ontology Views
|
|
|
|
> Companion to [the Framework](framework.md), [the Model](README.md), and
|
|
> [the Component Views](components.md). Holt calls Ontology "arguably the
|
|
> single most important part of MBSE, as all of the other elements that
|
|
> make up MBSE are ultimately traceable back to [it]" (*Systems Engineering
|
|
> Demystified*, 2nd ed., Ch. 2, p. 40). [framework.md §3](framework.md#3-the-ontology--oikoss-domain-specific-language)
|
|
> sketched this in condensed form (13 classes) to make one point: Oikos's
|
|
> domain ontology already exists as running code, not documentation. This
|
|
> document is the fuller treatment that sketch promised — a repeating
|
|
> Viewpoint (registered as Viewpoint 11 in
|
|
> [framework.md §4](framework.md#4-the-viewpoint-catalog--the-frameworks-template-for-views)),
|
|
> instantiated as four Views below.
|
|
|
|
**Every fact in this document was read directly from `seeds/ontology.yaml`
|
|
during this pass** (not carried over from ADR-0014's summary, though it is
|
|
cross-checked against it) — where the two disagree, that disagreement is
|
|
itself reported as a finding, not silently reconciled.
|
|
|
|
**Stakeholders for all four Views below:** engineers adding a new entity or
|
|
relationship type, anyone reasoning about whether a lifecycle transition is
|
|
actually gated or just documented, and anyone deciding whether a term used
|
|
elsewhere in this documentation set means what they think it means.
|
|
**Why they earn their place:** every Viewpoint in
|
|
[framework.md §4](framework.md#4-the-viewpoint-catalog--the-frameworks-template-for-views)
|
|
"traces terminology to the Ontology" (§3.1 of that document) — these four
|
|
Views are where that tracing actually terminates. **Notation:** tables
|
|
(the source data), Mermaid `graph`/`stateDiagram-v2` (the structure).
|
|
|
|
## Contents
|
|
|
|
| View | Answers |
|
|
|---|---|
|
|
| [11a. Entity Type Hierarchy](#11a-entity-type-hierarchy) | What can exist, and how is it classified? |
|
|
| [11b. Relationship Catalog](#11b-relationship-catalog) | How can two entities be connected, and with what multiplicity? |
|
|
| [11c. Lifecycle State Machines](#11c-lifecycle-state-machines) | What states can a governed entity be in, and what gates each transition? |
|
|
| [11d. Concrete Population](#11d-concrete-population) | What's actually instantiated, versus merely possible? |
|
|
|
|
---
|
|
|
|
## 11a. Entity Type Hierarchy
|
|
|
|
60 entity types, 5 abstract (cannot be instantiated directly — they exist
|
|
only as polymorphic relationship endpoints and `is-a` parents), organized
|
|
by `domain:` (8 values) and `layer:` (4 values: meta, infrastructure,
|
|
governance, cognition).
|
|
|
|
| Layer | Domains it contains | Entity type count |
|
|
|---|---|---|
|
|
| `meta` | meta | 1 (`entity`, the abstract root) |
|
|
| `infrastructure` | physical, compute, network, storage, software, external | 40 |
|
|
| `governance` | identity | 7 |
|
|
| `cognition` | cognition | 12 |
|
|
|
|
| Domain | Count | Abstract types in this domain |
|
|
|---|---|---|
|
|
| compute | 11 | `compute-entity`, `machine`, `container` |
|
|
| network | 10 | `network` |
|
|
| cognition | 12 | *(none)* |
|
|
| identity | 7 | *(none)* |
|
|
| software | 7 | *(none)* |
|
|
| external | 4 | *(none)* |
|
|
| storage | 4 | *(none)* |
|
|
| physical | 4 | *(none)* |
|
|
| meta | 1 | `entity` |
|
|
|
|
One 60-node diagram doesn't fit on a screen and, worse, tempts you to fall
|
|
back on subgraph grouping instead of explicit edges for the flatter
|
|
domains — which is what an earlier version of this section did: most leaf
|
|
types were boxed together visually but had no drawn `-->` from `entity` at
|
|
all. Split by domain instead, every type below has an explicit parent
|
|
edge — nothing is implied by proximity alone.
|
|
|
|
### Layer overview
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity — abstract root\nlayer: meta"] --> INFRA["infrastructure layer\n40 types — physical, compute, network,\nstorage, software, external"]
|
|
entity --> GOV["governance layer\n7 types — identity domain"]
|
|
entity --> COG["cognition layer\n12 types"]
|
|
```
|
|
|
|
### Domain: physical (4 types, all flat)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> site
|
|
entity --> ups
|
|
entity --> sensor
|
|
entity --> peripheral
|
|
```
|
|
|
|
### Domain: compute (11 types — the deepest nesting in the Ontology)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> ce["compute-entity — abstract"]
|
|
entity --> hypervisor
|
|
ce --> machine["machine — abstract"]
|
|
ce --> vm
|
|
ce --> container["container — abstract"]
|
|
machine --> proxmoxhost["proxmox-host"]
|
|
machine --> standalone["standalone-server"]
|
|
machine --> workstation
|
|
machine --> appliance
|
|
container --> lxc
|
|
container --> dockercontainer["docker-container"]
|
|
```
|
|
|
|
### Domain: network (10 types)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> net["network — abstract"]
|
|
entity --> netiface["network-interface"]
|
|
entity --> dnszone["dns-zone"]
|
|
entity --> dnsrecord["dns-record"]
|
|
entity --> ingress["ingress-route"]
|
|
entity --> certificate
|
|
entity --> firewallrule["firewall-rule"]
|
|
net --> lan
|
|
net --> mesh
|
|
net --> vlan
|
|
```
|
|
|
|
### Domain: storage (4 types, all flat)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> storagepool["storage-pool"]
|
|
entity --> volume
|
|
entity --> backuptarget["backup-target"]
|
|
entity --> dataset
|
|
```
|
|
|
|
`storage-pool`/`volume`/`dataset` look like they should nest (a pool
|
|
*contains* volumes, a volume *holds* datasets) — they don't, in the type
|
|
hierarchy. That containment is a **relationship** (`contains`,
|
|
`holds-dataset`, [§11b](#11b-relationship-catalog)), not an `is-a` parent.
|
|
Worth stating plainly since the two are easy to conflate: `parent:` says
|
|
"this is a kind of that"; a relationship says "this instance is connected
|
|
to that instance." Storage is the domain where the difference is most
|
|
visible.
|
|
|
|
### Domain: software (7 types, all flat)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> service
|
|
entity --> application
|
|
entity --> configrepo["config-repo"]
|
|
entity --> deploypipeline["deploy-pipeline"]
|
|
entity --> packageset["package-set"]
|
|
entity --> cluster
|
|
entity --> composestack["compose-stack"]
|
|
```
|
|
|
|
### Domain: external (4 types, all flat)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> domainreg["domain-registration"]
|
|
entity --> cloudservice["cloud-service"]
|
|
entity --> isplink["isp-link"]
|
|
entity --> vendordep["vendor-dependency"]
|
|
```
|
|
|
|
### Domain: identity (governance layer, 7 types, all flat)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> person
|
|
entity --> agent
|
|
entity --> idp["identity-provider"]
|
|
entity --> account
|
|
entity --> secret
|
|
entity --> key
|
|
entity --> accessgrant["access-grant"]
|
|
```
|
|
|
|
### Domain: cognition (12 types, all flat — the domain the agent's own logic runs on)
|
|
|
|
```mermaid
|
|
graph TD
|
|
entity["entity"] --> check
|
|
entity --> signal
|
|
entity --> classification
|
|
entity --> execution
|
|
entity --> feedback
|
|
entity --> pattern
|
|
entity --> skill
|
|
entity --> approval
|
|
entity --> document
|
|
entity --> runbook
|
|
entity --> investigation
|
|
entity --> task["task — added after ADR-0014"]
|
|
```
|
|
|
|
Every one of the 60 types above is a direct or indirect child of `entity`;
|
|
none is disconnected. The full flat list — every type with its exact
|
|
`parent:` — lives in `seeds/ontology.yaml` directly; reproducing all 60
|
|
rows as a table here would duplicate this section rather than clarify it.
|
|
|
|
**Finding: `task` is new since ADR-0014.** ADR-0014 (2026-07-08) documents
|
|
56 entity types under a hierarchy diagram that does not include `task` —
|
|
four fewer than the 60 verified here, meaning more than just `task` was
|
|
added in the interim (`task` accounts for one of the four) —
|
|
[README.md §1.5](README.md#15-operational-concept--the-ooda-loop) and
|
|
[framework.md §2](framework.md#2-the-goal--system-and-model-made-concrete)
|
|
both describe the Task model as a 2026-07-11 addition
|
|
(`plans/done/2026-07-11-goal-oriented-chat-control-panel.md`), after
|
|
ADR-0014 was written. `task` is now entity type #60, `domain: cognition`,
|
|
`layer: cognition`, described in the seed as *"A goal-structured unit of
|
|
agent work — one chat/session elevated to a task with a plan, lifecycle
|
|
status, and outcome."* This is exactly what Holt's Ontology principle
|
|
predicts: a new concept in the Model
|
|
([the Task lifecycle View](README.md#34-functional-flow--the-task-lifecycle-f3f4f5-packaged-for-a-human))
|
|
required a new term in the Ontology before it could be modeled
|
|
consistently — and the term was in fact added, not left implicit.
|
|
|
|
## 11b. Relationship Catalog
|
|
|
|
**47 relationship types**, each with a fixed `source → target` type pair
|
|
and a cardinality. This is the complete, current catalog — not the
|
|
5 illustrative example-graphs ADR-0014 used to gesture at a smaller set.
|
|
|
|
**Finding: this catalog has grown since ADR-0014.** ADR-0014 (2026-07-08)
|
|
titles its equivalent section "The Edge Catalog (34 edges)." Verified
|
|
directly against `seeds/ontology.yaml` during this pass: **47** relationship
|
|
types exist today — 13 more than ADR-0014 recorded. This is expected drift
|
|
over an 8-day span of active development (the Task model alone plausibly
|
|
added `involves`; `part-of` supports the `compose-stack` grouping), not a
|
|
documentation error — ADR-0014 is a point-in-time record and is not edited
|
|
after acceptance, per this repo's own convention
|
|
([docs/adr/README.md](../adr/README.md)). It is reported here so nobody
|
|
treats ADR-0014's count as current.
|
|
|
|
| Relationship | Source → Target | Cardinality |
|
|
|---|---|---|
|
|
| `hosts` | machine → compute-entity | one-to-many |
|
|
| `runs-hypervisor` | machine → hypervisor | one-to-one |
|
|
| `member-of` | proxmox-host → cluster | many-to-one |
|
|
| `part-of` | docker-container → compose-stack | many-to-one |
|
|
| `provides` | compute-entity → service | one-to-many |
|
|
| `runs` | service → application | one-to-many |
|
|
| `configured-by` | entity → config-repo | many-to-one |
|
|
| `deploys-to` | deploy-pipeline → entity | many-to-one |
|
|
| `routes-to` | ingress-route → service | many-to-one |
|
|
| `secured-by` | ingress-route → identity-provider | many-to-one |
|
|
| `uses-certificate` | ingress-route → certificate | many-to-one |
|
|
| `authenticates-via` | service → identity-provider | many-to-one |
|
|
| `in-zone` | dns-record → dns-zone | many-to-one |
|
|
| `resolves-to` | dns-record → entity | many-to-one |
|
|
| `depends-on` | service → service | many-to-many |
|
|
| `connects-via` | compute-entity → network | many-to-many |
|
|
| `has-interface` | compute-entity → network-interface | one-to-many |
|
|
| `interface-on` | network-interface → network | many-to-one |
|
|
| `mounts` | compute-entity → volume | many-to-many |
|
|
| `stores-on` | compute-entity → storage-pool | many-to-many |
|
|
| `contains` | storage-pool → volume | one-to-many |
|
|
| `holds-dataset` | volume → dataset | one-to-many |
|
|
| `backs-up-to` | entity → backup-target | many-to-many |
|
|
| `powered-by` | machine → ups | many-to-one |
|
|
| `located-at` | machine → site | many-to-one |
|
|
| `registered-with` | domain-registration → vendor-dependency | many-to-one |
|
|
| `owns` | person → agent | one-to-many |
|
|
| `authenticates` | identity-provider → person | one-to-many |
|
|
| `holds-grant` | agent → access-grant | one-to-many |
|
|
| `grants` | access-grant → secret | many-to-one |
|
|
| `can-decrypt` | compute-entity → secret | many-to-many |
|
|
| `checks` | check → entity | many-to-one |
|
|
| `raises` | check → signal | one-to-many |
|
|
| `about` | entity → entity | many-to-many |
|
|
| `classifies` | classification → signal | many-to-one |
|
|
| `precedes` | classification → execution | one-to-one |
|
|
| `targets` | execution → entity | many-to-one |
|
|
| `requires-approval` | execution → approval | one-to-one |
|
|
| `performs` | agent → execution | one-to-many |
|
|
| `decides` | person → approval | one-to-many |
|
|
| `produces` | execution → feedback | one-to-one |
|
|
| `contributes-to` | feedback → pattern | many-to-many |
|
|
| `informs` | pattern → skill | many-to-one |
|
|
| `guides` | skill → classification | one-to-many |
|
|
| `documents` | document → entity | many-to-one |
|
|
| `involves` | task → entity | many-to-many |
|
|
| `procedure-for` | runbook → entity | many-to-many |
|
|
|
|
Grouped by theme, the same 47 rows read as five coherent sub-ontologies —
|
|
this is the grouping ADR-0014 used, now complete rather than illustrative:
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph Cognition["Cognition — the OODA edges"]
|
|
CK["check"] -->|raises| SG["signal"]
|
|
CK -->|checks| EN["entity"]
|
|
CL["classification"] -->|classifies| SG
|
|
CL -->|precedes| EX["execution"]
|
|
EX -->|targets| EN
|
|
EX -->|requires-approval| AP["approval"]
|
|
AG["agent"] -->|performs| EX
|
|
PR["person"] -->|decides| AP
|
|
EX -->|produces| FB["feedback"]
|
|
FB -->|contributes-to| PT["pattern"]
|
|
PT -->|informs| SK["skill"]
|
|
SK -->|guides| CL
|
|
TK["task"] -->|involves| EN
|
|
DC["document"] -->|documents| EN
|
|
RB["runbook"] -->|procedure-for| EN
|
|
end
|
|
```
|
|
|
|
```mermaid
|
|
graph LR
|
|
subgraph Governance["Governance — identity and access"]
|
|
P["person"] -->|owns| A["agent"]
|
|
IDP["identity-provider"] -->|authenticates| P
|
|
A -->|holds-grant| AG["access-grant"]
|
|
AG -->|grants| S["secret"]
|
|
CE["compute-entity"] -->|can-decrypt| S
|
|
end
|
|
```
|
|
|
|
The remaining three groups (Infrastructure Topology, Network, Service
|
|
Dependencies) are unchanged in shape from
|
|
[ADR-0014 §4](../adr/0014-entity-model.md) — that ADR's diagrams for those
|
|
three are still an accurate illustrative subset of the table above; only
|
|
the Cognition and Governance groups gained new edges (`involves`,
|
|
`part-of`) worth re-drawing.
|
|
|
|
## 11c. Lifecycle State Machines
|
|
|
|
Six lifecycles are formally registered in `seeds/ontology.yaml`'s
|
|
`lifecycles:` block, each a named state machine with `states`,
|
|
`default_state`, `terminal_states`, and per-transition `requires:` — named
|
|
checks that [internal/ontology](../../internal/ontology) implements in Go.
|
|
This is the mechanism, not just the diagram: a transition without a
|
|
satisfied `requires:` check is refused at the code level, for these six
|
|
types.
|
|
|
|
**Refinement to README.md's Behavior view.** The `execution` lifecycle as
|
|
registered here has **13 states**, including a `verifying` state distinct
|
|
from `executing`, and a recovery transition `timed_out → verifying`
|
|
("check if the command completed anyway" — the seed's own comment).
|
|
[README.md §6.2](README.md#62-execution-state-machine--schema-defined-convention-enforced)'s
|
|
Execution state diagram previously omitted `verifying` as a separate
|
|
state and has been corrected there to match; the diagram below is the
|
|
ontology-accurate version and the two now agree.
|
|
|
|
```mermaid
|
|
stateDiagram-v2
|
|
[*] --> proposed
|
|
proposed --> approved: operator-approval
|
|
proposed --> auto_approved: autonomy-allows
|
|
proposed --> denied
|
|
approved --> executing: approval-token-valid
|
|
approved --> expired: approval-ttl-elapsed
|
|
auto_approved --> executing
|
|
executing --> verified: verification-passed
|
|
executing --> failed
|
|
executing --> timed_out
|
|
executing --> cancelled: operator-abort
|
|
timed_out --> verifying: check if it finished anyway
|
|
verifying --> verified: verification-passed
|
|
verifying --> failed
|
|
failed --> rolled_back: rollback-procedure-exists
|
|
failed --> rollback_failed
|
|
verified --> [*]
|
|
denied --> [*]
|
|
expired --> [*]
|
|
cancelled --> [*]
|
|
rolled_back --> [*]
|
|
rollback_failed --> [*]
|
|
```
|
|
|
|
The other five, with their `requires:` gates named explicitly (abbreviated
|
|
where a transition has no requirement):
|
|
|
|
| Lifecycle | States | Terminal | Notable gated transition |
|
|
|---|---|---|---|
|
|
| `infrastructure` | planned, provisioning, active, migrating, failed, deprecated, destroyed | destroyed | `deprecated → destroyed` requires **five** checks at once: `backups-verified`, `secrets-revoked-and-rekeyed`, `ingress-and-dns-removed`, `no-inbound-edges`, `archaeology-entry` — the strictest single transition in the entire Ontology |
|
|
| `signal` | raised, acknowledged, acting, muted, resolved, failed | resolved | `acknowledged → acting` requires `classification-exists` — the formal link between Orient and Decide, real in the Ontology even though [Roadmap §9.2](README.md#92-code-real--dead-code--schema-only-matrix) finds the classifier that would create that classification is dead code |
|
|
| `approval` | pending, approved, denied, expired, revoked | denied, expired, revoked | `pending → approved` requires `token-verified`; `approved → revoked` requires `not-yet-executing` — you cannot revoke an approval whose action has already started |
|
|
| `pattern` | hypothesized, validated, active, deprecated, invalidated | deprecated, invalidated | `hypothesized → validated` requires `evidence-count-5plus` **and** `confidence-0.7plus` jointly — matches [ADR-0006](../adr/0006-learning-proposal-only.md)'s Wilson-bound description exactly; `validated → active` requires `operator-approval`, annotated in the seed itself as *"S4: never automatic"* |
|
|
| `skill` | drafted, tested, active, refined, failed, deprecated | deprecated | `tested → active` and `refined → active` both require `operator-approval` — a skill can be authored and tested autonomously but never self-promotes to active |
|
|
|
|
**Finding: `task` has no registered lifecycle.** The `task` entity type
|
|
(§11a) has a real, documented behavior —
|
|
[README.md §3.4](README.md#34-functional-flow--the-task-lifecycle-f3f4f5-packaged-for-a-human)
|
|
shows `planning → awaiting_approval → executing ⇄ awaiting_input → done`/`failed`
|
|
as a state diagram, and it is enforced in application code (the
|
|
`agent_sessions.status` column, checked in `cmd/nomos`). But
|
|
`seeds/ontology.yaml`'s `lifecycles:` block registers only the six
|
|
machines above — there is no `task:` entry alongside `infrastructure`,
|
|
`signal`, `execution`, `approval`, `pattern`, `skill`. Practically: the
|
|
five other governed types get their transition-gating for free from the
|
|
shared `internal/ontology` machinery (per named `requires:` checks); the
|
|
Task lifecycle is instead hand-coded in `cmd/nomos`'s Go logic, a
|
|
structurally different (and unaudited-by-the-shared-mechanism) enforcement
|
|
path for what is, in every other respect, a first-class Ontology citizen.
|
|
This is a gap worth a deliberate decision — register `task` formally, or
|
|
document explicitly that Task's lifecycle is intentionally
|
|
application-layer rather than Ontology-layer — not an oversight this
|
|
document is fixing by writing it down.
|
|
|
|
## 11d. Concrete Population
|
|
|
|
What's actually instantiated versus merely possible in the type system —
|
|
per [ADR-0014](../adr/0014-entity-model.md) §1, **not independently
|
|
re-counted against the live database during this pass** (that would
|
|
require DB access this documentation effort didn't use; the figures below
|
|
are ADR-0014's, dated 2026-07-08, and should be treated as illustrative of
|
|
shape rather than a current census):
|
|
|
|
| Type | Count (as of ADR-0014) | Examples |
|
|
|---|---|---|
|
|
| `lxc` | 19 | jellyfin, caddy, dns, gitea, nextcloud, matrix |
|
|
| `service` | 25 | caddy, authentik, dns, jellyfin, paperless, matrix |
|
|
| `ingress-route` | 21 | `*.hubris.network` |
|
|
| `config-repo` | 6 | caddy-conf, gitea-customizations, mule-image |
|
|
| `proxmox-host` | 2 | hubris, strong |
|
|
| `workstation` | 2 | mac-mini, republic-laptop |
|
|
| `standalone-server` | 1 | netbird-vps |
|
|
| `vm` | 2 | zimaos, haos |
|
|
| `storage-pool` | 3 | local-lvm-hubris, library-hubris, ludo-lvm |
|
|
| `volume` | 2 | library, media-local |
|
|
|
|
**Why this View matters despite being the least current one here:** it is
|
|
the check against over-abstraction Holt warns about (p. 35) — an Ontology
|
|
with 60 types and 47 relationships is only worth having if real entities
|
|
actually populate a meaningful fraction of it. 88 active entities across
|
|
roughly a dozen concrete types (out of 55 non-abstract types) is a
|
|
reasonable population for a homelab of this size; a future re-audit of this
|
|
specific View is a cheap, well-scoped follow-up (query `entities GROUP BY
|
|
type`) that this pass explicitly did not do, rather than silently assuming
|
|
ADR-0014's numbers still hold.
|
|
|
|
## Keeping this document current
|
|
|
|
Re-derive §11a-11c directly from `seeds/ontology.yaml` whenever it changes
|
|
— these three Views are transcriptions of that file's structure, not
|
|
independent judgment, so they go stale the moment the file changes and
|
|
nobody re-runs the extraction. §11d is the one View here that was already
|
|
known to be a point-in-time snapshot when written; re-verify it against
|
|
live DB state before relying on it for a capacity or audit decision.
|