From 71a4391272da4a5455b08a6e7339a8db90b8a5aa Mon Sep 17 00:00:00 2001 From: Will Zimmerman Date: Mon, 27 Apr 2026 18:44:56 -0700 Subject: [PATCH 1/4] refactor: derive migration backup path from db_path, drop dead BackupConfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit backup_dir was only consumed by the migration-time backup. The nested BackupConfig (Enabled/IntervalMinutes/BackupDir) was never read — periodic backups are not wired up anywhere. Remove the YAML field, the /tmp/ap-avs-backup default, and the dead struct. The migrator now writes to _backup automatically, keeping backups co-located with the DB instead of falling back to /tmp. --- config/aggregator.example.yaml | 5 +---- core/config/config.go | 28 +--------------------------- 2 files changed, 2 insertions(+), 31 deletions(-) diff --git a/config/aggregator.example.yaml b/config/aggregator.example.yaml index aa6a2014..b0507238 100644 --- a/config/aggregator.example.yaml +++ b/config/aggregator.example.yaml @@ -6,12 +6,9 @@ environment: development ## Aggregator Server Configs +# Migration backups are written to -backup automatically. db_path: /tmp/ap-avs/db -# Backup directory for manual backups (via REPL) and database migrations -# Periodic automated backups are not currently implemented -backup_dir: /tmp/ap-avs-backup - ## Aggregator Address: Replace with your aggregator's address ecdsa_private_key: diff --git a/core/config/config.go b/core/config/config.go index ebd7402a..39059f88 100644 --- a/core/config/config.go +++ b/core/config/config.go @@ -87,8 +87,6 @@ type Config struct { // Account abstraction config SmartWallet *SmartWalletConfig - BackupConfig BackupConfig - SocketPath string Environment sdklogging.LogLevel @@ -166,12 +164,6 @@ type SmartWalletConfig struct { MaxWalletsPerOwner int } -type BackupConfig struct { - Enabled bool // Whether periodic backups are enabled - IntervalMinutes int // Interval between backups in minutes - BackupDir string // Directory to store backups -} - // These are read from configPath type ConfigRaw struct { EcdsaPrivateKey string `yaml:"ecdsa_private_key"` @@ -188,7 +180,6 @@ type ConfigRaw struct { AVSRegistryCoordinatorAddr string `yaml:"avs_registry_coordinator_address"` DbPath string `yaml:"db_path"` - BackupDir string `yaml:"backup_dir"` JwtSecret string `yaml:"jwt_secret"` SmartWallet struct { @@ -203,12 +194,6 @@ type ConfigRaw struct { MaxWalletsPerOwner int `yaml:"max_wallets_per_owner"` } `yaml:"smart_wallet"` - Backup struct { - Enabled bool `yaml:"enabled"` - IntervalMinutes int `yaml:"interval_minutes"` - BackupDir string `yaml:"backup_dir"` - } `yaml:"backup"` - SocketPath string `yaml:"socket_path"` // List of approved operator addresses that can process tasks @@ -381,11 +366,6 @@ func NewConfig(configFilePath string) (*Config, error) { configRaw.SmartWallet.MaxWalletsPerOwner = HardMaxWalletsPerOwner } - if configRaw.BackupDir == "" { - // If backup dir is not set, use the default path, usually this path will be mount from our docker compose host - configRaw.BackupDir = "/tmp/ap-avs-backup" - } - config := &Config{ EcdsaPrivateKey: ecdsaPrivateKey, Logger: logger, @@ -406,7 +386,7 @@ func NewConfig(configFilePath string) (*Config, error) { AggregatorAddress: aggregatorAddr, DbPath: configRaw.DbPath, - BackupDir: configRaw.BackupDir, + BackupDir: configRaw.DbPath + "_backup", JwtSecret: []byte(configRaw.JwtSecret), SmartWallet: &SmartWalletConfig{ @@ -423,12 +403,6 @@ func NewConfig(configFilePath string) (*Config, error) { // PaymasterOwnerAddress will be populated below by calling owner() on the paymaster contract }, - BackupConfig: BackupConfig{ - Enabled: configRaw.Backup.Enabled, - IntervalMinutes: configRaw.Backup.IntervalMinutes, - BackupDir: configRaw.Backup.BackupDir, - }, - SocketPath: configRaw.SocketPath, MacroVars: configRaw.Macros["vars"], MacroSecrets: configRaw.Macros["secrets"], From e6d47e06e4065c170fe16e67d8933c589569d617 Mon Sep 17 00:00:00 2001 From: Will Zimmerman Date: Fri, 1 May 2026 16:26:48 -0700 Subject: [PATCH 2/4] fix: wrap Telegram subject prefix inside block The Simulation:/Run #N:/Run Node: prefix now renders inside the code block alongside the workflow name, restoring the pre-rename behavior where the whole "Simulation: WorkflowName" chunk was code-wrapped. --- core/taskengine/summarizer_format_telegram.go | 7 +++-- core/taskengine/summarizer_format_test.go | 30 +++++++++---------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/core/taskengine/summarizer_format_telegram.go b/core/taskengine/summarizer_format_telegram.go index 5a670ba3..118e2c0c 100644 --- a/core/taskengine/summarizer_format_telegram.go +++ b/core/taskengine/summarizer_format_telegram.go @@ -118,7 +118,8 @@ func getStatusEmoji(s Summary) string { } } -// formatSubjectWithBoldName formats the subject with tags only around the workflow name +// formatSubjectWithBoldName formats the subject with tags around the +// prefix + workflow name (everything except the trailing status suffix). // Subject patterns: // - "Simulation: {name} successfully completed" // - "Run Node: {name} succeeded" @@ -206,13 +207,13 @@ func formatSubjectWithBoldName(subject string) string { // Extract the workflow name name := subject[nameStart:nameEnd] - // Build the formatted string: prefix + name + suffix + // Build the formatted string: prefix + name + suffix // Using prevents Telegram from auto-linking names that contain dots var sb strings.Builder + sb.WriteString("") if prefix != "" { sb.WriteString(html.EscapeString(prefix)) } - sb.WriteString("") sb.WriteString(html.EscapeString(name)) sb.WriteString("") sb.WriteString(html.EscapeString(suffix)) diff --git a/core/taskengine/summarizer_format_test.go b/core/taskengine/summarizer_format_test.go index a48a52cc..19d7bd54 100644 --- a/core/taskengine/summarizer_format_test.go +++ b/core/taskengine/summarizer_format_test.go @@ -782,7 +782,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { }, }, expectedContain: []string{ - "✅ Simulation: Recurring Payment successfully completed", // Only workflow name is code-wrapped + "✅ Simulation: Recurring Payment successfully completed", // Prefix + name code-wrapped "Network: Sepolia", "Time: Jan 22, 2026 at 4:51 AM UTC", "Trigger: (Simulated) Your scheduled task (every 3 days at 11:00 PM) triggered on Sepolia.", @@ -803,7 +803,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { }, }, expectedContain: []string{ - "✅ Run #3: Recurring Payment successfully completed", // Only workflow name is code-wrapped + "✅ Run #3: Recurring Payment successfully completed", // Prefix + name code-wrapped "Network: Sepolia", "Time: Jan 22, 2026 at 4:51 AM UTC", "Executed:", @@ -823,7 +823,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { Errors: []string{"transfer1: Insufficient balance for transfer"}, }, expectedContain: []string{ - "❌ Run #5: Recurring Payment failed to execute", // Only workflow name is code-wrapped + "❌ Run #5: Recurring Payment failed to execute", // Prefix + name code-wrapped "Network: Sepolia", "What Went Wrong:", "• transfer1: Insufficient balance for transfer", @@ -846,7 +846,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { }, }, expectedContain: []string{ - "⚠️ Run #2: My Workflow successfully completed", + "⚠️ Run #2: My Workflow successfully completed", "1 node was skipped by Branch condition.", "Network: Ethereum", "Executed:", @@ -867,7 +867,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { Errors: []string{"loop1 - skipped due to condition not met: `code1.data.balance >= code1.data.totalNeeded` evaluated to false"}, }, expectedContain: []string{ - "⚠️ Simulation: Copy of Test Recurring Batch Send successfully completed", + "⚠️ Simulation: Copy of Test Recurring Batch Send successfully completed", "1 node was skipped by Branch condition.", "Network: Sepolia", "What Went Wrong:", @@ -943,7 +943,7 @@ func TestFormatTelegramFromStructured_PRDFormat(t *testing.T) { Executions: []ExecutionEntry{{Description: "(Simulated) On-chain transaction successfully completed"}}, }, expectedContain: []string{ - "✅ Simulation: My Workflow successfully completed", + "✅ Simulation: My Workflow successfully completed", "Network: Sepolia", "Trigger: (Simulated) Scheduled task ran on Sepolia", "Executed:", @@ -1160,27 +1160,27 @@ func TestFormatSubjectWithBoldName(t *testing.T) { { name: "simulation success", subject: "Simulation: My Workflow successfully completed", - expected: "Simulation: My Workflow successfully completed", + expected: "Simulation: My Workflow successfully completed", }, { name: "simulation failure", subject: "Simulation: Test Workflow failed to execute", - expected: "Simulation: Test Workflow failed to execute", + expected: "Simulation: Test Workflow failed to execute", }, { name: "simulation partial", subject: "Simulation: Another Workflow partially executed", - expected: "Simulation: Another Workflow partially executed", + expected: "Simulation: Another Workflow partially executed", }, { name: "run number success", subject: "Run #3: Payment Flow successfully completed", - expected: "Run #3: Payment Flow successfully completed", + expected: "Run #3: Payment Flow successfully completed", }, { name: "run number failure", subject: "Run #15: Swap Workflow failed to execute", - expected: "Run #15: Swap Workflow failed to execute", + expected: "Run #15: Swap Workflow failed to execute", }, { name: "no prefix success", @@ -1195,17 +1195,17 @@ func TestFormatSubjectWithBoldName(t *testing.T) { { name: "workflow name with special chars", subject: "Simulation: Copy of Recurring Payment & Report successfully completed", - expected: "Simulation: Copy of Recurring Payment & Report successfully completed", + expected: "Simulation: Copy of Recurring Payment & Report successfully completed", }, { name: "run node success", subject: "Run Node: My Transfer succeeded", - expected: "Run Node: My Transfer succeeded", + expected: "Run Node: My Transfer succeeded", }, { name: "run node failure", subject: "Run Node: My Transfer failed at transfer1", - expected: "Run Node: My Transfer failed at transfer1", + expected: "Run Node: My Transfer failed at transfer1", }, { name: "deployed workflow success", @@ -1391,7 +1391,7 @@ func TestComposeSummary_SimulateTaskFromClientPayload(t *testing.T) { telegram := FormatForMessageChannels(summary, "telegram", nil) expectedTelegramContents := []string{ - "✅ Simulation: Test settings.name successfully completed", + "✅ Simulation: Test settings.name successfully completed", "Network: Sepolia", "Time:", "Trigger: (Simulated) Scheduled task ran on Sepolia", From 58564e0b440353d72faa2859d9811206d8655e4c Mon Sep 17 00:00:00 2001 From: Will Zimmerman Date: Fri, 1 May 2026 16:32:58 -0700 Subject: [PATCH 3/4] docs: added instructions for ./docs/changes --- docs/changes/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 docs/changes/README.md diff --git a/docs/changes/README.md b/docs/changes/README.md new file mode 100644 index 00000000..771b5a89 --- /dev/null +++ b/docs/changes/README.md @@ -0,0 +1,11 @@ +# EigenLayer-AVS change log + +Long-form notes for changes worth remembering after the PR merges. Format and editorial guidance live in the `create-change-doc` Claude command (`~/.claude/commands/create-change-doc.md` locally) — this README only carries project-specific notes. + +Filename: `YYYYMMDD-kebab-case-title.md`. Each entry opens with a header block (Date, Status, Branch, Related) and a body adapted to the change (typically Problem / Decision / Alternatives / Verification). See existing entries for examples. + +Earlier entries (`0001-…`, `0002-…`) used a sequential four-digit prefix; those filenames are referenced from merged commits and stay as-is. New entries use the date prefix. + +## Project-specific notes + +_None yet. Add things here that only apply to this repo: dashboards we cross-link to, tags or prefixes we use, runbooks worth pointing at._ From f16cbad99de9e8f67cab76051087cbb2d9e1328a Mon Sep 17 00:00:00 2001 From: Will Zimmerman Date: Fri, 1 May 2026 16:37:07 -0700 Subject: [PATCH 4/4] docs: fix backup-path hyphen/underscore mismatch in example yaml The comment said -backup but the code derives _backup. Caught by both Copilot and Claude review on PR #529. --- config/aggregator.example.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/aggregator.example.yaml b/config/aggregator.example.yaml index b0507238..e568d03b 100644 --- a/config/aggregator.example.yaml +++ b/config/aggregator.example.yaml @@ -6,7 +6,7 @@ environment: development ## Aggregator Server Configs -# Migration backups are written to -backup automatically. +# Migration backups are written to _backup automatically. db_path: /tmp/ap-avs/db ## Aggregator Address: Replace with your aggregator's address