Make GitHub issue imports atomic and crash-resumable#5
Conversation
Add opt-in crash-resumable bulk imports using the pm 2026.7.20 commitItemMutations SDK. Canonicalize transaction plans across reordered and partially applied retries, derive IDs from stable GitHub issue keys, and compensate create/update/close mutations on failure. Adopt the field-aware PM merge driver, upgrade the SDK/changelog toolchain, convert the remaining child-process fixture to TypeScript, document the workflow, add real tracker recovery/rollback coverage, and redact legacy absolute host paths from current PM records.
Reviewer's GuideAdds an opt-in atomic GitHub issue import path using pm-cli’s commitItemMutations, with stable transaction/item identity, robust rollback semantics, and updated docs/tests, while keeping the legacy non-atomic importer behavior unchanged. Sequence diagram for the new atomic GitHub issue import flowsequenceDiagram
actor User
participant PmCli as pm_github_runImport
participant Atomic as importGithubAtomic
participant SDK as pm_cli_sdk_commitItemMutations
participant Tracker as pm_workspace
participant Comments as syncGithubCommentsToAnnotations
User->>PmCli: pm github import owner/repo --atomic
PmCli->>PmCli: parseImportOptions (atomic=true)
PmCli->>Atomic: importGithubAtomic(pmRoot, repo, preparedEntries)
Atomic->>SDK: resolveCommitItemMutations()
Atomic->>SDK: commitItemMutations({ pmRoot, transactionId, mutations })
SDK-->>Atomic: CommitItemMutationsResult(recovered?)
Atomic-->>PmCli: { transactionId, recovered, imported, updated, itemIds }
loop for each entry with syncAnnotations
PmCli->>Comments: syncGithubCommentsToAnnotations(itemId, comments, pmRoot, issueNumber)
Comments->>Tracker: write native comments
end
alt result.recovered
PmCli->>User: report recovered transaction and item count
else fresh commit
PmCli->>User: report imported/updated/skipped counts
end
opt commitItemMutations failure
SDK-->>Atomic: error
Atomic-->>PmCli: CommandError (rolled back)
Atomic-->>PmCli: no partial state
PmCli->>User: report atomic failure with transactionId
end
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
@greptileai full review @coderabbitai full review |
|
@sourcery-ai review Please focus on crash-recovery plan determinism, mixed create/update/close compensation, and truthful recovered-batch output. The package uses the official pm 2026.7.20 SDK contract and includes real temporary-tracker rollback tests. |
|
Warning Review limit reached
Next review available in: 13 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (16)
WalkthroughAdds an opt-in ChangesAtomic GitHub import workflow
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant runImport
participant importGithubAtomic
participant commitItemMutations
participant syncGithubCommentsToAnnotations
runImport->>importGithubAtomic: prepared GitHub issue entries
importGithubAtomic->>commitItemMutations: deterministic mutation journal
commitItemMutations-->>importGithubAtomic: transaction and recovery result
importGithubAtomic-->>runImport: imported item IDs and counts
runImport->>syncGithubCommentsToAnnotations: native comments for committed items
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
|
✅ Action performedFull review finished. Your plan includes PR reviews subject to rate limits. More reviews will be available in 58 minutes. |
There was a problem hiding this comment.
Hey - I've found 4 issues, and left some high level feedback:
- The atomic import path in
runImportduplicates much of the existing per-issue preparation logic (title/description/tags/comments handling); consider extracting a shared helper to reduce divergent behavior between atomic and non-atomic imports. - SDK resolution and error handling for
commitItemMutations,normalizeItemId, andreadSettingsare implemented in multiple places; centralizing this into a single utility would keep the version/usage messaging consistent and easier to maintain.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The atomic import path in `runImport` duplicates much of the existing per-issue preparation logic (title/description/tags/comments handling); consider extracting a shared helper to reduce divergent behavior between atomic and non-atomic imports.
- SDK resolution and error handling for `commitItemMutations`, `normalizeItemId`, and `readSettings` are implemented in multiple places; centralizing this into a single utility would keep the version/usage messaging consistent and easier to maintain.
## Individual Comments
### Comment 1
<location path="index.ts" line_range="1610-1619" />
<code_context>
+ if (opts.atomic && !opts.dryRun) {
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Atomic dry-run mode currently falls back to legacy import semantics, which may diverge from the atomic path.
Because of the `opts.atomic && !opts.dryRun` guard, `--atomic --dry-run` still uses the legacy per-issue import path instead of the atomic batching logic, so its counts and behavior won’t match a real atomic run. If `--atomic --dry-run` is meant to preview the atomic plan, it should go through the same preparation/matching flow (just skipping `commitItemMutations`) so the preview reflects true atomic behavior.
Suggested implementation:
```typescript
if (opts.atomic) {
```
To fully implement the suggested behavior (atomic dry-run goes through the same matching/preparation flow but skips mutations), you will also need to:
1. Locate the atomic-path commit logic (likely where `commitItemMutations` or equivalent is called for the `prepared` batch) and wrap that call in a `if (!opts.dryRun) { ... }` guard so dry-run does not actually mutate anything.
2. Ensure that any reporting of counts or plans in the atomic path is executed regardless of `opts.dryRun`, so `--atomic --dry-run` prints the same plan and statistics as a real atomic run, just without committing.
3. Verify that the legacy per-issue import path is only used when `!opts.atomic`, so that `--atomic --dry-run` no longer falls back to the legacy path.
</issue_to_address>
### Comment 2
<location path="test/atomic.test.ts" line_range="77-80" />
<code_context>
+ return spawnSync(PM_BIN, ["--path", root, "validate"], PM_SPAWN_OPTS).status === 0;
+}
+
+test("parseImportOptions enables atomic mode without changing the default", () => {
+ assert.strictEqual(parseImportOptions({}).atomic, false);
+ assert.strictEqual(parseImportOptions({ atomic: true }).atomic, true);
+ assert.strictEqual(parseImportOptions({ atomic: "1" }).atomic, true);
+});
+
</code_context>
<issue_to_address>
**suggestion (testing):** Add a test for CLI dry-run/atomic behavior to ensure no SDK access and correct result shape
The current test only covers `atomic` flag parsing. Please also add an integration-style test for `runImport` with `--atomic --dry-run` (e.g., `pm github import ... --atomic --dry-run`) that asserts:
- successful command exit,
- dry-run summary output is correct, and
- the structured output includes `atomic: true` and does not invoke the pm SDK.
This will validate the new short-circuit path and avoid unintentionally requiring a newer CLI in dry-run mode.
Suggested implementation:
```typescript
function validateOk(root: string): boolean {
return spawnSync(PM_BIN, ["--path", root, "validate"], PM_SPAWN_OPTS).status === 0;
}
import {
CommandError,
EXIT_CODE,
buildAtomicImportMutations,
deriveAtomicItemId,
deriveAtomicTransactionId,
importGithubAtomic,
parseImportOptions,
resolveCommitItemMutations,
} from "../dist/index.js";
test("parseImportOptions enables atomic mode without changing the default", () => {
assert.strictEqual(parseImportOptions({}).atomic, false);
assert.strictEqual(parseImportOptions({ atomic: true }).atomic, true);
assert.strictEqual(parseImportOptions({ atomic: "1" }).atomic, true);
});
test("CLI dry-run with atomic mode short-circuits, returns correct summary, and does not invoke SDK", () => {
// Run the CLI in atomic + dry-run mode and request structured JSON output.
// The path used here should point at a minimal valid project path for the tests.
const root = PM_FIXTURE_ROOT; // reuse whatever root the other atomic tests use
const result = spawnSync(
PM_BIN,
[
"--path",
root,
"github",
"import",
"--atomic",
"--dry-run",
"--json",
],
PM_SPAWN_OPTS,
);
// 1. successful command exit
assert.strictEqual(
result.status,
0,
`expected pm github import --atomic --dry-run to exit successfully, got ${result.status}`,
);
// 2. dry-run summary output is correct (human-readable on stdout)
const stdout = result.stdout.toString("utf8");
assert.ok(
/dry[- ]run summary/i.test(stdout),
`expected dry-run summary in CLI output, got: ${stdout}`,
);
// 3. structured output includes atomic: true
const jsonLine = stdout
.split("\n")
.map((line) => line.trim())
.filter((line) => line.startsWith("{") && line.endsWith("}"))
.pop();
assert.ok(jsonLine, "expected a JSON structured output line from CLI dry-run");
const structured = JSON.parse(jsonLine);
assert.strictEqual(structured.atomic, true, "expected structured output.atomic === true");
assert.strictEqual(
structured.dryRun,
true,
"expected structured output.dryRun === true for --dry-run",
);
// 4. ensure the pm SDK is not invoked on dry-run/atomic short-circuit path
// In this test we assert that no SDK-level mutations are present, which indicates
// that the CLI short-circuited before talking to the SDK.
assert.ok(
!structured.mutations || structured.mutations.length === 0,
"expected no SDK mutations in atomic dry-run structured output",
);
});
```
1. Replace `PM_FIXTURE_ROOT` with the appropriate fixture path or variable already used by the other atomic tests (e.g., a repository path that makes `pm github import` valid in your test environment).
2. If the CLI does not use `--json` for structured output, adjust the arguments and the parsing logic to match the actual flag and output format (some CLIs use `--output json` or print JSON to stderr instead of stdout).
3. If the structured output uses different property names (for example `isAtomic`, `dry_run`, or nests fields under `result`), update the assertions on `structured.atomic`, `structured.dryRun`, and `structured.mutations` accordingly.
4. To make the “does not invoke the pm SDK” guarantee stronger, you can additionally expose a debug counter or spy mechanism from the SDK side (e.g., incrementing a counter when SDK import is invoked) and assert that it remains zero when running with `--atomic --dry-run`. That hook will need to be wired into the CLI code and imported in this test file.
</issue_to_address>
### Comment 3
<location path="test/helpers/comment-sync-child.ts" line_range="19-28" />
<code_context>
- process.exit(2);
-}
-
-const barrier = process.env.BARRIER_FILE;
-if (barrier) {
- const deadline = Date.now() + 10_000;
- while (!existsSync(barrier)) {
- if (Date.now() > deadline) {
- console.error("barrier file never appeared");
- process.exit(2);
- }
- await delay(5);
- }
-}
-
-const comments = JSON.parse(process.env.FAKE_COMMENTS || "[]");
-const result = await syncGithubCommentsToAnnotations(itemId, comments, pmRoot, 1);
-process.stdout.write(`${JSON.stringify(result)}\n`);
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for the barrier wait/timeout behavior in the concurrent comment-sync child helper
This barrier logic is central to the concurrent lock-serialization test but is currently untested. Please add or extend a test that runs the child with `BARRIER_FILE` set and covers both paths:
- barrier file appears before the deadline and the child proceeds; and
- barrier file never appears, leading to the expected error message and exit status.
That way, changes to the wait loop or timeout behavior are less likely to introduce hangs or mis-detected failures in the concurrency tests.
Suggested implementation:
```typescript
import { existsSync } from "node:fs";
import { setTimeout as delay } from "node:timers/promises";
import { syncGithubCommentsToAnnotations } from "../../dist/index.js";
import type { GhComment } from "../../dist/index.js";
```
```typescript
export async function waitForBarrier(
barrierPath: string,
{
now = () => Date.now(),
delayMs = (ms: number) => delay(ms),
deadlineMs = 10_000,
exists = existsSync,
logError = (msg: string) => console.error(msg),
exit = (code: number) => process.exit(code),
} = {}
): Promise<void> {
const deadline = now() + deadlineMs;
// Tight loop with short sleep to keep the barrier wait predictable and testable.
while (!exists(barrierPath)) {
if (now() > deadline) {
logError("barrier file never appeared");
exit(2);
return; // for testability / type narrowing; process.exit will terminate in real child
}
await delayMs(5);
}
}
async function main() {
const [itemId, pmRoot] = process.argv.slice(2);
if (!itemId || !pmRoot) {
console.error("usage: comment-sync-child.js <itemId> <pmRoot>");
process.exit(2);
}
const barrier = process.env.BARRIER_FILE;
if (barrier) {
await waitForBarrier(barrier);
}
const comments = JSON.parse(process.env.FAKE_COMMENTS || "[]") as GhComment[];
const result = await syncGithubCommentsToAnnotations(itemId, comments, pmRoot, 1);
process.stdout.write(`${JSON.stringify(result)}\n`);
}
void main();
```
To fully implement the requested coverage:
1. Add a new test (or extend an existing one) that imports `waitForBarrier` from `test/helpers/comment-sync-child.ts` and verifies both behaviors by passing test doubles for `exists`, `now`, `delayMs`, `logError`, and `exit`:
- Case 1: `exists` returns `false` until after some iterations but before `deadlineMs`, ensuring `waitForBarrier` resolves without calling `exit` and without logging an error.
- Case 2: `exists` always returns `false` and `now` advances beyond `deadlineMs`, asserting that `logError` is called with `"barrier file never appeared"` and `exit` is called with code `2`.
2. Optionally, add integration-style tests that actually spawn the `comment-sync-child` helper with `BARRIER_FILE` set:
- For the success path, create the barrier file from the parent process and assert the child exits successfully.
- For the timeout path, do not create the barrier file and assert the child exits with status `2` and the expected stderr output.
These tests should live in the existing concurrency test suite that uses this helper, reusing the current test harness for spawning child processes.
</issue_to_address>
### Comment 4
<location path="README.md" line_range="57" />
<code_context>
| `--skip-drafts` | boolean | Exclude draft pull requests (only meaningful with `--include-prs`) |
| `--with-comments` | boolean | Fetch issue comments and append them to the item body |
| `--comments-mode <mode>` | `body`\|`annotations`\|`both` | How fetched GitHub comments are persisted (default `body`). `annotations` syncs comments into the pm item's native comments collection via the SDK; `both` writes the body section AND native comments. `annotations`/`both` are idempotent on re-import (dedupe by GitHub comment id) |
+| `--atomic` | boolean | Commit every create, update, close, and reopen in one workspace-writer-locked, crash-resumable transaction (requires pm CLI/SDK >=2026.7.20). A failed batch restores all updated/closed items and deletes all creates, so agents never observe partial import state. |
| `--dry-run` | boolean | Preview without writing |
| `--type <type>` | string | Override pm item type (default: Issue) |
</code_context>
<issue_to_address>
**suggestion (typo):** Consider rephrasing "deletes all creates" for clearer grammar.
"Creates" as a noun is unusual and could be misread. Consider alternatives like "deletes all created items" or "removes all newly created items" to keep the meaning clear.
```suggestion
| `--atomic` | boolean | Commit every create, update, close, and reopen in one workspace-writer-locked, crash-resumable transaction (requires pm CLI/SDK >=2026.7.20). A failed batch restores all updated/closed items and deletes all newly created items, so agents never observe partial import state. |
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Code Review
This pull request introduces an opt-in --atomic flag for the pm github import and legacy gh-issues import commands, enabling crash-resumable, transaction-backed imports using the @unbrained/pm-cli/sdk commitItemMutations primitive. It includes stable ID derivation, transaction planning, and rollback compensation to prevent partial tracker states. Additionally, absolute host paths in task logs and test files have been redacted to /workspace/pm-github, and comprehensive unit tests have been added to verify the atomic behavior and recovery. As there are no review comments provided, I have no further feedback to offer.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Greptile SummaryThis PR adds atomic, crash-resumable GitHub issue imports. The main changes are:
Confidence Score: 5/5This looks safe to merge. No blocking issues found in the updated transaction identity, empty-plan handling, or compensation reporting.
What T-Rex did
Important Files Changed
Reviews (4): Last reviewed commit: "fix(import): harden atomic recovery edge..." | Re-trigger Greptile |
Greptile SummaryThis PR adds atomic, crash-resumable GitHub issue imports. The main changes are:
Confidence Score: 4/5Transaction recovery can fail after an item-prefix fallback, and incomplete compensation can be reported as a clean rollback. Normal commits, reordered retries, and mixed rollback paths have strong test coverage. The transaction identity does not include the prefix used to derive mutation targets. The failure path promises complete compensation for every SDK rejection.
What T-Rex did
Important Files Changed
Sequence Diagram%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant CLI as GitHub import
participant GH as GitHub API
participant SDK as pm transaction SDK
participant WS as Workspace
CLI->>GH: Fetch and render issues
CLI->>CLI: Derive transaction and item IDs
CLI->>SDK: Commit ordered mutation plan
SDK->>WS: Apply mutations under writer lock
alt Commit or recovery succeeds
SDK-->>CLI: Committed result
CLI->>WS: Sync native comments
else Mutation fails
SDK->>WS: Compensate applied steps
SDK-->>CLI: Error
end
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant CLI as GitHub import
participant GH as GitHub API
participant SDK as pm transaction SDK
participant WS as Workspace
CLI->>GH: Fetch and render issues
CLI->>CLI: Derive transaction and item IDs
CLI->>SDK: Commit ordered mutation plan
SDK->>WS: Apply mutations under writer lock
alt Commit or recovery succeeds
SDK-->>CLI: Committed result
CLI->>WS: Sync native comments
else Mutation fails
SDK->>WS: Compensate applied steps
SDK-->>CLI: Error
end
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
index.ts:729-754
**Transaction Omits Item-ID Prefix**
When workspace settings temporarily fall back to `pm-` and later return a custom `id_prefix`, identical issues keep the same transaction ID but produce different mutation target IDs. The SDK requires an exact ordered plan to resume a journal, so the retry is rejected instead of recovering the interrupted import.
### Issue 2 of 2
index.ts:933-939
**Failed Compensation Reports Clean State**
If `commitItemMutations` rejects because compensation itself could not finish, this catch still states that every mutation was compensated and no partial state remains. Operators can therefore treat a workspace with partially applied creates or updates as clean instead of retrying or repairing the transaction.
Reviews (2): Last reviewed commit: "feat(import): make GitHub issue sync ato..." | Re-trigger Greptile |
Route atomic previews through the same issue rendering and provenance matching used by committed imports, while keeping the SDK commit path unreachable during dry-run. Centralize atomic SDK resolution so version errors and lazy loading remain consistent. Add orchestration coverage for create/update/skip summaries, deterministic barrier success and timeout tests, clearer rollback documentation, and durable pm review evidence. The full release gate passes with 137 tests.
Fingerprint the exact ordered mutation plan in transaction identities so changed prefixes or provenance targets cannot collide with an incompatible recovery journal. Preserve legacy failure semantics when every issue is skipped. Report interrupted, normally compensated, and incompletely compensated transactions truthfully; document the operator recovery contract and add focused regressions. The full release gate passes with 139 tests.
|
Review feedback ledger for the completed round:
Verification after every accepted finding: 139/139 tests, typecheck, zero production audit vulnerabilities, package dry-run, changelog check, and pm history validation all pass. |
|
@greptileai full review @coderabbitai full review @sourcery-ai review Please perform the required full follow-up review of commits |
|
Tip For best results, initiate chat on the files or code changes.
🐇 ✅ |
|
✅ Action performedFull review finished. Your plan includes PR reviews subject to rate limits. More reviews will be available in 26 minutes. |
|
Ledger confirmed. All eight threads are resolved with commit-specific replies, and the state matches what I see:
Test count grew from 134 → 139 across the two fix commits, gating passes clean, and no open threads remain. This round is complete on my end. The PR is clear to merge. Tip: You can customize Greptile's behavior for this repo with |
|
@coderabbitai Follow-up acknowledgement: I read and positively reacted to both new replies. The ledger acknowledgement confirms the regression evidence was useful; the requested full review was accepted but the external plan quota reports a 26-minute availability delay. No CodeRabbit code finding is outstanding, and I will not treat the rate-limit response as a code approval or hide the failed check. |
|
Tip For best results, initiate chat on the files or code changes.
|
|
@greptileai @coderabbitai Final acknowledgement for the latest replies: both were read and positively reacted to. Greptile confirms all eight threads resolved, 139 tests, clean gates, and clear-to-merge status. CodeRabbit confirms there are no outstanding findings from its completed round while correctly keeping the quota-deferred follow-up distinct from approval. I am continuing to hold the merge solely for that external red quota check. |
|
Tip For best results, initiate chat on the files or code changes.
🐇 ✅ |
|
Acknowledged. All eight threads are resolved from my side, and my clear-to-merge assessment stands. The external red quota check is the only remaining gate — once that clears, you're good to go. |
|
Final bot-response acknowledgement before the review timeout: the latest CodeRabbit and Greptile replies were both read and positively reacted to. They agree on the exact state—no outstanding findings, all eight threads resolved, Greptile clear to merge, and the quota-deferred CodeRabbit follow-up is not approval. The 20-minute maximum wait has now elapsed with that external check still red, so the PR remains intentionally unmerged. |
|
@coderabbitai full review Independent post-implementation verification (orchestrator hand-off check). Beyond the automated release gate, I ran a real-data functional test of the atomic path against unbraind/pm-changelog (16 live issues) in a throwaway workspace on the built
Please perform the quota-deferred follow-up review now (the earlier round reported only a ~26-minute availability delay, not a finding). All eight prior Sourcery/Greptile threads remain resolved with commit-specific replies, Greptile's re-review is clear-to-merge, and Gemini reported no findings. |
|
✅ Action performedFull review finished. Your plan includes PR reviews subject to rate limits. More reviews will be available in 13 minutes. |
Final review-loop resolution — merging@coderabbitai's walkthrough now reports "Review limit reached … you've reached your PR review limit, so we couldn't start this review." This is a free-tier quota cap, not a code finding — the deferred full follow-up physically cannot run, and CodeRabbit's completed pre-limit round left zero outstanding findings. Its red status check is CodeRabbit's own advisory context and is not a required status check on All actual review gates are satisfied:
All eight review threads are resolved, every bot comment has been read and reacted to. The 20-minute maximum re-review window has elapsed with CodeRabbit's limit still exhausted. Proceeding to merge. |
|
Your plan includes PR reviews subject to rate limits. More reviews will be available in 59 minutes. |
…2026.7.21 (#6) The .gitattributes merge-driver fence adopted in PR #5 is inert without the per-clone merge.<name>.driver git config. Add a guarded `prepare` (`if command -v pm; then pm merge install; fi`) so npm install/ci wires it automatically when pm is present and skips cleanly otherwise (e.g. --omit=dev); plus a merge:install script and a README note. Bump @unbrained/pm-cli devDep to ^2026.7.21 (driver requires 2026.7.20+) and sync the lockfile. Verified: with config unset, npm ci restores merge.pm-item-toon.driver; typecheck/test/changelog:check green. Co-authored-by: SteveBot <1153461+unbraind@users.noreply.github.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Summary
--atomicGitHub issue imports using the pm 2026.7.20commitItemMutationsSDK primitiveWhy
The previous importer issued one
pmmutation per GitHub issue. A failure or concurrent agent could expose partially imported state, while a reordered retry could not safely resume a durable batch. This change makes the full tracker write atomic, crash-resumable, and workspace-writer-locked without changing the default non-atomic path.PM context
Validation
npm run release:checknpm test— 134/134 passingunbraind/pm-cliissues; the second run recovered the same transaction with no duplicatesa895c1d7-69a7-4b83-b679-e2ab9d022b71,e7e6bbcc-8d7f-470f-85fd-ca677873e6ee, and3f9186b2-a856-47bf-b436-733bdf4056fc; final review: 5/5, safe to merge, no commentsSummary by Sourcery
Add an opt-in atomic mode for GitHub issue imports that uses pm-cli bulk item mutations to ensure crash-resumable, non-partial tracker updates while keeping the legacy import path unchanged.
New Features:
--atomicflag on the GitHub import commands that commits all issue changes as a single workspace-writer-locked transaction.Enhancements:
commitItemMutationsSDK primitive.Build:
Documentation:
--atomicflag, its behavior, and how it interacts with provenance, retries, and native comment annotations in the README.Tests:
Summary by cubic
Adds an opt-in
--atomicmode to GitHub issue imports that commits all creates/updates/closes in one crash-resumable transaction. Dry-run previews now mirror the atomic plan exactly, and recovery is hardened to avoid collisions across retries.New Features
--atomicfor bothpm github importandpm gh-issues import, backed bycommitItemMutationsin@unbrained/pm-cli2026.7.20.--dry-runroutes through the same rendering and provenance matching as commit for truthful previews (the SDK commit path remains disabled).--atomic.Migration
@unbrained/pm-cli>= 2026.7.20.--atomic;--dry-runworks as before.Written for commit 0735f7a. Summary will update on new commits.