Repository navigation
fix(ci): the unattended repair leaves the model no credential to copy; both workflows pin felix-home at cb3a72ce - #26
Merged
Conversation
…; both workflows pin felix-home at cb3a72ce
.github/workflows/unattended.yml is felix-home's engine/harness/templates/unattended.yml
at cb3a72ce4f0d85d37253359ca73b4976e9d45201 (the merge of felix-home #344, engine
0.49.194), copied whole, with {{FELIX_REPO}} = LeavesJ/felix-home and {{FELIX_REF}} =
that sha. Both checkouts set persist-credentials: false, the push step hands git the
job's token itself through a credential helper, and the model's tools are scoped to
the checkout with .git and /proc denied (felix-home #341 and #344).
gate.yml moves its pin to the same commit, so the repair loop's pre-push gate and the
gate workflow run one engine.
Owner
Author
|
End to end:
The
|
This was referenced Sep 25, 2026
LeavesJ
added a commit
that referenced
this pull request
Oct 2, 2026
`python -m elenchus.web` served the unauthenticated surface with no ceiling on paid model calls. `spend.from_env` returns None, meaning unbounded, when nothing is exported. That is right for the CLI, the probes and the suite, and only scripts/serve_invitee.py exported a value, so the documented `python -m` path was not bounded at all. web/__main__.py now does the same setdefault, after _load_dotenv and before create_app, so precedence is export, then .env, then the default floor. The ordering is the fix. model_factory_for_this_process samples the environment at create_app time and __main__ builds the app at import, so the same line one place lower satisfies every grep for the variable and leaves the process exactly as unbounded. The test is behavioural: it seizes control inside create_app and reads the factory the entrypoint actually arrived with, and it enumerates entrypoints off the tree so a third one is covered the day it lands. It was watched red first and mutation-tested twice (the line moved below create_app; setdefault swapped for assignment). This is e625a88 of 2026-09-03 without its CI-pin hunk: main already pins felix-home at cb3a72ce (#26), and a workflow change is the founder's to merge. The founder chose this commit over PR #16 on 2026-10-01 ("whichever is the most recent"). #16 arms the choke point in app.py, touches no file this change touches, and its own author called it the stronger on the property; it stays open for the founder to close or land. Co-authored-by: Jarron Deng <jarrondeng@berkeley.edu>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A workflow file, so this one is yours to merge. Both pins move together, as #24 and the comment in
gate.ymlsay they must..github/workflows/unattended.ymlis now felix-home'sengine/harness/templates/unattended.ymlatcb3a72ce4f0d85d37253359ca73b4976e9d45201, the merge of felix-home #344 (2026-09-24, engine 0.49.194), copied whole, with its two per-repository placeholders filled in:{{FELIX_REPO}}isLeavesJ/felix-homeand{{FELIX_REF}}iscb3a72ce4f0d85d37253359ca73b4976e9d45201. Nothing else differs, and anyone can check that:diff <(git -C ~/Documents/Felix show cb3a72ce4f0d85d37253359ca73b4976e9d45201:engine/harness/templates/unattended.yml | sed -e 's#{{FELIX_REPO}}#LeavesJ/felix-home#' -e 's#{{FELIX_REF}}#cb3a72ce4f0d85d37253359ca73b4976e9d45201#') .github/workflows/unattended.ymlgate.ymlmoves its pin to the same commit, and the two comment lines that named the old one now name this one. Nothing else in it changes.What was wrong with the copy on main. Both checkouts left a credential on the runner for the model step to read.
actions/checkoutkeeps what it fetched with unless told otherwise: the job's token inrepo/.git/configas anhttp.extraheader, and the deploy key as a file under$RUNNER_TEMPthatfelix/.git/confignames by path. The model step runsclaude -pwith--allowedTools "Read,Edit", and a tool-name allow rule matches every path. felix-home measured it on the pinned 2.1.37 against a scripted Messages API: Read returned all three files, Edit put their contents in a tracked file, the gate's secret scan passed the diff (a base64 header and aghs_token are shapes it does not know), and the push step commits every tracked change. A model steered by a hostile failing log is the reader to plan for.What the template does now.
persist-credentials: false. The token stays out ofrepo/.git/config; the deploy key file and thecore.sshCommandnaming it are removed before the next step runs. Nothing between the fetch and the push needed either:felix repairand the escalation label go throughghwith their ownGH_TOKEN, and the gate reads refs the fetch already brought.GITHUB_TOKENin the Verify and push step's environment. A credential helper hands it to git at push time, written to no file and passed in no argument. An empty helper first clears any helper the runner already had, so this step's token is the one git offers.Read(//proc/**)is denied, so the step's ownANTHROPIC_API_KEYis not a file the model can copy out of/proc/self/environon a Linux runner. Its tools are scoped:--allowedTools "Read(./**),Edit(./**)"and--disallowedTools "Read(.git/**),Edit(.git/**)". A bare tool name matches every path, and a bare-path deny matches only under the working directory, so the allow had to be scoped too: measured on 2.1.37,Read,Editwith the deny alone still read and edited../felix/.git/config, where acore.hooksPathwould have made the verify step's own git commands run code with the token in their environment.Checked (T1).
diffabove prints nothing, and the file is 292 lines. No{{FELIX_...}}placeholder is left.comparereports identical), engine 0.49.194, and it holdsprojects/elenchus/project.jsonwithremote_match: LeavesJ/Elenchus, sofelix repairandfelix gateresolve this repository there.actions/checkout@v5; the Felix checkout isLeavesJ/felix-homeat the sha, read as a string;persist-credentialsisfalseon both.Read(./**),Edit(./**)and its deny list carriesRead(.git/**),Edit(.git/**)andRead(//proc/**); the Verify and push step, cut out of this file and run underbash -eagainst a stub git that runs the helper as git would and exits 7 on any push butpush origin HEAD:$BRANCH, pushes to the repaired branch, and the helper answerspassword=<the step's GITHUB_TOKEN>while the token is in no argument git was given. The same checks on the copy main holds today fail six ways: 2 of 2 checkouts keep a credential, the allow list is the bareRead,Edit, none of the three denies is present, and no helper answers.workflow_dispatchof this branch's workflow: run 36034788167, success, stopped at scope with nothing to repair; and thegateworkflow at the same pin, run 36034789391, success. See the comment below.Not changed. Nothing runs a model while
FELIX_UNATTENDEDis unset (gh variable list --repo LeavesJ/Elenchusshows none, checked 2026-09-24T17:30Z). Of the 48 runs of this workflow on record, 46 areskippedand 2 are dispatches that stopped at the scope step with nothing to repair; the budget and model steps have never run here, so no model has read a credential from the old copy. The deploy key and the API key secrets are untouched.