Skip to content

Let a writer keep a half-written directory out of an upload - #13

Closed
v-positronic wants to merge 1 commit into
mainfrom
skip-unfinished-dirs
Closed

Let a writer keep a half-written directory out of an upload#13
v-positronic wants to merge 1 commit into
mainfrom
skip-unfinished-dirs

Conversation

@v-positronic

Copy link
Copy Markdown
Collaborator

What

upload() and sync() take skip_dirs_containing: filenames that mark a local directory as not-to-be-uploaded. A directory holding one is skipped with its whole subtree, on every upload the registration performs.

pos3.sync('s3://bucket/run/', skip_dirs_containing=['.unfinished'])

Why

exclude matches glob patterns against a path. "A directory that contains .unfinished" is a fact about the directory's contents, and the path carries no sign of it — so a writer that marks its in-progress output has no way to keep it out of the destination.

The case this comes from: an episode recorder writes into <run>/<block>/<episode>/ and drops a .unfinished marker while the episode is open, clearing it on commit. A run that crashes leaves that directory half-written, and today it uploads to the customer-visible prefix like any other. The alternatives are a move (rename or relocate the directory so a glob can see it) or a post-hoc cleanup — one is machinery whose failure modes need their own retry semantics, the other deletes the artifact somebody wanted for debugging.

Behaviour

  • Upload half only. sync() passes it to the upload it registers; the download is untouched.
  • Not a delete. A directory the destination already holds is not removed there for gaining a marker — the marker keeps bytes from leaving, it does not reach back.
  • Reversible by the writer. A directory that finishes and drops its marker uploads on the next pass, which is what makes the in-flight episode's exclusion self-clearing.
  • Applied in the local scan, so the pruning costs one exists() per directory and skips the subtree rather than filtering it file by file.

Verification

uv run pytest — 140 passed. Three new tests: a marked directory and its subtree are absent from the scan while a sibling is kept; naming no marker keeps everything; a directory that loses its marker is scanned again.

Version bumped to 0.4.0 with a changelog entry — the consumer that needs it pins a release.

`exclude` matches glob patterns against a path, so it cannot say "a directory that contains
`.unfinished`" — that is a fact about the directory's contents, and the path carries no sign of it.
A writer marking its in-progress output therefore had no way to keep it out of the destination, and
the workarounds are a move or a post-hoc cleanup, which lose the artifact or the tidiness they were
meant to protect.

`skip_dirs_containing` names those marker files. The local scan skips a directory holding one, with
its whole subtree, on every upload the registration performs — so a directory that finishes and
drops its marker uploads on the next pass, and one that never finishes never leaves the machine.

Upload half only, deliberately: a directory the destination already holds is not deleted there for
gaining a marker.

Ticket: Positronic-Robotics/internal#388 #refs
@vertix

vertix commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 625905ed15

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pos3/__init__.py
Comment on lines +794 to 795
_filter_fileinfo(_scan_local(local_path, skip_dirs_containing), exclude),
_filter_fileinfo(self._scan_s3(scan_bucket, scan_prefix, profile), exclude),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve marked remote subtrees when deletion is enabled

When delete=True (the default) and a marked directory already has objects in S3, pruning that directory only from the local iterator makes _compute_sync_diff classify every remote entry under it as target-only; the deletion loop then schedules those objects for removal. Thus adding a marker deletes the previously uploaded subtree instead of merely preventing new bytes from leaving, contrary to the documented behavior. Keep marked prefixes out of to_delete while still suppressing their uploads.

Useful? React with 👍 / 👎.

@v-positronic

Copy link
Copy Markdown
Collaborator Author

Closing: the case that motivated this is no longer a case. Uploading a half-written episode directory to the run prefix is acceptable — every reader that opens the dataset there ignores it (the recorder marks it, the dataset listing skips it, and the episode reader refuses it), so what reaches S3 is an extra directory nobody opens rather than a broken episode anybody sees.

That leaves skip_dirs_containing as a feature with no consumer, which is not a good enough reason to add an option to a shared library. If a destination ever does need protecting from in-progress output, this branch is the shape — the local scan prunes a marked directory with its subtree, upload-half only, and three tests pin it.

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.

2 participants