Skip to content

Commit f1ba99c

Browse files
droid-ashclaude
andauthored
fix: Log Finalize Awaits Terminal Stream State (#177)
* fix: Log Finalize Awaits Terminal Stream State LogWriteStreamRegistry.finalize decided success/failure by reading entry.error at whatever moment its finally completed, instead of after the stream's terminal close state. That produced two wrong outcomes: a fully-flushed log with a stale non-destroying recorded error rejected (contradicting the file's own contract), and a close-time error arriving after the check silently missed. finalize now awaits the stream's terminal 'close' state before deciding, and the success predicate is state-based: resolve iff writableFinished && stream.errored === null (warn-and-resolve over a stale recorded error), else reject with entry.error ?? stream.errored. Supersedes the PR #173 decision to document rather than fix the unconditional recorded-error rejection, reopened on two independent reviewers converging (operator-side should-fix; CodeRabbit MAJOR / data integrity). * Update ship status and record PR URL * fix: address review feedback from @coderabbitai[bot] Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Update review-pr status Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: droid-ash <droid-ash@users.noreply.github.com> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent bc6e634 commit f1ba99c

8 files changed

Lines changed: 754 additions & 142 deletions

File tree

docs/memory/device-node/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ description: "Device interaction via gRPC: lifecycle, recording, log capture, an
88
| File | Description |
99
|------|-------------|
1010
| [android-ios-mirror](android-ios-mirror.md) | The Android/iOS mirror in device-node — platform clients, gRPC setups, recording/log providers, discovery probes: sharing is settled per pair by a measured diff, not parallel shape (`infra/commandFailure.ts`, `device/logWriteStream.ts`, `MAX_DIAGNOSTIC_OUTPUT_CHUNKS`), diagnostic buffers are bounded rings with a consumer, `simctl` plist fields degrade per field, driver recovery replays only an explicit allow-list, and `closeDriverChannel` closes a channel rather than killing a process. |
11-
| [log-capture](log-capture.md) | Per-test device log capture (manager, providers, Device integration) and the write-stream finalization contract: every exit from start/stop ends and flushes the log file's write stream through one shared `LogWriteStreamRegistry`, held per provider instance and keyed on the capture's output file path, so the file the CLI copies next is complete. `open()` attaches a persistent `error` listener that records the first error and never throws, and `finalize` fails the stop on it. |
11+
| [log-capture](log-capture.md) | Per-test device log capture (manager, providers, Device integration) and the write-stream finalization contract: every exit from start/stop ends and flushes the log file's write stream through one shared `LogWriteStreamRegistry`, held per provider instance and keyed on the capture's output file path, so the file the CLI copies next is complete. `open()` records the first stream `error` and never throws; `finalize` decides the stop from the stream's terminal `'close'` state. |

docs/memory/device-node/log-capture.md

Lines changed: 165 additions & 76 deletions
Large diffs are not rendered by default.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{"action":"enter","driver":"fab-new","event":"stage-transition","stage":"intake","ts":"2026-07-31T17:56:32Z"}
2+
{"args":"Fix stream-error reporting defect: finalize rejects unconditionally on recorded entry.error even when the log was fully flushed (writableFinished true); await terminal close/error state, update pinning test per Test Integrity, decide finalizeQuietly-before-finalize consumption behavior","cmd":"fab-new","event":"command","ts":"2026-07-31T17:56:32Z"}
3+
{"delta":"+4.6","event":"confidence","score":4.6,"trigger":"calc-score","ts":"2026-07-31T17:58:43Z"}
4+
{"delta":"+0.0","event":"confidence","score":4.6,"trigger":"calc-score","ts":"2026-07-31T18:00:32Z"}
5+
{"cmd":"fab-fff","event":"command","ts":"2026-07-31T18:01:08Z"}
6+
{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"apply","ts":"2026-07-31T18:01:13Z"}
7+
{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"review","ts":"2026-07-31T18:14:48Z"}
8+
{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"hydrate","ts":"2026-07-31T18:36:00Z"}
9+
{"event":"review","result":"passed","ts":"2026-07-31T18:36:00Z"}
10+
{"action":"enter","driver":"fab-fff","event":"stage-transition","stage":"ship","ts":"2026-07-31T18:44:54Z"}
11+
{"action":"enter","driver":"git-pr","event":"stage-transition","stage":"review-pr","ts":"2026-07-31T18:48:51Z"}
12+
{"event":"review","result":"passed","ts":"2026-07-31T19:06:49Z"}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
id: vojm
2+
name: 260731-vojm-log-finalize-terminal-state
3+
created: 2026-07-31T17:56:32Z
4+
created_by: ashish-noon
5+
change_type: fix
6+
issues: []
7+
progress:
8+
intake: done
9+
apply: done
10+
review: done
11+
hydrate: done
12+
ship: done
13+
review-pr: done
14+
plan:
15+
generated: true
16+
task_count: 9
17+
acceptance_count: 14
18+
acceptance_completed: 14
19+
confidence:
20+
certain: 2
21+
confident: 3
22+
tentative: 0
23+
unresolved: 0
24+
score: 4.6
25+
fuzzy: true
26+
dimensions:
27+
signal: 80.0
28+
reversibility: 68.0
29+
competence: 83.0
30+
disambiguation: 77.0
31+
stage_metrics:
32+
intake: {started_at: "2026-07-31T17:56:32Z", driver: fab-new, iterations: 1, completed_at: "2026-07-31T18:01:13Z"}
33+
apply: {started_at: "2026-07-31T18:01:13Z", driver: fab-fff, iterations: 1, completed_at: "2026-07-31T18:14:48Z"}
34+
review: {started_at: "2026-07-31T18:14:48Z", driver: fab-fff, iterations: 1, completed_at: "2026-07-31T18:36:00Z"}
35+
hydrate: {started_at: "2026-07-31T18:36:00Z", driver: fab-fff, iterations: 1, completed_at: "2026-07-31T18:44:54Z"}
36+
ship: {started_at: "2026-07-31T18:44:54Z", driver: fab-fff, iterations: 1, completed_at: "2026-07-31T18:48:51Z"}
37+
review-pr: {started_at: "2026-07-31T18:48:51Z", driver: git-pr, iterations: 1, completed_at: "2026-07-31T19:06:49Z"}
38+
prs:
39+
- https://github.com/droid-ash/finalrun-agent/pull/177
40+
change_type_source: explicit
41+
true_impact:
42+
added: 749
43+
deleted: 142
44+
net: 607
45+
tests:
46+
added: 116
47+
deleted: 8
48+
net: 108
49+
computed_at: "2026-07-31T18:48:51Z"
50+
computed_at_stage: ship
51+
summary: Log-stop finalization now decides from the write stream's terminal 'close' state — success iff writableFinished with no stream.errored, warn-and-resolve over a stale non-destroying record, deterministic rejection on a close-time error — superseding the unconditional recorded-error rejection, with registry and both-platform provider tests pinning the new outcomes and the quiet-first failure-response invariant.
52+
# true_impact: lazily created on first stage-finish that computes it (no placeholder here).
53+
last_updated: 2026-07-31T19:06:49Z

0 commit comments

Comments
 (0)