Hardening + observability pass on the seed flow.
- Plan-hash drift gate. dry_run writes a canonical plan.json and a
SHA-256 over the load order, per-object break-edge decisions, and
upsert-key strategy; run recomputes and refuses on mismatch. Closes
the "plan reviewed != plan executed" window.
- Retry/backoff wrapper (src/salesforce-fetch.ts) on every Salesforce
REST call — 429/5xx + REQUEST_LIMIT_EXCEEDED / UNABLE_TO_LOCK_ROW /
SERVER_UNAVAILABLE / REQUEST_RUNNING_TOO_LONG, 5 attempts with ±25%
jitter. Previously a single spurious 503 aborted whole runs.
- Defaulted-owner visibility. Dry-run report surfaces per-object counts
of rows whose OwnerId/User/Group/Queue references will default to
the seeding user at insert.
- Phase-2 cycle backfill log split — unresolvedTarget (scope gap) vs
unresolvedSelf (phase-1 failed) are now distinct counters instead
of silently bucketed.
- Rename looksEncrypted → looksPlaintext with inverted semantics so
the call site reads as a positive predicate.
- Docs: README "What's new in 0.2.1", ARCHITECTURE adds
Ownership/User/Group/Queue references section.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>