Skip to content

ci(phase-validation): the summary step SyntaxErrors on every run, and a quote-blind unescape reports it clean on CI #15152

Closed

Description

@mrveiss

Verdict

phase_validation.yml's summary step dies with a SyntaxError on every run under the Python the workflow pins, and prints Results not available instead of the maturity figure. The import-smoke guard reports that same program clean on CI, because _unescape_shell deletes the exact backslashes that break it.

Two defects, one of which is the reason the other is invisible.

1. The workflow step is broken at runtime

.github/workflows/phase_validation.yml:126 runs a single-quoted python3 -c. sh performs no escape processing inside '...', so the \" sequences reach the interpreter as backslashes — and a backslash inside an f-string replacement field is a SyntaxError on every Python version, including 3.12+ where PEP 701 relaxed the surrounding rules.

Run verbatim under bash with the pinned interpreter (setup-python at phase_validation.yml:52 pins 3.14):

SyntaxError: unexpected character after line continuation character

With the step's own error handling attached (2>/dev/null || echo "Results not available"), the observed output is:

Results not available

So the job summary has been reporting the fallback string in place of the maturity figure, silently, for as long as the line has existed. This is a defect in what a workflow reports and does not depend on any test.

2. _unescape_shell is quote-blind, which manufactures a false green

repo_tests/test_ci_import_smoke_paths_14252.py's _unescape_shell applies double-quote shell unescaping unconditionally, including to single-quoted programs that sh never escape-processes. It rewrites the program into something the step does not run — and rewrites it in the direction that hides the bug.

Measured against the unmodified base, same checkout, same workflow file:

Interpreter unparseable programs the guard reports imports it extracted for the broken step
3.10 [('phase_validation.yml', …)] []
3.14 [] ['json']

On the interpreter CI actually runs, the guard reports zero unparseable programs and claims to have checked json for a step that cannot start. A guard that rewrites its subject into something that passes is indistinguishable from a guard that passes.

The PEP 701 measurement — record this so nobody re-derives it

Two different strings are involved, and conflating them is what made this hard to see.

  • A — what sh hands the interpreter (backslashes intact): … print(f"…{r.get(\"overall_maturity\", 0):.1f}%")
  • B — what the extractor produces after quote-blind unescaping (\" → "): … print(f"…{r.get("overall_maturity", 0):.1f}%")
3.10 3.11 3.12 3.14
A (runtime truth) SyntaxError: f-string expression part cannot include a backslash SyntaxError: same SyntaxError: unexpected character after line continuation character SyntaxError: same
B (extractor output) SyntaxError: f-string: unmatched ( SyntaxError: same parses parses

A fails on every version. B is version-dependent — illegal before 3.12, legal from 3.12 under PEP 701's relaxation of nested quotes.

Consequences worth stating plainly:

  • CI runs 3.12/3.14, so shard 12 is green on the base. Any claim that this guard is red on Dev_new_gui, or that its failure is "environment-independent — no package, Python version or checkout layout is involved", is wrong. It was red only on a 3.10 checkout. This is precisely the trap chore(devenv): local verification runs fastapi 0.135.2 against a declared floor of >=0.141.1 — a local pass carries no information about CI #15091 records: a version-dependent failure whose message carries no version, read as environment-independent.
  • The regex _INLINE_PYTHON does have escape-awareness ((?:\\.|(?!\1).)*) and the capture is not truncated — verified: the capture ends on %") and the next character after the match is a space. The fragment that appears in the failure message is code[:60], display truncation in the assertion itself. Its docstring still describes the pre-fix behaviour and should be corrected; that stale text is what produced two independent mis-diagnoses.

Acceptance criteria

  1. phase_validation.yml's summary step prints the maturity figure under the pinned interpreter, demonstrated by running the line.
  2. _unescape_shell applies double-quote rules only to double-quoted programs; a single-quoted program is returned exactly as sh delivers it. Asserted directly, not via the workflow that happens to exercise it.
  3. _imported_names cannot report "no imports" for input it could not read.
  4. Every python -c site in every workflow is captured as one whole shell argument, asserted — a site the pattern cannot close on currently produces no scan entry at all.
  5. The stale docstrings on _INLINE_PYTHON and test_every_extracted_program_parses are corrected, and the parse-failure message names the interpreter version so a version-dependent failure cannot again read as environment-independent.

Related

Activity

  1. github-actions commented on Aug 28, 2026

    @github-actions
    Contributor

    PR #15128 (merged to Dev_new_gui) references this issue with a close keyword.

    fix(ci): stop a broken workflow step reporting clean, and the guard rewriting it into something that parses (#15152)

    If this issue is fully resolved, close it manually. If work remains, no action is needed.

  2. mrveiss commented on Aug 28, 2026

    @mrveiss
    OwnerAuthor

    Closing — verified against the merged base, not the PR diff

    Delivered by #15128, merged as 6af2946cc on Dev_new_gui. All references read from origin/Dev_new_gui.

    # Criterion (verb) Verdict Kind Evidence on the merged base
    1 Summary step prints the maturity figure under the pinned interpreter, demonstrated by running the line met executed .github/workflows/phase_validation.yml:130 is now backslash-free — the value is bound to m first and formatted with "{:.1f}%".format(m). I ran that exact single-quoted argument through sh -c, against a phase_validation_results.json holding overall_maturity: 73.25, on the pinned 3.14.6 (phase_validation.yml:52 pins 3.14) and on 3.12.13 / 3.10.12. All three print **Overall System Maturity:** 73.2%; none hits the Results not available fallback
    2 _unescape_shell applies double-quote rules only to double-quoted programs, asserted directly met unit repo_tests/test_ci_import_smoke_paths_14252.py:95-112 returns code unchanged unless quote == '"'; asserted directly at :293 test_a_single_quoted_program_keeps_its_backslashes, which checks both branches on the same literal — identity under ', unescaped under "
    3 _imported_names cannot report "no imports" for input it could not read met unit :154-172 parses without a try, so a SyntaxError leaves at the call site; asserted at :305 test_an_unreadable_program_raises_rather_than_reporting_no_imports via pytest.raises(SyntaxError)
    4 Every python -c site in every workflow captured as one whole shell argument, asserted met static/unit _INLINE_PYTHON_INVOCATION (:52) enumerates every invocation ignoring quoting, _ARGUMENT_END (:56) proves the capture ended on a real argument boundary, _uncaptured_invocations (:126) reports both "no match at all" and "capture ends mid-argument"; asserted at :263 test_no_python_dash_c_invocation_escapes_the_extractor. A heredoc form is handled separately (_HEREDOC_PYTHON, :59)
    5 Stale docstrings corrected; parse-failure message names the interpreter version met static The _INLINE_PYTHON comment block (:38-51) now names code[:60] as display truncation in the assertion rather than describing the capture as truncated; test_every_extracted_program_parses (:236) documents the PEP 701 version dependence, and its failure message interpolates sys.version_info.major/minor (:255-260) with an explicit instruction to confirm on the version CI runs before calling it a base failure

    The false green is also pinned directly: test_phase_validation_inline_programs_are_actually_checked (:279) requires that workflow's programs both parse and yield imports, so the step cannot be made green by rendering it invisible to the extractor.

    What is not proven here

    • No host evidence, and no CI-run evidence for criterion 1 specifically. I demonstrated the line by executing it locally on the pinned 3.14.6 with a synthetic results file — not by reading a phase_validation workflow run's job summary on Dev_new_gui. The first real run of that workflow after this merge is where the maturity figure should be visible in place of Results not available; that observation is still outstanding.
    • The guard's own CI status is as reported on the PR; I re-ran no shard.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions