Generate AAA orchestration tests (isolated or grouped) for ETL-to-Snowflake elements. Scoped to elements assigned to the current phase in ROADMAP.md. Use when the migrate-etl-package skill invokes test generation for an orchestration phase.
Generate and run functional equivalence tests for orchestration SQL (task graphs and stored procedures) based on the original control flow logic in the source definition file. Tests are generated BEFORE fixes are applied, serving as an independent oracle derived from the source-of-truth (the source definition file). All generated test artifacts are stored in <PACKAGE>/.migrate-etl-package/tests/orchestration/.
Autonomous mode: When spawned as a team agent, skip all interactive stopping points, make reasonable defaults for user-facing decisions, and document assumptions in the completion report. When done, send a
send_messagetomainsummarizing your results, and respond to anyshutdown_requestwithsend_messageusingtype: "shutdown_response"andapprove: true.
Tests follow the pattern with a two-phase Arrange:
etl_configuration infrastructure, mock dependency tables, schemas, ACT stored procedures. Run once per test file.TRUNCATE + INSERT for control variables and synthetic rows. Run before every ACT/ASSERT cycle to reset data to a known state.This sub-skill expects:
session_status.json — with source_file_path set, pending orchestration elements, and package_path.sql file path (from session status orchestration_file field)source_file_path in session status)ROADMAP.md — defines phases, element assignments, and test strategies (authored by migrate-etl-package)etl_configuration/ directory must exist at <CONVERTED_OUTPUT>/Output/SnowConvert/ETL/etl_configuration/CREATE TABLE, CREATE FUNCTION, and CREATE PROCEDURE on a user-provided database and schema<PACKAGE>/.migrate-etl-package/tests/orchestration/
<task_procedure_name>/
<element_name>.sql # Isolated test (one per element)
grouped_<group_name>.sql # Grouped test (shared ARRANGE for group)
...
test_report.md # Report consumed by orchestration-fixer
The value of this skill is in the ARRANGE and ASSERT sections — they encode what the element needs (infrastructure, seed data) and what correct behavior looks like (functional equivalence checks from the source definition file). The ACT section is a snapshot of the current orchestration SQL (the baseline). It is updated by the orchestration-fixer during the fix-test loop.
This skill (orchestration-test-gen):
ARRANGE:SETUP + ARRANGE:SEED + ASSERT written from source definition file (stable, written once)
ACT extracted from unfixed orchestration SQL (baseline snapshot)
Execute: ARRANGE:SETUP → ARRANGE:SEED → ACT → ASSERT
ACT may fail (expected), ASSERT may fail (expected)
Record baseline failures in test_report.md
Orchestration-fixer uses test file as workbench:
Fixer modifies ACT in the test file with proposed fix
First cycle: ARRANGE:SETUP → ARRANGE:SEED → ACT → ASSERT
Subsequent cycles: ARRANGE:SEED → ACT → ASSERT (SEED resets data)
If ASSERT fails → iterates (modifies ACT, re-runs SEED → ACT → ASSERT)
If ASSERT passes → applies proven fix to orchestration SQL file
Test file now contains the verified fix in its ACT section
Validation:
Re-runs test file as-is (ARRANGE:SETUP → ARRANGE:SEED → ACT → ASSERT)
Final artifact:
Test file with fixed ACT + source-derived assertions
Users can re-run this file at any time to verify functional equivalence
Copy this checklist and track your progress:
Orchestration Test Gen Progress:
- [ ] Phase 1: Configure test environment + analyze source definition file and orchestration SQL (phase-scoped)
- [ ] Phase 2: Arrange — generate test infrastructure (isolated or grouped)
- [ ] Phase 3: Assert — generate test assertions
- [ ] Phase 4: Act — build complete test files and execute
- [ ] Phase 5: Report results and write test_report.md
Check session_status.json for an existing test_environment key. If found, reuse the stored configuration (this happens during re-runs). If not found, detect the current connection details:
SELECT CURRENT_DATABASE(), CURRENT_SCHEMA(), CURRENT_ROLE(), CURRENT_WAREHOUSE();
Present them to the user:
Current connection details:
Database: <CURRENT_DATABASE>
Schema: <CURRENT_SCHEMA>
Role: <CURRENT_ROLE>
Warehouse: <CURRENT_WAREHOUSE>
All test objects will be created with CREATE OR REPLACE in this database and schema.
Would you like to use this connection for test execution?
If not, provide an alternative: DATABASE_NAME.SCHEMA_NAME
⚠️ STOPPING POINT (interactive mode): Use ask_user_question to get the user's answer. If running as an autonomous team agent, skip — detect the current connection and proceed with it as the test environment.
Store the configuration via CLI:
uv run --project <SKILL_DIR> python <SKILL_DIR>/scripts/track_status.py set-test-env <SESSION_JSON> <DATABASE> <SCHEMA>
Verify access by running a simple probe:
USE DATABASE <database>;
USE SCHEMA <schema>;
SELECT 1;
If this fails, ask the user to correct the database/schema name.
Read ROADMAP.md to identify the current phase number, then scope to the assigned elements:
Read session_status.json and filter elements where phase == <phase_num>. Only generate tests for these elements. Elements from other phases are ignored entirely in this invocation.
Skip test generation for elements that already have test files from a prior phase. Check for existing files at:
<PACKAGE>/.migrate-etl-package/tests/orchestration/<task_procedure_name>/<element_name>.sql<PACKAGE>/.migrate-etl-package/tests/orchestration/<task_procedure_name>/grouped_<group_name>.sqlIf a test file already exists, mark the element as already-tested and skip it.
Read the source definition file (extract: package variables, executable hierarchy, precedence constraints, event handlers — see {PLATFORM_DIR}/{orchestration_guide} for paths and namespaces) and the orchestration SQL in a single pass:
session_status.json for the elements list and statuses; read scan_results.json for the statements array (each CREATE TASK/CREATE PROCEDURE and its child elements, delimited by ---- Start/---- End tags)CONFIG block — contains variable definitions needed by child tasksDB.SCHEMA.TABLE) — these need rewriting in ACTRead both files ONCE. Build a mental index of element names → SQL bodies. Do NOT re-read per element during Phases 2-4.
Map source definition executables to orchestration SQL statements (phase-scoped only). Match by element name using the platform's element naming convention ↔ ---- Start tags. Classify each element as testable or skip per test-categorization.md. Mark disabled elements as skipped:disabled-in-source, Pipeline/dbt as skipped:dbt-dependency, and external deps as skip with documented coverage gaps.
Test generation plan for <package_name> — Phase <N>:
Test environment: <DATABASE>.<SCHEMA>
Phase elements: P (of T total), already tested: A, testable: X, skip: U
Element → Statement mapping: (list each with source element type, statement, test strategy)
Test strategy: I isolated test files, H grouped test files (G elements)
Proceed with test generation?
⚠️ STOPPING POINT (interactive mode): Ask user to confirm or adjust. If running as an autonomous agent, skip — proceed with the generated plan and document assumptions in the completion report.
Group phase elements by similarity before generating test files: (1) same EWI code + element type — generate one template, replicate for the rest; (2) same statement — read once, extract all element bodies in a single pass; (3) same test strategy (grouped) — already share a single test file. Process grouped tests first, then isolated tests grouped by EWI similarity.
For each testable element in the current phase, build the ARRANGE section. Check each element's test_strategy in session_status.json:
isolated elements, create one test file per element.grouped:<name> elements, create one test file per group with a shared ARRANGE that sets up infrastructure for all elements in the group.Identify what mock objects and data are needed:
From the source SQL definition (per the platform's sql_source_attribute):
FROM/JOIN) → DDL + data; Write (INSERT INTO/MERGE INTO) → DDL only; Truncate → DDL; Call → stub procedureFrom source variable definitions (for script/variable-dependent elements):
control_variables row using the platform's data type codesFrom assessment CSVs (supplementary): SqlObjects.csv, ObjectDependencies.csv, ObjectReferences.csv
For each read-dependency table, design 3-5 synthetic rows:
T-SQL to Snowflake type mapping: INT/BIGINT → NUMBER, VARCHAR(N)/NVARCHAR(N) → VARCHAR(N), DATETIME/DATETIME2 → TIMESTAMP_NTZ, DATE → DATE, BIT → BOOLEAN, FLOAT/REAL → FLOAT, DECIMAL(P,S) → NUMBER(P,S).
Isolated: <element_name>.sql per element. Grouped: grouped_<group_name>.sql per group — shared ARRANGE:SETUP + ARRANGE:SEED, then sequential ACT and ASSERT blocks per element.
Start each file with a metadata header (TEST, PACKAGE, GENERATED, ELEMENT(S), TEST STRATEGY, TARGET STATEMENT, SOURCE ELEMENT(S), TEST ENVIRONMENT). Build in two ARRANGE sub-sections:
Infrastructure Pre-Created:
control_variablestable,GetControlVariableUDF,UpdateControlVariable, and tagged helpers are pre-created by the orchestrator via_infrastructure.sql. Do NOT regenerate these. ARRANGE:SETUP = element-specific DDL only.
-- ============================================================
-- ARRANGE:SETUP
-- ============================================================
USE DATABASE <database>;
USE SCHEMA <schema>;
Use CREATE OR REPLACE on all objects.
CREATE SCHEMA IF NOT EXISTS <database>.<schema_name>. If user lacks CREATE SCHEMA, create mock tables in current schema and rewrite ACT references.test_act_<element_name>()._infrastructure.sql pattern.-- ============================================================
-- ARRANGE:SEED
-- ============================================================
Use TRUNCATE + INSERT INTO ... SELECT for all tables (Snowflake disallows TO_VARIANT() in VALUES). This is the data reset — runs before every ACT/ASSERT cycle.
control_variables from root task CONFIG. For procedure-based packages (SSC-FDM-SSIS0005), extract from the DELETE + INSERT ... UNION ALL block instead.Map every testable element (in this phase) to planned tests before writing assertions:
Coverage Matrix for <package_name> — Phase <N>:
Statement | Source Element Type | Test Strategy | Planned Tests
public.execute_sql_insert | Microsoft.ExecuteSQLTask | isolated | row_count, column_values, null_handling
public.script_set_variable | Microsoft.ScriptTask | grouped:setup | variable_assignment
public.dbt_data_flow | Microsoft.Pipeline | (skip) | dbt-test-gen
Rules: every phase element must appear; ExecuteSQLTask → row count + column values; ScriptTask → variable assertions; ForEachLoop → cumulative effects; Pipeline/external → explicitly marked with skip reason.
Define correct behavior from the original ETL logic in the source definition file — NOT the converted Snowflake SQL.
Trace synthetic input rows through the source SQL definition (per platform's sql_source_attribute) T-SQL logic to predict expected outputs. Generate assertions verifying the Snowflake code produces equivalent results (valid both before and after fixes).
For detailed templates by element type, see assertion-patterns.md. Key types:
Assertion density: One assertion per side effect. Each must have a positive check (expected state exists) AND a negative check (stale/pre-fix state does not remain). Assert exact row counts (COUNT(*) = N). See assertion-patterns.md for positive + negative pair templates.
Generate all assertions for a single element as a batched UNION ALL query (one snowflake_sql_execute call per element):
SELECT 'assert_01_{name}:' || CASE WHEN ({query}) THEN 'PASS' ELSE 'FAIL: ...' END AS result
UNION ALL
SELECT 'assert_02_{name}:' || CASE WHEN ({query}) THEN 'PASS' ELSE 'FAIL: ...' END
-- ... all assertions for this element
;
See assertion-patterns.md § Batched Assertion Format for templates.
For grouped tests, label each element's ASSERT block: -- ASSERT: <element_name> (1 of N in group <group_name>). Each element gets numbered assertions (assert_01_, assert_02_, etc.) when it has multiple side effects.
Strict assertion rule: Encode ONLY expected correct behavior from the source definition file. Do NOT write dual-outcome assertions. Pre-fix failures are expected and recorded in test_report.md.
Every assertion MUST include:
-- Assert: <what is being checked>
-- (Trace: <source element> → <transformation logic> → <expected result>)
Use the platform profile's traceability_format for the exact format.
Add the ACT section to each test file. Extract the element body from the current orchestration SQL and wrap as a stored procedure:
LET variable declarations, business logic, and UpdateControlVariable calls<database>.<schema>)EXECUTE DBT PROJECT statementsCREATE OR REPLACE PROCEDURE <database>.<schema>.test_act_<element_name>() ... (see stored-procedure-wrapping.md)CALL <database>.<schema>.test_act_<element_name>();For grouped tests, include one ACT block per element executed sequentially, then one ASSERT block per element. No explicit CLEANUP section — data reset is handled by re-running ARRANGE:SEED.
Execute each test file section-by-section using the snowflake_sql_execute MCP tool. See mcp-test-execution.md for the full execution protocol (section ordering, grouped file handling, infrastructure dedup).
Execution protocol (one snowflake_sql_execute call per step):
CREATE OR REPLACE PROCEDURE + CALLFor grouped test files (grouped_<group_name>.sql): execute all ACT blocks in order, then all ASSERT blocks separately, attributing results per element.
Categorize each failure:
.migrate-etl-package/tests/orchestration/, retry up to 3 timesDo NOT call
track_status.pydirectly. The orchestrator reads your baseline artifact and updatessession_status.jsonafter all test-gen agents complete. Your artifact's Status column is the source of truth.
Read session_status.json to verify all phase elements have terminal statuses.
Session cleanup — test objects remain in the user-provided schema. The user is responsible for cleanup; tests can be re-run (ARRANGE:SETUP is idempotent, ARRANGE:SEED resets data).
Present summary:
Orchestration test generation complete for <package_name> — Phase <N>:
Test environment: <DATABASE>.<SCHEMA>
Phase elements: P
Already tested (prior phase): A
Elements tested: T
Elements skipped: U (dbt-dependency: D, external: E, disabled: B)
Isolated tests: I (P passed, Q failed baseline)
Grouped tests: G files covering H elements
Infrastructure failures: R
Baseline failures (to be resolved by orchestration-fixer):
- <element_name> [isolated]: <failure reason> (source element: <source_name>)
- <group_name> [grouped]: <failure reason> (elements: <list>)
Write <PACKAGE>/.migrate-etl-package/tests/orchestration/test_report.md with:
MANDATORY self-check before returning — verify that {PACKAGE}/.migrate-etl-package/tests/orchestration/<task_procedure_name>/ contains at least 1 .sql test file and test_report.md exists. If ANY file is missing, generate it before returning. If you cannot write files, include full contents in your completion message so the orchestrator can write them.
Return to parent skill (migrate-etl-package/SKILL.md) for orchestration fixing.
These files are NOT loaded upfront. Read them only when their content is needed for the current step:
| File | When to load |
|---|---|
{PLATFORM_DIR}/{orchestration_guide} | Phase 1 — when reading the source orchestration structure |
| assertion-patterns.md | Phase 3 — when generating ASSERT SQL |
| stored-procedure-wrapping.md | Phase 2/4 — when wrapping element logic in stored procedures |
| test-categorization.md | ROADMAP creation — when understanding isolated vs grouped strategy |
| infrastructure-dedup.md | Phase 2 — when phase has 5+ isolated elements |
| mcp-test-execution.md | Phase 4 — when executing test files against Snowflake |
PACKAGE)N) and batch ID ({B}, format: B{P}.{M}, e.g., B1.1)TASK_SCHEMA) — pre-assigned, use for ALL created objectsDATABASE)ELEMENT_LIST) with archetype/clone annotationsSTRATEGY): isolated or grouped:<name>---- Start block/---- End block pairs. Non-container elements use ---- Start with no closing tag (body ends at next tag). See reference/orchestration-tags.md. (READ-ONLY)PLATFORM_DIR/ (element-types.md, ewi/)PHASES_DIR/SKILL_DIRROADMAP.md batch context section (provided in prompt)snowflake_sql_execute MCP tool — no Python connectionsPLATFORM_DIR/ewi/ when encountering markersThe following objects are ALREADY created in the batch schema by the orchestrator before this agent starts:
control_variables tableGetControlVariableUDF functionUpdateControlVariable procedureDo NOT include any of these in ARRANGE:SETUP. SETUP = element-specific DDL only.
PACKAGE/.migrate-etl-package/tests/orchestration/<task_procedure_name>/PHASES_DIR/baseline_batch_{B}.mdWrite artifacts incrementally — one element section appended at a time. The orchestrator handles partial artifacts: completed elements are already on disk and will not be re-processed on retry.
This agent MUST generate and execute test files for ALL assigned elements in the ELEMENT_LIST. Test generation cannot be skipped based on:
Every element MUST appear in the baseline report with a status (test-passed, test-failed, skipped with valid reason). Silent omission is a protocol violation.
Only these skip reasons are accepted by track_status.py:
disabled-in-source — element is disabled in the source definitioncontainer-only — element is a structural container with no executable logicfile-io-noop — element performs file I/O not applicable in Snowflakedbt-dependency — element is an EXECUTE DBT PROJECT invocationexternal-dependency — element has unfixable external dependenciesScriptTasks with no Snowflake equivalent (filesystem I/O, COM objects) are NOT skipped. They are test-failed with a reason. The TDD loop must be attempted.
Process one element at a time in the order provided:
Skip check — look for the platform's disabled_marker in the source definition for this element, or check if the element body in the orch SQL is entirely commented-out with the disabled marker visible. If disabled:
skipped, reason disabled-in-sourceGenerate test file:
TASK_SCHEMA as the test schema (not DATABASE.PUBLIC or any other schema)PACKAGE/.migrate-etl-package/tests/orchestration/<task_procedure_name>/set-test-env — use TASK_SCHEMA directlyRun baseline — execute in order: ARRANGE:SETUP → ARRANGE:SEED → ACT → ASSERT (batched)
Write test file to disk immediately after baseline run — do not defer.
Append this element's section to the baseline report immediately — do not defer.
Proceed to next element.
PHASES_DIR/baseline_batch_{B}.mdFor the first element processed, write the file header:
# Baseline Report — Batch {B}, Phase {N}
## Summary
| Element | Test File | Baseline Result | Failure Details |
|---------|-----------|-----------------|-----------------|
For subsequent elements, append the next table row directly.
Use this format for each row:
| element_name | test_file_path | passed / failed / skipped | failure summary or — |
After the summary table, write an Element Details section with enriched context for downstream fix agents:
## Element Details
### element_name — PASSED|FAILED|SKIPPED
- **EWI Codes**: {codes from ROADMAP task section, e.g. SSC-EWI-SSIS0004}
- **Issue Summary**: {one-line description}
- **Source Excerpt** (from source definition):
{relevant source definition excerpt for this element}
- **Failing Assertions**: {list of assert names that failed and their FAIL messages, or "—" if passed}
- **Applicable EWI Guide Section**: {e.g. "SSC-EWI-SSIS0004 § ScriptTask with DATEDIFF pattern", or "—" if no EWI}
This enriched baseline report enables fix agents to start fixing immediately without re-reading source files.
failed with a descriptive reason in the baseline report and move onAfter completing each element, assess your remaining context. If you have processed several elements and feel context pressure — large accumulated test outputs, many SQL results loaded into memory — stop after the current element's artifacts are written and report partial completion. The orchestrator will spawn a continuation agent for the remaining elements.
Prefer stopping early with artifacts on disk over running to exhaustion with nothing written.
When stopping early, send a completion message that includes:
"partial-completion: context pressure after N elements"When your work is complete, your agent will automatically return results to the orchestrator.
If you receive a shutdown_request, use send_message with type: "shutdown_response" and approve: true.