Repository navigation
senior-dev: keep the store in the run's record folder, write back a removed .senior-dev, and --state-dir - #1800
Open
ZeroPoint95 wants to merge 4 commits into
Open
ZeroPoint95 wants to merge 4 commits into
ZeroPoint95 wants to merge 4 commits into
Conversation
…it outside the folder A CyberGym run had anchored <folder>/.senior-dev to a store elsewhere through a link. The projection lock was reopened through that link on every write, so when the link stopped resolving an hour in, the next model turn ended the run unsubmitted with `open <folder>/.senior-dev/projection.lock: no such file or directory` while the store itself was intact. The store's directory is now made, made absolute and resolved through every link once when it is opened; the database, the flat records and the lock are all rooted on that real path. `codeaf senior-dev run --state-dir DIR`, or SENIOR_DEV_STATE_DIR, keeps the store in a directory of its own (the flag wins); one inside the folder, other than under its own .senior-dev, is refused before the run starts. The model's files stay in the folder's .senior-dev either way. A store that is itself removed still ends the run, now naming it. It remembers which directory it opened and writes into no other: a fresh empty directory in its place (made by the next tool output under .senior-dev, or by anything else) would let the next turn make the session again under the same id and hand the model a conversation that starts where the removal happened. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…fore making it Review of #1800. The store's identity check compared a Stat taken when it opened with one taken now, and ext4 and overlayfs give a directory made where one was just removed the old inode number back. It held only because sqlite's pooled connection kept senior-dev.db open; with the database closed, the check was fooled 3 out of 3 times in an alpine container and the next write went into the new directory. The store now keeps its directory open and compares through that handle. --state-dir made the directory before judging it, so a refused <folder>/state or <folder>/a/b/c was left in the tree. It is now judged from the deepest part that exists, and made only once allowed; a folder spelled with other case on APFS is refused as the folder. The "gone" ending no longer prints the path twice, and the four flag bullets reworded in the manual are restored. Co-Authored-By: Claude Opus 5.5 <[email protected]>
ZeroPoint95
marked this pull request as ready for review
October 9, 2026 16:09
ZeroPoint95
marked this pull request as draft
October 9, 2026 17:24
… a removed .senior-dev Both CyberGym runs lost .senior-dev to the model's own last command: OpenSSL 1.1.0's make clean deletes every link in the tree, and the rig's validate.py begins sudo rm -rf /src. The store is now kept out of the folder by default. - With no --state-dir and no SENIOR_DEV_STATE_DIR, a run codeaf carries keeps its store in a directory of its own in the record folder codeaf names on CODEAF_RECORDS: <record>/store, or store.1, store.2 for a later run of the same task. Only with no record folder does it stay in .senior-dev. - The brief, the checklist, the pinned command and the steering file are read again after every tool call and by senior-dev's own reads; one that is gone is written back as the run last read it, with an implement/notes-rewritten record and a line on the run's page. - The removed-store check now also holds the database and the records' directory open: an rm -rf that raced the store's own writes left the directory standing with its contents gone, and the directory alone still matched. Conversation reads are checked too, and a write cut short by the removal names the store instead of the temporary file it was renaming. Manual, the Notes comment and the change entry say where the store is now. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ore is made when it opens Review of #1800. Tool calls run at once (steploop's processor runs each in its own goroutine but the question), so a write-back could land after the model's own write of its checklist and put the old copy over it. readNote now holds the kept notes' lock throughout, and the copy is written to a file of its own and linked into place, which fails where anything already is; the file then read is the one kept. keepSteering appends under the same lock. Against the old write-back the new test fails in its first round. The record folder's store was made before the config, --asked and window refusals, so a refused launch left an empty store and the next got store.1. Where it goes is still decided early (recordStoreBase); the directory is made in newPipeline when the store opens (claimRecordStore). Also: the notes section's heading says where the database is now, the change entry says the store is checked before every write and every read of the conversation, and the page's notes-rewritten line has a test. Co-Authored-By: Claude Opus 5.5 <[email protected]>
ZeroPoint95
marked this pull request as ready for review
October 9, 2026 18:07
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.
Keeps senior-dev's session store out of the folder it works in, and writes back a
.senior-devthat a command deletes mid-run. A deleted.senior-dev, or a deleted link to one, no longer ends the run. Also adds--state-dir.What changed
.senior-dev.make cleandeletes every link in the tree, including the.senior-devlink to the store.validate.pystarts withsudo rm -rf /src.senior-dev.db,storage/andprojection.lockin<record>/store. It finds the record folder throughCODEAF_RECORDS, and a later run of the same task getsstore.1,store.2and so on. Where it goes is decided early, but the directory is made only when the store opens, after every refusal. The store goes in.senior-devonly when there is no record folder, when the record folder is inside the workspace, or when it cannot be written.filepath.EvalSymlinks), so a link that goes away no longer matters.--state-dir DIR/SENIOR_DEV_STATE_DIRboth win over the record folder, and the flag wins over the variable..senior-dev/is the exception..senior-devis written back.spec.md,checklist.md,pinned.txtandsteering.mdare read again after every tool call and whenever senior-dev reads them itself.implement · notes-rewrittenrecord is logged.keepSteeringappends under the same lock.tool-output/needed no change: it already makes its directory before each write.senior-dev.dbandstorage/open, and checks all three before every write and every read of the conversation. If any is gone or replaced, the run stops withits store <dir> was removed while the run was working, with the conversation in it.storage/as well catches anrm -rfthat raced the store's own writes. That leaves the directory standing with its contents gone.MkdirAll-and-retry. A new store would get the session made again under the same id, and the model would lose its history.--state-diris in the flags section. There are ten new probes: eight inTestTheVanishedStoreQuestionsReachTheStateDirand two in the main table.Notescomment inseniordev.gois updated.recordingHostgainsRecords()(it readsCODEAF_RECORDS, as codeaf's child host does),hermeticRunclears that variable, andmodelAPIServertakes an optional script.How it was checked
TestDurableSurvivesTheWorkspaceLinkGoingAwayfails ondevwithopen …/.senior-dev/projection.lock: no such file or directory, and passes here.TestANotesFolderTheWorkRemovesIsWrittenBackscripts a run whose model runsrm -rf .senior-dev.TestACarriedRunKeepsItsStoreInTheRecordFolder,TestARunWithNoRecordFolderKeepsItsStoreInTheFolder,TestTheStateDirFlagKeepsTheStoreWhereItSays(the record folder stays empty) andTestTheStateDirVariableWinsOverTheRecordFolder.TestARefusedLaunchLeavesNoStoreInTheRecordFolderfails if the store is made early.TestAWriteBackNeverOverwritesAFileWrittenMeanwhileruns a write-back beside the model's write for 500 rounds under-race. The old write-back fails it in round 1.alpine:3on overlayfs.TestARemovedStoreEndsTheRunWithoutAFreshOnecovers a store that is gone, replaced, made again, emptied, and replaced with the database closed. With the database closed, the old check was fooled 3 out of 3 times.gofmt,go vet ./..., race tests forinternal/seniordevand itsappandconfigpackages,internal/manualand the packed manual,make test-laws,codeaf-changes check,cmd/codeaf -run 'SeniorDev|AShellRun|Carried', andinternal/session -run 'Notes|SeniorDev'.make pr-readywas not run, as asked.bin/codeafran against a scripted provider on localhost:rm -rf .senior-devafter writing its checklist. Ondevthe run crashed onprojection.lock(exit 2) and forked the store (UNIQUE constraint failed: session.id). Here it finished (exit 0), wrotespec.mdandchecklist.mdback, and kept all 6 messages in<record>/store..senior-devlink is removed at the first call: finished (exit 0), with the store in<record>/store.--state-dir: finished (exit 0), with the store where the flag said.--state-dir <folder>/a/b: refused before any model call, and nothing was made in the folder.Not in this PR
keepNotesalreadyLstats and skips a linked.senior-dev, so the brief's observation does not hold and nothing needed changing.rm -rf /srcis left to the rig's backup.senior-dev crashed:.refused()has usedStatusCrashedsince senior-dev: a coding agent built into codeaf takes a whole task, in the folder itself #1488.Checklist
docs/changes/unreleased/1800-senior-dev-state-dir.mdinternal/manual/chat/senior-dev.md.github/known-red.txt🤖 Generated with Claude Code