Skip to main content
The previous page wrote a Memory you already accept. Now take the other path: capture a first, then let generation extract knowledge you will reuse later. Both paths may produce . Their premises, lineage, and failure modes differ. Two paths can leave Memory: write it, or capture then extract.
Generation models and extraction settings follow the latest published official docs. This page helps you see how the two paths differ, and how to check lineage.

Compare the two paths first

An explicit write keeps what you already decided to keep. Extraction stores a Source first, and it may produce no Memory. If the Agent already has an explicit instruction to remember a decision, write it directly. Do not invent a Source just to make the lineage look nicer. A prompt captured automatically should usually stay a Source first. Whether it deserves Memory is a later decision.

Check extraction capability

You need:
When it is disabled:
  • capture still succeeds;
  • the Source remains in the journal;
  • a pending Source window cannot run model extraction;
  • configure generation and restart the Server with --env-file.
Scheduling is a separate setting. The procedure calls flush_memory directly and does not depend on the periodic scheduler.

Complete lineage script

Create source_memory.py:
Run it:
Generation output is inherently variable. The tutorial uses an explicit durable decision to make a candidate likely, but it does not prescribe fixed model wording. The stable assertions are position, cursor, exact SourceRef, and entry lineage.

What to inspect at each step

Capture

Success is HTTP 202 with:
  • status="accepted";
  • an exact content SourceRef;
  • a numeric scope-local position.
A replay with the same identity and serialized payload bytes returns the original position. This idempotency check is not semantic canonicalization. Reordering metadata keys may change serialized bytes and produce source_conflict. A real retry should reuse the same serialized input. Changing content under the same identity must never overwrite the old Source. It returns HTTP 409.

Flush

Determine Source consumption from:
  • previous_cursor;
  • current_cursor;
  • processed_source_count;
  • the capture response position.
The current cursor must reach or pass the captured position. The scheduler may finish first, so a manual flush returning idle can be correct when the cursor has already reached the position.

Memory lineage

Do not rely on similar search text. Find an entry whose source_refs contains the exact SourceRef returned by capture. That proves the Memory entry came from this Source.

Extraction no-op

This is valid:
The model may produce no candidate, or deduplication may remove candidates identical to active content. The Source was consumed even though Memory did not change.

Use stricter assertions for vector verification

When capabilities.search_modes includes vector, you may add:
This lineage has completed an embedding round trip only when:
  • response mode == "vector";
  • a hit Citation identifies the source-linked entry from this run;
  • matched_by contains vector.
A hit for older Memory, an empty hit list, or mode=None proves nothing about this Source’s vector projection. An explicit vector request returns HTTP 422 when vector capability is unavailable. It does not fall back to FTS.

Cross-session recall depends on Scope

The script uses a random Scope for repeatable tests. To verify recall across sessions:
  1. replace it with a stable value such as project:powercontext-101;
  2. run capture and flush once;
  3. start a new Python process or agent session that runs only search and prepare;
  4. connect both processes to the same Server with exactly the same Scope.
If the second process finds nothing, check in this order:
  • Server URL;
  • database home or URL;
  • final scope_id;
  • whether the entry is active;
  • query relevance.
A session ID is not a project Scope.

Active, inactive, and history

An extracted entry uses the same lifecycle as explicit Memory:
  • revise creates another entry version;
  • retire marks the current entry inactive;
  • default listing, search, and PreparedContext use active content;
  • include_inactive=True and exact read preserve audit history.
Source lineage is stored with the entry version. Revision adds new evidence while exact historical evidence remains available on the old version.

Troubleshooting

Capture succeeded, but flush returns 503

Check generation capability and provider credentials. The Source remains stored, so the pending window may be retried after configuration is repaired.

Flush is processed, but no linked entry exists

This is an allowed no-op. Check whether the Source contains reusable knowledge, then inspect generation output and deduplication.

The cursor never reaches the position

Inspect readiness, generation diagnostics, and scheduler or flush errors. Do not report an unprocessed window as Memory.

Search has a hit, but lineage differs

The query found older similar Memory in the same Scope. Use source_refs and the exact entry Citation as proof.

The second capture conflicts

Content or metadata bytes changed under one source_id. Use a new ID for a new event; reuse the original input for a retry.

How you know it worked

After this page, you should be able to show that:
  • Capture creates a Source. It does not create Memory by itself.
  • The same input replayed keeps the original position.
  • After flush, the cursor reaches the capture position.
  • The entry linked to this Source is identified by exact source_refs.
  • A processed window may legally leave no Memory.
  • Finding it again in another process depends on a stable Scope.
  • An inactive entry leaves default recall. History remains.
If this does not match, start with Troubleshooting.

Next step

Handoff loop

Preparing writes a boundary Source. Commit creates the milestone.

If you are stuck

Capture succeeded, but search finds no Memory. Start there.