Commit Graph

305 Commits

Author SHA1 Message Date
gilesb
c5d70f61c8 Add debug logging for effect executor lookup
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 03:11:33 +00:00
gilesb
c3d6427883 Fix effect loading from IPFS and improve COMPOUND handling
- Remove effect:identity shortcut executor so effects load from IPFS by CID
- COMPOUND nodes now fall back to generic EFFECT executor for dynamic effects
- EFFECT nodes also fall back to generic executor when specific not found
- Update test assertions to match current implementation
- Raise error instead of silently skipping when effect executor not found

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 03:04:52 +00:00
gilesb
ad15ef1ce7 Add explicit EFFECT node handling with case-insensitive lookup
- EFFECT nodes now handled explicitly like SOURCE, COMPOUND, SEQUENCE
- Case-insensitive node type matching throughout
- Fallback executor lookup tries both upper and original case

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:21:30 +00:00
gilesb
164f1291ac Add SEQUENCE node handling for concatenating clips
Uses FFmpeg concat demuxer. Falls back to re-encoding if
stream copy fails (different codecs/formats).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:20:04 +00:00
gilesb
59c72500ac Fix status: check task result success flag, not just Celery success
Celery task "succeeds" (no exception) but may return {"success": False}.
Now we check the task result's success field AND output_cid before
marking run as completed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:13:03 +00:00
gilesb
84d465b264 Include failed runs in list_runs output
Failed runs were not showing in UI/CLI because list_runs only
included runs with status "pending" or "running", excluding "failed".

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:10:58 +00:00
gilesb
f28edf7ab6 Fix DogExecutor to use new process() API instead of effect_dog()
The dog effect was updated to use process() but DogExecutor
was still importing the old effect_dog() function.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:05:15 +00:00
gilesb
be4d0da84f Fix case sensitivity bug: S-expression node types are lowercase
Bug: S-expression plans produce lowercase node types (source, compound)
but code was checking uppercase (SOURCE, COMPOUND).

Fix: Use .upper() for node type comparisons.

Add TestNodeTypeCaseSensitivity tests to catch this regression.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:58:34 +00:00
gilesb
bfe96a431c Fail recipe if no output produced, add tests
- execute_recipe now returns success=False if output_cid is None
- Add TestRecipeOutputRequired tests to catch missing output
- Recipe must produce valid output to be considered successful

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:55:23 +00:00
gilesb
e1c0ebc0a2 Add integration tests for S-expression plan execution
Tests cover:
- SOURCE node resolution (fixed CID vs user input)
- COMPOUND node filter chain handling
- Cache lookup by code-addressed cache_id vs IPFS CID
- All plan step types (SOURCE, EFFECT, COMPOUND, SEQUENCE)
- Error handling for missing inputs

These tests would have caught the bugs:
- "No executor for node type: SOURCE"
- "No executor for node type: COMPOUND"
- Cache lookup failures by code-addressed hash

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:53:35 +00:00
gilesb
6c4b850487 Add COMPOUND node handling and fix cache lookups by code-addressed hash
- Add COMPOUND node handling in execute_recipe for collapsed effect chains
- Index cache entries by node_id (cache_id) when different from IPFS CID
- Fix test_cache_manager.py to unpack put() tuple returns

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:47:20 +00:00
gilesb
d08fbfc0bd Fix SOURCE node resolution for user inputs in execute_recipe
- SOURCE nodes with :input true now resolve CID from input_hashes
- Tries multiple name formats: exact, lowercase-dashes, lowercase-underscores
- Only return "completed" status for runs with actual output
- Add integration tests for SOURCE CID resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:36:48 +00:00
gilesb
bf188f4671 Remove dead code: execute_level, render_dog_from_cat, duplicate file_hash
- Remove execute_level() from tasks/execute.py (defined but never called)
- Remove render_dog_from_cat() from legacy_tasks.py (test convenience, never used)
- Remove duplicate file_hash() from legacy_tasks.py, import from cache_manager
- Remove unused hashlib import

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 01:21:55 +00:00
gilesb
d603485d40 Refactor to S-expression based execution with code-addressed cache IDs
Major changes:
- Add execute_recipe task that uses S-expression planner
- Recipe S-expression unfolds into plan S-expression with code-addressed cache IDs
- Cache IDs computed from Merkle tree of plan structure (before execution)
- Add ipfs_client.add_string() for storing S-expression plans
- Update run_service.create_run() to use execute_recipe when recipe_sexp available
- Add _sexp_to_steps() to parse S-expression plans for UI visualization
- Plan endpoint now returns both sexp content and parsed steps

The code-addressed hashing means each plan step's cache_id is:
  sha3_256({node_type, config, sorted(input_cache_ids)})

This creates deterministic "buckets" for computation results computed
entirely from the plan structure, enabling automatic cache reuse.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 00:27:24 +00:00
gilesb
2c27eacb12 Convert DAG nodes dict to steps list in get_run_plan()
The CLI expects {"steps": [...]} but DAG format stores {"nodes": {...}}.
Added _dag_to_steps() to convert between formats, including topological
sorting so sources appear first.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 00:00:00 +00:00
gilesb
1ad8fe9890 Fix plan storage path to match get_by_cid lookup
Store plan directly in CACHE_DIR/{cid} instead of CACHE_DIR/legacy/{cid},
which matches what cache_manager.get_by_cid() checks at line 396-398.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 23:50:46 +00:00
gilesb
3e3df6ff2a Code-addressed node IDs and remove JSON index files
- Compiler now generates SHA3-256 hashes for node IDs
- Each hash includes type, config, and input hashes (Merkle tree)
- Same plan = same hashes = automatic cache reuse

Cache changes:
- Remove index.json - filesystem IS the index
- Files at {cache_dir}/{hash}/output.* are source of truth
- Per-node metadata.json for optional stats (not an index)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 22:38:50 +00:00
gilesb
faf794ef35 Use IPFS as universal fallback for content lookup
When content isn't found in local cache, fetch directly from IPFS
using the CID. IPFS is the source of truth for all content-addressed data.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:20:28 +00:00
gilesb
a4bf0eae24 Add filesystem fallback when artdag Cache lookup fails
The artdag Cache object doesn't persist state across process restarts,
so cache.get(node_id) returns None even when files exist on disk.

Now we check the filesystem directly at {cache_dir}/nodes/{node_id}/output.*
when the in-memory cache lookup fails but we have a valid node_id from
the Redis index.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:18:20 +00:00
gilesb
9a1ed2adea Fetch source content from IPFS if not in local cache
When the Celery worker can't find source content in the local cache,
fetch it from IPFS using the CID. This ensures workers can execute
DAGs even when they don't share the same filesystem as the web server.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:15:08 +00:00
gilesb
f67aacdceb Add detailed logging to cache_manager put and get_by_cid
Debug why recipes are not found in cache after upload.
Logs now show each step of put() and get_by_cid().

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 21:07:09 +00:00
gilesb
da4e2e9d3d Fix stats counting to use ownership-based database queries
- Media: Only count video/image/audio/unknown types, not effects/recipes
- Effects: Use database count_user_items instead of filesystem scan
- Recipes: Use database count_user_items instead of loading all recipes

This ensures stats reflect user ownership via item_types table,
and prevents effects from being double-counted as media.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:51:14 +00:00
gilesb
f7fa683bcf Add logging to save_item_metadata for debugging
Track when items are being saved to database to diagnose
why recipes show 0 in stats but effects show correctly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:37:16 +00:00
gilesb
6c973203fc Add debug logging to recipe upload and get
To help diagnose why recipes are not found after upload.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:35:06 +00:00
gilesb
7e38b4a0c8 Fix undefined cache_manager in clear_user_data
Call get_cache_manager() to get the cache manager instance
before using it in effects and media deletion.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:10:22 +00:00
gilesb
8bf6f87c2a Implement ownership model for all cached content deletion
- cache_service.delete_content: Remove user's ownership link first,
  only delete actual file if no other owners remain

- cache_manager.discard_activity_outputs_only: Check if outputs and
  intermediates are used by other activities before deleting

- run_service.discard_run: Now cleans up run outputs/intermediates
  (only if not shared by other runs)

- home.py clear_user_data: Use ownership model for effects and media
  deletion instead of directly deleting files

The ownership model ensures:
1. Multiple users can "own" the same cached content
2. Deleting removes the user's ownership link (item_types entry)
3. Actual files only deleted when no owners remain (garbage collection)
4. Shared intermediates between runs are preserved

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:02:27 +00:00
gilesb
abe89c9177 Fix effects router to use proper ownership model
- Upload: Create item_types entry to track user-effect relationship
- List: Query item_types for user's effects instead of scanning filesystem
- Delete: Remove ownership link, only delete files if orphaned (garbage collect)

This matches the ownership model used by recipes and media, where multiple
users can "own" the same cached content through item_types entries.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:56:12 +00:00
gilesb
427de25e13 Fix recipe ownership tracking via item_types table
- Upload now creates item_types entry linking user to recipe
- List queries item_types for user's recipes (not all cached)
- Delete removes item_types entry (not the file)
- File only deleted when no users own it (garbage collection)

This allows multiple users to "own" the same recipe CID.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:40:39 +00:00
gilesb
a5a718e387 Remove owner check from recipe deletion (security fix)
Same reasoning as the list fix: the owner field from recipe content
could be spoofed. For L1, any authenticated user can delete recipes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:26:36 +00:00
gilesb
c5c7e5e162 Fix file_hash called after move in cache_manager.put
The dual-indexing code was calling file_hash(source_path) after
cache.put(move=True) had already moved the file, causing
"No such file or directory" errors on upload.

Now computes local_hash before the move operation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:07:45 +00:00
gilesb
b36aab33bb Fix clear-data to check recipe delete return value
The delete_recipe() returns (success, error) tuple but
clear-data wasn't checking the result, so failed deletes
weren't being reported.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 19:02:16 +00:00
gilesb
2e3d3a5c6d Store DAG plan to IPFS and track plan_cid in run_cache
- Add plan_cid column to run_cache schema
- Store DAG JSON to IPFS during execute_dag task
- Return plan_cid in run status and list APIs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 18:43:48 +00:00
gilesb
5b05dbd31e Fix clear-data to actually delete run_cache entries
- discard_run now deletes from run_cache and pending_runs tables
- Add delete_run_cache() and delete_pending_run() database functions
- Previously clear-data only cleared Redis, leaving DB cache intact

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:42:12 +00:00
gilesb
a0b113923e Fix IPFS CID detection in legacy_tasks node caching
- Rename misleading ipfs_cid variable to content_cid
- Detect IPFS CIDs by prefix (Qm or bafy) instead of truthy check
- Add clearer logging to show whether IPFS or local hash is used

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 17:13:25 +00:00
gilesb
cc29311d1c Add friendly name to recipe detail endpoint
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:57:15 +00:00
gilesb
87ff3d5d14 Remove owner filtering from recipe listing (security fix)
The owner field from recipe content could be spoofed to hide recipes
from users or make recipes appear to belong to someone else.

For L1, all recipes in cache are now visible to authenticated users.
Ownership tracking should use the naming service or cache metadata,
not untrusted data from recipe content.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:33:39 +00:00
gilesb
6adef63fad Fix get_recipe to handle both YAML and S-expression formats
The upload endpoint accepts both YAML and S-expression recipes, but
get_recipe only tried to parse S-expression. Now it detects the format
and parses accordingly.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:31:25 +00:00
gilesb
f1b90fe65d Add debug logging for recipe filtering
Shows owner and actor_id values when filtering recipes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:30:24 +00:00
gilesb
4b5066c525 Fix clear-data missing username arg for discard_run
The discard_run service method requires username as the third argument.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 15:02:09 +00:00
gilesb
280dddebd0 Fix authentication to support both header and cookie auth
All API endpoints now use require_auth or get_current_user which handle
both Authorization header (for CLI) and cookies (for browser). Previously
many endpoints only checked cookies via get_user_from_cookie.

Changed files:
- runs.py: list_runs, run_detail, run_plan, run_artifacts, plan_node_detail, ui_discard_run
- recipes.py: list_recipes, get_recipe, ui_discard_recipe
- storage.py: list_storage, add_storage_form, delete_storage, test_storage, storage_type_page
- cache.py: get_cached, list_media, get_metadata_form, update_metadata_htmx

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:56:52 +00:00
gilesb
79a74df2bb Add clear-data API endpoint
- DELETE /api/clear-data clears all user L1 data
- Deletes runs, recipes, effects, and media/cache items
- Preserves storage provider configurations
- Returns counts of deleted items and any errors

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:34:56 +00:00
gilesb
7d24ba4dd7 Add pagination and API improvements
- Add pagination to effects list with infinite scroll
- Refactor home stats into reusable get_user_stats function
- Add /api/stats endpoint for CLI/API clients
- Add has_more flag to recipes listing
- Add JSON API support to storage type page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:22:09 +00:00
gilesb
ee8719ac0b Fix media friendly names, metadata display, output recording, and plan display
- Add friendly name display to media detail and list pages
- Unpack nested meta fields to top level for template access
- Fix output_cid mismatch: use IPFS CID consistently between cache and database
- Add dual-indexing in cache_manager to map both IPFS CID and local hash
- Fix plan display: accept IPFS CIDs (Qm..., bafy...) not just 64-char hashes
- Add friendly names to recipe listing
- Add recipe upload button and handler to recipes list
- Add debug logging to recipe listing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:21:39 +00:00
gilesb
19634a4ac5 Add friendly names system for recipes, effects, and media
- Add friendly_names table with unique constraints per actor
- Create NamingService with HMAC-signed timestamp version IDs
- Version IDs use base32-crockford encoding, always increase alphabetically
- Name normalization: spaces/underscores to dashes, lowercase, strip special chars
- Format: "my-effect 01hw3x9k" (space separator ensures uniqueness)
- Integrate naming into recipe, effect, and media uploads
- Resolve friendly names to CIDs during DAG execution
- Update effects UI to display friendly names
- Add 30 tests covering normalization, parsing, and service structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 14:02:17 +00:00
gilesb
98ca2a6c81 Fix recipe listing, effects count, and add nav counts to all pages
- Fix list_by_type to return node_id (IPFS CID) instead of local hash
- Fix effects count on home page (count from _effects/ directory)
- Add nav_counts to all page templates (recipes, effects, runs, media, storage)
- Add editable metadata section to cache/media detail page
- Show more metadata on recipe detail page (ID, IPFS CID, step count)
- Update tests for new list_by_type behavior

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 13:30:11 +00:00
gilesb
9bb1c4278e Add recipe link, fix step count, add nav counts
- Make recipe name clickable link to recipe page on run detail
- Fix step count to use plan.steps length as fallback
- Add nav_counts support to base template for showing counts in brackets
- Add get_nav_counts helper in dependencies
- Pass nav_counts on home page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:53:00 +00:00
gilesb
73cc3e7c2f Add Share to L2 button for effects
- Add /effects/{cid}/publish endpoint to publish effects to L2
- Add Share to L2 button to effects detail page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:30:38 +00:00
gilesb
991db29e27 Fix input preview hash -> cid key mismatch
Template uses inp.cid and input.cid but router created previews with
'hash' key. Fixed both input_previews and run_inputs to use 'cid'.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:27:05 +00:00
gilesb
2cc7d88d2e Add effects count to home page dashboard
- Add Effects card to home page grid (now 3 columns)
- Add stats["effects"] count from cache.list_by_type('effect')
- Add tests for home page effects display

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:19:06 +00:00
gilesb
9f8aa54e2b Fix duplicate get_by_cid method shadowing recipe lookup
Bug: Two get_by_cid methods existed in L1CacheManager. The second
definition shadowed the first, breaking recipe lookup because the
comprehensive method (using find_by_cid) was hidden.

- Remove duplicate get_by_cid method (lines 470-494)
- Add regression test to ensure only one get_by_cid exists
- Add tests for template variables and recipe visibility

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 12:14:59 +00:00