LearnNewsExamplesServices
Frontmatter
id17087
titleTwo implementations of the same timestamp guard live side by side
stateClosed
labels
buggood first issueairefactoringagent-os
assignees[]
createdAtAug 14, 2026, 2:21 AM
updatedAtAug 14, 2026, 9:16 AM
githubUrlhttps://github.com/neomjs/neo/issues/17087
authorneo-opus-ada
commentsCount1
parentIssue17072
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 14, 2026, 9:16 AM

Two implementations of the same timestamp guard live side by side

Closed Backlog/active-chunk-16 buggood first issueairefactoringagent-os
neo-opus-ada
neo-opus-ada commented on Aug 14, 2026, 2:21 AM

Context

PR #17077 (closing #17076) guarded the summaries projections against a per-row timestamp defect that was failing whole query_summaries calls. PR #17083 (closing #17082) then fixed the identical defect on the memory surfaces, and put the guard in a new shared moduleai/services/memory-core/helpers/resolveRowTimestamp.mjs.

Both are merged. dev therefore now carries the same guard twice:

  • ai/services/memory-core/helpers/resolveRowTimestamp.mjs — the shared module, consumed by MemoryService.listMemories / queryMemories and conceptWalkMemoryGate.
  • ai/services/memory-core/SummaryService.mjs:156static resolveSummaryTimestamp(metadata), byte-equivalent logic, consumed at SummaryService.mjs:361 and :527.

This duplication was deliberate and is recorded as such in PR #17083's ## Deltas from ticket: at the time, #17077 was already approved at its head, and amending it to share the helper would have invalidated that approval for a refactor that could land afterwards. That constraint is now gone.

The Problem

Two implementations of one guard is the exact shape that let the original defect exist in two places: the summaries surface and the memory surface each carried their own unguarded projection, and fixing one taught nobody about the other. Re-introducing that shape — in the repair for it — is worth closing rather than living with.

There is no behavioral bug today. Both implementations are correct and both are covered:

  • test/playwright/unit/ai/services/memory-core/SummaryService.TimestampGuard.spec.mjs
  • test/playwright/unit/ai/services/memory-core/MemoryService.TimestampGuard.spec.mjs

The cost is future drift: the next person to change one (say, to alter the null → epoch-0 parity) has no signal that a second copy exists.

The Architectural Reality

  • resolveRowTimestamp is already generic — it takes any Chroma metadata row and is not summary- or memory-specific. Nothing about it needs to be per-service.
  • SummaryService.resolveSummaryTimestamp is a static on the class, reached via this.constructor.resolveSummaryTimestamp(...); the shared version is a plain named export. Both call sites in SummaryService are inside result .map() projections.
  • The JSDoc on both carries the same two non-obvious contracts: absent and unparseable collapse to the same null, and new Date(null) is epoch 0 so null-valued timestamps still project as 1970. Whichever survives must keep that prose — it is the part a future reader needs.

The Fix

  1. Import resolveRowTimestamp in SummaryService.mjs and use it at both projection sites (:361, :527).
  2. Delete the static resolveSummaryTimestamp method and update the two @returns JSDoc blocks that reference it by {@link} (:231, :417) to point at the shared helper.
  3. Update SummaryService.TimestampGuard.spec.mjs's unit-level arm, which currently calls the static directly, to exercise the shared helper.
  4. Keep behavior byte-identical — this is a consolidation, not a semantics change. Both existing specs must stay green without modification to their assertions.

Acceptance Criteria

  • SummaryService has no local timestamp-resolution method; both projections call the shared resolveRowTimestamp.
  • grep -rn "resolveSummaryTimestamp" ai/ test/ returns no hits.
  • Both TimestampGuard specs pass unchanged in their behavioral assertions (only the direct-helper-reference arm may be retargeted).
  • The null → epoch-0 parity and the absent-vs-unparseable contract remain documented on the surviving helper.

Out of Scope

  • Any change to what the guard does. The null → epoch-0 behavior in particular is pre-existing and deliberately preserved; narrowing it is a corpus-data decision, not part of this consolidation.
  • The other Memory Core timestamp projections already surveyed and found safe (MemoryCoreRecorderService projects SQLite epoch integers where NULL coerces rather than throws — see #17082).
  • Backfilling the malformed corpus rows bracketed on #17076.

Avoided Traps

  • Move the helper into SummaryService instead. Wrong direction: MemoryService and conceptWalkMemoryGate already consume the shared module, so collapsing toward the service would create two importers of a service singleton's static.
  • Delete one copy without moving the JSDoc. The contracts about absent-vs-unparseable and the epoch-0 parity are the load-bearing part; a bare function with the prose dropped loses what stops the next person from "fixing" the 1970 behavior.

Note for a first-time contributor

This is a good self-contained first issue: the target is two call sites and one deletion, the behavior must not change, and both surfaces already have full test coverage to prove you did not change it. Run:

npm run test-unit -- test/playwright/unit/ai/services/memory-core/SummaryService.TimestampGuard.spec.mjs test/playwright/unit/ai/services/memory-core/MemoryService.TimestampGuard.spec.mjs

Unlike #17076 and #17082, this one needs no running Agent OS plane — the specs use in-memory fixtures.

Duplicate and Collision Sweep

  • Live latest-open sweep at 2026-08-14T00:20Z: checked the latest 20 open issues (#17042-#17081); no equivalent owner. Vocabulary sweep across state=all for resolveSummaryTimestamp, resolveRowTimestamp, and duplicate-helper phrasing surfaced only the two closed parents.
  • A2A in-flight claim sweep at the same timestamp over the latest messages, all read-states: no claim on this scope.
  • Structure map: owning folder ai/services/memory-core — no new or relocated .mjs, so structural pre-flight does not fire.

Related

Related: #17076

Related: #17082

Origin Session ID: 4ad778d4-bdc6-44cc-b6ec-7ef2c9e7af03

Retrieval Hint: "resolveSummaryTimestamp resolveRowTimestamp duplicate guard consolidation SummaryService"

— Ada (@neo-opus-ada) ⚖️

tobiu added parent issue #17072 on Aug 14, 2026, 2:30 AM
dchaudhari7177 cross-referenced by PR #17095 on Aug 14, 2026, 8:24 AM
tobiu closed this issue on Aug 14, 2026, 9:16 AM