LearnNewsExamplesServices
Frontmatter
id12862
titlegetLastSyncId: cursor ahead of MAX(log_id) → permanent wake silence
stateClosed
labels
enhancementaiarchitecturemodel-experience
assigneesneo-gpt
createdAtJun 10, 2026, 9:47 PM
updatedAtJun 12, 2026, 9:19 PM
githubUrlhttps://github.com/neomjs/neo/issues/12862
authorneo-opus-vega
commentsCount2
parentIssue12849
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 12, 2026, 9:19 PM

getLastSyncId: cursor ahead of MAX(log_id) → permanent wake silence

Closed v13.1.0/archive-v13-1-0-chunk-1 enhancementaiarchitecturemodel-experience
neo-opus-vega
neo-opus-vega commented on Jun 10, 2026, 9:47 PM

Context

Surfaced by @neo-fable's /pr-review APPROVE of PR #12855 (Resolves #12849 — the wake-cursor flood). #12855 made getLastSyncId fail a corrupt/empty/negative cursor to MAX(log_id) (never 0). fable flagged the inverse failure as a hypothesis-tagged, non-blocking follow-up.

Live latest-open sweep: latest 15 open issues checked ~2026-06-10T19:43Z; no equivalent (#12849 = the flood/zero side, resolved by PR #12855; #12850 = digest over-count; this is the cursor>max silence side).

The Problem (hypothesis — needs V-B-A first)

Post-#12855, getLastSyncId trusts any parsed >= 0, including a cursor ahead of MAX(log_id). If a cursor file with a high value SURVIVES a GraphLog rebuild/reset (fresh data dir with a stale cursor, or a future id-compaction), then cursor > MAX(log_id)getGraphLogEntries's WHERE log_id > <huge> returns nothing → permanent wake silence until new ids catch up to the stale cursor. This is the inverse of #12849's flood — quieter and harder to notice (agents simply stop being woken).

V-B-A REQUIRED before implementing (per the hypothesis tag)

Confirm whether any real path produces cursor > MAX(log_id) — candidates to verify:

  • the memory-core restart wave (#12844),
  • a GraphLog compaction / rebuild,
  • the #12830 corrupted-memory cleanup,
  • any DB re-init that resets log_id while the lastSyncId file survives.

If no path can produce it, this is a non-issue → close. If reachable, the fix is ~3 lines.

The Fix (conditional on the V-B-A)

Clamp the resume cursor to the log tip in getLastSyncId (ai/daemons/wake/queries.mjs): Math.min(parsed, getMaxLogId(db)). A cursor ahead of MAX self-heals to the tip — symmetric with #12855's fail-to-tip-on-corruption. Add a regression in queries.spec.mjs: cursor>max → returns MAX(log_id).

Decision Record impact

none. Extends the "fail to the safe tip — never replay-from-0, never silence-past-tip" principle PR #12855 established for the wake cursor.

Acceptance Criteria

  • AC1 (V-B-A): determine whether any rebuild/reset path can leave lastSyncId > MAX(log_id). Document the finding (reachable + how, or not-reachable → close).
  • AC2 (conditional): if reachable, getLastSyncId clamps to Math.min(parsed, getMaxLogId(db)); a cursor>max resumes at the tip.
  • AC3 (conditional): queries.spec.mjs regression — cursor>max → MAX(log_id); existing branches stay green.

Out of Scope

  • The corrupt/empty/negative → 0 → flood side (resolved by PR #12855).
  • The digest over-count (#12850).
  • The cosmetic parseInt('13garbage') → 13 note from the same review — fable assessed it unreachable via integer-file truncation + narrowed by the atomic write (hygiene, not defense); not pursued.

Related

  • #12849 — the flood (zero) side; this is the inverse (cursor>max) side, same getLastSyncId.
  • #12850 — sibling wake-digest reliability bug.
  • #12844 — restart wave (a candidate cursor>max trigger to V-B-A).

Origin: PR #12855 /pr-review by @neo-fable, 2026-06-10 (hypothesis-tagged, needs-V-B-A). Retrieval Hint: "wake cursor ahead of MAX log_id permanent silence clamp Math.min getLastSyncId rebuild". Authored by @neo-opus-vega (Claude Opus 4.8).

tobiu referenced in commit d48ace4 - "fix(wake): clamp stale-high wake cursors (#12862) (#13010)" on Jun 12, 2026, 9:19 PM
tobiu closed this issue on Jun 12, 2026, 9:19 PM