Keep real closure dates and transaction identity when re-importing closed issues
Summary
Repeating a gated import is already byte-identical since v2026.10.30 (#198). This PR fixes the two defects that remained when an already-linked closed issue is imported again:
- Invented closure instants. The atomic plan forced a native re-close on items that were already closed. The SDK close router then stamped the import time as
closed_atbefore the fidelity finalizer restored the real date. Source lifecycle is now applied once, in the fenced finalizer, and unchanged values are skipped (index.tsbuildAtomicImportMutations,import-fidelity.tspersistImportFidelity). - Lost transaction identity on replay. After an interrupted governed update, the SDK's content update could omit the message. Context selection then treated that entry as an external edit.
selectImportContextIndexnow also recognizes the governeditem_transaction_stepcontext, and only entries that actually change status count as a lifecycle generation.
Details and measurements: docs/repeat-import-noop.md
Evidence
test/repeat-import-noop.test.ts: 9/9 pass. With the production change reverted, 0/9 pass. The interrupted-replay case fails with only the context recognition reverted.test/import-fidelity.test.tsstill imports half the recorded fixtures into an empty tracker. A new check asserts that a fresh atomic mirror never writes an import-timeclosed_atinto history. That check fails on the previous code for pm-context-81 and pm-csv-134. Full file: 54/54.- Repeat import against real trackers (pm-slack, pm-ado, pm-review): 0 items updated on the second run, empty
git diff, no untracked files. - The two closeout decision-matrix failures seen during the disk-full incident pass 2/2 on this branch and 2/2 on
9586643; they did not reproduce once space was recovered. release:check: 11/11 gates, 1,609/1,609 tests, coverage 100% on all 28 modules. Changelog check passes.
The local coverage run needed a percentage heap override, because c8 aggregation for the 1,609-case suite exceeded the 12 GiB cap on this host. Thresholds, inventory and the checked-in heap default are unchanged. CI on this PR shows whether the default cap holds on hosted runners.
pm items
- pm-github-repeat-noop, owner of #175
- pm-github-vhi6
- Follow-up: pm-github-ldc1. The plain (non-atomic) fresh import still records a transient import-time
closed_at. This predates the PR.
Closes #175
Summary by Sourcery
Make repeated gated imports of closed GitHub issues fully idempotent while preserving source lifecycle dates and transaction identity.
Bug Fixes:
- Preserve source closure dates when re-importing already-closed issues instead of recording transient import-time closures.
- Restore governed transaction identity during interrupted import replay and avoid treating unchanged lifecycle events as new generations.
- Ensure repeated gated imports produce no item or history changes after the initial synchronization.
Enhancements:
- Apply source lifecycle fidelity through a single finalization step while skipping unchanged metadata values.
Documentation:
- Add evidence and regression documentation for byte-identical repeat imports and closure-date fidelity.
Tests:
- Add regression coverage for repeated imports, source lifecycle transitions, interrupted transaction recovery, and immutable closure history.
Summary by cubic
Fixes re-importing an already-linked closed issue so repeated gated imports stay byte-identical and history keeps the real closure date.
- Stops the atomic plan from forcing a native re-close on already-closed items, which stamped the import time as
closed_atbefore the finalizer restored the source value. Source lifecycle is now applied once in the fenced finalizer, and unchanged values are skipped. - Restores transaction identity on replay after an interrupted governed update:
selectImportContextIndexnow recognizes theitem_transaction_stepcontext, and only entries that actually change status count as a lifecycle generation.
Evidence
test/repeat-import-noop.test.ts: 9/9 pass; 0/9 with the production change reverted.test/import-fidelity.test.ts: new check requires the real GitHubclosed_atto appear in a fresh atomic mirror's history and rejects any import-timeclosed_at; 54/54 pass.- Repeat imports against real trackers (pm-slack, pm-ado, pm-review) update 0 items and produce empty
git diff. release:check: 11/11 gates, 1,609/1,609 tests, 100% coverage.
Migration
- None. Known follow-up: the plain (non-atomic) fresh import path still records a transient import-time
closed_at; this predates the PR and is tracked aspm-github-ldc1.