LearnNewsExamplesServices
Frontmatter
id16275
titleDigest latest ignores grantedAt and lastModifiedAt event times
stateClosed
labels
bugdocumentationai
assigneesneo-kimi-iris
createdAtAug 1, 2026, 6:38 PM
updatedAtAug 1, 2026, 7:42 PM
githubUrlhttps://github.com/neomjs/neo/issues/16275
authorneo-kimi-iris
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 1, 2026, 7:42 PM

Digest latest ignores grantedAt and lastModifiedAt event times

Closed Backlog/active-chunk-11 bugdocumentationai
neo-kimi-iris
neo-kimi-iris commented on Aug 1, 2026, 6:38 PM

Context

Post-merge review follow-up from @neo-opus-grace on PR #16265 (merged 2026-08-01T14:24:55Z — "compute digest latest by recency, not iteration position"), delivered via A2A (MESSAGE:6006822e-cdf4-4dea-898c-53435fe4fbe7): the JSDoc's unconditional "recency, never iteration position" overclaims. Verified at source before filing (V-B-A): two of the four digest buckets carry a true event-time field that the resolver never reads.

The Problem

resolveEventTimestamp() (ai/services/memory-core/CoalescingEngineService.mjs:57) reads payload.sentAt, then envelope emittedAt, else null (last-write-wins). The per-bucket field inventory, verified 2026-08-01 against the emitters:

Bucket True event time on the wire Read by the resolver?
sent_to_me payload.sentAt (the message's own send time) ✅ yes
task_state_changed payload.lastModifiedAt (canonical transition clock per #15106 / #15114; validated by parseTaskStateChangedTrace at ai/services/memory-core/heartbeatPulseEvaluator.mjs:153) ❌ no
permission_granted payload.grantedAt (delivery-time stamp; documented wire contract at ai/services/memory-core/WakeSubscriptionService.mjs:1428-1433) ❌ no
heartbeat_pulse none exists on the payload n/a — envelope emittedAt only

Consequence: under an out-of-order queue (replay batch, restart re-walk, multi-source evaluation — the exact cases PR #16265 targeted), the task_state_changed and permission_granted latest pointers resolve by wrap/arrival time (emittedAt), which equals position under in-order arrival — the stale-pointer failure class the PR closed for sent_to_me only. Separately, the _buildDigestEnvelope JSDoc (CoalescingEngineService.mjs:409) unconditionally claims every bucket's latest is "chosen by RECENCY (max timestamp), never by iteration position" — true only where a payload timestamp is actually read.

The Architectural Reality

  • Resolver: CoalescingEngineService.mjs:48-76; bucket loop :448-454; overclaiming JSDoc :402-421.
  • grantedAt is minted in WakeSubscriptionService.mjs:1431-1433, which "restores this documented wire-contract field in its own delivery path" — the contract exists; the digest side never consumes it.
  • lastModifiedAt is the graduated A2A transition clock (#15106 AC: "reports lastModifiedAt"), carried by the immutable typed GraphLog rows of #15114.
  • Decision Record impact: aligned-with ADR 0002 (wake substrate standards alignment §6.4 — the file's own @see).

The Fix

  1. resolveEventTimestamp(): read payload.sentAtpayload.grantedAtpayload.lastModifiedAt → envelope emittedAtnull. Each event type carries only its own field, so precedence among the three payload fields never collides in practice.
  2. JSDoc precision at _buildDigestEnvelope: per-bucket resolution truth — true event time where the payload carries one (sentAt / grantedAt / lastModifiedAt); heartbeat_pulse resolves by envelope emittedAt (wrap/arrival time); timestamp-less payloads keep last-write-wins. The unconditional "never by iteration position" claim goes.
  3. Spec (test/playwright/unit/ai/services/memory-core/CoalescingEngineService.spec.mjs): an out-of-order permission_granted pair reordered by grantedAt; an out-of-order task_state_changed pair reordered by lastModifiedAt; heartbeat_pulse falls back to emittedAt; timestamp-less keeps last-write-wins; existing sent_to_me recency tests run unmodified.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Digest breakdown.permission_granted.latest payload.grantedAt (wire contract, WakeSubscriptionService.mjs:1428-1433) latest = max grantedAt among candidates envelope emittedAt, then LWW JSDoc out-of-order unit spec
Digest breakdown.task_state_changed.latest payload.lastModifiedAt (#15106 graduated contract) latest = max lastModifiedAt among candidates envelope emittedAt, then LWW JSDoc out-of-order unit spec
Digest breakdown.heartbeat_pulse.latest envelope emittedAt (no payload clock exists) unchanged LWW when absent JSDoc (truthful per-bucket wording) pinned by unit spec
Digest breakdown.sent_to_me.latest payload.sentAt unchanged (PR #16265 behavior) envelope emittedAt, then LWW JSDoc existing specs, unmodified

Acceptance Criteria

  • grantedAt reorders an out-of-order permission_granted pair; falsifying test RED before the fix, GREEN after.
  • lastModifiedAt reorders an out-of-order task_state_changed pair; falsifying test RED before, GREEN after.
  • heartbeat_pulse and timestamp-less behavior unchanged (envelope emittedAt / last-write-wins), pinned by tests.
  • All existing #16265 specs pass unmodified (no sent_to_me regression).
  • The _buildDigestEnvelope JSDoc states the per-bucket resolution truthfully; no unconditional "never by iteration position" claim remains.

Out of Scope

  • Minting a payload timestamp for heartbeat_pulse (a wire-contract change — separate decision, not needed here).
  • Any WakeSubscriptionService change — the fields already exist on the wire.
  • Emmy's #16273 hydrated GraphService edge cache lane (claimed; different concern, no interaction).

Avoided Traps / Gold Standards Rejected

  • Reading log-plane anchors as event time (logId, GraphLog row time): logId is a cursor/catchup anchor, not a clock; the payload fields are the graduated contract.
  • Inventing a new generic payload.timestamp convention: the per-type fields already exist and are documented; the resolver only had to read them. A new field would duplicate contract surface for zero gain.

Related

  • #16263 (ticket behind PR #16265, the recency change this completes)
  • #15106 (graduated lastModifiedAt transition-clock contract)
  • #15114 (immutable typed task_state_changed GraphLog rows)
  • #16273 (Emmy's adjacent claimed lane — no interaction, named for collision hygiene)

Live latest-open sweep: checked latest 20 open issues (created-desc) at 2026-08-01T16:38Z; no equivalent found. A2A in-flight sweep (last ~60 min, all read-states): only #16273 / #15825 claim-class messages; no claim on this scope.

Origin Session ID: session_fdc69689-d147-442f-8e12-1a2bc72ae4ee

Retrieval Hint: query_raw_memories("digest latest recency grantedAt lastModifiedAt resolveEventTimestamp CoalescingEngineService")

Authored by Iris (@neo-kimi-iris, Kimi K3, Kimi Code CLI) 🌈

tobiu referenced in commit 6fd4844 - "fix(ai): read grantedAt + lastModifiedAt for digest latest (#16275) (#16277)" on Aug 1, 2026, 7:42 PM
tobiu closed this issue on Aug 1, 2026, 7:42 PM