Skip to content

fix(ci): unblock Full Lifecycle Tests workflow#592

Merged
frankbria merged 3 commits into
mainfrom
fix/lifecycle-ci-pytest-timeout
May 19, 2026
Merged

fix(ci): unblock Full Lifecycle Tests workflow#592
frankbria merged 3 commits into
mainfrom
fix/lifecycle-ci-pytest-timeout

Conversation

@frankbria
Copy link
Copy Markdown
Owner

@frankbria frankbria commented May 19, 2026

Summary

  • Add pytest-timeout>=2.3.0 to dev dependencies — fixes pytest: error: unrecognized arguments: --timeout=1800 (root cause of the failing weekly run 25981293079)
  • Scope the lifecycle job to environment: staging so it can resolve ANTHROPIC_API_KEY (currently only set on the staging env, not at repo level). Without this, even after the timeout fix every test would be silently skipped by tests/lifecycle/conftest.py.
  • Capture pytest output via tee artifacts/pytest.log and use --basetemp=artifacts/pytest so the failure-artifact upload step yields something useful (previous /tmp/pytest-*/ path was always empty when pytest itself failed to start).

Test plan

  • CI green on this PR (lifecycle workflow does not run on PRs — non-event)
  • After merge / against this branch: gh workflow run lifecycle.yml --ref fix/lifecycle-ci-pytest-timeout -f mode=cli -f model=haiku
  • Verify the dispatched run completes (cost ~$0.10–0.50 on haiku) and --timeout=1800 is accepted by pytest
  • Confirm ANTHROPIC_API_KEY resolves (no "all tests skipped" outcome)

Notes

  • See tasks/node24-migration-plan.md (not in this PR — kept local) for follow-up on the Node.js 20 deprecation warning surfaced by the same run.

Summary by CodeRabbit

  • Chores

    • CI: lifecycle job now targets the staging environment; test runs capture full test output to artifacts and upload the artifacts directory with graceful handling when none are found.
  • Tests

    • Lifecycle tests: adjusted to use a safer batch-run mode and ensure tasks are promoted to READY before execution.
    • Added a pytest-timeout dependency to prevent hanging tests and improve stability.

Review Change Stack

Weekly scheduled run failed with `pytest: error: unrecognized
arguments: --timeout=1800` because pytest-timeout was passed to pytest
but never declared as a dev dependency.

- Add pytest-timeout>=2.3.0 to [project.optional-dependencies].dev
- Scope lifecycle job to `environment: staging` so it can resolve
  ANTHROPIC_API_KEY (currently only present on the staging env, not at
  repo level), otherwise conftest skips the entire suite.
- Capture pytest stdout/stderr to artifacts/pytest.log via tee and use
  --basetemp inside the workspace, so failure artifacts upload even
  when pytest fails to start.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 19, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 980447c3-8960-42cf-a123-0423446d5c7c

📥 Commits

Reviewing files that changed from the base of the PR and between e710ed5 and ef1e5b3.

📒 Files selected for processing (1)
  • tests/lifecycle/conftest.py

Walkthrough

The lifecycle CI job targets the staging environment; pytest output is saved to artifacts/pytest.log via tee with exit status preserved, and artifacts/ is uploaded with warn-if-missing. pytest-timeout is added to dev deps. Lifecycle tests drop --execute and the fixture promotes BACKLOG tasks to READY.

Changes

Lifecycle Workflow Improvements

Layer / File(s) Summary
Workflow environment and artifact capture
.github/workflows/lifecycle.yml
The lifecycle job now targets staging. The job creates artifacts/, runs pytest piping output to artifacts/pytest.log via tee while preserving pytest's exit status, and uploads artifacts/ with if-no-files-found: warn.
Test invocations and dev dependency
pyproject.toml, tests/lifecycle/test_cli_lifecycle.py
Added pytest-timeout>=2.3.0 to [project.optional-dependencies].dev. Two lifecycle CLI tests remove the --execute flag from cf work batch run invocations.
Fixture: promote tasks to READY
tests/lifecycle/conftest.py
initialized_workspace docstring updated; fixture runs cf tasks set status READY --all --from BACKLOG and asserts success so --all-ready picks up READY tasks.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 In staging fields the tests hop light,
Logs tucked in artifacts through day and night,
Timeouts set, pipes keep the truth,
No --execute sprinting uncouth,
The rabbit nods — the run looks right. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(ci): unblock Full Lifecycle Tests workflow' directly addresses the main objective: fixing a CI workflow that was failing due to missing pytest-timeout dependency and environment configuration issues.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/lifecycle-ci-pytest-timeout

Comment @coderabbitai help to get the list of available commands and usage tips.

@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

PR Review: fix(ci): unblock Full Lifecycle Tests workflow

This is a clean, well-targeted CI fix. The root cause diagnosis is correct and the changes are minimal and focused.


What works well

Root cause fix is correct. Adding pytest-timeout>=2.3.0 to pyproject.toml resolves the actual error (unrecognized arguments: --timeout=1800). The lock file correctly resolves to 2.4.0.

exit "${PIPESTATUS[0]}" is critical and correctly applied. Without it, the pipeline exit code would always be 0 (from tee), masking pytest failures entirely. This is the right fix for the pipe-into-tee pattern.

Artifact path fix (artifacts/ vs /tmp/pytest-*/). The old path was always empty when pytest failed to start — this is a genuine improvement that will make failure debugging useful.

if-no-files-found: warn prevents the artifact upload step from failing when the run was cancelled or bailed early. Good defensive addition.


One thing to verify before merging

The environment: staging addition assumes a GitHub environment named exactly staging is configured on this repo with ANTHROPIC_API_KEY scoped to it. If the environment name differs or has approval/protection rules that gate manual dispatches, lifecycle runs will hang waiting for approval or fail with a missing-environment error — which would be a regression from the current "silently skipped" behavior.

Confirm: does the repo have an environment named staging with ANTHROPIC_API_KEY set and no blocking protection rules?


Minor notes (non-blocking)

  • --basetemp=artifacts/pytest puts all test tmpfiles under artifacts/, which will be uploaded on failure. Intentional and fine, but some lifecycle tests may write sizeable temp files — worth watching artifact size on first runs.
  • An alternative to PIPESTATUS[0] would be adding set -o pipefail at the top of the run: block. Either approach is correct; PIPESTATUS is explicit and works well here.
  • The pytest-json-report dual-entry in uv.lock's requires-dist is a pre-existing artifact, not introduced by this PR.

Overall: LGTM pending confirmation that the staging environment exists and is correctly configured. The fix is minimal, correct, and directly addresses the reported CI failure.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/lifecycle.yml:
- Around line 89-97: The workflow step named "Upload test artifacts on failure"
currently uses a mutable tag actions/upload-artifact@v4; replace that mutable
tag with the corresponding full commit SHA for actions/upload-artifact to pin
the action for supply-chain security. Locate the step with name "Upload test
artifacts on failure" and the uses line referencing actions/upload-artifact@v4
and change it to uses: actions/upload-artifact@<FULL_COMMIT_SHA> (the specific
commit SHA from the actions/upload-artifact repository release you trust).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cd53c37a-e120-45bf-bf1d-91122d4a8846

📥 Commits

Reviewing files that changed from the base of the PR and between ac19311 and 214297e.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • .github/workflows/lifecycle.yml
  • pyproject.toml

Comment on lines 89 to +97
- name: Upload test artifacts on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: lifecycle-failure-${{ github.run_id }}
path: |
/tmp/pytest-*/
artifacts/
retention-days: 7
if-no-files-found: warn
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
# Verify unpinned GitHub Actions references in workflow files.
# Expected: no matches after pinning to full 40-char commit SHAs.

rg -n --glob '.github/workflows/*.yml' --glob '.github/workflows/*.yaml' \
  '^\s*uses:\s*[^@]+@(?!(?:[a-f0-9]{40})$).+'

Repository: frankbria/codeframe

Length of output: 326


🏁 Script executed:

#!/bin/bash

# First, find and examine the lifecycle.yml file
echo "=== Finding workflow files ==="
fd -e yml -e yaml '.github/workflows'

echo -e "\n=== Checking lifecycle.yml content (lines 85-100) ==="
if [ -f ".github/workflows/lifecycle.yml" ]; then
  sed -n '85,100p' ".github/workflows/lifecycle.yml"
else
  echo "lifecycle.yml not found"
fi

echo -e "\n=== Checking all uses: statements in workflow files ==="
rg -n 'uses:\s*' '.github/workflows/' -t yaml | head -20

echo -e "\n=== Checking specifically for action versions ==="
rg -n 'uses:\s*.*@' '.github/workflows/' -t yaml

Repository: frankbria/codeframe

Length of output: 5889


Pin actions/upload-artifact to a full commit SHA.

Line 91 uses a mutable tag (@v4), which weakens supply-chain security and provenance guarantees.

Suggested fix
-      - name: Upload test artifacts on failure
-        if: failure()
-        uses: actions/upload-artifact@v4
+      - name: Upload test artifacts on failure
+        if: failure()
+        uses: actions/upload-artifact@<FULL_COMMIT_SHA>
🧰 Tools
🪛 zizmor (1.25.2)

[error] 91-91: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/lifecycle.yml around lines 89 - 97, The workflow step
named "Upload test artifacts on failure" currently uses a mutable tag
actions/upload-artifact@v4; replace that mutable tag with the corresponding full
commit SHA for actions/upload-artifact to pin the action for supply-chain
security. Locate the step with name "Upload test artifacts on failure" and the
uses line referencing actions/upload-artifact@v4 and change it to uses:
actions/upload-artifact@<FULL_COMMIT_SHA> (the specific commit SHA from the
actions/upload-artifact repository release you trust).

The lifecycle CLI test passed `--execute` to `cf work batch run`, but
that flag only exists on `cf work start` (single task). Batch always
executes (use `--dry-run` to preview). The flag was silently broken
since the test was added (bdb6686) because the lifecycle suite was
never green in CI — the pytest-timeout dep error masked it.

Fixes the AssertionError surfaced by the first non-skipped lifecycle
run after the dep fix:

  Error: No such option: --execute
@frankbria
Copy link
Copy Markdown
Owner Author

Response to review feedback

Test failure on dispatched lifecycle run

The first non-skipped lifecycle run (now possible after the pytest-timeout fix) surfaced a latent test bug:

Error: No such option: --execute

tests/lifecycle/test_cli_lifecycle.py passed --execute to cf work batch run, but that flag only exists on cf work start (single task) — cf work batch run always executes (use --dry-run to preview). The test had been wrong since it was added (bdb6686) but the pytest-timeout dep error prevented it from ever actually running. Fixed in e710ed5.

CodeRabbit — SHA-pin actions/upload-artifact@v4

Deferred. This repo pins ~20 actions across 7 workflows by mutable tag; pinning just upload-artifact here would be inconsistent and not a real supply-chain improvement (cargo-cult security). The right move is a repo-wide SHA-pinning pass with Dependabot updates wired in, tracked separately from this CI fix.

@claude (review bot) — staging environment exists with ANTHROPIC_API_KEY?

Confirmed before the workflow change. From gh api repos/.../environments and gh secret list --env staging:

  • staging environment exists (created 2025-10-25)
  • ANTHROPIC_API_KEY is set on it (updated 2025-12-16)
  • No protection_rules configured, so workflow_dispatch runs won't hang waiting for approval

Repo-level secrets do not contain ANTHROPIC_API_KEY — confirming the original symptom. Scoping the job to environment: staging is the minimum change needed for the secret to resolve.

Re-dispatching the lifecycle workflow now that e710ed5 is on the branch.

@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Claude finished @frankbria's task in 38s —— View job


Updated Assessment

  • Read trigger comment and understand context
  • Review latest commits (e710ed5) and current file state
  • Post updated assessment

All open questions from the initial review are now resolved. Here's the updated status:

staging environment confirmation — confirmed. The environment exists, ANTHROPIC_API_KEY is scoped to it (not at repo level, which explains the original silent-skip symptom), and no protection rules will block workflow_dispatch runs. environment: staging is the correct minimal fix.

--execute flag fix (e710ed5) — correct. cf work batch run has no --execute flag; that flag is cf work start-only. The two call sites in tests/lifecycle/test_cli_lifecycle.py (lines 41 and 70) are now fixed. This was a latent bug that could never have been caught before because the --timeout=1800 failure prevented pytest from starting at all.

SHA-pinning deferral — reasonable. Pinning one action in isolation while ~20 others across 7 workflows remain on mutable tags creates inconsistency without meaningful security improvement. A repo-wide pinning pass with Dependabot automation is the right approach.


Final diff assessment

The three-commit stack is clean:

  • pyproject.toml: pytest-timeout>=2.3.0 correctly placed in dev group, resolves the root-cause error
  • .github/workflows/lifecycle.yml: environment: staging, artifact capture via tee, PIPESTATUS[0] exit code preservation, artifacts/ upload path — all correct and non-redundant
  • tests/lifecycle/test_cli_lifecycle.py: two --execute removals, no other changes

LGTM. This is ready to merge.

@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Follow-up review (continuing from prior comments): 1) Missed item - the --execute flag removal from cf work batch run in test_cli_lifecycle.py is correct; --execute belongs on cf work start per CLI_WIREFRAME, not batch run. Good fix bundled here. 2) Still-open: confirm the staging GitHub environment exists with ANTHROPIC_API_KEY set and no required-reviewer protection, or workflow_dispatch runs will block on approval. 3) CodeRabbit pinning note (non-blocking): actions/upload-artifact@v4 can be SHA-pinned for supply-chain hardening but does not need to block this PR. Status: ready to merge once staging environment is confirmed.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/lifecycle/test_cli_lifecycle.py`:
- Around line 68-72: The test calls cf("work", "batch", "run", ...) but does not
assert the command succeeded; capture the result of that cf invocation and
assert its exit code indicates success before proceeding to check task statuses.
Specifically, assign the call to a variable (e.g., result = cf(...)) and assert
on result.exit_code (or result.returncode depending on cf’s return object)
equals 0 (or use result.ok) immediately after the cf("work", "batch", "run",
...) call in tests/lifecycle/test_cli_lifecycle.py so failures fail fast and do
not allow later IN_PROGRESS checks to mask regressions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2a21f2f7-e9c2-4b30-9d02-89641d9760ac

📥 Commits

Reviewing files that changed from the base of the PR and between 214297e and e710ed5.

📒 Files selected for processing (1)
  • tests/lifecycle/test_cli_lifecycle.py

Comment on lines 68 to 72
cf(
"work", "batch", "run",
"--all-ready", "--execute", "--engine", "react",
"--all-ready", "--engine", "react",
timeout=1800,
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Assert batch execution success before checking task statuses.

Line 68-72 runs cf work batch run without validating the exit code. If that command fails, the later IN_PROGRESS check can still pass and hide a real regression.

Suggested fix
-        cf(
+        run_result = cf(
             "work", "batch", "run",
             "--all-ready", "--engine", "react",
             timeout=1800,
         )
+        assert run_result.returncode == 0, (
+            f"cf work batch run failed (exit {run_result.returncode}).\n"
+            f"stdout tail:\n{run_result.stdout[-1000:]}\n"
+            f"stderr:\n{run_result.stderr[-500:]}"
+        )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/lifecycle/test_cli_lifecycle.py` around lines 68 - 72, The test calls
cf("work", "batch", "run", ...) but does not assert the command succeeded;
capture the result of that cf invocation and assert its exit code indicates
success before proceeding to check task statuses. Specifically, assign the call
to a variable (e.g., result = cf(...)) and assert on result.exit_code (or
result.returncode depending on cf’s return object) equals 0 (or use result.ok)
immediately after the cf("work", "batch", "run", ...) call in
tests/lifecycle/test_cli_lifecycle.py so failures fail fast and do not allow
later IN_PROGRESS checks to mask regressions.

`cf tasks generate` creates tasks in BACKLOG status by design (the CLI
prints next-step hints suggesting `cf tasks set status READY --all` to
promote them). The lifecycle fixture skipped this step, so
`cf work batch run --all-ready` returned "No READY tasks found" and
exited 0 — the test passed the batch-run assertion but the agent
never actually built anything, and acceptance checks correctly
flagged the missing csv_stats.py.

Add the promotion step to initialized_workspace and update the
docstring (which still referenced the no-longer-valid --execute flag).
@claude
Copy link
Copy Markdown

claude Bot commented May 19, 2026

Follow-up Review (conftest.py / test_cli_lifecycle.py changes)

Building on my previous review — the latest commit adds fixture-level task promotion and drops the --execute flag. Both are correct.

cf tasks set status READY --all --from BACKLOG (conftest.py)

Verified the command exists in codeframe/cli/app.py and matches the exact signature used. This is the right fix: cf tasks generate creates tasks in BACKLOG state, so without this step --all-ready finds nothing to run and every lifecycle test silently passes (vacuously). This is a meaningful correctness fix.

Removal of --execute from batch run (test_cli_lifecycle.py)

Also correct. --execute is only a valid flag on cf work start, not cf work batch run. The tests were passing an unrecognized flag — its removal is a bug fix, not a behavioral change. Batch run already executes by default.

No new concerns

The two changes work together cleanly: the fixture ensures tasks are in READY state, and batch run picks them up without any flag-plumbing noise.

Previous review note still stands: confirm the staging GitHub environment is configured with ANTHROPIC_API_KEY and no blocking approval rules before merging.

@frankbria frankbria merged commit c7ea606 into main May 19, 2026
12 checks passed
@frankbria frankbria deleted the fix/lifecycle-ci-pytest-timeout branch May 19, 2026 18:55
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