diff --git a/cumulus/client/parachain-inherent/src/mock.rs b/cumulus/client/parachain-inherent/src/mock.rs
index 950cba2aaa7dec9e9dc8312191722f95f820cd6d..e08aca932564b94b66dcbe053c03be59620085e3 100644
--- a/cumulus/client/parachain-inherent/src/mock.rs
+++ b/cumulus/client/parachain-inherent/src/mock.rs
@@ -17,17 +17,17 @@
 use crate::{ParachainInherentData, INHERENT_IDENTIFIER};
 use codec::Decode;
 use cumulus_primitives_core::{
-	relay_chain, InboundDownwardMessage, InboundHrmpMessage, ParaId, PersistedValidationData,
+	relay_chain, relay_chain::UpgradeGoAhead, InboundDownwardMessage, InboundHrmpMessage, ParaId,
+	PersistedValidationData,
 };
 use cumulus_primitives_parachain_inherent::MessageQueueChain;
+use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
 use sc_client_api::{Backend, StorageProvider};
 use sp_crypto_hashing::twox_128;
 use sp_inherents::{InherentData, InherentDataProvider};
 use sp_runtime::traits::Block;
 use std::collections::BTreeMap;
 
-use cumulus_test_relay_sproof_builder::RelayStateSproofBuilder;
-
 /// Relay chain slot duration, in milliseconds.
 pub const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32 = 6000;
 
@@ -68,10 +68,12 @@ pub struct MockValidationDataInherentDataProvider<R = ()> {
 	pub xcm_config: MockXcmConfig,
 	/// Inbound downward XCM messages to be injected into the block.
 	pub raw_downward_messages: Vec<Vec<u8>>,
-	// Inbound Horizontal messages sorted by channel.
+	/// Inbound Horizontal messages sorted by channel.
 	pub raw_horizontal_messages: Vec<(ParaId, Vec<u8>)>,
-	// Additional key-value pairs that should be injected.
+	/// Additional key-value pairs that should be injected.
 	pub additional_key_values: Option<Vec<(Vec<u8>, Vec<u8>)>>,
+	/// Whether upgrade go ahead should be set.
+	pub upgrade_go_ahead: Option<UpgradeGoAhead>,
 }
 
 /// Something that can generate randomness.
@@ -176,6 +178,7 @@ impl<R: Send + Sync + GenerateRandomness<u64>> InherentDataProvider
 		sproof_builder.current_slot =
 			((relay_parent_number / RELAY_CHAIN_SLOT_DURATION_MILLIS) as u64).into();
 
+		sproof_builder.upgrade_go_ahead = self.upgrade_go_ahead;
 		// Process the downward messages and set up the correct head
 		let mut downward_messages = Vec::new();
 		let mut dmq_mqc = MessageQueueChain::new(self.xcm_config.starting_dmq_mqc_head);
diff --git a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs
index b7fc3489da2549978f948f299b04ae87c441e16c..6b72179075f445228f0a4133fa805ed6dbb01c02 100644
--- a/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs
+++ b/cumulus/polkadot-omni-node/lib/src/nodes/manual_seal.rs
@@ -21,12 +21,14 @@ use crate::common::{
 };
 use codec::Encode;
 use cumulus_client_parachain_inherent::{MockValidationDataInherentDataProvider, MockXcmConfig};
-use cumulus_primitives_core::ParaId;
+use cumulus_primitives_core::{CollectCollationInfo, ParaId};
+use polkadot_primitives::UpgradeGoAhead;
 use sc_consensus::{DefaultImportQueue, LongestChain};
 use sc_consensus_manual_seal::rpc::{ManualSeal, ManualSealApiServer};
 use sc_network::NetworkBackend;
 use sc_service::{Configuration, PartialComponents, TaskManager};
 use sc_telemetry::TelemetryHandle;
+use sp_api::ProvideRuntimeApi;
 use sp_runtime::traits::Header;
 use std::{marker::PhantomData, sync::Arc};
 
@@ -147,6 +149,18 @@ impl<NodeSpec: NodeSpecT> ManualSealNode<NodeSpec> {
 					.header(block)
 					.expect("Header lookup should succeed")
 					.expect("Header passed in as parent should be present in backend.");
+
+				let should_send_go_ahead = match client_for_cidp
+					.runtime_api()
+					.collect_collation_info(block, &current_para_head)
+				{
+					Ok(info) => info.new_validation_code.is_some(),
+					Err(e) => {
+						log::error!("Failed to collect collation info: {:?}", e);
+						false
+					},
+				};
+
 				let current_para_block_head =
 					Some(polkadot_primitives::HeadData(current_para_head.encode()));
 				let client_for_xcm = client_for_cidp.clone();
@@ -169,6 +183,12 @@ impl<NodeSpec: NodeSpecT> ManualSealNode<NodeSpec> {
 						raw_downward_messages: vec![],
 						raw_horizontal_messages: vec![],
 						additional_key_values: None,
+						upgrade_go_ahead: should_send_go_ahead.then(|| {
+							log::info!(
+								"Detected pending validation code, sending go-ahead signal."
+							);
+							UpgradeGoAhead::GoAhead
+						}),
 					};
 					Ok((
 						// This is intentional, as the runtime that we expect to run against this
diff --git a/prdoc/pr_6885.prdoc b/prdoc/pr_6885.prdoc
new file mode 100644
index 0000000000000000000000000000000000000000..986d76962289ac53a6697c0b708f06b69794fe38
--- /dev/null
+++ b/prdoc/pr_6885.prdoc
@@ -0,0 +1,11 @@
+title: 'Omni-node: Detect pending code in storage and send go ahead signal in dev-mode.'
+doc:
+- audience: Runtime Dev
+  description: |-
+    When using the polkadot-omni-node with manual seal (`--dev-block-time`), it is now possible to perform runtime
+    upgrades. The node will detect the pending validation code and send a go-ahead signal to the parachain.
+crates:
+- name: cumulus-client-parachain-inherent
+  bump: major
+- name: polkadot-omni-node-lib
+  bump: patch