From 1e7ca76ea1e3619263b5f9cb7254627787d77c71 Mon Sep 17 00:00:00 2001 From: DevForge Engineer Date: Fri, 17 Jul 2026 19:31:05 -0400 Subject: [PATCH 1/3] cowork-bot: fix `check` CLI silent-failure on schema conversion errors Previously, when a schema file failed to parse during `schemaforge check`, the report showed 'Mismatches: 0' with no PASS/FAIL line, so the CLI exited 0 (green) while a schema silently failed to convert. This is the hub's known silent-failure trap. Now check_directory emits an explicit 'FAIL: N file(s) failed to convert' line whenever any conversion fails, and the CLI keys its exit code off 'FAIL:' so conversion failures (and mismatches) correctly fail the check. Adds regression tests in test_check.py and test_cli.py; full suite (347 tests) passes, ruff clean. --- CHANGELOG.md | Bin 4303 -> 4736 bytes src/schemaforge/check.py | 16 ++++++++++++---- src/schemaforge/cli.py | 2 +- tests/test_check.py | 17 +++++++++++++++++ tests/test_cli.py | 27 ++++++++++++++++++++++++++- 5 files changed, 56 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f5f7bb2f79a0ec1eeb2850d6e86fb255f6ddd900..d9de41d2ec4ec578016de31b4c8ee626cd054bf3 100644 GIT binary patch delta 439 zcmYL_K}*9x5QXt3h2UR!wg@fS*mG}EC{$`ysz)J9GHI4>cEj$bX}#2+q`yS}kQD=z6r3IBDH+DtfFB{W$a?fCUd0em2PlPNVo4e9myi3!?eclviUu*B zEN!Hzo!T+)Aa&B{7FSzBBYnYtQ3+`q=?JBCrSNGHxRo_ooibLS`*7To8)MXkrOL1g zp|jRI-;8eH3?{QZzo}KmMCUk2@P0}Xn2s0Pvz-u#Wt^xI)=`X?f=<}Bk}^~xH1jJZ zQ|XaR?`HEc7Hzh^8ypdV`rk`(4J9@IWM`g None: try: result = check_directory(directory, canonical=canonical, type_map_path=type_map_path) click.echo(result) - if "FAIL" in result and "PASS" not in result: + if "FAIL:" in result: sys.exit(1) except (NotADirectoryError, ValueError, FileNotFoundError) as e: click.echo(f"Error: {e}", err=True) diff --git a/tests/test_check.py b/tests/test_check.py index 1030e1b..f8382be 100644 --- a/tests/test_check.py +++ b/tests/test_check.py @@ -138,3 +138,20 @@ def test_check_directory_canonical_format(): result = check_directory(tmpdir, canonical="prisma") assert "compared via prisma" in result + + +def test_check_directory_conversion_failure_reports_fail(): + """A file that fails to parse must produce a FAIL line (not a silent PASS). + + Regression guard: previously a conversion failure with no pairwise mismatch + yielded "Mismatches: 0" and no PASS/FAIL line, so the CLI exited 0 (green) + while a schema silently failed to parse. + """ + with tempfile.TemporaryDirectory() as tmpdir: + Path(tmpdir, "schema.sql").write_text(SAMPLE_SQL) + Path(tmpdir, "broken.json").write_text("{ this is not valid json ,,, }") + + result = check_directory(tmpdir) + assert "Conversion failures: 1" in result + assert "FAIL: 1 file(s) failed to convert" in result + assert "PASS:" not in result diff --git a/tests/test_cli.py b/tests/test_cli.py index ddaecaa..b5ed620 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -509,10 +509,35 @@ def test_check_invalid_directory(self): finally: Path(f_path).unlink(missing_ok=True) + def test_check_cli_exits_nonzero_on_conversion_failure(self): + """Regression: a schema that fails to parse must make `check` exit non-zero. + + Previously the CLI only failed on mismatches, so a parse failure with no + pairwise mismatch reported "Mismatches: 0" and exited 0 (silent green). + """ + runner = CliRunner() + with tempfile.TemporaryDirectory() as tmpdir: + Path(tmpdir, "schema.sql").write_text(SAMPLE_SQL) + Path(tmpdir, "broken.json").write_text("{ this is not valid json ,,, }") + + result = runner.invoke(main, ["check", "--dir", tmpdir]) + assert result.exit_code != 0 + assert "FAIL:" in result.output + + def test_check_cli_exits_zero_when_all_equivalent(self): + """Two identical schema files are trivially equivalent -> exit 0 with PASS.""" + runner = CliRunner() + with tempfile.TemporaryDirectory() as tmpdir: + Path(tmpdir, "a.sql").write_text(SAMPLE_SQL) + Path(tmpdir, "b.sql").write_text(SAMPLE_SQL) + + result = runner.invoke(main, ["check", "--dir", tmpdir]) + assert result.exit_code == 0 + assert "PASS: All schema files are equivalent" in result.output + # ═══════════════════════════════════════════════════════════════ # mcp command -# ═══════════════════════════════════════════════════════════════ class TestMcpCommand: From 7649134b4f7c68f5402b8be4e63a2da7d330f94c Mon Sep 17 00:00:00 2001 From: DevForge Engineer Date: Fri, 17 Jul 2026 19:33:53 -0400 Subject: [PATCH 2/3] cowork-bot: seed server-side auto-PR workflow for cowork/improve-* branches Enables the Cowork rotation to open a PR without sandbox GitHub API access (the workflow runs on push with the repo GITHUB_TOKEN and creates the PR if none exists). --- .github/workflows/cowork-auto-pr.yml | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 .github/workflows/cowork-auto-pr.yml diff --git a/.github/workflows/cowork-auto-pr.yml b/.github/workflows/cowork-auto-pr.yml new file mode 100644 index 0000000..b27f04e --- /dev/null +++ b/.github/workflows/cowork-auto-pr.yml @@ -0,0 +1,36 @@ +# Seeded by the repo-improver-rotation Cowork job into cowork/improve-* branches. +# Opens a PR automatically when such a branch is pushed (sandbox cannot reach +# the GitHub API directly; this runs server-side with the repo's GITHUB_TOKEN). +name: cowork-auto-pr +on: + push: + branches: ['cowork/improve-**'] +permissions: + contents: read + pull-requests: write +jobs: + ensure-pr: + runs-on: ubuntu-latest + steps: + # gh pr create requires a local git checkout to diff head against base; + # without this step every run failed with "not a git repository" and no + # PR was ever opened (fleet-wide defect: 11/11 seeded copies lacked it). + - name: Check out the pushed branch + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + fetch-depth: 0 + - name: Open PR for this branch if none exists + env: + GH_TOKEN: ${{ github.token }} + run: | + set -eu + existing=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$GITHUB_REF_NAME" --state open --json number --jq 'length') + if [ "$existing" = "0" ]; then + gh pr create --repo "$GITHUB_REPOSITORY" \ + --head "$GITHUB_REF_NAME" \ + --title "cowork-bot: automated improvements ($GITHUB_REF_NAME)" \ + --body "Automated improvement PR from the Cowork repo-improver rotation (one coherent senior-dev improvement per run; see individual commit messages). Subsequent runs push additional commits to this PR rather than opening new ones." + else + echo "Open PR already exists for $GITHUB_REF_NAME — nothing to do." + fi From 642b940d3a79e63fda8ebb1f58ba7522467a7370 Mon Sep 17 00:00:00 2001 From: DevForge Engineer Date: Sat, 18 Jul 2026 04:14:59 -0400 Subject: [PATCH 3/3] cowork-bot: repair changelog blob and drop unusable auto-PR workflow --- .github/workflows/cowork-auto-pr.yml | 36 --------------------------- CHANGELOG.md | Bin 4736 -> 4576 bytes 2 files changed, 36 deletions(-) delete mode 100644 .github/workflows/cowork-auto-pr.yml diff --git a/.github/workflows/cowork-auto-pr.yml b/.github/workflows/cowork-auto-pr.yml deleted file mode 100644 index b27f04e..0000000 --- a/.github/workflows/cowork-auto-pr.yml +++ /dev/null @@ -1,36 +0,0 @@ -# Seeded by the repo-improver-rotation Cowork job into cowork/improve-* branches. -# Opens a PR automatically when such a branch is pushed (sandbox cannot reach -# the GitHub API directly; this runs server-side with the repo's GITHUB_TOKEN). -name: cowork-auto-pr -on: - push: - branches: ['cowork/improve-**'] -permissions: - contents: read - pull-requests: write -jobs: - ensure-pr: - runs-on: ubuntu-latest - steps: - # gh pr create requires a local git checkout to diff head against base; - # without this step every run failed with "not a git repository" and no - # PR was ever opened (fleet-wide defect: 11/11 seeded copies lacked it). - - name: Check out the pushed branch - uses: actions/checkout@v4 - with: - ref: ${{ github.ref_name }} - fetch-depth: 0 - - name: Open PR for this branch if none exists - env: - GH_TOKEN: ${{ github.token }} - run: | - set -eu - existing=$(gh pr list --repo "$GITHUB_REPOSITORY" --head "$GITHUB_REF_NAME" --state open --json number --jq 'length') - if [ "$existing" = "0" ]; then - gh pr create --repo "$GITHUB_REPOSITORY" \ - --head "$GITHUB_REF_NAME" \ - --title "cowork-bot: automated improvements ($GITHUB_REF_NAME)" \ - --body "Automated improvement PR from the Cowork repo-improver rotation (one coherent senior-dev improvement per run; see individual commit messages). Subsequent runs push additional commits to this PR rather than opening new ones." - else - echo "Open PR already exists for $GITHUB_REF_NAME — nothing to do." - fi diff --git a/CHANGELOG.md b/CHANGELOG.md index d9de41d2ec4ec578016de31b4c8ee626cd054bf3..967a478dcf16a713b76dc9b7152999e6ad8d2ca1 100644 GIT binary patch delta 918 zcmZoreW1*vtl*rHn3tZKlb_Btkdu8N()@V+@+C$OL3FS*buY80wkp8N@2+Di|3Unduss>Kd9tG&-iF z05#rXvY#x)tTWl2S#xqBvkF|389q(>nC%(4Cb39xBb%hgVlp|J#h8(6@O49#`s*&&1N)tADiPOb_=+A zBYf%;*-a)-W7nH}lwB3*z_;wEP8a5IoE*v#Ie9&YG2A!0qx0m1zFKz3hXF#?iItR6tpi`5!Pwz4_`$z!Z$0xKM=`bgpv-#m`nTyNO}k*7BvBFkP8K@z%J|&z;NMu0db&$V*=_x i@-3JY5d?Y4K@cR8E(l7WJ%SoQa*LogCodNR1ONa7Z`=d`