Skip to content

Add dispatch: cron orchestration for the ingest pipeline - #9

Open
CodyCBakerPhD wants to merge 5 commits into
mainfrom
claude/self-hosted-runner-cron-ihv7to
Open

Add dispatch: cron orchestration for the ingest pipeline#9
CodyCBakerPhD wants to merge 5 commits into
mainfrom
claude/self-hosted-runner-cron-ihv7to

Conversation

@CodyCBakerPhD

Copy link
Copy Markdown
Member

Summary

This PR introduces dispatch, a cron-driven orchestration system that automates the data ingest pipeline for registered labs. It coordinates downloading raw data from DANDI, detecting new sessions, running lab-specific conversion scripts, and uploading standardized output.

Key Changes

  • dispatch.py: Main orchestration script that:

    • Downloads incoming dandisets via dandi download
    • Discovers new sessions by globbing and comparing against a manifest
    • Detects conversion script changes via SHA256 hashing
    • Runs lab conversion commands with template variable substitution
    • Uploads standardized output via dandi upload
    • Supports --dry-run, --skip-download, --skip-upload, and --only <lab> flags
  • registry.py: Project registry loader that:

    • Parses projects.yaml to define lab configurations
    • Validates required fields and dandiset ID format (six digits)
    • Detects duplicate incoming dandiset IDs
    • Provides Project dataclass with script path resolution
  • state.py: Per-project manifest manager that:

    • Tracks converted sessions in .ingest_state.json
    • Records conversion script SHA256 for change detection
    • Implements session discovery (new vs. already-converted)
    • Provides file hashing utility for script versioning
  • projects.yaml: Registry configuration with Kemere lab as initial example, defining:

    • Incoming/standardized dandiset ID mappings
    • Session glob patterns for discovery
    • Conversion command templates with variable substitution
    • Optional overwrite flags for script-change-triggered reprocessing
  • Comprehensive test suite (dispatch/tests/):

    • Unit tests for session discovery, state persistence, and manifest round-tripping
    • Registry validation tests (required fields, ID format, duplicates)
    • Dispatch orchestration tests with mocked subprocess calls
    • Dry-run verification ensuring no filesystem/subprocess side effects
  • Documentation (README.md): Usage guide, layout explanation, credential setup, and project onboarding instructions

  • Environment declaration (dispatch/envs/pyproject.toml): Minimal dependencies (pyyaml, pytest for tests)

  • CI integration: Added Dispatch job to .github/workflows/test.yml to run unit tests on every commit

Notable Implementation Details

  • Idempotent design: repeated runs with no new sessions are cheap no-ops (except upload check)
  • Script change detection forces full reprocessing via overwrite_flag to handle breaking changes
  • Manifest lives alongside standardized output (excluded from DANDI upload via dotfile convention)
  • Command templating supports {repo_root}, {incoming_dir}, {standardized_dir} substitution
  • Graceful error handling: one project's failure doesn't block others; failures are logged and reported
  • Dry-run mode logs all actions without touching filesystem or running subprocesses

https://claude.ai/code/session_01NbcD48t2AYkB1YdLA2zQCm

…ngest

Adds dispatch/dispatch.py, a generic entrypoint intended to be run on a
schedule by the self-hosted runner in data-ingest-runner. Per registered
project (dispatch/projects.yaml) it:

  1. dandi-downloads the incoming dandiset from dandi.emberarchive.org
  2. diffs discovered sessions against a per-project manifest
     (<standardized_dir>/.ingest_state.json) to find unconverted sessions,
     also forcing a full reprocess if the conversion script's own contents
     (sha256) have changed since the manifest was last written
  3. runs the lab's conversion command over new/changed sessions
  4. dandi-uploads the standardized output

registry.py validates projects.yaml; state.py owns the manifest read/write.
Includes unit tests (registry validation, manifest round-trip, command
templating/dry-run with subprocess mocked out) wired into CI as a new
Dispatch job in test.yml. Kemere is registered as the first (and so far
only) project, with placeholder dandiset ids to be filled in once assigned.
@CodyCBakerPhD CodyCBakerPhD self-assigned this Aug 6, 2026
@CodyCBakerPhD
CodyCBakerPhD marked this pull request as ready for review August 6, 2026 20:14
projects.yaml -> projects.json (stdlib json, drops the pyyaml dependency).

Session discovery pulled out of the project registry into a new
dispatch/sessions.json + sessions.py: a single glob per project didn't
generalize (labs may need multiple raw subtrees or exclusions), and that
shape can evolve independently of a project's dandiset ids/conversion
command. sessions.json is keyed by lab, each entry an include-glob list
(unioned) plus an optional exclude-glob list (matched by basename or
path relative to the incoming project dir).

dispatch.py now loads both registries and looks up each project's session
spec by lab name (erroring per-project, not fatally, if one is missing).
Tests updated/added for both files; all 21 pass, ruff clean.
Comment thread README.md Outdated
CodyCBakerPhD and others added 3 commits August 6, 2026 16:45
Signed-off-by: Cody Baker <51133164+CodyCBakerPhD@users.noreply.github.com>
Registers the ai_generated pytest marker repo-wide in the root
pyproject.toml and applies it (via module-level pytestmark) to every test
in dispatch/tests/, since that whole suite was AI-authored this session.

Adds CLAUDE.md documenting the rule for future AI-agent sessions: mark
AI-generated tests with this marker (module-level pytestmark by default,
per-function @pytest.mark.ai_generated when a module mixes AI-generated
and human-written tests), and register new suites against the same root
pytest config rather than adding a competing one.
setuptools needed an explicit (empty) [tool.setuptools] py-modules = []
block to build a code-less, dependency-only package; hatchling doesn't
need a placeholder section for that -- 'bypass-selection = true' under
[tool.hatch.build.targets.wheel] says outright that the wheel ships no
files, which is what py-modules = [] was standing in for anyway.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants