Skip to content

fix(ci): both workflows pin felix-home 6fe5f986, and every action they run is named by full commit sha - #29

Open
LeavesJ wants to merge 1 commit into
mainfrom
claude/unattended-pin-6fe5f986
Open

LeavesJ wants to merge 1 commit into
mainfrom
claude/unattended-pin-6fe5f986

Conversation

@LeavesJ

@LeavesJ LeavesJ commented Oct 8, 2026

Copy link
Copy Markdown
Owner

This changes workflow files, so you merge it. Both pins move together, as #24 and gate.yml's pin comment require: gate.yml and unattended.yml both check out felix-home at 6fe5f98688456fa95a7198e4c0eceadb9fe43741.

The pin. 6fe5f986 was felix-home origin/main when this was made (git ls-remote, 2026-10-08T19:30Z). It is the merge of felix-home #416 (2026-10-03) and carries engine 0.49.273. It contains projects/elenchus/project.json with remote_match: LeavesJ/Elenchus, so felix which resolves this repository there. felix-home's own gate passed on it: run 37152268315 reused #416's green PR run.

unattended.yml: the template, nothing else

It is felix-home's engine/harness/templates/unattended.yml at the pin, copied whole, with {{FELIX_REPO}} set to LeavesJ/felix-home and {{FELIX_REF}} set to the sha (quoted). Nothing else differs. This command prints nothing:

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

felix-home's own comparison (felix_unattended_template_diff, which feeds the "workflow matches template" row of felix unattended) says match for this copy. For main's copy it says differ at line 63.

Two felix-home template changes since cb3a72ce come with it:

  • #403 (75f94b4d). Both checkouts use actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 instead of the movable @v5.

  • #391 (44c0e869), carrying #335. The repair is limited to what the brief names. The scope step runs felix repair --implicated and passes the implicated file set on as a step output. If the log names no file in this checkout, it stops (unnamed) before any budget or model. The verify step refuses to push in four cases, and labels the PR needs-founder where it can:

    • the attempt changed a file outside that set;
    • it changed the Felix checkout;
    • it edited a workflow file;
    • felix scope --base calls the attempt's own committed diff red.

    After that comes the gate, as before. A new regate job, which alone holds actions: write, then dispatches the gate on the pushed commit, because the token's push starts no run. Both --implicated and scope --base are in the pinned engine, so the template and the engine it checks out match.

gate.yml: what felix bootstrap renders, plus this repository's own steps

The base is felix_bs_render_workflow python LeavesJ/felix-home 6fe5f986… from engine/harness/lib/bootstrap.sh at the pin:

diff <(bash -c 'eval "$(git -C ~/Documents/Felix show 6fe5f98688456fa95a7198e4c0eceadb9fe43741:engine/harness/lib/bootstrap.sh)"; felix_bs_render_workflow python LeavesJ/felix-home 6fe5f98688456fa95a7198e4c0eceadb9fe43741') .github/workflows/gate.yml

Each remaining difference from that render, and why:

Where Render This file Why
Header three lines adds the "ONE JOB, DELIBERATELY" paragraph Unchanged from main. It explains the fork path below.
After the first checkout — Decide whether the harness is reachable (id: ctx) Unchanged from main. A fork PR gets no FELIX_DEPLOY_KEY, so the job degrades on that path and does not fail a required check.
Felix checkout, Confirm the governing project, Run the gate no if: if: steps.ctx.outputs.harness == 'yes' Part of the same fork path, as on main.
Felix checkout comment the renderer's plus three paragraphs: the pin names 6fe5f986 and says to move it with unattended.yml; felix-home not felix (the rename); not continue-on-error The pin note is this repository's own (main's named cb3a72ce). The other two are kept as they were on main.
Python setup actions/setup-python@a26af69b… # v5.6.0, "3.12" actions/setup-python@ece7cb06… # v6.3.0, "3.14" Main ran @v6 and 3.14, and this keeps both, pinned. v6.3.0 runs on Node 24. The renderer's v5.6.0 runs on Node 20, which the renderer's own checkout comment avoids ("v5, the lowest major that runs on Node 24").
Install pip install -e ".[dev]" pip install --upgrade pip, then pip install -e ".[dev,web]" "ruff==0.16.0" "pytest==9.1.1", with its comment Unchanged from main. The web extra is under test, and ruff/pytest are pinned to the versions the gate was authored against.
Run the gate — the "No ANTHROPIC_API_KEY on purpose" comment Unchanged from main.
End — Run what a fork can run, Say what did not run Unchanged from main. These are the fork path's steps.

What this changes against main's gate.yml (beyond the pins):

  • Both checkouts now set persist-credentials: false (felix-home #379). The job token is not left in repo/.git/config, and the deploy key file is not left under $RUNNER_TEMP, where the pull request's own code, which the gate runs, could read them. This job pushes nothing and reads Felix from disk, so nothing after the fetch needs either. This PR's own gate run is the check on that.
  • The comments are the renderer's wherever it has one.
  • The checkout, setup and install steps lose their names (Check out Elenchus, Check out Felix, Set up Python, Install dependencies), because the renderer writes them unnamed. The job is still gate, so the required check is unaffected.
  • main's "CI reads code and reports a status" comment over permissions is gone. The renderer has none there.

Every uses: names a full commit sha

Action Was Now Resolved by
actions/checkout (2 in each file) @v5 @fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0 gh api repos/actions/checkout/git/ref/tags/v5 answers commit fbc6f399…, the same commit as v5.1.0
actions/setup-python @v6 @ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 gh api repos/actions/setup-python/git/ref/tags/v6 answers commit ece7cb06…, which is v6.3.0 (a lightweight tag, so there is nothing to dereference)

Each sha is the commit its tag pointed at today, so the action code that runs is the same as on main. Only the tag owner's ability to move it is gone.

semgrep 1.175.0, --config p/github-actions

$ semgrep scan --metrics=off --config p/github-actions .github/workflows/gate.yml .github/workflows/unattended.yml
Ran 11 rules on 2 files: 1 finding.
    .github/workflows/unattended.yml
    ❯❱ yaml.github-actions.security.workflow-run-target-code-checkout.workflow-run-target-code-checkout
           95┆ - uses: actions/checkout@fbc6f3992d24b796d5a048ff273f7fcc4a7b6c09 # v5.1.0
           97┆     ref: ${{ github.event.workflow_run.head_branch || github.ref }}

On main's two files the same command gives 6 findings: github-actions-mutable-action-tag at gate.yml 43, 110 and 118 and at unattended.yml 66 and 96, plus this one.

The one left is there by design. The repair has to check out the failing branch in order to repair it. The job's if: limits it to workflow_dispatch or a failed run whose head_repository is this repository, so a fork's branch never gets here. Both checkouts keep no credential, and the model step is scoped to ./** with .git/** and //proc/** denied (#26).

Checked

  • Both files parse (Ruby YAML.safe_load). gate: 9 steps. unattended: repair has 7 steps and regate has 1 (permissions: actions: write). Both Felix ref: values parse as strings.
  • FELIX_UNATTENDED is unset: gh variable list --repo LeavesJ/Elenchus lists no variables (2026-10-08T19:28Z). So no budget, model or push step can run, and regate (gated on pushed == 'yes') is skipped.
  • Not touched: secrets, repository variables, branch protection.

CI on this PR and a dispatched unattended repair run on this branch follow in a comment.

…y run is named by full commit sha

unattended.yml is felix-home's templates/unattended.yml at
6fe5f98688456fa95a7198e4c0eceadb9fe43741 (engine 0.49.273) with its two
placeholders filled, nothing else. It carries felix-home #391 (a repaired
commit is scope-checked against the brief's implicated files and re-gated by
dispatch) and #403 (both checkouts pinned to actions/checkout v5.1.0's sha).

gate.yml is what felix bootstrap renders at the same commit (#354, #379: every
uses: pinned by sha, both checkouts keep no credential), with this
repository's fork path, Python 3.14 install and pin note kept. setup-python
stays on v6, pinned to v6.3.0's sha, rather than the v5.6.0 the renderer
writes, which runs on Node 20.

A workflow change: the founder merges it.
@LeavesJ

LeavesJ commented Oct 8, 2026

Copy link
Copy Markdown
Owner Author

This PR's own runs.

  • gate on the new pin, run 37832570241: success. It graded merge commit c361edd.
    • The Felix checkout fetched 6fe5f98688456fa95a7198e4c0eceadb9fe43741, and felix which resolved project: elenchus, gate: gate.sh.
    • Both checkouts ran with persist-credentials: false and logged "Removing auth" after the fetch, and the gate still had everything it needed.
    • setup-python ece7cb06… (v6.3.0) installed CPython 3.14.8.
    • Results: format, lint, test (1442 passed, 35 skipped), confidential, secret, lift-bank, ignore-leak and inversion were all ok, so gate: pass. probes ok (5 found).
    • The fork-path steps were skipped, as they should be for a same-repository PR. They are unchanged from main and not exercised here.
  • unattended repair dispatched on this branch, run 37832711131: success, with FELIX_UNATTENDED unset.
    • Both checkouts at fbc6f399… (v5.1.0) succeeded.
    • Scope the failure ran the pinned engine's felix repair --implicated, which found this branch's gate still running and reported nothing to repair.
    • Spend from the budget, Attempt a repair and Verify and push were skipped, and so was the regate job (pushed was not yes).
    • The report step said: "Nothing to repair … Nothing was invoked or spent."

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