Repository navigation
The catalog (Phase 1): a queryable, append-only episode registry - #30
Merged
Merged
Conversation
Turns Forge from a per-dataset tool into a system of record: an append-only set
of Parquet tables (episodes, quality_scores) written with pyarrow and queried
with embedded DuckDB, on a local directory or an s3:// / gs:// bucket.
forge catalog init ./forge-catalog
forge ingest ./my_dataset --catalog ./forge-catalog
forge query "SELECT task, count(*) FROM episodes GROUP BY task" -c ./forge-catalog
forge catalog stats --catalog ./forge-catalog
New module forge/catalog/:
- schema.py — pyarrow schemas, SCHEMA_VERSION, SCORER_VERSION, validate_rows()
- writer.py — the only writer; atomic part-files + manifest-last commit protocol
- catalog.py — Catalog class: init/open (version guard), sql(), commit_batch,
stats(), views (episodes, quality_scores, v_latest_quality);
manifest-driven reads so queries never see a partial batch
- ingest.py — discover -> content-hash -> skip-if-exists -> register -> score
- cli.py — forge catalog init/stats, forge ingest, forge query
Design notes:
- Reuses existing internals — the readers behind `forge inspect` for metadata and
QualityAnalyzer.analyze_episode (the engine behind `forge quality`) for scoring.
No metadata/scoring logic reimplemented; no existing behavior changed.
- Idempotent: episodes keyed by an xxh3 content hash of their proprio stream, so
re-running an ingest over the same sources is a no-op.
- Crash-safe: each flush commits atomically (manifest written last); reads load
only manifested part-files.
- Cloud-native: catalog and/or source can live in S3/GCS (reuses forge.io fsspec;
DuckDB reads via the same fsspec client — works with real S3 and MinIO).
- New deps behind the [catalog] extra: duckdb, xxhash, pyarrow. The base CLI
never imports them; they load only when a catalog command runs.
Docs: README "The catalog" section, forge/catalog/README.md, a runnable
droid_100 example (forge/catalog/catalog_example_droid_100/, excluded from the
built package), the design RFC (docs/forge_data_engine_design.md), and a
CHANGELOG entry. Tests: tests/test_catalog.py (22 tests) — schema validation,
commit protocol + crash recovery, idempotent re-ingest, v_latest_quality version
selection, synthetic-dataset integration, and memory:// cloud. No existing tests
change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Turns Forge from a per-dataset tool into a system of record: an append-only set of Parquet tables —
episodes+quality_scores— written with pyarrow and queried with embedded DuckDB, on a local directory or ans3:///gs://bucket.This is Phase 1 of the Forge Data Engine design (embeddings/dedup/curation/snapshots are Phases 2–4).
New module
forge/catalog/schema.pySCHEMA_VERSION,SCORER_VERSION,validate_rows()(fails loudly)writer.pycatalog.pyCatalogclass:init/open(version guard),sql(),commit_batch,stats(), views; manifest-driven readsingest.pycli.pyforge catalog init/stats,forge ingest,forge queryDesign highlights
forge inspectfor metadata andQualityAnalyzer.analyze_episode(the engine behindforge quality) for scoring. No metadata/scoring logic reimplemented; no existing behavior changed. (No refactor was needed — both were already cleanly importable.)duckdb/xxhash/pyarrowlive behind the[catalog]extra;forge --helpnever imports them.Quality columns
quality_scoresmirrorsEpisodeQuality's flat metric names exactly:ldlj, sparc, dead_fraction, gripper_chatter_rate, joint_path_length, overall_saturation, mean_entropy, psd_high_fraction, state_action_consistency, static_fraction, jitter_ratio(+overall_score,flags), one row per(episode_id, scorer_version).Testing
tests/test_catalog.py— 22 tests, all passing: schema validation, manifest-last commit protocol + crash recovery, hash-based idempotent re-ingest,v_latest_qualitytwo-version selection, synthetic-dataset ingest→query→stats integration,memory://cloud, and CLI.mainare unchanged).lerobot/droid_100— local and fully-cloud (S3 source → S3 catalog against MinIO).Docs & example
forge/catalog/README.md(architecture, storage layout, commit protocol); CHANGELOG entry.forge/catalog/catalog_example_droid_100/— a small, ready-to-query example catalog built from droid_100, with its own README. Excluded from the built wheel/sdist (verified) but kept in the repo for demos.🤖 Generated with Claude Code