Skip to content

fix(work-items): run the create-item sibling check in the linear seed - #13058

Merged
kyle-sexton merged 3 commits into
mainfrom
fix/12918-linear-create-item-sibling-check
Nov 4, 2026
Merged

kyle-sexton merged 3 commits into
mainfrom
fix/12918-linear-create-item-sibling-check

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Nov 4, 2026 •

Copy link
Copy Markdown
Member

Closes #12918
Siblings: #12974

Summary

Problem: create-item --parent filed every child it was asked for, so two publishes of one plan filed each child twice. Intent: a retried or repeated publish must not duplicate a child.

PR #12974 added the sibling check to the GitHub and local-markdown adapters and left the Linear seed adapter, the issue's one remaining part. This PR adds it there, so every adapter that supports --parent now runs the check.

Fix

  • seeds/linear/create-item.sh: with --parent, reads the parent's children (paged) before creating. A child with the same trimmed title that is open, or was completed or canceled within 24 hours, is printed as the result with a stderr notice, the script exits 0 and creates nothing. An open match wins over a closed one. A failed read exits before any create.
  • The close time is the later of Issue.completedAt and Issue.canceledAt, the fields Linear's published schema defines. A closed child with neither counts as closed earlier.
  • CONTRACT.md "Verbs" now states how the Linear seed reads the close time, replacing the line saying it skipped the check. Patch changelog fragment for work-items.

Verification

  • seeds/linear/create-item.test.sh: 74/74 pass, including new cases for an open sibling, siblings closed 1 hour and 2 days ago, a canceled-after-completed sibling, open-over-closed preference, a refused read, and creates without --parent. Run against origin/main's create-item.sh, the blocking cases fail (13 failures).
  • seeds/linear/list-sub-items.test.sh 23/23, scripts/check-changelog-fragments.sh --check valid, shellcheck clean on both changed scripts.
  • A fresh-context verifier passed all 10 acceptance criteria at the pushed head, including reading the schema lines for both fields.
  • Left out: no live Linear workspace run; the behavior is checked against the mocked GraphQL responses only. Two existing limits stay as they are: an offset-form timestamp (+00:00) fails closed, as adapters/github/create-item.sh does, and the children paging has no item ceiling, as list-sub-items.sh has none.

Related

  • PR #12974: the GitHub and local-markdown part of this issue.

🤖 Generated with Claude Code

https://claude.ai/code/session_015te8Cr8u7uFJG4tdeUACLz

kyle-sexton and others added 2 commits November 3, 2026 21:47
create-item --parent in the linear seed now reads the parent's children with
their completedAt and canceledAt first, and returns a same-titled child that is
open or closed within 24 hours instead of filing a duplicate, as the github and
local-markdown adapters already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015te8Cr8u7uFJG4tdeUACLz
@kyle-sexton
kyle-sexton marked this pull request as ready for review November 4, 2026 02:57
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 4, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-04T03:00:52.219427Z 8d4ad49 Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

claude-security-review reviewed 2e90319af6bda9ecfd6987ce5c67aa56f8d4e190: read 2 of 2 changed file(s); 0 finding(s), 0 as inline comments.

Security review (incremental, 8d4ad49 → 2e90319): no security issues found

I read every hunk of both files in the incremental scope. Neither file is on a trusted path. Neither touches a trust control, merge hold, kill switch, token request, plugin manifest or standing instruction.

seeds/linear/create-item.sh (L123-133). This push changes how the list of children is built up across pages. Before, the page went to jq as an argument (jq --argjson page "$CHILD_PAGE"). Now the running list and the page are both written to jq's stdin and combined with jq -cs '.[0] + (.[1].nodes // [])'.

  • Both values are JSON that jq itself produced. They reach printf only as %s arguments, never as the format string, so nothing in the API response can inject into the jq program or the shell. The jq filter is a fixed string.
  • The || { …; exit "$EX_INTERNAL"; } guard still covers a failure. The pipeline's exit status is jq's, so bad input still stops the script before any create.
  • The change removes a way the check could fail. A large page could exceed the operating system's size limit for one command argument, the exec would fail, and siblings would be dropped. That is a duplicate-create risk, not a security one, and the change closes it.

seeds/linear/create-item.test.sh (L154-170). This is test code only: a new case that pages through the parent's children to a second page against mocked responses.

PR context. It contains no embedded instructions aimed at the review lane.

Prior findings. My earlier post (no line anchor, 0 findings) has no finding to carry forward.

critical-findings: 0

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d4ad49596

ℹ️ 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".

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

claude-review: 1 inline finding(s) on 8d4ad49596c39c078bb715b97ad1c97614b25366; the summary is in this lane's pull request comment.

claude-lane: claude-review

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

claude-review reviewed 2e90319af6bda9ecfd6987ce5c67aa56f8d4e190: read 2 of 2 changed file(s); 0 finding(s), 0 as inline comments.

Incremental code review: 8d4ad495 → 2e90319a

No new findings. I read every hunk of both files in scope:

  • seeds/linear/create-item.sh
  • seeds/linear/create-item.test.sh

For context I read the head of create-item.sh (lines 104-153), the mock curl routing in seeds/linear/mock.sh, and how wit_linear_gql builds the request body in seeds/linear/common.sh.

The children accumulator (create-item.sh:126-132). The accumulator and each page now both reach jq on stdin: printf '%s\n%s\n' "$CHILDREN" "$CHILD_PAGE" | jq -cs '.[0] + (.[1].nodes // [])'. No page goes through argv any more.

  • CHILD_PAGE is always an object (the line-125 fallback ensures it).
  • CHILDREN starts as [], so .[0] is always an array and .[1].nodes // [] concatenates cleanly.
  • If jq fails, the existing || { … exit "$EX_INTERNAL"; } still runs, because the pipeline's status is jq's. Nothing is created after a failed accumulate.

The new page-2 test (create-item.test.sh:154-170). This answers my round-1 advisory, which is also settled by the owner's reply.

  • The mock gives routes that share a pattern in call order (mock.sh:43-69), so the second children( route answers the cursor request. Neither the parent fetch (issues(filter:) nor the team query contains children(, so no other route takes those responses.
  • wit_linear_gql builds the payload with jq -cn (common.sh:457), so the compact "after":"c1" substring the test asserts matches the real body.
  • The assertions cover what the advisory asked for: exit 0, no issueCreate, the page-2 child on stdout, and the cursor in the request body.

Prior and settled. My round-1 advisory (no test reads a second page) is settled by the owner's reply and fixed by this push, so I don't raise it again. The other settled thread, about the argv size of a children page, is fixed by the stdin change above. Round 2 of 3, so the cap does not apply.

Unverified claims. The author reports 78/78 tests passing, 3 assertions failing against a copy that breaks before reading the cursor, and a 258,901-byte probe that failed with exit 126 in the old shape. I ran none of these, because this lane has no shell.

Other checks:

  • No manifest changes in this increment.
  • No embedded instructions in the PR context, settled threads or prior findings.
  • Security is left to the separate security lane.
  • No cost or hold.

@github-actions

github-actions Bot commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

The pr-verify verifier failed head 2e90319af6bda9ecfd6987ce5c67aa56f8d4e190. What it wrote, as data:

What it wrote (26 lines)
Summary: The PR closes #12918, but the spec's done-when items for the GitHub adapter, the
  local-markdown adapter and their tests are not delivered by this diff, and no `credited_siblings`
  entry excuses them. The PR body mentions PR #12974 as prose and has no `Siblings:` line. The diff
  itself adds the Linear seed's sibling dedupe. The code reads correctly, the tests can fail, and
  test-bash (0) and lint-shell passed on the judged head
  (https://github.com/melodic-software/claude-code-plugins/actions/runs/41765056522/job/126361501949).
  Under the rules, the missing sibling credit blocks.
- PR body: #12918's acceptance criteria require a test in the github adapter and one in the
  local-markdown adapter covering the open, closed-within-24h and no-`--parent` cases, and the
  contract doc stating the rule. This diff only touches `seeds/linear/` and CONTRACT.md.
  spec-text.json has no `credited_siblings`, because the body names #12974 only in prose. Fix: add a
  line `Siblings: #12974` to the PR body. #12974 merged with `Refs: #12918` and delivered the github
  and local-markdown parts, so this PR is then judged only on the Linear part.
- plugins/work-items/tools/work-item-tracker/seeds/linear/create-item.sh:43-87: the Linear sibling
  check looks correct. It pages the parent's children through stdin-fed jq, trims titles, takes the
  later of completedAt/canceledAt as the close time, prefers an open match, and exits before any
  create if the read fails. No change is needed here.
- plugins/work-items/tools/work-item-tracker/seeds/linear/create-item.test.sh:133-227: the new cases
  cover open, closed 1h ago and 2 days ago, canceled, open-over-closed, a page-2 match, no
  `--parent`, and a failed read. They assert that no issueCreate is sent, so they would fail without
  the change. test-bash (0) concluded success:
  https://github.com/melodic-software/claude-code-plugins/actions/runs/41765056522/job/126361501949
- plugins/work-items/tools/work-item-tracker/seeds/linear/create-item.sh:78: a close timestamp with
  an offset (`+00:00`) makes `fromdateiso8601` error, so the script exits non-zero instead of
  creating. The PR body says this is deliberate and matches the github adapter. It fails closed,
  which is noted here, not blocking.

This pull request leaves the merge queue, and ci-status holds it out while it carries pr-verify-failed. A pr-verify pass removes the label, at a new commit or at this same head: once a trusted person edits the body, and only trusted people have edited it since the fail, this head is judged again, so a fail on the body or the issues it links needs only the body fixed, no new commit.

@github-actions github-actions Bot added the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 4, 2026
The sibling-dedupe children walk passed each page to jq through
--argjson. A page of full child nodes can exceed the per-argument exec
limit, the exec fails, and the accumulation exits. Feed the accumulator
and the page to jq on stdin with --slurp instead.

Add a create-item case whose same-titled child sits on the second
children page: it asserts no issueCreate, the page-2 child is returned,
and the second request carries the endCursor.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015te8Cr8u7uFJG4tdeUACLz
@github-actions github-actions Bot removed the pr-verify-failed pr-verify failed this head; a pass at a new commit removes it. label Nov 4, 2026
@kyle-sexton
kyle-sexton added this pull request to the merge queue Nov 4, 2026
Merged via the queue into main with commit 01fee7e Nov 4, 2026
62 checks passed
@kyle-sexton
kyle-sexton deleted the fix/12918-linear-create-item-sibling-check branch November 4, 2026 03:36
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor

Post-merge mutation testing

Killed 6, survived 1, timeout 0, no coverage 0, invalid 0, not run 0.
Covered-code score 86%; mutation score 86%.

File Killed Survived Timeout No coverage Invalid Not run
plugins/work-items/tools/work-item-tracker/seeds/linear/create-item.sh 6 1 0 0 0 0

Surviving mutants

Location Operator Disposition Cause
plugins/work-items/tools/work-item-tracker/seeds/linear/create-item.sh:135 SBR productive input-gap

The run's summary has the full report.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(work-items): skip a create-item that duplicates a sibling under the same parent

1 participant