Microdesign — tested, aligned with the code. Normative reference for implementation. The nightly ager (apply_executor_ager, introvertiva_propose, introvertiva_apply) is gated by the user-interaction signal: a Metnos on holiday no longer deprecates executors for calendar inactivity. The introvertiva has deterministic quality filters: vocab validator on proposed_name, skip of flow args (entries, from_step, results) and template values {{stepN.field}}, min_uses threshold at 10. Proposal volume at 6 with high expected quality.
← Documentation index Microdesign › mnestome

Metnos

mnestome — the emergent graph of all mnests
Microdesign —
Aligned with Architecture (ch. 9, Figure 6) and the Dialogue on executors.
New concept introduced by (does not replace anything existing).

Audience: whoever implements the data schema, the ager, the snapshots.
Reading time: 16 minutes.

Contents

  1. Scope and boundaries
  2. What the mnestome is
  3. What the mnestome is NOT
  4. The mnestome broth — a useful metaphor
  5. Data schema
  6. Operations: insert, query, update
  7. The ager: how decay operates
  8. Pruning, snapshots, archival
  9. Linguistic variant: mnestome vs mnestoma
  10. Bootstrap: the first mnestome
  11. Observability and debugging
  12. Open questions

1. Scope and boundaries

This document defines the mnestome: the data structure that holds the set of all active mnests and governs their lifecycle. Technically it is a weighted directed graph persisted in SQLite; ontologically it is Metnos's relational memory — what it remembers having done together.

Boundaries

The document covers:

It does not cover, and defers to other docs:

2. What the mnestome is

The mnestome is a weighted directed graph in which:

The mnestome emerges. No one designs it a priori: it is born empty at first boot and forms turn after turn, while the gateway observes which executors pass output to which others. The shapes it takes reflect how the system is used: an «invoice archiving» user's mnestome has different clusters than a «daily news roundup» user's, even when the seed of executors is the same.

Why a graph, not a list. The «graph» shape captures a property the list would not: operational proximity. Two executors that often appear one after the other in the same turn are closer than two that never meet. The graph makes it explicit; the list hides it. Almost every Vaglio, ager and synt decision is easier to formulate in graph terms.

3. What the mnestome is NOT

The word «memory» in an AI agent has too many meanings. It is worth saying clearly what the mnestome is not:

What the mnestome is NOTWhere that concept lives, then
The LLM's conversational memory (recent turns, in-token context).In the working memory of the execution trace.
The episodic memory of past conversations (what Roberto said yesterday).In the episodic memory, indexed by sender. In the episodic memory store.
The semantic memory of facts about Roberto and his world («the dog's name is Fido»).In the semantic memory, extracted via reflection and approved by the user.
The audit log of completed actions.In workspace/.audit/, append-only, JSONL.
The user's file workspace (notes, invoices, photos).In workspace/, managed separately.
The definition of capabilities (the individual executors).In workspace/executors/, file by file.

The mnestome is the observed relation between executors: who worked with whom, how often, how recently. Nothing more.

3-bis. The mnestome broth — a useful metaphor

To understand what a mnestome really does, it helps to stop thinking of it as a SQLite table for a moment and start thinking of it as a living environment: a broth in which small particles float, meet, attract one another, combine. Out of that dynamic, more complex structures emerge through progressive aggregation — a process that, every so often, recalls what we see in other natural systems made of many elements interacting with each other.

What follows is a metaphor, not an identity. I use it with restraint: it is meant to show three things the mnestome actually does — aggregate what is similar, recognise what is missing, let new structures arise when recognition meets the right material — without claiming a literal parallel that is not there.

The particles of the broth

At first the broth is empty. Then the particles begin to appear: small traces of life that the system collects while the user uses it. They come in a few species, each with its shape and role:

mnest (passage between executors) executor fast-path proto-mnest The particles drift (animation: hover over the illustration).
Fig. 1 — The mnestome broth at rest. Four species of particles: mnests (observed passages), executors (graph nodes), fast-paths (introspective shortcuts), and proto-mnests (desires not yet fulfilled).

Each particle has a story: a mnest is the memory that “A served B”; an executor is a small programme that knows how to do one specific thing; a fast-path is a shortcut the system has learned (the canonical request “X is solved like this”); a proto-mnest is a mnest half-done: real src, dst desired but not yet existing. You can see immediately in the drawing that the proto-mnest has a different shape, a sort of open receptor: there is an empty slot waiting to be filled.

From repetition to fastpath

In the broth, nothing is truly random. Particles attract each other based on semantic similarity, measured with the BGE-M3 multilingual model. When the user repeatedly does the same thing — three “download this page” requests that always turn into get_urls followed by describe_entries — the mnests observed across the three turns refer to the same pair of executors. Those mnests form a cross-turn cluster: evidence that “this pipeline works”.

At this point something interesting happens: the cluster does not remain a blurry cloud, it crystallises. It becomes a fastpath, a preformed path that describes the sequence in a compact, directly executable form. From that moment on, the PLANNER no longer needs to call the language model to re-plan the pipeline from scratch: it reads the fastpath and uses it directly. The figure below shows the four phases — observation, cluster, crystallisation, reuse — in sequence.

download X get_urls describe_entries output download Y get_urls describe_entries output download Z get_urls describe_entries output turn 1 turn 2 turn 3 mnest cluster FASTPATH: get_urls → describe_entries the cluster becomes a fastpath new input PLANNER LLM (not invoked) output three repeated turns produce observed mnests mnests converge into a cross-turn cluster the cluster crystallises into a preformed path the PLANNER uses the fastpath directly, without invoking the LLM phase 1 phase 2 phase 3 phase 4
Fig. 2 — The birth of a fastpath. Three repeated turns produce mnests that converge into a cluster, the cluster crystallises into a preformed path, and the PLANNER uses it directly, skipping the language model.

The fastpath that emerges is not an abstraction: it is precisely that concrete sequence, memoised, ready to use. The saving is tangible — the call to the LLM planner (about twelve seconds) is skipped entirely, and the user gets the answer in under a second. The cluster that produced it stays in the mnestome as a trace of how the fastpath was reached; the fastpath itself lives in its own separate registry (see fast_path.html).

Receptors: the proto-mnest and the desire

Sometimes, as the system works, something more peculiar happens: the planner would like to call an executor that does not exist. Maybe the user asked something new, and in the ideal chain the final step should have been a compose_report that has never been written. What gets recorded, at that moment, is a proto-mnest: a trace with a known src and a dst that is only a name, not yet a programme.

We can see it as a receptor: a shape with an empty gap where the dst would fit. The gap is not a graphic detail, it is the desired signature — it declares what would be needed to close the shape. The receptor waits: it stays stable as long as no one fills it, and becomes more visible as it shows up in the same way, turn after turn.

read_url (exists) src executor observed mnest (user turn) gap proto-mnest desired dst: «compose_report» does not exist yet The proto-mnest waits. The dashed gap is the missing dst.
Fig. 3 — The proto-mnest as a receptor. On the left, the src executor exists; on the right, the proto-mnest shows clearly the open gap that awaits a dst.

Introspective functions: who recognises receptors

Somewhere in the broth, the introspective functions work: the silent crafts of synt in its introvertive mode and of the introvertiva module. They have no final function of their own; they exist to make other things happen. Their hallmark is the ability to recognise receptors: to read the open gap and understand which dst would close it.

When an introspective function meets a proto-mnest that has been seen more than once (the threshold is three recurring observations, ), it approaches, analyses it, reads the desired signature, and proposes: it emits a candidate for synthesis. The proposal says: “this receptor exists, it recurs, and here is a possible dst that would close it”.

Synthesising an executor: from open gap to closed receptor synt at work... Receptor (proto-mnest) waits for a capability compose_report synt synt approaches synt at work inside the receptor executor created, synt leaves proto-mnest closed, new capability in the pool 0s 3.5s 7s 10.5s 14s
Fig. 4 — The synthesis dance. Synt enters the receptor's gap, works on creating a new executor, leaves it in place and exits. The proto-mnest becomes an active mnest: the new capability joins the executor pool.

Aggregation: fast-paths that compose

Another interesting phenomenon happens in the broth: two fast-paths that live adjacent and often appear in the same turn end up hooking together to form a longer fast-path.

Consider a typical turn: “download me this page” triggers an L1 single-tool fast-path; immediately after, “tell me what it says” triggers another L1 fast-path. The system observes the succession, records the sequence, and when it repeats three times promotes it to an L2 multi-step fast-path. From that moment on the pair «download + describe» is a single recognised sequence.

L1 «download» L1 «describe» L2 = download + describe describe_url synthesised executor threshold: 50 repetitions 3 repetitions L2→L3 promotion
Fig. 5 — Fast-path aggregation. Two L1 fast-paths combine into an L2 after three passes; after fifty, the sequence is stable enough to be unified into a single executor.

The sequence does not stop at two. Three adjacent fast-paths can form a longer chain, and so on. The system keeps the whole chain, measures how often it repeats, and beyond a threshold (50 uses by default) a nightly job called multi_tool_promote presents the pipeline to synt as a proto-mnest: “here is a sequence worth unifying into a single executor”.

From pipeline to synthesis: two paths to the same outcome

At this point it is worth pausing to observe that synt receives proto-mnests from two different paths, independent of one another, converging at the same point.

The direct path: the introspective functions observe the mnestome and identify the receptors that recur — those proto-mnests that present themselves in the same way turn after turn, threshold three recurring observations. When they find one stable enough, they propose directly an executor that would close the gap. There is no need to go through a fast-path: the analysis of the single receptor is enough. This is the classic case, and historically the first that synt learned to handle.

The indirect path: an L2 fast-path pipeline that has accumulated enough uses (fifty by default) is promoted by the nightly job multi_tool_promote to a proto-mnest. This time the proto-mnest is not born from a failed request but from a sequence that worked many times: the desired signature describes a unified executor that wraps the whole chain of calls.

The two paths lead to the same entry point. Synt receives the proto-mnest, reads the signature, and translates it into a complete structure: it runs its five stages (naming, signature, tests, description, code), produces a new executor, signs it with ed25519, registers it in the catalog. The receptor is no longer open: the dst exists, the proto-mnest is promoted to an active mnest. The new capability is unique, reusable, and — whether it comes from the direct analysis of a receptor or from the aggregation of a pipeline — enters the executor pool like all the others.

The executor always has priority over the fast-path (bidirectional rule). The rule holds in both directions of time. From synthesis to fast-path: once synthesis has produced the describe_urls executor, the aggregated pipeline [get_urls, describe_entries] that originated it is immediately demoted: its shape dissolves back into the broth, and every new request goes directly through the monolithic executor. From candidate fast-path to non-creation: symmetrically, if the system is about to record a new fast-path and discovers that the executor that would do the same thing already exists in the catalog, the fast-path is not created: there is nothing to memoise if the unified capability is already available. The runtime applies the rule on the fly in the matcher (auto-demote) and in the recording (silent skip); the planner knows it from its prompt and never emits the double sequence when the unified executor is in the pool. The form has priority over the shortcut, both incoming and outgoing.

All together: a system in equilibrium

What emerges, seen from a distance, is a system in dynamic equilibrium: closed but alive, where every component takes part in a cycle. Particles are born from the observation of the turn, they attract by similarity, form clusters, some clusters become fast-paths, some fast-paths aggregate into pipelines, mature pipelines become proto-mnests that ask for synthesis, synthesis produces new executors, the new executors generate further mnests and everything starts over.

At the same time there is a cycle of forgetting: the nightly ager (see chapter 6) decays the unused particles, reduces their weight, moves them to decaying state, eventually archives them. Things that are no longer used disappear. The broth cleans itself.

All of this happens inside a single SQLite file of a few MB. The metaphor of the broth is not a teaching trick: it describes faithfully enough what the code does. Understanding this level makes the chapters that follow — the data schema, the operations, the ager — obvious: they read like the technical explanation of a process you have already seen in motion.

4. Data schema

The mnestome lives in workspace/.mnestoma/mnest.sqlite, a single SQLite file. The choice of SQLite is deliberate: zero services to start, zero external dependencies, trivial backup (file copy), proven reliability.

Table executors

A lightweight replica of the manifest, in fast-read mode for gateway queries. The canonical manifest always remains workspace/executors/<name>/manifest.toml; this table is an index.

CREATE TABLE executors (
 name TEXT NOT NULL,
 version TEXT NOT NULL,
 state TEXT NOT NULL, -- seed | active | quarantine | archived
 loaded_at TEXT NOT NULL, -- ISO 8601
 manifest_hash TEXT NOT NULL, -- blake3
 PRIMARY KEY (name, version)
);
CREATE INDEX idx_executors_state ON executors(state);

Table mnests

CREATE TABLE mnests (
 id TEXT PRIMARY KEY, -- ULID
 src_executor TEXT NOT NULL,
 src_version TEXT NOT NULL,
 dst_executor TEXT NOT NULL, -- canonical name OR desired name (proto)
 dst_version TEXT, -- NULL for proto
 weight REAL NOT NULL CHECK (weight BETWEEN 0 AND 1),
 uses INTEGER NOT NULL CHECK (uses >= 1),
 ts_first TEXT NOT NULL,
 ts_last TEXT NOT NULL,
 decay_lambda REAL NOT NULL DEFAULT 0.018,
 state TEXT NOT NULL, -- proto | active | decaying | superseded
 tags TEXT, -- JSON array
 desired_sig TEXT, -- JSON, only for proto
 CHECK (ts_last >= ts_first),
 UNIQUE (src_executor, src_version, dst_executor, dst_version, state)
);
CREATE INDEX idx_mnests_dst ON mnests(dst_executor);
CREATE INDEX idx_mnests_src ON mnests(src_executor);
CREATE INDEX idx_mnests_weight ON mnests(weight DESC);
CREATE INDEX idx_mnests_state ON mnests(state);

Table events

Append-only, records every reinforcement or decay applied. Allows the weight trajectory of a mnest to be reconstructed and to be recomputed later if the formula changes.

CREATE TABLE events (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 mnest_id TEXT NOT NULL,
 ts TEXT NOT NULL,
 kind TEXT NOT NULL, -- reinforce | decay | state_change
 delta REAL, -- weight delta (NULL for state_change)
 new_state TEXT, -- only for state_change
 reason TEXT, -- e.g. "ager nightly", "synt approved"
 FOREIGN KEY (mnest_id) REFERENCES mnests(id)
);
CREATE INDEX idx_events_mnest ON events(mnest_id);
CREATE INDEX idx_events_ts ON events(ts);

Table canonical_query_log

A log of user requests in lemma form, used by the introvertive fast-path (L1 single-tool, ). Every time the planner decides the first executor of a turn, a row is inserted or reinforced here: canonical query (lowercase condensed form), chosen tool, observed argument values. After three consolidated passes, the canonical_matcher uses BGE-M3 cosine to match new requests against this log and skip the planner.

CREATE TABLE canonical_query_log (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 canonical_query TEXT NOT NULL,
 tool_name TEXT NOT NULL,
 args_shape TEXT NOT NULL, -- placeholder template (back-compat)
 args_observed TEXT, -- ACTUAL argument values
 uses INTEGER NOT NULL DEFAULT 1,
 ok_count INTEGER NOT NULL DEFAULT 0,
 fail_count INTEGER NOT NULL DEFAULT 0,
 ts_first TEXT NOT NULL,
 ts_last TEXT NOT NULL,
 state TEXT NOT NULL DEFAULT 'candidate',
 UNIQUE(canonical_query, tool_name, args_shape)
);

The args_observed column (added via idempotent migration) stores the concrete argument values observed on the first planner pass. When a new request matches the row, the argument extractor (args_extractor) reuses them directly, avoiding re-running regex extraction or calling an LLM. The analogous multi-step sequences live in a separate sqlite (multi_tool_paths.sqlite); see fast_path.html for the full picture.

View v_mnestome

The «live» graph for read queries. Excludes superseded and shows active + proto:

CREATE VIEW v_mnestome AS
SELECT id, src_executor, src_version, dst_executor, dst_version,
 weight, uses, ts_last, state, tags, desired_sig
FROM mnests
WHERE state IN ('active', 'proto');

5. Operations: insert, query, update

Insert / reinforcement: record_passing

The only write operation during a turn. Atomic per pair. Pseudocode:

def record_passing(src, dst, turn_ctx) -> mnest_id:
 with conn.transaction:
 if dst.exists:
 row = SELECT * FROM mnests
 WHERE src_executor = src.name AND src_version = src.version
 AND dst_executor = dst.name AND dst_version = dst.version
 AND state = 'active'
 if row:
 w = decay(row.weight, now - row.ts_last, row.decay_lambda) + REINFORCE_DELTA
 UPDATE mnests SET weight = clamp01(w), uses = uses + 1, ts_last = now
 INSERT INTO events (mnest_id, ts, kind, delta, reason)
 VALUES (row.id, now, 'reinforce', REINFORCE_DELTA, turn_ctx.id)
 return row.id
 else:
 id = ulid
 INSERT INTO mnests (id, src_*, dst_*, weight, uses, ts_first, ts_last, state, tags)
 VALUES (id,..., BOOTSTRAP_WEIGHT, 1, now, now, 'active', tags)
 INSERT INTO events (mnest_id, ts, kind, delta, reason)
 VALUES (id, now, 'reinforce', BOOTSTRAP_WEIGHT, turn_ctx.id)
 return id
 else:
 # proto-mnest
 id = ulid
 desired = build_desired_signature(turn_ctx)
 INSERT INTO mnests (..., dst_executor=dst.desired_name, dst_version=NULL,..., state='proto', desired_sig=desired)
 return id

Main queries

QueryUse
top_k_outgoing(executor, k)Given an executor, returns the k heaviest outgoing mnests.
top_k_incoming(executor, k)Given an executor, returns the k heaviest incoming mnests.
walk(start, max_depth)Weighted BFS from the starting node, up to a maximum depth. Used by the gateway to build the execution context.
recurring_protos(min_uses, since)List of proto-mnests that have crossed a recurrence threshold. Trigger of the synt.
by_tag(tag)All mnests with a given tag. Used by observability and the ager for clustering.
decayingMnests in decaying state. Used by the ager for archival proposals.

State updates

State transitions are made by specific components (ager, synt, gateway in case of executor quarantine). All are accompanied by an events row with kind = 'state_change' and a mandatory reason; this is the principle of reversibility (ch. 14 of the Architecture): you do not change state without a trace of the why.

6. The ager: how decay operates

The ager is the process that applies decay and signals the state transitions that pure time requires. It runs once a day, in a night window (default: 04:00 local time), in a long but low-impact transaction:

  1. Decay: for every active mnest, compute weight_new = weight_old · exp(-λ · Δt) where Δt is the time elapsed since the last event. Update the weight and write a decay-kind events row.
  2. Decay state: every mnest that falls below the decay weight threshold moves to decaying; it is signalled in the proposals channel to the gateway.
  3. Archive proposal: every decaying mnest below archive weight with ts_last > 90 days enters the list of archival proposals shown to Roberto.
  4. Proto purge: every proto-mnest that falls below min proto weight is deleted (anti-noise).
  5. Recurring proto detection: search proto-mnests with uses >= SYNTH_TRIGGER_USES and weight >= SYNTH_TRIGGER_WEIGHT; signal them to the synt as candidates for synthesis.
  6. Snapshot: on the first of the month, copy the SQLite file into workspace/.mnestoma/snapshots/YYYY-MM.sqlite.

The ager is a pure example of a component that only does maintenance: it does not modify the structure of mnests, does not create executors, does not speak to the user. It proposes; the deciders are synt + Roberto, or the archival policy that the gateway applies in the next turn.

7. Pruning, snapshots, archival

Monthly snapshot

On the first of the month (UTC), the ager produces a snapshot of the live file:

workspace/.mnestoma/snapshots/2026-04.sqlite # current month
workspace/.mnestoma/snapshots/2026-03.sqlite.zst # older, compressed
workspace/.mnestoma/snapshots/2026-02.sqlite.zst...

The snapshot serves three purposes: rollback in case of corruption of the live file, historical analysis of the graph, trace for whoever wants to understand how Metnos «formed» over time. Snapshots older than 12 months are .zst-compressed but never deleted: history is a first-rate datum.

Live file pruning

The live file would stay up even when full, but for performance and cleanliness a periodic pruning helps:

Executor archival

When an executor moves to archived, its mnests move to superseded with reason = 'executor archived'; they remain readable, are excluded from the v_mnestome view, and after 90 days move to the archive table.

8. Linguistic variant: mnestome vs mnestoma

The term has two normative forms:

The double form is intentional and to be preserved. Translating mnestoma into mnestome in English gives the English reader the right reflex (the biological suffix evokes an emergent graph, like the microbiome). Translating mnestome into mnestoma in Italian follows the phonetic rule that avoids suffixes felt as incongruous (mnestome in Italian would sound like a verbal imperative, «you, memorise!»).

9. Bootstrap: the first mnestome

At first boot, the mnestome is empty. The question «then where do we start?» has three concrete answers:

  1. Start empty. For most instances, the mnestome is empty at first boot and forms over a month of use. It is slow but correct: the user's specificity enters the graph's shape immediately.
  2. Minimal seed (recommended). A seed of 12-15 known mnests, hard-coded in the repo, wires the most common passings between seed executors (e.g. read_files → read_files_pdf, get_urls → render_texts). They have state = 'seed' to distinguish them and weight BOOTSTRAP_WEIGHT (see mnest.html ch. 6).
  3. Import from snapshot. For migrations or sharing (future), a clean snapshot can be imported. Import goes through validation: the cited executors must exist and be signed.

10. Observability and debugging

The user can inspect the mnestome from the CLI (via the Metnos mnestome command):

Metnos mnestome list # all active mnests
Metnos mnestome list --tag invoice # filtered by tag
Metnos mnestome top 20 # the 20 heaviest
Metnos mnestome graph executor read_files # neighbourhood of an executor
Metnos mnestome proto # current proto-mnests
Metnos mnestome history mnest_01HW... # event history of a mnest
Metnos mnestome snapshot --month 2026-03 # opens the monthly snapshot

The web dashboard has a graphical view of the same content, with filters and drill-down. The transparency rule: the user can always know what Metnos thinks it knows.

11. Open questions

  1. Write concurrency. The gateway is a single process, so writes are naturally serial. When remote executors arrive in the future (ch. 4 of the Architecture), the gateway stays unique but calls may arrive from multiple turns in parallel. Is SQLite WAL enough? Open.
  2. Schema migration. The SQLite file lives long. How do we manage schema migrations across Metnos versions? Numbered declarative migrations, Alembic-style? Open.
  3. Snapshot privacy. Monthly snapshots contain executor names and tags, but no contents. The privacy surface is low, but if one wanted to share a snapshot for debug, a tag redaction function would be useful. To be designed if needed.
  4. Integration with episodic memory. The mnestome and the episodic memory of conversations are kept separate by design (ch. 3). Does it make sense to have an explicit cross-reference (a mnest citing the turn_id that generated it)? The inverse (a turn citing the mnests it reinforced) is already in the audit. Open.

first in the triad
executor
The node of the mnestome.
second in the triad
mnest
The edge of the mnestome.
level 1
Architecture, ch. 9
The big picture.
index
Microdesign
All docs.

Metnos — mnestome microdesign
New canonical doc (concept introduced by ).