nit: I’d prefer avoiding having this dual state. Here are 2 variations which do that:
Preserving behavior:
 0diff --git a/src/validation.cpp b/src/validation.cpp
 1index 7e82c3bbad..008bfe4d71 100644
 2--- a/src/validation.cpp
 3+++ b/src/validation.cpp
 4@@ -2423,8 +2423,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
 5         return true;
 6     }
 7 
 8-    auto fScriptChecks{true};
 9-    std::string script_check_reason;
10+    const char* script_check_reason{nullptr};
11     if (!m_chainman.AssumedValidBlock().IsNull()) {
12         constexpr int64_t TWO_WEEKS_IN_SECONDS{60 * 60 * 24 * 7 * 2};
13         // We've been configured with the hash of a block which has been externally verified to have a valid history.
14@@ -2458,7 +2457,6 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
15             //  artificially set the default assumed verified block further back.
16             // The test against the minimum chain work prevents the skipping when denied access to any chain at
17             //  least as good as the expected chain.
18-            fScriptChecks = false;
19         }
20     } else {
21         script_check_reason = "assumevalid=0 (always verify)";
22@@ -2573,16 +2571,15 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
23              Ticks<SecondsDouble>(m_chainman.time_forks),
24              Ticks<MillisecondsDouble>(m_chainman.time_forks) / m_chainman.num_blocks_total);
25 
26-    if (fScriptChecks != m_prev_script_checks_logged && GetRole() == ChainstateRole::NORMAL) {
27-        if (fScriptChecks) {
28-            Assume(!script_check_reason.empty());
29+    if ((script_check_reason != nullptr) != m_prev_script_checks_logged && GetRole() == ChainstateRole::NORMAL) {
30+        if (script_check_reason) {
31             LogInfo("Enabling script verification at block #%d (%s): %s.",
32                     pindex->nHeight, block_hash.ToString(), script_check_reason);
33         } else {
34             LogInfo("Disabling script verification at block #%d (%s).",
35                     pindex->nHeight, block_hash.ToString());
36         }
37-        m_prev_script_checks_logged = fScriptChecks;
38+        m_prev_script_checks_logged = script_check_reason != nullptr;
39     }
40 
41     CBlockUndo blockundo;
42@@ -2593,7 +2590,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
43     // doesn't invalidate pointers into the vector, and keep txsdata in scope
44     // for as long as `control`.
45     std::optional<CCheckQueueControl<CScriptCheck>> control;
46-    if (auto& queue = m_chainman.GetCheckQueue(); queue.HasThreads() && fScriptChecks) control.emplace(queue);
47+    if (auto& queue = m_chainman.GetCheckQueue(); queue.HasThreads() && script_check_reason != nullptr) control.emplace(queue);
48 
49     std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
50 
51@@ -2652,7 +2649,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
52             break;
53         }
54 
55-        if (!tx.IsCoinBase() && fScriptChecks)
56+        if (!tx.IsCoinBase() && script_check_reason != nullptr)
57         {
58             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
59             bool tx_ok;
 0diff --git a/src/validation.cpp b/src/validation.cpp
 1index 7e82c3bbad..79ad71b64c 100644
 2--- a/src/validation.cpp
 3+++ b/src/validation.cpp
 4@@ -2423,8 +2423,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
 5         return true;
 6     }
 7 
 8-    auto fScriptChecks{true};
 9-    std::string script_check_reason;
10+    const char* script_check_reason{nullptr};
11     if (!m_chainman.AssumedValidBlock().IsNull()) {
12         constexpr int64_t TWO_WEEKS_IN_SECONDS{60 * 60 * 24 * 7 * 2};
13         // We've been configured with the hash of a block which has been externally verified to have a valid history.
14@@ -2458,7 +2457,6 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
15             //  artificially set the default assumed verified block further back.
16             // The test against the minimum chain work prevents the skipping when denied access to any chain at
17             //  least as good as the expected chain.
18-            fScriptChecks = false;
19         }
20     } else {
21         script_check_reason = "assumevalid=0 (always verify)";
22@@ -2573,16 +2571,15 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
23              Ticks<SecondsDouble>(m_chainman.time_forks),
24              Ticks<MillisecondsDouble>(m_chainman.time_forks) / m_chainman.num_blocks_total);
25 
26-    if (fScriptChecks != m_prev_script_checks_logged && GetRole() == ChainstateRole::NORMAL) {
27-        if (fScriptChecks) {
28-            Assume(!script_check_reason.empty());
29+    if (script_check_reason != m_prev_script_check_reason && GetRole() == ChainstateRole::NORMAL) {
30+        if (script_check_reason) {
31             LogInfo("Enabling script verification at block #%d (%s): %s.",
32                     pindex->nHeight, block_hash.ToString(), script_check_reason);
33         } else {
34             LogInfo("Disabling script verification at block #%d (%s).",
35                     pindex->nHeight, block_hash.ToString());
36         }
37-        m_prev_script_checks_logged = fScriptChecks;
38+        m_prev_script_check_reason = script_check_reason;
39     }
40 
41     CBlockUndo blockundo;
42@@ -2593,7 +2590,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
43     // doesn't invalidate pointers into the vector, and keep txsdata in scope
44     // for as long as `control`.
45     std::optional<CCheckQueueControl<CScriptCheck>> control;
46-    if (auto& queue = m_chainman.GetCheckQueue(); queue.HasThreads() && fScriptChecks) control.emplace(queue);
47+    if (auto& queue = m_chainman.GetCheckQueue(); queue.HasThreads() && script_check_reason) control.emplace(queue);
48 
49     std::vector<PrecomputedTransactionData> txsdata(block.vtx.size());
50 
51@@ -2652,7 +2649,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state,
52             break;
53         }
54 
55-        if (!tx.IsCoinBase() && fScriptChecks)
56+        if (!tx.IsCoinBase() && script_check_reason)
57         {
58             bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */
59             bool tx_ok;
60diff --git a/src/validation.h b/src/validation.h
61index fea11c5515..6e426a4660 100644
62--- a/src/validation.h
63+++ b/src/validation.h
64@@ -560,7 +560,7 @@ protected:
65     //! Cached result of LookupBlockIndex(*m_from_snapshot_blockhash)
66     mutable const CBlockIndex* m_cached_snapshot_base GUARDED_BY(::cs_main){nullptr};
67 
68-    std::optional<bool> m_prev_script_checks_logged GUARDED_BY(::cs_main){};
69+    std::optional<const char*> m_prev_script_check_reason GUARDED_BY(::cs_main){};
70 
71 public:
72     //! Reference to a BlockManager instance which itself is shared across all