diff --git a/skyvern/core/script_generations/transform_workflow_run.py b/skyvern/core/script_generations/transform_workflow_run.py index ac53448b..78b8b0ac 100644 --- a/skyvern/core/script_generations/transform_workflow_run.py +++ b/skyvern/core/script_generations/transform_workflow_run.py @@ -101,8 +101,9 @@ async def transform_workflow_run_to_code_gen_input(workflow_run_id: str, organiz task_count=len(tasks), ) - # Single query for all actions + # Single query for all actions (returns desc order for timeline; reverse for chronological) all_actions = await app.DATABASE.get_tasks_actions(task_ids=task_ids_list, organization_id=organization_id) + all_actions.reverse() for action in all_actions: if action.task_id: actions_by_task_id[action.task_id].append(action) diff --git a/skyvern/forge/sdk/db/CLAUDE.md b/skyvern/forge/sdk/db/CLAUDE.md new file mode 100644 index 00000000..5e3778d8 --- /dev/null +++ b/skyvern/forge/sdk/db/CLAUDE.md @@ -0,0 +1,11 @@ +# Database Layer + +## `get_tasks_actions` — Sort Order is DESC (Intentional) + +`get_tasks_actions()` returns actions in **descending** `created_at` order. This is intentional — do NOT change it to ascending. + +**Why:** The primary consumer is `get_workflow_run_timeline()` in `service.py`, which feeds the frontend timeline UI. The frontend renders actions with `index={actions.length - index}` and expects newest-first (DESC) ordering for correct display numbering. + +**Script generation** (`transform_workflow_run.py`) needs chronological (ascending) order for code generation. It reverses the result with `all_actions.reverse()` after fetching. + +**History:** PR #8551 changed this to ASC for script gen correctness but broke the timeline. PR #8606 synced the change to cloud. This was fixed by reverting to DESC and reversing in the script gen caller instead. diff --git a/skyvern/forge/sdk/db/agent_db.py b/skyvern/forge/sdk/db/agent_db.py index 8a70114c..20d5b5cd 100644 --- a/skyvern/forge/sdk/db/agent_db.py +++ b/skyvern/forge/sdk/db/agent_db.py @@ -577,10 +577,9 @@ class AgentDB(BaseAlchemyDB): select(ActionModel) .filter(ActionModel.organization_id == organization_id) .filter(ActionModel.task_id.in_(task_ids)) - .order_by(ActionModel.created_at) + .order_by(ActionModel.created_at.desc()) ) actions = (await session.scalars(query)).all() - # Must match get_task_actions_hydrated: no empty_element_id so None element_ids stay None return [hydrate_action(action) for action in actions] except SQLAlchemyError: