Skip to content

reRootOrphanedChains: exhausting maxPasses is indistinguishable from a completed repair #1167

Description

@lilyshen0722

Raised as a non-blocking note during the #1161 gate (pod 57632) and shipped unaddressed, which is fine — it was non-blocking — but it now lives only in a merged PR's pod message, so filing it where it survives.

Measured on origin/main at cde4d8e5, 05:35:50Z.

The behaviour

Message.reRootOrphanedChains (backend/models/pg/Message.ts:346) converges by breaking when a pass finds nothing:

static async reRootOrphanedChains(maxPasses = 32): Promise<{ reRooted: number; passes: number }> {
  for (let i = 0; i < maxPasses; i += 1) {
    // ... SELECT one level, UPDATE by id ...
    const n = rows.length;
    passes += 1;
    reRooted += n;
    if (n === 0) break;
  }
  return { reRooted, passes };
}

If the loop instead exits by exhausting maxPasses, un-rooted rows remain and the return value is indistinguishable from a completed repair. The caller (deleteOlderThan, :412-419) logs:

[pg-retention] re-rooted N orphaned reply row(s) in 32 pass(es) after deleting D

which reads as success. A caller cannot detect the difference either: { reRooted, passes } carries no flag, and passes === maxPasses is ambiguous — a chain of exactly depth 32 converges in 32 passes and breaks legitimately.

Reachability

The doc comment at :331 states the convergence property: "depth d converges in d passes." So the threshold is a reply chain deeper than 32. Unlikely in a chat pod, and the deeper the chain the more the repair matters — which is the wrong way round for a silent cap.

Why it is worth a line

The failure direction is safe: unrepaired rows render expanded, which is noisy and non-destructive — the same argument that justifies the catch in deleteOlderThan. So this is not urgent. But the whole point of that function is to leave no orphans behind, and right now a partial repair reports as a total one, on the only surface anybody watches.

Suggested fix

Distinguish the two exits, e.g. return { reRooted, passes, converged } where converged is set by the break, and have the caller say so when it is false. One line in each place.

Not verified

I have not constructed a >32-deep chain to observe the truncation. This is read from the loop's structure, not reproduced. The live orphan count is currently 0 (measured 04:23Z, instance-wide, edges=250 rooted=250), so nothing is being truncated today.

Context: #1161 (TASK-043) merged at 7f677235, 05:18:01Z. Tests at retentionReRoot.test.js cover convergence (:102, "a deeper chain converges, one level per pass") but not exhaustion.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions