It counts the memories the cycle deleted, and reads the number back nowhere

Syke

A local memory agent that watches your other agents' sessions and rewrites one MEMEX projection each cycle, wrapping every LLM mutation of its graph in a fingerprinted recovery point and a gate that forbids rewriting a creation timestamp — while computing, and discarding, the count of memories the cycle removed.

Carries 0 of 7 rubric mechanisms. Most systems here carry none or one (41%), and a dash means the mechanism was not found at this commit — not that the system needed it. Each mark is one LLM reviewer's reading of the code at this commit rather than a run of it — known limits.

  • Tombstone
  • Trust state
  • Bi-temporal
  • Scope enforced
  • Mutation audit
  • Human review
  • Negative evals

1. Executive Summary

Syke is "a local memory agent that works with your other AI agents" — AGPL-3.0, Python 3.12+, 32,408 lines with 11,539 lines of tests. It runs as an ambient daemon, reads local agent activity across supported harnesses — adapters ship for Claude Code, Codex, Cursor, Copilot, opencode, Hermes, Pi and Antigravity — and "maintains a coherent timeline of objects, intent, and progress in prose", serving it as MEMEX.md plus syke ask, syke record and syke memex.

The data model is deliberately small. A memory is an id, a user id, prose, a created-at and an optional updated-at. A link joins two memories and carries a free-text reason. There is one current MEMEX, enforced as a singleton by a primary-key CHECK. That is the whole graph.

Which means the interesting engineering is not the schema — it is that an LLM rewrites this graph on a schedule, unsupervised, and Syke has thought hard about what that can be allowed to do.

Every synthesis cycle is bracketed. capture_baseline fingerprints every memory and link — id, content hash, created-at — and the current MEMEX. create_recovery_point clones the database, preferring a copy-on-write clone where the filesystem supports one and falling back to SQLite's backup API, and runs integrity checks on the copy before accepting it. A synthesis lock prevents two cycles at once, a recovery fence is published, and if the process dies mid-cycle try_reconcile_before_database_use reconciles the interruption before anything touches the database again.

Then the cycle runs, and validate_state_after_cycle decides whether to keep it. Its invariants are the right ones:

  • a pre-existing memory's created_at may not change, nor a link's, nor the MEMEX's — the agent may revise what it believes, not when it first knew it;
  • no link may reference a missing memory;
  • the identity must remain a singleton, and no row may exist outside it;
  • the FTS index must match the memories table;
  • exactly one current MEMEX must survive, and it must not be empty.

A failure produces a rejection marked repairable, and the cycle is retried rather than discarded.

Twelve lines into that function's diff section, the gate computes this:

stats["memories_removed"] = len(removed_memory_ids)
stats["removed_memory_ids"] = removed_memory_ids

and never mentions either again. There is no issues.append for removed memories anywhere in the function, and memories_removed and removed_memory_ids appear nowhere else in the package. The synthesis backend reads semantic_gate.get("valid", False), which is derived from issues; the stats ride along in the result and are consulted by nothing.

So a cycle that deletes ninety per cent of the graph is valid. The recovery point taken minutes earlier means the state is restorable — that is the part Syke got right, and most systems here do not have it — but nothing notices that it should be restored, and the number that would notice has already been computed and put in a dictionary.

2. Mental Model

A memory is prose with a first-seen time. That time is immutable.

A link is a typed-by-prose edge, and ON DELETE RESTRICT means the graph will not let a linked memory vanish quietly.

A cycle is one LLM pass over the graph, bracketed by a fingerprint, a database clone and a gate.

The MEMEX is the single projection every other agent reads.

Diagram — every LLM cycle is bracketed by a fingerprinted baseline and a database clone, and gated afterwards on invariants that forbid rewriting a creation time but say nothing about how much was deleted
Diagram source
%% caption: every LLM cycle is bracketed by a fingerprinted baseline and a database clone, and gated afterwards on invariants that forbid rewriting a creation time but say nothing about how much was deleted
flowchart TB
    OBS["ambient daemon observes local<br/>harness sessions — adapters for<br/>Claude Code, Codex, Cursor,<br/>Copilot, opencode, Hermes, Pi"] --> CYCLE
    REC["syke record"] --> CYCLE
    LOCK["acquire_synthesis_lock"] --> CYCLE
    BASE["capture_baseline —<br/>fingerprints every memory and link:<br/>id, content hash, created_at"] --> CYCLE
    RP["create_recovery_point —<br/>copy-on-write clone if the<br/>filesystem allows, else SQLite<br/>backup; integrity-checked"] --> CYCLE
    CYCLE["LLM synthesis cycle<br/>mutates the graph"] --> GATE{"validate_state_after_cycle"}
    GATE -->|"created_at changed on a<br/>pre-existing memory / link / memex"| FAIL["issue → rejection<br/>(repairable, retried)"]
    GATE -->|"link references a missing memory"| FAIL
    GATE -->|"identity not a singleton, or<br/>rows outside the identity"| FAIL
    GATE -->|"FTS does not match memories"| FAIL
    GATE -->|"zero, duplicate or empty MEMEX"| FAIL
    GATE -->|"memories_removed = N,<br/>removed_memory_ids = [...]"| STATS["recorded in stats"]
    STATS -.->|"no issues.append;<br/>no reader anywhere in the package"| NOWHERE["nothing consults it —<br/>a cycle that deletes most of<br/>the graph is 'valid'"]
    GATE --> V{"valid?"}
    V -->|"yes"| ACCEPT["accept; write the MEMEX"]
    ACCEPT --> HIST[("memex-history — immutable,<br/>content-hashed, receipt-linked.<br/>Versions of the PROJECTION,<br/>not a record of the mutations")]
    ACCEPT --> PROJ["MEMEX.md + syke ask / memex"]
    CRASH["process dies mid-cycle"] -.-> RECON["try_reconcile_before_database_use<br/>reconciles before the next use"]
    RECON -.-> RP

3. Architecture

Area Role
syke/db.py The four-table schema, the FTS index, the CHECK constraints
syke/db_safety.py Recovery points, baselines, the gate, interrupted-cycle reconciliation
syke/db_access.py The database lease and lock
syke/llm/backends/pi_synthesis.py The cycle, and what it does with the gate's verdict
syke/memory/memex*.py The projection, its token budget, and its immutable history
syke/observe/ Harness adapters and the session catalogue
syke/distribution/ Getting MEMEX.md to the other agents
syke/control.py Write-once receipts and admitted external records

4. Essential Implementation Paths

syke/db_safety.py:948-1176 — the gate. Read the issue conditions as a list and then look for the one that is missing.

syke/db_safety.py:252-513 — baseline capture and recovery-point creation, including the copy-on-write attempt and the integrity checks on the clone.

syke/llm/backends/pi_synthesis.py:1371-1400 — where the verdict is consumed, and where the stats are not.

5. Memory Data Model

Four tables and an FTS index, with more CHECK constraints than most schemas here carry: ids must be non-empty after trimming, syke_identity and current_memex are singletons by primary-key constraint, and both link endpoints are foreign keys with ON DELETE RESTRICT.

That last one is a real decision. It means the database itself refuses to leave a dangling edge, so the gate's links reference missing memories check is a belt beside a brace — and it means a synthesis cycle that wants to delete a linked memory has to remove the link first, an act the baseline diff will see as removed_link_ids.

What a memory does not carry is any epistemic state: no status, no confidence, no source, no supersession pointer. updated_at records that a revision happened, not what it replaced. The link's reason is free prose. So the graph holds what is currently believed and, apart from the immutable created_at, nothing about how it came to be believed — which is why this report carries no marks despite the care visible in the safety layer.

6. Retrieval Mechanics

FTS5 with a porter tokenizer over memory content for syke ask, and the whole MEMEX.md for everything else. The projection is token-budgeted with an "exact, reproducible budget header" and an over_budget flag, which is the right shape: the consumer can see that the projection was truncated rather than guessing.

7. Write Mechanics

Two paths. syke record takes an explicit statement from a person or an agent. The synthesis cycle is the interesting one: it reads observed harness activity and rewrites the graph and the projection in one LLM pass, under the lock, inside the bracket described above.

The bracket is genuinely good work. Cloning the database before an unsupervised LLM mutation, checking the clone's integrity before trusting it, fingerprinting the prior state, taking a lock, publishing a fence, and reconciling an interrupted cycle before the next database use — that is more operational care than most memory systems in this corpus apply to anything.

Which makes the gap conspicuous rather than careless. Everything needed to bound deletion is present: the baseline fingerprints, the diff, the count, the ids, and a restorable snapshot. The missing piece is three lines — a threshold, an issues.append, and a number in a config.

8. Agent Integration

Adapter seeds for eight harnesses, a distributed MEMEX.md, and a CLI the other agents are told to call. Syke's position is that it is the memory agent and the others are its clients, which is a cleaner separation than embedding memory in each harness — and it means the gate above is the single place where quality control over the shared memory has to happen, since no client is checking.

9. Reliability, Safety, and Trust

The safety layer is the report's subject, so the summary is short: it is thorough about crashes and structure, and silent about volume.

One more asymmetry is worth noting. memex_history.py is "[i]mmutable, receipt-linked history for accepted MEMEX content", written once, content-hashed with sha256. So every accepted version of the projection is kept. The graph-level change set — created, revised and removed ids for memories and links, assembled by the gate as stats["graph_change"] — is persisted nowhere.

That is the difference between versioning an artifact and recording what changed, and it is why audit_log is withheld here: the history answers "what did the MEMEX say on Tuesday", not "which memories were removed, when, and by which cycle." The second question is answerable from data the system already computes.

10. Tests, Evals, and Benchmarks

11,539 lines of tests across the daemon, IPC, metrics, state, CLI contract, auth flow, prompt building and adapter guides. test_build_prompt.py asserts that internal failure strings — "private history failure", "private database failure" — do not reach the model's view, which is a good instinct about what a prompt should not contain.

There is no test file named for the safety layer, and nothing asserts what the gate does with a cycle that deletes memories — which is consistent with the behaviour being unspecified rather than chosen.

11. For Your Own Build

Bracket unsupervised mutation. Fingerprint before, snapshot before, gate after, reconcile on restart. Syke's version of this is worth copying whole, including the copy-on-write attempt with a portable fallback and the integrity check on the snapshot before it is trusted as one.

Make creation time immutable and check it. "A pre-existing memory's created_at changed" is a sharp, cheap invariant that catches a whole class of LLM misbehaviour — an agent rewriting history rather than revising belief — and almost nothing else in this corpus asserts it.

Then bound the destructive direction too. If you have already computed how many records a cycle removed, decide what number is too many. A gate that lists every way the graph can be malformed and no way it can be emptied is answering the question it is easiest to answer.

And if you keep an immutable history, consider whether it records the artifact or the change. Versions of the output tell you what you believed; the change set tells you what happened, and it is usually the one you want at three in the morning.

12. Open Questions

Whether unbounded deletion is intended. Forgetting is a legitimate function of a synthesis agent, and a cycle that removes a stale memory is doing its job; what is unspecified is where that stops. Nothing in the tree records a decision.

What syke ask does when the FTS index and the graph disagree between cycles. The gate repairs and rebuilds the index at cycle end, but reads happen continuously.

Whether the MEMEX history is ever read back. It is written once and hashed; no consumer was traced here.

Appendix: File Index

Path What to read it for
syke/db_safety.py:948-1176 The gate's invariants, and the count it does not use
syke/db_safety.py:252-513 Baseline fingerprints and integrity-checked recovery points
syke/db.py:70-115 Four tables, singleton CHECKs, and ON DELETE RESTRICT
syke/llm/backends/pi_synthesis.py:1371-1400 The verdict consumed, the stats ignored
syke/memory/memex_history.py An immutable history of the projection
syke/observe/seeds/ Adapter seeds for eight harnesses

History

2026-09-1662c1c9cf… — first reading, at a commit dated 23 August 2026. Screened before opening, from a shallow clone: six files scanned, no auto-run surfaces, three build-time execution points, no unpinned surfaces and nothing inside the dependency cooldown. Nothing was installed, built or run.