Skip to content

fix(ci): the unattended repair is felix-home's template again, pinned to the Felix commit it was copied from - #24

Merged
LeavesJ merged 2 commits into
mainfrom
claude/unattended-template-pin
Sep 24, 2026
Merged

LeavesJ merged 2 commits into
mainfrom
claude/unattended-template-pin

Conversation

@LeavesJ

@LeavesJ LeavesJ commented Sep 24, 2026 •

Copy link
Copy Markdown
Owner

A workflow file, so this one is yours to merge. It supersedes #22.

.github/workflows/unattended.yml is now felix-home's engine/harness/templates/unattended.yml at 685d37beedc4590a989ccd145db3069af8e7f76a, copied whole, with its two per-repository placeholders filled in: {{FELIX_REPO}} is LeavesJ/felix-home and {{FELIX_REF}} is 685d37beedc4590a989ccd145db3069af8e7f76a. Nothing else differs, and anyone can check that:

diff <(git -C ~/Documents/Felix show 685d37beedc4590a989ccd145db3069af8e7f76a:engine/harness/templates/unattended.yml | sed -e 's#{{FELIX_REPO}}#LeavesJ/felix-home#' -e 's#{{FELIX_REF}}#685d37beedc4590a989ccd145db3069af8e7f76a#') .github/workflows/unattended.yml

What was wrong on main. Actions runs a step that names no shell as bash -e {0}: every step of gate run 35866814309 prints shell: /usr/bin/bash -e {0}. The scope step ran set -uo pipefail; felix repair | tee, so any non-zero exit ended the step at the pipe before rc was read. On main today, a reached ceiling (2) or a red path (3) fails the job and labels nothing, and every other non-zero exit fails it the same way. The step cut out of this file and run under bash -e against a stub felix:

felix repair exit main this PR
0, a brief proceed proceed
1, runs unreadable step exits 1 job fails
2, ceiling reached or uncounted step exits 2, no label needs-founder, stop
3, red path step exits 3, no label needs-founder, stop
4, checkout Felix does not govern step exits 4 job fails, names the binding
5, nothing to repair (felix-home#323, live on main since 1375e6ee) step exits 5, job red job passes, nothing spent

#22 copied the #303 template's step. That fixes the case statement but keeps the tee pipe, and the same probe gives the same six rows as main.

Main's copy checks out felix-home with no ref, so it has read engine 0.49.164 or later since #323 landed (01:21Z on 2026-09-24). From then on, a dispatch on a green branch, or a failure superseded by a newer gate run, ends at exit 5 and fails the job red. It fails closed: no budget is spent and no model runs.

Pinned, not the default branch. Felix is checked out at 685d37beedc4590a989ccd145db3069af8e7f76a, not whatever felix-home's main holds when the job runs. The reason and the cost are written in the workflow, above the checkout:

  • Why. The scope step is a contract with felix repair's exit statuses, and that contract changed four times on 2026-09-23 (felix-home #303, #321, #323, #329) while this file did not. Unpinned, the copy was right only until the next felix-home merge. The job also holds a write token, and an API key once switched on.
  • Cost. projects/elenchus/risk.tsv and gate.sh in felix-home are read at the pin too. A path made red there after the pin is not red to this loop until the pin moves.

The pin is part of the template (felix-home #328), not a local edit, so every copy carries it.

Both workflows on one Felix. #23 now pins gate.yml to the same sha (d00abf2 on its branch). Its run 35950620078 checked Felix out at 685d37b and passed, 1439 tests. So the repair loop's pre-push felix gate and the gate workflow run the same engine. Merge the two together, and move both pins together from now on.

Checked (T1).

  • Copy equals the template. The diff above prints nothing, and the file is non-empty (225 lines). No {{FELIX_...}} placeholder is left.
  • The pinned sha. It is felix-home main (compare reports identical), engine 0.49.168, and it holds projects/elenchus/project.json with remote_match: LeavesJ/Elenchus. So felix repair resolves this repository there, and the case statement matches that engine's lib/repair.sh.
  • Parses. 7 steps; the Felix checkout is LeavesJ/felix-home at the sha, read as a string; both checkouts are actions/checkout@v5. The deploy key and secrets are unchanged, and it is the same repository as before.
  • The scope step under bash -e. The table above, from the step cut out of this file.
  • End to end. A workflow_dispatch of this workflow on this branch. See the comment below.

Not changed. Nothing runs a model while FELIX_UNATTENDED is unset (gh variable list shows none). All 40 runs of this workflow on record are skipped, so felix unattended counts 0 dry-run failures.

… to the Felix commit it was copied from

.github/workflows/unattended.yml is engine/harness/templates/unattended.yml
at felix-home 685d37be (engine 0.49.168), whole, with {{FELIX_REPO}} and
{{FELIX_REF}} filled in and nothing else changed.

Actions runs a step with no shell as `bash -e {0}`. The old scope step's
`felix repair | tee` under pipefail ended there on any non-zero exit, before
rc was read, so a reached ceiling or a red path failed the job and labelled
nothing. Since felix-home #323 moved nothing to repair to exit 5, a dispatch
on a green branch or a superseded failure also fails the job, because the
unpinned checkout already reads that engine. The template's step reads every
status explicitly: 0 proceeds, 2 and 3 escalate with needs-founder, 1 and 4
fail the job, 5 passes having spent nothing.

The Felix checkout is pinned to 685d37be rather than felix-home's default
branch. The reason and its cost are written above the checkout, from the
template (felix-home #328).

Supersedes #22, whose step keeps the pipe.
@LeavesJ

LeavesJ commented Sep 24, 2026

Copy link
Copy Markdown
Owner Author

End to end: workflow_dispatch of this branch's workflow, run 35950755377, success.

  • The Felix checkout: ref: 685d37beedc4590a989ccd145db3069af8e7f76a, then HEAD is now at 685d37b in felix-home.
  • Scope the failure ran as shell: /usr/bin/bash -e {0}. The real felix repair read this branch's gate run 35950747280 as in_progress and printed "Still running" (exit 5). The step then printed nothing to repair on claude/unattended-template-pin and exited 0.
  • Report: "Nothing to repair: the gate is green, still running, ended without...". No budget step and no model step ran, and no label was applied.

On main's copy, the same dispatch would have ended the scope step at the pipe with exit 5, and the job would have been red.

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