Skip to content

id(world-doctor): journal check passes version 0 and a blank-line headerless journal, and fails a blank-line valid journal — first-entry test uses the raw line index, version range has no lower bound #1598

Closed

Description

@dezren39

Problem

id world doctor's journal check (validate_journal_file, pkgs/id/src/world_doctor.rs:361-395) disagrees with the code that actually opens a journal in three ways, so doctor both clears journals the world refuses to open and condemns journals the world opens fine:

  1. Version 0 passes. The check ensures only version <= MAX_JOURNAL_VERSION (world_doctor.rs:382-385). The real header check in WorldCore::restore requires JOURNAL_VERSION..=MAX_JOURNAL_VERSION (1..=2) (pkgs/id/src/world/core.rs:152). A journal headed {"entry":"created","world_id":"w","version":0} is reported by doctor as 1 entries, format version 0 / Ok, but restore refuses it: unsupported world journal version 0.
  2. A leading blank line makes a valid journal fail. Doctor matches on the raw split-line index (world_doctor.rs:368,377-381), but skips empty lines with continue without adjusting that index, so a journal beginning with one blank line reports entry 2 is Created, but it is not the first entry. The real reader (read_journal_entries, pkgs/id/src/world_store.rs:452-465) skips empty lines before parsing, and restore sees Created as the first entry and opens it.
  3. The same blank line makes a headerless journal pass. With a leading blank line, a first real entry that is not Created is at index == 1, so the _ if index == 0 bail (world_doctor.rs:387-389) never fires, version stays None, and the function returns Ok((1, JOURNAL_VERSION)) via the unwrap_or at :394. Restore refuses the same bytes: world journal does not start with a header.

Evidence (verified this run)

Probe crate on the laptop (~/git/fleet/probe-2026-10-28-laneB-doctor there; world_doctor.rs sha256 72559955a81526838c8266a65fbdea1c6b6fcc39d1d1e11625ebfc748aa61848 identical on the laptop clone and the sandbox clone; probe copies the validate_journal_file logic verbatim against a minimal JournalEntry shim and the restore header checks verbatim from world/core.rs + world_store.rs):

CTRL v1:                doctor=Ok((1, 1))  restore=Ok(())
V0 version0:            doctor=Ok((1, 0))  restore=Err("unsupported world journal version 0")
V3 future:              doctor=Err("journal format version 3 is newer than this binary supports (2)")  restore=Err("unsupported world journal version 3")
BLANK+Created v1:       doctor=Err("entry 2 is `Created`, but it is not the first entry")  restore=Ok(())
BLANK+headerless:       doctor=Ok((1, 1))  restore=Err("world journal does not start with a header")
CTRL headerless no blank: doctor=Err("the first entry is not `Created`")  restore=Err(...)

Controls behave: a normal v1 journal passes both, and a v3 (future) journal is refused by both — the validator is alive; its first-entry test uses the wrong counter and its version range is missing the lower bound.

Why it matters

Doctor is the tool a user is told to run when a world will not open ("restore the journal from a backup, or recreate the world" is its own fix text). A check that says Ok for a journal restore will refuse — and Fail for one restore accepts — sends that user to recreate or restore over a working world, or to trust a broken one. Doctor's whole contract is that its verdict matches the opener's.

Acceptance criteria

  • validate_journal_file refuses a Created header with version 0 (lower bound JOURNAL_VERSION), matching WorldCore::restore's 1..=MAX_JOURNAL_VERSION range; versions 1 and 2 still pass, 3 still fails.
  • First-entry checks are made against the first parsed entry, not the raw line index: a journal with leading/interior blank lines and a Created first entry validates; a journal whose first parsed entry is not Created is refused regardless of blank lines.
  • Malformed-entry error messages may keep the raw line number (that is the useful diagnostic there).
  • New unit tests in world_doctor.rs pin all three cases (version 0, blank-line + Created, blank-line + headerless) alongside the existing journal test; existing doctor tests still pass.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    on Oct 28, 2026
  2. developing-today commented on Oct 28, 2026

    @developing-today
    Contributor
  3. added
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    work/self-assignedAn agent grabbed this item directly, outside the queue
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 28, 2026
  4. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

    • Agent: new-issue-creator-code-b (new-issue-creator job, Lane B)
    • Session / ID: new-issue-creator-code-b run 2026-10-28 00:20 CDT; filed this run
    • Found via: filed this run; self-assigned during new-issue-creator run under guarded trivial self-solve (lifecycle protocol §4 — small, clearly scoped, low-risk, easy verification; no safety labels)
    • Task: fix validate_journal_file in world_doctor.rs — version lower bound + first-entry checks against the first parsed entry, with pinning unit tests
    • Expectation: effort/xs; done = PR merged with the three probe cases flipped and world_doctor tests green on the laptop / CI
    • Links: probe ~/git/fleet/probe-2026-10-28-laneB-doctor (laptop); branch creator-b/1598-doctor-journal-first-entry
  5. added a commit that references this issue on Oct 28, 2026
  6. dezren39 commented on Oct 28, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: new-issue-creator-code-b (new-issue-creator job, Lane B)
    • Session / ID: new-issue-creator-code-b run 2026-10-28 00:20 CDT
    • Found via: filed this run; self-assigned during new-issue-creator run (guarded trivial self-solve, lifecycle protocol §4)
    • Task: fix validate_journal_file — version lower bound + first-entry checks against the first parsed entry, with pinning unit tests
    • Expectation: done — PR fix(id): make world doctor journal check match restore (#1598) #1600 merged (merge 63ba170, fix cd50501). Real-crate cargo test --lib world_doctor on the laptop: 12/12 passed including the new a_journal_version_zero_and_blank_lines_match_restore; rustfmt clean. PR CI was still QUEUED (runner backlog) at merge; merged under the standing directive on local verification.
    • Links: PR fix(id): make world doctor journal check match restore (#1598) #1600; probe ~/git/fleet/probe-2026-10-28-laneB-doctor (laptop)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobstatus:done

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions