Orchestrata Ultimate › Plans › architecture-ontology-draft

Architecture Ontology — RATIFIED (implementation pending)

Source: plans/architecture-ontology-draft.md at 8a2cb42ad · last changed 2026-07-27 · first committed 2026-07-18 · 543 lines.

Prose pending.

Plan text

Architecture Ontology — RATIFIED (implementation pending)

STATUS: RATIFIED 2026-07-19 — all 8 open questions ruled (see §9); schema not yet applied. This is the ontology-before-graph-import step (project doctrine: never dump docs into a graph without a schema). It distills a node/relationship/property/constraint vocabulary for representing software & system architecture knowledge in the orchestrata operational Neo4j KG, from the system-design Claude Code plugin. It is written as an EXTENSION PROPOSAL compatible with src/orchestrata_ultimate/knowledge_graph/canonical_schema.py (SPEC-106 FR-001): every node type gets an explicit canonical identity key + MERGE semantics; every relationship gets direction + cardinality + property set; everything is project-scoped unless genuinely global. No ingestion code, no writer changes — this is the schema draft. Nothing here is committed or applied.


1. Purpose & sources

Purpose. Give the KG a first-class vocabulary for design/architecture knowledge — the systems we design, their components/interfaces/dataflows, the requirements (BDD) and quality attributes that drive them, the decisions (ADRs) and trade-offs, the patterns applied, the technology/cloud choices gated by requirements, capacity/cost models, deployment topology, and SLOs/alerts/risks — so that context injection, provenance queries, and cross-project search can reason over architecture the same way they already reason over code structure, specs, tests, and runtime events.

Method mine. The plugin's 6-phase workflow is the concept taxonomy: Phase 0 ground truth → Phase 1 BDD requirements + constraints → Phase 2 capacity & cost → Phase 3 high-level design (components/dataflow/data model/APIs/storage) → Phase 4 decisions & trade-offs (ADRs, patterns, technology choices) → Phase 5 cross-cutting / WAF review (failure modes, SLOs, security, regions) → Phase 6 deliverables (design doc / OpenSpec change / ADR log). Node families are derived from the material, not assumed.

Sources read (plugin root …/system-design/skills/system-design/):

# Source Concepts it contributed to this ontology
— README.md 6-phase workflow; "vendor-neutral in principle, Azure-concrete"; the notification-service worked-example framing; "name the requirement that forces it" gate.
— SKILL.md The 6 phases as concept families; principles (constraint-driven, simplest-thing, reversibility → one-way vs two-way door, fit-existing, commit-and-justify). Immutability of ADRs; "a decision is only justified by a requirement."
1 references/bdd.md Scenario (Given/When/Then); requirement-as-testable-scenario; ubiquitous language; edge/failure scenarios.
2 references/tradeoffs.md TechnologyChoice decision axes (datastore, sync-vs-async, consistency, caching, partitioning, service boundaries, idempotency, rate-limiting); "access patterns decide the store."
3 references/azure-patterns.md Pattern catalog (Retry, Circuit Breaker, Saga, CQRS, Queue-Based Load Leveling, Strangler Fig, …); WAFPillar (5 pillars); antipattern as review checklist; "patterns compose."
4 references/adr-template.md ArchitectureDecision (Nygard: Status/Context/Decision/Alternatives/Consequences); immutability & supersede lifecycle; when a decision warrants an ADR (one-way door).
5 references/design-doc-template.md DesignDoc deliverable structure; Goals/Non-goals; the decision index table; open questions.
6 references/openspec.md Change (OpenSpec change proposal); spec deltas ADDED/MODIFIED/REMOVED; requirement↔scenario mapping; design.md links to ADRs.
7 references/cost-estimation.md CostModel / CostLineItem; cost meters & driver taxonomy; billing model; TCO-not-unit-price; run-rate / 10× / top-3-drivers.
8 references/cloud-regions.md Region (+ Geography/AvailabilityZone); residency boundary; resiliency ladder (single→multi-zone→multi-region); RTO/RPO.
9 references/monitoring-alerting-slo.md ServiceLevelObjective (SLI/SLO/error budget), Alert (symptom/burn-rate), incident response.
10 references/observability.md Three signals (metrics/logs/traces); golden signals / RED; feeds SLO's SLI definitions.
11 references/messaging-servicebus-eventhubs.md Message-vs-event distinction; async Dataflow semantics (sync/async, ordering, replay, DLQ); broker as CloudService category.
12 references/databases-postgres-mysql-redis.md Datastore CloudService catalog entries (Postgres/MySQL/Redis Flexible Server); HA topology; feeds DataEntity store choice.
13 references/databricks-fabric-analytics.md Lakehouse/analytics CloudService entries; batch/streaming dataflow class.
14 references/azure-storage.md Object/file/block CloudService entries (Blob/ADLS/Files/Lustre); storage-family selection → DataEntity persistence.
15 references/ontology-stardog-ttl.md Competency-questions method (adopted for §2); OWL-to-infer vs SHACL-to-validate (informs the two-graph split note); confirms RDF/Stardog is the Memgraph/SPEC-105 side, not this Neo4j graph.
16 references/kubernetes-aks-helm.md ComputePlatform as a TechnologyChoice/CloudService (AKS/Container Apps/App Service/VMs); k8s objects vocabulary; Helm as deploy artifact.
17 references/gpu-partitioning-hpc.md GPU/MIG CloudService entries; capacity input for CapacityModel (GPU-slice sizing).
18 references/image-bakery.md Golden-image / immutable-infra as a Pattern + Artifact (image); rebuild-and-roll deploy model.
19 references/azure-networking.md Network topology (VNet/subnet/NSG/private endpoint/hub-spoke) → Deployment/Environment placement; trust boundaries for security review.
20 references/api-management-apim.md API gateway as a Gateway-Pattern implementation + CloudService; ApiContract policy surface; placement-behind-WAF.
21 references/identity-rbac-iam.md Two identity planes; trust boundaries; least-privilege — Phase-5 Security QualityAttribute + Risk inputs.
22 references/key-vault-secrets.md Secret-handling hierarchy; secrets as a security Risk/Constraint ("eliminate with managed identity first").
23 references/linux-storage-auth-azure.md Storage authn/authz layers → security Constraints on DataEntity access.
24 references/iac-terraform-bicep.md IaC layer boundary (provision infra vs deploy apps) → Deployment/Environment; IaC module as Artifact; Bicep-vs-Terraform an ADR.
25 references/cicd-github-actions-argocd.md CI/CD + GitOps push-vs-pull → Deployment delivery model; DORA metrics feed SLO/ops.
26 references/azure-icons.md Presentation-only (icon lookup/terms) — contributes no ontology node; noted for completeness (diagram rendering, not graph data).

Existing project conventions read: src/orchestrata_ultimate/knowledge_graph/canonical_schema.py (canonical NodeType/RelationshipType constants, CANONICAL_MERGE_KEYS, _CONSTRAINT_NAMES, CONSTRAINTS DDL, apply()/check()); plans/kg-schema-gaps.md (split-brain debt, unwired provenance edges, Component keyed on name, Task gaps); scripts/sync_architecture_to_neo4j.py (the current architecture writer — see §7 for coexistence/supersession).


2. Design principles (inherited from SPEC-106 + project doctrine)

  1. One canonical MERGE key per label, everywhere — mirrors CANONICAL_MERGE_KEYS. Every new label below declares exactly one identity key and one uniqueness constraint. No label gets two ways to be identified.
  2. Project-qualified identity is the default — the KG holds 100+ projects (multi-project invariant, plans/kg-project-scoped-identity.md). Any architecture entity whose natural id recurs across projects (a component named "Gateway", an ADR numbered 0001, an environment "prod") keys on id = "{project}:{natural_id}" and keeps the bare value as a display property (name / natural_id / slug), exactly the File/Directory/Package precedent. project here = Project.slug, and must not contain :.
  3. Global (unqualified) identity ONLY for genuinely cross-project reference data — the vendor/cloud service catalog (CloudService), the design/cloud pattern catalog (Pattern), the WAF pillars (WAFPillar), and cloud regions (Region) are shared reference vocabularies that mean the same thing in every project. These key on their global natural id (name / code). A project's own components, ADRs, requirements, SLOs, costs are project-scoped.
  4. Reuse/extend existing canonical types; never mint a duplicate — where a concept already has a canonical label (Architecture, Component, Pattern, Requirement/RequirementItem/Spec, Scenario, UserStory, Change, Artifact) this proposal REUSES it and says so per-type in §3. New labels are introduced only for concepts with no existing home.
  5. ADRs are append-only / immutable — an ArchitectureDecision's body (context/decision/alternatives/consequences) is frozen once status = Accepted. The only mutable field is status; reversal is a new node linked by SUPERSEDES. Neo4j has no per-property immutability, so enforcement is writer-side + a check()-style conformance guard (open question §9.1).
  6. Constraint-driven graph — the "name the requirement that forces it" gate is a first-class edge: TechnologyChoice -[:FORCED_BY]-> RequirementItem. A technology choice or component with no FORCED_BY/SATISFIES edge is the graph-level signal of an unjustified decision (the plugin's core review rule, made queryable).
  7. DDL is generated from the identity table — same discipline as canonical_schema.py: constraints are rendered CREATE CONSTRAINT <name> IF NOT EXISTS FOR (n:Label) REQUIRE n.<key> IS UNIQUE from a single {label: key} map, so DDL can never drift from identity (§5).
  8. This ontology targets the Neo4j OPERATIONAL graph only. RDF/OWL/TTL/SHACL and Stardog (from ontology-stardog-ttl.md) are a different stack — that is SPEC-105 domaingen/ + the Memgraph/Stardog semantic side, explicitly out of scope per kg-schema-gaps.md. See §9.4 for what, if anything, belongs on the Memgraph side.

3. Node types

Legend — Reuse: REUSE (existing canonical label, unchanged), REUSE+EXT (existing label, this proposal extends/re-scopes it — migration noted), NEW. Scope: project (id = {project}:…) or global.

Node label Purpose Canonical identity key Scope Key properties (typed) Provenance (phase / ref) Reuse-vs-new
Architecture Root of a designed system ("Notification Service"); the design-time root a Project owns. id = "{project}:{name}" project name:str, version:str, description:str, status:str Phase 0/3; sync_architecture_to_neo4j.py REUSE+EXT — canon Architecture currently keys on id; the writer keys on bare name. This re-scopes id to {project}:{name} and supersedes the writer's global-name node (§7).
Component A service/module with a single responsibility + its compute platform. id = "{project}:{name}" project name:str, responsibility:str, compute_platform:str, protocol:str, required:bool, boundary:str (owns-its-data?) Phase 3; SKILL Components; sync writer REUSE+EXT — canon Component keys on bare name (Tier C, 5 live nodes). This proposes project-qualifying it ({project}:{name}), following the Package/File/Directory precedent; migration + the runtime-vs-design overlap are the top reconciliation (§9.2).
ApiContract A key interface/endpoint/message contract a component exposes. id = "{project}:{component}:{name}" project name:str, kind:enum(rest|grpc|graphql|message|event), shape:str, sync:bool, version:str Phase 3 APIs/contracts; messaging; apim NEW
DataEntity A logical data-model entity + the access patterns it serves. id = "{project}:{name}" project name:str, access_patterns:list[str], consistency:enum(strong|eventual), store_kind:str, pii:bool Phase 3 data model; tradeoffs; storage/db refs NEW
Requirement A functional requirement (SHALL/MUST). id ("{project}:{natural}") project title:str, text:str, critical_path:bool Phase 1 REUSE (existing tier)
RequirementItem The atomic testable requirement clause code/design traces to. id project req_tag:str, text:str Phase 1; canon TRACES_TO target REUSE
Scenario A BDD Given/When/Then acceptance scenario. id ("{project}:{natural}") project title:str, given:str, when:str, then:str, is_edge_case:bool Phase 1 bdd.md REUSE (LABEL_SCENARIO exists)
UserStory A user story grouping requirements. id project title:str, as_a:str, i_want:str, so_that:str Phase 1 REUSE
QualityAttribute A non-functional target (NFR): latency p99, availability, RPS, consistency, budget. id = "{project}:{slug}" project name:str, metric:str, target:str, measured_as:str, pillar:str Phase 1 NFR + Phase 5 WAF NEW
WAFPillar Well-Architected pillar (review lens). name global name:enum(Reliability|Security|CostOptimization|OperationalExcellence|PerformanceEfficiency), concern:str Phase 5 azure-patterns.md NEW (reference data — 5 fixed nodes)
Constraint A hard constraint OR architecture invariant (compliance, budget, mandated tech, "Gateway never serves HTML"). id = "{project}:{slug}" project name:str, kind:enum(compliance|budget|mandated-tech|invariant|team|security), severity:enum(CRITICAL|HIGH|MEDIUM|LOW), description:str Phase 1 constraints; Phase 5 security; supersedes sync writer's Rule NEW — deliberately absorbs the sync writer's architecture-Rule nodes so the Rule label stays reserved for Prolog rules (§7, §9.3).
CapacityModel Order-of-magnitude capacity estimate (RPS, storage/yr, working set). id = "{project}:{name}" project name:str, peak_rps:int, avg_rps:int, read_write_ratio:str, storage_gb_yr:float, working_set_fits_memory:bool, binding_constraint:str Phase 2 NEW
CostModel A cost estimate for a design (run rate, 10× growth, drivers). id = "{project}:{name}" project name:str, run_rate_monthly:float, currency:str, at_10x_dominant_meter:str, top_drivers:list[str], priced_at:date, region:str Phase 2 cost-estimation.md NEW
CostLineItem One metered cost row (compute-hours, GB-month, egress…). id = "{project}:{costmodel}:{n}" project component:str, meter:str, quantity:float, unit_price:float, monthly:float, billing_model:enum(consumption|reservation|spot|hybrid) Phase 2 driver taxonomy NEW
Environment A deployment environment (dev/staging/prod). id = "{project}:{name}" project name:str, tier:enum(dev|test|staging|prod) Phase 6 iac/cicd NEW
Region A cloud region/zone placement target. code global code:str (e.g. eastus), display_name:str, geography:str, has_availability_zones:bool, sovereign:bool Phase 5 cloud-regions.md NEW (reference data)
Deployment A component deployed to an environment/region with a delivery model. id = "{project}:{component}:{environment}" project delivery_model:enum(push|pull-gitops), resiliency:enum(single-zone|multi-zone|multi-region), replicas:int, network_placement:str Phase 5/6 networking/cicd/regions NEW
ArchitectureDecision An ADR: one one-way-door decision, immutable body. id = "{project}:ADR-{NNNN}" project adr_number:int, title:str, status:enum(Proposed|Accepted|Deprecated|Superseded), date:date, deciders:list[str], context:str, decision:str, alternatives:str, consequences:str, one_way_door:bool Phase 4 adr-template.md NEW — DISTINCT from canon Decision (Tier C runtime/pipeline governance decision); do not conflate (§9.5).
TradeOff A named pillar-vs-pillar (or cost-vs-x) tension a decision accepts. id = "{project}:{slug}" project name:str, improves:str, at_cost_of:str, rationale:str Phase 4/5 WAF "name the trade-off" NEW
Risk An identified risk/threat to the design. id = "{project}:{slug}" project name:str, description:str, likelihood:enum(low|med|high), impact:enum(low|med|high), mitigation:str Phase 5 security/failure review NEW
FailureMode What happens when a specific dependency is slow/down. id = "{project}:{slug}" project name:str, trigger:str, blast_radius:str, degradation:str Phase 5 Reliability NEW
TechnologyChoice A committed technology decision for this system ("we use Postgres for X"). id = "{project}:{slug}" project name:str, category:str, rationale:str, reversibility:enum(one-way|two-way) Phase 4 tradeoffs.md NEW
CloudService A catalog entry: a concrete vendor service + its vendor-neutral kind. name global name:str (e.g. Azure Service Bus), vendor:str, neutral_kind:str (e.g. message-broker), category:str, docs_url:str Phases 3/4 all Azure refs NEW (reference data)
ServiceLevelObjective An SLO (SLI + target + window + error budget). id = "{project}:{component}:{name}" project name:str, sli:str, target:str (e.g. 99.9%), window:str, error_budget:str, burn_alert_windows:list[str] Phase 5 monitoring-alerting-slo.md NEW
Alert A symptom/burn-rate alert on an SLO with a runbook. id = "{project}:{slug}" project name:str, alert_on:str, tier:enum(page|ticket|log), burn_rate:str, runbook_url:str Phase 5 monitoring NEW
Pattern A named design/cloud/GoF/operational pattern or antipattern. name global name:str, category:enum(cloud|gof|operational|antipattern), problem_area:str, pillars:list[str], description:str, source:str Phase 4 azure-patterns.md; sync writer patterns REUSE+EXT — canon Pattern (Tier F, key name). This keeps name global and adds category/pillars/source; folds the sync writer's operational-protocol patterns in under category=operational.
Change An OpenSpec change proposal (proposal + deltas + design + tasks). id = "{project}:{change_id}" project change_id:str, title:str, status:enum(proposed|in-progress|archived), scope:str Phase 6 openspec.md REUSE+EXT — LABEL_CHANGE exists but has NO canonical key yet; this proposal gives it one (§5, §9.6).
Artifact A deliverable file: design doc, IaC module, Helm chart, golden image, diagram. id project name:str, artifact_type:enum(design-doc|adr|iac-module|helm-chart|image|diagram), path:str Phase 6 design-doc/iac/image-bakery REUSE (existing Artifact)

Count: 21 NEW · 4 REUSE+EXT · 6 REUSE = 31 node types touched (25 written by this ontology; 5 reference-data / global; the rest project-scoped).


4. Relationship types

Direction is from → to. Cardinality per canonical instance. Relationship names REUSE existing REL_* constants where one fits (marked); new names follow the SCREAMING_SNAKE convention of canonical_schema.py.

Relationship From → To Cardinality Properties Semantics (one sentence) Provenance Reuse
HAS_ARCHITECTURE Project → Architecture 1:N — A project owns one or more (versioned) system architectures. Phase 0 NEW
HAS_COMPONENT Architecture → Component 1:N — The architecture is composed of these components. Phase 3; sync writer REUSE (sync writer edge)
EXPOSES Component → ApiContract 1:N — The component exposes this interface/contract. Phase 3 NEW
DATA_FLOWS_TO Component → Component N:N protocol:str, sync:bool, critical_path:bool, ordering:str, via:str (broker/CloudService name) Data flows from one component to another on the traced path. Phase 3 dataflow; messaging NEW
DEPENDS_ON Component → Component N:N kind:enum(sync-rpc|async|read|shared-db) The component depends on another to function. Phase 3/5 NEW
PERSISTS_TO Component → DataEntity N:N mode:enum(read|write|read-write) The component reads/writes this data entity. Phase 3 NEW
RELATES_TO DataEntity → DataEntity N:N cardinality:str (1:N…), via:str Data-model relationship between entities. Phase 3 data model NEW
RUNS_ON Component → TechnologyChoice N:1 — The component runs on this compute-platform choice. Phase 3 k8s/compute NEW
HAS_SCENARIO RequirementItem → Scenario 1:N — The requirement is made testable by these BDD scenarios. Phase 1 bdd NEW
SATISFIES Component → RequirementItem N:N — The component satisfies (implements) this requirement. Phase 3/4 NEW
TRACES_TO (code) Function/Class → RequirementItem N:N — Existing code-→-requirement trace (reused as-is; design layer sits above it). canon REL_TRACES_TO REUSE
HAS_QUALITY_ATTRIBUTE RequirementItem → QualityAttribute 1:N — The requirement carries this NFR target. Phase 1 NFR NEW
MEASURED_ON QualityAttribute → Component N:N — The NFR is measured on this component. Phase 5 NEW
ADDRESSES_PILLAR QualityAttribute → WAFPillar N:1 — The NFR maps to a WAF pillar. Phase 5 NEW
MUST_FOLLOW Component → Constraint N:N — The component must obey this constraint/invariant. Phase 1/5; sync writer edge REUSE (sync writer edge; retargeted Rule→Constraint)
CONSTRAINS Constraint → Architecture N:1 — A system-wide constraint scopes the whole architecture. Phase 1 NEW
MODELED_BY Architecture → CapacityModel 1:N — Capacity estimate for the system. Phase 2 NEW
INFORMS CapacityModel → CostModel 1:1 — Capacity quantities are the sizing inputs for cost. Phase 2 NEW
HAS_LINE_ITEM CostModel → CostLineItem 1:N — The cost model breaks down into metered line items. Phase 2 NEW
METERS CostLineItem → CloudService N:1 — The line item meters this cloud service. Phase 2 NEW
DECIDES ArchitectureDecision → Component|DataEntity|TechnologyChoice N:N — The ADR governs this design element. Phase 4 NEW
CHOSE ArchitectureDecision → TechnologyChoice N:1 — The decision's chosen option. Phase 4 NEW
REJECTED ArchitectureDecision → CloudService|TechnologyChoice N:N reason:str The alternative that lost, with the concrete reason. Phase 4 alternatives NEW
JUSTIFIED_BY ArchitectureDecision → RequirementItem N:N — The decision is justified by a requirement (reuse of provenance edge). Phase 4; canon provenance REUSE
WEIGHS ArchitectureDecision → TradeOff N:N — The decision knowingly accepts this trade-off. Phase 4/5 NEW
SUPERSEDES ArchitectureDecision → ArchitectureDecision N:1 date:date A new ADR reverses an earlier one (immutability lifecycle). Phase 4; canon has unwired SUPERSEDES REUSE (previously-unwired edge, gaps #2)
SELECTS TechnologyChoice → CloudService N:1 — The choice binds a concrete cloud/vendor service. Phase 4 NEW
FORCED_BY TechnologyChoice → RequirementItem N:N — The "name the requirement that forces it" gate — a choice with no FORCED_BY is unjustified. Phase 4 core rule NEW
APPLIES ArchitectureDecision|Component → Pattern N:N — A design/cloud pattern applied here. Phase 4 NEW
IMPLEMENTS Component → Pattern N:N — A component concretely implements a pattern (reuse of canon IMPLEMENTS). Phase 4; canon REL_IMPLEMENTS REUSE
USES_PATTERN Component → Pattern N:N — Legacy synonym written by sync writer; converge onto APPLIES/IMPLEMENTS. sync writer REUSE (deprecate)
THREATENS Risk → Component|QualityAttribute N:N — The risk threatens this element. Phase 5 NEW
AFFECTS FailureMode → Component N:N — The failure mode affects this component. Phase 5 NEW
MITIGATED_BY Risk|FailureMode → Pattern|Constraint N:N — Mitigation applied to the risk/failure. Phase 5 NEW
HAS_SLO Component → ServiceLevelObjective 1:N — The component carries this SLO. Phase 5 NEW
MEASURES ServiceLevelObjective → QualityAttribute N:1 — The SLO operationalizes an NFR. Phase 5 NEW
ALERTS_ON Alert → ServiceLevelObjective N:1 — The alert fires on this SLO's burn. Phase 5 NEW
DEPLOYED_AS Component → Deployment 1:N — The component's deployment instance. Phase 6 NEW
IN_ENVIRONMENT Deployment → Environment N:1 — Where the deployment runs. Phase 6 NEW
IN_REGION Deployment → Region N:N — Region/zone placement. Phase 5 regions NEW
PROPOSES Change → ArchitectureDecision N:N — An OpenSpec change proposes these decisions. Phase 6 openspec NEW
MODIFIES Change → Requirement N:N delta:enum(ADDED|MODIFIED|REMOVED) The change's spec delta on a requirement. Phase 6 openspec NEW
DOCUMENTS Artifact → Architecture N:1 — A design doc documents the architecture. Phase 6 NEW
REFERENCES Artifact → ArchitectureDecision N:N — The design doc links out to ADRs. Phase 6 NEW

Count: ~38 new/retargeted + 6 reused = ~44 relationship types.


5. Constraints & indexes (what apply() would emit)

Following canonical_schema.py, DDL is generated from a single {label: key} map. New uniqueness constraints (Neo4j 5 syntax), each idempotent:

-- Project-scoped (id = "{project}:{natural}")
CREATE CONSTRAINT api_contract_id           IF NOT EXISTS FOR (n:ApiContract)          REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT data_entity_id            IF NOT EXISTS FOR (n:DataEntity)           REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT quality_attribute_id      IF NOT EXISTS FOR (n:QualityAttribute)     REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT constraint_id             IF NOT EXISTS FOR (n:Constraint)           REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT capacity_model_id         IF NOT EXISTS FOR (n:CapacityModel)        REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT cost_model_id             IF NOT EXISTS FOR (n:CostModel)            REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT cost_line_item_id         IF NOT EXISTS FOR (n:CostLineItem)         REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT environment_id            IF NOT EXISTS FOR (n:Environment)          REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT deployment_id             IF NOT EXISTS FOR (n:Deployment)           REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT architecture_decision_id  IF NOT EXISTS FOR (n:ArchitectureDecision) REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT tradeoff_id               IF NOT EXISTS FOR (n:TradeOff)             REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT risk_id                   IF NOT EXISTS FOR (n:Risk)                 REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT failure_mode_id           IF NOT EXISTS FOR (n:FailureMode)          REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT technology_choice_id      IF NOT EXISTS FOR (n:TechnologyChoice)     REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT slo_id                    IF NOT EXISTS FOR (n:ServiceLevelObjective) REQUIRE n.id  IS UNIQUE;
CREATE CONSTRAINT alert_id                  IF NOT EXISTS FOR (n:Alert)                REQUIRE n.id   IS UNIQUE;
CREATE CONSTRAINT change_id                 IF NOT EXISTS FOR (n:Change)               REQUIRE n.id   IS UNIQUE;  -- fills the LABEL_CHANGE gap

-- Global reference data
CREATE CONSTRAINT waf_pillar_name           IF NOT EXISTS FOR (n:WAFPillar)            REQUIRE n.name IS UNIQUE;
CREATE CONSTRAINT region_code               IF NOT EXISTS FOR (n:Region)               REQUIRE n.code IS UNIQUE;
CREATE CONSTRAINT cloud_service_name        IF NOT EXISTS FOR (n:CloudService)         REQUIRE n.name IS UNIQUE;
-- Pattern.name (pattern_name) already declared canon; extend properties only, no new constraint.

-- Non-identity lookup indexes (query-shaped)
CREATE INDEX architecture_decision_status   IF NOT EXISTS FOR (n:ArchitectureDecision) ON (n.status);
CREATE INDEX component_project              IF NOT EXISTS FOR (n:Component)             ON (n.project);
CREATE INDEX technology_choice_category     IF NOT EXISTS FOR (n:TechnologyChoice)     ON (n.category);
CREATE INDEX cloud_service_neutral_kind     IF NOT EXISTS FOR (n:CloudService)         ON (n.neutral_kind);
CREATE INDEX risk_impact                    IF NOT EXISTS FOR (n:Risk)                 ON (n.impact);

Re-scoping migrations (breaking — gated, not auto-applied): Architecture id name → "{project}:{name}"; Component id name → "{project}:{name}". These change existing canonical keys and touch the 5 live Component nodes + the sync writer's Architecture node, so they run through the existing migrate_neo4j_canonical.py + check() conformance path, never a blind apply() (open questions §9.2). The Change constraint is additive (label existed with no key). All other constraints are net-new labels — zero migration.


6. Core ontology diagram

Diagram projects-orchestrata-ultimate-plans-architecture-ontology-draft-maaa7f86f5c

Global reference-data nodes (WAFPillar, Region, CloudService, Pattern) are labeled (global); every other node is project-scoped.


7. Coexistence with scripts/sync_architecture_to_neo4j.py

The current writer creates a single global Architecture{name} plus Component{name}, Rule{name}, Pattern{name} and the edges HAS_COMPONENT, HAS_RULE, HAS_PATTERN, MUST_FOLLOW, USES_PATTERN. This proposal supersedes it as follows:

Until a converged writer exists (out of scope here — schema draft only), the two can coexist read-only: the writer keeps producing global nodes; nothing in this draft is applied. The converged writer is a separate follow-up that the user green-lights after this schema is ratified.


8. Worked example — a notification service (the README's example)

Project slug acme-notify. ~22 nodes/edges in Cypher-MERGE pseudocode, demonstrating every identity key. (Reference data — CloudService, Pattern, WAFPillar, Region — is MERGEd globally, unqualified.)

// --- reference data (global; shared across all 100+ projects) ---
MERGE (sb:CloudService {name: "Azure Service Bus"})
  SET sb.vendor="azure", sb.neutral_kind="message-broker", sb.category="messaging";
MERGE (pg:CloudService {name: "Azure Database for PostgreSQL - Flexible Server"})
  SET pg.vendor="azure", pg.neutral_kind="relational-db", pg.category="database";
MERGE (qll:Pattern {name: "Queue-Based Load Leveling"})
  SET qll.category="cloud", qll.problem_area="messaging", qll.pillars=["Reliability","Cost","Performance"];
MERGE (cc:Pattern {name: "Competing Consumers"}) SET cc.category="cloud", cc.problem_area="messaging";
MERGE (rel:WAFPillar {name: "Reliability"}) SET rel.concern="resiliency, availability, recovery";
MERGE (eu:Region {code: "westeurope"}) SET eu.geography="Europe", eu.has_availability_zones=true;

// --- project + system root (project-scoped) ---
MERGE (p:Project {slug: "acme-notify"});
MERGE (a:Architecture {id: "acme-notify:Notification Service"})
  SET a.name="Notification Service", a.version="1.0.0", a.status="draft";
MERGE (p)-[:HAS_ARCHITECTURE]->(a);

// --- components ---
MERGE (api:Component {id: "acme-notify:Notify API"})
  SET api.name="Notify API", api.responsibility="Accept + validate send requests", api.compute_platform="Container Apps";
MERGE (wrk:Component {id: "acme-notify:Dispatch Worker"})
  SET wrk.name="Dispatch Worker", wrk.responsibility="Drain queue, call providers", wrk.compute_platform="Container Apps";
MERGE (a)-[:HAS_COMPONENT]->(api);
MERGE (a)-[:HAS_COMPONENT]->(wrk);
MERGE (api)-[:DATA_FLOWS_TO {protocol:"AMQP", sync:false, critical_path:true, via:"Azure Service Bus"}]->(wrk);

// --- requirement (BDD) + NFR gate ---
MERGE (r:Requirement {id: "acme-notify:REQ-send"}) SET r.title="Send notification", r.critical_path=true;
MERGE (ri:RequirementItem {id: "acme-notify:REQ-send-01"}) SET ri.text="A send survives a provider outage of up to 1h";
MERGE (r)-[:HAS_ITEM]->(ri);
MERGE (sc:Scenario {id: "acme-notify:SC-send-outage"})
  SET sc.given="the email provider is down", sc.when="a send is requested", sc.then="the request is queued and retried within 1h", sc.is_edge_case=true;
MERGE (ri)-[:HAS_SCENARIO]->(sc);
MERGE (qa:QualityAttribute {id: "acme-notify:availability-send"})
  SET qa.name="Send availability", qa.metric="success-rate", qa.target="99.9% / 30d", qa.pillar="Reliability";
MERGE (ri)-[:HAS_QUALITY_ATTRIBUTE]->(qa);
MERGE (qa)-[:ADDRESSES_PILLAR]->(rel);
MERGE (api)-[:SATISFIES]->(ri);

// --- decision (ADR) + the "name the requirement" gate ---
MERGE (tc:TechnologyChoice {id: "acme-notify:async-via-servicebus"})
  SET tc.name="Async dispatch via Service Bus", tc.category="messaging", tc.reversibility="one-way";
MERGE (tc)-[:SELECTS]->(sb);
MERGE (tc)-[:FORCED_BY]->(ri);              // <-- the gate: choice justified by REQ-send-01
MERGE (adr:ArchitectureDecision {id: "acme-notify:ADR-0001"})
  SET adr.adr_number=1, adr.title="Async notifications via a durable queue",
      adr.status="Accepted", adr.date=date("2026-07-18"), adr.one_way_door=true,
      adr.decision="Dispatch through Azure Service Bus; API only enqueues",
      adr.consequences="Eventual consistency; consumers must be idempotent";
MERGE (adr)-[:CHOSE]->(tc);
MERGE (adr)-[:REJECTED {reason:"direct RPC cannot survive a 1h provider outage"}]->(pg);
MERGE (adr)-[:JUSTIFIED_BY]->(ri);
MERGE (adr)-[:APPLIES]->(qll);
MERGE (wrk)-[:IMPLEMENTS]->(cc);

// --- ops: SLO + alert + deployment ---
MERGE (slo:ServiceLevelObjective {id: "acme-notify:Notify API:send-success"})
  SET slo.sli="send success rate", slo.target="99.9%", slo.window="30d", slo.error_budget="0.1%";
MERGE (api)-[:HAS_SLO]->(slo);
MERGE (slo)-[:MEASURES]->(qa);
MERGE (al:Alert {id: "acme-notify:send-fastburn"})
  SET al.alert_on="2% budget in 1h", al.tier="page", al.runbook_url="docs/runbooks/send.md";
MERGE (al)-[:ALERTS_ON]->(slo);
MERGE (dep:Deployment {id: "acme-notify:Dispatch Worker:prod"}) SET dep.delivery_model="pull-gitops", dep.resiliency="multi-zone";
MERGE (wrk)-[:DEPLOYED_AS]->(dep);
MERGE (dep)-[:IN_REGION]->(eu);

Every project-scoped id is {project}:{natural}; every reference node is global. Re-running this MERGE block is idempotent (the identity keys guarantee it).


9. Open questions — RULED 2026-07-19

All eight questions were answered on 2026-07-19 (session 242). Rulings: (1) ADR immutability: BOTH — writer-side guard on Accepted-body SET and a body_sha conformance gate. (2) Component: UNIFY design-time with runtime-ownership Component and project-qualify to {project}:{name}; migrate the 5 live bare-name nodes via migrate_neo4j_canonical.py. (3) Fold sync-writer architecture rules into Constraint{kind:"invariant"}; Rule reserved for Prolog. (4) Semantic twin: YES as phase 2 — ADR/ DesignDoc prose chunked into Memgraph keyed by the Neo4j id; not blocking phase 1. (5) ArchitectureDecision and Decision stay two distinct labels. (6) CloudService: lazy creation + a curated ~30-service seed with neutral_kind; full-catalog ingestion rejected. (7) Confirmed: OpenSpec owns LABEL_CHANGE, id = "{project}:{change_id}". (8) Converged writer: BOTH, phased — rewrite sync_architecture_to_neo4j.py onto this ontology first, then extend pipeline/session KG writers; triggers = on-demand /system-design runs write immediately, and session start initiates a FULL architecture sync (user amendment 2026-07-19, superseding the earlier seed-only trigger): the whole design corpus is idempotently MERGEd on boot — cheap at this layer's scale (unlike the code graph, which stays incremental per SPEC-023) — run BACKGROUNDED so session start never blocks on Neo4j, with ruling 1's guard enforced inside it (never mutates an Accepted ADR body).

Original questions (as posed)

  1. ADR immutability enforcement. Neo4j has no per-property immutability. Do you want (a) writer-side guard only (the writer refuses to SET body props on an ArchitectureDecision whose status=Accepted), (b) a check()-style conformance gate that flags any Accepted-ADR body drift (comparing against a content hash property body_sha), or (c) both? Recommendation: (c) — hash on accept, gate in the conformance check, mirroring canonical_schema.check().
  2. Component re-scoping (breaking). Canon currently keys Component on bare name (5 live Tier-C nodes used by query_engine.volk_work_context). This proposal project-qualifies it to {project}:{name} (multi-project invariant, Package/File/Directory precedent). Also, is the design/architecture Component the SAME node as the runtime-ownership Component ((volk)-[:OWNS]->(:Component)) — i.e. one unified Component per system module — or two distinct labels? Recommendation: unify (one Component, project-qualified); the volk owns the same architectural component it works on. This is the single most consequential reconciliation and needs your sign-off before any migration.
  3. Rule label collision. The sync writer's architecture "rules" currently share the Rule label with Tier-A Prolog rules. This draft moves them to Constraint{kind:"invariant"}. Confirm that's the wanted resolution (vs. a new ArchitectureRule label). Recommendation: fold into Constraint — fewer labels, and they are design invariants.
  4. RDF/Memgraph vs Neo4j split. ontology-stardog-ttl.md describes OWL/TTL/ SHACL/SPARQL — that stack is SPEC-105 domaingen/ + Stardog, and the Memgraph HA graph holds the semantic/RAG chunks. This ontology is Neo4j-only and models design records as operational data. Nothing architectural here belongs on Memgraph except: if you want semantic retrieval over ADR/design-doc prose ("find decisions about queue back-pressure"), the ADR/DesignDoc text should also be chunked into the Memgraph semantic graph (as SemanticChunk), with the Neo4j ArchitectureDecision.id as the cross-graph key. Confirm whether ADR prose gets that semantic-layer twin, or stays Neo4j-only.

    Phase-2 implementation note (2026-07-19): the twin label is ArchitectureChunk (own vector index architecture_chunk_embedding_idx), NOT the SemanticChunk this parenthetical suggested: SemanticChunk sits in the ingest pruner's STALE_CHUNK_LABELS and its match predicate (has id, no chunk_id, no DERIVED_FROM) would have flagged live twin chunks for deletion under --prune-stale-labels --apply. Ruling 4's mandate (Neo4j id as the cross-graph key, same embedder/vector space as the code ingest) is unchanged. Implemented in semantic_graph/architecture_twin.py + scripts/sync_architecture_semantic_twin.py.

  5. ArchitectureDecision vs canon Decision. Kept DISTINCT here (Decision = Tier-C runtime/pipeline/governance decision; ArchitectureDecision = design ADR). Confirm you want two labels (recommended — different lifecycles, different immutability rules) rather than overloading Decision with a kind.
  6. CloudService catalog ingestion. Is the Azure service catalog (714 icons / the services named across the 26 refs) ingested as global reference data (one-time seed, versioned), or only created lazily when a project's TechnologyChoice SELECTS one? Recommendation: lazy + a small curated seed of the ~30 services the refs actually name, tagged with neutral_kind so vendor-neutral queries work; full-catalog ingestion is a separate call.
  7. Change canonical key. LABEL_CHANGE exists in canon with NO merge key. This draft assigns id = "{project}:{change_id}". Confirm the OpenSpec change is the intended owner of that label (vs. a generic git/spec change).
  8. Scope of the converged writer. This is a schema draft only. When ratified, do you want the follow-up to (a) rewrite sync_architecture_to_neo4j.py onto this ontology, (b) add architecture extraction to the pipeline/session KG writers, or (c) both — and on what trigger (session-start seed vs. on-demand /system-design run)?

10. Non-goals (explicit)



## Related

- [Plans](/projects/orchestrata-ultimate/plans/index)
- [Orchestrata Ultimate — project index](/projects/orchestrata-ultimate/index)
- [Workflow](/projects/orchestrata-ultimate/workflow)
- [Tasks](/projects/orchestrata-ultimate/tasks)