A local run may not write `state/`
rank: decision outranks: [] doc_id: PTV-DEC-0022
Section titled “rank: decision outranks: [] doc_id: PTV-DEC-0022”A local run may not write state/
Section titled “A local run may not write state/”Date: 2026-08-25
Status: open
Supersedes: none
Superseded-by: none — current
Answers: PTV-FND-0058 — “whether local runs may write these files at all”
Terminal state of this record: one ruling, one carve-out, and one consequence
named for G-P3-9 clause 3. It writes no state and changes no code.
Context
Section titled “Context”PTV-FND-0058 (#356) records that scripts/backfill-state-phase.mjs can write
current_phase: null over a better-informed remotely-derived value, because
null means the derivation saw nothing, not there is no phase. It leaves
open the wider question: may a local run write these files at all?
state/README.md states the same hazard and defers to this record.
The ruling
Section titled “The ruling”No. A local run may not write state/.
The reason is not hygiene. The counterexample already exists in this repo’s history, and it is benign only by accident.
On 2026-08-25 a local sweep run wrote state/null0-toll.yaml with
contract_drift: true, contract_drift_reason: sha_mismatch. The scheduled
sweep on main wrote contract_drift: false for the same observed sha
167f29b6e06c. Both were correct. The registry entry changed from
3faaee22857f to 167f29b6e06c in c28049228261, and the local run’s branch
was based before that commit — so true was right against the registry it could
see and false right against the updated one.
Nothing in either file records which registry it was computed against.
That is tolerable while the local value is a working-tree modification someone inspects and discards, which is what happened. It stops being tolerable the moment such a value is committed: the file then holds a verdict whose correctness depends on a base it does not name, and every reader after that point is comparing a current observation against an unstated one.
This repo already carries that exact shape and is treating it as a defect.
PTV-FND-0059 records state/<slug>.yaml holding a contract_sha the sweep
never rewrites, true relative to a May registry and stale against every one
since. Permitting local writes would manufacture the same shape deliberately,
and at a higher rate than the one instance that produced the finding.
The carve-out
Section titled “The carve-out”A local run may write state under a path that names itself as local and is
matched by .gitignore, if that is useful during development.
It may not write the sweep-owned files — state/<slug>.yaml,
state/transitions/<slug>.jsonl. And nothing derived from a local write is
citable as gate evidence, in a verification round or anywhere else. A local
derivation is a development aid; it is not an observation of the fleet.
Consequence for G-P3-9 clause 3
Section titled “Consequence for G-P3-9 clause 3”G-P3-9’s third clause requires the phase-state location be populated for
petrova-codes itself. It is not: state/transitions/petrova-codes.jsonl does
not exist and state/petrova-codes.yaml reads current_phase: null,
gate_open: null.
Two fixes were available. This ruling removes one of them.
Derive the self-entry from the local checkout— a local write. Excluded.- Grant the scheduled sweep access to
petrova-codesitself. The named target..github/workflows/state-sweep.yml:79records that the token has no access to this repo, which is why the control plane does not sweep itself despiteregistry.yaml:23saying to treat it uniformly with consumer repos.
Clause 3 is therefore blocked on the sweep, not pending on the P3 track. It is not work that the phase can complete by trying harder, and it should be recorded as blocked with the sweep’s self-access as its named target rather than carried as unfinished.
The PTV-SCF-0001 P2 waiver’s void condition stays live. What changes is the
reason: F-26 is no longer undischarged because the work was not done, but
because one clause of its gate depends on a credential grant outside the phase.
That distinction belongs in the P3 close, whatever the close decides.
What this does not license
Section titled “What this does not license”- It does not license closing
G-P3-9. Two clauses hold; the third does not. - It does not license writing
state/transitions/petrova-codes.jsonlby hand to satisfy the clause. A hand-written transitions log is a local write with extra steps, and it would be thePTV-FND-0059shape authored on purpose. - It does not rule on
scripts/backfill-state-phase.mjscontinuing to exist. It rules that its output may not be committed to the sweep-owned paths.
Sources
Section titled “Sources”docs/findings/20260825-1120-a-local-run-clobbers-remotely-derived-state-with-null.md—PTV-FND-0058, the open question this answers.docs/findings/20260825-1200-a-state-field-nothing-writes-outlives-the-value-it-copied.md—PTV-FND-0059, the shape a local write would manufacture.state/README.md:47-56— the hazard as stated in the directory it describes..github/workflows/state-sweep.yml:79— the token has no access to petrova-codes.registry.yaml:23— treat petrova-codes uniformly with consumer repos.docs/decisions/2026-08-14-ptv-scf-0001-p3-open.md:232—G-P3-9clause 3.docs/decisions/2026-08-13-waiver-ptv-scf-0001-p2.md:139-142— the void condition.
Sign-off
Section titled “Sign-off”- Subagent: PTV-SCF-0001 P3 (session 2026-08-25)
- Human: ☑ (proxy) countersign — that a local run may not write
state/, that the gitignored development carve-out is permitted and not citable as evidence, and thatG-P3-9clause 3 is blocked on granting the scheduled sweep access topetrova-codesitself. Countersigned byhuman:devarnoon2026-08-25, by directive in session; ticked by the agent as scribe, not as signatory.