Skip to content

fix: route closed-issue imports through pm close for pm-cli 2026.8.3 - #28

Merged
unbraind merged 4 commits into
mainfrom
fix/close-reason-terminal-transitions
Aug 3, 2026
Merged

fix: route closed-issue imports through pm close for pm-cli 2026.8.3#28
unbraind merged 4 commits into
mainfrom
fix/close-reason-terminal-transitions

Conversation

@unbraind

@unbraind unbraind commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Summary

pm-cli 2026.8.3 enforces governance.require_close_reason: pm create --status closed and pm update --status closed are now hard close_reason_required errors. The old 2026.7.29 bypass that silently auto-routed these and defaulted a close reason is gone. This package relied on that bypass in one path.

Root cause

runImport (non-atomic create path) created new closed GitHub issues with pm create --status closed, so importing a not-yet-known closed issue failed under the enforced policy. The atomic path and the re-import reconciliation path were already correct (they route closure through pm close --reason); only the plain create path used the now-broken invocation.

Changes

  • index.ts — the create path now creates a closed upstream issue as open, then closes it via pm close --reason "GitHub issue #N closed", mirroring the atomic and reconciliation paths.
  • Provenance timestamps — thread GitHub closed_at through GhIssuePreparedGithubImport → every close site (create path, reconciliation, and the atomic close mutation) as --completed-at / completedAt, so imported items keep their real completion time instead of the import time. The reason stays factual provenance; no outcome is fabricated.
  • test/handler-failures.test.ts — the createLinkedItem fixture used the same pm create --status closed pattern to build a closed linked item; switched to create-then-close so the fixture is still a genuinely closed item under the enforced policy, without changing what the tests assert.
  • Pins @unbrained/pm-cli at ^2026.8.3 (peer >=2026.8.3).

pm item

Gates

gate result
npm run typecheck pass
npm run build pass
npm test 239/239 pass (incl. the two previously-failing tests)
npm run coverage pass — index.ts 88.83% lines / 80.15% branches / 89.94% functions (thresholds 88/79/89)
npm run changelog:check up to date

No governance policy, lint rule, or coverage threshold was weakened.

Summary by Sourcery

Route imports of closed GitHub issues through explicit pm close operations and preserve source completion timestamps under the updated pm-cli governance policy.

Bug Fixes:

  • Fix non-atomic GitHub issue imports that previously tried to create items born closed, which now violate pm-cli close-reason governance constraints.

Enhancements:

  • Thread GitHub closed_at into PreparedGithubImport and pm close mutations so imported items retain their original completion time instead of the import time.

Build:

  • Update peer and dev dependency pins to require pm-cli 2026.8.3 in line with the new close-reason enforcement.

Tests:

  • Adjust linked-item test fixtures to create items open and then close them via pm close so closed-status tests remain valid under the new governance rules.

Chores:

  • Record the pm governance item history and spec files related to this change in the repository.

Summary by cubic

Fixes imports of closed GitHub issues and project items under @unbrained/pm-cli 2026.8.3 by creating items open, then closing via pm close --reason with --completed-at. Also fixes id parsing so create‑then‑close reliably closes the new item.

  • Bug Fixes

    • Route all closures through pm close --reason; pass GitHub closed_at as --completed-at.
    • Project import: omit --status closed on create/update; close after with a provenance reason.
    • parseCreatedItemId now reads the flat { id } from pm create --json (drops the unused { item.id } shape).
    • Create path: if the new id can’t be parsed, count as skipped and don’t report a successful import; share a single close-argv builder to keep reason and timestamp consistent.
    • Tests: switch fixtures to create‑then‑close; add a contract test for the real pm create --json envelope and an end‑to‑end closed‑issue import that asserts the item lands closed.
  • Dependencies

    • Pin @unbrained/pm-cli to ^2026.8.3 (peer >=2026.8.3).

Written for commit e1b8db4. Summary will update on new commits.

Review in cubic

Bump peerDependencies to >=2026.8.3 and devDependencies to ^2026.8.3 so the
package tracks the published latest. Required for the close_reason enforcement
fix in the follow-up commit.
pm-cli 2026.8.3 enforces governance.require_close_reason: pm create --status
closed and pm update --status closed are now hard close_reason_required errors
(the old auto-route bypass that defaulted a reason is gone).

runImport's non-atomic create path created new closed GitHub issues with
pm create --status closed, so importing a not-yet-known closed issue failed.
Create such items open, then close them through pm close --reason, mirroring
the already-correct atomic path and the re-import reconciliation path.

Thread the GitHub closed_at completion timestamp through GhIssue ->
PreparedGithubImport -> every close site (create path, reconciliation, and the
atomic close mutation) as --completed-at / completedAt, so imported items keep
their real completion time instead of the import time. The reason stays factual
provenance (GitHub issue #N closed); no outcome is fabricated.

The handler-failure test fixture createLinkedItem used the same
pm create --status closed pattern to build a closed linked item; switch it to
create-then-close so the fixture is still a genuinely closed item under the
enforced policy, without changing what the tests assert.

Refs: pm-github-rwq9
@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@sourcery-ai

sourcery-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Reviewer's Guide

Routes imports of closed GitHub issues through pm close with a provenance reason and optional completion timestamp, aligns test fixtures with new governance requirements, and pins pm-cli to the version that enforces require_close_reason.

Sequence diagram for importing a closed GitHub issue via pm close

sequenceDiagram
  actor Github
  participant IndexTs as index_ts
  participant PmCli as pm_cli

  Github->>IndexTs: runImport(issue)
  IndexTs->>IndexTs: prepareGithubImport(issue)
  Note over IndexTs: prepared.closedAt = issue.closed_at

  IndexTs->>PmCli: pmRun(["create", "--status", "open", "--json", ...])
  PmCli-->>IndexTs: created stdout
  IndexTs->>IndexTs: parseCreatedItemId(stdout)

  alt status was closed
    IndexTs->>PmCli: pmRun(["close", createdId, "--reason", ..., "--completed-at", closedAt])
    PmCli-->>IndexTs: close result
  end

  opt syncAnnotations
    IndexTs->>IndexTs: syncGithubCommentsToAnnotations(createdId, comments, pmRoot, issue.number)
  end
Loading

File-Level Changes

Change Details Files
Route creation of closed imported items through a two-step create-then-close flow, preserving GitHub completion timestamps and complying with pm-cli close-governance.
  • Extend GhIssue and PreparedGithubImport to carry GitHub closed_at as closedAt for provenance.
  • In atomic import mutations, pass options.completedAt when closing items if a source closedAt exists.
  • In reconciliation, build pm close argument lists that include --completed-at when the imported GitHub issue has a closedAt timestamp.
  • In the non-atomic create path, create items as open when the upstream issue is closed, then close them via pm close with a provenance reason and --completed-at if available, reusing the parsed created item id for both closing and annotation sync.
index.ts
Align test fixtures with the new close-reason requirements while preserving their intended status and tags.
  • Update the createLinkedItem helper to create closed fixtures as open, then close them via pm close with a factual test reason.
  • Keep fixture tagging and final status behavior unchanged so existing assertions remain valid.
test/handler-failures.test.ts
Pin the pm-cli dependency to the version that enforces require_close_reason and record the associated pm item history.
  • Update peer and dev dependency ranges for @unbrained/pm-cli to >=2026.8.3 / ^2026.8.3.
  • Add the pm item provenance files under .agents/pm to track the governance change and its implementation history.
  • Regenerate package-lock.json to reflect the new pm-cli version.
package.json
.agents/pm/history/pm-github-rwq9.jsonl
.agents/pm/issues/pm-github-rwq9.toon
package-lock.json
.gitattributes

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Summary by CodeRabbit

  • Bug Fixes

    • GitHub issue imports now preserve the original closure timestamp.
    • Closed issues are imported and explicitly closed with a required reason.
    • Import failures leave newly created items open and report an error.
    • Reconciliation and non-atomic import paths now retain completion metadata consistently.
  • Tests

    • Updated linked-item scenarios to validate required close reasons and successful closure.
  • Chores

    • Added merge handling rules for project-management files.

Walkthrough

GitHub issue imports now preserve closed_at, pass completion metadata to close operations, and create closed issues as open before explicitly closing them. The PM CLI version, fixtures, PM records, and merge drivers were updated accordingly.

Changes

GitHub close import

Layer / File(s) Summary
Completion timestamp propagation
index.ts
GhIssue and PreparedGithubImport carry GitHub completion timestamps. Atomic close mutations pass them as completedAt.
Non-atomic close workflow
index.ts
Existing items use --completed-at. New closed issues are created open, parsed from JSON, and then closed with the source timestamp.
CLI enforcement and validation support
package.json, test/handler-failures.test.ts, .agents/pm/..., .gitattributes
The PM CLI version and closed-item fixtures were updated. PM issue records document the fix. PM file merge drivers were added.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant GitHub
  participant Importer
  participant pm-cli
  GitHub->>Importer: Return issue with closed_at
  Importer->>pm-cli: Create issue as open with JSON output
  pm-cli-->>Importer: Return created item ID
  Importer->>pm-cli: Close item with --completed-at
  pm-cli-->>Importer: Return close result
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The description clearly explains the close-reason enforcement fix, timestamp propagation, dependency update, tests, and validation results.
Title check ✅ Passed The title clearly identifies the primary change: routing closed-issue imports through pm close for pm-cli 2026.8.3.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/close-reason-terminal-transitions

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@index.ts`:
- Around line 2412-2425: Update the parseCreatedItemId failure branch within the
mustClose handling to increment skipped and continue immediately after logging
the missing created item ID. Keep the existing successful close and
close-command failure paths unchanged so an item is counted as imported only
when the required close operation completes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3ceb0261-0751-4091-8652-6d063f309861

📥 Commits

Reviewing files that changed from the base of the PR and between 29480dc and 99a56eb.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (6)
  • .agents/pm/history/pm-github-rwq9.jsonl
  • .agents/pm/issues/pm-github-rwq9.toon
  • .gitattributes
  • index.ts
  • package.json
  • test/handler-failures.test.ts

Comment thread index.ts
@greptile-apps

greptile-apps Bot commented Aug 3, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes closed-issue imports under @unbrained/pm-cli 2026.8.3, which enforces governance.require_close_reason and now rejects pm create --status closed and pm update --status closed as hard errors. The fix routes all closed-status transitions through pm close --reason, and threads GitHub's closed_at as --completed-at so imported items preserve their real completion time. It also corrects a pre-existing parser bug (parseCreatedItemId was reading a nested {item:{id}} shape that the CLI has never emitted — it has always emitted a flat {id,...} envelope), which meant the create-then-close path was silently inert on every run.

  • runImport (issue import): Creates closed issues as open, then closes via pm close --reason + --completed-at; reconciliation path updated identically via shared githubCloseArgs helper.
  • runProjectImport (project board import): Both the create path (new items) and update path (re-import of existing items with a closed board Status mapping) now go through create/update-then-close, avoiding the forbidden --status closed argument.
  • parseCreatedItemId: Fixed to read parsed?.id (flat envelope), with a new contract test that runs real CLI output to prevent shape drift; previous P1 already addressed per review thread.

Confidence Score: 5/5

Safe to merge — all three previously broken paths (issue-import create, project-import create, project-import update) are correctly routed through pm close --reason, and the always-failing parseCreatedItemId parser is fixed and contract-tested against real CLI output.

The two issues flagged in the previous review round are both resolved: parseCreatedItemId now reads the flat {id} envelope the CLI actually emits, and the create-then-close path correctly counts a parse failure as skipped rather than imported. Error handling on all new close-after-create and close-after-update branches is symmetric with the existing error accounting. The new end-to-end tests assert final item status (not just import counters), which is the signal that would have caught the original regression. No governance policy was weakened, and all coverage thresholds are met.

Files Needing Attention: No files require special attention.

Important Files Changed

Filename Overview
index.ts Core fix: parseCreatedItemId reads flat {id} envelope (not {item:{id}}); runImport and runProjectImport create-then-close paths handle all error branches correctly with proper skipped/imported/updated accounting; closedAt threading via githubCloseArgs is clean and applied consistently across reconciliation, create-then-close, and atomic mutation paths.
test/comments-sync.test.ts Unit test updated to pin the real flat envelope shape; adds a contract test that runs actual pm create --json output through parseCreatedItemId and cross-checks the parsed id against the workspace, preventing silent shape drift.
test/handler-failures.test.ts Adds three new end-to-end tests: closed project-item create path, closed board-status update path, and the never-before-covered runImport create-then-close path that catches the always-broken parseCreatedItemId regression; createLinkedItem fixture updated to create-then-close under the enforced policy.
package.json Bumps @unbrained/pm-cli dev dependency and peer requirement to ^2026.8.3 / >=2026.8.3 to match the close-reason enforcement this PR is built against.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[GitHub issue / project item fetched] --> B{status === closed?}
    B -- No --> C[pm create --status open/in_progress/etc.]
    B -- Yes --> D[pm create --status open + --json]
    D --> E{parseCreatedItemId\nparsed?.id flat envelope}
    E -- id found --> F[pm close --reason\nGitHub provenance\n+ --completed-at closed_at]
    E -- id missing --> G[skipped++ continue]
    F --> H{close.ok?}
    H -- Yes --> I[syncAnnotations if needed\nimported++]
    H -- No --> J[skipped++ continue]
    C --> K[imported++]
Loading

Reviews (8): Last reviewed commit: "Close the create-then-close path the imp..." | Re-trigger Greptile

Comment thread index.ts

@sourcery-ai sourcery-ai 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.

Hey - I've found 1 issue, and left some high level feedback:

  • The new create-then-close flow duplicates close argument construction in several places; consider extracting a small helper to build closeArgs (including optional --completed-at) to keep behavior consistent and reduce repetition.
  • In runImport, the branch where mustClose is true but createdId is undefined only logs and leaves the item open; if this represents a hard failure for closed imports, you may want to increment skipped or return an explicit error to make the behavior clearer.
  • The GhIssue/PreparedGithubImport closed_at/closedAt handling is now spread across multiple sites; adding a single normalization point (e.g., a small helper to map GitHub issue objects to PreparedGithubImport) could reduce the risk of future divergence in how completion timestamps are propagated.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new create-then-close flow duplicates close argument construction in several places; consider extracting a small helper to build `closeArgs` (including optional `--completed-at`) to keep behavior consistent and reduce repetition.
- In `runImport`, the branch where `mustClose` is true but `createdId` is undefined only logs and leaves the item open; if this represents a hard failure for closed imports, you may want to increment `skipped` or return an explicit error to make the behavior clearer.
- The GhIssue/PreparedGithubImport `closed_at`/`closedAt` handling is now spread across multiple sites; adding a single normalization point (e.g., a small helper to map GitHub issue objects to `PreparedGithubImport`) could reduce the risk of future divergence in how completion timestamps are propagated.

## Individual Comments

### Comment 1
<location path="index.ts" line_range="2413-2422" />
<code_context>
+          skipped++;
+          continue;
+        }
+      } else {
+        console.error(`#${issue.number}: could not parse created item id — left open`);
+      }
+    }
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Consider treating a failed `createdId` parse as a hard failure when `mustClose` is true.

In the `mustClose` branch, a `parseCreatedItemId` failure currently leaves the item open and the import continues without incrementing `skipped`, so callers won’t see that we failed to mirror the upstream closed status. Align this with how `close` failures are handled (e.g., increment `skipped` and `continue`, or use a safer fallback) to avoid silently ending up with open items that should be closed.

```suggestion
    if (mustClose) {
      if (createdId) {
        const closeArgs = ["--path", pmRoot, "close", createdId, "--reason", `GitHub issue #${issue.number} closed`];
        if (closedAt) closeArgs.push("--completed-at", closedAt);
        const close = pmRun(closeArgs);
        if (!close.ok) {
          console.error(`#${issue.number}: close after import failed — ${close.stderr}`);
          skipped++;
          continue;
        }
      } else {
        console.error(`#${issue.number}: could not parse created item id — treating as failed close`);
        skipped++;
        continue;
      }
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread index.ts
@unbraind

unbraind commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Pushed a follow-up commit addressing all three review findings, plus a root cause found while verifying them.

What changed

  1. The accounting defect all three of you flagged (CodeRabbit 🟠 Major, Greptile P1, Sourcery bug_risk): the mustClose branch with no parsed id logged "left open" and then fell through to imported++. It now increments skipped and continues, matching the adjacent !close.ok branch.

  2. The root cause underneath it. parseCreatedItemId read parsed.item.id, but pm create --json emits a flat envelope — {id, status, changed_field_count} — with no item wrapper. Verified against the real CLI. createdId was therefore undefined on every run, so the close after create never happened: every newly-imported closed issue landed OPEN and every --comments-mode=annotations sync was silently skipped, while the import still reported success. The create-then-close mechanism this PR exists to add was inert in production.

    The unit test could not catch this because it asserted the same fabricated {item:{id}} shape the parser expected — the test and the parser agreed with each other instead of with the CLI.

  3. Sourcery's duplication note: the two close sites now share a single githubCloseArgs(id) closure, so the --reason provenance and --completed-at evidence cannot drift apart.

New tests — the point is that none of them can pass against the old parser:

  • the unit test pins the real flat shape and asserts the {item:{id}} wrapper returns undefined, so the old behaviour cannot come back green;
  • a contract test parses the output of a real pm create --json run and asserts there is no item wrapper;
  • an end-to-end test imports an unlinked, already-closed issue and asserts the final status is closed. Verified to fail against the old parser and pass against the new one.

Gates: typecheck clean, 241/241 tests pass, release:check (incl. coverage) green, changelog:check up to date.

Please re-review the new commit.

@greptileai
@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

@unbraind, I will perform a full review of the follow-up commit. I will verify the flat pm create --json contract, the close-failure accounting, shared close arguments, and the new regression coverage.


✏️ Learnings added
Learnt from: unbraind
URL: https://github.com/unbraind/pm-github/pull/28

Timestamp: 2026-08-03T22:50:50.723Z
Learning: In this repository, `pm create --json` emits a flat JSON envelope that includes `id`, `status`, and `changed_field_count`; it does not wrap the created item in an `item` property. Code that parses newly created PM item IDs must read the top-level `id`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

✅ Action performed

Full review finished.


Your included review limit is currently reached under our Fair Usage Limits Policy. This review may still proceed through usage-based billing if eligible. Your next included review will be available in 34 minutes.

The initial sweep fixed runImport (issue import) and the createLinkedItem
test fixture, but missed runProjectImport (the github project import
handler). That handler still issued pm create --status closed for project
board items wrapping closed issues, and pm update --status closed for
re-imports whose board Status option maps to closed. Both are hard
close_reason_required errors under pm-cli 2026.8.3 — a user-facing bug:
importing a GitHub project with closed items would fail.

Fix the create path to create open then close via pm close --reason with
factual provenance (the wrapped issue repo#number, or the project ref for
draft issues). Fix the update path to omit --status when the mapped status
is closed, then close via pm close --reason after the update.

Also fix a latent defect in parseCreatedItemId: it looked for
parsed.item.id but pm create --json emits { id: ... } at the top level, so
the create-then-close path in runImport could not read the new id and
silently left the item open instead of closing it. Now accepts both
parsed.id and parsed.item.id.

Added two handler-level tests: a closed upstream issue on the create path,
and a closed board-status mapping on the update path. Updated the
parseCreatedItemId unit test for the real emit shape.

Refs: pm-github-rwq9
Re-applies the three review findings from round 1, which were lost when this
branch was force-updated, and drops the `item` fallback that was added instead.

parseCreatedItemId read `parsed.item.id`, but `pm create --json` emits a flat
receipt — {id, status, changed_field_count} — with no `item` wrapper (mutations
return a flat receipt; only queries such as `pm read`/`pm list` wrap). The id
was therefore always undefined, so the close after create never ran: every
newly-imported closed issue landed OPEN, and every --comments-mode=annotations
sync was silently skipped, while the import still reported success. Nothing
failed loudly because the parser returns undefined rather than throwing.

No `item` fallback is kept. A fallback for a shape the host has never emitted
is dead code no real CLI can exercise, and a test asserting it restores the
exact failure mode that hid the bug: a parser and a test agreeing with each
other rather than with the CLI.

- parseCreatedItemId reads the flat `id`.
- The unit test pins the real shape and asserts the wrapper shape yields
  undefined, so the old behaviour cannot return green.
- A contract test parses output from a REAL `pm create --json` run and asserts
  there is no `item` wrapper, so host drift fails at the parser rather than
  silently downstream.
- A new end-to-end test imports an unlinked, already-closed issue and asserts
  the final status is closed. Verified to fail against the old parser.

Also re-applied from review:
- The mustClose branch with no parsed id logged "left open" and then fell
  through to imported++, reporting a closed issue as a successful import. It
  now counts skipped and continues, matching the adjacent close-failure branch
  and the project-import path, which already handled this correctly.
- The two close sites built identical argv; extracted githubCloseArgs(id) so
  the reason and --completed-at evidence cannot drift apart.

typecheck clean, 243/243 tests pass.
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.

1 participant