Skip to content

Architecture — Project VELA

Version 0.1 · Consumed by AI coding agents. Keep authoritative.

0. System overview

---
config:
  look: handDrawn
---
flowchart LR
    subgraph Sources
        Y[Yamcs / replay harness] --> T
    end
    subgraph L1[Layer 1 — Detection]
        T[Telemetry ingest] --> F[Telecommand context filter]
        F --> D1[ECOD screen]
        F --> D2[GAT supervised model]
        D1 & D2 --> E[Event former + scorer]
    end
    subgraph L2[Layer 2 — Knowledge]
        KG[(Neo4j spacecraft graph)]
        VS[(Qdrant: manuals, FMECA, past anomalies)]
    end
    subgraph L3[Layer 3 — Reasoning: LangGraph]
        S[Supervisor] --> TR[Triage agent]
        S --> GI[Graph investigator]
        S --> HV[Hypothesis validator]
        S --> PD[Procedure drafter]
    end
    E -->|anomaly event| S
    GI <--> KG
    GI & HV & PD <--> VS
    PD --> OUT[Investigation report + drafted procedure + citations]
    OUT --> UI[Web console / shadow-mode log]

Layers communicate via the three MCP servers (see 04-MCP-SERVERS.md). Agents never touch Neo4j/Qdrant/telemetry stores directly — MCP tools only. This keeps the reasoning layer swappable and auditable.

1. Layer 1 — Detection

  • Telecommand context filter (build first, it’s the cheapest win). Join telemetry windows with telecommand history. A deviation within a configurable window after a related command (relatedness from the knowledge graph: Telecommand-[:AFFECTS]->TelemetryChannel) is tagged expected-response and suppressed from alarming, but logged. Rationale: ESA-ADB telemetry ships with an overabundance of telecommands, and command responses are a dominant false-positive source that most published methods ignore.
  • ECOD (unsupervised, from pyod): always-on cheap screen per channel. Chosen for competitive precision at very low compute in ESA-ADB follow-up studies.
  • GAT (supervised, PyTorch Geometric): multivariate model over channel graph; strongest published performer on full Mission 1 channel set. Train on ESA-ADB annotations. Channel graph edges seeded from Neo4j (MEASURES/AFFECTS relations), not learned from scratch.
  • Event former: merges point detections into events (min-duration, gap-merge), assigns severity from channel criticality in KG. Output schema:
{"event_id":"uuid","satellite":"esa-m1","channels":["ch_44","ch_45"],"t_start":"...","t_end":"...","score":0.91,"suppressed_as_command_response":false,"detector":"gat"}
  • Research track (not critical path): telemetry foundation-model embeddings (Chronos/MOMENT-class, RCD-style pretraining) for cold-start on new satellites. Isolated in vela/research/tsfm/. Never blocks demo.

2. Layer 2 — Knowledge

Neo4j ontology (authoritative node labels & relations)

Nodes: Satellite, Subsystem (EPS, AOCS, TTC, OBDH, TCS, PAYLOAD), Component, TelemetryChannel, Telecommand, FailureMode, AnomalyCase, Procedure, DocSection.

Relations:

(Satellite)-[:HAS_SUBSYSTEM]->(Subsystem)-[:HAS_COMPONENT]->(Component)
(Component)-[:MEASURED_BY]->(TelemetryChannel)
(Telecommand)-[:AFFECTS]->(TelemetryChannel|Component)
(Component)-[:HAS_FAILURE_MODE]->(FailureMode)
(FailureMode)-[:MANIFESTS_IN]->(TelemetryChannel)
(FailureMode)-[:MITIGATED_BY]->(Procedure)
(AnomalyCase)-[:INVOLVED]->(TelemetryChannel|Component)
(AnomalyCase)-[:RESOLVED_BY]->(Procedure)
(FailureMode|Procedure|Component)-[:DOCUMENTED_IN]->(DocSection)

Every DocSection carries {doc_id, section_ref, page, sha256} so citations are mechanically verifiable.

Graph temperatures & overlay write discipline (day-one, retrofit-deadly)

The graph has three temperatures. Design graph (from docs): quasi-static, batch-ingested, human-gated, hash-cited — spacecraft topology doesn’t change in orbit. Operational overlay (AnomalyCase, BOUND_TO confidences, component health states): the only evolving region — every write goes through a typed, invertible operator vocabulary from day one (OpenCase, LinkEvidence, UpdateBindingConfidence, ExpireHealthState, each with an exact inverse), applied atomically and recorded in the audit stream. Overturned hypotheses invert their case writes — no rebuild; health-state edges carry temporal validity windows and expire. (Pattern adapted from LiveGraph’s operator algebra, ACM CAIS 2026, applied only to the overlay.) Telemetry: never enters the graph; TSDB + windowed MCP queries only. v2 fleet correlation materializes live case state via event-sourced projection over this operator log — trigger unchanged per 10-SCALE.

Qdrant collections

  • manuals — chunked ops manuals/user guides (chunk ≈ 512 tokens, overlap 64), payload: {doc_id, section_ref, page, subsystem}.
  • anomaly_cases — past anomaly reports, one vector per case summary + per lesson-learned.
  • Embeddings: bge-m3 locally (air-gap parity); do not use a cloud-only embedder.

Ingestion pipeline (vela/ingest/)

PDF/markdown → docling parse → section chunker → (a) Qdrant upsert, (b) LLM-assisted entity/relation extraction → human-review TSV → Neo4j load. Graph writes always pass human review in v1; extraction precision matters more than recall.

3. Layer 3 — Reasoning (LangGraph)

State: InvestigationState {event, hypotheses[], evidence[], verdict, procedure_draft, citations[], audit_log[]}.

  • Supervisor — routes; hard caps: ≤ 12 tool calls per hypothesis, ≤ 3 open hypotheses.
  • Triage — severity/context summary; checks command-response suppression wasn’t wrong; cheap model (Qwen via Together/vLLM).
  • Graph investigator — deterministic-first: Cypher traversal from affected channels → components → failure modes; ranks by graph proximity + past-case similarity. LLM interprets, does not guess topology.
  • Hypothesis validator — for each candidate failure mode, checks expected telemetry signature (from FMECA/manual) against observed windows via telemetry MCP; supports/contradicts/insufficient. Reflection loop: contradicted → back to investigator with reason logged.
  • Procedure drafter — grounds on MITIGATED_BY procedures + manual sections; outputs numbered steps, each step cites DocSection or is flagged UNGROUNDED (blocks release of the draft).
  • Models: Claude (claude-sonnet-4-6 class) for investigator/validator/drafter; local Qwen for triage and air-gap mode. Model choice is config, not code.

Runtime commitments (LangGraph 1.2-class features — deliberate, not defaults)

  • Durable execution: Postgres checkpointer, stable thread_id per investigation. Investigations survive crashes and resume mid-graph; checkpoint time-travel replay is a product feature — forensic replay for anomaly review boards, exposed in the UI as “replay this investigation.”
  • interrupt() as the approval gate: procedure-draft release pauses the graph at a first-class interrupt; human approval resumes from that exact checkpoint. Low-confidence binding escalations use the same mechanism. Approval is graph structure, not a UI convention.
  • Per-node TimeoutPolicy on every agent node (wall-clock + idle); timeout clears the attempt’s writes and routes to the supervisor’s degraded path (emit insufficient, never hang).
  • Parallel hypothesis validation: top-N hypotheses validate concurrently as isolated subgraphs (context isolation — child keeps raw traversal, parent receives clean summaries); state merges via explicit reducers. Caps unchanged: ≤ 3 open hypotheses, ≤ 12 tool calls each.
  • Harness decision (evaluated July 2026): deepagents v0.5 rejected as core harness — its model-managed planning/spawning is the non-determinism our audit posture exists to eliminate; control flow stays a deterministic blueprint. Logged as a v2 candidate for an open-ended fleet-research agent. Claude Agent SDK rejected for core (single-vendor loop conflicts with airgap multi-model routing); remains our dev tooling.

Audit & safety invariants (enforced in code, tested in CI)

  1. No agent output asserts a telemetry value not returned by a telemetry MCP call in this investigation.
  2. Every narrative claim carries a citation resolvable against KG/Qdrant (vela/audit/citation_check.py).
  3. No code path constructs or transmits telecommands. Grep-guarded in CI.
  4. Full audit log (every tool call, every hypothesis transition) persisted per investigation — this is the anomaly-review-board artifact.

4. Integration & deployment

  • Yamcs adapter (vela/adapters/yamcs/): parameter subscription + archive replay + command history via Yamcs HTTP/WebSocket API. The replay harness feeds ESA-ADB CSVs through the same interface, so demo and live share one code path.
  • Modes: cloud (Anthropic API, managed Neo4j/Qdrant) and airgap (vLLM + Qwen, self-hosted stores, no egress). Mode is a single config flag; CI runs the golden-case eval in both.
  • UI: FastAPI + minimal React console — event list, investigation timeline, evidence panel with citations, procedure draft with accept/edit. Shadow mode = same UI, read-only against live feed.

5. Repo layout

vela/
adapters/yamcs/ detection/{filter,ecod,gat,events}/
ingest/ kg/{schema.cypher,loaders}/
mcp/{telemetry,knowledge,procedure}/
agents/{supervisor,triage,investigator,validator,drafter}/
audit/ api/ ui/
research/tsfm/ evals/ docs/

6. Storage & scale posture

Replay/demo telemetry: Parquet + DuckDB. Pilot telemetry: TimescaleDB behind the Yamcs adapter. tenant_id + satellite_id are mandatory in every contract, key, MCP request, and audit record from day one. OpenTelemetry tracing across MCP calls and agent nodes from Week 1 (the trace backs the audit log). All other scale decisions are trigger-gated — see 10-SCALE-ARCHITECTURE.md; building past a trigger early is treated as a defect.

7. Pinned stack

Python 3.12 · uv · LangGraph ≥ 0.4 · langchain-anthropic · neo4j 5.x · qdrant-client · PyTorch + torch-geometric · pyod · FastMCP · FastAPI · docling · pytest + ruff + mypy. Node 22 for UI. Docker compose for neo4j+qdrant+yamcs dev stack.