Testing setup:
- Add pyproject.toml with mypy and pytest configuration
- Add requirements-dev.txt for development dependencies
- Create tests/ directory with test fixtures
- Add 17 unit tests for DAG transformation pipeline
Type annotations:
- Add app/types.py with TypedDict definitions for node configs
- Add typed helper functions: transform_node, build_input_name_mapping,
bind_inputs, prepare_dag_for_execution
- Refactor run_recipe to use the new typed helpers
Regression tests for today's bugs:
- test_effect_cid_key_not_effect_hash: Verifies CID uses 'cid' key
- test_source_cid_binding_persists: Verifies bound CIDs in final DAG
Run tests with: pytest tests/ -v
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The EFFECT executor looks for config["cid"] or config["hash"],
but the transformation was setting config["effect_hash"].
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix all registry lookups to use "cid" instead of "hash" key
- app/routers/recipes.py: asset and effect resolution
- tasks/execute_sexp.py: effect config lookups
- server_legacy.py references (now deleted)
- Prefer IPFS CID over local hash in cache operations
- cache_service.py: import_from_ipfs, upload_content
- orchestrate.py: plan caching
- legacy_tasks.py: node hash tracking
Remove ~7800 lines of dead code:
- server_legacy.py: replaced by modular app/ structure
- tasks/*_cid.py: unused refactoring only imported by server_legacy
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Refactor to use IPFS CID as the primary content identifier:
- Update database schema: content_hash -> cid, output_hash -> output_cid
- Update all services, routers, and tasks to use cid terminology
- Update HTML templates to display CID instead of hash
- Update cache_manager parameter names
- Update README documentation
This completes the transition to CID-only content addressing.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Variable inputs can now be referenced by:
- Node ID (e.g., source_4)
- Config name (e.g., "Second Video")
- Snake case (e.g., second_video, second-video)
- Node name from def binding
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Instead of falling through to YAML parsing (which gives confusing errors),
return a clear message that artdag.sexp is required but not installed.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add format detection that correctly handles ; comments
- Import artdag.sexp parser/compiler with YAML fallback
- Add execute_step_sexp and run_plan_sexp Celery tasks
- Update recipe upload to handle both S-expr and YAML formats
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract username from actor_id format (@user@server)
- Set total_steps and executed from recipe nodes
- Use recipe name for display instead of hash
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Check multiple locations for nodes: dag.nodes, recipe.nodes, pipeline, steps
- Add dagre layout libraries for cytoscape DAG visualization
- Fix inputs parsing when stored as JSON string in Redis
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SOURCE nodes with config.asset now get content_hash from registry.
EFFECT nodes with config.effect now get effect_hash from registry.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated cache detail button from "Publish to IPFS" to "Share to L2"
- Added Share to L2 button on recipe detail page
- Added Share to L2 button on run detail page
- Created /recipes/{id}/publish endpoint
- Created /runs/{id}/publish endpoint (publishes run output)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The template expects recipe.steps and recipe.yaml but the recipe
data has dag.nodes. Convert nodes (list or dict) to steps format
and add YAML dump for source display.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Made recipe and inputs optional in RunStatus model
- Convert DAG nodes from list format to dict format when running recipes
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Updated requirements.txt to use art-common@11aa056 with l2_server field
- All routers now import UserContext from artdag_common
- Removed duplicate UserContext from auth_service.py
- dependencies.py sets l2_server from settings on user context
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The CLI client sends multipart file uploads but the server expected JSON.
Changed the /recipes/upload endpoint to accept UploadFile and return
the additional fields (name, version, variable_inputs, fixed_inputs)
that the client expects.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Recipes: Now content-addressed only (cache + IPFS), removed Redis storage
- Runs: Completed runs stored in PostgreSQL, Redis only for task_id mapping
- Add list_runs_by_actor() to database.py for paginated run queries
- Add list_by_type() to cache_manager for filtering by node_type
- Fix upload endpoint to return size and filename fields
- Fix recipe run endpoint with proper DAG input binding
- Fix get_run_service() dependency to pass database module
Storage architecture:
- Redis: Ephemeral only (sessions, task mappings with TTL)
- PostgreSQL: Permanent records (completed runs, metadata)
- Cache: Content-addressed files (recipes, media, outputs)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Full implementation of runs, recipes, cache routers with templates
- Auth and storage routers fully migrated
- Jinja2 templates for all L1 pages
- Service layer for auth and storage
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Phase 2 of the full modernization:
- App factory pattern with create_app()
- Settings via dataclass with env vars
- Dependency injection container
- Router stubs for auth, storage, api, recipes, cache, runs
- Service layer stubs for run, recipe, cache
- Repository layer placeholder
Routes are stubs that import from legacy server.py during migration.
Next: Migrate each router fully with templates.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>