Systematic Codebase Exploration: Mapping the cuzk Proving Engine's Architecture

Introduction

In the middle of a deep-dive investigation into the SUPRASEAL_C2 Groth16 proof generation pipeline for Filecoin's Proof-of-Replication (PoRep) system, a critical sub-session was launched with a straightforward but ambitious mandate: read every source file in the extern/cuzk/ workspace and return a comprehensive architectural summary. The cuzk proving engine—a Rust-based daemon being developed as part of the Curio Filecoin mining stack—represents a new approach to orchestrating GPU-accelerated proof generation. Understanding its current state was a prerequisite for the optimization work that would follow.

This article examines the complete sub-session, spanning six messages that together constitute a masterclass in systematic codebase exploration. From the initial exploration mandate ([msg 0]) through the methodical file-by-file reading ([msg 1], [msg 2], [msg 3], [msg 4]) to the final comprehensive synthesis ([msg 5]), the session reveals not just the state of the cuzk codebase but a replicable methodology for understanding complex distributed systems. The articles [1], [2], [3], [4], [5], and [6] each examined individual messages in this session in depth; this article synthesizes the entire chunk, tracing the arc from reconnaissance to architectural documentation.

The Exploration Mandate: Setting the Stage

The session began with a meticulously crafted request from the user ([msg 0]). The user enumerated exactly 20 files spanning the entire workspace—from Cargo.toml manifests through core library modules, protobuf definitions, server implementations, daemon entry points, benchmark tools, and configuration examples—and asked for a comprehensive summary covering six specific aspects of the architecture:

  1. The current Engine architecture (how jobs flow from gRPC → scheduler → worker → prover)
  2. The current prover.rs: what's implemented vs stubbed for each proof type
  3. The current scheduler: how priority works, what's missing for GPU affinity
  4. The current config: what's already configurable
  5. The protobuf API: all message types and RPCs
  6. Any TODOs or stubs marked in the code Additionally, the user requested the full content of four key files—prover.rs, engine.rs, scheduler.rs, and types.rs—identifying them as the files most likely to be modified in subsequent work. This request was not a casual inquiry; it was a carefully scoped reconnaissance mission designed to establish a shared understanding between the orchestrator and the subagent before any modifications were made [3]. The user's file list revealed several deliberate decisions. The 20 files were ordered architecturally, not alphabetically: workspace configuration first, then the core library, then the protobuf definitions, then the server, daemon, bench tool, and finally the example configuration. This dependency-aware ordering reflected a top-down decomposition of the system. The six summary questions mirrored this traversal, asking about the engine architecture (the big picture), then drilling into the prover (the heart of the computation), then the scheduler (the orchestrator), then the config (the knobs), then the API (the contract), and finally the TODOs (the unfinished business) [3]. The user also made a deliberate choice about what to exclude. The workspace contained more than 20 files—there were test scripts, build artifacts, a rust-toolchain.toml, and a Cargo.lock—but the user judged these as less critical for architectural understanding. By constraining the reading list, the user ensured the subagent would not get lost in irrelevant details [3].

The Reading Process: Methodical Data Acquisition

The assistant's response in [msg 1] began with a strategic announcement: "I'll read all the files systematically. Let me start by reading them all in parallel batches." This opening statement encoded several reasoning decisions [5].

First, the assistant recognized the need for systematic ordering. The files formed a dependency graph, and reading them in a logical order—workspace manifest first, then core library, then protocol definitions—mirrored the architectural layering and helped build understanding incrementally.

Second, the assistant chose breadth over depth initially. The first action was to read the directory listing of the workspace root, providing a map of the territory before diving into individual files. This reconnaissance revealed that the workspace contained six active crates (cuzk-bench, cuzk-core, cuzk-daemon, cuzk-ffi, cuzk-proto, cuzk-server) plus supporting files like rust-toolchain.toml, test-e2e.sh, and cuzk.example.toml [5].

Third, the assistant read files in dependency order. Within cuzk-core, the assistant read the module declaration (lib.rs) first to understand the module structure, then read each module file in the order they appeared: config, types, engine, scheduler, prover. This mirrored the dependency chain—config defined configuration structures with no dependencies on other modules, types defined common types used everywhere, engine depended on both, scheduler depended on types, and prover depended on types [5].

The assistant split the reading across two messages ([msg 1] and [msg 2]), covering the core engine files and proto Cargo.toml in the first batch, then the remaining infrastructure—protobuf build configuration, proto definitions, gRPC server implementation, daemon entry point, benchmarking tool, and example configuration—in the second batch. This split reflected an understanding of the tool's limitations and the user's cognitive load: reading 20 files in one burst would produce an overwhelming wall of text [4].

Beyond the Explicit List: Proactive Discovery

After completing the user's enumerated list, the assistant took a critical step beyond the literal instructions. In [msg 3], the assistant announced: "Let me also check for the cuzk-ffi directory and any other files that might be relevant." This was a moment of methodological significance—the assistant recognized that a complete understanding required looking beyond the explicit list [2].

Three specific decisions were visible here:

Checking the cuzk-ffi directory. The workspace Cargo.toml (read in [msg 1]) contained a commented-out line: # "cuzk-ffi", # Future: C ABI for Go embedding. The assistant noticed this and decided to check whether the directory existed, finding that it contained a src/ subdirectory. This was significant: it told the team that someone had started scaffolding the FFI layer, even though it was not yet compiled as part of the workspace [2].

Reading test-e2e.sh. This file was not in the user's list. The assistant discovered it through the initial directory listing and recognized its value for understanding operational context—what environment variables were required, what ports were used, what cleanup procedures were expected. The script revealed that the daemon used port 9826 for testing, relied on FIL_PROOFS_PARAMETER_CACHE pointing to /var/tmp/filecoin-proof-parameters, and started both a daemon and a bench tool [2].

Reading rust-toolchain.toml. This small file specified the Rust toolchain version (1.86.0) and components (clippy, rustfmt). While seemingly trivial, it was crucial for reproducibility, telling any developer working on this project exactly which Rust version to use [2].

The follow-up in [msg 4] revealed that the cuzk-ffi/src directory was empty—zero entries. This seemingly unremarkable discovery was in fact a quiet revelation. The empty directory was not a void; it was a footprint. It confirmed that the FFI bridge was unimplemented, that the current integration boundary was the gRPC server, and that the project's roadmap included an FFI layer but had not prioritized it. The empty directory served as a boundary marker for the investigation itself: everything inside the active crates was implemented (to varying degrees); everything in cuzk-ffi was not [1].

The Comprehensive Synthesis: From Files to Architecture

The culmination of the exploration was [msg 5], where the assistant returned a comprehensive summary that synthesized the 20+ files into a coherent architectural document. This was not a simple concatenation of file contents; it was a structured analysis organized into eight sections [6]:

1. Workspace Structure

The assistant mapped the workspace's five active crates plus the empty cuzk-ffi placeholder, providing a high-level view of the project's organization.

2. Engine Architecture: Job Flow

The assistant traced a proof request through five stages:

3. Prover: Implemented vs Stubbed

The analysis revealed that only one proof path was fully implemented: prove_porep_c2(). This was the most computationally intensive proof—the "Proof of Replication Commit Phase 2"—and its implementation involved parsing a Curio-specific JSON format, base64-decoding the phase 1 output, constructing a prover ID via LEB128 varint encoding, and calling seal::seal_commit_phase2().

Three proof functions were stubbed:

4. Scheduler: Priority and Missing Features

The scheduler's core data structure was a BinaryHeap<QueueEntry> with a custom Ord implementation that prioritized by enum value (Critical=4 > High=3 > Normal=2 > Low=1) and used FIFO within priority tiers. The assistant cataloged what was missing for multi-GPU support:

5. Configuration: Declared vs Used

One of the most revealing sections was the catalog of configuration fields that were declared but unused:

6. Protobuf API: Complete Reference

The assistant provided a table of all eight RPCs in the ProvingEngine service, documenting which were implemented and which were stubs. The pattern was clear: RPCs critical for the basic proof flow (Submit, Await, Prove) were fully implemented, while management RPCs (Cancel, Evict) were stubs.

The protobuf schema revealed several forward-looking design decisions:

7. TODOs and Stubs

The assistant identified six stubs and one explicit TODO, along with documented Phase 1+ plans embedded in comments throughout the codebase. These comments served as a lightweight specification for future development [6].

8. Full File Contents

Finally, the assistant reproduced the complete source of the four key files—types.rs (190 lines), engine.rs (405 lines), scheduler.rs (113 lines), and prover.rs (282 lines)—providing a permanent reference for subsequent work [6].

The Knowledge Produced

The sub-session produced several types of knowledge that would be essential for the optimization work ahead:

Architectural Documentation: A comprehensive map of the cuzk engine's architecture, including the flow from gRPC to prover and the responsibilities of each component. This was not available in any single source file; it had to be synthesized from the entire codebase.

Implementation Status: A clear inventory of what was implemented (PoRep C2) and what was stubbed (WinningPoSt, WindowPoSt, SnapDeals). This directly informed the next phase of development.

Gap Analysis: A catalog of missing features—multi-GPU support, batch collection, SRS eviction, cancellation—with references to where they should be implemented.

Configuration Reference: A complete listing of all configuration parameters with their usage status, revealing the gap between the designed configuration schema and the Phase 0 implementation.

API Reference: A complete table of all RPCs, message types, and their fields, documenting the contract between the daemon and its clients.

Phase Roadmap: Implicit documentation of the Phase 0 → Phase 1+ transition, with specific features assigned to each phase.

The Methodology: Lessons for Codebase Exploration

The sub-session demonstrated a replicable methodology for systematic codebase exploration that is worth distilling:

1. Start with reconnaissance. Before reading any individual file, read the directory listing to understand the project's structure. This provides a map of the territory and reveals files that may not have been in the initial request.

2. Read in dependency order. Files form a dependency graph. Reading them in topological order—from foundational types upward through layers of abstraction—builds understanding incrementally and ensures each file's dependencies have been seen first.

3. Batch strategically. Split large reading tasks into digestible chunks. The assistant split 20 files across two messages, creating natural boundaries that reduced cognitive load.

4. Go beyond the explicit list. The most valuable discoveries often lie outside the requested scope. The assistant's proactive checks of cuzk-ffi, test-e2e.sh, and rust-toolchain.toml revealed critical architectural information that the user had not explicitly requested.

5. Synthesize, don't just enumerate. The final summary was not a concatenation of file contents but a structured analysis that extracted the architecture, categorized the implementation status, compared configuration against usage, and organized TODOs into a roadmap.

6. Document limitations honestly. Throughout the summary, the assistant was careful to document what was not implemented, what was stubbed, and what assumptions were made. This honesty set accurate expectations and provided a clear target for future work.

Conclusion

The sub-session spanning messages 0 through 5 represents a complete arc of systematic codebase exploration: from the initial mandate, through methodical data acquisition, proactive discovery beyond the explicit scope, and finally to comprehensive synthesis. The result was a detailed architectural document that captured the exact state of the cuzk proving engine at a critical juncture in its development.

The cuzk engine, as revealed by this exploration, was a system in its infancy but with a well-conceived architecture. Phase 0 had established the complete proof flow for the most important proof type (PoRep C2), built a priority-based scheduler, designed a comprehensive gRPC API, and created a configuration schema that anticipated future features. The stubs and TODOs provided a clear roadmap for Phase 1: implement the remaining proof types, add multi-GPU support with affinity-aware scheduling, build SRS management, and eventually create a C ABI for direct Go embedding.

For the broader SUPRASEAL_C2 investigation, this exploration established the ground truth. The optimization proposals that would follow—Sequential Partition Synthesis, Persistent Prover Daemon, Cross-Sector Batching—would all be grounded in the architectural understanding gained from this systematic reading. As the old engineering adage goes: you cannot optimize what you do not understand. The cuzk codebase, through this exploration, was now understood.