diff --git a/core/taskengine/summarizer_format_email.go b/core/taskengine/summarizer_format_email.go index ba9278f6..f83d6ef3 100644 --- a/core/taskengine/summarizer_format_email.go +++ b/core/taskengine/summarizer_format_email.go @@ -335,7 +335,7 @@ func buildFeesSectionHTML(s Summary) string { if s.Workflow != nil && s.Workflow.IsSimulation { // Heading omitted — the placeholder line carries enough context on its own. return `
` + - `

⛽ (cost estimated at deploy)

` + + `

⛽ (see cost estimate before deploy)

` + `
` } diff --git a/core/taskengine/summarizer_format_telegram.go b/core/taskengine/summarizer_format_telegram.go index 48e7e14e..034b7afb 100644 --- a/core/taskengine/summarizer_format_telegram.go +++ b/core/taskengine/summarizer_format_telegram.go @@ -250,11 +250,11 @@ func formatSubjectWithBoldName(subject string) string { // Format: "⛽ Cost: 0.000003 ETH ($0.01), 1.2 USDC ($1.20)" — native // token first, comma-separated, USD parenthetical per token. Unpriceable // tokens render as "$?". For simulations the line collapses to the static -// "⛽ (cost estimated at deploy)" placeholder. Returns "" when there's +// "⛽ (see cost estimate before deploy)" placeholder. Returns "" when there's // nothing to render. func formatTelegramCostLine(s Summary) string { if s.Workflow != nil && s.Workflow.IsSimulation { - return "⛽ (cost estimated at deploy)\n" + return "⛽ (see cost estimate before deploy)\n" } if s.Fees == nil || len(s.Fees.Total) == 0 { return "" diff --git a/core/taskengine/summarizer_format_test.go b/core/taskengine/summarizer_format_test.go index cf3007e9..74de99d2 100644 --- a/core/taskengine/summarizer_format_test.go +++ b/core/taskengine/summarizer_format_test.go @@ -1069,7 +1069,7 @@ func TestFormatTelegramFromStructured_RunnerAndFees(t *testing.T) { if strings.Contains(out, "(~") || strings.Contains(out, "Value fee:") { t.Errorf("Telegram should not render gas-units detail or Value fee line, got:\n%s", out) } - if strings.Contains(out, "(cost estimated at deploy)") { + if strings.Contains(out, "(see cost estimate before deploy)") { t.Errorf("deployed run should not show simulation placeholder, got:\n%s", out) } @@ -1290,7 +1290,7 @@ func mustBigInt(s string) *big.Int { } // TestFormatTelegramFromStructured_Simulation_PlaceholderCost confirms simulation -// runs render the "(cost estimated at deploy)" placeholder instead of fake-precision +// runs render the "(see cost estimate before deploy)" placeholder instead of fake-precision // numbers. Sim gas prices are conservative chain defaults, so any specific ETH/gas // figure would mislead the user. func TestFormatTelegramFromStructured_Simulation_PlaceholderCost(t *testing.T) { @@ -1318,7 +1318,7 @@ func TestFormatTelegramFromStructured_Simulation_PlaceholderCost(t *testing.T) { out := FormatForMessageChannels(summary, "telegram", nil) - if !strings.Contains(out, "⛽ (cost estimated at deploy)") { + if !strings.Contains(out, "⛽ (see cost estimate before deploy)") { t.Errorf("simulation should render the deploy-time placeholder, got:\n%s", out) } for _, banned := range []string{"Cost:", "0.0000105 ETH", "21 K gas", "platform fee"} { @@ -1729,7 +1729,7 @@ func TestComposeSummary_SimulateTaskFromClientPayload(t *testing.T) { } // The simulation Cost placeholder uses italic text; allow that but no other italics. - if strings.Contains(telegram, "") && !strings.Contains(telegram, "(cost estimated at deploy)") { + if strings.Contains(telegram, "") && !strings.Contains(telegram, "(see cost estimate before deploy)") { t.Errorf("Telegram should not contain italic annotation other than the cost placeholder, got:\n%s", telegram) } diff --git a/docs/changes/20260502-notification-cost-line-and-runner.md b/docs/changes/20260502-notification-cost-line-and-runner.md index e24fee28..0897080c 100644 --- a/docs/changes/20260502-notification-cost-line-and-runner.md +++ b/docs/changes/20260502-notification-cost-line-and-runner.md @@ -45,7 +45,7 @@ Single-line, multi-token, native unit first with USD parenthetical per token: | ETH gas + USDC value-fee | `⛽ Cost: 0.000003 ETH ($0.01), 1.2 USDC ($1.20)` | | Unpriceable ERC20 | `⛽ Cost: 0.000003 ETH ($0.01), 0.005 PEPE ($?)` | | Read-only deployed (no on-chain steps) | (line omitted) | -| Simulation | `⛽ (cost estimated at deploy)` — static, no numbers | +| Simulation | `⛽ (see cost estimate before deploy)` — static, no numbers | Telegram drops bullets, gas-units, and value-fee detail; email matches. Breakdowns live on the dashboard. Simulation collapses to the placeholder because sim gas prices are conservative chain defaults rather than real network conditions — any specific number would mislead. @@ -83,7 +83,7 @@ To make simulation cogs[] non-empty in the first place, the Tenderly client now - `TestPercentOfRaw` — value-fee percentage math against raw amounts. - `TestTokenBucketToTokenTotal` — stablecoin shortcut, zero-rounding omission, missing price service path. - `TestFormatTelegramFromStructured_RunnerAndFees` and `TestFormatTelegramFromStructured_MultiToken_USDPlaceholder` — end-to-end render assertions. -- Production verification: the next deployed run on Sepolia after `cba1ac8` showed the Telegram block ordering with Runner-and-Network folded onto one line; the simulation summary correctly rendered `⛽ (cost estimated at deploy)` after `4f784c9` (`vm.IsSimulation` flowing through to the formatter). +- Production verification: the next deployed run on Sepolia after `cba1ac8` showed the Telegram block ordering with Runner-and-Network folded onto one line; the simulation summary correctly rendered `⛽ (see cost estimate before deploy)` after `4f784c9` (`vm.IsSimulation` flowing through to the formatter). - The aggregator's existing tests pass with the price service possibly nil: executor (`executor.go:321,478`), fee estimator (`fee_estimator.go:195`), and the new `buildTotalsFromVM` all guard against `nil` rather than panicking. ## Cross-repo coordination