Create a new PersonalityMutationStore.
A storage adapter implementing the IStorageAdapter interface. The same adapter used by EmergentToolRegistry can be reused.
Record a new personality mutation.
Inserts a mutation record with initial strength of 1.0 and the current timestamp. The mutation ID is generated deterministically from the current time and a random suffix.
The mutation parameters (agent, trait, delta, reasoning, values).
The generated mutation ID.
Load all active mutations for a given agent.
Returns only mutations whose strength is above the 0.1 pruning threshold, ordered by creation time (newest first).
The agent whose mutations to load.
An array of PersonalityMutation records.
Compute the effective (strength-weighted) delta for each trait.
For each active mutation, multiplies the raw delta by the mutation's current strength, then sums per trait. This gives a realistic picture of how much each trait has drifted from baseline, accounting for decay.
The agent whose effective deltas to compute.
A map of trait name to effective delta (sum of delta * strength).
Decay all active mutations by the given rate and prune expired ones.
The amount to subtract from each mutation's strength. Typically 0.05 (the default from SelfImprovementConfig).
A DecayResult with counts of decayed and pruned mutations.
for production paths — use decayForAgent: this method is UNSCOPED (touches every agent's rows) and non-atomic (row-by-row writes double-decay on retry). Kept for administrative / maintenance use only.
For each mutation with strength above 0.1:
rate from its strength.This implements Ebbinghaus-style forgetting: mutations that aren't reinforced by repeated adaptation gradually fade away.
Agent-scoped, idempotent, atomic decay (spec batch-1 C6).
Runs one transaction that (a) inserts the (agent_id, cycle_id) guard
row FIRST — a replayed cycle id aborts as a no-op before touching any
mutation row; (b) deletes this agent's rows already at/below the 0.1
prune threshold (pre-existing sub-threshold rows never decay again
under decayAll's > 0.1 selector — this closes that leak); (c)
decays the remaining rows, pruning any whose post-decay strength is at
or below the threshold. Requires a transaction-capable adapter — the
decay unit must be all-or-nothing so a mid-cycle failure cannot
double-decay on retry. Runs on SQLite (3.24.0 or later, for the
guard's ON CONFLICT clause) and on PostgreSQL.
Each call writes a fresh randomUUID() token into the guard row's
owner_token and the time into its applied_at, reads the token back,
and decays only when the row holds its own token; every other call for
that cycle returns skipped. A token belongs to one call, so two calls
in one millisecond never both own a cycle. A guard row an earlier release
wrote has no owner_token, so its cycle stays applied. Before its
transaction the first call adds owner_token to a guard table an earlier
release made without it; when that ALTER fails, the call fails and the
next one runs it again.
The agent whose mutations decay (other agents' rows
are untouched — the class defect in decayAll).
Strength subtracted per cycle (typically
SelfImprovementConfig.personality.decayRate, default 0.05).
Stable-per-cycle identity (e.g. day:2026-07-20 UTC
buckets from decay-on-adapt). Replays are no-ops via the guard table.
Persistence layer for personality mutations with decay, on SQLite or PostgreSQL through the storage adapter.
Follows the same
ensureSchema()pattern as EmergentToolRegistry: a cached promise guards against concurrent DDL execution, and all DML methods await schema readiness before proceeding. A failed schema step is not cached: the next call runs it again.Example