You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Follow-up to #1985 (the guarded edit_soul tool, ADR 0081). The due-diligence on that PR found the field's consensus is that versioning + rollback is reactive — it lets a human catch and revert drift but doesn't detect it. The recommended addition is a periodic baseline anchor / drift check. ADR 0081 documents this as a known gap; this issue tracks building it.
Why it's cheap: soul-history already did the hard part
write_soul snapshots every outgoing persona to soul-history (#1691), so we already have — with zero new storage — the two things a drift check needs:
a baseline (earliest authored version, or an operator-pinned "known-good" revision), and
the full edit trail.
list_soul_versions() / read_soul_version(id) / soul_revision() already expose them. This turns "build a persona-history subsystem" into "add a comparator + a scheduled trigger."
Design
Baseline = earliest soul-history entry, or an operator-pinned revision.
Signals — cheap first, LLM only if enabled:
Deterministic (free, no model): edits since baseline, net size delta, section churn (baseline sections dropped / new ones added), % of baseline retained via difflib.SequenceMatcher. Catches silent accretion and "losing sight of the persona."
Semantic (opt-in LLM-judge): compare baseline vs current → {drift_score, identity_preserved, doctrine_leak, rationale}. The doctrine_leak field also closes the other feat(persona): guarded edit_soul tool — the agent can refine its own SOUL.md (ADR 0081) #1985 follow-up (the persona-vs-doctrine guard — flag when operating instructions accrete into SOUL, the CLAUDE.md/AGENTS.md bloat failure). One judge covers both.
Altitude: a scheduled curation pass — same lineage as dream/distill (ADR 0054): read-only, runs daily or after every N self-edits.
So the whole loop reuses infrastructure: soul-history (baseline + trail), the event bus (surface), the curation-subagent altitude, and the existing restore (remedy).
Open decision
How aggressive the metric is:
Deterministic-only — free, no model call, ships fast. Catches accretion/size/section drift.
+ LLM-judge — also catches semantic drift + doctrine leak, at the cost of a scheduled model call.
Recommendation: start deterministic-only (immediately useful, no cost), add the judge as an opt-in second stage that also subsumes the doctrine-leak guard.
Hermes's self-evolution repo runs a "semantic preservation — must not drift from original purpose" gate (offline, human-reviewed).
Letta added a read-only persona guard after unconstrained self-edits degraded identity.
Drift literature (arXiv 2601.04170 "Adaptive Behavioral Anchoring") recommends anchoring to a baseline period.
Scope
Not blocking #1985 (that ships with reactive guards: off-by-default, section-scoped, snapshot+rollback, operator notice). This is the proactive layer on top.
Follow-up to #1985 (the guarded
edit_soultool, ADR 0081). The due-diligence on that PR found the field's consensus is that versioning + rollback is reactive — it lets a human catch and revert drift but doesn't detect it. The recommended addition is a periodic baseline anchor / drift check. ADR 0081 documents this as a known gap; this issue tracks building it.Why it's cheap: soul-history already did the hard part
write_soulsnapshots every outgoing persona to soul-history (#1691), so we already have — with zero new storage — the two things a drift check needs:list_soul_versions()/read_soul_version(id)/soul_revision()already expose them. This turns "build a persona-history subsystem" into "add a comparator + a scheduled trigger."Design
Baseline = earliest soul-history entry, or an operator-pinned revision.
Signals — cheap first, LLM only if enabled:
% of baseline retainedviadifflib.SequenceMatcher. Catches silent accretion and "losing sight of the persona."{drift_score, identity_preserved, doctrine_leak, rationale}. Thedoctrine_leakfield also closes the other feat(persona): guarded edit_soul tool — the agent can refine its own SOUL.md (ADR 0081) #1985 follow-up (the persona-vs-doctrine guard — flag when operating instructions accrete into SOUL, the CLAUDE.md/AGENTS.md bloat failure). One judge covers both.Altitude: a scheduled curation pass — same lineage as dream/distill (ADR 0054): read-only, runs daily or after every N self-edits.
Surface + remedy — both already exist:
persona.drift_detected(score + rationale) on the event bus (thepersona.self_editednotice seam added in feat(persona): guarded edit_soul tool — the agent can refine its own SOUL.md (ADR 0081) #1985), → console alert;POST /api/config/soul/history/{id}/restore).So the whole loop reuses infrastructure: soul-history (baseline + trail), the event bus (surface), the curation-subagent altitude, and the existing restore (remedy).
Open decision
How aggressive the metric is:
Recommendation: start deterministic-only (immediately useful, no cost), add the judge as an opt-in second stage that also subsumes the doctrine-leak guard.
Prior art (from the #1985 due diligence)
Scope
Not blocking #1985 (that ships with reactive guards: off-by-default, section-scoped, snapshot+rollback, operator notice). This is the proactive layer on top.