@@ -70,15 +70,19 @@ def test_runtime_live_e2e_workflow_supports_pr_and_manual_triggers():
7070
7171
7272def test_runtime_live_e2e_workflow_checks_out_pr_head_with_persist_credentials_false ():
73- """Security-critical: every live-e2e checkout must target the PR head SHA
74- and MUST set persist-credentials: false so GITHUB_TOKEN is not persisted
75- into .git/config where post-checkout steps could use it."""
73+ """Security-critical: every checkout in this workflow must target the PR head
74+ SHA and MUST set persist-credentials: false so GITHUB_TOKEN is not persisted
75+ into .git/config where post-checkout steps could use it.
76+
77+ Five checkouts in current shape: 4 live-e2e jobs (claude-live, claude-live-bare,
78+ claude-live-opus, codex-live) + 1 static-offline gate job. All five must
79+ satisfy the head-SHA + persist-credentials invariant."""
7680 text = read_workflow ()
7781
7882 checkout_count = text .count ("actions/checkout@v4" )
7983 head_ref_count = text .count ("ref: ${{ github.event.pull_request.head.sha }}" )
8084 # Each checkout must have a matching head-SHA ref.
81- assert checkout_count == 4
85+ assert checkout_count == 5
8286 assert head_ref_count == checkout_count , (
8387 f"Every checkout@v4 must pin to the PR head SHA "
8488 f"(found { checkout_count } checkouts, { head_ref_count } head-SHA refs)"
@@ -92,6 +96,23 @@ def test_runtime_live_e2e_workflow_checks_out_pr_head_with_persist_credentials_f
9296 )
9397
9498
99+ def test_runtime_live_e2e_workflow_gates_live_jobs_on_static_offline ():
100+ """Live-e2e jobs MUST NOT run if static-offline fails. Captain env-approval
101+ is expensive; gating on static-offline prevents wasted approvals on runs
102+ that would fail on static checks anyway."""
103+ text = read_workflow ()
104+
105+ # The gate job must exist inline (not only in the standalone ci-static.yml).
106+ assert " static-offline:\n " in text , (
107+ "Expected a static-offline job in runtime-live-e2e.yml as the needs: target"
108+ )
109+ # All 4 live jobs must declare needs: static-offline.
110+ needs_count = text .count ("needs: static-offline" )
111+ assert needs_count == 4 , (
112+ f"Expected all 4 live-e2e jobs to declare 'needs: static-offline', got { needs_count } "
113+ )
114+
115+
95116def test_runtime_live_e2e_workflow_narrows_default_permissions ():
96117 text = read_workflow ()
97118
0 commit comments