Skip to content

create: no way to create an item in a terminal state, forcing every importer into a two-command create-then-close dance #887

Closed

Description

@unbraind

Summary

Since 2026.8.3, governance.require_close_reason is enforced on every programmatic transition into closed — both pm create --status closed and pm update <id> --status closed fail with close_reason_required. That is a good change: it stops tools inventing immutable closure evidence.

But pm create has no way to supply a reason, so there is no single command that can create an already-closed item. pm close accepts --close-reason, --completed-at, --resolution, --expected-result, --actual-result; pm create accepts none of them.

Every importer therefore has to implement the same dance:

  1. pm create --status open ... --json
  2. parse the id back out of the envelope
  3. pm close <id> --reason "..." [--completed-at ...]

Why this is worth fixing

Absorbing 2026.8.3 across our fleet required implementing that identical workaround three separate times — in a GitHub importer, a CSV importer, and a Beads importer. It is not a niche path: importing history from another tracker is inherently full of already-closed records.

The cost is not just duplication:

  • It is 3x the commands and round-trips for the common bulk case. A 500-row CSV of closed records goes from 500 to 1500 subprocess invocations.
  • It forces every importer to parse the create envelope just to recover the id — an extra coupling to output shape that has nothing to do with the user intent. We shipped a real production bug through exactly this path: our parser read a {item:{id}} wrapper that the CLI has never emitted, create "succeeded", the follow-up close silently never ran, and every imported closed record landed open while the import reported success. A single-command form removes that failure mode entirely.
  • It is not atomic. A crash between step 1 and step 3 leaves an item in the wrong state with no marker saying so. For an importer that is a correctness problem, not just an ergonomic one.

Proposed change

Let pm create accept the closure metadata it already understands on close, and require it when --status closed is requested under the governance policy:

pm create --title "Legacy ticket" --type Task \
  --status closed \
  --close-reason "Imported from Beads record bd-1421 (source status: done)" \
  --completed-at 2026-03-04T11:02:00Z

Semantics that would preserve the policy intent:

  • --status closed without a reason keeps failing exactly as it does today — the governance guarantee is unchanged.
  • --status closed with --close-reason succeeds and records the reason as author-supplied closure evidence, identically to pm close.
  • The same treatment for the sibling closure fields already on close (--completed-at, --resolution, --expected-result/--actual-result), so a single create can carry the source record's real provenance.

This keeps the "tools must not invent closure evidence" rule fully intact — it only removes the requirement that the evidence arrive in a second command.

Alternative

If folding the flags into create is undesirable, an --emit-id-style guarantee plus a documented, stable create envelope would at least remove the parsing hazard — but it would not fix the atomicity or the round-trip count.

Context

Found while upgrading a 20-package extension fleet from 2026.7.29 to 2026.8.3. Happy to send a PR if the shape above is acceptable.

Activity

  1. coderabbitai commented on Aug 3, 2026

    @coderabbitai
    🔗 Related PRs

    #146 - fix: agent never-block reliability, MCP/version coherence, and CI/docs hardening [closed]
    #230 - Single-call mutation ergonomics: pm create/update --body-file, pm close -m/-r/-d, --resolution as close reason [closed]
    #429 - Accept close message fallback and reduce compatibility complexity [merged]
    #712 - SDK context integrity: atomic Plans, batch similarity, sparse settings, and tombstones [merged]
    #846 - SDK-owned terminal lifecycle policy and durable project history [merged]


    📝 Issue Planner

    Check the box below or use the @coderabbitai plan command to generate an implementation plan and prompts that you can use with your favorite coding assistant.

    • Create Plan

    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  2. unbraind commented on Aug 4, 2026

    @unbraind
    OwnerAuthor

    Tracked in pm-ykdt4m.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions