Add identity-then-dog recipe

Chains identity effect followed by dog effect on cat input.
Demonstrates effect chaining: SOURCE → EFFECT → EFFECT → output

Since identity(cat) = cat, the result is dog(cat) = dog video.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
gilesb
2026-01-09 12:14:04 +00:00
parent 10c6af3736
commit 5b420b4e1f
3 changed files with 294 additions and 0 deletions

140
mint_cat_copy.py Normal file
View File

@@ -0,0 +1,140 @@
#!/usr/bin/env python3
"""
Mint cat-copy.jpg by running the identity-cat recipe through artdag.
Records full provenance: inputs, effects, software, hardware, owner.
"""
import hashlib
import json
import shutil
import sys
from datetime import datetime, timezone
from pathlib import Path
# Add artdag to path
sys.path.insert(0, str(Path(__file__).parent.parent / "artdag"))
from artdag.dag import DAG, Node, NodeType
from artdag.engine import Engine
from artdag import nodes # Register executors
def file_hash(path: Path) -> str:
"""Compute SHA3-256 hash of a file."""
hasher = hashlib.sha3_256()
actual_path = path.resolve() if path.is_symlink() else path
with open(actual_path, "rb") as f:
for chunk in iter(lambda: f.read(65536), b""):
hasher.update(chunk)
return hasher.hexdigest()
def main():
# Registry hashes (from git.rose-ash.com/art-dag/registry)
CAT_HASH = "33268b6e167deaf018cc538de12dbe562612b33e89a749391cef855b320a269b"
IDENTITY_HASH = "640ea11ee881ebf4101af0a955439105ab11e763682b209e88ea08fc66e1cc03"
ARTDAG_HASH = "96a5972de216aee12ec794dcad5f9360da2e676171eabf24a46dfe1ee5fee4b0"
WORKSTATION_HASH = "964bf6e69dc4e2493f42375013caffe26404ec3cf8eb5d9bc170cd42a361523b"
# Input
cat_path = Path("/home/giles/Pictures/cat.jpg")
output_path = Path("/home/giles/Pictures/cat-copy.jpg")
print("=== Minting cat-copy.jpg via identity-cat recipe ===\n")
# Verify input
input_hash = file_hash(cat_path)
print(f"Input: {cat_path}")
print(f"Input hash: {input_hash}")
assert input_hash == CAT_HASH, "Input hash mismatch!"
# Build DAG: SOURCE -> EFFECT(identity) -> output
dag = DAG(metadata={
"recipe": "identity-cat",
"recipe_url": "https://git.rose-ash.com/art-dag/recipes",
})
source_node = Node(
node_type=NodeType.SOURCE,
config={"path": str(cat_path)},
inputs=[],
name="source_cat",
)
source_id = dag.add_node(source_node)
effect_node = Node(
node_type="EFFECT",
config={"effect": "identity"},
inputs=[source_id],
name="apply_identity",
)
effect_id = dag.add_node(effect_node)
dag.set_output(effect_id)
# Execute through artdag engine
print(f"\nExecuting DAG through artdag engine...")
cache_dir = Path.home() / ".artdag" / "cache"
engine = Engine(cache_dir)
result = engine.execute(dag)
if not result.success:
print(f"FAILED: {result.error}")
sys.exit(1)
# Copy output to final location
shutil.copy2(result.output_path, output_path)
output_hash = file_hash(output_path)
print(f"\nOutput: {output_path}")
print(f"Output hash: {output_hash}")
# Verify identity property
assert output_hash == input_hash, "Identity property violated!"
print(f"\n✓ Identity property verified: output == input")
# Record provenance
provenance = {
"minted_at": datetime.now(timezone.utc).isoformat(),
"minted_by": "@giles@artdag.rose-ash.com",
"output": {
"name": "cat-copy",
"content_hash": output_hash,
"local_path": str(output_path),
"url": None, # To be filled in after upload
},
"inputs": [
{"name": "cat", "content_hash": CAT_HASH}
],
"recipe": {
"name": "identity-cat",
"url": "https://git.rose-ash.com/art-dag/recipes"
},
"effects": [
{"name": "effect:identity", "content_hash": IDENTITY_HASH}
],
"infrastructure": {
"software": {"name": "infra:artdag", "content_hash": ARTDAG_HASH},
"hardware": {"name": "infra:giles-hp", "content_hash": WORKSTATION_HASH}
},
"execution": {
"time_seconds": result.execution_time,
"nodes_executed": result.nodes_executed,
"nodes_cached": result.nodes_cached
}
}
# Save provenance
provenance_path = output_path.with_suffix(".provenance.json")
with open(provenance_path, "w") as f:
json.dump(provenance, f, indent=2)
print(f"\nProvenance saved: {provenance_path}")
print(f"\nUpload {output_path} to rose-ash.com, then provide URL to complete minting.")
return provenance
if __name__ == "__main__":
main()