Guidance for Claude Code working on the roboflow repository.
Roboflow: Distributed data transformation pipeline converting robotics bag/MCAP files to trainingable datasets (LeRobot format).
Key characteristics:
- Horizontal scaling for large dataset processing
- Schema-driven message translation (CDR, Protobuf, JSON)
- Zero-copy arena allocation for memory efficiency
- Cloud storage support (OSS, S3) for distributed workloads
The project uses a Cargo workspace with 7 crates:
| Crate | Purpose |
|---|---|
roboflow-core |
Error types, registry, values |
roboflow-storage |
S3, OSS, Local storage (always available) |
roboflow-executor |
Stage-based task executor for distributed pipelines |
roboflow-media |
Image and video encoding/decoding |
roboflow-dataset |
Dataset writers, sources (MCAP, bag), streaming converters |
roboflow-pipeline |
Pipeline execution and stages for dataset processing |
roboflow-distributed |
TiKV client, catalog, circuit breaker, worker coordination |
Import patterns:
- Use facade re-exports from
roboflow:use roboflow::{Robocodec, DatasetWriter, ...} - Or direct crate imports:
use roboflow_core::Result;
cargo build # Standard build
cargo test # All tests (including integration tests)
cargo test --test minio_integration_tests # MinIO integration tests onlyAll integration tests assume the following infrastructure is running:
| Service | Purpose | Docker Compose Service |
|---|---|---|
| MinIO | S3-compatible object storage | minio, minio-init |
| TiKV | Distributed KV storage | tikv |
| PD | TiKV placement driver | pd |
Start infrastructure before running tests:
docker compose up -dImportant: Integration tests should FAIL if infrastructure is not available, rather than being skipped. Do not use #[ignored] attributes for infrastructure-dependent tests. This ensures CI catches missing infrastructure early.
cargo fmt
cargo clippy --all-targets -- -D warningsFollow Conventional Commits format:
<type>: <description>
[optional body]
[optional footer]
Types:
| Type | Usage |
|---|---|
feat: |
New feature |
fix: |
Bug fix |
refactor: |
Code restructuring (no functional change) |
style: |
Code style changes (formatting, etc.) |
docs: |
Documentation only |
test: |
Adding or updating tests |
chore: |
Maintenance tasks (deps, config, etc.) |
perf: |
Performance improvements |
Examples:
feat: add distributed catalog for TiKV backend
fix: correct frame alignment in streaming converter
refactor: extract storage layer into separate crate
style: apply code formatting fixes
PR titles should follow the same Conventional Commits format as commit messages:
Do:
feat: add graceful shutdown handling for distributed workersfix: correct frame alignment in streaming converterdocs: update storage configuration examples
Don't:
[Phase 7.2] Add graceful shutdown← No internal project tagsAdding graceful shutdown← Use imperative moodAdded graceful shutdown← Use imperative mood
PR titles are public-facing and should be clean, descriptive, and free of internal project management artifacts (phase numbers, sprint tags, etc.).
Use descriptive branch names with prefixes:
| Prefix | Usage |
|---|---|
feat/ |
New features |
fix/ |
Bug fixes |
docs/ |
Documentation changes |
refactor/ |
Code restructuring |
test/ |
Test changes |
Examples:
feat/graceful-shutdownfix/frame-alignmentdocs/pr-conventionsrefactor/storage-layer
- Create feature branch from
main - Make commits following the convention above
- Push to remote
- Create PR with clear description and test checklist
- Ensure CI passes (
make lint && cargo test)
If your branch falls behind main:
git fetch origin main
git rebase origin/mainIf conflicts occur:
- Resolve conflicts in the affected files
git add <resolved-files>git rebase --continue
After successful rebase, force push:
git push --force-with-leaseNever use git push --force - always use --force-with-lease to prevent overwriting others' work.
If your PR is merged but you have local commits that weren't included (e.g., documentation updates made after the PR was created):
-
Switch to main and pull latest:
git checkout main && git pull -
Create a new branch for the orphaned changes:
git checkout -b new-branch-name
-
Cherry-pick the specific commit:
git cherry-pick <commit-hash>
-
Push and create a new PR.
Automated review tools (e.g., Greptile) may provide feedback on PRs. When addressing review comments:
- Read the comment carefully to understand the specific issue
- Make targeted fixes that address the exact concern
- Verify tests pass after changes
- Commit fixes with descriptive messages (e.g.,
fix: address Greptile review comments) - Push updates; the PR will automatically re-run checks
| Flag | Purpose |
|---|---|
distributed |
TiKV distributed coordination (always enabled) |
dataset-hdf5 |
HDF5 dataset format support |
dataset-parquet |
Parquet dataset format support |
dataset-depth |
Depth image support |
dataset-all |
All dataset formats |
cloud-storage |
S3/OSS cloud storage support |
gpu |
GPU compression (Linux only) |
jemalloc |
jemalloc allocator (Linux only) |
cli |
CLI support for binaries |
profiling |
Profiling support |
cpuid |
CPU-aware detection (x86_64 only) |
io-uring-io |
io_uring support (Linux 5.6+) |
Note: Storage (S3/OSS) and dataset formats (Parquet, LeRobot) are always available.
The project uses docker-compose for local development infrastructure:
docker compose up -d # Start all services (MinIO, TiKV, PD)
docker compose up -d minio minio-init # Start only MinIO
docker compose down # Stop all servicesServices:
| Service | Purpose | Ports |
|---|---|---|
| MinIO | S3-compatible object storage | 9000 (API), 9001 (Console) |
| TiKV | Distributed KV storage | 20160 |
| PD | TiKV placement driver | 2379, 2380 |
Pre-created buckets: roboflow-datasets, roboflow-raw, roboflow-temp
TiKV Host Configuration:
For TiKV tests to work from the host (not inside Docker), PD advertises its client URL as http://pd:2379. You must add this hostname to your /etc/hosts:
# Add to /etc/hosts
127.0.0.1 pdOr use the provided script:
./scripts/setup-hosts.sh # Requires sudoRunning E2E Tests:
# Start infrastructure
docker compose up -d
# Run all e2e tests (requires TiKV + MinIO)
cargo test --test batch_submission_e2e_test -- --nocapture
# Run MinIO-only tests (no TiKV required)
cargo test --test batch_minio_only_e2e_test -- --nocaptureVideo files follow the LeRobot v2.1 directory structure:
{prefix}/videos/chunk-{chunk:03d}/{camera}/episode_{episode:06d}.mp4
Example:
dataset/episode_001/videos/chunk-000/observation.images.cam_left/episode_000000.mp4
Key configuration:
ConcurrentEncoderConfig.key_prefix: Relative path within bucket (e.g.,"dataset/episode_001"), NOT a full S3 URLchunk_index: Typically 0 for single-episode datasetsepisode_index: Zero-padded episode number
Storagetrait uses&Path(notimpl AsRef<Path>) for dyn-compatibilityLocalStorageimplementsSeekableStoragefor seekable readsStorageFactorycreates backends from URL schemes (file://, s3://, oss://)- Environment variables for OSS:
OSS_ACCESS_KEY_ID,OSS_ACCESS_KEY_SECRET,OSS_ENDPOINT
- TOML config at
crates/roboflow-dataset/src/kps/config.rsfor topic mappings - v1.2 spec tests in
tests/kps_v12_tests.rsare authoritative - Writers use streaming patterns
FragmentEncoderrequires uniquecamera_idfor each camera to prevent temp file collisionsConcurrentEncoderConfig.key_prefixmust be a relative path (nots3://bucket/...)- Temp filenames include camera ID:
fragment_{pid}_{camera_id}_{counter}_{nonce}.mp4
- Always use arena allocation for message data (~22% overhead if skipped)
- Arena types are in
robocodec, imported viause robocodec::arena::Arena
- Remove unused code rather than marking it as
#[allow(dead_code)] - Compiler warnings about unused functions/imports indicate code that should be removed
- Keep the codebase lean - only add
#[allow(dead_code)]when explicitly requested
- When encountering unused variable warnings, think critically about whether:
- The variable should be removed entirely (if truly not needed)
- The variable should be used (if it serves a purpose that was overlooked)
- Prefixing with
_(e.g.,_fragment_index) suppresses the warning but may hide bugs - Example:
_fragment_indexin an upload function likely indicates the URL should include the fragment index to prevent overwrites
robocodec: https://github.com/archebase/robocodec (I/O, codecs, arena)