Skip to content

feat: detect and fail loudly on patch failures in setup/migration/solution scripts - #144

Open
joellabes wants to merge 26 commits into
mainfrom
feature/patch-failure-detection
Open

joellabes wants to merge 26 commits into
mainfrom
feature/patch-failure-detection

Conversation

@joellabes

@joellabes joellabes commented Apr 15, 2026

Copy link
Copy Markdown
Collaborator

Problem: when switching to patch-based migrations, a patch could silently fail and cause downstream problems.

Change Summary

Harness exit code detection:

  • Adds run_script_checked helper that captures shell script exit codes via a temp file, since session.send_keys(..., block=True) only waits for tmux completion and never sees the command exit code
  • Wires exit code checking into all three script execution paths: setup.sh (→ SETUP_FAILED), migration.sh (→ SETUP_FAILED), solution.sh (→ UNKNOWN_AGENT_ERROR)
  • Captures terminal pane output before returning on setup failure (so logs are always written)

Shell script strict mode pattern:

  • Adds set -euo pipefail at the top of all setup.sh (65), migration.sh (13), and solution.sh (65) files so that an inner patch failure propagates to the script exit code
  • Adds set +euo pipefail after the last patch/fi block in all scripts — patches run under strict mode, post-patch dbt commands (which may legitimately fail in puzzle-state tasks) run permissively
  • Adds exit 0 at the end of all scripts so the final exit code is not inherited from a failing dbt run
  • Removes dbt run || true escape hatches (now redundant — strict mode is already off when dbt runs)

CI validation for Snowflake patches:

  • Adds scripts_python/validate_snowflake_patches.py — simulates the full Snowflake patch lifecycle for every task with a Snowflake variant: migration → setup patches → solution patches, each in an isolated temp directory
  • Adds .github/workflows/validate-patches.yml — runs on PRs that touch migrations, shared projects, or task patches
  • Coverage check ensures every .patch file in the repo is exercised (tasks with no Snowflake variant are excluded — covered by existing DuckDB CI)
  • Pre-deletes files that solution patches will create, to simulate the real container state

Other:

  • Consolidates analytics_engineering005 identical duckdb/snowflake setup patches into a single changes.patch
  • Updates task templates to include set -euo pipefail / set +euo pipefail / exit 0 by default

Before: airbnb001 Snowflake/dbt-fusion migration silently dropped a hunk and the trial continued into the agent phase with a corrupted project state.

After: Any failing patch call exits the script non-zero, which the harness catches and records as SETUP_FAILED rather than continuing.

Known Issues (pre-existing, caught by new CI)

  • airbnb003/setup/changes.snowflake.patch — hunks reference post-migration context lines that do not match; needs regeneration
  • analytics_engineering007/solutions/changes.patch — STRPTIME context line broken by migration in fact_inventory.sql

Test Plan

  • All 84 unit tests pass (uv run pytest tests/)
  • Run airbnb001 DuckDB variant — setup should fail with SETUP_FAILED if patch does not apply cleanly
  • Validate Snowflake patches CI passes (currently 2 pre-existing failures listed above)

🤖 Generated with Claude Code

joellabes and others added 26 commits April 15, 2026 11:59
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire run_script_checked into base_setup, migration_setup, and sage_agent
so failures in setup/migration/solution scripts propagate as errors rather
than being silently ignored.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds scripts_python/validate_snowflake_patches.py which simulates the
full Snowflake container lifecycle for every task with a Snowflake
variant: migration → setup patches → solution patches. Each task is
tested against all of its migrations (dbt and dbt-fusion independently).

A coverage check confirms every .patch file in the repo is exercised.
Patches in tasks with no Snowflake variant are explicitly excluded (they
are covered by the existing DuckDB CI).

Also adds .github/workflows/validate-patches.yml which runs the script
on PRs touching migrations, projects, patches, task.yaml, or the script
itself.

Currently finds 3 pre-existing failures:
- airbnb003/setup/changes.snowflake.patch (hunks fail post-migration)
- analytics_engineering007/solutions/changes.patch (STRPTIME context line)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Solution patches that create new files (--- /dev/null) fail if those
files already exist in the shared project (which holds the reference
"solved" state). Setup.sh removes them via shell commands — not patches
— to produce the puzzle state, so our patch-only simulation must do the
same.

Added files_created_by_patch() to parse /dev/null source hunks, and
pre_delete_created_files=True for solution patch validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously the pane was only captured after successful setup, so
setup failures produced empty panes/ directories and "No panes data
found" in the HTML report. Move the capture_pane call before the
setup failure checks so logs are always available for debugging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Patches must succeed (set -euo pipefail enforces this), but subsequent
dbt commands are allowed to fail for task-specific reasons.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on scripts

Patches run under strict mode and must succeed. Everything after
(dbt deps, dbt run, mkdir, etc.) runs permissively — same intent as
individual || true escapes but cleaner and harder to forget.

Also removes now-redundant || true from dbt commands that follow
patch blocks (e.g. airbnb001 setup.sh).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ripts

set +euo pipefail stops bash aborting mid-script on failure, but the
script's exit code is still the last command's exit code. Adding
exit 0 ensures scripts always succeed once patches have passed —
setup tasks like airbnb001 intentionally leave dbt in a broken state,
and solution scripts let tests determine correctness rather than
the exit code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These scripts had set -euo pipefail removed (1a79d1d) but still end
with dbt run which can legitimately fail as part of the puzzle state.
exit 0 ensures setup always succeeds once the puzzle is established.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous commit accidentally replaced entire file contents with just
'exit 0' instead of appending it. Restore the original content from
1a79d1d with exit 0 added at the end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The base changes.patch partially applies on Snowflake: Hunk #1 fails
(schema="main" context doesn't match post-migration "public"), but
Hunk #2 (trailing newline fix) succeeds. This left changes.snowflake.patch's
own Hunk #2 (same newline fix) failing under set -euo pipefail.

Fix: remove Hunk #2 from changes.snowflake.patch entirely — the newline
is already handled by changes.patch. Also corrects a trailing-space
mismatch in src_reviews.sql context that caused failures with BSD patch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The patch-based approach for dbt_project.yml was fragile: the file in
the Snowflake container differs from the shared project (missing the
42-line quickbooks model-refs block), causing the hunk to succeed with
a large offset but Hunk #2 (flags removal) to fail under set -euo pipefail.

Replace dbt_project.yml changes with a yq command that works regardless
of file layout. Remove that section from changes.patch entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant