Recurrence and Executable Recovery Contracts
Tracked by pm-83cz0o, pm-qljv, pm-surv, pm-h8tpeh, and pm-f05lsg.
Agent Quick Context
- Reuse an active matching item; reopen a terminal matching item.
pm item reopenis the noun-first recurrence command. It never creates a second item and never rewrites the earlier close event.- Recovery guidance is capability-aware: it names an executable command on the current surface, or installs the package that owns the command first.
- Generated
AGENTS.mdguidance detects the target repository's test command. When no executable test contract exists, it prints an explicit placeholder instead of naming a pm-cli repository script that the target does not have.
Reopen Terminal Work
pm item reopen pm-a1b2 "The production failure recurred after deployment"
pm item reopen pm-a1b2 "The customer reproduced the issue" --status in_progress
The command accepts only the workspace open or in-progress status. While
holding the item lock it verifies that the current status is terminal, records
one reopen history event with structured recurrence context, and delegates the
state change to the normal update pipeline. Active metadata drops stale
closed_at, completed_at, close_reason, resolution, expected_result,
actual_result, and fixed_version values. Earlier history retains the exact
closure values.
Compact output keeps the recurrence receipt because the prior and next status,
reason, and terminal evidence are the proof that this was a recurrence rather
than a generic edit. An already-active item fails with
item_already_active and points to pm get <id> --full; continue that item with
pm update instead of manufacturing another recurrence event.
SDK and MCP
import { PmClient, reopen } from "@unbrained/pm-cli/sdk";
const pm = new PmClient({ pmRoot: "/workspace/.agents/pm" });
const viaClient = await pm.reopen(
"pm-a1b2",
"The production failure recurred",
{ status: "in_progress" },
);
const viaFunction = await reopen(
"pm-c3d4",
"The customer reproduced the issue",
{},
{ pmRoot: "/workspace/.agents/pm" },
);
console.log(viaClient.recurrence.previous_terminal);
console.log(viaFunction.recurrence.from_status);
Generic action hosts use item-reopen with required id and reason fields.
The action participates in the generated action schema, SDK dispatch coverage,
CLI/SDK parameter parity, command grammar, and MCP pm_run surface.
Tracker Preflight Recovery
Every SDK command that requires project state now enters through one shared filesystem preflight instead of maintaining a command-local settings check. The public aggregate SDK exposes the same primitive for packages and embedded hosts:
import {
assertInitializedTracker,
assertReadableTrackerRoot,
} from "@unbrained/pm-cli/sdk";
await assertReadableTrackerRoot(candidateRoot); // an empty directory is valid
await assertInitializedTracker(projectRoot); // settings.json is required
The refusal contract distinguishes four states before any command-specific read or mutation begins:
| Selected root state | Code | Exit | Recovery |
|---|---|---|---|
| Path does not exist | tracker_root_missing |
3 | Tokenized, non-interactive pm --pm-path <path> init --defaults --agent-guidance skip |
Directory exists but settings.json does not |
tracker_not_initialized |
3 | The same initialization retry |
| Path or ancestor is a regular file | tracker_root_not_directory |
2 | Select a directory; never suggest pm init against the file |
| Directory cannot be read or searched | tracker_root_unreadable |
1 | Repair permissions, then retry the same selected root |
listTrackerPreflightRecoveryContracts publishes this four-state corpus from
@unbrained/pm-cli/sdk/contracts.
scoreTrackerPreflightRecoveryClosure verifies code, exit class, recovery kind,
and executable retry evidence. The repository recovery gate runs these probes
beside the 18 closed-domain probes, so its ratcheted corpus is 22 and a removed
state, failed retry, or unsafe file-path initialization suggestion blocks the
release. The surface-replication gate separately requires at least 50 shared
preflight invocations across the 45 migrated SDK modules and forbids the former
inline error body from returning.
Duplicate Intake
Create and copy similarity governance inspect all lifecycle statuses. The
strongest active match returns a pm get <id> --full reuse path. The strongest
terminal match returns:
pm item reopen <id> "<recurrence reason>"
Strict duplicate refusal exposes the same command and tokenized arguments in
its structured recovery envelope. Advisory mode adds a compact
likely_duplicate_recovery:<reuse|reopen>:<id> warning so agents can select the
correct lifecycle action without reparsing prose.
Capability-Aware Reindex Recovery
When semantic search detects stale vectors, it inspects the active extension
command registry. If reindex is active, the direct recovery is:
pm reindex --mode hybrid
If the command is absent, recovery is a two-step executable sequence:
pm package install search-advanced --project
pm reindex --mode hybrid
The human warning and structured vector_index_recovery tokens come from the
same resolution. This prevents a base installation from suggesting an
unavailable command.
Target-Aware Generated Test Guidance
pm init --agent-guidance add resolves the linked-test command in this order:
node scripts/run-tests.mjs testwhen that repository script exists.- The declared package manager's
testscript (pnpm test,bun run test,yarn test, ornpm test). <your project test command>when the target does not declare an executable test contract.
The managed guidance block is versioned, so a later init can replace an older pm-managed block without changing surrounding repository instructions.