Repository navigation
Description
id(artifact): a blank line in artifacts.jsonl bricks Directory::open — and its torn-tail truncation is silent
Summary
ArtifactLog::open (pkgs/id/src/artifact.rs:573-593, main 0f5b4cbb) parses
every line of the artifact journal strictly. A single blank line — in the
middle, as a trailing extra newline (...\n\n), or as the file's only
content — fails the whole open with artifact line N is corrupt, even though
every artifact in the file is intact. ArtifactLog is loaded by
Directory::open (pkgs/id/src/directory.rs:473), so the failure takes the
whole directory down with it.
The sibling journal loaded in the very same Directory::open treats blank
lines differently: Directory::replay explicitly skips them
(directory.rs:535-537: if line.trim().is_empty() { continue; }).
Second divergence in the same function: when open drops a crash-torn final
line it truncates the file with no log line at all (artifact.rs:578-584).
Both sibling loaders warn with the byte offset when they do exactly this —
Directory::replay at directory.rs:525-528 ("dropping torn final entry at
byte {kept_len}") and FileJournal::open at world_store.rs:116-119. A torn
artifact tail therefore loses a signed artifact (e.g. a friend-removal record
replayed at directory.rs:475-490) with no trace anywhere.
Evidence (verified this run)
Laptop probe ~/git/fleet/probe-2026-10-29-artifactlog/ — the open loop
copied verbatim from artifact.rs (main 0f5b4cbb), artifacts as minimal
JSON objects, run 2026-10-29:
| case | ArtifactLog::open |
directory-style parse |
|---|---|---|
| two valid lines (control) | Ok, 2 stored | Ok, 2 |
| valid, blank, valid | Err "artifact line 2 is corrupt: EOF while parsing a value" | Ok, 2 |
valid, valid, blank (\n\n ending) |
Err "artifact line 3 is corrupt" | Ok, 2 |
file is only "\n" |
Err "artifact line 1 is corrupt" | Ok, 0 |
valid + torn tail (no trailing \n) |
Ok, 1 — file truncated, no warning emitted by the code path | — |
Blank lines reach this file in ordinary ways: a hand edit, an editor that
adds a final newline, a restore/sync that appends one. Nothing in the append
path (artifact.rs:601-608, one line + \n per write) ever needs a blank
line to be fatal.
Why it matters
- Availability: one stray newline makes
Directory::openfail, which is on
the world-open path (world_store.rsopen_worldloads the directory) —
the directory is down until someone hand-repairs the file, despite zero
corrupt artifacts. - Observability: the silent truncation is the only one of the three JSONL
loaders in this crate that drops data without a warning, so a lost
artifact leaves nothing to investigate.
Acceptance criteria
-
ArtifactLog::openskips blank/whitespace-only lines the way
Directory::replaydoes; a file with blank lines in any position
(including a\n\nending and a blank-only file) opens with all its
artifacts intact. - The torn-tail truncation in
ArtifactLog::openemits a warning naming
the file and the byte offset, matching theDirectory::replay/
FileJournal::openwording. - A genuinely corrupt non-blank line still fails the open with the
existingartifact line N is corruptcontext. - Regression tests cover: blank middle, blank final, blank-only, torn
tail (opens, truncates, warns), and corrupt-line refusal.
Dedup
Searched open + closed issues for "ArtifactLog", "artifact blank line" and
"torn": #1521 (closed) fixed the same class for Directory::replay and notes
ArtifactLog is "loaded in the same open", but covered only the torn tail of
the directory journal, not the artifact log's blank-line refusal or its
silent truncation. #1499/#1522/#1523/#1544/#1620 are the petname, group,
world-fork, records and outbox loaders respectively — none touches
artifact.rs.
Activity
developing-today commentedon Oct 29, 2026
CODE-1026
dezren39 commentedon Oct 29, 2026
Filed this run by the issue creator (lane A) after verification on current main.
What:
ArtifactLog::open(artifact.rs:573-593) refuses any blank line in the artifact journal — the whole open fails, andDirectory::open(directory.rs:473) loads it in the same open, so one stray newline takes the directory down with every artifact intact. Its torn-tail truncation is also the crate's only silent one (both sibling loaders warn with the byte offset).Evidence: laptop probe
~/git/fleet/probe-2026-10-29-artifactlog/(open loop verbatim, main0f5b4cbb): control Ok(2); blank-middle Err line 2; blank-final Err line 3; blank-only Err line 1; torn tail Ok(1) + silent truncate; directory-style parse Ok on all blank cases. Full table in the issue body.State: labelled
queue/queued+task/triage; arepo-task/v1triage packet is enqueued onrepo-health-code-pilotfor the worker. Not self-solved: the fix shape (skip blanks + warn, matching the siblings) is triage's pin.Found via: filed this run
15 remaining items
Merge pull request #3376 from developing-today/queue/5140-2128-artifa…
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done — fixed and merged
cargo test --lib artifactat PR head 5cd3af0, full output captured to a file on the laptop: 28 passed / 0 failed (exit 0), includingblank_lines_in_the_journal_are_skippedanda_corrupt_line_after_a_blank_line_reports_its_physical_line. Codex review of the head: clean, no findings.