Repository navigation
feat: file uploads and an AI-agent path on the submit page - #114
Conversation
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
services/uploads/ validates a signed-in submitter's screenshot or PDF and writes it straight to the deployment's private uploads repository through its own GitHub App, one parentless commit and ref per file. Stores nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
new-entry.yml gives only the scaffold step a read-only token for the uploads repository. uploads-cleanup.yml deletes a submission's files when its issue closes, and sweeps unreferenced files older than 14 days. Both do nothing while UPLOADS_REPO is unset. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The scaffolder reads files attached on the submit page from the private uploads repository, under the issue author's id only, and runs them through the same checks as downloaded files: images.mjs and attachments.mjs now split fetching from checking and saving. Only scripts/lib/uploads.mjs sees the uploads token. Output without upload: lines is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With submit.uploads_url set (and sign-in on), signed-in submitters get a picker on the screenshots and file questions. Images are re-encoded in the browser, each file uploads as it is picked, the draft remembers what was uploaded, and the issue body carries upload: references. Blank uploads_url leaves the page exactly as it was; validate.mjs rejects uploads_url without signin_url. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A declined submission's issue is closed with the built-in GITHUB_TOKEN, which starts no issues: closed run, so its files waited for the daily sweep. Also run on pull_request: closed for same-repository entry/ drafts, resolve the issue from the branch name, and keep any set another open submission by the same author still names. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run generate writes submit/agent-instructions.txt and submit/answers.schema.json from _data/schema.yml, so an AI agent helping a submitter gets the exact questions and options. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Read git/matching-refs in one call: GitHub ignores per_page/page there, so the paging loop never ended once the uploads repo held 100 refs. - A failed uploads token mint no longer fails every submission; the scaffolder drafts the entry and warns per upload: answer. - pr-closed cleanup keeps files while the issue is still open. - One concurrency group per closed issue or pull request, so queued close runs are not cancelled. - Re-check the per-user caps after a write and remove only the branch this request created, so parallel POSTs cannot exceed them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- removeItem looks the item up again after its DELETE answers, so two quick removes no longer take out the wrong file. - A failed direct send with uploaded files falls back to the copy-out box, which keeps the upload: lines, instead of a link that drops them. - Split the page's upload rendering into uploads-view.js (stateless); uploads.js keeps the state and the requests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 435ef3245a
ℹ️ 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".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After a 401 the page is signed out, so count() was 0 and the fallback opened GitHub's form without the files, unannounced. The files are still held in the draft: the fallback now says only a fresh sign-in sends them and still offers GitHub's form without them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 697de15f29
ℹ️ 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".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Summary
Two independent additions to
/submit/, both off-by-default or zero-config for existing deployments.File uploads (optional, signed-in submitters only). Screenshots and decks can be attached on the submit page instead of dragged onto the issue afterwards.
services/uploads/: a stateless Vercel function (zero deps). It verifies the submitter's GitHub token, sniffs and validates the file (PNG/JPEG/WebP; PDF ≤ 4 MiB), and writes it with the deployment's own GitHub App to a private uploads repo as one parentless commit per file onu/<user-id>/<set>/<sha256>. Vercel hosts nothing. Per-user caps of 3 drafts and 9 files are re-checked after each write.upload:<set>/<sha256>.<ext>references. The scaffolder resolves them only under the issue author's id, using a read-only token minted per job and passed to the scaffold step only. It re-hashes the bytes and applies the existing image and attachment checks. A failed mint or a missing file becomes a warning on the draft and never a failed submission.uploads-cleanup.ymldeletes files when the issue (or its merged draft) closes, and runs a daily sweep for anything older than 14 days that no open submission names. It fails closed when it can't list issues. The scaffolder never deletes.submit.uploads_urlis set; requiressignin_url."Using an AI agent?"
npm run generatenow writessubmit/agent-instructions.txtandsubmit/answers.schema.jsonfrom_data/schema.yml. Import answers on/submit/loads the agent's JSON into the form and runs the form's own checks.Design and as-built notes:
docs/submit-uploads-plan.md. Admin setup:docs/admin-guide.md→ Files attached on the submit page. Decision log:docs/decisions.md(2026-10-16).Review
/freview(completeness + principal review) found 1 blocker, 2 major and 4 minor issues. All are fixed in40a2452and435ef32:matching-refsuploads.jswas split into a controller and a statelessuploads-view.js.Test plan
npm test: 1309/1309npm run test:build: 155/155npm run lint,npm run validate,generate --check,prettier --check .,actionlintnpm run test:flows(a11y, browser): not run locallyservices/uploads, create the private uploads repo and App, setUPLOADS_APP_ID/UPLOADS_APP_PRIVATE_KEY/UPLOADS_REPOandsubmit.uploads_url, then submit with a screenshot and a deckKnown gap: a declined submission's files wait for the daily sweep (up to 14 days), because the automation closes the issue with
GITHUB_TOKEN, which starts no workflow.🤖 Generated with Claude Code