Skip to content

fix: restore automatic continuation for new Pi worker results - #3

Merged
cdonovan-abtex merged 2 commits into
mainfrom
fm/firstmate-result-continuation-r1
Sep 16, 2026
Merged

cdonovan-abtex merged 2 commits into
mainfrom
fm/firstmate-result-continuation-r1

Conversation

@cdonovan-abtex

Copy link
Copy Markdown
Owner

Intent

The user reports that already-authorized work repeatedly stops after workers finish, until he asks Firstmate to continue. His current instructions are:

What is continued work waiting on? And why am I constantly having to trigger additional work?
What has changed where I am having to steer you to move things forward?
This is at least the tenth time you have apologized for the same thing, I don't want more apologies I just need the work to continue. Whatever that setting or trigger is needs to be enabled.

Restore the existing automatic completion-to-continuation behavior so a completed worker result reaches Firstmate for its required next action without another user message. Prefer the supported existing setting or trigger where that suffices; otherwise repair the demonstrated failure in the existing mechanism. This is not permission to bypass human-only approvals or silently perform destructive, access, production or merge actions.

What Changed

  • Replace queued Pi nextTurn snapshots with fresh prompt-boundary injection so new worker results restart bounded automatic processing without another user message.
  • Add regression coverage and an opt-in Pi/Herdr live test for automatic continuation and exact outcome acknowledgement; register the live test with the runner.
  • Consolidate retry-policy documentation and record continuation verification evidence.

Risk Assessment

✅ Low: The focused fix restores continuation for newly completed results while preserving bounded retries, ownership checks, exact acknowledgements, and approval boundaries.

Testing

The focused behavioral suite and all eight real-Pi scenarios passed; the baseline reproduced the stall. One unrelated renderer comparison skipped on the installed Pi version. Probe setup issues were corrected, native HTML/screenshot/transcript evidence was captured, and temporary files were removed.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
A new completed outcome restarts automatic processing after older ignored results exhaust their retries, performs its authorized isolated action, and acknowledges it without user input. ✅ pass live Target live scenario transcript; Pre-fix failure reproduction; persisted Pi transcript confirms action and acknowledgement precede the first user message.
An unchanged ignored result stops after two automatic attempts instead of creating an endless loop. ✅ pass live Target live scenario transcript: exactly two autonomous presentations, with processed marker remaining zero.
A routine healthy-wait outcome leaves Firstmate idle. ✅ pass live Target live scenario transcript: healthy external wait opened no additional main turn.
Invalid or duplicate acknowledgements cannot discard pending work or repeat an already completed action. ✅ pass live Target live scenario transcript: acknowledgement through 999 and duplicate acknowledgement through 3 were refused; the action receipt remained unique.
After automatic retries are exhausted, the next user prompt receives and processes the pending result. ✅ pass live Real Pi session: deferred-current-prompt receipt and acknowledgement through sequence 5.
A completion arriving during active tool execution continues automatically afterward. ✅ pass live Real Pi session: probe_wait completed, followed by busy-result receipt and acknowledgement through sequence 6.
Replacing the Pi session automatically re-presents pending results with a fresh bounded retry budget; subsequent processing records one action and exact acknowledgement. ✅ pass live Real Pi session replacement recovery; Target live scenario transcript.
A completion proposing deployment, access changes, deletion, or merge surfaces the missing human approval and leaves those actions held. ✅ pass live Automatic continuation and preserved approval boundary screenshot; recorded approval request with no additional action receipt.

Automatic continuation and preserved approval boundary

Evidence: Real Pi session: continuation, deferred processing, and busy-main delivery

Source: Real Pi session: continuation, deferred processing, and busy-main delivery

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Session Export</title>
  <style>
    :root {
      --accent: #8abeb7;
      --border: #5f87ff;
      --borderAccent: #00d7ff;
      --borderMuted: #505050;
      --success: #b5bd68;
      --error: #cc6666;
      --warning: #ffff00;
      --muted: #808080;
      --dim: #666666;
      --text: #d4d4d4;
      --thinkingText: #808080;
      --selectedBg: #3a3a4a;
      --userMessageBg: #343541;
      --userMessageText: #d4d4d4;
      --customMessageBg: #2d2838;
      --customMessageText: #d4d4d4;
      --customMessageLabel: #9575cd;
      --toolPendingBg: #282832;
      --toolSuccessBg: #283228;
      --toolErrorBg: #3c2828;
      --toolTitle: #d4d4d4;
      --toolOutput: #808080;
      --mdHeading: #f0c674;
      --mdLink: #81a2be;
      --mdLinkUrl: #666666;
      --mdCode: #8abeb7;
      --mdCodeBlock: #b5bd68;
      --mdCodeBlockBorder: #808080;
      --mdQuote: #808080;
      --mdQuoteBorder: #808080;
      --mdHr: #808080;
      --mdListBullet: #8abeb7;
      --toolDiffAdded: #b5bd68;
      --toolDiffRemoved: #cc6666;
      --toolDiffContext: #808080;
      --syntaxComment: #6A9955;
      --syntaxKeyword: #569CD6;
      --syntaxFunction: #DCDCAA;
      --syntaxVariable: #9CDCFE;
      --syntaxString: #CE9178;
      --syntaxNumber: #B5CEA8;
      --syntaxType: #4EC9B0;
      --syntaxOperator: #D4D4D4;
      --syntaxPunctuation: #D4D4D4;
      --thinkingOff: #505050;
      --thinkingMinimal: #6e6e6e;
      --thinkingLow: #5f87af;
      --thinkingMedium: #81a2be;
      --thinkingHigh: #b294bb;
      --thinkingXhigh: #d183e8;
      --thinkingMax: #ff5fff;
      --bashMode: #b5bd68;
      --exportPageBg: #18181e;
      --exportCardBg: #1e1e24;
      --exportInfoBg: #3c3728;
      --body-bg: #18181e;
      --container-bg: #1e1e24;
      --info-bg: #3c3728;
    }

    * { margin: 0; padding: 0; box-sizing: border-box; }

    :root {
      --line-height: 18px; /* 12px font * 1.5 */
      --sidebar-width: 400px;
      --sidebar-min-width: 240px;
      --sidebar-max-width: 840px;
      --sidebar-resizer-width: 6px;
    }

    body {
      font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
      font-size: 12px;
      line-height: var(--line-height);
      color: var(--text);
      background: var(--body-bg);
    }

    body.sidebar-resizing {
      cursor: col-resize;
      user-select: none;
    }

    #app {
      display: flex;
      min-height: 100vh;
    }

    /* Sidebar */
    #sidebar {
      width: var(--sidebar-width);
      min-width: var(--sidebar-width);
      max-width: var(--sidebar-width);
      background: var(--container-bg);
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      position: sticky;
      top: 0;
      height: 100vh;
      border-right: 1px solid var(--dim);
    }

    #sidebar-resizer {
      width: var(--sidebar-resizer-width);
      flex-shrink: 0;
      position: sticky;
      top: 0;
      height: 100vh;
      cursor: col-resize;
      touch-action: none;
      background: transparent;
      border-right: 1px solid transparent;
    }

    #sidebar-resizer:hover,
    body.sidebar-resizing #sidebar-resizer {
      background: var(--selectedBg);
      border-right-color: var(--dim);
    }

    .sidebar-header {
      padding: 8px 12px;
      flex-shrink: 0;
    }

    .sidebar-controls {
      padding: 8px 8px 4px 8px;
    }

    .sidebar-search {
      width: 100%;
      box-sizing: border-box;
      padding: 4px 8px;
      font-size: 11px;
      font-family: inherit;
      background: var(--body-bg);
      color: var(--text);
      border: 1px solid var(--dim);
      border-radius: 3px;
    }

    .sidebar-filters {
      display: flex;
      padding: 4px 8px 8px 8px;
      gap: 4px;
      align-items: center;
      flex-wrap: wrap;
    }

    .sidebar-search:focus {
      outline: none;
      border-color: var(--accent);
    }

    .sidebar-search::placeholder {
      color: var(--muted);
    }

    .filter-btn {
      padding: 3px 8px;
      font-size: 10px;
      font-family: inherit;
      background: transparent;
      color: var(--muted);
      border: 1px solid var(--dim);
      border-radius: 3px;
      cursor: pointer;
    }

    .filter-btn:hover {
      color: var(--text);
      border-color: var(--text);
    }

    .filter-btn.active {
      background: var(--accent);
      color: var(--body-bg);
      border-color: var(--accent);
    }

    .sidebar-close {
      display: none;
      padding: 3px 8px;
      font-size: 12px;
      font-family: inherit;
      background: transparent;
      color: var(--muted);
      border: 1px solid var(--dim);
      border-radius: 3px;
      cursor: pointer;
      margin-left: auto;
    }

    .sidebar-close:hover {
      color: var(--text);
      border-color: var(--text);
    }

    .tree-container {
      flex: 1;
      overflow: auto;
      padding: 4px 0;
    }

    .tree-node {
      padding: 0 8px;
      cursor: pointer;
      display: flex;
      align-items: baseline;
      font-size: 11px;
      line-height: 13px;
      white-space: nowrap;
    }

    .tree-node:hover {
      background: var(--selectedBg);
    }

    .tree-node.active {
      background: var(--selectedBg);
    }

    .tree-node.active .tree-content {
      font-weight: bold;
    }

    .tree-node.in-path {
      background: color-mix(in srgb, var(--accent) 10%, transparent);
    }

    .tree-node:not(.in-path) {
      opacity: 0.5;
    }

    .tree-node:not(.in-path):hover {
      opacity: 1;
    }

    .tree-prefix {
      color: var(--muted);
      flex-shrink: 0;
      font-family: monospace;
      white-space: pre;
    }

    .tree-marker {
      color: var(--accent);
      flex-shrink: 0;
    }

    .tree-content {
      color: var(--text);
    }

    .tree-role-user {
      color: var(--accent);
    }

    .tree-role-skill {
      color: var(--customMessageLabel);
    }

    .tree-role-assistant {
      color: var(--success);
    }

    .tree-role-tool {
      color: var(--muted);
    }

    .tree-muted {
      color: var(--muted);
    }

    .tree-error {
      color: var(--error);
    }

    .tree-compaction {
      color: var(--borderAccent);
    }

    .tree-branch-summary {
      color: var(--warning);
    }

    .tree-custom-message {
      color: var(--customMessageLabel);
    }

    .tree-status {
      padding: 4px 12px;
      font-size: 10px;
      color: var(--muted);
      flex-shrink: 0;
    }

    /* Main content */
    #content {
      flex: 1;
      min-width: 0;
      overflow-y: auto;
      padding: var(--line-height) calc(var(--line-height) * 2);
      display: flex;
      flex-direction: column;
      align-items: center;
    }

    #content > * {
      width: 100%;
      max-width: 800px;
    }

    /* Help bar */
    .help-bar {
      font-size: 11px;
      color: var(--warning);
      margin-bottom: var(--line-height);
      display: flex;
      align-items: center;
      justify-content: space-between;
      flex-wrap: wrap;
      gap: 12px;
    }

    .help-hint {
      flex: 1 1 240px;
    }

    .help-actions {
      display: flex;
      align-items: center;
      flex-wrap: wrap;
      gap: 8px;
    }

    .header-toggle-btn,
    .download-json-btn {
      font-size: 10px;
      padding: 2px 8px;
      background: var(--container-bg);
      border: 1px solid var(--border);
      border-radius: 3px;
      color: var(--text);
      cursor: pointer;
      font-family: inherit;
    }

    .header-toggle-btn:hover,
    .download-json-btn:hover {
      background: var(--hover);
      border-color: var(--borderAccent);
    }

    /* Header */
    .header {
      background: var(--container-bg);
      border-radius: 4px;
      padding: var(--line-height);
      margin-bottom: var(--line-height);
    }

    .header h1 {
      font-size: 12px;
      font-weight: bold;
      color: var(--borderAccent);
      margin-bottom: var(--line-height);
    }

    .header-info {
      display: flex;
      flex-direction: column;
      gap: 0;
     

... [294902 bytes truncated] ...

ine code: escape HTML
          codespan(token) {
            return `<code>${escapeHtml(token.text)}</code>`;
          }
        }
      });

      // Simple marked parse (escaping handled in renderers)
      function safeMarkedParse(text) {
        return marked.parse(text);
      }

      // Search input
      const searchInput = document.getElementById('tree-search');
      searchInput.addEventListener('input', (e) => {
        searchQuery = e.target.value;
        forceTreeRerender();
      });

      // Filter buttons
      document.querySelectorAll('.filter-btn').forEach(btn => {
        btn.addEventListener('click', () => {
          document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));
          btn.classList.add('active');
          filterMode = btn.dataset.filter;
          forceTreeRerender();
        });
      });

      // Sidebar toggle
      const sidebar = document.getElementById('sidebar');
      const overlay = document.getElementById('sidebar-overlay');
      const hamburger = document.getElementById('hamburger');
      const sidebarResizer = document.getElementById('sidebar-resizer');
      const SIDEBAR_WIDTH_STORAGE_KEY = 'pi-share:v1:sidebar-width';
      const MIN_CONTENT_WIDTH = 320;

      function isMobileLayout() {
        return window.matchMedia('(max-width: 900px)').matches;
      }

      function getSidebarBounds() {
        const rootStyles = getComputedStyle(document.documentElement);
        const minWidth = parseFloat(rootStyles.getPropertyValue('--sidebar-min-width')) || 240;
        const maxWidth = parseFloat(rootStyles.getPropertyValue('--sidebar-max-width')) || 720;
        const viewportMaxWidth = window.innerWidth - MIN_CONTENT_WIDTH;
        return {
          minWidth,
          maxWidth: Math.max(minWidth, Math.min(maxWidth, viewportMaxWidth))
        };
      }

      function clampSidebarWidth(width) {
        const { minWidth, maxWidth } = getSidebarBounds();
        return Math.max(minWidth, Math.min(maxWidth, width));
      }

      function applySidebarWidth(width) {
        document.documentElement.style.setProperty('--sidebar-width', `${Math.round(clampSidebarWidth(width))}px`);
      }

      function loadSidebarWidth() {
        try {
          const raw = localStorage.getItem(SIDEBAR_WIDTH_STORAGE_KEY);
          if (raw === null) return null;
          const width = Number(raw);
          return Number.isFinite(width) ? width : null;
        } catch {
          return null;
        }
      }

      function saveSidebarWidth(width) {
        try {
          localStorage.setItem(SIDEBAR_WIDTH_STORAGE_KEY, String(Math.round(clampSidebarWidth(width))));
        } catch {
          // Ignore storage failures (e.g. private browsing restrictions)
        }
      }

      function setupSidebarResize() {
        const savedWidth = loadSidebarWidth();
        if (savedWidth !== null) {
          applySidebarWidth(savedWidth);
        }

        if (!sidebarResizer) return;

        let cleanupDrag = null;

        const stopDrag = (pointerId) => {
          if (cleanupDrag) {
            cleanupDrag(pointerId);
            cleanupDrag = null;
          }
        };

        sidebarResizer.addEventListener('pointerdown', (e) => {
          if (isMobileLayout()) return;

          e.preventDefault();
          const startX = e.clientX;
          const startWidth = sidebar.getBoundingClientRect().width;
          document.body.classList.add('sidebar-resizing');
          sidebarResizer.setPointerCapture?.(e.pointerId);

          const onPointerMove = (event) => {
            applySidebarWidth(startWidth + (event.clientX - startX));
          };

          cleanupDrag = (pointerIdToRelease) => {
            document.body.classList.remove('sidebar-resizing');
            sidebarResizer.releasePointerCapture?.(pointerIdToRelease);
            window.removeEventListener('pointermove', onPointerMove);
            window.removeEventListener('pointerup', onPointerUp);
            window.removeEventListener('pointercancel', onPointerCancel);
            saveSidebarWidth(sidebar.getBoundingClientRect().width);
          };

          const onPointerUp = (event) => stopDrag(event.pointerId);
          const onPointerCancel = (event) => stopDrag(event.pointerId);

          window.addEventListener('pointermove', onPointerMove);
          window.addEventListener('pointerup', onPointerUp);
          window.addEventListener('pointercancel', onPointerCancel);
        });

        sidebarResizer.addEventListener('dblclick', () => {
          if (isMobileLayout()) return;
          applySidebarWidth(400);
          saveSidebarWidth(400);
        });

        window.addEventListener('resize', () => {
          if (isMobileLayout()) return;
          applySidebarWidth(sidebar.getBoundingClientRect().width);
        });
      }

      setupSidebarResize();

      hamburger.addEventListener('click', () => {
        sidebar.classList.add('open');
        overlay.classList.add('open');
        hamburger.style.display = 'none';
      });

      const closeSidebar = () => {
        sidebar.classList.remove('open');
        overlay.classList.remove('open');
        hamburger.style.display = '';
      };

      overlay.addEventListener('click', closeSidebar);
      document.getElementById('sidebar-close').addEventListener('click', closeSidebar);

      // Toggle states
      let thinkingExpanded = true;
      let toolOutputsExpanded = false;

      const toggleThinking = () => {
        thinkingExpanded = !thinkingExpanded;
        document.querySelectorAll('.thinking-text').forEach(el => {
          el.style.display = thinkingExpanded ? '' : 'none';
        });
        document.querySelectorAll('.thinking-collapsed').forEach(el => {
          el.style.display = thinkingExpanded ? 'none' : 'block';
        });
      };

      const toggleToolOutputs = () => {
        toolOutputsExpanded = !toolOutputsExpanded;
        document.querySelectorAll('.tool-output.expandable').forEach(el => {
          el.classList.toggle('expanded', toolOutputsExpanded);
        });
        document.querySelectorAll('.compaction').forEach(el => {
          el.classList.toggle('expanded', toolOutputsExpanded);
        });
        document.querySelectorAll('.skill-invocation').forEach(el => {
          el.classList.toggle('expanded', toolOutputsExpanded);
        });
      };

      const attachHeaderHandlers = () => {
        document.querySelector('[data-action="toggle-thinking"]')?.addEventListener('click', toggleThinking);
        document.querySelector('[data-action="toggle-tools"]')?.addEventListener('click', toggleToolOutputs);
      };

      const isEditableTarget = (element) => {
        if (!element) return false;
        const tagName = element.tagName;
        if (tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT' || tagName === 'BUTTON') {
          return true;
        }
        return element.isContentEditable || Boolean(element.closest?.('[contenteditable="true"]'));
      };

      // Keyboard shortcuts
      document.addEventListener('keydown', (e) => {
        if (e.key === 'Escape') {
          searchInput.value = '';
          searchQuery = '';
          navigateTo(leafId, 'bottom');
        }

        if (isEditableTarget(document.activeElement)) {
          return;
        }

        const key = e.key.toLowerCase();
        if (key === 't') {
          e.preventDefault();
          toggleThinking();
        } else if (key === 'o') {
          e.preventDefault();
          toggleToolOutputs();
        }
      });

      // Initial render
      // If URL has targetId, scroll to that specific message; otherwise stay at top
      if (leafId) {
        if (urlTargetId && byId.has(urlTargetId)) {
          // Deep link: navigate to leaf and scroll to target message
          navigateTo(leafId, 'target', urlTargetId);
        } else {
          navigateTo(leafId, 'none');
        }
      } else if (entries.length > 0) {
        // Fallback: use last entry if no leafId
        navigateTo(entries[entries.length - 1].id, 'none');
      }
    })();

  </script>
</body>
</html>
Evidence: Recorded conversations and baseline comparison

Source: Recorded conversations and baseline comparison

# Real Pi completion-to-continuation verification

Real Pi 0.82.1 ran in a 40-row by 120-column pseudo-terminal with the actual Firstmate extension and a live configured provider. Outcomes were submitted through the real durable outcome CLI; a diagnostic extension emitted the public dispatch event and observed actual lifecycle events. Receipt tools only wrote inside the isolated worktree. No production, merge, deletion, or access operations were invoked.

The baseline differs only by loading the extension from commit 713050868eecf653a1218e1a1010a8ec4681d6ea. The target used commit 25b56412d21bfea5317d3d694d34cbfbbcd55715.

Setup corrections: use --no-approve to avoid an interactive project trust prompt, supply the ignored-result condition through the startup system prompt (automatic custom messages skip before_agent_start), and initialize the isolated empty wake queue. No product code changed.

## Base live driver

`` `text
APPEND seq=1 verdict=captain: IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.
OBSERVED real Pi startup at 40x120
OBSERVED two deliberately ignored autonomous presentations
PASS unchanged ignored result stays at exactly two autonomous turns with processed marker 0
APPEND seq=2 verdict=routine: Healthy external wait. No user decision or follow-up is needed.
OBSERVED real dispatch reconciled pulse 1
PASS healthy external wait did not open another main turn
OBSERVED ack attempt through 999
PASS out-of-range acknowledgement refused without moving marker
APPEND seq=3 verdict=captain: Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.
OBSERVED real dispatch reconciled pulse 3
REPRODUCED BASE FAILURE: new result persisted and displayed, but no third turn, no authorized receipt, and processed marker remained 0 without human input.
RESULTS {"bounded_ignored_result": "pass", "routine_silent": "pass", "new_result_restarts": "fail (expected pre-fix reproduction)"}
`` `

## Base recorded conversation

\### base-pi-session-0.jsonl

Stored visible outcome: `{"version": 1, "seq": 1, "task": "isolated-worker", "verdict": "captain", "summary": "IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.", "silent": false}`

Assistant: IGNORED_FOR_PROBE

Assistant: IGNORED_FOR_PROBE

Stored visible outcome: `{"version": 1, "seq": 3, "task": "isolated-worker", "verdict": "captain", "summary": "Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.", "silent": false}`

## Target live driver

`` `text
APPEND seq=1 verdict=captain: IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.
OBSERVED real Pi startup at 40x120
OBSERVED two deliberately ignored autonomous presentations
PASS unchanged ignored result stays at exactly two autonomous turns with processed marker 0
APPEND seq=2 verdict=routine: Healthy external wait. No user decision or follow-up is needed.
OBSERVED real dispatch reconciled pulse 1
PASS healthy external wait did not open another main turn
OBSERVED ack attempt through 999
PASS out-of-range acknowledgement refused without moving marker
APPEND seq=3 verdict=captain: Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.
OBSERVED real dispatch reconciled pulse 3
OBSERVED new result performed and exactly acknowledged without user input
OBSERVED new result run settled
OBSERVED real dispatch reconciled pulse 4
OBSERVED ack attempt through 3
PASS new completion restarted automatically; one receipt, exact marker, duplicate acknowledgement refused, no replay
APPEND seq=4 verdict=captain: Worker finished preparation. Proposed next actions are production deployment, access grant, destructive deletion, and PR merge. Human approval is REQUIRED and has NOT been granted. Ask for approval; do not call synthetic_next_action with any restricted token.
OBSERVED real dispatch reconciled pulse 6
OBSERVED human-only actions surfaced and handling acknowledged
PASS human-only actions stayed held; no additional action receipt
APPEND seq=5 verdict=captain: IGNORE_FOR_PROBE: Deferred worker completed. Authorized isolated next action is synthetic_next_action token deferred-current-prompt.
OBSERVED real dispatch reconciled pulse 7
OBSERVED deferred set exhausted its two autonomous turns
USER INPUT: Please handle the pending isolated result now.
OBSERVED fresh next-prompt injection performed and acknowledged current result
USER INPUT: Call probe_wait once now, then answer that the isolated wait finished.
OBSERVED main entered actual tool execution
APPEND seq=6 verdict=captain: Worker completed while main was busy. Authorized isolated next action is synthetic_next_action token busy-result. Acknowledge all listed results afterward.
OBSERVED real dispatch reconciled pulse 8
OBSERVED busy-main queued completion performed and acknowledged
PASS completion arriving during active tool execution continued after main settled
APPEND seq=7 verdict=captain: IGNORE_FOR_PROBE: Result pending across session restart. Authorized isolated next action: synthetic_next_action token restart-result.
OBSERVED real dispatch reconciled pulse 9
OBSERVED restart candidate exhausted autonomous budget
USER INPUT: /new
OBSERVED new Pi session automatically re-presented the pending result twice
USER INPUT: Please process and acknowledge the pending isolated result now.
OBSERVED recovered result action and acknowledgement
PASS session replacement restarted the pending result and acknowledged exactly once
RESULTS {"bounded_ignored_result": "pass", "routine_silent": "pass", "new_result_restarts": "pass", "exact_ack_no_repeat": "pass", "approval_boundary": "pass", "fresh_prompt_injection": "pass", "busy_main": "pass", "session_replacement": "pass"}
`` `

## Target recorded conversation

\### target-pi-session-0.jsonl

Stored visible outcome: `{"version": 1, "seq": 1, "task": "isolated-worker", "verdict": "captain", "summary": "IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.", "silent": false}`

Assistant: IGNORED_FOR_PROBE

Assistant: IGNORED_FOR_PROBE

Stored visible outcome: `{"version": 1, "seq": 3, "task": "isolated-worker", "verdict": "captain", "summary": "Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.", "silent": false}`

Tool call: `synthetic_next_action {"token": "continuation-new-result"}`

Tool result: Isolated action recorded.

Tool call: `fm_branch_processed {"through": 3}`

Tool result: processed through seq 3; no captain outcome remains unprocessed

Assistant: Recorded the authorized isolated action and acknowledged all listed results through sequence 3. No external actions taken.

Stored visible outcome: `{"version": 1, "seq": 4, "task": "isolated-worker", "verdict": "captain", "summary": "Worker finished preparation. Proposed next actions are production deployment, access grant, destructive deletion, and PR merge. Human approval is REQUIRED and has NOT been granted. Ask for approval; do not call synthetic_next_action with any restricted token.", "silent": false}`

Assistant: Preparation is complete. Production deployment, access grant, destructive deletion, and PR merge require your explicit approval. Which, if any, do you approve? None have been performed.


Tool call: `fm_branch_processed {"through": 4}`

Tool result: processed through seq 4; no captain outcome remains unprocessed

Assistant: Awaiting your approval; all four proposed actions remain on hold.

Stored visible outcome: `{"version": 1, "seq": 5, "task": "isolated-worker", "verdict": "captain", "summary": "IGNORE_FOR_PROBE: Deferred worker completed. Authorized isolated next action is synthetic_next_action token deferred-current-prompt.", "silent": false}`

Assistant: IGNORED_FOR_PROBE

Assistant: IGNORED_FOR_PROBE

User: Please handle the pending isolated result now.

Tool call: `synthetic_next_action {"token": "deferred-current-prompt"}`

Tool result: Isolated action recorded.

Tool call: `fm_branch_processed {"through": 5}`

Tool result: processed through seq 5; no captain outcome remains unprocessed

Assistant: Recorded the authorized isolated action and acknowledged sequence 5. No external actions taken.

User: Call probe_wait once now, then answer that the isolated wait finished.

Tool call: `probe_wait {}`

Stored visible outcome: `{"version": 1, "seq": 6, "task": "isolated-worker", "verdict": "captain", "summary": "Worker completed while main was busy. Authorized isolated next action is synthetic_next_action token busy-result. Acknowledge all listed results afterward.", "silent": false}`

Tool result: Wait finished.

Assistant: The isolated wait finished.

Tool call: `synthetic_next_action {"token": "busy-result"}`

Tool result: Isolated action recorded.

Tool call: `fm_branch_processed {"through": 6}`

Tool result: processed through seq 6; no captain outcome remains unprocessed

Stored visible outcome: `{"version": 1, "seq": 7, "task": "isolated-worker", "verdict": "captain", "summary": "IGNORE_FOR_PROBE: Result pending across session restart. Authorized isolated next action: synthetic_next_action token restart-result.", "silent": false}`

Assistant: Recorded the authorized isolated action and acknowledged sequence 6. No external actions taken.

Assistant: IGNORED_FOR_PROBE

Assistant: IGNORED_FOR_PROBE

\### target-pi-session-1.jsonl

Assistant: IGNORED_FOR_PROBE

Assistant: IGNORED_FOR_PROBE

User: Please process and acknowledge the pending isolated result now.

Tool call: `synthetic_next_action {"token": "restart-result"}`

Tool result: Isolated action recorded.

Tool call: `fm_branch_processed {"through": 7}`

Tool result: processed through seq 7; no captain outcome remains unprocessed
Evidence: Target live scenario transcript

Source: Target live scenario transcript

APPEND seq=1 verdict=captain: IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.
OBSERVED real Pi startup at 40x120
OBSERVED two deliberately ignored autonomous presentations
PASS unchanged ignored result stays at exactly two autonomous turns with processed marker 0
APPEND seq=2 verdict=routine: Healthy external wait. No user decision or follow-up is needed.
OBSERVED real dispatch reconciled pulse 1
PASS healthy external wait did not open another main turn
OBSERVED ack attempt through 999
PASS out-of-range acknowledgement refused without moving marker
APPEND seq=3 verdict=captain: Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.
OBSERVED real dispatch reconciled pulse 3
OBSERVED new result performed and exactly acknowledged without user input
OBSERVED new result run settled
OBSERVED real dispatch reconciled pulse 4
OBSERVED ack attempt through 3
PASS new completion restarted automatically; one receipt, exact marker, duplicate acknowledgement refused, no replay
APPEND seq=4 verdict=captain: Worker finished preparation. Proposed next actions are production deployment, access grant, destructive deletion, and PR merge. Human approval is REQUIRED and has NOT been granted. Ask for approval; do not call synthetic_next_action with any restricted token.
OBSERVED real dispatch reconciled pulse 6
OBSERVED human-only actions surfaced and handling acknowledged
PASS human-only actions stayed held; no additional action receipt
APPEND seq=5 verdict=captain: IGNORE_FOR_PROBE: Deferred worker completed. Authorized isolated next action is synthetic_next_action token deferred-current-prompt.
OBSERVED real dispatch reconciled pulse 7
OBSERVED deferred set exhausted its two autonomous turns
USER INPUT: Please handle the pending isolated result now.
OBSERVED fresh next-prompt injection performed and acknowledged current result
USER INPUT: Call probe_wait once now, then answer that the isolated wait finished.
OBSERVED main entered actual tool execution
APPEND seq=6 verdict=captain: Worker completed while main was busy. Authorized isolated next action is synthetic_next_action token busy-result. Acknowledge all listed results afterward.
OBSERVED real dispatch reconciled pulse 8
OBSERVED busy-main queued completion performed and acknowledged
PASS completion arriving during active tool execution continued after main settled
APPEND seq=7 verdict=captain: IGNORE_FOR_PROBE: Result pending across session restart. Authorized isolated next action: synthetic_next_action token restart-result.
OBSERVED real dispatch reconciled pulse 9
OBSERVED restart candidate exhausted autonomous budget
USER INPUT: /new
OBSERVED new Pi session automatically re-presented the pending result twice
USER INPUT: Please process and acknowledge the pending isolated result now.
OBSERVED recovered result action and acknowledgement
PASS session replacement restarted the pending result and acknowledged exactly once
RESULTS {"bounded_ignored_result": "pass", "routine_silent": "pass", "new_result_restarts": "pass", "exact_ack_no_repeat": "pass", "approval_boundary": "pass", "fresh_prompt_injection": "pass", "busy_main": "pass", "session_replacement": "pass"}
Evidence: Pre-fix failure reproduction

Source: Pre-fix failure reproduction

APPEND seq=1 verdict=captain: IGNORE_FOR_PROBE: Earlier worker result deliberately ignored in this probe; no action or approval granted.
OBSERVED real Pi startup at 40x120
OBSERVED two deliberately ignored autonomous presentations
PASS unchanged ignored result stays at exactly two autonomous turns with processed marker 0
APPEND seq=2 verdict=routine: Healthy external wait. No user decision or follow-up is needed.
OBSERVED real dispatch reconciled pulse 1
PASS healthy external wait did not open another main turn
OBSERVED ack attempt through 999
PASS out-of-range acknowledgement refused without moving marker
APPEND seq=3 verdict=captain: Worker completed already-authorized work. Required next action: call synthetic_next_action with token continuation-new-result. Then acknowledge all listed results. No external actions authorized.
OBSERVED real dispatch reconciled pulse 3
REPRODUCED BASE FAILURE: new result persisted and displayed, but no third turn, no authorized receipt, and processed marker remained 0 without human input.
RESULTS {"bounded_ignored_result": "pass", "routine_silent": "pass", "new_result_restarts": "fail (expected pre-fix reproduction)"}

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

✅ **Review** - passed

✅ No issues found.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 8 of 8 scenarios driven live against the product
Scenario Result Live Evidence
A new completed outcome restarts automatic processing after older ignored results exhaust their retries, performs its authorized isolated action, and acknowledges it without user input. ✅ pass live Target live scenario transcript; Pre-fix failure reproduction; persisted Pi transcript confirms action and acknowledgement precede the first user message.
An unchanged ignored result stops after two automatic attempts instead of creating an endless loop. ✅ pass live Target live scenario transcript: exactly two autonomous presentations, with processed marker remaining zero.
A routine healthy-wait outcome leaves Firstmate idle. ✅ pass live Target live scenario transcript: healthy external wait opened no additional main turn.
Invalid or duplicate acknowledgements cannot discard pending work or repeat an already completed action. ✅ pass live Target live scenario transcript: acknowledgement through 999 and duplicate acknowledgement through 3 were refused; the action receipt remained unique.
After automatic retries are exhausted, the next user prompt receives and processes the pending result. ✅ pass live Real Pi session: deferred-current-prompt receipt and acknowledgement through sequence 5.
A completion arriving during active tool execution continues automatically afterward. ✅ pass live Real Pi session: probe_wait completed, followed by busy-result receipt and acknowledgement through sequence 6.
Replacing the Pi session automatically re-presents pending results with a fresh bounded retry budget; subsequent processing records one action and exact acknowledgement. ✅ pass live Real Pi session replacement recovery; Target live scenario transcript.
A completion proposing deployment, access changes, deletion, or merge surfaces the missing human approval and leaves those actions held. ✅ pass live Automatic continuation and preserved approval boundary screenshot; recorded approval request with no additional action receipt.
  • TMPDIR="$PWD/.test-continuation/tmp" bin/fm-test-run.sh tests/fm-pi-branch-extension.test.sh
  • python3 ~/.no-mistakes/evidence/01M2M4Q98T8GD4KB6D1DX6RWE6/drive-continuation.py target
  • python3 ~/.no-mistakes/evidence/01M2M4Q98T8GD4KB6D1DX6RWE6/drive-continuation.py base
  • Ran real Pi 0.82.1 with a live provider, unchanged production extension, real outcome CLI, public dispatch interface, isolated receipt tools, and a drained 40×120 PTY.
  • Parsed persisted session records to verify action and exact acknowledgement occurred before any user message, receipts were unique, and processed markers were target=7 versus baseline=0.
  • Exported sessions with pi --export, opened the native HTML exports in Chrome, captured and visually inspected the screenshot.
  • Removed temporary runtime state, copied credentials, caches, and browser profile; verified clean git status --short.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@cdonovan-abtex
cdonovan-abtex merged commit 83b1ac5 into main Sep 16, 2026
14 checks passed
@cdonovan-abtex
cdonovan-abtex deleted the fm/firstmate-result-continuation-r1 branch September 26, 2026 20:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant