Multi-Branch Tracker Merge Safety
Tracked by pm-wc1r, with the integrity and concurrency fixes pm-9q2t, pm-cxyv, pm-gpo7, pm-m3nl, pm-wwfd, and pm-xdn6. Fresh-init fence ownership is tracked by pm-1w3ljt; runtime-cache index governance by pm-hous; local allocation safety by pm-khdq; fence-coverage completeness and drift detection by pm-i4fx; package-defined item coverage by pm-5rexki; non-item JSON coverage by pm-gjicmx; portable driver identity by pm-w91mvg; pending receipt validation by pm-ysqb6n; receipt classification by pm-jtwsct; direction-independent item conflict selection by pm-dlx7v7; corrected reconciliation guidance by pm-lwmstb; cross-branch id collision safety by pm-pibw; auditable merge history by pm-9j2r3b; durable conflict decisions by pm-rh98vo; continuous conformance by pm-76dnfg; workspace-wide CI enforcement by pm-pdr8t1; post-merge reconciliation by pm-mfkv92; linked-command execution trust by pm-ed28wi; this repository's own adoption by pm-iwsj.
Lossless receipt health gating is tracked by pm-baksix, and executable remediation that performs settlement is tracked by pm-r0p3at. Immutable record sealing and retained re-anchor evidence are tracked by pm-javbsq and pm-aka8m7; preferred-era receipt-summary compatibility is tracked by pm-wn3ee5. Lossless concurrent acceptance-criteria composition is tracked by pm-inn5y5. Recency-aware scalar convergence, bounded fresh-clone decision evidence, and audited pre-durable receipt dispositions are tracked by pm-7wzb6d, pm-mg13iz, and pm-cf4t42.
pm stores project context as reviewable repository files. Concurrent agents can therefore use ordinary branches and worktrees, but tracker artifacts need semantic merge behavior: raw line merging cannot preserve TOON collection counts, JSON object structure, or append-only history hash chains.
Install the repository merge contract
Fresh pm init runs this automatically when the tracker is inside a Git
worktree. It writes the shared fence and configures the current clone before
returning success. Use pm init --no-merge-fence only when another system
deliberately owns Git merge configuration.
Existing trackers and fresh clones can install or repair the contract directly:
pm merge install
git add .gitattributes
git commit -m "chore(pm): install tracker merge drivers"
pm merge install writes an idempotent, fenced .gitattributes block and repository-local git config entries. The attributes are committed; the driver commands are clone-local, so every collaborator and fresh CI clone that performs merges must run the install command.
The clone-local driver values record the absolute runtime launcher and bundled
dist/cli.js path resolved by the installing SDK. The installer preserves an
absolute PATH symlink when it resolves to the executing runtime, so a runtime
manager can retarget that launcher during upgrades without breaking every clone.
Relative PATH entries, inaccessible launchers, and different runtimes are ignored;
without a matching launcher, the installer retains the executing binary path.
Git therefore does not depend
on a bare pm command or the caller's later PATH when it merges tracker data.
The item-path placeholder is stored as bare %P: Git performs the required
shell quoting when it expands the placeholder. Receipt ingestion also removes
one legacy matching quote pair so receipts written by older clone-local drivers
remain reconcilable.
pm validate --check-storage-integrity and pm health also compare every
clone-local driver definition with the installed SDK. Exact commands remain the
fast path. A command installed by another valid @unbrained/pm-cli package is
also accepted when its Node and dist/cli.js paths exist, its manifest owns the
pm bin, and the driver arguments are semantically identical. This keeps
copied worktrees and upgraded global installs healthy without accepting an
arbitrary executable. A missing, malformed, or semantically stale definition is
reported even when the committed attribute fence is correct.
Health and validation name pm merge install as the repair command for missing
or drifted drivers. This upgrade behavior is tracked by
pm-rcjyft.
The installer publishes the shared .gitattributes fence only after the clone-local driver commands are configured. If the repository Git config is read-only or another Git process holds its lock, the command returns the stable merge_git_config_unwritable error with recovery guidance and leaves an absent fence absent. Use pm merge install --dry-run --json to inspect the contract in intentionally read-only workspaces.
Fence coverage contract
The fenced block is generated by one shared coverage contract (buildMergeAttributePatterns) covering every mergeable artifact class: tracker-wide **/*.toon and **/*.md item patterns, explicit per-type diagnostic patterns, all tracker **/*.jsonl paths under the relationship-event driver with the more-specific history/*.jsonl rule overriding history streams, root settings.json, and every nested **/*.json document. The tracker-wide item rules protect extension-defined type folders even when a receiving branch has not installed the package that registered them; explicit type rows still make active-registry drift diagnosable. The broad JSON rules automatically cover schema, managed-extension, evaluation-corpus, and future package-owned authoritative JSON without another hand-maintained allowlist. The broad JSONL rule is required because RelationshipEventStore supports package-owned custom relative paths anywhere below the tracker root. Two mechanisms keep the committed fence from drifting out of that contract:
pm schema add-type/pm schema remove-typerefresh the fence automatically when it is installed (warningmerge_fence_refreshed); repos inside git that never installed it get the actionablemerge_fence_not_installedhint instead.pm validate(thestorage_integritycheck) audits the committed fence against the active schema's type folders and reportsvalidate_merge_fence_driftwith the exact missing/stale attribute lines when they diverge — for example after editing type folders out-of-band. Rerunpm merge installand commit.gitattributesto clear it.
Preview without mutation:
pm merge install --dry-run --json
Artifact semantics
| Artifact | Driver | Merge behavior |
|---|---|---|
Item .toon / .md |
pm-item-toon / pm-item-markdown |
Three-way field merge; append-like collections use set union, updated_at uses the latest timestamp, scalar conflicts select the value from the later document update with stable value order as an equal-timestamp tie-break, and canonical serialization recomputes TOON counts. |
history/*.jsonl |
pm-history |
Preserves the common prefix and both divergent suffixes, orders deterministically, then re-anchors the resulting hash chain. |
tracker **/*.jsonl except the later history/*.jsonl override |
pm-relationship |
Covers default and package-owned custom relationship event paths, unions divergent suffixes by eventId (timestamp-ordered, ours-first on ties), and renumbers sequence consecutively so the strict-sequence store loader accepts the merged stream. |
root settings.json and nested **/*.json |
pm-json |
Recursively merges objects per key. Arrays compose when both branches preserve the base and add distinct entries, so independent extension installs and evaluation additions merge without weakening edit/removal conflict detection. |
The tests collection has an additional execution-safety rule. Its semantic
identity excludes provenance so the same command/context does not duplicate
when branches record different authorship metadata. A test definition newly
contributed by the other merge side is persisted with
provenance.source_kind=merge_union while retaining its author, creation time,
and source ref. pm test --run and pm test-all refuse that command before
spawning a process until the receiving clone acknowledges its fingerprint or
uses the two-part project-policy plus per-run override documented in
Testing. pm validate --check-command-references reports outstanding entries. This is the threat
boundary for append-like data that becomes executable after a merge; ordinary
notes, files, and other non-executable collections retain normal set-union
behavior.
Although acceptance_criteria is serialized as a semicolon-delimited scalar
for backward compatibility, the item driver treats its parsed criteria as a
collection. Independent additions compose in branch order, removals made by
either side remain removed, the field is reported in union_fields, and a
lossless merge does not create a scalar conflict decision. This gives repeated
pm update --add-ac mutations the same multi-branch preservation guarantee as
native append-like metadata without a storage migration.
When both sides change the same item scalar differently, the driver retains the
value from the document with the later metadata.updated_at timestamp. Equal
timestamps fall back to stable value ordering, so reversing Git's ours/theirs
labels still converges to the same result. Item results, driver results, and
receipts expose conflict_resolution: latest_document_update and
requested_preference_applied: false; each conflict decision adds
retained_side and resolution_basis so review automation can distinguish a
recency decision from the stable tie-break. The caller's
requested_preference remains observable but does not override item scalar
convergence. Readers continue to accept legacy preferred_side and
stable_value_order receipts and normalize the legacy preferred key. JSON
leaf conflicts retain their explicit preferred-side policy. Git keeps the item
path conflicted so a human or coordinating agent must review the discarded
value and explicitly git add the resolution.
The exact object { "pm_item_scalar_missing": true } is reserved for
JSON-stable missing-value evidence. Item merge inputs reject that object as a
present metadata value, while the hashing primitive independently separates
present and missing domains. This prevents extension data from impersonating a
deletion without wrapping every ordinary scalar in receipt output.
The driver result's guidance always points unresolved conflicts to pm merge report. When a clone-local receipt exists, guidance includes its privacy-safe receipt and item ids for exact correlation; discarded values remain confined to the local receipt and never appear in generic logs or tracker history. Tracked by pm-fbrz7p.
For item conflicts, the driver writes a clone-local receipt below the Git
directory and a durable privacy-safe sidecar below merge-receipts/ in the
tracker. The local receipt contains retained and discarded values so recovery
does not depend on a reflog. The tracked sidecar applies the versioned
bounded_non_sensitive_scalars_v1 policy. Built-in lifecycle statuses,
priority integers from 0 through 4, bounded risk/confidence/severity ordinals,
and null are stored with both their value and matching hash. All other values
remain hash-only. value_availability distinguishes bounded_inline, mixed,
and hash_only, while the clone-local receipt remains clone_local. This lets
a fresh-clone reviewer recover ordinary control-plane decisions without
publishing titles, descriptions, custom statuses, or other potentially private
content. When both copies exist the SDK deduplicates them and prefers the
locally recoverable copy:
pm merge report
pm merge report --include-reconciled
The underlying public SDK exports are mergeItemDocuments, mergeHistoryStreams, mergeRelationshipEventStreams, mergeJsonDocuments, runMergeDriver, runMergeInstall, installMergeFence, findGitWorkspaceRoot, runMergeReconcile, runMergeReceiptReport, runMergeReceiptEvidenceReport, inspectMergeReceiptEvidence, listMergeReceipts, auditMergeDriverConfiguration, refreshMergeAttributeFenceIfInstalled, buildMergeAttributePatterns, and auditMergeAttributeFence from @unbrained/pm-cli/sdk. installMergeFence accepts explicit tracker and workspace roots, so custom init hosts do not depend on process cwd or CLI globals.
listMergeReceipts is the compatibility projection for callers that only need
validated receipts. It cannot distinguish an empty evidence store from a store
whose candidates were all rejected. Gates and diagnostic integrations should
use inspectMergeReceiptEvidence, whose invalid_evidence_count preserves
that distinction without returning malformed contents.
runMergeReceiptEvidenceReport and pm merge report --json expose the same
loss-aware contract through complete, invalid_evidence_count, bounded
invalid_evidence[], invalid_evidence_truncated, and
clone_local_evidence_resolved. Each rejected candidate reports a stable
reason plus its clone_local, durable, or copy-consistency source. Schema and
identity failures additionally expose a bounded validation_error such as
required_fields, item_path, filename, or durable_decisions. A safe
receipt filename is returned as receipt_id; unsafe candidate names are
represented only by candidate_name_hash, and malformed contents are never
returned. The detail list is capped at 100 rows while the count remains exact,
so automated gates stay token-bounded. Directory traversal distinguishes a
truly absent store from a non-directory or unreadable ancestor on Windows and
POSIX instead of treating platform-specific ENOENT/ENOTDIR spellings as
equivalent. Full health also cross-checks the privacy-safe receipt summaries in
append-only history against valid pending and reconciled evidence. Missing
evidence emits merge_receipt_history_reference_missing:<n> with bounded item,
history-line, and receipt-id-or-hash coordinates. Recover an available receipt
from an authoritative clone or backup first. For an exact reference whose
history-event timestamp predates tracked durable receipts, pm merge reconcile --force can append a merge_reconcile audit event containing the original
item, line, receipt id, timestamp, and the closed
legacy_clone_local_only reason. Health accepts only that exact coordinate and
reports it through accepted_missing_merge_receipt_dispositions; it never
rewrites or deletes the original history. The audit event must also occur after
the referenced history line; an earlier event cannot pre-authorize a later
missing receipt. Missing post-durable evidence remains blocking and cannot be
dispositioned through this compatibility path. The CLI
exits nonzero when evidence is incomplete, even when the valid-receipt count is
zero. Current SDK implementations always emit the new field, while its optional
type preserves structural compatibility for existing typed adapters and test
fixtures. A structurally valid preferred-era history summary that contains its
complete privacy-safe receipt evidence is accepted in place: health counts it
as accepted_legacy_merge_receipt_references and does not demand an external
receipt file that the older writer never created. Incomplete, contradictory, or
modern summaries still fail closed. runMergeReceiptReport remains the
compatible valid-only report.
Historical receipt reader compatibility
Tracked by pm-2xtv84 and
pm-v66zir. Version-1 preferred-era and
identified requested_preference/clone_local receipts may omit base, ours,
or theirs when the original value was undefined. Readers normalize those slots to the explicit missing-value marker
in memory. They still require retained/discarded evidence and valid collections;
reading never rewrites the original file. Collection failures expose a bounded
validation_path, such as union_fields or decisions[0]. The diagnostic
category collections is not a property that should be added to a receipt.
The writer now encodes all five scalar slots itself, including undefined
retained/discarded values supplied directly through the SDK, before JSON storage.
Paired clone-local and historical hash-only copies accept the original scalar hash encoding throughout inspection and settlement. Compatibility does not apply to durable copies declaring the modern value policy. Changed values or immutable coordinates remain copy-provenance failures. Hash-only evidence does not recover the original private value.
Receipts from an aborted rebase
Tracked by pm-466m0j. New receipts
created during a rebase capture only its original commit and item blob in the
typed MergeReceiptOperation contract. After aborting, run:
pm merge reconcile --dry-run --json
pm merge reconcile --message "Record restored original rebase state" --json
Settlement requires Git to have left every active merge/rebase/cherry-pick/revert
operation, HEAD to match the original commit, and the item in HEAD, index, and
working tree to match the original blob. The item history must already be clean.
The history transaction rechecks the proof against its exact protected item
snapshot. Apply appends an explicit merge_reconcile event with
abandoned_receipts and the reason original_git_state_restored; it then marks
both copies reconciled with that settlement reason. The result counts these in
receipts.abandoned, separately from applied merge decisions. Preview performs
the same eligibility checks and leaves both history and receipts untouched.
If receipt persistence fails after the audit commits, retry reuses the identical
verified disposition and finishes the receipt writes without appending another
audit event. An unrelated earlier audit does not satisfy this check.
Old receipts without operation coordinates remain pending for ordinary review.
Tracked merge-receipts/ sidecars are durable provenance and must remain tracked.
Deleting or ignoring them would lose the evidence needed by other clones.
Cross-branch id collision safety
Tracked by pm-qx95lz.
Item ids are <prefix> plus random base36 characters. Local allocation reserves
both live documents and retained history, but cannot see independent creations
on another branch. The four-character default is a small, synchronized-workspace
setting; it is not a fleet uniqueness guarantee. ids.token_length accepts 4–12.
The allocator increases width after 32 local collisions to escape local density;
this does not measure concurrent branch activity.
For a shared base containing n reserved ids and m unsynchronized creations,
choose width L so m*(m-1)/(2*(36^L-n)) stays below the desired per-sync
collision probability. This birthday union bound assumes independent uniform
allocation and all branches know the same reserved ids. At one million reserved
ids, eight characters keep 1,000 concurrent creations below one in a million;
nine characters do so for 10,000 creations. Configure the width before branching:
pm config project set ids_token_length 8
The SDK item merge refuses an empty common ancestor with
item_identity_conflict, even for equal item content: document equality alone
cannot prove a common creation. It leaves the driver output untouched instead
of combining two identities through scalar preference. The history merge also
refuses different create events across its inputs, multiple creates in either
input, or creations on both sides when the ancestor lacks a create event even
when their bytes match, before fast-forwarding or reanchoring. A common ancestor
containing the shared create event remains valid.
storage_integrity detects multiple physical documents and a second create event
within one history stream, reporting its line in history_unparseable_streams
as an invalid lifecycle. This check also protects merges Git resolves without
invoking a content driver. pm get and SDK item lookup refuse multiple physical
matches with item_identity_ambiguous; the message lists tracker-relative paths
and SDK error context exposes a typed paths array. Format preference cannot
select a winner. Only candidate paths are probed, so lookup
does not enumerate every item in the workspace.
Preserve both branches when a collision is reported. Recover each item from its unambiguous source branch, create the second under a fresh id, and update its references before retrying the merge. Do not repair a fused stream merely to make its hashes pass: a new hash chain does not recover a lost identity.
Within one working tree, create and copy serialize on the candidate id and
recheck every built-in and extension-defined type folder before the
authoritative write. A raced collision fails with item_id_collision instead
of replacing the existing document.
Executable convergence evidence
pm-1tns7o replaces the tag-only property
with generated complete-document comparisons. Fixed branch snapshots are folded
in original, reversed, and rotated order, with both ours/theirs directions.
Cases cover conflicting scalar writes and bodies, collection additions, distinct
and equal timestamps, disjoint edits, and optional-field deletion. Independent
expected documents are constructed directly for every N-branch case to check
retained values and preserved context. A policy table
also asserts that the compatibility preferred_side option is directional.
The field-class inventories are pinned so changes require an explicit test update.
These properties exercise the declared three-way policies; they do not turn the document-level timestamp into per-field causal provenance. In particular, the N-branch conflict fixture compares competing writes to the same scalar set. Arbitrary histories, ordered executable test definitions with receiving-clone trust, and relationship-event ordering retain their separate conformance contracts.
Required post-merge gate
Reconciliation resolves receipt discovery and settlement from the requested tracker. An explicit tracker outside Git does not consume clone-local receipts from an unrelated invocation directory; see pm-q5grv5.
After every branch merge that touches .agents/pm, run:
pm merge reconcile --dry-run --json
pm merge reconcile --message "Reconcile merged tracker histories" --json
# Required only to accept a receipt that lacks qualifying exact hash proof:
pm merge reconcile --force --message "Accept reviewed merge decisions" --json
The preview reports every drifted stream and pending receipt without mutation.
It applies the same exact-snapshot proof requirement as apply: stale or
unprovable receipts appear as failed streams in both modes, tracked by
pm-ao0bu9.
Lossless receipts do not become discarded-value decisions and reconcile without
--force, but pm health reports merge_receipts_pending:<n> and remains
non-green until the apply pass settles them. pm history-repair cannot clear
that receipt finding. When a drifted item also has a pending receipt, the
history_drift remediation map prioritizes pm merge reconcile only
when canonical item path, changed-field, and merged-value hash evidence all
attribute that finding to one or more receipts loaded from the clone-local Git
evidence store or the authoritative durable receipt store. Every field declared by each receipt must match its current merged-value
hash, even when only a subset appears in the history reconciliation diff.
Disjoint valid receipts may collectively cover a multi-field reconciliation;
the audit and settlement then retain every individually proven receipt id.
Serialized source claims are ignored. Receipt readers validate the complete
bounded schema, safe identifiers, filename and item-path identity, timestamps,
and bounded decision structure before a sidecar enters health or
reconciliation. Reads use size-preflighted, no-follow regular-file descriptors;
durable decision values must conform to the declared bounded-inline policy or
remain hash-only. Qualifying durable-only receipts
are reloaded from the authoritative store and accepted only after exact canonical
item path, declared-field, and merged-value hash verification. Legacy receipts,
receipts whose declared fields disagree with their hashes, same-item tampering,
and drift on unrelated items fail closed to the normal pm history-repair
guidance. Health indexes authoritative evidence once by item and reconciliation
uses the same per-item groups with a fixed receipt-only worker pool, so committed
sidecars cannot amplify drift scans into unbounded parallel repair work.
The machine-executable remediation for a missing receipt reference names
pm merge reconcile --dry-run. It never publishes --force as an executable
hint: the human-readable summary explains that exact eligible pre-durable
coordinates may be dispositioned by a separately reviewed force pass. Ordinary
pending lossless receipts retain their unforced apply remediation.
Apply-mode reconciliation repeats the same proof against the exact
item snapshot used by the audited history rewrite. The audit event and
settlement include only the individually proven receipt id, so one valid receipt
cannot authorize an untrusted same-item sibling. Failed or unproven receipts
remain pending unless the coordinator explicitly reviews and supplies --force.
Snapshot verification accepts both the current presence-domain digest and the
legacy version-1 scalar digest, so already-written receipts remain repairable;
new receipts always use the collision-free presence-domain scheme.
Receipts with discarded scalar values retain the distinct
merge_decisions_unreviewed:<n> finding. Matching authoritative hash proof
allows those receipts to settle without --force; without qualifying proof,
the apply pass refuses them until the coordinator explicitly supplies --force
after review. The history_drift_merge_receipt remediation may therefore emit
an unforced apply command that clears merge_decisions_unreviewed when the
receipt proves the exact current merged values. This prevents routine repair
from hiding unfinished reconciliation while avoiding redundant force for an
already-proven canonical snapshot.
The same --force boundary also handles an unrecoverable pre-durable
clone-local-only history reference. Preview and apply results expose
missing_history_references_before, legacy_disposition_eligible,
legacy_disposition_recorded, and missing_history_references_after. When
coordinates exceed the bounded health response, explicit result guidance
requires repeated dry-run and reviewed force passes until health no longer
reports truncated coordinate details and the remaining count is zero. An apply
result cannot return ok: true while any missing reference or truncated
evidence remains.
It exits nonzero while either merge-critical validation check is non-green, so
CI and explicit post-merge hooks cannot approve unresolved receipts or drift.
The apply pass uses the audited history rewrite boundary to append a
merge_reconcile event whose patch reproduces the merged item exactly. The
event includes privacy-safe receipt provenance; a clean receipt-bearing stream
gets a no-op merge event so the merge remains addressable even when replay
already matches. The command immediately validates history drift plus storage
integrity and exits nonzero when an invariant remains red. Default Git hooks
remain unchanged: repositories that want automatic enforcement must opt in by
invoking this command from their own post-merge hook.
The default validation surface includes storage_integrity. It fails on unreadable item documents, history conflict markers, malformed history tails, live items whose latest history operation is delete (a delete/modify resurrection candidate), and unparseable settings/schema files. This prevents the ordinary tolerant read path from turning corruption into a green gate.
A history union can be structurally valid while replayed history and the chosen item document describe different effective state. In that case inspect both authors' events before reconciliation, then run the audited repair:
pm history <item-id> --verify --strict-exit
pm history-repair <item-id> --dry-run
pm history-repair <item-id>
pm merge reconcile --dry-run --json
history-repair records the reconciliation patch and classifies its changed fields against the final item. Append-only collection unions and deterministic reordering are reported as preserved context without a data-loss warning. Fields whose replayed values are actually removed or replaced remain loud with discarded event authors/operations and recovery guidance. Re-apply any intended losing mutation as a normal pm update so it remains explicit and auditable.
History events now declare an item-hash epoch. Epochs are immutable writer
contracts, not aliases for the current item serializer: epoch 1 sorts linked
tests; epoch 2 preserves their insertion order and has both an earlier
field-frozen form and a later expanded form because the writer surface grew
before the marker advanced; epoch 3 is the current form. Verification accepts
both recorded epoch-2 forms without allowing one entry to mix them, and repair
preserves the form evidenced by each event. Unversioned streams are verified
against the supported legacy canonicalizations. Current-document comparison
selects the resolved epoch candidate that matches the chain head. A union merge
may consume suffixes written by both epoch-2 forms, but its synthesized output
uses one form for the complete re-anchored stream so every stored after_hash
is exactly the next stored before_hash. An unknown explicit epoch is
reported as unsupported_item_hash_version and repair refuses to guess. This
keeps version incompatibility distinct from item corruption and is tracked by
pm-2htk4p and
pm-2qahia.
New history events also carry record_hash_version and record_hash. Unlike
the item anchors, the record hash covers the complete immutable event: author
and agent provenance, timestamp, operation, message, context, patch, item-hash
epoch, and before/after anchors. A record that carries only one envelope field,
uses an unsupported record epoch, or changes after sealing fails verification.
Entries created before this envelope remain readable as explicit
item_state_only coverage; they are not presented as record-authenticated.
Every maintenance rewrite verifies a present record hash before changing the
stream and reseals the output. When anchors or patch representation change, the
entry's append-only reanchor_evidence retains the prior anchors, item epoch,
patch digest, and prior record envelope. Lenient repair additionally retains
the complete replaced patch operations. verifyHistoryRewriteEvidence()
reconstructs every available prior envelope and rejects a record that was
modified and merely resealed after maintenance. Redaction never copies sensitive patch
values into new evidence: it keeps the digest and drops a retained patch if the
patch itself matched a redaction rule. Compaction baselines retain the pruned
entry count and ordered stream digest plus an explicit
individual_pruned_entries_require_pre_compaction_stream limitation. A holder
of the pre-compaction stream can therefore validate the checkpoint, while the
post-compaction stream does not falsely claim that it can reconstruct content
that was intentionally pruned.
Delete versus modify policy
A delete on one branch and an edit on another is not safely resolvable by a generic file driver. The merged workspace must not silently resurrect the item. storage_integrity reports the live document plus delete-terminated history as a hard finding. The coordinator chooses one policy explicitly:
- Honor deletion: remove the live item document, keep its append-only history, and commit the resolution.
- Honor modification: restore/recreate through pm so the history contains an explicit post-delete operation and rationale.
Never remove conflict markers manually while leaving the authoritative item and history semantically inconsistent.
Runtime receipts and retention
transactions/ and checkpoints/ are per-branch crash-recovery state, not shared project context. pm init places both below the resolved tracker root in the managed .gitignore block, including custom --pm-path roots.
Ignore rules do not remove files that were already committed. pm health
(integrity) and pm validate (storage_integrity) therefore scan Git's
index for tracked files below runtime/, search/, locks/,
transactions/, and checkpoints/. A finding includes repository-relative
paths plus an exact git rm --cached -r -- ... command. That command removes
only the indexed copies; local caches and recovery receipts remain on disk
under the managed ignore fence. The intentional
search/eval-queries.json relevance-evaluation corpus remains tracked and is
excluded from this cache diagnostic.
Terminal SDK transaction journals use the same checkpoints.retention_days policy as rollback checkpoints. The default GC sweep includes both receipt classes:
pm gc --dry-run
pm gc --scope transactions --dry-run
pm gc --scope transactions
Only aged committed or compensated journals are removed. applying, compensating, unreadable, and unparseable journals are always retained so cleanup cannot destroy live recovery state.
Temporary-clone acceptance test
Before releasing merge-contract changes, validate the packed package in a temporary Git repository: initialize pm, install the merge contract, create one base item, branch twice, append disjoint metadata/history on both branches, merge, and run the strict validation commands above. Include a same-key conflict case and prove Git leaves it unresolved while the output remains parseable.
The packed first-run matrix exercises the installed README workflow, a real comment/history merge, and MCP on supported platforms.