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-designClaude Code plugin. It is written as an EXTENSION PROPOSAL compatible withsrc/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)
- 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. - 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 onid = "{project}:{natural_id}"and keeps the bare value as a display property (name/natural_id/slug), exactly the File/Directory/Package precedent.projecthere =Project.slug, and must not contain:. - 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. - 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.
- ADRs are append-only / immutable — an
ArchitectureDecision's body (context/decision/alternatives/consequences) is frozen oncestatus = Accepted. The only mutable field isstatus; reversal is a new node linked bySUPERSEDES. Neo4j has no per-property immutability, so enforcement is writer-side + acheck()-style conformance guard (open question §9.1). - 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 noFORCED_BY/SATISFIESedge is the graph-level signal of an unjustified decision (the plugin's core review rule, made queryable). - DDL is generated from the identity table — same discipline as
canonical_schema.py: constraints are renderedCREATE CONSTRAINT <name> IF NOT EXISTS FOR (n:Label) REQUIRE n.<key> IS UNIQUEfrom a single{label: key}map, so DDL can never drift from identity (§5). - 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-105domaingen/+ the Memgraph/Stardog semantic side, explicitly out of scope perkg-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
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:
Architecture— re-scope id to{project}:{name}; the writer's globalname-keyed node ("Orchestrata Ultimate") becomes theorchestrata-ultimateproject's Architecture. Migration throughmigrate_neo4j_canonical.py.Component— re-scope to{project}:{name}; the writer's 5 nodes (VizInt UI, Gateway, Kernel, Redis, Neo4j) get theorchestrata-ultimateprefix and gainresponsibility/compute_platform(already present asresponsibility).Rule→Constraint— the writer's architecture "rules" ("Gateway never serves HTML/JS/CSS", "Never hardcode service IPs") are design invariants, not Prolog rules. They MUST NOT keep theRulelabel (which the canon reserves for Tier-A Prolog rules keyed onid). They move toConstraint{kind:"invariant"}; theMUST_FOLLOWedge is retargetedComponent → Constraint. This resolves a live label collision (§9.3).Pattern— the writer's operational protocols (PID file, Health check, Graceful shutdown…) are kept, re-taggedcategory:"operational", alongside the cloud/GoF catalog entries. EdgeUSES_PATTERNis deprecated in favour ofIMPLEMENTS(converge later; keep the synonym until writers migrate).HAS_RULE/HAS_PATTERN(Architecture→Rule/Pattern) — replaced by Architecture→Constraint viaCONSTRAINSand Component→Pattern viaIMPLEMENTS.
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_shaconformance gate. (2) Component: UNIFY design-time with runtime-ownership Component and project-qualify to{project}:{name}; migrate the 5 live bare-name nodes viamigrate_neo4j_canonical.py. (3) Fold sync-writer architecture rules intoConstraint{kind:"invariant"};Rulereserved 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)ArchitectureDecisionandDecisionstay two distinct labels. (6) CloudService: lazy creation + a curated ~30-service seed withneutral_kind; full-catalog ingestion rejected. (7) Confirmed: OpenSpec ownsLABEL_CHANGE,id = "{project}:{change_id}". (8) Converged writer: BOTH, phased — rewritesync_architecture_to_neo4j.pyonto this ontology first, then extend pipeline/session KG writers; triggers = on-demand/system-designruns 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)
- ADR immutability enforcement. Neo4j has no per-property immutability. Do
you want (a) writer-side guard only (the writer refuses to
SETbody props on anArchitectureDecisionwhosestatus=Accepted), (b) acheck()-style conformance gate that flags any Accepted-ADR body drift (comparing against a content hash propertybody_sha), or (c) both? Recommendation: (c) — hash on accept, gate in the conformance check, mirroringcanonical_schema.check(). Componentre-scoping (breaking). Canon currently keysComponenton barename(5 live Tier-C nodes used byquery_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.Rulelabel collision. The sync writer's architecture "rules" currently share theRulelabel with Tier-A Prolog rules. This draft moves them toConstraint{kind:"invariant"}. Confirm that's the wanted resolution (vs. a newArchitectureRulelabel). Recommendation: fold intoConstraint— fewer labels, and they are design invariants.- RDF/Memgraph vs Neo4j split.
ontology-stardog-ttl.mddescribes OWL/TTL/ SHACL/SPARQL — that stack is SPEC-105domaingen/+ 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 (asSemanticChunk), with the Neo4jArchitectureDecision.idas 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 indexarchitecture_chunk_embedding_idx), NOT theSemanticChunkthis parenthetical suggested:SemanticChunksits in the ingest pruner'sSTALE_CHUNK_LABELSand its match predicate (hasid, nochunk_id, noDERIVED_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 insemantic_graph/architecture_twin.py+scripts/sync_architecture_semantic_twin.py. ArchitectureDecisionvs canonDecision. 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 overloadingDecisionwith akind.CloudServicecatalog 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'sTechnologyChoiceSELECTSone? Recommendation: lazy + a small curated seed of the ~30 services the refs actually name, tagged withneutral_kindso vendor-neutral queries work; full-catalog ingestion is a separate call.Changecanonical key.LABEL_CHANGEexists in canon with NO merge key. This draft assignsid = "{project}:{change_id}". Confirm the OpenSpec change is the intended owner of that label (vs. a generic git/spec change).- 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.pyonto 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-designrun)?
10. Non-goals (explicit)
- No ingestion code, no writer changes. This is the schema draft that must
precede import.
sync_architecture_to_neo4j.pyis untouched; no converged writer is written here. - No
apply()run / no migration executed. The DDL in §5 and the migrations in §7 are proposals; nothing is applied to any live graph. - No RDF/OWL/SHACL/Stardog artifacts. That is SPEC-105
domaingen/+ the Memgraph/Stardog side, out of scope (§9.4). - No Memgraph semantic-graph changes. Cross-graph twinning of ADR prose (§9.4) is flagged as an option, not designed here.
- No canonical_schema.py edit. The reuse/extend/new decisions are a proposal for the user to ratify; folding them into the canon is a separate, gated step.
- Not a design-doc/ADR authoring tool. This models architecture as graph data; the plugin's authoring workflow is unchanged.
## 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)