Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .sudocode/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cache.db*
issues/
specs/
worktrees/
11 changes: 11 additions & 0 deletions .sudocode/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.1.5",
"worktree": {
"worktreeStoragePath": ".sudocode/worktrees",
"autoCreateBranches": true,
"autoDeleteBranches": false,
"enableSparseCheckout": false,
"branchPrefix": "sudocode",
"cleanupOrphanedWorktreesOnStartup": true
}
}
4 changes: 4 additions & 0 deletions .sudocode/issues.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{"id":"i-4ygf","uuid":"ac0bbd62-bb4c-49cc-97c2-7dd2d34ffc41","title":"Extend workflow Block model for DAG metadata","content":"Update the base `Block` model in `skyvern/forge/sdk/workflow/models/block.py` to support the DAG metadata described in [[s-4bnl]].\n\n## Requirements\n- Ensure the Block dataclass / Pydantic model exposes the `id: str` field used for persistence (validate that it is present and documented).\n- Add/confirm a `label: str` field representing the author-facing identifier that must be unique per workflow; include validation helpers if necessary.\n- Introduce an optional `next_block_label: str | None = None` attribute so sequential blocks can point to the next block by label.\n- Update related DTO subclasses and serializers within the workflow SDK so the new attributes round-trip through API payloads.\n- Maintain backward compatibility for existing sequential workflows (e.g., default `next_block_label` derives from list order when absent).\n- Add or update tests covering serialization/deserialization of the new fields.\n\n## Definition of Done\n- `skyvern/forge/sdk/workflow/models/block.py` and dependent DTOs expose the new attributes with type hints and docstrings.\n- Validation enforces uniqueness/consistency where feasible without breaking legacy workflows.\n- Tests demonstrate that legacy workflows still deserialize without explicit `next_block_label` while new DAG-aware workflows do.\n- Any necessary documentation or comments reference the branching workflow spec.","status":"open","priority":2,"assignee":null,"archived":0,"archived_at":null,"created_at":"2025-11-14 06:25:27","updated_at":"2025-11-14 06:25:27","closed_at":null,"parent_id":null,"parent_uuid":null,"relationships":[],"tags":[]}
{"id":"i-2hqk","uuid":"5fc0520f-4b49-44f1-9493-07807ef37fc8","title":"Add ConditionalBlock with BranchCondition and BranchCriteria scaffolding","content":"Implement the DAG branching models outlined in [[s-4bnl]] by introducing dedicated ConditionalBlock structures and branch metadata in the workflow SDK.\n\n## Scope\n- Extend `skyvern/forge/sdk/workflow/models` to add a `ConditionalBlock` model (building on the base Block hierarchy) that owns `branches: list[BranchCondition]`.\n- Create a first-class `BranchCondition` model with fields `id`, `criteria`, `next_block_label`, `description`, `order`, and `is_default`, ensuring serialization/deserialization coverage across DTOs.\n- Introduce a `BranchCriteria` abstraction that defines the interface for evaluating branch logic; for now only establish the interface contract plus stubs/hooks so we can later plug in Jinja templates, JSONLogic, pythonic expressions, or other engines.\n- Update relevant schemas, SDKs, and validators so conditional blocks and branches round-trip through storage and APIs while remaining backward compatible with existing sequential workflows.\n- Add tests validating branch ordering, default branch uniqueness, and serialization of conditional blocks.\n\n## Out of Scope\n- Choosing or implementing a specific BranchCriteria evaluation engine (Jinja/JSONLogic/python). Capture pros/cons analysis in a follow-up issue before implementation.\n","status":"open","priority":2,"assignee":null,"archived":0,"archived_at":null,"created_at":"2025-11-14 06:31:33","updated_at":"2025-11-14 06:31:33","closed_at":null,"parent_id":null,"parent_uuid":null,"relationships":[],"tags":["sdk","spec","workflow"]}
{"id":"i-8wis","uuid":"c2e08d03-a116-420d-ad0d-641e142b99d1","title":"Refactor workflow execution engine for DAG traversal","content":"Update the workflow executor to follow the DAG semantics established in [[s-4bnl]] so workflows can branch via `next_block_label` and conditional branches.\n\n## Requirements\n- Introduce `workflow.entry_block_label` (or equivalent mechanism) and ensure legacy workflows default to their first block when the field is absent.\n- Refactor execution state to track `current_block_label` instead of list indexes, honoring `block.next_block_label` and conditional branches’ chosen edges.\n- Implement cycle detection/validation to guard against infinite loops; provide clear failure messaging when a cycle is detected.\n- Ensure failure handling, retries, and dependency resolution operate correctly when traversing the DAG rather than a flat list.\n- Persist run metadata that records the block label/id and any branch taken for observability and debugging.\n- Maintain backward compatibility: sequential workflows without explicit `next_block_label` should behave exactly as before.\n\n## Definition of Done\n- Execution engine code reflects DAG traversal with comprehensive unit tests covering sequential fallbacks, branching paths, validation failures, and branch auditing data.\n- Legacy workflows keep working without user changes, while new workflows can express branching paths that execute correctly.\n- Logging/metrics show which branch was taken to support analytics and debugging.\n","status":"open","priority":2,"assignee":null,"archived":0,"archived_at":null,"created_at":"2025-11-14 06:33:50","updated_at":"2025-11-14 06:33:50","closed_at":null,"parent_id":null,"parent_uuid":null,"relationships":[],"tags":["executor","workflow"]}
{"id":"i-9pmm","uuid":"80bb0648-9ecd-4fca-bc2b-dee134e30287","title":"Update script generation and caching for branching workflows","content":"Align script generation and caching logic with the branching workflow design in [[s-4bnl]].\n\n## Requirements\n- Update the code that emits workflow execution scripts so it can represent `branches` as explicit `if/elif/.../else` blocks based on ordered `BranchCondition` definitions, routing to `next_block_label` targets.\n- Ensure generated scripts deterministically reference `workflow_permanent_id` and `block_label` so caching remains unique per node even when multiple paths converge.\n- Extend caching so newly visited block labels (including branch-specific paths) create entries keyed by `(workflow_permanent_id, block_label)` and can be reused on subsequent runs regardless of which branch led to them.\n- Reconcile executed block graphs after each run to persist cache entries for newly traversed branches while avoiding collisions with legacy sequential workflows.\n- Verify caching remains deterministic and branch-aware via tests that exercise multi-branch workflows.\n\n## Definition of Done\n- Script generation code outputs branch-aware control flow structures with ordered evaluation semantics.\n- Cache management recognizes DAG paths and maintains per-label entries.\n- Tests demonstrate deterministic script output/caching for workflows that include branching and legacy sequential behavior.\n","status":"open","priority":2,"assignee":null,"archived":0,"archived_at":null,"created_at":"2025-11-14 06:35:19","updated_at":"2025-11-14 06:35:19","closed_at":null,"parent_id":null,"parent_uuid":null,"relationships":[],"tags":["caching","scripts","workflow"]}
1 change: 1 addition & 0 deletions .sudocode/specs.jsonl
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"s-4bnl","uuid":"e76fc025-2282-43e7-b805-3ba01095ca0c","title":"Conditional Workflow Branching","file_path":"specs/conditional_workflow_branching.md","content":"## Overview\n\nSkyvern workflows currently execute blocks strictly in sequence: block _n_ runs after block _n − 1_ and there is only one valid path through the workflow definition. This spec introduces the first phase of a DAG-oriented workflow model by adding a conditional block that can branch to multiple successors. All block types gain an optional `next_block_label` pointer, while conditional blocks hold a list of branch edges that choose the next block at runtime. The system must continue to accept existing list-based workflow definitions so users can adopt DAG capabilities incrementally.\n\n## Goals\n\n- Represent workflow control flow as a graph where each block may point to a next block, and conditional blocks fan out through branch edges.\n- Introduce a `BranchCondition` model that captures `if / elif / elif / else` semantics with ordered evaluation and optional `next_block_label`.\n- Update workflow storage, APIs, SDKs, and execution so both sequential and DAG-style definitions run without breaking current customers.\n- Provide validation, authoring affordances, and observability to help users design and debug branching workflows.\n\n## Non-Goals\n\n- Redesigning the workflow editor UI beyond what is required to author conditional branches and optional next pointers.\n- Implementing loop/retry constructs or arbitrary parallel fan-out execution (this spec only covers single-path branching).\n- Changing how workflow parameters, scripts, or caching behave beyond what is necessary to route control flow.\n\n## Architecture\n\n### Data Model Updates\n\n- Extend the base `Block` model in `skyvern/forge/sdk/workflow/models/block.py` (and all DTO subclasses) with:\n - `id: str` (existing internal identifier) used for persistence.\n - `label: str` (existing, author-controlled) used as the stable node reference in the DAG; must be unique per workflow.\n - `next_block_label: str | None = None` referencing another block label in the same workflow.\n - `metadata.graph_coordinates` (optional) to aid editors in laying out DAG nodes; nullable for back-compat.\n- Introduce `ConditionalBlock` (builds on existing block hierarchy) with `branches: list[BranchCondition]`.\n- Define `BranchCondition` as a first-class dataclass / pydantic model with:\n - `id: str` for diff-friendly updates.\n - `criteria: BranchCriteria | None` describing the Boolean condition. `None` marks the `else` branch.\n - `next_block_label: str | None` to jump to another block when the condition matches. `None` indicates the workflow should terminate after the branch.\n - `description: str | None` for editor display.\n - `order: int` to enforce deterministic `if/elif` evaluation order.\n - `is_default: bool` convenience flag to identify the `else` branch (must be unique per conditional block).\n- `BranchCriteria` supports a declarative DSL or structured config that can reference workflow parameters, previous block outputs, environment facts, and optional LLM evaluations (e.g., boolean classification prompts). Initial implementation may reuse existing expression evaluators (e.g., Jinja templates, JSONLogic, or pythonic expressions) but must be explicit about supported operators and provide hooks for invoking an LLM-based evaluator when configured.\n\n### Serialization & Backwards Compatibility\n\n- Persist new fields in workflow YAML/JSON definitions, REST/Fern schemas, generated SDKs, and database rows.\n- When loading legacy workflows (list of blocks without `next_block_label`), auto-populate an in-memory chain by following list order so execution behaves exactly as today.\n- When saving workflows authored in the legacy shape (no explicit `next_block_label`), continue to accept the flat list and omit DAG metadata in the stored payload to avoid churn.\n- Ensure API responses always include `next_block_label` (explicit or inferred) so modern clients can treat the workflow as a graph.\n- Migrations: if workflows are stored in the database as JSON blobs, no table migration is required beyond ensuring serializers default the new fields to `null`. If relational tables exist, add nullable columns with default `NULL`.\n\n### Branch Evaluation Semantics\n\n- Evaluate branch conditions top-to-bottom by `order` to mimic `if/elif/elif/.../else`.\n- `criteria` evaluates inside an execution context containing workflow params, accumulated block outputs, system variables, and (optionally) results from LLM boolean evaluators. Criteria must be side-effect free and return truthy/falsey values regardless of the underlying evaluator.\n- Exactly one branch fires per conditional block:\n 1. Iterate ordered branches until one returns `True`.\n 2. If none match, fall back to the branch where `is_default=True`. Validation must ensure exactly one default exists (or zero if the author wants a “drop out” when nothing matches).\n 3. If no branch matches and no default exists, log a warning, mark the workflow as failed (configurable), and stop execution to avoid silent drops.\n- The chosen branch’s `next_block_label` determines the next block. `None` means the workflow ends successfully after executing the conditional block.\n\n### Workflow Execution Engine\n\n- Refactor the executor to walk a DAG:\n - Maintain `current_block_label`, starting from a new `workflow.entry_block_label` (defaults to the first block for legacy workflows).\n - After each block completes, prefer `block.next_block_label` if set. Conditional blocks ignore `next_block_label` and instead use the branch result.\n - Detect cycles at validation time; runtime should guard against infinite loops by tracking visited blocks and aborting with a descriptive error if validation was bypassed.\n- Update persistence so workflow run records capture both the block ID and the branch taken for auditing/debugging.\n- Any dependency resolution (e.g., fetching outputs from previous blocks) now uses graph traversal rather than implicit list indexes.\n- Failure handling: if a block fails, preserve existing retry/rollback semantics. Branching does not change error propagation rules.\n\n### Script Generation & Caching\n\n- Update script generation so the emitted `run_workflow` (and any block-level helper functions) encode `branches` as explicit `if/elif/elif/.../else` statements based on the ordered `BranchCondition` list. Each branch condition must be rendered into executable code that evaluates the configured `BranchCriteria` and routes to the referenced `next_block_label`.\n- Cache keys must remain unique per block label. When the workflow runner encounters a block whose label has no cached script entry, create a new script block after the run completes using the recorded block definition and resolved branch logic.\n- After each workflow run, reconcile the executed block graph against the cached script store to discover newly visited labels (including branches that may not execute every run) and persist their corresponding code artifacts so future runs can short-circuit via caching.\n- Ensure regenerated scripts remain deterministic even when multiple branches share downstream blocks; caching should key on the tuple `(workflow_permanent_id, block_label)` to avoid collisions between legacy sequential runs and new DAG paths.\n\n### Authoring & Tooling\n\n- Workflow editor (web and CLI) must allow:\n - Setting or clearing `next_block_label` for any block.\n - Converting an existing block into a conditional block with ordered branches and marking one as default (`else`).\n - Visualizing outgoing edges so users understand the DAG.\n- Provide helper commands/utilities to auto-wire `next_block_label` for legacy workflows to minimize author effort.\n- Documentation must explain how to define branch criteria, including examples for parameter checks, previous block comparisons, and LLM-evaluated prompts.\n\n### Validation & Observability\n\n- Add graph validation to ensure:\n - All referenced `next_block_label` values point to existing blocks.\n - The graph is acyclic (DAG) or, at minimum, that cycles are explicitly disallowed with clear errors.\n - Each conditional block has at most one `else` branch and strictly increasing `order` values.\n - Entry block is defined and reachable.\n- Extend logging/metrics to include which branch path was taken, enabling path analytics.\n- Surface validation errors to API/UI clients with actionable messages (e.g., “Branch order must be unique per block”).\n\n## Testing Strategy\n\n- Unit tests for data model serialization/deserialization covering `next_block_label`, branch ordering, and default branch handling.\n- Executor unit tests that:\n - Run legacy sequential workflows and confirm behavior is unchanged.\n - Exercise multi-branch conditionals, including `else`, missing matches, and terminal branches.\n - Validate cycle detection and error reporting.\n- Integration test that loads a mixed workflow (sequential blocks feeding into a conditional block with two branches) and verifies both branches can be executed depending on input parameters.\n- UI/e2e tests to ensure users can author branches, configure default behavior, and that persisted workflows reopen with the correct DAG wiring.\n\n## Open Questions\n\n1. Should we introduce a `workflow.entry_block_label` field now, or infer the first block dynamically until multiple entry points are supported?\n2. What evaluation options should `BranchCriteria` support (existing template engine, JSONLogic, CEL, bespoke DSL, LLM prompts), and how do we sandbox them for security?\n3. Do we need to expose branch-level analytics (counts, success/failure) in the short term, or is logging sufficient for the first iteration?\n4. How should we serialize workflows that intentionally omit `next_block_label` to signal “stop after this block” versus leaving it `None` accidentally? Consider explicit sentinel values or validation rules.\n","priority":2,"archived":0,"archived_at":null,"created_at":"2025-11-12 07:26:40","updated_at":"2025-11-12 09:06:27","parent_id":null,"parent_uuid":null,"relationships":[],"tags":[]}
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ pre-commit run --all-files
- Reference relevant documentation
- Provide reproduction steps for bugs
- Be specific about the problem and expected behavior

# Spec and Issue Management
This project uses sudocode for spec and issue management. Make sure to update issue status when working on an issue and close the issue when done with work.
When writing specs and creating issues that refer to specs, make sure to created references to bi-directionally link issues and specs. Refer to spec and issue content to gather more context about your current task. When editing spec or issue content, you can use your provided MVP tools but you can also update their contents directly by modifying the markdown files in .sudocode/specs/ and .sudocode/issues.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a minor typo: 'created references' should be 'create references'. Also, the guidelines mention updating markdown files in .sudocode/specs/ and .sudocode/issues, yet the managed files are JSONL. Consider clarifying the file paths/formats.

Suggested change
When writing specs and creating issues that refer to specs, make sure to created references to bi-directionally link issues and specs. Refer to spec and issue content to gather more context about your current task. When editing spec or issue content, you can use your provided MVP tools but you can also update their contents directly by modifying the markdown files in .sudocode/specs/ and .sudocode/issues.
When writing specs and creating issues that refer to specs, make sure to create references to bi-directionally link issues and specs. Refer to spec and issue content to gather more context about your current task. When editing spec or issue content, you can use your provided MVP tools but you can also update their contents directly by modifying the markdown files in .sudocode/specs/ and .sudocode/issues.

Loading
Loading