Goal
create-issues adds each new issue to the linked Projects v2 board but leaves the Status field empty, so every card lands in "No Status" (outside every column). Set a default Status (and map the P* and size labels to the Priority/Size fields) when adding an item, so cards land categorized.
Symptom
After gh-toolkit create-issues <owner/repo> <issues.json> on a repo linked to a board, the issues appear as board items but all sit under "No Status". The Priority and Size single-select fields are also empty, even though the issue labels carry that data (P0, M, etc).
Reproduced on lipex360x/ledgerflow (2026-06-06): 8 issues created, all 8 in "No Status", Priority/Size blank.
Root cause
scripts/create-issues.sh, function add_to_board() (around lines 104-118): it runs the addProjectV2ItemById mutation but pipes the result to /dev/null, discarding the returned item.id. It never calls updateProjectV2ItemFieldValue, so no field is set. The item is added with empty Status/Priority/Size.
Fix path (chosen)
Extend add_to_board() (or add a sibling set_board_fields()):
- Capture the
item.id returned by addProjectV2ItemById (stop discarding it).
- Query the linked project once for the
Status, Priority, Size single-select field ids and their option ids (GraphQL projectV2.fields -> ProjectV2SingleSelectField). Cache for the run.
- Set
Status to Backlog via updateProjectV2ItemFieldValue.
- Map the issue's labels to fields, best-effort:
P0|P1|P2 -> Priority; XS|S|M|L|XL -> Size.
- Best-effort and idempotent: if a field or option does not exist on that project, skip it silently (a project without a
Size field must not break). Keep board work non-fatal to the exit code, matching the current contract (issue creation is the primary contract).
Default Status is Backlog (the entry column). Rejected alternative: leaving Status configurable via a flag now. That is scope creep; a fixed Backlog default solves the reported bug, and a --default-status flag can be a later issue if needed.
Implementation order (TDD)
Tests are bats with a PATH-injected gh mock (per this repo's CLAUDE.md). Land as separate commits. Use chained assertions (&& \).
- Red - in
tests/create-issues.bats, extend the gh mock to record GraphQL mutation calls to a log file, and add a test asserting that after create-issues, the recorded calls include an updateProjectV2ItemFieldValue setting Status to the Backlog option for the created item. Commit: test: create-issues sets board Status (failing). Confirm it fails because no such mutation is issued today (not a mock wiring error).
- Green - implement field-setting in
create-issues.sh until the test passes. Add the Priority/Size mapping with their own assertions. Commit: feat: create-issues sets Status/Priority/Size on board items.
- Refactor - extract the field-resolution helper if it bloats
add_to_board; keep tests green. Commit: refactor: extract board field resolution.
Also extend the dry-run so the omission is visible in preview (see AC): the dry-run currently exits before any board logic (lines 66-73) and prints only DRY_RUN:issue/<title>. Make it also print the intended fields, for example DRY_RUN:issue/<title>:status=Backlog,priority=P0,size=M.
Files to touch
| File |
What changes |
scripts/create-issues.sh |
capture item id; resolve field/option ids; set Status + map Priority/Size; extend dry-run output |
tests/create-issues.bats |
mock records GraphQL mutations; assert Status/Priority/Size set; assert dry-run prints intended fields |
README.md |
document board field behaviour |
SKILL.md |
note that create-issues sets Status=Backlog and maps Priority/Size |
Acceptance criteria
Tick each item by editing this issue body (gh issue edit 1) as you land the corresponding commit. Do not batch ticks at the end.
Out of scope
- A configurable
--default-status flag (separate issue if wanted).
- Backfilling Status on issues created before this fix (one-off, not part of the script contract).
- Changing the shared single "Board" project model.
Constraints (from this repo's conventions)
- Bash 3.2 compatible: guard array access with
${#arr[@]} before iterating.
- Output contract: any
FAILED line goes to stdout; logs and WARN go to stderr via log::*.
- Bats: chain assertions with
&& \; keep setup statements unchained.
- Never use
--no-verify. Never add a non-bash runtime.
Branch
git checkout main && git pull
git checkout -b fix/board-status-fields-1
Open the PR with Closes #1.
Goal
create-issuesadds each new issue to the linked Projects v2 board but leaves theStatusfield empty, so every card lands in "No Status" (outside every column). Set a defaultStatus(and map theP*and size labels to thePriority/Sizefields) when adding an item, so cards land categorized.Symptom
After
gh-toolkit create-issues <owner/repo> <issues.json>on a repo linked to a board, the issues appear as board items but all sit under "No Status". ThePriorityandSizesingle-select fields are also empty, even though the issue labels carry that data (P0,M, etc).Reproduced on
lipex360x/ledgerflow(2026-06-06): 8 issues created, all 8 in "No Status", Priority/Size blank.Root cause
scripts/create-issues.sh, functionadd_to_board()(around lines 104-118): it runs theaddProjectV2ItemByIdmutation but pipes the result to/dev/null, discarding the returneditem.id. It never callsupdateProjectV2ItemFieldValue, so no field is set. The item is added with emptyStatus/Priority/Size.Fix path (chosen)
Extend
add_to_board()(or add a siblingset_board_fields()):item.idreturned byaddProjectV2ItemById(stop discarding it).Status,Priority,Sizesingle-select field ids and their option ids (GraphQLprojectV2.fields->ProjectV2SingleSelectField). Cache for the run.StatustoBacklogviaupdateProjectV2ItemFieldValue.P0|P1|P2->Priority;XS|S|M|L|XL->Size.Sizefield must not break). Keep board work non-fatal to the exit code, matching the current contract (issue creation is the primary contract).Default Status is
Backlog(the entry column). Rejected alternative: leaving Status configurable via a flag now. That is scope creep; a fixedBacklogdefault solves the reported bug, and a--default-statusflag can be a later issue if needed.Implementation order (TDD)
Tests are bats with a PATH-injected
ghmock (per this repo'sCLAUDE.md). Land as separate commits. Use chained assertions (&& \).tests/create-issues.bats, extend theghmock to record GraphQL mutation calls to a log file, and add a test asserting that aftercreate-issues, the recorded calls include anupdateProjectV2ItemFieldValuesettingStatusto theBacklogoption for the created item. Commit:test: create-issues sets board Status (failing). Confirm it fails because no such mutation is issued today (not a mock wiring error).create-issues.shuntil the test passes. Add the Priority/Size mapping with their own assertions. Commit:feat: create-issues sets Status/Priority/Size on board items.add_to_board; keep tests green. Commit:refactor: extract board field resolution.Also extend the dry-run so the omission is visible in preview (see AC): the dry-run currently exits before any board logic (lines 66-73) and prints only
DRY_RUN:issue/<title>. Make it also print the intended fields, for exampleDRY_RUN:issue/<title>:status=Backlog,priority=P0,size=M.Files to touch
scripts/create-issues.shtests/create-issues.batsREADME.mdSKILL.mdAcceptance criteria
Backlogcolumn, not "No Status"P0/P1/P2labels set the boardPriorityfield;XS..XLsetSizeDRY_RUN:issue/<title>:status=Backlog,priority=P0,size=M)bats tests/; chained-assert audit prints nothing;shellcheck -x install.sh scripts/*.sh lib/*.sh bin/gh-toolkit tests/*.batscleanOut of scope
--default-statusflag (separate issue if wanted).Constraints (from this repo's conventions)
${#arr[@]}before iterating.FAILEDline goes to stdout; logs and WARN go to stderr vialog::*.&& \; keep setup statements unchained.--no-verify. Never add a non-bash runtime.Branch
git checkout main && git pull git checkout -b fix/board-status-fields-1Open the PR with
Closes #1.