Commit graph

11 commits

Author SHA1 Message Date
Scott Werner
2575ab85fc
Merge pull request #747 from fabro-sh/codex/blob-hash-vocabulary
Some checks failed
Rust / Format (push) Waiting to run
Rust / Clippy (push) Waiting to run
Rust / Generated Docs (push) Waiting to run
Rust / Test (Linux) (push) Waiting to run
Rust / Test (macOS) (push) Waiting to run
TypeScript / Typecheck (push) Has been cancelled
TypeScript / Test (push) Has been cancelled
TypeScript / Build (push) Has been cancelled
Unify blob hash vocabulary
2026-08-18 15:55:43 -04:00
Scott Werner
3e6b23ce76 Keep loaded workflow-version closures out of implicit copies
LoadedWorkflowVersionClosure owns every file of every version in the
dependency graph, so an advertised Clone invites accidental deep copies
of the whole set. Drop the derive until a consumer needs owned copies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:02:54 -04:00
Scott Werner
1e29347227 Cover rejection of broken transitive includes under file run goals
The positive run-goal tests only asserted fixture shape, so a regression
that stopped pushing the file-goal template root would keep them green
while broken nested includes were silently accepted. Pin the rejection
path directly.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:02:54 -04:00
Scott Werner
2f2097be54 Anchor run-goal template validation at the version entrypoint
Create-time validation of workflow.toml run goals anchored includes at
workflow.toml for inline goals and at the goal file's directory for file
goals, while the run engine inlines the effective goal into the
entrypoint graph and renders it under the entrypoint's template source.
That divergence rejected layouts `fabro run` executes fine and accepted
layouts that fail at render time. Anchor both goal forms at the
entrypoint so validation matches the runtime, and pin the anchor with a
nested-entrypoint test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:02:54 -04:00
Scott Werner
5a5cfbdaa0 Parse template dependencies whose paths collide with discovery roots
Batched dependency discovery pre-seeded roots into the path-keyed result
map and reused that map as the traversal-dedup set, so a loaded include
target whose path matched a root (e.g. a goal template including the
graph file that anchors an inline prompt) was recorded but never parsed,
silently accepting invalid template content that per-root discovery used
to reject. Dedup traversal on the full (path, root, content) occurrence
instead, which also stops re-parsing identical duplicate roots.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:02:25 -04:00
Scott Werner
408cd2f745 Simplify workflow-version closure validation and loading
- Replace the discarded dependency-closure map in put/get with a
  visitor-based walk so only get_closure retains loaded versions
- Hold the closure root structurally in LoadedWorkflowVersionClosure
  instead of asserting its presence in the map with expect()
- Drop the visited-set parameter that guarded against impossible
  content-address cycles
- Move template-discovery error source-name extraction into
  TemplateDiscoveryError::source_name() where the variants are owned
- Collapse repeated TemplateSource construction into a TemplateRoots
  collector and share the config file-reference validation pipeline
  between dockerfile and run-goal references
- Deduplicate test helpers (version_id, version_with_goal_file,
  impl Into<String> config fixtures)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 13:02:11 -04:00
Scott Werner
63025bb748 Close workflow goals over version dependencies 2026-08-18 13:01:03 -04:00
Scott Werner
bf4265e1b8 Unify blob hash vocabulary 2026-08-17 13:56:19 -04:00
Scott Werner
8c3ff6216c Revert "Serialize workflow-version canonical bytes once at construction"
This reverts commit 8090d7030984862564a929ee9264e93911014e00.

The cached canonical field was optimizing an unmeasured path: without
the (deferred) O(closure) dependency re-validation multiplier, the
repeated serialization is microseconds for realistic versions. Compute
canonical bytes on demand like the environment, automation, and MCP
stores do, rather than carrying a serde-skipped cache field, a
construction bootstrap, and doubled memory for it. Purely in-memory:
stored blobs and version IDs are unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:34:52 -04:00
Scott Werner
20c9fba0b1 Serialize workflow-version canonical bytes once at construction
WorkflowVersion::new serialized the whole version just to enforce the
size limit and threw the bytes away, the store re-serialized them to
write the blob, and every read re-serialized a third time for the
canonicality comparison. Cache the canonical bytes on the struct at
construction (skipped during serde) and expose them as an infallible
borrow; the now-unconstructable InvalidShape store error variant goes
away with it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:34:52 -04:00
Scott Werner
178320e7a5 Add immutable workflow version resource
Add the WorkflowVersion domain resource with exactly entrypoint, files,
and workflow_dependencies, plus strict WorkflowPath validation and
deterministic canonical raw JSON. Semantic validation of graph imports,
templates, file references, workflow.toml rules, Dockerfile paths, and
exact child-workflow dependency bindings lives in the new
fabro-workflow-version crate, which validates the complete stored
dependency closure through the shared blob store before writing a root.
The authenticated create-only POST /api/v1/workflow-versions endpoint
ships with its OpenAPI contract, Rust type replacements, and generated
TypeScript client.

Squashed from the resource commits of the original combined branch;
the walker unification this builds on landed separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-13 16:34:52 -04:00