Author: Apoorv Gadiya, a contributor to Google Summer of Code 2026

GSoC project: Automated Compilation Verification Across All Concerto Code Generation Languages

Org: Accord Project · Repo: concerto-codegen · Mentors: Diana Lease, Ertugrul Karademir


1. Problem Statement

Concerto is the modeling language of the Accord Project. concerto-codegen generates code and schemas for 16 targets Java, TypeScript, Go, C#, Rust, GraphQL, Protobuf, OpenAPI, Avro, PlantUML, Markdown, and more.

Before this project, CI mostly answered one question:

Did the generator run without crashing?

It did not answer:

Is the generated output valid in its target environment?

That gap is dangerous. A Java visitor can omit an import. A Rust visitor can emit a structure that fails cargo check. A GraphQL visitor can produce SDL that graphql-js rejects. Snapshot tests may still pass, because they only compare text to yesterday’s text.

The GSoC idea (techdocs#483 -CodeGen Test Harness) and my proposal framed three concrete failures in the old setup:

  1. No language runtimes in CI -the pipeline lived in Node.js. There was no Java, Rust, Go, or .NET toolchain available to exercise generated code.
  2. No compilation / validation step -tests checked that files were created, not that they compiled or validated.
  3. No structured verification corpus -there was no shared, versioned set of .cto models driving generate-and-verify across targets in Docker/CI.

Goal: CI should fail immediately when generated output is invalid for any supported target -turning “hope it works” into “verified against tsc / javac / dotnet / cargo / protoc / …”.


2. Solution

The delivered solution matches the three pillars from the proposal, refined with mentor feedback and what actually scaled in the repo:

Pillar What shipped
Docker images Shared concerto-verify-base + one image per target with the right toolchain
GitHub Actions matrix verify-codegen.yml -one parallel job per target, fail-fast: false
Test corpus + local suite verification/corpus/ for Docker; test/verification/ Mocha suites for day-to-day runs

2.1 Overview

Two complementary layers live inside concerto-codegen (not a separate repo):

Layer Location Role
Local Mocha verification test/verification/ Generate from fixtures – run host tools. Fast while developing.
Docker verification verification/docker/ + CI Generate with Concerto CLI inside containers – verify with real compilers/linters.

All sixteen fromcto targets are covered:

Target Kind Verifier
TypeScript compiled tsc --noEmit
JSON Schema schema ajv compile
GraphQL schema graphql buildSchema
Protobuf schema protoc
C# compiled dotnet build
Rust compiled cargo check
Java compiled javac
Go compiled go build
OData schema xmllint --noout
Mermaid schema mermaid.parse
XML Schema schema xmllint --schema
OpenAPI schema redocly lint
Avro schema avro-tools idl
PlantUML schema plantuml -syntax
Vocabulary schema VocabularyManager
Markdown lint markdownlint

Design rules that kept the harness clear:

  1. Generate from a known corpus.
  2. Hand artifacts to a real external tool.
  3. Fail the job if that tool rejects them.
  4. Document intentional skips when a pre-existing visitor bug blocks a case -never weaken the verifier to fake green.

2.2 Approach

Work landed incrementally -one (or a few) targets per PR -so every merge expanded CI coverage without drowning reviewers.

Foundation (#246)
Shared base image, TypeScript + JSON Schema targets, docker-run.js, matrix workflow, local Mocha suites. The base image installs the Concerto CLI and symlinks this checkout’s codegen into the CLI’s module tree so PRs verify branch visitors, not a published npm copy.

Expand the matrix
Compiled targets (C#, Rust, Java, Go) then schema/doc targets (GraphQL, Protobuf, OData, Mermaid, XML Schema, OpenAPI, Avro, PlantUML, Vocabulary, Markdown), in reviewable batches.

Reproducibility
Centralize toolchain pins in verification/docker/versions.json and inject them as Docker build args locally and in CI.

This is same as in the proposal timeline (compiled targets first, schema batch later, matrix + docs last), adapted to how the community actually reviews: many small green PRs beats one giant dump.

2.3 Architecture

Layer 1 -Inputs
  verification/corpus/manifest.json + models/
  test/verification/cases.js (local)

Layer 2 -Images
  verification/docker/base/          # Node + CLI + branch codegen
  verification/docker/<target>/      # toolchain layered on base
  verification/docker/versions.json  # pinned images / npm / jars / crates

Layer 3 -Generate + verify
  run-case.sh - concerto compile --target X
  entrypoint.sh - tsc | javac | go build | protoc | …

Layer 4 -CI gate
  .github/workflows/verify-codegen.yml
  matrix per target, fail-fast: false, failure artifacts uploaded

Container flow

manifest.json
     │
     ▼
for each case - run-case.sh (generate)
     │
     ▼
if artifacts exist - target verifier
     │
     ▼
non-zero exit fails the CI job

Why Docker? Contributors should not need JDK 17, .NET 8, Rust, Go, protoc, and PlantUML JARs installed locally for CI to be trustworthy. Alpine-based images + GHA layer cache keep the matrix practical (the latency risk called out in the proposal).

Why symlink branch codegen? Without replacing nested @accordproject/concerto-codegen under the global CLI, verification would test published codegen and miss PR regressions.

2.4 Coding Examples

Entrypoint shape (every target)

Local builds always pass pinned versions

scripts/verification/docker-run.js (simplified)

Dockerfiles take ARGs -no hardcoded tool versions

Compiled targets: multi-stage toolchain copy (Go)

Generation (Node/CLI) and compilation (Go/Java/.NET/Rust) share one container -same pattern for C#, Java, and Rust.

CI matrix

External deps that the proposal flagged (Jackson for Java, serde/chrono for Rust, NuGet for C#) are installed inside those target images not left as “hope the runner has them.”

2.5 Version Pinning

Sixteen Dockerfiles with hard-coded typescript@5.7.2, JAR URLs, and SDK tags drift. Pins now live in one file:

verification/docker/versions.json

docker-versions.js emits --build-arg flags. Local docker-run.js and CI both consume them. Bump once; every image rebuild picks it up.

2.6 How to Run

npm run verify:docker                 # all Docker targets
npm run verify:docker:typescript      # one target
npm run test:verify                   # local Mocha verification

Failures leave artifacts under verification/work-<target>/ (also uploaded by CI). Maintainer docs: verification/VERIFICATION.md.


3. PRs

On accordproject/concerto-codegen by apoorv7g:

Verification harness

PR Title Status
#246 Docker verification foundation (TypeScript + JSON Schema) Merged
#256 GraphQL + Protobuf Docker targets Merged
#259 C# Docker verification Merged
#260 Rust Docker verification Merged
#262 GraphQL + Protobuf validation tests Merged
#263 Java Docker verification Merged
#266 OData Docker verification Merged
#268 Mermaid Docker verification Merged
#270 XML Schema, OpenAPI, Avro Docker targets Merged
#271 PlantUML + Vocabulary Docker targets Merged
#272 Go + Markdown Docker targets; version pinning Open
PR Title Status
#241 Fix HR fixture Category duplicate (unblocks tsc) Merged
#243 Level enum NONE + JSON Schema fixes Merged
#244 Early cross-format verification attempt Closed (superseded)
#252 Earlier C# verification attempt Closed (superseded by #259)
#258 TypeScript DateTimestring Open

Foundation work shipped in v5.1.0.


4. Issues Created

The harness is also a bug radar. Issues filed while standing up compile/schema checks:

Issue Title Status
#237 Generated TypeScript for HR models fails tsc Closed
#240 JSON Schema output fails Ajv Closed
#248 GraphQL output fails graphql-js Open
#249 Protobuf verification failures Open
#264 Rust output fails cargo check Open
#265 Java output fails javac Open

Few issues were created after this blog was written, for more information check concerto-codegen-issue-page

Also referenced: techdocs#483 (idea), #257 (DateTime typing – PR #258).


5. Learnings and Acknowledgement

Learnings

Open source is as much about communication and collaboration as it is about writing code. Keeping mentors updated, asking questions, and discussing ideas early made the development process much smoother.

I learned to spend more time understanding the architecture and debugging problems instead of jumping straight into writing code. That shift in mindset has made me a better engineer.

Git is simply a tool, and merge conflicts are a normal part of collaborative development. Don’t be scared of it.

Contributing to a mature open source project taught me the importance of writing maintainable code, accepting feedback, and iterating on solutions through code reviews.

Acknowledgement

This project would not have been possible without the incredible support of the Accord Project community.

A huge thank you to my mentors, Ertugrul Karademir and Diana Lease, for their constant guidance, thoughtful reviews, and encouragement throughout the program. Their feedback not only helped improve my contributions but also helped me grow as a developer.

A special thanks to Matt Roberts for always taking the time to provide detailed feedback and for going above and beyond to help whenever I needed guidance.

I’d also like to thank the reviewers, organization administrators, and everyone in the Accord Project community who took the time to review my pull requests, answer questions, and make new contributors feel welcome. It has been a pleasure contributing to such a collaborative and supportive open source community.

Finally, thank you to Google for continuing to organize Google Summer of Code and making opportunities like this possible. It has been an incredibly rewarding experience, and I look forward to continuing my contributions to the Accord Project even beyond GSoC.