fix: an unreadable file must not read as an absent one - #1
Merged
Conversation
`Path.exists()` returns False when the OS denies access — macOS TCC, unix permissions, an unmounted share. Used as a guard before a read, it turns "I may not read this" into "this does not exist" and the caller falls back to defaults with nothing in the output to say so. Found in the field on 2026-07-20: a sibling deployment reported an empty review folder while nine finished videos sat in it, because the folder scan was guarded by `exists()` and the process had lost Documents access. The number 0 was indistinguishable from a real empty folder. Acting on it would have meant reporting "nothing to schedule" against a full queue. The same guard sits in front of this repo's own state: plan.json, the damping ledger, the performance store and config.json. An unreadable plan looks like no plan, so the scheduler books against built-in defaults; an unreadable store looks like no history, so the damped planner sees an empty record. Both give confident, wrong output. Adds postpeer_pilot/safe_read.py: a read either returns content, returns None because the file genuinely is absent, or raises Unreadable. Callers that want a default now choose it explicitly, so the fallback is a decision in the code rather than an accident of the filesystem. Applied to plan.active, plan.series_slots, plan.ledger_entries, plan.captions_by_post, perf.latest, perf meta-token lookup, config.load and config.api_key. Regression tests cover absent, present and chmod-000 paths, including that an unreadable plan or config raises instead of defaulting. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Path.exists()returns False when the OS denies access — macOS TCC, unix permissions, an unmounted share. Used as a guard before a read, it turns "I may not read this" into "this does not exist", and the caller falls back to defaults with nothing in the output to say so.Found in the field (2026-07-20). A sibling deployment reported an empty review folder while nine finished videos sat in it: the scan was guarded by
exists()and the process had lost Documents access. The reported0was indistinguishable from a genuinely empty folder — acting on it would have meant reporting "nothing to schedule" against a full queue.The same guard sits in front of this repo's own state:
plan.jsonconfig.jsonChange
Adds
postpeer_pilot/safe_read.py. A read either returns content, returnsNonebecause the file genuinely is absent, or raisesUnreadable. Callers that want a default now choose it explicitly, so the fallback is a decision in the code rather than an accident of the filesystem.Applied to
plan.active,plan.series_slots,plan.ledger_entries,plan.captions_by_post,perf.latest, the meta-token lookup inperf,config.loadandconfig.api_key.Tests
Five regression tests covering absent, present and
chmod 000paths — including that an unreadable plan and an unreadable config raise instead of quietly defaulting. Suite: 24 passed.Behaviour for genuinely absent files is unchanged, so no caller needs updating.
🤖 Generated with Claude Code