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 taggedexpected-responseand 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/AFFECTSrelations), 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-m3locally (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_BYprocedures + manual sections; outputs numbered steps, each step citesDocSectionor is flaggedUNGROUNDED(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_idper 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
TimeoutPolicyon every agent node (wall-clock + idle); timeout clears the attempt’s writes and routes to the supervisor’s degraded path (emitinsufficient, 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)
- No agent output asserts a telemetry value not returned by a telemetry MCP call in this investigation.
- Every narrative claim carries a citation resolvable against KG/Qdrant (
vela/audit/citation_check.py). - No code path constructs or transmits telecommands. Grep-guarded in CI.
- 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) andairgap(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.
