Architecture · A2-A4 · Frontends, IR & Contracts

Different languages in. One system-wide picture out.

A frontend understands one language. Intermediate Representation (IR) is the shared vocabulary every frontend translates into. That lets CUPID reuse the same signals, graphs, and reports—and creates the foundation for finding contracts that break between languages.

HONEST BOUNDARYCurrent: Python, C, and C++ lower into CUPID IR; TypeScript is recognized but not lowered yet. Designed next: TypeScript, Go, YAML contract facts, and the ContractGraph on this page. A new frontend enables the work; it does not create cross-language warnings by itself.
01 · The translation layer

Each language gets a translator. The rest of CUPID stays reusable.

Python AST, Tree-sitter, and a native compiler parser do not need to agree. Each frontend handles its own syntax, then lowers it into the same IR facts: functions, calls, imports, classes, locations, and capabilities.

DIFFERENT LANGUAGES

Source code arrives

Every language has different grammar and semantics.

Python CC++TypeScript next Go designed
➞
FRONTENDS · THE TRANSLATORS

Understand local syntax

Use the best parser for that language: Python ast, Tree-sitter, libclang, or a future TypeScript/Go parser. C and C++ reuse a shared C-family walker where their structures match.

➞
IR · INTERMEDIATE REPRESENTATION

CUPID's common vocabulary

A Python call and a C++ call look different in source, but both become a language-neutral CALL fact with a caller, callee, arguments, and location.

➞
ONE REUSABLE PIPELINE

Analyze once

Signals judge local code. Graphs connect files and facts. Reports rank and explain the result. They consume IR, not language-specific syntax.

Signals GraphsReports
Plain English: frontends are translators; IR is the common language. Adding a language means teaching one translator—not rebuilding every analysis rule.
02 · Autar today

The product is one flow, but its contracts live in five worlds.

Autar is a useful example because each component can pass its own compiler and tests while the connection to the next component is already broken.

TYPESCRIPT

Starts analysis

The API starts RepositoryAnalysisWorkflow by name and chooses a Temporal queue.

PYTHON + TEMPORAL

Runs the workflow

Python defines and registers the workflow, then schedules Iris activities and child workflows.

YAML + PYTHON

Executes knowledge

KB scan steps produce named outputs that qualifiers, scoring, profiles, and reports consume.

PYTHON ↔ GO

Executes safely

The Python Hermes client sends handwritten JSON to Go's /exec sandbox gateway.

PYTHON → TYPESCRIPT

Ships the result

Iris produces versioned JSON; a TypeScript transformer turns it into product-facing analysis.

The risk is not one bad file. It is a chain of separately handwritten contracts connecting languages, processes, and deployment units.

03 · The designed ContractGraph

Turn invisible agreements into explicit, joinable edges.

Each frontend emits facts independently. The graph linker joins them using stable contract keys such as a workflow name, task queue, HTTP method and path, JSON field, or KB output name.

CONTRACT 01 · TEMPORAL

A workflow is a name plus a route

TypeScript starts a Python workflow by string. The Python worker must define it, register it, and listen on the same task queue.

TypeScript caller
  STARTS_WORKFLOW ▶ Python workflow
  USES_QUEUE ▶ Temporal task queue

What CUPID could catch: a renamed workflow, missing worker registration, mismatched queue, or incompatible input that every language accepts on its own.

CONTRACT 02 · HERMES

An HTTP payload is a shared API

Python handwrites the request to Go's /exec route. Both sides depend on the same field names without one generated definition.

Python client
  CALLS_ENDPOINT ▶ Go route
  WRITES_FIELD ▶ JSON contract

What CUPID could catch: Go changing exit to exit_code while its own tests still pass and Python fails only at runtime.

CONTRACT 03 · IRIS OUTPUT

Product data crosses Python into TypeScript

Iris produces versioned analysis JSON. A TypeScript transformer reads those fields and turns them into the result shown by the product.

Python Iris producer
  PRODUCES_FIELD ▶ Analysis output
TypeScript transformer
  READS_FIELD ▶ Analysis output

What CUPID could catch: a field keeping the same name but changing shape, then silently becoming an empty analysis instead of a visible crash.

CONTRACT 04 · KNOWLEDGE BASE

YAML outputs behave like internal APIs

A scan step names an output. Later qualifiers, reports, and profiles refer to that name, so the YAML is an executable dataflow rather than passive configuration.

YAML scan step
  PRODUCES_OUTPUT ▶ KB output
Qualifier / report / profile
  REFERENCES_OUTPUT ▶ KB output

What CUPID could catch: a misspelled reference, use before definition, or same-entity overwrite that quietly turns a detection into an empty value.

STEP 01

Extract

Each frontend records what its language produces, consumes, defines, calls, and registers.

STEP 02

Join

Contract keys connect facts that live in different files, languages, services, or configuration.

STEP 03

Score

Risk rises with fan-in, criticality, missing schema/tests, and silent fallback; existing protection lowers it.

STEP 04

Explain

The report names the boundary, affected consumers, current safeguards, missing safeguard, and safest next action.

04 · What this finds in Autar

Four risks, ranked by customer impact—not by how unusual the syntax looks.

A useful architecture warning must account for safeguards that already exist. CUPID should explain the missing join, not pretend the whole boundary is unprotected.

1

ANALYSIS_OUTPUT_SCHEMA_DRIFT_RISK

STRONGEST AUTAR FINDING
PROTECTED TODAY

Output versions, v1/v2/v3 transformers, transformer tests, and validation after the conversion.

MISSING JOIN · WHY IT MATTERS

The raw Python result is still read through dynamic TypeScript records, and transform failure can return an empty analysis. A broken contract may look like a successful scan with no findings, which damages product trust.

2

RPC_CONTRACT_WITHOUT_SHARED_SCHEMA

VERY STRONG
PROTECTED TODAY

Go and Python each have useful local tests. Hermes also protects workspace paths, stream termination, and exit behavior.

MISSING JOIN · WHY IT MATTERS

There is no Hermes-specific OpenAPI, protobuf, generated client, or cross-language consumer/provider contract test. CUPID can identify the exact fields and callers affected before an integration fails.

3

CROSS_LANGUAGE_WORKFLOW_CONTRACT_RISK

VALID · PARTLY MITIGATED
PROTECTED TODAY

Autar has a Python activity registry, explicit worker registration, task-queue routing, and TypeScript orchestrator tests.

MISSING JOIN · WHY IT MATTERS

No static check joins the TypeScript start call to the Python definition, worker registration, queue, and input/output shape as one chain. The value is complete-chain validation, not another warning about strings.

4

DECLARATIVE_CONFIG_CONTRACT_RISK

VALID · NARROWER SCOPE
PROTECTED TODAY

KB schema validation already checks structure, duplicate slugs, and dangling relationship targets.

MISSING JOIN · WHY IT MATTERS

It does not fully prove output define-before-use, same-entity uniqueness, or every output reference in the dataflow. CUPID can show which detections and reports silently disappear when an output breaks.

Why output schema drift ranks first: a Temporal mismatch usually fails loudly. Iris output drift can fail quietly: Python produces a rich result, TypeScript cannot interpret it, and fallback returns a valid-looking empty analysis. Silent success is harder to detect and more damaging to customer trust than an obvious crash.
THE VALUE IN ONE SENTENCE
Frontends understand each language. IR understands the whole codebase. ContractGraph understands what can break between them.

Compilers protect code inside one language. CUPID's architecture layer can protect workflow names, HTTP payloads, analysis schemas, and declarative outputs that cross those boundaries.

Back to system architecture Locate frontends in the code map Locate IR in the code map