Skip to content

T052 — persist planned-writeback.json sidecar for natural transient-exhaust resume #12

@brettheap

Description

@brettheap

Deferred follow-up from Slice 2 (PR #3, merged in commit c328be5). Task T052 in specs/002-mock-call-real-crm/tasks.md:198.

Problem

The Slice 2 resume coordinator (src/opencloser/slice2/resume.py) replays missing CRM writes from a persisted writeback.json. Today the orchestrator writes that file only after every emit_* operation succeeds, so a real TransientDataverseError retry-exhaust leaves no replay artifact and resume_session raises ResumeError("writeback.json missing").

This means the natural resume_needed recovery path (which Slice 2 ships and documents — FR-023) cannot actually be exercised end-to-end without an orchestrator-side change. The resume coordinator's design is correct; the gap is purely in artifact-write ordering.

Fix (architectural)

Persist a planned-writeback.json sidecar artifact before the first emit_* attempt, mirroring the planned payloads the adapter would issue (the adapter already captures these for dry-run). The post-success writeback.json can stay as the authoritative final artifact.

Likely shape:

  1. New writer function (e.g. write_planned_writeback_artifact) called at orchestrator start, right after the readiness gate passes.
  2. slice2/resume.py loads planned-writeback.json (or writeback.json, in that order) instead of strictly requiring writeback.json.
  3. writeback.json continues to be written on full success — keeps the "the final write succeeded" signal.

Test impact

The integration test tests/integration/test_us4_idempotency.py::test_us4_natural_transient_exhaust_yields_resume_needed currently asserts BOTH the RESUME_NEEDED exit and the ResumeError("writeback.json missing") raise — that second assertion will need updating when this lands, to assert successful resume replay instead. The test docstring explicitly flags this as a follow-up dependency.

Origin

Surfaced by the 2026-05-24 multi-agent swarm code review:

  • Resume-coordinator audit (MEDIUM): "the resume path cannot recover natural transient-exhaust failures end-to-end".
  • Test-pyramid audit (HIGH): "the runner's TransientDataverseError → RESUME_NEEDED branch is verified by inspection only via synthesized state".

Tracked in:

  • src/opencloser/slice2/resume.py:26-38 (KNOWN LIMITATION docstring)
  • specs/002-mock-call-real-crm/tasks.md:198 (DEFERRED entry)
  • specs/002-mock-call-real-crm/contracts/cli-slice2.md (T052 paragraph)

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions