Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
Automated review by Claude — fully automated, no human in the loop. Please treat the findings with appropriate skepticism and verify before acting. Thanks for the PR! The 1. The diff bundles the already-merged #3523 — the branch needs a rebase. 2. Dead overflow guard in let log_time = start_nanos
.checked_add(entry.timestamp_offset_nanos)
.unwrap_or(start_nanos + entry.timestamp_offset_nanos);The The rebase (#1) is the main thing needed before this is cleanly reviewable. Generated by Claude Code |
4caf043 to
785d809
Compare
|
@phil-opp — fixed both points:
Full gate: |
|
🤖 Automated review (Claude) — fully automated; no human vetted this. Advisory only, not an approval, and I can't approve or merge. Re-reviewed after the force-push to
No new issues found. The export logic reads clean and is purely additive: payload passed through verbatim, per-topic 1-based sequence, Generated by Claude Code |
|
@phil-opp Could you proceed with this merge? Claude re-review is clean (no blocking issues) and all CI checks are green. If it's in the queue for trunk, please submit it — or let me know if the author-side |
|
🤖 Automated review by Claude — fully automated review; no human has vetted this. Advisory only, not an approval (and I can't approve or merge). The remux logic itself looks correct, and the round-trip tests genuinely exercise it: the payload is passed through verbatim, Dependency footprint for a one-shot subcommand. Given the repo's supply-chain-audit gate and the "discuss non-trivial changes first" convention, it seems worth an explicit call on whether this belongs in the core CLI as-is, behind an optional Generated by Claude Code |
|
Addressed the dependency-footprint flag —
Verified locally: default build + surface snapshot test green; all 5 |
|
🤖 Automated review (Claude Code) — fully automated; no human vetted this. Advisory only, not an approval; I'm posting like an outside contributor and can't approve or merge. Re-reviewed after the feature-gate commit No new issues found — the remux path is purely additive and the round-trip/filter tests genuinely exercise it (write Generated by Claude Code |
|
🤖 Automated review (Claude Code) — fully automated, no human vetted this; advisory only, not an approval, and I can't approve or merge. Fresh re-read of the current head ( The feature itself looks correctly scoped — The round-trip test Generated by Claude Code |
|
Addressed the test-quality issue — new head
Verified: all 5 |
|
🤖 Automated review (Claude Code) — fully automated, no human vetted this; advisory only, not an approval, and I can't approve or merge. Re-read of the new head The test-quality issue from my last review is resolved. No new issues in this commit. Feature scoping is unchanged and still correct ( Generated by Claude Code |
|
Blocking: Please reject cases where the output refers to the same file as the input. "run_export" opens the ".drec" reader and then calls "File::create(output)". If "--output" is the input path—or a hardlink or symlink to it—"File::create" truncates the recording before its entries are read. The buffered reader may then produce an empty or partial export, while the original recording has already been irreversibly destroyed. Please compare input/output file identity rather than only their path strings, or write to a temporary file and atomically rename it after a successful export. A regression test covering identical paths and, ideally, aliases to the same file would be valuable. Written by codex |
|
@phil-opp fixed — new head
Regression tests cover all three collapse cases (identical path, hardlink alias, symlink alias → all rejected with Verified: all 9 |
|
Trunk merge queue root cause (not this PR's changes): The queue failure is the
Both reach every workspace Fix (this PR, Once this merges and carries the waiver onto |
a40175b to
248d042
Compare
|
🤖 Automated review by Claude (Claude Code) — fully automated, no human has vetted this. Advisory only, not an approval (and I can't approve or merge). Fresh re-read of head The two commits since the last automated review both check out:
No new issues in the export path itself. Feature scoping remains correct ( Two cosmetic non-blocking nits: Generated by Claude Code |
248d042 to
ee89598
Compare
|
🤖 Automated re-review by Claude — fully automated, no human vetted this. Advisory only, not an approval. Re-reviewed at The export feature is unchanged and still checks out: correctly scoped behind the off-by-default Generated by Claude Code |
|
Review generated by Claude Code. Thanks for the follow-ups. The rebase, the 1. Bare relative filenames are rejected. In 2. It doesn't compile on Windows. Smaller things while you're in there:
|
|
🤖 Automated review by Claude — fully automated review, no human has vetted this; advisory only, not an approval (I can't approve or merge). Reviewed from the diff. Re-reviewed at
Independent re-read of the export path shows nothing new: correctly gated behind the off-by-default One non-blocking coordination note (unchanged from last time): this PR and #3543 both add lines to the same Generated by Claude Code |
The daemon stops a node by SIGTERMing its process group and escalates to a group SIGKILL only while the node is still registered. A bare guard died on that first SIGTERM, so the node was unregistered, the escalation was skipped, and a shell ignoring SIGTERM (plus its background forks) survived, reparented to init. Armed mode now installs SIGTERM/SIGINT/SIGHUP handlers before spawning, forwards the stop signal to the shell, blocks until the shell is reaped, then re-raises. The node stays registered across the escalation, so the group SIGKILL actually lands on the whole group. Also: - drop the feature-gated line (from dora-rs#3541) from the cli-surface snapshot - daemon: never look `dora` up on PATH when the guard binary is not the `dora` binary; fall back to plain `sh -c` instead (a PATH `dora` could be a different version or an unrelated binary) - e2e: a TERM-ignoring shell under `--stop-after` must not orphan anything Signed-off-by: harsh839 <harshbhargav440@gmail.com>
The daemon stops a node by SIGTERMing its process group and escalates to a group SIGKILL only while the node is still registered. A bare guard died on that first SIGTERM, so the node was unregistered, the escalation was skipped, and a shell ignoring SIGTERM (plus its background forks) survived, reparented to init. Armed mode now installs SIGTERM/SIGINT/SIGHUP handlers before spawning, forwards the stop signal to the shell, blocks until the shell is reaped, then re-raises. The node stays registered across the escalation, so the group SIGKILL actually lands on the whole group. Also: - drop the feature-gated line (from dora-rs#3541) from the cli-surface snapshot - daemon: never look `dora` up on PATH when the guard binary is not the `dora` binary; fall back to plain `sh -c` instead (a PATH `dora` could be a different version or an unrelated binary) - e2e: a TERM-ignoring shell under `--stop-after` must not orphan anything Signed-off-by: harsh839 <harshbhargav440@gmail.com>
|
Review generated by Claude Code. Thanks @harsh839. We want this feature, and the remux itself looks right. Before it merges, there are a few design points we'd like to settle, because once the command ships in a default build its shape is covered by the 1.0 CLI freeze. 1. Command placement: This gives later commands an obvious place (for example 2. Metadata parameters are dropped. The export keeps 3. Feature gate. Since 4. Write to a temporary file and rename. 5. Smaller things:
Generated by Claude Code |
|
🤖 Automated review (fully automated Claude Code review; no human vetted this — advisory only). This is a fresh re-read of Empty payloads are written as 0-byte messages on an
Writing the IPC stream for Generated by Claude Code |
|
Thanks for the design review, this is the push I needed, and you are right that the feature gate and the snapshot comment were me making a stability policy change in a place that hides it well. Working through it like this:
The one I have a question on is parameters. A companion channel changes the layout of the output, which is the thing people will end up writing readers against, so I would rather not pick that shape on my own. Unless you tell me otherwise I will take your fallback for now: say clearly in the docs and the module comment that parameters are not exported, drop the lossless wording, and follow up with the companion channel once you have settled the layout. That does mean an exported image topic is not decodable on its own in the meantime, which I would rather state plainly than paper over. I will handle the cli-surface.txt collision with 3543 at rebase time, keeping both header blocks. |
…-on-default Resolves the review on dora-rs#3541. `dora export` becomes `dora recording export`, so the recording lifecycle (`dora record`, `dora replay`, `dora recording export`) is one noun rather than three commands that read as unrelated verbs. The default output path replaces the input extension instead of appending to it, so `capture.drec` yields `capture.mcap` and not `capture.drec.mcap`. `mcap-export` is now a default feature. The command is the only stable way to get data back out of a `.drec` recording, so gating it behind `--features mcap-export` left release binaries, the wheel and `dora self update` without it, and the dedicated CI step was compensating for a gate the default build should not have needed. It is now covered by the existing CLI smoke gate, and it is part of the surface `cli-surface.txt` pins. The export writes a sibling temp file and renames it into place only on success, so a failure part-way through can no longer leave a truncated `.mcap` where a readable one used to be. Leaving the previous output untouched after a failure is now the tested behaviour rather than the untested one. A recorded message with no payload (`data: None`, what a typical `send_output("tick", pa.array([]))` trigger records) was written as a zero-byte message, which is not a valid Arrow IPC stream: any consumer of that channel failed on the message. It is now written as the zero-length `NullArray` stream that `dora replay` delivers for the same recording, pinned by a round-trip test that decodes the exported bytes. The per-message path no longer rebuilds an `Arc<Channel>` and re-clones the topic; one `HashMap` holds the channel id and next sequence per topic and messages go out through `write_to_known_channel`. A `--topics` entry that matched nothing is now reported instead of silently exporting less than asked for. `metadata.parameters` is not exported: it carries `width`/`height`/ `encoding` for images and `request_id`/`goal_id` for services and actions, and MCAP has no per-message metadata field to put them in. The module docs, `docs/cli.md` and the `--output` help say so, and the "lossless" framing is gone, rather than the export silently dropping data while claiming otherwise. Signed-off-by: harsh839 <harshbhargav440@gmail.com> Assisted-by: Claude
|
Went through the list and pushed
The export writes to a sibling temp file and renames it into place only on success, so a failure part-way through can no longer leave a truncated The empty-payload case was a real bug. A Per message the old path rebuilt an On One heads-up for whenever these two merge: #3543 edits the same |
|
🤖 Automated review (fully automated review by Claude Code; not vetted by a human) I re-read the diff at Here is how the points from the 2026-09-24 design review and the empty-payload comment stand in this commit:
Still open from earlier, no action needed now: #3543 edits the same Generated by Claude Code |
|
🤖 This is a fully automated review by Claude Code, with no human in the loop. Treat it as advisory only. I re-read A failed final flush still renames a truncated
Generated by Claude Code |
`mcap`'s `finish` does flush the stream it wraps — `write_summary_and_footer_magic` ends in `writer.flush()?` (mcap-0.25.0 `write.rs:1460`), and `CountingCrcWriter` forwards that to the inner writer rather than swallowing it — so a full disk is already reported as a failure before the rename, and the temp-file-and-rename holds. A review claimed otherwise, on the grounds that `finish` never flushes and both drops discard the error. It is true that both drops discard it, and that the guarantee currently rests on one line inside a dependency. `a_flush_that_fails_fails_the_export` now pins it: it hands `write_mcap` a stream that fails the way a full disk does, and requires the export to fail. It also fails if the failure can only be seen at drop time, which is the case the review described, so it is a real oracle rather than a tautology. That is all the test needs from `write_mcap`, so it takes the stream instead of the path — which also puts the file next to the rename it is written for. Signed-off-by: harsh839 <harshbhargav440@gmail.com> Assisted-by: Claude
|
Went looking for this one and could not reproduce it, so I did not change the export path — but I pinned the guarantee with a test instead. Pushed The claim is that What you are right about is that both drops would swallow an error, so the guarantee rests on one line inside a dependency that nobody here wrote down. The part I would point at: give that writer a second flush that fails and the test goes red, because by then the only thing left to notice it is a No behaviour change to the export, so the round-trip, filter and failure tests are as they were: 18 export tests and 440 CLI lib tests pass, plus the CLI surface snapshot. I have not run the recording e2e on this branch; nothing in the diff touches spawn or lifecycle. |
|
🤖 This is a fully automated review by Claude Code (no human in the loop). Treat it as advisory. I re-read Correction to the previous automated comment: the finding that "a failed final flush still renames a truncated
Two small follow-ups:
Otherwise I found no new issues. Generated by Claude Code |
|
🤖 Additional review notes (Claude Code). Found while reviewing head
Generated by Claude Code |
dora export to remux .drec recordings to MCAPdora recording export to remux .drec recordings to MCAP
A dev-dependency comment still said `dora export`. The command is `dora recording export`. Signed-off-by: harsh839 <harshbhargav440@gmail.com> Assisted-by: Claude
|
Both nits taken, and the title change was the more important of the two — trunk squash-merges with it, so the changelog would have named a command that does not exist. Title is now The dev-dependency comment in No code path changed, so the gates are as they were: 18 export tests, 440 CLI lib tests, the CLI surface snapshot, fmt and clippy clean. |
|
🤖 Automated follow-up review (Claude Code). This review is fully automated, with no human in the loop. Treat it as advisory only. The only commit since the last automated review is One issue that hasn't been raised on this PR before:
Still open from before:
Generated by Claude Code |
…t feature `dora replay` warns and skips a record it cannot decode. Export aborted on the first one, so a `.drec` written by a 1.x daemon that added an event could not be exported by an older binary at all: `InterDaemonEvent` is `#[non_exhaustive]` and postcard rejects an unknown variant index outright. Export is the tool whose whole job is getting data back out of a recording, so that is the wrong place to be the strict one — and one corrupt record cost the user every other message in the file. It now skips with a per-record warning, reports the count, and still fails when it emitted nothing usable, which is `dora replay`'s rule for the same reason: skipping must not turn systematic format drift into a file that looks like an empty recording. Read errors stay fatal, so a torn or over-long record still fails the export. `failed_export_keeps_the_previous_output_and_cleans_up` triggered its failure with unparseable event bytes, which no longer fail. It is about atomicity, not decoding, so it now fails the read instead — a record whose length prefix is over `MAX_RECORD_BYTES`. A torn tail would not do: `read_next_record` treats every short read as a clean EOF on purpose, so that recording exports the records that were fully written. The `mcap-export` feature gated the whole `Recording` subcommand group, so the next subcommand added under it would inherit the MCAP dependency by accident. Nothing disables it — the wheel, `dora self update` and CI all take default features — so it was four `cfg`s and a feature flag guarding a build nobody makes. Dropped; `mcap` and `same-file` are unconditional now. `--no-default-features` was already broken by the tracing and redb-backend features (8 errors before and after this change). Signed-off-by: harsh839 <harshbhargav440@gmail.com> Assisted-by: Claude
|
Agreed, and this one was a real defect rather than a doc nit, so it is fixed in The forward-compat case is the sharp end. Export now warns per record, counts, reports the count, and still fails when it That last part is You predicted the test would need a different trigger, and it did. Two new tests: one bad record among good ones exports the rest with sequence On the feature gate: dropped entirely, which is the version of your second Local: 442 CLI lib tests, 2 surface snapshot tests, fmt and clippy clean. The |
|
🤖 Automated review (Claude Code) — this review is fully automated, with no human in the loop. Treat it as advisory only. The new commit
All 19 Generated by Claude Code |
|
🤖 Automated review (fully automated review by Claude Code — no human has vetted this; please verify the findings). I re-reviewed the commits since the last automated review (
Generated by Claude Code |
|
Automated review by Claude (fully automated, not a human review) — reviewed at I re-read the whole diff. The commit that is new since the last automated review is
Still open or worth knowing:
On scope: about 390 lines of the 1.3k are production code and about 740 are tests. The 10 new crates are Generated by Claude Code |
|
🤖 Automated review (fully automated Claude Code review; no human vetted this). I re-reviewed head
Generated by Claude Code |
|
🤖 Automated review (fully automated review by Claude Code — no human has vetted this; advisory only). Re-reviewed at
One small item from the 09-29 review is still open after these commits. The doc comment on Generated by Claude Code |
Summary
Adds a new
dora recording exportCLI subcommand that remuxes a dora recording (.drec) into an MCAP (.mcap) file — Tier 1 of #3489.The command nests under
recordingso it reads as what it is — an operation on a recording — and leaves room for the other recording verbs. Without--outputit exports to<input>.mcap, replacing an existing file at that path.Each recorded
Outputentry becomes one MCAP message:{node_id}/{output_id}arrow-ipc(payload is passed through without re-encoding — the bytes end up verbatim in the.mcap)publish_time= the producer's HLC timestamp (metadata.timestamp()), i.e. the same stampdora topic hzreports after fix(cli,daemon): time topic hz from the producer HLC stamp; clear stale watchers on daemon reconnect #3523 merged — so timestamps agree across both viewslog_time= recording wall clock (start_nanos + timestamp_offset_nanos)u32::try_from, errors rather than silently wrapping past 2^32)dataflow_idand the originaldescriptor_yaml, so an MCAP retains the recording's provenance--topics node_a/output_x,node_b/output_yoptionally filters which topics are exported. Non-Outputentries (e.g.OutputClosed, extension messages) are skipped.If
--outputnames the input recording itself — as the identical path, or as a hardlink/symlink alias, including bare relative paths — export refuses to run rather than truncate the source (guarded viasame-file, which works on both Unix and Windows).Docs & coverage
docs/cli.mdgains adora recording exportsection documenting the arrow-ipc note for sinks like Foxglove, the default output path, and thatmetadata.parametersare not carried into the MCAP.mcap-exportis a default feature, so a plaincargo install dora-clihas the command.cargo test -p dora-cli --lib→ 440 passed, incl. 18 export tests: topic filter, round-trip (.drec→.mcapback out, asserting topic/encoding/sequence/log_time/publish_time/payload bytes + the metadata record), alias guards (identical path, hardlink, symlink, and bare-relative paths), default-output behaviour, a failed export leaving the previous output and temp file alone, and a failed final flush failing the export.cargo clippy -p dora-cli --all-targets -- -D warningsclean;cargo fmt --all -- --checkclean; cargo-denyadvisories/licenses/bans/sourcesok.cli-surface.txt, with no header change: it is on by default, so it belongs in the listing.dora recording export's--helpordering no longer collides withdora trace'sdisplay_order.This PR takes over #3489 from where the #3523 hz/producer-stamp work left off. @phil-opp would you be the reviewer?
Refs #3489