Skip to content

llmXive Pipeline

llmXive Pipeline #331

name: llmXive Pipeline
on:
schedule:
# FR-001 default cadence: every 3 hours.
- cron: "0 */3 * * *"
workflow_dispatch:
# FR-002: optional inputs to scope a manual run to a project and/or stage.
inputs:
project_id:
description: "Restrict run to this project ID (e.g. PROJ-001-foo)."
required: false
type: string
stage:
description: "Run only this stage."
required: false
type: choice
options:
- ""
- brainstorm
- flesh_out
- idea_select
- project_initialize
- speckit_specify
- speckit_clarify
- speckit_plan
- speckit_tasks
- speckit_analyze
- speckit_implement
- research_review
- paper_initialize
- paper_speckit_specify
- paper_speckit_clarify
- paper_speckit_plan
- paper_speckit_tasks
- paper_speckit_analyze
- paper_speckit_implement
- paper_review
- status_report
permissions:
contents: write
issues: write
pull-requests: write
# Serialize all runs of this workflow on a branch (cron + manual dispatch) so two
# runs never advance the same project / race on the shared git state. A new run
# QUEUES behind the in-flight one (we never cancel mid-stage — that would strand a
# project's partial artifacts); GitHub keeps at most one pending run per group.
concurrency:
group: llmxive-pipeline-${{ github.ref }}
cancel-in-progress: false
jobs:
pipeline:
runs-on: ubuntu-latest
# Public repo → unlimited Action minutes. The plan stage (planner lit-search +
# plan convergence panel) can run well past 60 min; a short cap would time it
# out every cron and the project would never advance. Give slow stages room
# (well under GitHub's 6 h hard ceiling). A run still EXITS as soon as
# `--max-tasks` is satisfied — this is only a safety cap, not a target.
timeout-minutes: 330
steps:
- name: Checkout
uses: actions/checkout@v5
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.11"
cache: pip
- name: Install package
run: pip install -e .
- name: Preflight (FR-024 fail-fast preconditions)
env:
DARTMOUTH_CHAT_API_KEY: ${{ secrets.DARTMOUTH_CHAT_API_KEY }}
DARTMOUTH_API_KEY: ${{ secrets.DARTMOUTH_API_KEY }}
HF_TOKEN: ${{ secrets.HF_TOKEN }}
run: python -m llmxive preflight
- name: Run pipeline (stub until US1)
env:
DARTMOUTH_CHAT_API_KEY: ${{ secrets.DARTMOUTH_CHAT_API_KEY }}
DARTMOUTH_API_KEY: ${{ secrets.DARTMOUTH_API_KEY }}
HF_TOKEN: ${{ secrets.HF_TOKEN }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PROJECT_ID: ${{ github.event.inputs.project_id }}
STAGE: ${{ github.event.inputs.stage }}
run: |
ARGS=(run --max-tasks 5)
if [[ -n "${PROJECT_ID:-}" ]]; then ARGS+=(--project "$PROJECT_ID"); fi
if [[ -n "${STAGE:-}" ]]; then ARGS+=(--stage "$STAGE"); fi
python -m llmxive "${ARGS[@]}"
- name: Persist pipeline progress
# Commit + push whatever the pipeline produced (advanced stage, new
# artifacts, run-log telemetry) back to the branch this run checked out,
# so progress survives the ephemeral runner. always(): even a transient
# endpoint failure leaves the project parked at its stage (no partial
# artifacts — the stage guards unlink those) and we still keep the
# run-log. [skip ci] on the message avoids retriggering the pipeline.
#
# main is a HIGH-TRAFFIC branch (personality every 30m, pages deploy,
# submission-intake all push concurrently), so a plain push loses the
# race during this multi-minute run. Mirror the proven
# pipeline-personality pattern: pull --rebase + push in a 5x retry loop.
if: always()
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add state projects .llmxive || true
if git diff --cached --quiet; then
echo "no pipeline changes to commit"
else
git commit -m "chore(pipeline): persist run progress [skip ci]"
pushed=false
for i in 1 2 3 4 5; do
if git pull --rebase origin "${GITHUB_REF_NAME}" && git push origin "HEAD:${GITHUB_REF_NAME}"; then
pushed=true; break
fi
echo "push attempt $i failed (branch moved); retrying..." >&2
sleep $((5 * i))
done
[ "$pushed" = true ] || { echo "could not persist after 5 attempts" >&2; exit 1; }
fi