Skip to content

Commit eabbfc4

Browse files
cliffhallclaude
andcommitted
docs(skills): fail closed on a missing ITEM_ID and an incomplete dump (#2452 review)
Clear ITEM_ID before the GHSA lookup so a failed listing cannot leave an earlier id in place; guard item-edit and item-delete on a non-empty id, and split the issue-side lookup from the Status edit so the delete recipe does not inherit it; end every dump guard with false so an incomplete dump returns non-zero; widen projectItems to first:100. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: cliffhall <[email protected]>
1 parent 8a78c74 commit eabbfc4

2 files changed

Lines changed: 28 additions & 12 deletions

File tree

‎.claude/skills/board-ops/SKILL.md‎

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Look it up by **title** in the full listing instead, then feed that item id to
6262

6363
```sh
6464
GHSA=GHSA-xxxx-yyyy-zzzz # the advisory's real id
65+
ITEM_ID= # never let an earlier lookup's id survive a failed one
6566
BOARD=$(gh project item-list 28 --owner modelcontextprotocol --format json --limit 2000)
6667
if jq -e '(.items | length) == .totalCount' <<<"$BOARD" >/dev/null; then
6768
ITEM_ID=$(jq -r '.items[] | select(.content.type=="DraftIssue")
@@ -180,23 +181,33 @@ through the repository also means the issue number cannot match another repo's
180181
issue — board #11 really does carry a `modelcontextprotocol/servers` card. For
181182
#11, swap in its node id `PVT_kwDOCt2Azc4BA5sz`.
182183

183-
Check the id is non-empty before using it: `item-edit --id ""` fails with an
184+
The mutation runs only on a non-empty id: `item-edit --id ""` fails with an
184185
opaque node-resolution error rather than saying the card was not found. The
185186
`|| ITEM_ID=` matters too — on a GraphQL error (a number that is a PR, not an
186187
issue; a rate limit) `gh api` still prints the raw error JSON to stdout, which
187-
would otherwise land in `ITEM_ID` as a non-empty "id".
188+
would otherwise land in `ITEM_ID` as a non-empty "id". `first:100` is the
189+
connection's maximum page; it counts the boards one issue is on, not the cards
190+
on a board, so it has no board-size exposure.
188191

189192
```sh
190193
N=<ISSUE_NUMBER>
191194
ITEM_ID=$(gh api graphql -F n="$N" -f query='query($n:Int!){
192195
repository(owner:"modelcontextprotocol",name:"inspector"){issue(number:$n){
193-
projectItems(first:20){nodes{id project{id}}}}}}' \
196+
projectItems(first:100){nodes{id project{id}}}}}}' \
194197
--jq '.data.repository.issue.projectItems.nodes[]
195198
| select(.project.id=="PVT_kwDOCt2Azc4BJVxt") | .id') || ITEM_ID=
196199
[ -n "$ITEM_ID" ] || echo "#$N has no card on #28 (or the lookup failed)" >&2
197-
# e.g. Status → In Review, when its PR opens
198-
gh project item-edit --project-id PVT_kwDOCt2Azc4BJVxt --id "$ITEM_ID" \
199-
--field-id PVTSSF_lADOCt2Azc4BJVxtzg5iI8c --single-select-option-id 159c8a02
200+
```
201+
202+
Then edit it — e.g. Status → In Review, when its PR opens:
203+
204+
```sh
205+
if [ -n "$ITEM_ID" ]; then
206+
gh project item-edit --project-id PVT_kwDOCt2Azc4BJVxt --id "$ITEM_ID" \
207+
--field-id PVTSSF_lADOCt2Azc4BJVxtzg5iI8c --single-select-option-id 159c8a02
208+
else
209+
echo "no ITEM_ID — nothing edited" >&2
210+
fi
200211
```
201212

202213
### Delete a card
@@ -206,8 +217,13 @@ not planned / obsolete / superseded shipped nothing, so its card is **deleted**,
206217
not parked in Done:
207218

208219
```sh
209-
# ITEM_ID from the issue-side lookup in "Move an existing card" above.
210-
[ -n "$ITEM_ID" ] && gh project item-delete 28 --owner modelcontextprotocol --id "$ITEM_ID"
220+
# ITEM_ID from the issue-side LOOKUP block in "Move an existing card" above —
221+
# the lookup only, not the item-edit that follows it.
222+
if [ -n "$ITEM_ID" ]; then
223+
gh project item-delete 28 --owner modelcontextprotocol --id "$ITEM_ID"
224+
else
225+
echo "no ITEM_ID — nothing deleted" >&2
226+
fi
211227
```
212228

213229
Deleting the card removes it from the board only — **the issue itself is
@@ -281,7 +297,7 @@ gh project item-list 28 --owner modelcontextprotocol --format json --limit 2000
281297
jq -e '(.items | length) == .totalCount' "$BOARD_TMP/board-snapshot.json" >/dev/null \
282298
&& echo "snapshot: $BOARD_TMP/board-snapshot.json" \
283299
|| { echo "SNAPSHOT INCOMPLETE — raise --limit and retake it before editing options" >&2
284-
rm -f "$BOARD_TMP/board-snapshot.json"; }
300+
rm -f "$BOARD_TMP/board-snapshot.json"; false; }
285301
```
286302

287303
Note the printed path; you need it to recover.
@@ -307,7 +323,7 @@ gh project item-list 28 --owner modelcontextprotocol --format json --limit 2000
307323
> "$BOARD_TMP/board-broken.json"
308324
jq -e '(.items | length) == .totalCount' "$BOARD_TMP/board-broken.json" >/dev/null \
309325
|| { echo "board-broken.json INCOMPLETE — raise --limit and re-run" >&2
310-
rm -f "$BOARD_TMP/board-broken.json"; } # so the steps below fail, not undercount
326+
rm -f "$BOARD_TMP/board-broken.json"; false; } # so the steps below fail, not undercount
311327
jq -r '[.items[]|select(.status==null)|.id]' "$BOARD_TMP/board-broken.json" \
312328
> "$BOARD_TMP/lost-ids.json"
313329
jq -r --slurpfile L "$BOARD_TMP/lost-ids.json" '($L[0]) as $lost

‎.claude/skills/issue-triage/SKILL.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ gh issue list --repo modelcontextprotocol/inspector --state open --limit 1000 \
5757
for P in 28 11; do
5858
gh project item-list $P --owner modelcontextprotocol --format json --limit 2000 > "$D/b$P.json"
5959
jq -e '(.items | length) == .totalCount' "$D/b$P.json" >/dev/null \
60-
|| { echo "board #$P listing INCOMPLETE — raise --limit and re-run" >&2; rm -f "$D/b$P.json"; }
60+
|| { echo "board #$P listing INCOMPLETE — raise --limit and re-run" >&2; rm -f "$D/b$P.json"; false; }
6161
done
6262
# Union of BOTH boards, filtered to this repo — org boards can hold other repos' issues.
6363
jq -s '[.[].items[] | select(.content.type=="Issue"
@@ -230,7 +230,7 @@ gh issue list --repo $R --state all --limit 2000 \
230230
for P in 28 11; do gh project item-list $P --owner modelcontextprotocol \
231231
--format json --limit 2000 > "$D/b$P.json"
232232
jq -e '(.items | length) == .totalCount' "$D/b$P.json" >/dev/null \
233-
|| { echo "board #$P listing INCOMPLETE — raise --limit and re-run" >&2; rm -f "$D/b$P.json"; }
233+
|| { echo "board #$P listing INCOMPLETE — raise --limit and re-run" >&2; rm -f "$D/b$P.json"; false; }
234234
done
235235
jq -nr --slurpfile o "$D/i.json" --slurpfile a "$D/b28.json" --slurpfile b "$D/b11.json" --arg R "$R" '
236236
($o[0] | map({key:(.number|tostring), value:{st:.state, sr:(.stateReason // ""),

0 commit comments

Comments
 (0)