Skip to content

Preserve duplicate-record refusal across incomplete recovery tails #173

Description

@flyingrobots

Outcome and invariant

Keep demonstrated duplicate record identities as a typed refusal when a segment stage ends in an incomplete record header, record payload or seal. This is a separate T-13.2 / KEEP-RECOVERY-010 finding under #131: appending incomplete bytes must not turn previously demonstrated corruption into authority to discard evidence.

Verified main failure

On main 6051abb25a9fd33ae7ee0de5614514b709a4d82a, construct a canonical segment header followed by two exact copies of the one-zero record. The header occupies 64 bytes; each record occupies 145 bytes. With no tail, the public classifier correctly refuses DuplicateRecordIdentity at indexes 0/1 and offsets 64/209.

Append any of these tails and the same classifier instead admits truncation:

  • One byte of a next record header: TailHeader { record_index: 2, offset: 354, required: 112, observed: 1 }.
  • A canonical record header plus partial payload: Record { record_index: 2, offset: 354, expected: 145, observed: 120 }.
  • The 16-byte seal magic: Seal { offset: 354, required: 128, observed: 16 }.

The copied-Docker public probe fingerprints each exact corrupted stage, admits its bytes, assesses it, and successfully obtains plan_recovery_stage_discard for all three. It compiles and fails at known duplicate corruption escaped as discard plans. No filesystem removal was executed; the demonstrated failure is authorization, not an assertion that data was actually deleted.

recovery_segment_classifier.rs validates the identity index only in admit_reusable; recognized partial seals return earlier, and truncated record/header errors return through classify_cursor_error without checking already-complete records.

Acceptance

  • Every truncation-return path preserves duplicate refusal for preceding complete admitted records, with exact identity/index/offset diagnostics.
  • Preserve canonical incomplete-stage classifications, existing corrupt-framing diagnoses, record/resource limits, and bounded allocation; do not silently repair duplicates.
  • Public classifier and fingerprint-bound assessment/planning regressions fail on the unfixed revision and pass with the correction. Include the no-tail control and all three demonstrated tail classes, plus a bounded generated record/tail sweep.
  • Complete stage and reusable-prefix duplicate handling remain correct. Define diagnostic precedence where a tail also contradicts framing, without hiding the corruption or emitting a discardable state.
  • Replay the class through the existing recovery fuzz path when available; retain counterexamples. Do not substitute seed counts or source-text tests for runtime evidence.
  • Update current requirements/evidence, finish independent exact-head review and required checks.

Scope and prerequisites

One coherent duplicate-invariant correction, branched from origin/main. No prerequisite on #171/#172: that PR fixes a different incomplete-seal fixed-framing contract, and these failures exist with canonical partial seal magic. Shared classifier code alone is not a dependency; integration must preserve both fixes. No new namespace, on-disk format, complete-future-prefix feasibility campaign, or reopening of #99's separate v2 incomplete-retention-stage disposition decision. The safe merged state refuses known duplicates before discard planning while preserving noncontradictory existing recovery outcomes.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions