ctx.memory-backed dedupe fails open, and gcp-watcher is very likely re-posting on every scan
Found while designing a new proactive agent, by reading the existing ones as reference. Two independent things combine into a live defect.
1. The substrate: ctx.memory.recall is semantic and returns nothing for opaque payloads
This is not inferred — it is documented in shipped code by the agent that hit it and worked around it.
internal-agents/market-intel/x-reply-radar/agent.ts:742-746:
ctx.memory.recall is SEMANTIC (Supermemory): it ranks stored items by how similar their content is to the query and ignores tags. Stashing opaque ids under a tag and recalling with generic text returned nothing — so the seen-set was always empty and every link looked fresh (the repeats).
And persona.ts:165-169, blunter:
saves succeed (return an id) but Supermemory /v3/search returns 0 even for a doc saved seconds earlier, so loadSeen has been coming back empty (seen:0 every run).
x-reply-radar abandoned memory-based dedup as a result and now relies on contiguous time windows.
2. gcp-watcher uses exactly the pattern that fails
agents/gcp-watcher/agent.ts:
:268 const [item] = await ctx.memory.recall('gcp snapshot', { tags: ['gcp-watcher:snapshot'], limit: 1 });
:277 await ctx.memory.save(JSON.stringify(snap), { tags: ['gcp-watcher:snapshot'], scope: 'workspace' });
:158 const signature = lines.slice().sort().join('\n');
:159 if (lines.length > 0 && signature !== last?.signature) { ...post... }
The query is the generic string gcp snapshot; the stored content is an opaque {"signature":"…"} blob. Content similarity between them is negligible, and tags are ignored. So recall returns nothing, last is undefined, signature !== last?.signature is always true, and it posts on every scan where any signal is firing.
Expected impact: repeated identical Slack alerts on every tick for as long as any GCP signal stays lit. Please confirm against the channel — if the alerts are repeating, this is the cause.
3. Why "fails open" is the important phrase
A dedupe that errors is safe: it announces itself. This one silently returns an empty seen-set, re-notifies everyone, and reports success. The agent looks healthy in every log while doing precisely the thing dedupe exists to prevent. Any agent adopting this pattern inherits it invisibly.
Also in the same file, independent of the above
agent.ts:158 computes one global signature over the whole alert set (lines.sort().join()) and posts if it changed. Acceptable for a handful of infra signals, but it means one new line re-posts every line. Any consumer scaling this pattern to a per-item domain gets a full re-broadcast on every single change.
Other call sites worth checking
internal-agents/github-inbox/agent.ts:260 — recalls with the bare tag string as the query, same shape. Also :120 has a silent 500-id cap.
internal-agents/.../hn-monitor — safe, apparently by accident: it recalls with the long descriptive sentence that is also embedded in the saved content (agent.ts:1539/1556), which is the workaround x-reply-radar arrived at by trial. Also backstopped by ctx.files.
Asked for
- Confirm whether the GCP Slack channel is showing repeats. That settles impact in one look.
- Move
gcp-watcher and github-inbox dedupe to ctx.files (exact keyed read/write), or to the descriptive-sentence recall pattern hn-monitor and x-reply-radar use.
- Make the failure mode loud. If a seen-set comes back empty when the agent has previously saved, that should be surfaced, not treated as "nothing seen yet". Absence of knowledge is not permission to notify.
- Longer-term: either fix
/v3/search cloud-side or document that ctx.memory must never back an idempotency decision. Today the same API name means "semantic recall" and is being read as "key-value lookup" by callers.
Filed by Chief on behalf of a design review. Source quotes verified directly against both repos. Not labelled for dispatch.
ctx.memory-backed dedupe fails open, andgcp-watcheris very likely re-posting on every scanFound while designing a new proactive agent, by reading the existing ones as reference. Two independent things combine into a live defect.
1. The substrate:
ctx.memory.recallis semantic and returns nothing for opaque payloadsThis is not inferred — it is documented in shipped code by the agent that hit it and worked around it.
internal-agents/market-intel/x-reply-radar/agent.ts:742-746:And
persona.ts:165-169, blunter:x-reply-radarabandoned memory-based dedup as a result and now relies on contiguous time windows.2.
gcp-watcheruses exactly the pattern that failsagents/gcp-watcher/agent.ts:The query is the generic string
gcp snapshot; the stored content is an opaque{"signature":"…"}blob. Content similarity between them is negligible, and tags are ignored. Sorecallreturns nothing,lastisundefined,signature !== last?.signatureis always true, and it posts on every scan where any signal is firing.Expected impact: repeated identical Slack alerts on every tick for as long as any GCP signal stays lit. Please confirm against the channel — if the alerts are repeating, this is the cause.
3. Why "fails open" is the important phrase
A dedupe that errors is safe: it announces itself. This one silently returns an empty seen-set, re-notifies everyone, and reports success. The agent looks healthy in every log while doing precisely the thing dedupe exists to prevent. Any agent adopting this pattern inherits it invisibly.
Also in the same file, independent of the above
agent.ts:158computes one global signature over the whole alert set (lines.sort().join()) and posts if it changed. Acceptable for a handful of infra signals, but it means one new line re-posts every line. Any consumer scaling this pattern to a per-item domain gets a full re-broadcast on every single change.Other call sites worth checking
internal-agents/github-inbox/agent.ts:260— recalls with the bare tag string as the query, same shape. Also:120has a silent 500-id cap.internal-agents/.../hn-monitor— safe, apparently by accident: it recalls with the long descriptive sentence that is also embedded in the saved content (agent.ts:1539/1556), which is the workaroundx-reply-radararrived at by trial. Also backstopped byctx.files.Asked for
gcp-watcherandgithub-inboxdedupe toctx.files(exact keyed read/write), or to the descriptive-sentence recall patternhn-monitorandx-reply-radaruse./v3/searchcloud-side or document thatctx.memorymust never back an idempotency decision. Today the same API name means "semantic recall" and is being read as "key-value lookup" by callers.Filed by Chief on behalf of a design review. Source quotes verified directly against both repos. Not labelled for dispatch.