1. Executive Summary
PLUR is a local-first shared memory for coding agents: the agent
writes plain-text engrams into ~/.plur/engrams.yaml, which
is the source of truth, and SQLite, PGLite or Postgres attach only as an
index the code describes as a cache rather than truth. What sets it
apart is a commitment level on every engram, where draft
keeps a lesson retrievable and searchable but out of the model's
context. Its weak points come after that: nothing in this tree can
approve a draft, the direct correction path overwrites without a record,
and a retired engram is deliberately invisible to the dedup that would
stop it coming back.
Five of seven marks, and the one that decides the character
of the system is trust_state. An engram carries a
commitment — exploring, leaning, decided, locked or draft —
and skipForApproval returns true for draft
(packages/core/src/inject.ts:172-173), which the injection
loop uses to skip that engram in both its selection pass and its
spreading-activation pass (:657, :697). A
draft is still retrievable and still searchable; it just never reaches
the model's context. The producer is agent-reachable:
plur_learn takes a commitment argument that
flows into the persisted shape, and feedback cannot launder the value
because the transition function returns an unrecognised state
untouched.
The other four. bitemporal on a temporal block the
schema calls bi-temporal anchoring, with valid_from and
valid_until beside learned_at and
ingested_at, filtered by the retrieval path and the
injection gate independently of the record timestamps.
scope_enforced on a scope with two read-side predicates,
the stricter of which is exact membership carrying an explicit rule in
the source that an empty permitted list must match nothing — "a
principal with no permitted scopes must see nothing. NEVER treat an
empty list as 'no filter'". audit_log on an
append-only monthly JSONL of twenty-one mutation event types, fsynced,
in the store's own directory. negative_eval on three cases
asserting a draft absent from a populated injection, one of them through
spreading activation.
Four findings sit against the design. A retired engram cannot
stop itself returning, on purpose. _hashDedup
matches only active rows (packages/core/src/index.ts:1833),
and the codebase frames that as a feature twice over — "its content
hash cannot resurrect it" — with a committed test titled
"retired engrams are excluded from dedup — new write creates new
engram" asserting the new id differs
(packages/core/test/reference-count.test.ts:149-155). That
is a deliberate design choice and it is the reason
tombstone is withheld.
Nothing here can approve a draft. The schema comment
points at a separate enterprise repository for the review-queue write
sites, and the shipped dashboard is read-only, so draft is
a one-way state in this tree.
The direct correction path destroys what it
replaces. _updateEngramReturning overwrites the
row in place (:5923) with no history event and no version
bump, while the two model-mediated paths both preserve the prior
statement in a history event.
The benchmark harness is not here. The README says the harness is published so every number can be reproduced, and names the separate repository that holds it six lines later; a commit in July removed 2,238 lines including the corpus importer from this tree.
2. Mental Model
A memory is a sentence the model wrote and a set of judgements about it. The statement is stored verbatim — the only rewriting on the main path strips tool-call envelope artifacts and derives an eighty-character summary. Everything else on the engram is metadata about that sentence: who asserted it, how it was arrived at, how sure the author is, when it holds, and who may see it.
Two independent gates decide what reaches the model. Status decides whether an engram is retrievable at all; retired ones are not. Commitment decides whether a retrievable engram may be injected; drafts are not. The separation is the design: a draft can be found, cited in a review and argued about without ever being presented to the model as something it knows.
Forgetting is a reference count. Each repeat of the same content increments a counter; forgetting decrements it, and only at zero does the engram retire — with its full text still in the file.
Diagram source
%% caption: the model authors a statement, a content hash absorbs a repeat against active engrams only, and the engram lands in engrams.yaml with an audit event appended; retrieval filters on status, scope and validity while injection additionally skips drafts, and forgetting decrements a reference count that retires at zero without removing the row or blocking the same text being written again
flowchart TB
AG["the model writes a statement<br/>plur_learn"]
HASH{"content hash matches<br/>an ACTIVE engram?"}
BUMP["reference count incremented,<br/>duplicate recorded"]
NEW["new engram: scope, commitment,<br/>validity window, attribution"]
YAML[("engrams.yaml — the source of truth")]
HIST[("history/YYYY-MM.jsonl<br/>append-only, fsynced,<br/>never synced to a team store")]
IDX[("optional index:<br/>SQLite, PGLite or Postgres<br/>a cache, not truth")]
READ["retrieval: BM25, optionally<br/>fused with a local embedder"]
F1{"status active?<br/>scope visible?<br/>currently valid?"}
OUT["returned to a search"]
F2{"commitment is draft?"}
INJ["injected into context"]
HELD["retrievable, never injected —<br/>and nothing here can approve it"]
FORGET["plur_forget: decrement"]
RET["at zero: status retired,<br/>row and text remain"]
AGAIN["the same text written again<br/>creates a NEW engram"]
AG --> HASH
HASH -- yes --> BUMP
HASH -- no --> NEW
NEW --> YAML
NEW --> HIST
YAML --> IDX
YAML --> READ
READ --> F1
F1 --> OUT
OUT --> F2
F2 -- no --> INJ
F2 -- yes --> HELD
YAML --> FORGET
FORGET --> RET
RET --> AGAIN3. Architecture
Ten packages. core (39,629 lines) holds the schema, the
stores, retrieval, injection, feedback, tensions, packs and sync.
cli (12,532) and mcp (5,625) are the two human
and agent surfaces; dsh, claw,
ui, migrate are smaller, and three Python
packages adapt the store for Hermes and LangChain.
The storage layer is deliberately layered:
YamlPrimaryStore reads and writes the whole engram array,
and the indexed stores mirror it into a table whose data
column holds the engram as JSON or JSONB with the queryable fields
promoted into columns. The scope filter is pushed down into each of them
rather than applied after loading.
Deployment and ergonomics
- What has to run: Node. Nothing else — BM25 needs no model, and the ONNX embedder is optional and local.
- Fully local and offline: yes on the default path; the batch-dedup and failure-report paths call a model.
- Hand-repairable: entirely. The store is YAML a person can read and edit, and the indexes rebuild from it.
- Install: npm packages per surface, plus plugin manifests for two agent runtimes.
4. Essential Implementation Paths
- Learn.
packages/mcp/src/tools.ts:1125sanitises the statement (:1245), checks the pin quota and calls into the core.Plur.learn(packages/core/src/index.ts:2850) asserts writability, resolves the validity window (:2858), takes the store lock, runs the content-hash dedup (:2901-2904), mints an id, builds the shape (:2948), appends (:3119), syncs the index (:3123), appends the history event (:3124-3129) and writes provenance. - Dedup.
_hashDedup(:1829-1838) matches onstatus === 'active'and the content hash; a hit increments the reference count instead of writing. - Retrieve.
packages/core/src/fts.ts:448-488scores BM25 with k1 1.2 and b 0.75;packages/core/src/hybrid-search.ts:135-197runs the lexical leg on a rewritten query and the embedding leg on the original, widening both limits, and merges by reciprocal rank at k=60 (:63-80). - Filter.
Plur._filterEngrams(:4777-4878) applies status (:4837), the scope allow-list (:4843-4844), a domain prefix, scope visibility (:4861), temporal validity (:4872) and a minimum strength. - Inject.
packages/core/src/inject.tsselects and spreads, skipping drafts at both stages (:657,:697) and expired engrams at:140-153. - Correct. Three paths:
_updateEngramReturning(:5905-5928) overwrites;plur_report_failure(:8020-8084) has a model rewrite a procedural statement and writes the old text into aprocedure_evolvedevent with a version bump and a previous-version reference; the batch dedup (packages/core/src/learn-async.ts:237-300) records the old and new statement with the deciding model as the actor. - Forget.
Plur.forget(:6249) decrements the reference count and at zero sets status retired (:6427-6432), optionally rewriting the rationale, and appends a retirement event with the before and after counts. - Adjudicate.
packages/mcp/src/tools.ts:3880-3897andpackages/cli/src/commands/tensions.ts:113-129confirm, dismiss or resolve a tension; a resolve retires the loser. Both call the same three core functions —confirmTension,dismissTension,resolveTension— and neither passes an actor, which is whyhuman_reviewwas withdrawn on the 2026-09-19 re-read.plur_tensionsis a declared MCP tool whoseactionenum isconfirm | dismiss | resolve, and after a scan the tool's own reply tells the model what to do next: "Review each tension: action:"confirm" (real), action:"dismiss" (false positive), or action:"resolve" + winner:<engram_id> (retire the loser)" (:3955). The agent that wrote both engrams can decide which of them survives, and an unresolved tension does not withhold either — it adds awarningsfield to the injection result (:1622,:1662). The adjudication is a real and well-shaped mechanism; it is not a human one.
5. Memory Data Model
The engram schema is 553 lines of Zod
(packages/core/src/schemas/engram.ts). Beyond the statement
it carries: lineage (source, derivation count, pack, abstract,
derived-from), classification (a knowledge type with a memory class and
a cognitive level, a domain, tags), activation (retrieval and storage
strength, frequency, last accessed), a relation block with broader,
narrower, related, conflicts, supersedes and superseded-by, weighted
associations, knowledge anchors, dual coding with an example and an
analogy, provenance with an origin, a chain, a licence and a
reserved signature whose algorithm the schema says is
not yet specified, an attribution block naming the asserting runtime,
model and tool, a claim class, feedback and usage counters, and optional
temporal, episodic, exchange and insight blocks.
Temporal: learned_at,
valid_from, valid_until and
ingested_at beside created_at,
updated_at and each source's stored_at.
bitemporal earned.
Trust: commitment and status, both filtering.
trust_state earned.
Scoping: a free-form scope with two predicates.
scope_enforced earned.
Tombstone: withheld — see section 9.
A status value with no writer. The
status enum admits candidate and
dormant, and the schema says of them: "'dormant' and
'candidate' are NOT assigned by any code today"
(:352-354), which a search confirms. The consequence is
that plur_promote, a shipped MCP tool advertised as
activating candidate engrams, has nothing this codebase can produce to
act on. Separately candidates.yaml is allocated and synced
with the sync module noting it has no in-core writer.
6. Retrieval Mechanics
BM25 with the standard constants over a rewritten lexical query, optionally fused with a local ONNX embedding search over the original query. Fusion is reciprocal rank at k=60, with the lexical leg fetching three times the limit and the embedding leg twice, widened to five and three times for queries the system classifies as aggregations.
There is no relevance threshold in either leg, and the code contains a comment that reasons as though there were one — treating an empty embedding result as meaning no neighbours cleared a threshold, when the embedding search returns the top N regardless of score and an empty result can only mean an empty corpus or an unavailable embedder. The fusion score is surfaced deliberately so that a caller can apply a cutoff.
Failure modes. The embedder being unavailable degrades to BM25 silently, which is the right default for a local-first tool. A draft is retrievable and uninjectable, so an agent that reads search results directly sees something the injector would have withheld. And the index stores are caches: a stale index is a stale answer, with the YAML still correct.
7. Write Mechanics
The model writes the sentence. No summariser stands between the agent's words and the file, which is the point of a store whose selling line is that memory is text you can read rather than weights you cannot. The regex-based ingest path captures verbatim groups; only the batch and failure-report paths put a model between input and storage, and both record what they replaced.
Repetition is a counter, not a duplicate. The same
content within the same scope increments a reference count. Forgetting
decrements it. That makes plur_forget a vote rather than a
delete, which is defensible, and it means a single forget on a
thrice-learned engram changes nothing visible.
What the correction story does not do. The path most likely to be used from a library — the direct update — replaces the statement with no record that it ever said anything else, while the two paths that need a model both preserve the prior text. The version and previous-version fields exist on the schema and only one of the three paths sets them.
Operational cost
- A write is a YAML rewrite of the array, an index upsert and a JSONL append.
- A read is BM25 in process, optionally an ONNX pass, no network.
- The audit history grows monthly and is never pruned or synced.
8. Agent Integration
43 MCP tools, of which about a dozen write. The model's path to memory is injection rather than a recall call — the injector selects, spreads activation over associations and assembles a block — with the draft gate and the validity gate applied inside that assembly.
The human surfaces are the CLI, the YAML file itself, and a dashboard that is read-only by design: one route renders and a single POST reveals a folder, documented as writing nothing and bound to loopback.
One naming issue is worth flagging for anyone reading the tool list: session end takes a parameter called engram suggestions and writes them straight through the learn path with no approval step.
An unparseable time fails in two different directions, and
the module says why. A stored bound that cannot be parsed is
treated as absent — the engram stays visible — because
"stored engrams have their fields validated by
TemporalSchema and hiding content over a data error is the
worse outcome." A caller-supplied evaluation instant that cannot be
parsed throws a RangeError, because "it is an explicit
argument, so an unparseable value throws ... rather than silently
answering as of the present moment."
The same malformed string fails open as data and closed as an
argument, and the reasoning is the difference between them: a bad stored
value is the store's problem and should not cost the reader a memory,
while a bad now is the caller's question and answering a
different one quietly is worse than refusing. The now
option also accepts an RFC 3339 instant rather than only a date, with a
date still resolving to the end of its day so that a
valid_until equal to today is not expired — the behaviour
the older lexical comparison happened to have, preserved deliberately
rather than by accident.
9. Reliability, Safety, and Trust
Trust state — awarded. A discrete state that withholds from injection while leaving the engram retrievable, produced through an agent-facing argument, enforced inside the injector, and pinned by three tests. The caveat is substantial and belongs in the same paragraph: nothing in this repository lifts it. The schema points at a separate enterprise repository for the review-queue write sites, and the shipped dashboard cannot write. A draft written here stays a draft.
Bitemporal — awarded. Two axes with producers on each and independent filters on both the retrieval and the injection path, including an expiry that can be parsed from the statement text and is echoed back to the caller rather than applied silently.
Scope — awarded. Two predicates, one segment-aware so a project whose name shares a prefix with another is excluded rather than matched, and one exact membership whose empty case is documented in the source as a security rule and asserted by a test.
Audit log — awarded. Append-only, fsynced, twenty-one event types, in the store's own directory, with a reader tool. One property worth stating: the history directory is not in the sync path, so a store pushed to a team carries the engrams without the record of how they got there.
Human review — withheld, and the previous reading's own
record said why. It read "or the same three actions through
the MCP tool", and that clause is the finding rather than a detail.
plur_tensions (packages/mcp/src/tools.ts:3855)
declares action: confirm | dismiss | resolve to the model,
the handler (:3880-3897) calls the same
confirmTension / dismissTension /
resolveTension the CLI calls, no actor is recorded on
either path, and the tool's post-scan reply instructs the model to work
the queue itself. So the producer can adjudicate its own contradictions.
Nothing waits in the meantime either: an unresolved tension adds a
warnings field to the injection result, not a withholding.
The dashboard is still correctly not counted — it writes nothing — and
the adjudication machinery itself is good; it is a contradiction ledger
with a verdict, which is worth having and is not a human gate. The mark
that does carry the withholding here is trust_state,
above.
Negative evaluation — awarded. Three draft-gate cases with positive controls in the same assertion, including one through spreading activation, plus scope and validity exclusions.
Tombstone — withheld, and it is a deliberate design decision rather than an omission. Retirement is soft: the row and the full statement stay in the file, which is the right choice for a store meant to be read by humans. But the content-hash dedup matches only active rows, so the retired text does not block its own re-assertion. The codebase states this twice as a benefit, and a committed test asserts that re-learning a forgotten statement produces a new engram with a new id. The mark asks for a record keyed on the value that a later write consults; here the record exists and the write path deliberately looks past it.
A project config cannot quietly redirect memory to someone
else's host. A .plur.yaml in a cloned repository
can name both remote_url and remote_token.
resolveProjectRemoteFromConfig
(packages/core/src/project-remote.ts:79-99) adopts them
only if isDirectoryTrusted returns true for the directory
the config was read from, fails closed on any error in that check, and
the opencode adapter prints the refusal unconditionally rather than
behind a debug flag — "is not a trusted directory, and those
settings would send prompt text to the host they name". The scope
half of the same file is gated the same way, and both are resolved from
one read of one resolved path, with the TOCTOU between "the file trust
was checked against" and "the file whose fields are adopted" named in a
comment as the reason.
What the delete story promises and does not do. The tool table describes a retired memory as eventually pruned, and a comparison page describes erasure as a real file delete a user can watch happen. No pruning of retired engrams exists in the tree; the only deletion of an engram row is an outbox cleanup after a successful remote push. Erasure is a status flip, and a person who wants the text gone edits the YAML.
10. Tests, Evals, and Benchmarks
5,085 it/test cases in 417 files and 85,302
lines against 68,932 lines of source, with dedicated adversarial, meta
and fixture directories under the core package's tests.
.not.toContain assertions number 347, concentrated in named
files for the draft gate, scope pushdown, read-side visibility,
supersession, validity instants and the pinned quota.
The benchmark harness is in a different repository, and this
one says so. The README's benchmark section opens by saying the
harness is published so every number can be reproduced, and six lines
later names plur-ai/plur-bench as the source of truth for
every figure the project publishes. A commit on 8 July 2026 removed the
in-repo harness — 2,238 lines across a runner, a per-question protocol,
its tests and the LongMemEval corpus importer — and the
benchmark/ directory now holds one file, a latency
micro-benchmark for branch comparison. The data and result directories
are gitignored, and the project's contributor notes say result JSONs are
no longer committed here.
Two things follow, and both are the project's own words rather than
an inference. The only measurement this tree can show is an n=30 fixture
run reported at 80.0% R@5 in a dated benchmark document, against README
headline figures of 92.2% to 97.6% at n=500. And a self-audit document
in docs/reports/ states of an earlier figure: "The raw
run artifact for the 86.7% / 93.3% measurement is not archived in this
repository" and "the claim is widely propagated, the artifact
is not." That document's own remediation instructions point at the
runner and the result file the July commit deleted.
The honest summary for a reader: the harness is published, just not here, and nothing in this checkout reproduces a published number.
11. For Your Own Build
Steal
- Separate retrievable from injectable. A state that keeps an engram searchable while withholding it from the model's context is the cheapest review mechanism there is, and it is four lines in the injector.
- Enforce the gate in the injector, not in the caller. Every path that assembles context passes the same predicate, including the spreading-activation pass that is easy to forget.
- Make the empty grant match nothing, and write the reason in the source. The comment saying never to treat an empty permitted list as no filter is the kind that survives a refactor by someone who did not write it.
- Append the mutation history beside the store and fsync it. Twenty-one event types with an actor and a reason cost one append per write.
- Echo a parsed expiry back to the caller. Silently inferring a validity window from statement text would be a trap; saying what was inferred is not.
- Count references instead of deleting. A memory learned three times and forgotten once should not disappear.
Avoid
- A dedup that looks past what you retired. Whether that is right depends on whether forgetting means this was wrong or I do not need this now — the choice is defensible, but the two readings need different mechanisms and only one of them is built.
- A withholding state with no way to lift it in the same repository. A draft that nothing here can approve is a one-way door, and the pointer to another repository is not usable by a reader of this one.
- A correction path that overwrites without a record, beside two others that preserve it. The one most likely to be called from a library is the one that forgets.
- A status value nothing assigns, and a shipped tool whose whole purpose is to act on it.
Fit
Right if you want an agent's memory to be plain text you can read, diff and edit, shared across several MCP clients, with no service to run and no model required on the default path. The draft gate, the scope rules and the audit history are all well built. Wrong if you need a rejected statement to stay rejected, an in-repo way to approve what was drafted, or to reproduce the published retrieval numbers from this checkout.
12. Open Questions
- What approves a draft outside the enterprise repository? The gate is enforced here and the lift is not, which leaves the mark's most useful half elsewhere.
- Should retirement block re-assertion? The current answer is deliberate and tested; the alternative needs a second state meaning judged wrong rather than not needed.
- Will the direct update path gain a history event? The two model-mediated paths already write one, and the version fields exist on the schema.
- What removes a retired engram? The tool description promises pruning and nothing implements it.
Appendix: File Index
| Path | Lines | What it holds |
|---|---|---|
packages/core/src/schemas/engram.ts |
553 | The engram schema; the temporal block (141-146), the unassigned status values (352-354), the reserved signature (56-57), the enterprise pointer (538) |
packages/core/src/index.ts |
— | learn (2850), _hashDedup (1829-1838),
_filterEngrams (4777-4878),
_updateEngramReturning (5905-5928), forget
(6249-6457), the failure-report rewrite (8020-8084) |
packages/core/src/inject.ts |
— | skipForApproval (172-173), the two skip sites (657,
697), the validity gate (140-153) |
packages/core/src/history.ts |
— | The event union (7), the record shape (26-40),
appendHistory (58-112) |
packages/core/src/scope-util.ts |
— | isScopeWithin (70-74),
makeVisibilityPredicate (108-116),
scopeAllowFilter and its empty-list rule (126-141) |
packages/core/src/fts.ts,
hybrid-search.ts, embeddings.ts |
— | BM25 (448-488), RRF at k=60 (63-80), the optional ONNX search |
packages/core/src/storage.ts,
store/yaml-primary-store.ts |
— | The store root layout (29-40) and the YAML source of truth |
packages/mcp/src/tools.ts |
— | 43 tools; plur_learn (1021), plur_forget
(1822), plur_tensions (3855-3897),
plur_promote (3781) |
packages/cli/src/commands/tensions.ts |
— | The confirm, dismiss and resolve commands (51-53, 113-129) |
benchmark/micro.ts |
313 | The only file in benchmark/: a latency
micro-benchmark |
packages/core/test/draft-approval-gate.test.ts |
— | The three draft exclusions (38-67) |
packages/core/test/reference-count.test.ts |
— | The retired-dedup behaviour, asserted (149-155) |
| tests | 85,302 in 417 files | 5,085 cases, 347 .not.toContain assertions |
Searches recorded for the negative claims
rg -n "status\s*[:=]\s*'candidate'" --glob '*.ts' packages/*/src # 0: declared and never assigned
rg -n "status\s*[:=]\s*'dormant'" --glob '*.ts' packages/*/src # 1, a comment; no assignment
rg -i 'tombstone' --glob '*.ts' packages # 1, a test title about soft retirement
rg -i 'prune' --glob '*.ts' packages/*/src # no pruning of retired engrams
ls benchmark/ # one file: micro.ts
ls benchmark/run.ts benchmark/results/baseline-main-hybrid.json # both absent; the self-audit points at them
git show 19b74b5 --stat # the harness removal: 2,238 lines
History
2026-09-25 — census re-measured at the same pin, e26901c1….
The header band, section 10, the file index, the family paragraph and
the verdict carried the first reading's figures from
d005139. At this pin: 939 commits by thirteen authors
between 19 March and 18 September 2026, 68,932 lines of TypeScript,
85,302 lines of tests in 417 files, 5,085
it/test cases and 347
.not.toContain assertions. Each counting method first
reproduced the published line, file and author counts at
d005139; the case count reproduced within fifteen, so the
method is named where the number appears. Counted by reading the tree;
nothing was built or run. No mark moved.
2026-09-19 — re-pinned to e26901c1…,
release 0.20.1, four commits on. human_review is
withdrawn; five marks stand. The four files the mark rested on
— packages/mcp/src/tools.ts,
packages/core/src/inject.ts,
packages/cli/src/commands/tensions.ts,
packages/core/src/schemas/tension.ts — carry byte-identical
blobs at both pins, so this is a correction to the previous reading
rather than upstream drift. That reading's own evidence record contained
the refutation: "or the same three actions through the MCP
tool." plur_tensions declares
action: confirm | dismiss | resolve on the agent's tool
surface, the handler calls the same core functions the CLI calls with no
actor on either path, and after a scan the tool's reply tells the model
to work the queue. The producing agent can therefore adjudicate the
contradiction between two engrams it wrote, and an unresolved tension
withholds nothing — it adds a warnings field to the
injection result. The adjudication ledger is still described in sections
4 and 9, where it stands on its own. The withholding mechanism in this
system is the draft commitment, and that is trust_state,
already awarded. New in the range and now in section 9: a
.plur.yaml in a cloned repository may name a
remote_url and a remote_token, and
resolveProjectRemoteFromConfig adopts them only from a
directory an explicit plur trust has covered, failing
closed on any error and printing the refusal unconditionally. Screened
again first: thirty-four files, three auto-run surfaces, one build-time
execution point, thirteen unpinned surfaces, fifteen dependency files
inside the cooldown, a CLAUDE.md recorded as data. Nothing
installed, built or run.
2026-09-16 — f6e97819…
— re-read at a commit dated 2026-09-16, 8 commits past the previous pin.
All six marks re-tested and held, and the validity axis is better
defended: an unparseable stored bound is treated as absent so a data
error cannot hide a memory, while an unparseable caller-supplied
now throws a RangeError rather than silently
answering as of the present moment. The evaluation instant now accepts
RFC 3339 as well as a date, with a date still resolving to the end of
its day. Screened before reading: three auto-run surfaces, one
build-time execution point, 13 unpinned dependency surfaces and three
dependency files inside the seven-day cooldown. Nothing was installed,
built or run.
2026-09-08 — d005139e…
— first reading, at the head of main, on a commit from the
same day. Screened before anything was read: three auto-run surfaces
(two plugin manifests and a hooks directory), one build-time execution
point, twelve unpinned surfaces; nothing was installed or run, and the
read was made from a full clone. Six marks. trust_state
rests on a draft commitment enforced inside the injector at both its
passes, with the caveat that nothing in this repository can approve one.
bitemporal, scope_enforced,
audit_log, human_review and
negative_eval rest on the temporal block and its two
independent filters, the segment-aware and exact-membership scope
predicates, the fsynced monthly JSONL, the tension adjudication, and the
three draft-gate exclusions with controls. tombstone is
withheld on a decision rather than an omission: the dedup deliberately
looks past retired rows and a committed test pins that. The benchmark
position was verified rather than repeated — the harness was removed
from this tree in 19b74b5 and lives in a separate
repository the README names, so no published number is reproducible from
this checkout, which the project's own self-audit document also states.
The reading covers the engram schema, the write and dedup paths,
retrieval and injection, the scope predicates, the history log and the
tension queue; the packs and exchange subsystems, the sync outbox, the
migration package and the Python adapters were treated as context.