Phase 3: add items and mark them consumed from an issue form - #4
Merged
Conversation
Phase 3: adding or updating an item needs no git knowledge and takes about 30
seconds. Three issue forms (add an item, change status, verify a location) are
parsed by scripts/apply_issue.py, which edits data/items.csv and hands the
workflow a PR title and body.
Prefill instead of dropdowns. The plan called for location dropdowns generated
from locations.csv; that is dropped. 173 options is miserable on a phone, and
regenerating form YAML from data would have the repo editing its own workflow
files. Instead the site links to issues/new?template=add-item.yml&location=...
and GitHub prefills by field id, so scanning a drawer's QR sticker and tapping
"Add an item here" arrives with the location filled in. Free-text locations are
validated by the parser, which suggests close matches on a typo.
Field labels are the contract between the forms and the parser, because GitHub
renders forms as '### <label>' sections. Rename a label and the parser silently
stops seeing that field -- no error, the value just disappears. check_forms.py
runs in CI and fails on any form field the parser doesn't read, any handler
with no form, and any field missing an id (which would break prefill). It
caught exactly that: verify-location's free-text field is labelled "Anything
there that isn't listed", which no handler read, so things found but not
recorded -- the most useful output of a verification pass -- would never have
reached the PR body. Now surfaced there explicitly.
Nothing is written when input is bad. Unknown location, ambiguous item name,
non-numeric quantity, or an unparseable date exits 2 with a message addressed
to the person who filed the issue; the workflow posts it as a comment and
stops. Verified against ten synthetic event payloads covering every error and
success path, with validate.py run on each result.
Two deliberate wiring choices:
- The generated PR gets no separate check run, because a PR opened with
GITHUB_TOKEN does not trigger other workflows. So the workflow runs
validate.py itself before pushing -- bad data never reaches a PR at all.
- git push --force, not --force-with-lease: each run is a fresh checkout with
no remote-tracking ref for the branch, so a lease check refuses with "stale
info" on the edited-issue path. The generated PR body warns that editing the
issue rebuilds the branch.
Issue text is never interpolated into a run: block -- apply_issue.py reads
GITHUB_EVENT_PATH itself, and the commit message is assembled in a file rather
than a heredoc. On a public repo, ${{ github.event.issue.body }} in a shell
command is a command-injection hole. The workflow also only runs for
OWNER/MEMBER/COLLABORATOR authors, so strangers cannot spawn branches and PRs;
review gates merging either way, so that check is about spam, not privilege.
Co-Authored-By: Claude Opus 5 <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.
Adding or updating an item now needs no git knowledge and takes about 30 seconds. Base is
main, which already has Phases 1–2.Three issue forms — add an item, change status, verify a location — are parsed by
scripts/apply_issue.py, which editsitems.csvand handsissue-to-pr.ymla PR title and body. The issue closes when the PR merges.One plan change worth your agreement
The plan called for location dropdowns generated from
locations.csv. I dropped that. 173 options is miserable on a phone, and regenerating form YAML from data would mean the repo editing its own workflow files on every data change.Instead the site links to
issues/new?template=add-item.yml&location=213-DRW-27— GitHub prefills by fieldid. Scan the QR sticker on a drawer → tap "Add an item here" → the location is already filled in. Free-text locations are validated by the parser, which suggests close matches on a typo:The site gains three actions: Add an item here and I verified this on the location banner, used up? on every item row.
Field labels are the contract, so CI now enforces it
GitHub renders forms as
### <label>sections, so a label is what the parser matches on. Rename one and the parser silently stops seeing that field — no error, the value just disappears into the void.scripts/check_forms.pyruns in CI and fails on any form field the parser doesn't read, any handler with no form, and any field missing anid(which would break prefill). It immediately caught a real instance:verify-location.yml's free-text field is labelled "Anything there that isn't listed", which no handler read — so things found but not recorded, the most valuable output of a verification pass, would never have reached the PR body. Now surfaced there explicitly.Nothing is written when input is bad
Unknown location, a name matching five items, a non-numeric quantity, an unparseable date — each exits 2 with a message addressed to the person who filed the issue. The workflow posts it as a comment and stops;
items.csvis untouched.Verified against ten synthetic event payloads covering every error and success path, running
validate.pyon each result. Sample outputs:Two wiring choices to be aware of
GITHUB_TOKENdoesn't trigger other workflows, sovalidate.ymlwon't fire on it. The workflow therefore runsvalidate.pyitself before pushing — bad data never reaches a PR at all. Swap in a PAT if you'd rather see the check on the PR.git push --force, not--force-with-lease. Each run is a fresh checkout with no remote-tracking ref for the branch, so a lease check refuses with "stale info" on the edited-issue path. Consequence: editing an issue rebuilds itsinv/*branch from scratch, discarding hand-written commits there. The generated PR body says so.Security notes, given this is public
Issue text is never interpolated into a
run:block —apply_issue.pyreadsGITHUB_EVENT_PATHitself, and the commit message is assembled in a file rather than a heredoc. On a public repo,${{ github.event.issue.body }}inside a shell command is a command-injection hole.The workflow also only runs for
OWNER/MEMBER/COLLABORATORauthors. Without that, anyone on the internet could open issues that spawn branches and PRs. Review gates merging either way, so this is about spam, not privilege — but it does mean a new lab member needs repo access before their forms work.Can't be fully tested until this merges
Issue templates and their prefill only resolve from the default branch. So the forms won't render, and the site's "Add an item here" links won't work, until this is on
main. Everything testable in isolation has been tested; the end-to-end path needs a real issue after merge. Filing a throwaway Add an item issue and deleting the resulting PR is the fastest smoke test.Housekeeping
phase2-search-siteis stale and safe to delete — its work is inmain, and it briefly carried this commit before I moved it to a properly named branch. Say the word and I'll delete it.🤖 Generated with Claude Code