1. Executive Summary
ctx is an Apache-2.0 Go system of roughly 140,000 lines under
internal/, positioned as "a system, not a prompt" —
file-based context that any tool able to read files can consume, with a
CLI, an MCP server, a VS Code extension, and a documentation site.
Two things in it are worth the atlas's attention, and both concern the moment memory is changed by a model rather than the moment it is written.
The consolidation pass writes inside a guarded path
scope. ctx calls its background consolidation
dream, and dream/guard.go decides where a
dream may write:
"A target is allowed iff it resolves under
dreams/orideas/relative to projectRoot, OR underspecs/when the action isActionPromote— the one sanctioned boundary crossing (deliberate declassification into a tracked spec)."
Every other background consolidator in this atlas can write wherever
its code happens to point. CowAgent rewrites
MEMORY.md nightly, ByteRover
curates documents, Magic Context
re-verifies and marks. None of them has a stated, enforced answer to
which files may a background LLM pass modify, and the answer
matters most for exactly the systems whose store is the user's own
project tree.
Naming a single sanctioned crossing — promote, into
specs/ — is the better half of the idea. A guard with no
exit becomes a guard people disable; a guard with one named,
disposition-gated exit stays enforceable.
The test suite carries a corruption corpus taken from the literature.
"corruptedFixture is a regression corpus modeled on the corrupted-artifact appendix of arXiv 2605.12978 ('Useful Memories Become Faulty When Continuously Updated by LLMs'): one well-formed proposal among several corrupted ones (an unknown status enum, stripped evidence, a dropped target). It guards the review/dedup gate against silently admitting a faulty artifact."
TestCorruptedArtifactGate feeds four proposals through
the real reader and the schema gate and asserts that exactly
one — the well-formed, provenance-bearing one — survives. This
is the first system in the atlas that tests against a documented failure
mode from published research rather than against its own happy path, and
the failure mode chosen is precisely the one the atlas keeps warning
about: memory degrading as an LLM repeatedly rewrites it.
Reservations. Correction is structural — entries fold into themed regions — with no trust state and nothing recording that a digested claim was later judged wrong. And the surface is very large for what it does, with 61,000 lines of CLI.
2. Mental Model
Diagram source
%% caption: digesting an entry into a themed region leaves it in staging rather than moving it, and every background proposal passes a provenance schema gate and a write-scope guard before reaching the resumable ledger
flowchart TB
ST[("staging<br/><i>entries land here first, undigested</i>")]
ST -->|"AddTheme folds a gist bullet into the root's<br/>Themes region, creating it if absent —<br/><b>nothing moves out of staging</b>"| RD[("root document<br/><i>Themes, then a region per theme;<br/>entries digested into place</i>")]
SC["dream, background: scan"] --> PR["proposals"]
PR --> SG{"schema gate<br/>provenance required"}
SG -->|invalid| REJ["rejected, not admitted"]
SG -->|valid| WG{"write-scope guard"}
WG -->|"dreams/ · ideas/"| OK["allowed"]
WG -->|"specs/"| PROM["only on promote"]
WG -->|"anything else"| NO["refused, with a reason"]
OK --> AP["apply"]
PROM --> AP
AP --> LG[("ledger, resumable")]
style SG fill:#e7efe9,stroke:#3d6b59
style WG fill:#e7efe9,stroke:#3d6b59Two gates in series, both highlighted, and they check different things. The schema gate requires provenance before a proposal is admitted at all; the write-scope guard then decides where it may land, refusing with a reason rather than silently dropping. Naming a theme is deliberately separate from digesting entries into it — a theme can exist before anything has moved.
3. Architecture
Largest packages under internal/, in lines of Go
excluding tests:
cli(61,118),config(21,044),write(13,084),err(8,183),mcp(5,145),journal(4,228),hub(2,605),assets(2,509),ctxctl(1,807),entity(1,600),disclosure(1,569),rc(1,483),trace(1,301),dream(1,278),drift(925).
The interesting logic is concentrated in the small packages:
dream, disclosure, drift,
journal, entity.
Diagram source
%% caption: the same two paths in one view — entries digested into themed regions, and background proposals gated on provenance and write scope
flowchart TD
E["entries"] --> Stage["staging"]
Theme["AddTheme:<br/>name a theme<br/>first"] --> Root["root doc<br/>## Themes"]
Stage --> Dig["digest<br/>into region"]
Dig --> Root
Scan["dream scan"] --> Prop["proposals"]
Prop --> Gate{"schema gate\nprovenance<br/>required"}
Gate -->|invalid| Rej["rejected"]
Gate -->|valid| WS{"write-scope guard"}
WS -->|dreams/ ideas/| Apply["apply"]
WS -->|specs/ on promote| Apply
WS -->|elsewhere| Ref["refused<br/>with a<br/>reason"]
Apply --> Led["ledger —<br/>resumable"]4. Essential Implementation Paths
A write scope for the consolidator
WriteScope(projectRoot, target, action) returns a
GuardDecision carrying "Allowed plus a registry-sourced
refusal Reason", and errors only when a path cannot be resolved. Two
details make it more than a path check.
Refusals carry a reason from a registry. A guard that returns a bare false produces an unexplainable failure; one that returns a registered reason produces a message an operator can act on, and a set of refusal categories that can be counted.
The exception is gated on the disposition, not the
caller. specs/ opens only when the action is
ActionPromote, described as "deliberate declassification
into a tracked spec". Tying the boundary crossing to what the pass
decided rather than to who is asking is the right axis: it
means the guard cannot be widened by adding a privileged caller.
This is the governed write gateway pattern applied to the background path specifically, which is where this atlas has repeatedly found governance missing. A human write goes through review in several systems here; the nightly job goes wherever its code points in all of them but this one.
A corruption regression corpus
Most memory systems in this atlas are tested against their own intended behaviour. ctx is tested against a published account of how systems like it fail.
The fixture is four proposals: one clean, and three corrupted in ways
the paper's appendix catalogues — an unknown status enum, stripped
evidence, a dropped target. The assertion is not "the pipeline survives"
but that exactly one proposal, clean-1, passes
ProposalValid, and that the corrupted entries are
"rejected, not silently admitted".
Silent admission is the failure that matters. A pipeline that rejects loudly is debuggable; one that accepts a proposal with stripped evidence produces a memory that looks well-formed and is not, and the structural-loss guard in ByteRover exists because the same thing happens during curation. ctx is the only system here that keeps a corpus of the failure and re-runs it.
Requiring provenance for survival is the other half — a proposal with its evidence stripped is rejected by construction rather than by a heuristic about its content.
Name the theme before digesting into it
AddTheme "declares a theme in a root: it folds the gist
bullet into the root's ## Themes region, creating that
region when the root has none", and the docstring is explicit that
"Nothing moves out of staging — this is how a theme is
named before any entries are digested into it."
Separating declaring a category from filing things into it prevents the failure that turned this atlas's own taxonomy into a list once: categories created implicitly, one per item, because filing and naming happened in the same step. Making theme creation a distinct, visible act with its own command puts a human decision in front of structure growth.
An append-only ledger in two formats at once
AppendLedger "appends one disposition to the append-only
ledger at <dreamsDir>/ledger.md… Each entry is
written as a Markdown list item whose payload is the JSON encoding of
the LedgerEntry — human-readable as a list, and
machine-parseable by ReadLedger. The ledger is never
rewritten, only" appended.
Two properties, both cheap. It is genuinely append-only, so the
record of what the dream decided cannot be edited by the next dream. And
the same file is a document a person reads and a structure a program
parses, which removes the usual choice between an audit log nobody opens
and a changelog nothing can query. That, with
resume_test.go and state.go, gives the recoverable
background work and append-only memory
audit shapes at once. Proposals are staged to disk with
SafeWriteFileAtomic at secret permissions before the gate
runs, so a crash mid-pass leaves the input intact.
5. Memory Data Model
Markdown in the project tree. Entries stage, themes are declared into
a root's ## Themes region, entries digest into per-theme
regions. disclosure/ carries regions.go,
invariant.go, split.go, move.go,
validate.go — region manipulation with invariants and a
validator, tested by apply_invariants_test.go and
apply_conserve_test.go.
A conservation test on an apply path is a good sign: it asks whether
the rewrite lost anything, which is the same question ByteRover's
detectStructuralLoss asks and which most rewrite paths in
this atlas never ask.
What is absent:
- No trust state on an entry.
- No value-level tombstone. A digested claim later judged wrong is edited or folded away; nothing prevents the next dream proposing it again from the same staged material.
- No explicit scope key beyond the project tree itself — scoping here is the filesystem, which is honest for a per-project tool and does not extend to multi-user use.
6. Retrieval Mechanics
There is no ranker. Retrieval is progressive disclosure over files: roots carry themes, themes carry regions, and any tool that can read files can walk them. That places ctx with GenericAgent and nanobot — the cheapest retrieval architecture in the atlas, which works while the corpus stays small and human-organized, and fails silently when a reader does not know a region exists.
The drift/ package exists to detect when the organized
structure and the underlying reality have diverged, which is the
mechanism that failure mode calls for.
7. Write Mechanics
Staged entries, an explicit digest step, and a dream
pass that proposes rather than rewrites. Proposals are validated before
application and applied within the guard. journal/ (4,228
lines) records the history alongside.
Notably, write/ is its own 13,000-line package with
err/ at 8,183 — a system that treats writing files and
reporting failures as first-class concerns rather than incidental ones,
which matches a design whose store is the user's own repository.
8. Agent Integration
CLI, MCP server, a VS Code extension, a skills contract
(CONTRIBUTING-SKILLS.md), and steering/hooks packages. The
stated position is that it "works with any AI tool that can read files;
no model or vendor lock-in", which is the same bet Basic Memory makes.
9. Reliability, Safety, and Trust
human_review is the mark this system is built
around, and it runs on every proposal. dream never
writes: it emits proposals into a gitignored dreams/
notebook and stops, under a type comment saying so — "The dream
never acts on a proposal." Three CLI verbs dispose of one:
accept applies the recommendation, reject
records a refusal with no mutation, and amend substitutes
an action the person chooses in place of the model's, each taking an
optional human note. All three append to the ledger, and
Decision is a closed four-value set —
accepted | rejected | amended | skipped — declared as
"the human's disposition recorded in the ledger". The
amend path is the part most review surfaces in this atlas
lack: the reviewer is not restricted to yes or no.
scope_enforced is not earned, and the guard is
worth describing as what it is. WriteScope
resolves a target relative to the project root and allows it only under
dreams/ or ideas/, plus specs/
when the action is ActionPromote — "the one sanctioned
boundary crossing (deliberate declassification into a tracked
spec)". That is a write-authorization boundary on a directory, not
a stored key applied as a filter on a read, and the read path has no
scope at all: progressive disclosure means any tool that can read files
can read everything. The guard is a good mechanism aimed at a different
risk — a background pass writing outside its sanctioned area — and it
belongs in the prose rather than in the mark.
The rejection ledger is one property short of a tombstone,
and the missing property is where the id comes from.
Seen(entries, proposalID) reports whether the ledger
already holds a disposition, PendingProposals drops
anything it returns true for, and the docstring is explicit that this
includes refusals: "Rejections count as seen, by design — the dream
does not re-nag a rejected disposition unless the source content
changes." So a refusal is durable and it is consulted
before the next surfacing, which is more than most systems here manage.
What it is keyed on is ProposalID, and no Go code produces
one: proposals.go only unmarshals
proposals.json, which the consolidation skill writes. The
type comment asks for stability — "Stable so v2 supersession is not
foreclosed" — and nothing verifies it or derives it from the
content. The content-change half is handled separately and properly, by
DeltaSelect comparing a per-path content hash, so a changed
source file re-enters the scan. The gap is between those two: a rejected
claim that reappears under a new proposal id, from a file that
also changed, is offered again.
Strengths:
- A write-scope guard on the consolidation pass, with one disposition-gated exception and registry-sourced refusal reasons.
- A corruption regression corpus drawn from published research on LLM memory degradation, asserting rejection rather than survival.
- Provenance required for a proposal to pass the gate.
- Theme declaration separated from digestion, so structure grows by decision.
- Conservation and invariant tests on the region-apply path.
- An append-only ledger, never rewritten, readable as Markdown and parseable as JSON, with atomic staging at restrictive permissions.
- A drift package aimed at the failure mode a ranker-free design has.
Gaps:
- No trust state or tombstone; correction is structural editing, and the rejection record is keyed on an identifier the model supplies.
- No ranker, so recall depends on the reader knowing where to look.
- Very large surface, dominated by a 61,000-line CLI.
- Filesystem-scoped, with no multi-user story and no scope key on any read.
- The guard protects the project tree, not the memory's
meaning — a dream may write a wrong thing inside
dreams/all it likes.
10. Tests, Evals, and Benchmarks
1,932 Test functions across 428 test files, over 209,370
lines of Go. Tests are dense in the small packages —
disclosure/ alone has apply, invariant, conserve,
convention, firstrun and abort tests, and dream/ has guard,
ledger, resume, validate, proposals and the corrupted-corpus test.
Nothing was run for this review, and no retrieval-quality benchmark exists — consistent with a design that has no ranker to benchmark. The measurement that would matter here is whether progressive disclosure actually gets read: how often a relevant region exists and the agent never opens it. Nothing measures that, and it is the same unmeasured false-negative the atlas notes for GenericAgent.
11. For Your Own Build
Steal
- Give the background consolidator a write scope, enforced by path, with refusals carrying a registered reason. A nightly LLM pass that can write anywhere in the user's repository is a risk most systems here simply run.
- Gate the one sanctioned crossing on the disposition, not on the caller, so the boundary cannot be widened by adding a privileged path.
- Build a regression corpus from published failure modes. Testing against documented ways systems like yours degrade is more useful than another happy-path fixture, and the paper appendices are already written.
- Assert rejection, not survival. The interesting property is that a malformed artifact does not get admitted silently.
- Require provenance for admission, so evidence-stripped material fails by construction.
- Separate naming a category from filing into it, or categories multiply one-per-item.
- Test conservation on any rewrite path — did the transformation lose anything?
- Write the audit log in two formats at once. A Markdown list whose items are JSON payloads is readable by a person and parseable by a program, which is why it might actually get read.
Avoid
- Structural correction without a tombstone, in a system whose dream pass re-reads staged material.
- No ranker and no measurement of whether disclosure is reached.
- Surface area far exceeding the mechanisms that make it distinctive.
Fit
Borrow:
WriteScopeand its disposition-gated exception, which is a small function with an outsized safety return.- The corrupted-artifact corpus idea, which any system with an LLM rewrite path can adopt in an afternoon.
- Theme-before-digest.
Do not copy:
- Progressive disclosure as the whole retrieval story unless the corpus stays small and curated.
- Region folding as the correction story if a background pass can re-propose.
12. Open Questions
- What stops a dream re-proposing something a human folded away?
- Is the write-scope guard applied to every write path, or only to dream apply?
- How often does an agent fail to open a region that would have answered it?
- Does
driftdetect divergence between themes and reality, or only within the documents? - Are the refusal reasons counted anywhere, or only shown?
Appendix: File Index
- Write-scope guard:
internal/dream/guard.go(WriteScope,GuardDecision,ActionPromote),guard_helpers.go. - Corruption corpus:
internal/dream/corrupted_test.go(TestCorruptedArtifactGate,testdata/corrupted-2605.12978.json). - Proposal lifecycle:
internal/dream/proposals.go,validate.go,ledger.go,state.go,scan.go,apply.go. - Disclosure and regions:
internal/disclosure/addtheme.go,regions.go,invariant.go,split.go,move.go,validate.go. - Human disposition:
internal/cli/dream/core/dispose/dispose.go(Accept:30,Reject:55,Amend:82),internal/cli/dream/core/review/review.go,internal/config/dream/types.go:85-100(the four decisions). - Dedup-against-seen:
internal/dream/ledger.go:91-111(Seen),proposals.go:98-122(PendingProposals),state.go:106-122(DeltaSelect, the per-path content hash). - Drift detection:
internal/drift/. - History:
internal/journal/, withparser/codex_convert.goandcodex_path.goreading Codex sessions.
Appendix: Recorded Searches
Run from the root of the checkout at the pinned commit.
| Claim | Command | Result at this pin |
|---|---|---|
| No scope key on any read path | grep -rniE "scope|tenant|user_id|namespace|owner" --include="*.go" internal/disclosure internal/dream |
Every hit is the write guard or a comment; no read filters on a stored key |
| Nothing in Go produces a proposal id | grep -rn "Proposal{" --include="*.go" internal cmd and
read internal/dream/proposals.go |
Only zero-value literals in error returns; the run's proposals are
unmarshalled from proposals.json |
| A rejection is consulted before re-surfacing | read Seen at internal/dream/ledger.go:104
and PendingProposals at proposals.go:113 |
PendingProposals drops any proposal with a ledger
entry, rejections included |
| The ledger is append-only | read AppendLedger at
internal/dream/ledger.go:104 |
Opens for append, writes one JSON line per disposition, never rewrites |
| The memory mechanism did not move | git diff --stat <prev-pin>..HEAD -- internal/dream |
Empty |
| Tree and suite size | find . -name "*.go" | xargs wc -l | tail -1;
grep -rh "^func Test" --include="*_test.go" . | wc -l |
209,370 lines; 1,932 test functions across 428 files |
History
2026-09-11 — 43d0ba7c…
— re-read, 273 files and 29,287 insertions past the previous pin in a
single commit — and internal/dream/, the package
this report is about, is byte-identical. The growth is product
surface: a Codex session parser in
internal/journal/parser/, a Tauri desktop app, a VS Code
extension, and three new skill assets. Both mark changes are
first-reading corrections. scope_enforced is
withdrawn: it rested on WriteScope, which authorises a
write against a directory allowlist, and the atlas's mark is a
stored key applied as a filter on a read — of which there is
none here, since progressive disclosure is any tool reading files.
human_review is added, and it should have been there at the
first reading: dream emits proposals and never acts on
them, and accept, reject and
amend are three CLI verbs a person runs against one
proposal id, with amend substituting the person's action
for the model's recommendation and every disposition landing in the
ledger. audit_log holds on that same ledger, unchanged. The
rejection record is described precisely for the first time: it is
durable and it is consulted — PendingProposals
drops anything Seen returns true for, rejections included —
but it is keyed on a ProposalID that no Go code produces,
because the consolidation skill writes proposals.json; the
content-change half is handled separately by DeltaSelect
over a per-path hash. That is one property short of
tombstone, and the missing property is named. Suite 1,932
test functions across 428 files over 209,370 lines of Go. Screened
before reading: three context-injection surfaces, three build-time exec
paths, eleven dependency manifests inside the cooldown; nothing was
built or run.
2026-07-28 — ce5a8328…
— first reading.