Skip to content

Route terminal CSV row imports through pm close for pm-cli 2026.8.3 - #57

Merged
unbraind merged 4 commits into
mainfrom
route-terminal-csv-imports-through-pm-close
Aug 3, 2026
Merged

unbraind merged 4 commits into
mainfrom
route-terminal-csv-imports-through-pm-close

Conversation

@unbraind

@unbraind unbraind commented Aug 3, 2026 •

Copy link
Copy Markdown
Owner

pm-cli 2026.8.3 enforces governance.require_close_reason on every programmatic transition into closed. Both pm create --status closed and pm update <id> --status closed are now hard close_reason_required errors — the 2026.7.29 bypass that invented the reason "Closed via pm update" is gone. Only pm close --reason may close an item.

What changed

A CSV row whose status is closed is now created open and immediately transitioned through pm close --reason.

The reason text matters here. The CSV import format carries no close-reason or resolution column, so there is no genuine closure evidence to forward. Rather than invent an outcome the source never recorded (e.g. "completed"), the reason states the import provenance factually:

Imported from <source> (source status: closed)

canceled is not gated by that policy and is still applied directly at create time, unchanged.

What deliberately did not change

The transaction-marker semantics are untouched: presence of the csv-tx/csv-txrow row marker — not item status — remains the applied signal. A legitimately closed imported row is still recognised on resume and never re-imported, and a compensated create still has its markers stripped before closing, so it is correctly not recognised.

Verification

typecheck, build, test and release:check (coverage gate, prod audit, pack dry-run, changelog:check) all pass against 2026.8.3. Pin moved to ^2026.8.3 with the lockfile refreshed.

pm items

  • pm-csv-mmi3 — Route terminal-status CSV imports through pm close for pm-cli 2026.8.3

Tracked fleet-wide under the absorption epic pm-cli-website-uqzw, which lives in the maintainer private companion tracker and is therefore not linkable from here.

Summary by Sourcery

Route terminal-status CSV imports through pm-cli’s pm close --reason with provenance-aware close reasons to comply with governance.require_close_reason in pm-cli 2026.8.3.

New Features:

  • Introduce provenance-based close reason generation for terminal CSV statuses via a shared helper used by create and update flows.

Bug Fixes:

  • Ensure CSV rows with status closed are created as open and then closed via pm close --reason, avoiding reason-less terminal transitions rejected by pm-cli 2026.8.3.

Enhancements:

  • Standardize terminal status handling for imported items by always applying closed/canceled via pm close --reason with factual import provenance instead of invented outcomes.

Build:

  • Bump pm-cli peer and dev dependency constraints to 2026.8.3 to align with new close-reason enforcement.

Chores:

  • Add PM agent history and issue tracking files for the pm-csv-mmi3 change.

Summary by cubic

Route terminal CSV imports through pm close --reason to comply with @unbrained/pm-cli 2026.8.3, and harden the non-atomic create‑then‑close path by compensating failures and only claiming success when closure is verified; also remove a dead JSON parse fallback to prevent silent open orphans.

  • Bug Fixes

    • Closed CSV rows: create as open, then close via pm close --reason using "Imported from (source status: closed)"; update flow now uses the same provenance reason (including canceled).
    • Make create‑then‑close all‑or‑nothing: if pm close fails, compensate the orphan (close) with --validate-close off; only treat as compensated when status is verified closed; if verification fails, report "could not be verified" with the created id; if compensation fails, include the id and state it was left open; if the create id can’t be recovered, hard‑fail the row.
    • Remove the parsed.item?.id fallback; only parse the flat {id,...} from pm create --json to avoid masking failures.
  • Dependencies

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

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

Review in cubic

pm-cli 2026.8.3 enforces governance.require_close_reason on every programmatic
transition into `closed`: `pm create --status closed` and
`pm update --status closed` are now hard close_reason_required errors, and the
2026.7.29 bypass that invented the reason "Closed via pm update" is gone. Only
`pm close --reason` may close an item.

A CSV row whose status is `closed` is therefore created `open` and immediately
transitioned through `pm close --reason`. The CSV source carries no
close-reason or resolution column, so the reason states the import provenance
factually — "Imported from <source> (source status: closed)" — rather than
inventing an outcome such as "completed" that the source never recorded.

`canceled` is not gated by that policy and is still applied directly at create
time, unchanged.

The transaction-marker semantics are unaffected: presence of the csv-tx row
marker, not item status, remains the applied signal, so a legitimately closed
imported row is still recognised on resume and never re-imported, while a
compensated create still has its markers stripped before closing.

Pin moved to ^2026.8.3 with the lockfile refreshed. typecheck, build, tests and
release:check (coverage, audit, pack, changelog) all pass against 2026.8.3.
@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 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.

Sorry @unbraind, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Reviewer's Guide

Routes terminal-status CSV imports through pm close --reason to comply with pm-cli 2026.8.3’s close-reason enforcement, and updates dependency pins and PM metadata accordingly.

Sequence diagram for CSV import of terminal closed status via pm close --reason

sequenceDiagram
  actor CsvSource
  participant CsvImporter
  participant upsertCreate
  participant pm_cli

  CsvSource->>CsvImporter: CSV row (status = closed)
  CsvImporter->>upsertCreate: upsertCreate(pmRoot, p(status=closed), source, extraTags)
  upsertCreate->>pm_cli: pm --path pmRoot create --title p.title --status open --json
  pm_cli-->>upsertCreate: { id }
  upsertCreate->>pm_cli: pm --path pmRoot close id --reason importCloseReason("closed", source)
  pm_cli-->>upsertCreate: closed item
Loading

File-Level Changes

Change Details Files
Ensure CSV-imported rows with terminal status transition via pm close --reason using a provenance-based close reason string.
  • Document that upsertCreate/upsertUpdate now route terminal transitions through pm close --reason instead of setting closed directly at create/update time.
  • Introduce importCloseReason helper to build factual provenance close reasons for terminal statuses based on CSV source and status.
  • Modify upsertCreate to map CSV status closed to open at create time, then immediately call pm close --reason for successfully created items.
  • Change upsertUpdate to always apply terminal closed/canceled via pm close --reason using importCloseReason instead of hard-coded reasons like "completed"/"canceled".
  • Adjust JSON parsing in upsertCreate to capture the created id in a local variable and perform the follow-up close when required.
index.ts
Align package tooling with pm-cli 2026.8.3 and record the governance change in PM agent files.
  • Bump @unbrained/pm-cli peerDependency and devDependency versions from 2026.7.29 to 2026.8.3.
  • Refresh package-lock.json to reflect the new pm-cli version and dependency graph.
  • Add PM agent history and issue tracking files for pm-csv-mmi3 to document the change.
  • Leave .gitattributes content structurally unchanged (touched only by metadata or formatting).
package.json
package-lock.json
.agents/pm/history/pm-csv-mmi3.jsonl
.agents/pm/issues/pm-csv-mmi3.toon
.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

Warning

Review limit reached

@unbraind, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 8 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a561786b-a33c-410b-a229-de46ef58fd77

📥 Commits

Reviewing files that changed from the base of the PR and between 75bcb41 and e390525.

⛔ 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-csv-mmi3.jsonl
  • .agents/pm/issues/pm-csv-mmi3.toon
  • .gitattributes
  • index.ts
  • package.json
  • test/atomic.test.ts

Summary by CodeRabbit

  • New Features

    • CSV imports now preserve factual provenance when marking items as closed or canceled.
    • Closed imports are created and then transitioned to closed status with an explanatory reason.
  • Bug Fixes

    • Updated status handling to avoid inaccurate hardcoded completion or cancellation reasons.
  • Chores

    • Updated the PM CLI compatibility requirements to version 2026.8.3 or later.
    • Improved merge handling for PM documentation and issue files.

Walkthrough

CSV imports now create closed items as open and transition them with pm close --reason. Close reasons include the source label and original terminal status. The PM CLI requirement is raised to 2026.8.3.

Changes

CSV import status handling

Layer / File(s) Summary
Closed-status import transition
package.json, index.ts
The import flow creates closed rows as open, closes them with pm close --reason, and uses provenance-based reasons for closed and canceled updates.
PM issue tracking and merge configuration
.agents/pm/issues/pm-csv-mmi3.toon, .agents/pm/history/pm-csv-mmi3.jsonl, .gitattributes
The PM issue and history record the change and requirements. Merge-driver rules now cover PM .toon and Markdown files.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CSVImport
  participant upsertCreate
  participant PMCLI
  CSVImport->>upsertCreate: import row with closed status
  upsertCreate->>PMCLI: pm create with open status
  upsertCreate->>PMCLI: pm close --reason with provenance
  PMCLI-->>CSVImport: return imported item
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.
Title check ✅ Passed The title clearly summarizes routing terminal CSV imports through pm close for pm-cli 2026.8.3.
Description check ✅ Passed The description accurately explains the CSV import changes, close-reason handling, dependency update, and verification results.
✨ 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 route-terminal-csv-imports-through-pm-close

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.

@greptile-apps

greptile-apps Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

Greptile Summary

The PR adapts terminal CSV imports to pm-cli 2026.8.3 and completes the previously requested safeguards around failed create-then-close operations.

  • Creates closed CSV rows as open before transitioning them through pm close --reason.
  • Compensates failed post-create closes and requires positive verification that the created item is closed.
  • Adds regression coverage for missing IDs, failed compensation, and unverifiable status lookups.
  • Updates the pm-cli dependency requirement to 2026.8.3.

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported open-orphan and unknown-status compensation failures are addressed at the current head, and no blocking failure remains.

Important Files Changed

Filename Overview
index.ts Routes closed-row creation through a reasoned close and completes the prior compensation fix by hard-failing missing IDs and positively verifying rollback closure.
test/atomic.test.ts Adds focused regression coverage for successful compensation, missing create IDs, failed compensation, and failed status verification.
package.json Raises the pm-cli peer and development dependency requirements to versions that enforce close reasons.
package-lock.json Refreshes the lockfile for the pm-cli 2026.8.3 dependency update.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[CSV row with closed status] --> B[pm create with open status]
    B --> C{Create ID recovered?}
    C -- No --> D[Report hard failure for manual reconciliation]
    C -- Yes --> E[pm close with provenance reason]
    E --> F{Close succeeded?}
    F -- Yes --> G[Import succeeds]
    F -- No --> H[Compensate created item]
    H --> I{Status verified closed?}
    I -- Yes --> J[Report failed row as compensated]
    I -- No --> K[Report actionable failure with item ID]
Loading

Reviews (4): Last reviewed commit: "Require positive evidence that the orpha..." | Re-trigger Greptile

Comment thread index.ts Resolved

@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 `@package.json`:
- Around line 57-61: Update the `@unbrained/pm-cli` entry in peerDependencies to
match the tested ^2026.8.3 range used in devDependencies, unless compatibility
evidence for later major versions is added.
🪄 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: 95601fa8-c3d1-494c-8cbe-62012c632d59

📥 Commits

Reviewing files that changed from the base of the PR and between 75bcb41 and 7a55fb8.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (5)
  • .agents/pm/history/pm-csv-mmi3.jsonl
  • .agents/pm/issues/pm-csv-mmi3.toon
  • .gitattributes
  • index.ts
  • package.json

Comment thread package.json Resolved
The closed-row create-then-close sequence in upsertCreate is not atomic,
and Greptile's P1 review of PR #57 found two distinct defects in that
block (index.ts ~1759-1772):

1. Silent skip on an unrecoverable id. The follow-up close was guarded by
   `if (id && p.status === "closed")`, so when `pm create --json` exited 0
   but the id could not be parsed (empty/unparseable JSON), the close was
   silently skipped and upsertCreate returned "" as if nothing was wrong.
   A row whose source said `closed` was then reported as imported-and-fine
   while actually open. For a `closed` row an unrecoverable id is now a
   hard failure: an error is thrown naming the title, so the row is
   reported as failed rather than silently imported. The created open
   orphan cannot be compensated without its id, so the error states that
   for manual reconciliation. The flat {id,status,changed_field_count}
   envelope parse is unchanged.

2. No compensation when `pm close` fails. The item was already persisted
   as open when the throw happened; the non-atomic caller
   (processImportRow) counted the row as skipped and moved on, leaving an
   undiscoverable open orphan that a retry without --key-field would
   duplicate. The created orphan is now compensated (closed) via the same
   compensateCreate() the atomic transaction path uses, so a failed row is
   all-or-nothing and a retry reconciles cleanly. Because compensation is
   an internal rollback rather than a user closure, compensateCreate() now
   closes with --validate-close off, so a tracker under strict
   closure-validation governance cannot block the rollback and re-leak the
   orphan. If compensation also fails, the thrown error carries the
   created id and states the item was left OPEN, keeping the partial state
   actionable.

Compensation (all-or-nothing) is preferred over a pure id-carrying error
because a CSV importer retries failed rows, and without --key-field an
open orphan left behind would be duplicated on retry. The id-carrying
error is retained as the fallback for the one case compensation cannot
fix itself.

The upsertCreate docstring is rewritten to state the guarantee the code
actually provides; it no longer implies the create-then-close sequence is
atomic.

Tests (test/atomic.test.ts):
- a `closed` row whose `pm close` fails (strict closure-validation
  governance): asserts the orphan is compensated (closed) on disk and the
  error states "compensated (closed)", not merely that it throws;
- a `closed` row whose create id cannot be recovered (fake pm emits {}):
  asserts imported is NOT incremented (hard failure, not a silent open
  import) and the error names the unrecoverable id;
- a `closed` row where compensation also fails (fake pm fails all close):
  asserts the error carries the created id and states the item was left
  OPEN.

The fake-pm wrapper is tracker-scoped (marker-file gated) and transparent
to concurrent test files. No existing assertion is weakened.

Gates: typecheck, build, test (108 pass), coverage (lines 82.58 /
branches 75.86 / funcs 88.42, all above thresholds), audit:prod,
pack:dry-run, changelog:check all green.

pm item: pm-csv-mmi3
Comment thread index.ts Resolved
pm create --json emits a flat receipt with no item wrapper; only query commands
such as pm read and pm list wrap. A fallback for the wrapper is dead code no
real CLI can exercise, and keeping it invites a test that asserts the fallback
and so re-creates the failure mode it is meant to guard. The sibling pm-github
package shipped a silent production bug from trusting exactly that wrapper.

Filed upstream as unbraind/pm-cli#888 asking for the envelope contract to be
documented and an SDK parser provided so consumers stop hand-parsing it.
@unbraind

unbraind commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Pushed fixes for both round-1 findings. Summary for re-review:

  • Greptile P1 (partial-import leak) — fixed. The created orphan is now compensated via the same compensateCreate() the atomic path uses (with --validate-close off, since a rollback is not a user closure and governance must not be able to block the cleanup). The unrecoverable-id case is now a hard failure instead of a silent success. Three regression tests assert the observable outcomes, not just that something throws.
  • CodeRabbit (peer range) — declined with rationale on the thread: >= is a deliberate fleet-wide convention because the peer is the host CLI on a near-daily release cadence; the enforced floor is pm_min_version in manifest.json, which is correct here.
  • Also removed a dead parsed.item?.id fallback from the create-envelope parse. See JSON envelope shape differs between mutations and queries with no documented contract, and it silently cost us a production bug pm-cli#888.

Gates: typecheck clean, 108/108 tests, release:check green.

@greptileai
@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

@unbraind I will perform a full review of the updated pull request. I will verify the compensation path, closed-status import behavior, regression coverage, and dependency constraints.

✅ 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 20 minutes.

Review follow-up. `itemStatus()` returns undefined whenever the lookup itself
fails — non-zero exit, malformed JSON, or an absent status field — and the
verification asked "is it still open?". Undefined therefore fell through to the
success path, so a row was reported as compensated (closed) while the orphan
might still be open. A retry without --key-field would then duplicate it, which
is the exact leak the compensation exists to prevent: the guard was accepting
absence of evidence as evidence.

The check now demands a status of exactly `closed`. The error text distinguishes
all three outcomes — verified closed, still open, and could not be verified —
and every one of them carries the created id so the partial state stays
actionable.

A test drives a fake pm whose status lookup fails while both closes also fail;
reverting the check to the old form makes it fail. The existing success
assertion is tightened from "compensated (closed)" to "compensated (verified
closed)", since an unchecked claim of compensation is what allowed this.

typecheck clean, 109/109 tests pass, release:check green.
@unbraind

unbraind commented Aug 3, 2026

Copy link
Copy Markdown
Owner Author

Round-2 finding fixed (Greptile P1 — unknown status masked failed compensation). The verification now requires a status of exactly closed as positive evidence, and distinguishes verified-closed / left-open / could-not-be-verified, all carrying the created id. Regression test added and verified to fail against the previous check.

Gates: typecheck clean, 109/109 tests, release:check green.

@greptileai
@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown

@unbraind I will perform a full review of the updated pull request. I will verify the compensation-status checks, error outcomes, created-ID propagation, and regression coverage.

✅ 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 8 minutes.

@unbraind
unbraind merged commit f030771 into main Aug 3, 2026
9 checks passed
@unbraind
unbraind deleted the route-terminal-csv-imports-through-pm-close branch August 3, 2026 23:58
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