OpenClaw to Hermes Migration (2/13) — What to Preserve, Partially Port, or Discard
A code review record classifying operational scripts into three tiers: preserve, partial-preserve, and discard.
What This Post Covers
- An asset classification framework (preserve / partial-preserve / discard) for migrating a legacy agent system to a successor
- A concrete case applying the Strangler Fig + Subset Migration strategy to a memory pipeline
- Classification results evaluated across LOC, external dependencies, and replaceability axes
- A method for slimming down a bloated single file (1,310 LOC) using the migration as a natural entry point
Problem Definition: Port Everything or Cut Deep?
The OpenClaw inventory comprises multiple agents, multiple scheduler/daemon processes, and multiple operational scripts. When moving to the successor system, Hermes, the choice is not binary. A full port carries accumulated technical debt along with it; a minimal port discards hard-won domain logic. Code-review-based asset classification resolves that gap.
Decision Context — The Five Structural Constraints That Drive Classification
Before classifying assets, the target architecture must be fixed. Classification criteria derive from the question: "Can this code fit into the destination structure?"
- Strategy: Strangler Fig (incremental replacement) + Subset Migration (partial port)
- Memory: SQLite + Python library (memcore). No daemon. Memory is accessed by importing the library, not via a separate process.
- Directory: Isolated under
~/Hermes/. OpenClaw under~/Project/is preserved as a read-only backup. - Agents: A new "Hermes management" agent is created. The "OpenClaw management" agent is demoted to backup-only duty.
- Cutover: Dual execution (running the same functionality on both sides for comparison) → cutover after verification passes.
These five constraints automatically derive the classification criteria: "Can it be absorbed into the memcore library? Can it be folded into a Hermes profile? Does a replacement already exist?"
Preserve (Must Port)
| File | LOC | Port Form | Value |
|---|---|---|---|
| user-pattern-stage.py | 260 | memcore.dialectic module |
Highest. U-tag 3-phase (observe → hypothesize → verify), Honcho-inspired, anti-bias guard |
| confidence-decay.py | 82 | memcore cron tick | Decays opinion confidence by 0.02; removes entries below 0.30 |
| topics-validate.py | 131 | memcore consistency check | Ensures bank ↔ topics coherence |
| topics-expand.py | 186 | memcore auto-registration | Auto-registers newly created files |
| bank-lint.py | 176 | memcore validator | Validates Retain tag format and data integrity |
| entity-audit.py | 152 | memcore stale detection | Detects 30-day stale entries via SQLite timestamp queries |
| docs-snapshot.sh | 141 | Hermes cron payload | Detects docs changes; only the URL needs updating |
| release-check.sh | 66 | Hermes cron payload | Detects version changes |
Classification Principle
The decisive axis for a preserve verdict is absence of external dependencies. Every preserved asset uses only the standard library; file I/O routes through lib_common.py. The migration reduces to a single operation: replacing lib_common.py with memcore.io. Average size is around 175 LOC — small enough to verify equivalence with unit tests.
Top-Priority Asset: user-pattern-stage.py
This file implements a three-phase dialectic (observe → hypothesize → verify) for tracking user utterance patterns and blocking confirmation bias. No equivalent exists in Hermes. Whether or not this single file gets ported accounts for a significant share of the migration's justification.
Partial-Preserve
| File | LOC | Handling |
|---|---|---|
| bank-size-watch.py | 176 | Weakened after SQLite migration. Extract only the MemTree split logic. |
| proactive-exception-alerts.py | 1,310 | Severely bloated. Extract only the escalation-reason scoring logic (~300 LOC); delegate notification routing and context collection to the Hermes gateway/hook infrastructure. |
Bloated File Slim-Down Pattern
proactive-exception-alerts.py at 1,310 LOC carries the highest accumulated debt. Decomposing its structure reveals three layers:
- Scoring logic (~300 LOC): the core evaluation of escalation reasons — migration target
- Notification routing: delegatable to the Hermes gateway
- Context collection: delegatable to the Hermes hook infrastructure
Migration provides a natural entry point for slimming bloated files like this. Reducing from 1,310 to 300 LOC is debt elimination without functional loss. Layers 2 and 3 have equivalent capabilities in the destination infrastructure, so the rewrite cost approaches zero.
Discard
| File | LOC | Replacement |
|---|---|---|
| heartbeat-tick.py | 262 | 100% replaced by Hermes cron + on_turn_start hook |
heartbeat-tick.py provides functionality that the destination infrastructure (cron + on_turn_start hook) covers by default. No migration value. Discard here means isolation into the read-only backup (~/Project/), not physical deletion — the file remains accessible for reference at any time.
Code Review Checklist (Reusable)
Applying these questions in sequence to each file converges on a verdict.
- Does the destination have an equivalent feature? — If yes, discard candidate.
- Are there external dependencies? — If none, porting cost is low → preserve candidate.
- Is file size proportional to its responsibility? — If bloated, partial-preserve (slim down).
- Is there unique domain logic? — If present and expensive to reimplement, top-priority preserve.
- Can equivalence be verified by tests? — Must be true for dual-execution cutover to hold.
Four Distilled Principles
- Porting difficulty is determined by the dependency graph, not LOC. Preserved assets port quickly because they carry no external dependencies beyond
lib_common.py. - The work lives in verification, not in the port itself. Moving code takes a day or two; dual-execution verification takes longer.
- Unique domain logic has nonlinear value relative to LOC.
user-pattern-stage.pyis 260 LOC, but its lack of any replacement makes it the highest-priority asset. - Migration is a natural opportunity for debt reduction. Slimming a bloated file (1,310 LOC) during migration costs less than a standalone refactoring project.
Scope of Applicability and Open Questions
This classification technique applies readily to the memory and pipeline layer of a "legacy system → successor system" migration. The same criteria do not transfer unchanged to the UI layer or the data schema layer, where schema compatibility, data volume, and migration downtime become the dominant variables.
Open questions:
- Do the assets classified as preserve actually demonstrate equivalence under dual-execution? This remains an assumption not yet confirmed by data.
- Is the slim-down ratio seen in the partial-preserve case (1,310 → 300 LOC) a reproducible pattern, or a structural peculiarity of
proactive-exception-alerts.py? Needs isolation.
Series overview: Series index
๋๊ธ
๋๊ธ ์ฐ๊ธฐ