Capture compute execution environment in Task provenance (#106) - #516
Capture compute execution environment in Task provenance (#106)#516dotsdl wants to merge 1 commit into
Conversation
Addresses the #106 comment requesting that Task execution provenance record the software environment a Task ran in. - compute/environment.py: best-effort capture_environment() trying micromamba/mamba/conda/pip (--json), first success wins, structured as {tool, packages, captured_at}; never raises. - ComputeServiceSettings.capture_environment (default True): the service captures its environment once at startup and sends it at registration. - storage: a content-addressed ComputeEnvironment node (hashed tool+packages), MERGE'd at registration so identical environments across services/claims are stored once; each claim links its TaskProvenance attempt to it via RAN_IN. The node outlives the registration, so an attempt's environment survives service teardown. Surfaced as TaskAttempt.environment in get_task_history / get_tasks_details. - migration v07_to_v08: adds the ComputeEnvironment.hash uniqueness constraint (load-bearing for the dedup MERGE, and keeps Neo4jStore.check consistent); idempotent, no data migration. - tests: capture unit tests (mocked subprocess fallbacks) + ComputeEnvironment model round-trips + integration tests for surfacing, dedup, and survival of registration expiry. Docs + news updated. Builds on #514. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Code reviewReviewed the environment-capture work (diff scoped against Findings1. Deduplicated
Scenario: Service A (started Monday) and Service B (started Friday) run the same image. A 2. Environment capture runs up to four 60s subprocesses synchronously in
Scenario: On an HPC node where 3. A malformed
Scenario: An authenticated compute client (or a future capture path) sends Checked and cleared (considered, but not real)
🤖 Generated with Claude Code |
Summary
Records the software environment each
Taskexecution attempt ran in, as part of durable execution provenance — addressing this request on #106:Builds on #514 (the introspection/provenance work); this PR is based on
feature/task-introspection-0.8.0and will retarget tomainonce #514 merges.What it does
compute/environment.py): a best-effortcapture_environment()triesmicromamba→mamba→conda→pip(each--json), takes the first that yields a parseable package listing, and returns{"tool": ..., "packages": {name: version}, "captured_at": ...}. Never raises; a service with no package manager simply records no environment. Gated byComputeServiceSettings.capture_environment(defaultTrue), captured once at startup (the env is fixed for the service's lifetime) and sent at registration.ComputeEnvironmentnode (hash of tool + package map),MERGE'd at registration so identical environments across services and claims are stored once. Each claim links itsTaskProvenanceattempt to the environment viaRAN_IN. The node deliberately outlives theComputeServiceRegistration, so an attempt's environment survives service teardown (expiry/deregistration) — exactly the case provenance exists to record.TaskAttempt.environment(fromget_task_historyand the most-recent-attempt inget_tasks_details) carries the captured{tool, packages, captured_at}.v07_to_v08— adds theComputeEnvironment.hashuniqueness constraint. Unlike the (removed)TaskProvenanceindex, this one is load-bearing: it makes the dedupMERGEcorrect under concurrency and keepsNeo4jStore.checkconsistent on upgrade. Idempotent; no data migration.Design notes
TaskProvenancenode (likehostname) would undercut the "provenance nodes are tiny" property; the content-addressed node stores each distinct environment once.TaskProvenancelabel indexes were dropped because every provenance query is Task-anchored and never scans the label. Here the constraint backs aMERGEonComputeEnvironment.hash— a genuine keyed lookup — so it earns its keep.Testing
capture_environmentfallback/parse/failure paths (mocked subprocess), andComputeEnvironmentmodel round-trips (order-independent hash,from_capture/from_node/to_capture_dict), plusTaskAttempt.environment.get_task_history; deduplication across services (same env → one node, different env → two); and survival of registration expiry. Docs (compute.rst,introspection.rst,operations.rst) and the#106news fragment updated.🤖 Generated with Claude Code