Skip to content

Producer suite: after-outcome hold, grow-archive lookup and halted event across restart #384

Closed

Description

@lbacik

This was generated by AI during triage.

Split out of #383. Three of the ten producer failures there have their own root causes, and none of them depend on the handoff now design question that #383 keeps.

Reproduced on main at f9855e8 (after #382) with the --require-all variant in Docker. The result matches #383: 31 passed and 10 failed.

Scenario Failure
sealed-after-outcome archive must stay unsealed while held
export-torn-read harness grow-archive failed: ... no unsealed archive to grow
halt-records-last-halt a halted event must be recorded

See the agent brief below.

Activity

  1. lbacik commented on Nov 10, 2026

    @lbacik
    OwnerAuthor

    This was generated by AI during triage.

    Agent Brief

    Category: bug
    Summary: Make sealed-after-outcome, export-torn-read and halt-records-last-halt pass in the producer suite. There are three independent defects: two in the conformance harness and one in the engine.

    Current behavior (verified by reading the code and reproducing the suite run):

    1. finish-attempt drops the after-outcome hold. The harness verb finish-attempt deletes the hold flag (hold.json) unconditionally, so that a blocked executor can run on. The GatedPublisher gate is where an after-outcome hold is supposed to pause finalization, and it never sees the hold. So the attempt finalizes and seals straight away, and attempts show reports sealed: true while the suite still expects the hold to apply. finish-attempt then waits for the active attempt to clear, which could never happen under a live after-outcome hold.
    2. grow-archive cannot find the attempt's archive. The contract attempt_id is the attempt's raw started_at timestamp, which contains :. AttemptArchive names its directory with : replaced by -. The harness's archive-candidate lookup filters run directories by attempt_id in run_dir.name, which never matches. So grow-archive answers failed: no unsealed archive to grow even while the attempt is active.
    3. The halted Contract Event does not survive the restart. On halt, the harness daemon records the halt in HaltStore and emits the native halted event before it exits. The Contract Event buffer is in memory only and starts empty after a restart. So after start-container + release, instance.last_halt is correct, but recent_events has no halted entry.

    Desired behavior:

    1. Under hold {point: after-outcome}, finish-attempt records the scripted outcome and leaves the after-outcome hold in place. It returns once the outcome is recorded and finalization is paused at the gate (attempt still unsealed, outcome present). The next release lets finalization continue, seal the archive, and emit sealed for that attempt. Holds that block the executor (after-claim, native-phase) are still cleared by finish-attempt, as today.
    2. grow-archive with a contract attempt_id (or null, meaning the active attempt) finds that attempt's unsealed archive files, whatever the directory-name sanitising. The second export then shows a torn read.
    3. After an engine-decided halt and the next start, recent_events holds exactly one halted Contract Event whose reason matches the recorded last_halt. Seeding it at startup from the durable halt record is the expected approach, the same pattern Seed last_finished post-provenance and keep restart contract events valid #382 used to seed last_finished post-provenance. A start with no recorded halt must not invent one, and the event must stay a valid Contract Event (the suite's contract-events scenario checks this).

    Key interfaces:

    • Harness verb finish-attempt: how it handles holds, and when it decides it has finished (outcome recorded vs. attempt cleared) when an after-outcome hold is armed.
    • Harness verb grow-archive: how it matches a contract attempt_id to an AttemptArchive directory. Preferably derive it from the same sanitising rule AttemptArchive uses, not a second copy of it.
    • HaltStore / instance.last_halt, and the in-memory Contract Event buffer (CONTRACT_EVENT_NAMES includes halted with a reason extra).

    Acceptance criteria:

    • The producer suite reports PASS for sealed-after-outcome, export-torn-read and halt-records-last-halt, run in Docker as the CI producer job does, in both variants (--require-all with full capabilities, and --capabilities "").
    • Every scenario that passes today still passes, contract-events and publication-pending-recovery included.
    • Unit or integration tests cover: finish-attempt under an after-outcome hold leaves the attempt unsealed with its outcome recorded; grow-archive resolving a colon-bearing attempt_id; a restart after a recorded halt shows one halted event in recent_events, and a restart without one shows none.
    • uv run pytest is green.

    Out of scope:

  2. added
    bugSomething isn't working
    ready-for-agentFully specified implementation issue ready for an unattended agent
    on Nov 10, 2026
  3. added a commit that references this issue on Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingready-for-agentFully specified implementation issue ready for an unattended agent

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions