Skip to content

fix: keep backstop delivery receipts usable after task teardown - #209

Merged
dnth merged 2 commits into
mainfrom
fm/fm-backstop-receipt-after-teardown
Oct 5, 2026
Merged

dnth merged 2 commits into
mainfrom
fm/fm-backstop-receipt-after-teardown

Conversation

@dnth

@dnth dnth commented Oct 5, 2026

Copy link
Copy Markdown
Owner

Intent

Fix the delivery-receipt command the wake drain prints after a task's status file is gone. The STATUS OUTCOME BACKSTOP in bin/fm-wake-drain.sh printed bin/fm-branch-outcome.sh deliver --task <id> --status-ident <ident> --through <N>. Merge reconciliation (bin/fm-todo-project.sh --check --reconcile) and scout teardown delete state/.status, after which --through refuses ("status file is missing or unreadable"; bin/fm-branch-outcome.sh deliver). On 2026-10-05 this happened for two tasks, and the obligation stayed open until the --endpoint form was used by hand. Make the owed receipt recordable after the status file is gone, with the smallest correct change. Keep the identity check and never mark events that were not owed. bin/fm-branch-outcome.sh's header contract owns the ledger.

Chosen approach (deliberate): the backstop now prints one deliver --task T --status-ident I --endpoint N receipt line per shown undelivered captain-facing event, instead of a single --through hint per task. --endpoint is identity-keyed and records after teardown; because the drain only prints endpoints that are owed obligations, no non-owed endpoint is ever named. deliver's --through and --endpoint semantics are intentionally unchanged (--through still refuses on a missing file or mismatched identity; --endpoint still refuses a non-event endpoint while the named-identity file exists). Only the deliver header comment in bin/fm-branch-outcome.sh was updated to describe which form survives teardown. Supervision docs already say to run the printed deliver receipt for each backstop entry, so no doc change.

Acceptance criteria:

  • AC1: regression test: a captain-facing completion obligation exists, the task's status file is removed (as teardown does), and the receipt path the backstop prints succeeds and discharges exactly that obligation; the test fails on the parent commit.
  • AC2: regression test: no receipt is ever recorded for an endpoint that was not an owed obligation, and the status-file identity check still refuses a mismatched identity when the file exists.
  • AC3: changed tests green via bin/fm-test-run.sh --changed, FM_LINT_JOBS=1 bin/fm-lint.sh clean, and the PR's full GitHub CI suite green.

Tests run the wake drain only with FM_TASK_ID unset (the drain refuses inside a task worker). Run lint with FM_LINT_JOBS=1.

Firstmate-Validation-Generation: 3c956fea2703f65bb22d6831ad18cf09

What Changed

  • Print a complete, identity-keyed deliver --endpoint command for each shown undelivered backstop event, so its receipt remains recordable after status-file teardown.
  • Count each event and its receipt command together toward the backstop output limit, and clarify the delivery command’s teardown contract.
  • Add regression coverage for receipts after teardown, owed-event-only commands, and refusal of non-event endpoints, mismatched identities, and --through receipts after teardown.

Risk Assessment

✅ Low: Captain, this bounded change preserves complete identity-keyed receipts, names only shown owed events, and leaves delivery semantics unchanged.

Testing

The focused regression script and four isolated live CLI scenarios passed. The parent command reproduced the reported failure. CLI transcripts and ledger state were captured; disposable files were removed. The broad changed-test selection was inspected only; lint and CI remain outside this assigned test phase.

  • Live validation: ✅ go - 4 of 4 scenarios driven live against the product
Scenario Result Live Evidence
Execute a long task's displayed receipt after status cleanup; exactly one obligation is discharged and retry records nothing extra ✅ pass live Live CLI transcript: long-task scenario and parent regression
Drain multiple completion events; separate receipts name only undelivered obligations and execute after cleanup ✅ pass live Live CLI transcript: multiple-events scenario
Attempt a non-event endpoint, mismatched-identity through receipt, and missing-file through receipt; each refuses without ledger writes ✅ pass live Live CLI transcript: refusals scenario
Overflow the backstop with long tasks and descriptions; displayed event-command pairs stay within budget and every command remains executable ✅ pass live Live CLI transcript: budget scenario
Evidence: Live CLI receipts, refusals, ledger state, and parent regression
$ bin/fm-wake-drain.sh
STATUS OUTCOME BACKSTOP (captain-facing task events without a recorded delivery receipt):
fm-delivery-backstop-receipt-after-scout-status-teardown-long-id done: shipped then torn down
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task fm-delivery-backstop-receipt-after-scout-status-teardown-long-id --status-ident 64513:38835374 --endpoint 29


exit=0
Removed status file before executing displayed receipt
$ bin/fm-branch-outcome.sh deliver --task fm-delivery-backstop-receipt-after-scout-status-teardown-long-id --status-ident 64513:38835374 --endpoint 29
delivered: 1 receipt(s) recorded for fm-delivery-backstop-receipt-after-scout-status-teardown-long-id


exit=0
persisted delivery ledger: [{"task": "fm-delivery-backstop-receipt-after-scout-status-teardown-long-id", "statusIdent": "64513:38835374", "endpoint": 29, "epoch": 1791205955}]
$ bin/fm-branch-outcome.sh deliver --task fm-delivery-backstop-receipt-after-scout-status-teardown-long-id --status-ident 64513:38835374 --endpoint 29
delivered: 0 receipt(s) recorded for fm-delivery-backstop-receipt-after-scout-status-teardown-long-id


exit=0
persisted delivery ledger: [{"task": "fm-delivery-backstop-receipt-after-scout-status-teardown-long-id", "statusIdent": "64513:38835374", "endpoint": 29, "epoch": 1791205955}]
PASS: long command survives teardown; exact receipt and idempotence
$ bin/fm-branch-outcome.sh deliver --task multi --status-ident 64513:38835376 --endpoint 12
delivered: 1 receipt(s) recorded for multi


exit=0
$ bin/fm-wake-drain.sh
STATUS OUTCOME BACKSTOP (captain-facing task events without a recorded delivery receipt):
multi failed: second
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task multi --status-ident 64513:38835376 --endpoint 41
multi done: third
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task multi --status-ident 64513:38835376 --endpoint 53


exit=0
$ bin/fm-branch-outcome.sh deliver --task multi --status-ident 64513:38835376 --endpoint 41
delivered: 1 receipt(s) recorded for multi


exit=0
$ bin/fm-branch-outcome.sh deliver --task multi --status-ident 64513:38835376 --endpoint 53
delivered: 1 receipt(s) recorded for multi


exit=0
persisted delivery ledger: [{"task": "multi", "statusIdent": "64513:38835376", "endpoint": 12, "epoch": 1791205955}, {"task": "multi", "statusIdent": "64513:38835376", "endpoint": 41, "epoch": 1791205956}, {"task": "multi", "statusIdent": "64513:38835376", "endpoint": 53, "epoch": 1791205956}]
PASS: one receipt per shown owed event; working and delivered event excluded
$ bin/fm-branch-outcome.sh deliver --task ref --status-ident 64513:38835382 --endpoint 14

error: 14 is not a captain-facing event endpoint in ref.status

exit=1
persisted delivery ledger: []
$ bin/fm-branch-outcome.sh deliver --task ref --status-ident 1:1 --through 28

error: cannot mark ref through 28: status file identity changed; re-drain for the current receipt identity

exit=1
persisted delivery ledger: []
$ bin/fm-branch-outcome.sh deliver --task ref --status-ident 64513:38835382 --through 28

error: cannot mark ref through 28: status file is missing or unreadable

exit=1
persisted delivery ledger: []
PASS: non-event, mismatched identity, missing-file through refuse without ledger writes
$ bin/fm-wake-drain.sh
STATUS OUTCOME BACKSTOP (captain-facing task events without a recorded delivery receipt):
task-00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835386 --endpoint 307
task-01-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-01-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835389 --endpoint 307
task-02-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-02-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835390 --endpoint 307
task-03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835391 --endpoint 307
task-04-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-04-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835392 --endpoint 307
task-05-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-05-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835393 --endpoint 307
task-06-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-06-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835394 --endpoint 307
task-07-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx done: zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz [truncated]
STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: bin/fm-branch-outcome.sh deliver --task task-07-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835395 --endpoint 307
STATUS OUTCOME BACKSTOP: 22 more omitted (byte cap)


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835386 --endpoint 307
delivered: 1 receipt(s) recorded for task-00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-01-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835389 --endpoint 307
delivered: 1 receipt(s) recorded for task-01-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-02-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835390 --endpoint 307
delivered: 1 receipt(s) recorded for task-02-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835391 --endpoint 307
delivered: 1 receipt(s) recorded for task-03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-04-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835392 --endpoint 307
delivered: 1 receipt(s) recorded for task-04-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-05-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835393 --endpoint 307
delivered: 1 receipt(s) recorded for task-05-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-06-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835394 --endpoint 307
delivered: 1 receipt(s) recorded for task-06-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
$ bin/fm-branch-outcome.sh deliver --task task-07-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx --status-ident 64513:38835395 --endpoint 307
delivered: 1 receipt(s) recorded for task-07-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx


exit=0
persisted delivery ledger: [{"task": "task-00-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835386", "endpoint": 307, "epoch": 1791205961}, {"task": "task-01-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835389", "endpoint": 307, "epoch": 1791205962}, {"task": "task-02-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835390", "endpoint": 307, "epoch": 1791205962}, {"task": "task-03-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835391", "endpoint": 307, "epoch": 1791205962}, {"task": "task-04-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835392", "endpoint": 307, "epoch": 1791205962}, {"task": "task-05-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835393", "endpoint": 307, "epoch": 1791205962}, {"task": "task-06-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835394", "endpoint": 307, "epoch": 1791205962}, {"task": "task-07-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "statusIdent": "64513:38835395", "endpoint": 307, "epoch": 1791205962}]
PASS: global budget preserved; 8 shown event/complete-command pairs; omitted events unrecorded
$ ~/.no-mistakes/worktrees/dd71c22cc6d7/01M462MP7BXP7XYWQWGN37PGZM/.receipt-validation/parent/bin/fm-wake-drain.sh
STATUS OUTCOME BACKSTOP (captain-facing task events without a recorded delivery receipt):
short-task done: regression baseline
STATUS OUTCOME BACKSTOP: after relaying these to the captain, record the delivery receipt: bin/fm-branch-outcome.sh deliver --task short-task --status-ident 64513:38835662 --through 26


exit=0
$ bin/fm-branch-outcome.sh deliver --task short-task --status-ident 64513:38835662 --through 26

error: cannot mark short-task through 26: status file is missing or unreadable

exit=1
persisted delivery ledger: []
PASS: parent reproduces reported failure after teardown
Evidence: Targeted regression tests
FM_TEST_BEGIN 2026-10-05T13:11:25Z tests/fm-wake-drain-unread-status.test.sh family=watcher-wake-lock expected_gate_skip=none
ok - a note: answer buried under a later routine note: is surfaced with both lines
ok - already-presented note: lines are not re-surfaced on the next drain
ok - a brand-new note: after presentation is surfaced without replaying handled lines
ok - a queued status signal annotates every unread note, not only the newest
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 911291.1791205894.8VpWOH
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 911291.1791205894.8VpWOH
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 911291.1791205894.8VpWOH
ok - a pending-reply resolution buried under a later note surfaces once and closes OPEN DECISIONS
ok - unread status over the former byte cap preserves every line
ok - presentation cursor advances only through its captured endpoint
ok - a reused task id starts its replacement status log unread at byte zero
ok - OPEN DECISIONS still folds needs-decision/blocked independently of unread notes
WAKE_ACK_REQUIRED: after handling completes run bin/fm-wake-drain.sh --ack-through 1 --recovery-generation 929818.1791205901.YZ8LKP
ok - an empty-queue backstop presentation still preserves the status for its later signal annotation
ok - routine working and delivered done lines print nothing on an empty-queue drain
ok - the wake drain resurfaces a parked terminal status until its delivery receipt is recorded
ok - the wake drain resurfaces a terminal status buried under routine work until delivered
ok - a delivery receipt is idempotent and retires the backstop entry
ok - the wake drain surfaces a completion that has neither outcome nor delivery receipt
delivered: 1 receipt(s) recorded for fm-delivery-backstop-receipt-after-scout-status-teardown-long-id
ok - the long task's complete printed receipt records delivery after status teardown
delivered: 1 receipt(s) recorded for multi
ok - the backstop prints a per-event receipt for each shown undelivered event only
ok - --endpoint on a non-event, an identity-mismatched --through, and a torn-file --through all refuse without appending
FM_TEST_END 2026-10-05T13:11:50Z tests/fm-wake-drain-unread-status.test.sh exit=0 duration_ms=24708 gate_skip=false
FM_TEST_SUMMARY total=1 failed=0 skipped_gate=0 duration_ms=24761
FM_TEST_SUMMARY_FAMILY family=watcher-wake-lock count=1 duration_ms=24708 failed=0
FM_TEST_SLOWEST rank=1 script=tests/fm-wake-drain-unread-status.test.sh duration_ms=24708
Evidence: Reproducible live CLI driver
import os, pathlib, subprocess, shlex, json, shutil
root=pathlib.Path.cwd()
work=root/'.receipt-validation'
evidence=pathlib.Path('~/.no-mistakes/evidence/01M462MP7BXP7XYWQWGN37PGZM')
log=[]
def run(args,home,expected=0):
    env=os.environ.copy()
    for k in list(env):
        if k.startswith('FM_'): env.pop(k)
    env.update(FM_HOME=str(home),FM_ROOT_OVERRIDE=str(home),TMPDIR=str(work/'tmp'),FM_WEDGE_ALARM_EXEC='discard')
    p=subprocess.run(args,cwd=root,env=env,text=True,capture_output=True)
    log.extend(['$ '+shlex.join(map(str,args)),p.stdout,p.stderr,'exit='+str(p.returncode)])
    assert p.returncode==expected,(args,p.returncode,p.stdout,p.stderr)
    return p.stdout+p.stderr
def home(name):
    h=work/name
    (h/'state').mkdir(parents=True)
    return h
def status(h,task,content):
    f=h/'state'/f'{task}.status';f.write_text(content)
    s=f.stat();return f,f'{s.st_dev}:{s.st_ino}'
def receipts(out):
    prefix='STATUS OUTCOME BACKSTOP: after relaying it to the captain, record its receipt: '
    return [shlex.split(s[len(prefix):]) for s in out.splitlines() if s.startswith(prefix)]
def ledger(h):
    f=h/'state'/'completion-deliveries.jsonl'
    rows=[json.loads(s) for s in f.read_text().splitlines()] if f.exists() else []
    log.append('persisted delivery ledger: '+json.dumps(rows));return rows
def deliver(h,task,ident,flag,ep,expected=0):
    return run(['bin/fm-branch-outcome.sh','deliver','--task',task,'--status-ident',ident,flag,str(ep)],h,expected)
try:
    h=home('long-task');task='fm-delivery-backstop-receipt-after-scout-status-teardown-long-id'
    f,ident=status(h,task,'done: shipped then torn down\n');ep=f.stat().st_size
    out=run(['bin/fm-wake-drain.sh'],h);cmds=receipts(out)
    assert cmds==[['bin/fm-branch-outcome.sh','deliver','--task',task,'--status-ident',ident,'--endpoint',str(ep)]]
    f.unlink();log.append('Removed status file before executing displayed receipt')
    run(cmds[0],h);rows=ledger(h)
    assert [(r['task'],r['statusIdent'],r['endpoint']) for r in rows]==[(task,ident,ep)]
    run(cmds[0],h);assert len(ledger(h))==1
    log.append('PASS: long command survives teardown; exact receipt and idempotence')

    h=home('multiple-events');f,ident=status(h,'multi','done: first\nworking: busy\nfailed: second\ndone: third\n')
    ep1=len(b'done: first\n');working=ep1+len(b'working: busy\n');ep2=working+len(b'failed: second\n');ep3=f.stat().st_size
    deliver(h,'multi',ident,'--endpoint',ep1)
    out=run(['bin/fm-wake-drain.sh'],h);cmds=receipts(out)
    assert [int(c[-1]) for c in cmds]==[ep2,ep3]
    f.unlink()
    for c in cmds:run(c,h)
    assert [r['endpoint'] for r in ledger(h)]==[ep1,ep2,ep3]
    log.append('PASS: one receipt per shown owed event; working and delivered event excluded')

    h=home('refusals');f,ident=status(h,'ref','working: busy\ndone: shipped\n');size=f.stat().st_size
    out=deliver(h,'ref',ident,'--endpoint',len(b'working: busy\n'),1)
    assert 'not a captain-facing event endpoint' in out and ledger(h)==[]
    out=deliver(h,'ref','1:1','--through',size,1)
    assert 'status file identity changed' in out and ledger(h)==[]
    f.unlink();out=deliver(h,'ref',ident,'--through',size,1)
    assert 'status file is missing or unreadable' in out and ledger(h)==[]
    log.append('PASS: non-event, mismatched identity, missing-file through refuse without ledger writes')

    h=home('budget');expected={}
    for i in range(30):
        task=f'task-{i:02d}-'+('x'*56)
        f,ident=status(h,task,'done: '+('z'*300)+'\n')
        expected[task]=(ident,f.stat().st_size)
    out=run(['bin/fm-wake-drain.sh'],h);cmds=receipts(out)
    lines=out.splitlines();start=next(i for i,l in enumerate(lines) if l.startswith('STATUS OUTCOME BACKSTOP'))
    pairs=[]
    for i,l in enumerate(lines):
        if l.startswith('STATUS OUTCOME BACKSTOP: after relaying it'):
            pairs.extend([lines[i-1],l])
    assert 0<len(cmds)<30
    assert len(('\n'.join(pairs)+'\n').encode())<=4000
    for c in cmds:
        task=c[c.index('--task')+1];ident,ep=expected[task]
        assert c==['bin/fm-branch-outcome.sh','deliver','--task',task,'--status-ident',ident,'--endpoint',str(ep)]
        (h/'state'/f'{task}.status').unlink();run(c,h)
    assert len(ledger(h))==len(cmds)
    log.append(f'PASS: global budget preserved; {len(cmds)} shown event/complete-command pairs; omitted events unrecorded')

    base=work/'parent';shutil.copytree(root/'bin',base/'bin')
    old=subprocess.check_output(['git','show','13507173930a06c8f242c8c10a156ec2b3af83ca:bin/fm-wake-drain.sh'])
    (base/'bin'/'fm-wake-drain.sh').write_bytes(old)
    h=home('parent-home');f,ident=status(h,'short-task','done: regression baseline\n')
    out=run([str(base/'bin'/'fm-wake-drain.sh')],h)
    oldcmd=[shlex.split(l[l.index('bin/fm-branch-outcome.sh deliver'):]) for l in out.splitlines() if 'bin/fm-branch-outcome.sh deliver' in l]
    assert len(oldcmd)==1 and '--through' in oldcmd[0]
    f.unlink();out=run(oldcmd[0],h,1)
    assert 'status file is missing or unreadable' in out and ledger(h)==[]
    log.append('PASS: parent reproduces reported failure after teardown')
finally:
    (evidence/'live-receipt-transcript.log').write_text('\n'.join(log)+'\n')

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 1 issue found → auto-fixed ✅
  • 🚨 bin/fm-wake-drain.sh:323 - The new receipt command still passes through a 219-character prose cap. For the valid 56-character task ID fm-delivery-backstop-receipt-after-scout-status-teardown, identity 16777234:123456789, and endpoint 27, the command is 223 characters; truncation cuts the identity and removes --endpoint. Relaying the shown completion, tearing down its status file, and executing the printed command therefore still leaves the obligation undischarged. Task creation permits IDs up to 64 characters (bin/fm-pr-lib.sh:113). Preserve the complete executable command at bin/fm-wake-drain.sh:322–323 and count its full length in the paired output budget at bin/fm-wake-drain.sh:324–326; cap only descriptive event text.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 4 of 4 scenarios driven live against the product
Scenario Result Live Evidence
Execute a long task's displayed receipt after status cleanup; exactly one obligation is discharged and retry records nothing extra ✅ pass live Live CLI transcript: long-task scenario and parent regression
Drain multiple completion events; separate receipts name only undelivered obligations and execute after cleanup ✅ pass live Live CLI transcript: multiple-events scenario
Attempt a non-event endpoint, mismatched-identity through receipt, and missing-file through receipt; each refuses without ledger writes ✅ pass live Live CLI transcript: refusals scenario
Overflow the backstop with long tasks and descriptions; displayed event-command pairs stay within budget and every command remains executable ✅ pass live Live CLI transcript: budget scenario
  • bin/fm-test-run.sh --list --changed --base 13507173930a06c8f242c8c10a156ec2b3af83ca
  • env -u FM_TASK_ID TMPDIR="$PWD/.receipt-validation/tmp" FM_HOME="$PWD/.receipt-validation/home" bin/fm-test-run.sh tests/fm-wake-drain-unread-status.test.sh
  • python3 .receipt-validation/drive.py: real isolated CLI checks, persisted ledger assertions, and parent failure reproduction
  • Removed disposable validation homes and copied runtime files; verified clean working tree.
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

dnth added 2 commits October 5, 2026 21:02
…tatus teardown

The wake drain's STATUS OUTCOME BACKSTOP printed a single deliver --through hint,
which refuses once merge reconciliation or scout teardown deletes state/<id>.status,
leaving the owed completion obligation open. Print one --endpoint receipt per shown
undelivered event instead; it is identity-keyed, records after teardown, and names
only owed endpoints.
@dnth
dnth merged commit 8f2a37e into main Oct 5, 2026
32 of 33 checks passed
@dnth
dnth deleted the fm/fm-backstop-receipt-after-teardown branch October 5, 2026 15:13
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