Skip to main content

How to add a proto schema

All inter-layer contracts in Aucert are defined as Protobuf schemas in proto/. This guide covers adding new messages, modifying existing ones, and regenerating code.

Prerequisites​

  • protoc compiler installed
  • Bazel configured (or standalone protoc plugins)

Current schemas​

FilePurpose
proto/pipeline.protoMCPMessage envelope, TestRunRequest/Result, BugReport
proto/knowledge-graph.protoKGQuery, KGResponse, KGNode, KGEdge

Steps​

Step 1: Edit or create the .proto file​

// proto/pipeline.proto
syntax = "proto3";
package aucert.pipeline;
option java_package = "com.aucert.platform.domain.pipeline.model";

// Add new message types below existing ones
message TestSuiteConfig {
string suite_id = 1;
string name = 2;
repeated string test_scope = 3;
map<string, string> parameters = 4;
}

Step 2: Follow backward-compatibility rules​

danger

Breaking changes to proto schemas can silently corrupt data. Follow these rules strictly.

Always safe:

  • Add new fields (with new field numbers)
  • Add new message types
  • Add new enum values
  • Add new oneof members

Never do:

  • Change an existing field number
  • Change a field's type
  • Remove a field that's in use (mark as reserved instead)
  • Rename a field (wire format uses numbers, not names, but it breaks generated code)
// Correct way to remove a field
message MyMessage {
reserved 3; // Field 3 was "old_field", removed in v2
reserved "old_field"; // Prevent accidental reuse of the name
string new_field = 4;
}

Step 3: Regenerate code​

# Using Bazel (preferred)
bazel build //proto:all

# Generated output goes to schemas/generated/
# This creates code for: Kotlin, TypeScript, Python
caution

NEVER edit files in schemas/generated/. They are overwritten on every build. If you need custom behavior, wrap the generated types in your own code.

Step 4: Verify generated code compiles​

# Kotlin
cd backend/platform && ./gradlew compileKotlin

# TypeScript
cd frontend/apps/console && pnpm tsc --noEmit

# Python
cd ml && uv run python -c "from schemas.generated import pipeline_pb2"

Step 5: Update consuming code​

After regenerating, update any code that uses the modified messages:

  1. Backend — Update services/handlers in backend/platform/
  2. Frontend — Update API clients in frontend/apps/console/
  3. ML — Update any ML pipeline consumers in ml/

Step 6: Update context files​

If your schema change introduces a new domain concept:

  • Add to .context/GLOSSARY.md
  • Update .context/ARCHITECTURE.md if it affects the pipeline

MCP message envelope​

All inter-layer data flows through the MCPMessage envelope:

message MCPMessage {
string task_id = 1;
string source_layer = 2; // e.g., "generation"
string target_layer = 3; // e.g., "execution"
bytes payload = 4; // Serialized inner message
map<string, string> context_snapshot = 5;
double confidence_score = 6;
string trace_id = 7;
int64 timestamp_ms = 8;
}

The payload field contains the serialized inner message (e.g., TestRunRequest). Layers deserialize only the messages they understand.

Troubleshooting​

Bazel build fails with "field number already used"

Two fields share the same number. Check your .proto file for duplicate field numbers. Each field in a message must have a unique number.

Generated Kotlin code has compilation errors

Ensure java_package option matches the expected package structure:

option java_package = "com.aucert.platform.domain.pipeline.model";

After fixing, clean and regenerate: bazel clean && bazel build //proto:all

TypeScript types don't match the proto schema

Check that the TypeScript proto plugin is configured correctly in the Bazel BUILD file. Regenerate and verify:

bazel build //proto:all
cd frontend/apps/console && pnpm tsc --noEmit

What's next​