Skip to content

feat: key hold decisions by content hash - #1319

Merged
Zach Dunn (zachdunn) merged 7 commits into
cursor/classifier-event-log-e3ddfrom
cursor/hold-release-events-e47f
Oct 28, 2026
Merged

Zach Dunn (zachdunn) merged 7 commits into
cursor/classifier-event-log-e3ddfrom
cursor/hold-release-events-e47f

Conversation

@zachdunn

@zachdunn Zach Dunn (zachdunn) commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Depends on #1318. A hold belongs to one upload. A key is held only when it has its own ai.hold row and the blob is not released. A blob_hold=held row does not hide a key the classifier never held, so the kill switch, an old CLI, and holdFlaggedUploads: false still mean the file is public. A release still follows the bytes: it applies to every key in the workspace that has a hold row for that hash, and approve or re-hold moves all of those copies.

What it does / what it is not

  • blob_hold is the lookup for a hash. The held list and admin counts join it to keys that still have a content hash and their own ai.hold row.
  • Deleting a file, or the retention sweep, drops that key's content hash, so the key leaves the held list and the comment count. The event log stays.
  • Approve and re-hold record one override for the hash, then restore or move every held copy. They do not write ai.hold. If the override write fails, nothing is moved into the held bucket.
  • On an overwrite with new bytes, the old content hash is deleted before the classifier rows are cleared. If the new hash is not stored, the pending row still holds the key.
  • GET …/files/review returns sha. Approve and hold accept expected_sha and answer 409 on a mismatch. The file page, uploads approve, and MCP approve send that hash when they have one. Slack stays optional.
  • Not the perceptual-hash reuse gap. That is a later PR.

How to try it

uploads approve --pr 123
uploads approve --pr 123 --all
uploads meta get <key>

uploads meta get shows ai.hold=approved after a release. The stored row stays pending. uploads approve --all sends the hash from the held list.

Technical notes

Approved rows with no content hash are skipped and left approved. After the migration, that skip count is:

SELECT COUNT(*) FROM file_metadata
WHERE meta_key = 'ai.hold' AND meta_value = 'approved';

When keys that share a hash mix pending and approved, and a pending copy still sits in the held bucket, the migration does not mark that hash released. Restoring those bytes from SQL is not possible, so the pending copy stays held instead of 404ing as a release. A pending row with no hash stays pending and stays off public surfaces. It is not on the held list.

Test plan

  • Re-hold of one key moves every copy of those bytes that has a hold row
  • Deleting a held file removes it from the held list
  • An identical upload the classifier did not hold (old CLI) stays public while a held blob exists
  • A release survives a new key with the same hash, and new bytes are held again
  • Backfill of a hashed approval, and the skip count for a row with no hash
  • API hold, comment, classifier, retention, and route tests; MCP list_held approve
  • CI Lint & Format and Test on 32a4eb0c
Open in Web Open in Cursor 

Release and re-hold append an override instead of writing ai.hold=approved.
Readers share deriveHoldState, and a legacy approved row still counts as released.

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 32a4eb0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@buildinternet/uploads Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one): 2
  • coderabbit:review
  • review
🚫 Excluded labels (none allowed): 1
  • wip

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 4d13d825-cbc8-495b-b984-1f9b2cfbfe94

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
A blob_hold row is the hold decision for a workspace and content hash. Approve and hold record that row with the override and do not write ai.hold.

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
@uploads-sh

uploads-sh Bot commented Oct 28, 2026

Copy link
Copy Markdown

No uploads on this PR yet

This PR changes UI files (apps/web/src/pages/f/[workspace]/[...key].astro). Screenshots help reviewers see what changed.

  • From the CLI: uploads put before.png after.png --pr 1319
  • From an agent: ask it to attach screenshots with uploads.sh
  • By hand: drag images into the PR description and they're imported automatically
Tips for agents

Label each shot so reviewers know what they're seeing. Labels apply to every file in a call, so run one put per shot:

uploads put after.png --pr 1319 --meta path=/settings --state after --caption "Saved settings toast"
uploads put chat.png --pr 1319 --app ios --screen "Chat" --state after

No CLI? With the Uploads MCP, call sign_upload_url for files on disk (or put) with repo, pr: 1319 and the same screen, state and caption fields.

This comment updates with each upload, so you don't need to post the images yourself. A shot or two in the PR description is still welcome. Check what's attached with uploads list --pr 1319. More in the github-screenshots skill or the agent docs.

Workspace admins can turn this comment off in Settings → GitHub · docs

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
@cursor cursor Bot changed the title feat: derive hold release from the event log feat: key hold decisions by content hash Oct 28, 2026
Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
list_held now describes sha. The docs catalog snapshot follows that description.

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
A key is held only when it has its own ai.hold row and the blob is not released. Approve and re-hold move every such key, and deleting a file drops it from the held list.

Co-authored-by: Zach Dunn <zachdunn@users.noreply.github.com>
@zachdunn
Zach Dunn (zachdunn) merged commit 90bb1de into main Oct 28, 2026
4 checks passed
@zachdunn
Zach Dunn (zachdunn) deleted the cursor/hold-release-events-e47f branch October 28, 2026 12:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants