Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions tests/test_scheduling.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,3 +680,49 @@ def test_get_program_name_extracted(self):
def test_read_program_state_extracted(self):
# read_program_state exists in the workflow but depends on file I/O
assert "read_program_state" in _funcs


# ---------------------------------------------------------------------------
# Workflow step ordering — repo-memory must be available before scheduling
# ---------------------------------------------------------------------------

class TestWorkflowStepOrdering:
"""Verify that the repo-memory clone step appears before the scheduling step.

The scheduling pre-step reads persisted state from repo-memory. If the
clone happens after scheduling, the script cannot see previous-run state,
causing incorrect selection/skip behaviour.
"""

CLONE_STEP = "Clone repo-memory for scheduling"
SCHED_STEP = "Check which programs are due"

def _load_steps(self):
"""Return the list of pre-step names from workflows/autoloop.md."""
import os
import re

wf_path = os.path.join(os.path.dirname(__file__), "..", "workflows", "autoloop.md")
with open(wf_path) as f:
content = f.read()
step_names = []
for m in re.finditer(r'^\s*-\s*name:\s*(.+)$', content, re.MULTILINE):
step_names.append(m.group(1).strip())
return step_names

def test_clone_step_exists(self):
"""A step that clones repo-memory for scheduling must exist."""
steps = self._load_steps()
assert self.CLONE_STEP in steps, (
f"Expected step '{self.CLONE_STEP}' not found. Steps: {steps}"
)

def test_clone_before_scheduling(self):
"""The repo-memory clone step must come before 'Check which programs are due'."""
steps = self._load_steps()
clone_idx = steps.index(self.CLONE_STEP)
sched_idx = steps.index(self.SCHED_STEP)
assert clone_idx < sched_idx, (
f"'{self.CLONE_STEP}' (index {clone_idx}) must come before "
f"'{self.SCHED_STEP}' (index {sched_idx}). Steps: {steps}"
)
22 changes: 22 additions & 0 deletions workflows/autoloop.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,28 @@ imports:
- shared/reporting.md

steps:
- name: Clone repo-memory for scheduling
env:
GH_TOKEN: ${{ github.token }}
GITHUB_REPOSITORY: ${{ github.repository }}
GITHUB_SERVER_URL: ${{ github.server_url }}
run: |
# Clone the repo-memory branch so the scheduling step can read persisted state
# from previous runs. The framework-managed repo-memory clone happens after
# pre-steps, so we perform an early shallow clone here.
MEMORY_DIR="/tmp/gh-aw/repo-memory/autoloop"
BRANCH="memory/autoloop"
mkdir -p "$(dirname "$MEMORY_DIR")"
REPO_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git"
AUTH_URL="$(echo "$REPO_URL" | sed "s|https://|https://x-access-token:${GH_TOKEN}@|")"
if git ls-remote --exit-code --heads "$AUTH_URL" "$BRANCH" > /dev/null 2>&1; then
git clone --single-branch --branch "$BRANCH" --depth 1 "$AUTH_URL" "$MEMORY_DIR" 2>&1
echo "Cloned repo-memory branch to $MEMORY_DIR"
else
mkdir -p "$MEMORY_DIR"
echo "No repo-memory branch found yet (first run). Created empty directory."
fi

- name: Check which programs are due
env:
GITHUB_TOKEN: ${{ github.token }}
Expand Down
Loading