SDK-owned terminal lifecycle policy and durable project history

Merged
UN
3mo ago
+1835-950

What changed

  • moved the close transaction into the public SDK lifecycle layer and reduced the CLI close module to a compatibility shim
  • added public terminal reason and predecessor-edge policy primitives shared by CLI, SDK, and MCP routes
  • stopped update --status closed and direct terminal create from synthesizing immutable close reasons
  • preserved blocked_by dependency rows through close, reopen, and reclose while clearing only transient blocked-state scalars
  • added SDK migration documentation, a compilable SDK-only example, classified public-surface additions, regenerated error provenance, and generated changelog entries

Why

Terminal lifecycle policy lived privately in CLI command modules. That caused two concrete defects: one terminal route bypassed close-reason governance by inventing evidence, and closing a dependent deleted the ordering edges needed to understand historical project context. The SDK now owns this tranche of lifecycle semantics so every presentation surface uses one operation and consumers can inspect or select policy explicitly.

PM lineage

pm-xm0id4 and pm-2ew0w3 are complete. pm-z5pmf8 remains open and unclaimed because the create/update/delete/restore/context/planning SDK-to-CLI inversions are explicitly outside this delivered lifecycle tranche.

Verification

  • 6,610 tests; 100% statements, branches, functions, and lines
  • build, typecheck, ESLint, jscpd, CodeFactor/static-quality, 100% docstring coverage
  • SDK public-surface, package parity, entrypoint cost, CLI transport, token-budget, context/retrieval evaluation gates
  • isolated compiled CLI: predecessor survives close → reopen → reclose; reasonless update returns close_reason_required
  • fresh packed npm and Bun consumers import SDK lifecycle primitives and run contained npx/bunx binaries
  • pnpm audit --audit-level moderate: no known vulnerabilities
  • secrets scan, packed-npx smoke, and 88-command package-first dogfood
  • Sentry/telemetry: critical=0, high=0 over 14 days; best-effort telemetry gate passed

Hosted analysis and review gates are expected to complete against this immutable PR head.

Summary by Sourcery

Move terminal lifecycle semantics into an SDK-owned close operation while aligning reason governance and predecessor-edge policy across CLI, SDK, and MCP surfaces.

Bug Fixes:

  • Enforce consistent close_reason_required behavior across direct close, update --status closed, and direct terminal create so terminal transitions never synthesize immutable reasons.
  • Preserve blocked_by predecessor dependency edges across close, reopen, and reclose cycles while still clearing transient blocked state scalars.

Enhancements:

  • Expose SDK lifecycle primitives including closeItem, terminal transition policy, and reason/order governance as public API and update CLI to act as a thin compatibility shim.
  • Introduce terminal lifecycle policy primitives (reason resolution and ordering-edge handling) with a compilable SDK example and dedicated documentation.
  • Update error provenance, mocks, and tests so MCP, dedupe, and integration workflows consume the new SDK lifecycle modules consistently.

Documentation:

  • Add SDK lifecycle policy documentation and a compilable example demonstrating terminal reason and ordering-edge policy usage.

Tests:

  • Expand unit and integration coverage for terminal reason governance, predecessor preservation across lifecycle transitions, and SDK lifecycle policy primitives.

Summary by cubic

Moves terminal lifecycle to SDK-owned operations for consistent close behavior across CLI, SDK, and MCP. Fixes reason governance, preserves predecessor edges, and hardens close provenance (keeps existing reasons and normalizes ids) to keep project history intact. Addresses pm-2ew0w3 and pm-xm0id4; advances pm-z5pmf8.

  • Bug Fixes

    • Enforce close_reason_required across direct close, update --status closed, and direct terminal create; no synthesized reasons, and existing reasons are never overwritten.
    • Normalize canonical ids on input (accept ids with or without the pm- prefix) and clear only transient blocked state while preserving blocked_by edges through close → reopen → reclose.
    • Surface reason provenance via resolveTerminalReason/requireTerminalReason; update tests and runtime contracts to assert ordering survival and provenance.
  • Migration

    • SDK: use closeItem/runClose plus requireTerminalReason, applyTerminalOrderingPolicy, and TerminalTransitionPolicy from @unbrained/pm-cli/sdk.
    • CLI: when using pm create --status closed or pm update --status closed, pass a reason via --close-reason or --message; reasonless transitions now fail with close_reason_required under governance. Expect blocked_by edges to remain after close.