diff --git a/polkadot/Cargo.lock b/polkadot/Cargo.lock
index 6c853480ca2fff7b1933d391aeb35b8d2e3b0d99..10b016a8dce414d2a46595d35c65d7ef2912d621 100644
--- a/polkadot/Cargo.lock
+++ b/polkadot/Cargo.lock
@@ -4313,7 +4313,7 @@ checksum = "feb3b2b1033b8a60b4da6ee470325f887758c95d5320f52f9ce0df055a55940e"
 
 [[package]]
 name = "polkadot"
-version = "0.8.22"
+version = "0.8.19"
 dependencies = [
  "assert_cmd",
  "futures 0.3.5",
@@ -4633,6 +4633,21 @@ dependencies = [
  "wasm-timer",
 ]
 
+[[package]]
+name = "polkadot-node-core-provisioner"
+version = "0.1.0"
+dependencies = [
+ "bitvec",
+ "derive_more 0.99.9",
+ "futures 0.3.5",
+ "lazy_static",
+ "log 0.4.8",
+ "polkadot-node-subsystem",
+ "polkadot-primitives",
+ "sp-core",
+ "tokio 0.2.21",
+]
+
 [[package]]
 name = "polkadot-node-core-runtime-api"
 version = "0.1.0"
diff --git a/polkadot/Cargo.toml b/polkadot/Cargo.toml
index c5d741b155d0ac31361e05f3855ec92a242b301c..a56de658b36fad339ae278cc2b0e0e57ea383e4a 100644
--- a/polkadot/Cargo.toml
+++ b/polkadot/Cargo.toml
@@ -4,7 +4,7 @@ path = "src/main.rs"
 
 [package]
 name = "polkadot"
-version = "0.8.22"
+version = "0.8.19"
 authors = ["Parity Technologies <admin@parity.io>"]
 edition = "2018"
 
@@ -51,6 +51,7 @@ members = [
 	"node/core/candidate-validation",
 	"node/core/chain-api",
 	"node/core/proposer",
+	"node/core/provisioner",
 	"node/core/runtime-api",
 	"node/network/bridge",
 	"node/network/pov-distribution",
diff --git a/polkadot/node/core/backing/src/lib.rs b/polkadot/node/core/backing/src/lib.rs
index 11f82d3d8357fc9abaee18d8fcdb936f292395fc..f7608f0d0f4b152ec7b1a4c039df2bde1b7ab1d7 100644
--- a/polkadot/node/core/backing/src/lib.rs
+++ b/polkadot/node/core/backing/src/lib.rs
@@ -36,10 +36,9 @@ use polkadot_primitives::v1::{
 };
 use polkadot_node_primitives::{
 	FromTableMisbehavior, Statement, SignedFullStatement, MisbehaviorReport,
-	ValidationOutputs, ValidationResult, SpawnNamed,
+	ValidationOutputs, ValidationResult,
 };
 use polkadot_subsystem::{
-	Subsystem, SubsystemContext, SpawnedSubsystem,
 	messages::{
 		AllMessages, AvailabilityStoreMessage, CandidateBackingMessage, CandidateSelectionMessage,
 		CandidateValidationMessage, NewBackedCandidate, PoVDistributionMessage, ProvisionableData,
@@ -54,6 +53,7 @@ use polkadot_subsystem::{
 		request_from_runtime,
 		Validator,
 	},
+	delegated_subsystem,
 };
 use statement_table::{
 	generic::AttestedCandidate as TableAttestedCandidate,
@@ -772,45 +772,7 @@ impl util::JobTrait for CandidateBackingJob {
 	}
 }
 
-/// Manager type for the CandidateBackingSubsystem
-type Manager<Spawner, Context> = util::JobManager<Spawner, Context, CandidateBackingJob>;
-
-/// An implementation of the Candidate Backing subsystem.
-pub struct CandidateBackingSubsystem<Spawner, Context> {
-	manager: Manager<Spawner, Context>,
-}
-
-impl<Spawner, Context> CandidateBackingSubsystem<Spawner, Context>
-where
-	Spawner: Clone + SpawnNamed + Send + Unpin,
-	Context: SubsystemContext,
-	ToJob: From<<Context as SubsystemContext>::Message>,
-{
-	/// Creates a new `CandidateBackingSubsystem`.
-	pub fn new(spawner: Spawner, keystore: KeyStorePtr) -> Self {
-		CandidateBackingSubsystem {
-			manager: util::JobManager::new(spawner, keystore)
-		}
-	}
-
-	/// Run this subsystem
-	pub async fn run(ctx: Context, keystore: KeyStorePtr, spawner: Spawner) {
-		<Manager<Spawner, Context>>::run(ctx, keystore, spawner, None).await
-	}
-}
-
-impl<Spawner, Context> Subsystem<Context> for CandidateBackingSubsystem<Spawner, Context>
-where
-	Spawner: SpawnNamed + Send + Clone + Unpin + 'static,
-	Context: SubsystemContext,
-	<Context as SubsystemContext>::Message: Into<ToJob>,
-{
-	fn start(self, ctx: Context) -> SpawnedSubsystem {
-		self.manager.start(ctx)
-	}
-}
-
-
+delegated_subsystem!(CandidateBackingJob(KeyStorePtr) <- ToJob as CandidateBackingSubsystem);
 
 #[cfg(test)]
 mod tests {
diff --git a/polkadot/node/core/provisioner/Cargo.toml b/polkadot/node/core/provisioner/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..6c6477c904432e2d2a7f739e557fbb63db7c7047
--- /dev/null
+++ b/polkadot/node/core/provisioner/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "polkadot-node-core-provisioner"
+version = "0.1.0"
+authors = ["Parity Technologies <admin@parity.io>"]
+edition = "2018"
+
+[dependencies]
+bitvec = { version = "0.17.4", default-features = false, features = ["alloc"] }
+derive_more = "0.99.9"
+futures = "0.3.5"
+log = "0.4.8"
+polkadot-primitives = { path = "../../../primitives" }
+polkadot-node-subsystem = { path = "../../subsystem" }
+
+[dev-dependencies]
+lazy_static = "1.4"
+sp-core = { git = "https://github.com/paritytech/substrate", branch = "master" }
+tokio = "0.2"
diff --git a/polkadot/node/core/provisioner/src/lib.rs b/polkadot/node/core/provisioner/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..712c1a834f6c8560b57c42a93bf17d21570958d1
--- /dev/null
+++ b/polkadot/node/core/provisioner/src/lib.rs
@@ -0,0 +1,844 @@
+// Copyright 2020 Parity Technologies (UK) Ltd.
+// This file is part of Polkadot.
+
+// Polkadot is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+
+// Polkadot is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License
+// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
+
+//! The provisioner is responsible for assembling a relay chain block
+//! from a set of available parachain candidates of its choice.
+
+#![deny(missing_docs)]
+
+use bitvec::vec::BitVec;
+use futures::{
+	channel::{mpsc, oneshot},
+	prelude::*,
+};
+use polkadot_node_subsystem::{
+	delegated_subsystem,
+	errors::{ChainApiError, RuntimeApiError},
+	messages::{
+		AllMessages, ChainApiMessage, ProvisionableData, ProvisionerInherentData,
+		ProvisionerMessage, RuntimeApiMessage,
+	},
+	util::{
+		self, request_availability_cores, request_global_validation_data,
+		request_local_validation_data, JobTrait, ToJobTrait,
+	},
+};
+use polkadot_primitives::v1::{
+	validation_data_hash, BackedCandidate, BlockNumber, CoreState, Hash, OccupiedCoreAssumption,
+	SignedAvailabilityBitfield,
+};
+use std::{collections::HashMap, convert::TryFrom, pin::Pin};
+
+struct ProvisioningJob {
+	relay_parent: Hash,
+	sender: mpsc::Sender<FromJob>,
+	receiver: mpsc::Receiver<ToJob>,
+	provisionable_data_channels: Vec<mpsc::Sender<ProvisionableData>>,
+	backed_candidates: Vec<BackedCandidate>,
+	signed_bitfields: Vec<SignedAvailabilityBitfield>,
+}
+
+/// This enum defines the messages that the provisioner is prepared to receive.
+pub enum ToJob {
+	/// The provisioner message is the main input to the provisioner.
+	Provisioner(ProvisionerMessage),
+	/// This message indicates that the provisioner should shut itself down.
+	Stop,
+}
+
+impl ToJobTrait for ToJob {
+	const STOP: Self = Self::Stop;
+
+	fn relay_parent(&self) -> Option<Hash> {
+		match self {
+			Self::Provisioner(pm) => pm.relay_parent(),
+			Self::Stop => None,
+		}
+	}
+}
+
+impl TryFrom<AllMessages> for ToJob {
+	type Error = ();
+
+	fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
+		match msg {
+			AllMessages::Provisioner(pm) => Ok(Self::Provisioner(pm)),
+			_ => Err(()),
+		}
+	}
+}
+
+impl From<ProvisionerMessage> for ToJob {
+	fn from(pm: ProvisionerMessage) -> Self {
+		Self::Provisioner(pm)
+	}
+}
+
+enum FromJob {
+	ChainApi(ChainApiMessage),
+	Runtime(RuntimeApiMessage),
+}
+
+impl From<FromJob> for AllMessages {
+	fn from(from_job: FromJob) -> AllMessages {
+		match from_job {
+			FromJob::ChainApi(cam) => AllMessages::ChainApi(cam),
+			FromJob::Runtime(ram) => AllMessages::RuntimeApi(ram),
+		}
+	}
+}
+
+impl TryFrom<AllMessages> for FromJob {
+	type Error = ();
+
+	fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
+		match msg {
+			AllMessages::ChainApi(chain) => Ok(FromJob::ChainApi(chain)),
+			AllMessages::RuntimeApi(runtime) => Ok(FromJob::Runtime(runtime)),
+			_ => Err(()),
+		}
+	}
+}
+
+#[derive(Debug, derive_more::From)]
+enum Error {
+	#[from]
+	Sending(mpsc::SendError),
+	#[from]
+	Util(util::Error),
+	#[from]
+	OneshotRecv(oneshot::Canceled),
+	#[from]
+	ChainApi(ChainApiError),
+	#[from]
+	Runtime(RuntimeApiError),
+	OneshotSend,
+}
+
+impl JobTrait for ProvisioningJob {
+	type ToJob = ToJob;
+	type FromJob = FromJob;
+	type Error = Error;
+	type RunArgs = ();
+
+	const NAME: &'static str = "ProvisioningJob";
+
+	/// Run a job for the parent block indicated
+	//
+	// this function is in charge of creating and executing the job's main loop
+	fn run(
+		relay_parent: Hash,
+		_run_args: Self::RunArgs,
+		receiver: mpsc::Receiver<ToJob>,
+		sender: mpsc::Sender<FromJob>,
+	) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
+		async move {
+			let job = ProvisioningJob::new(relay_parent, sender, receiver);
+
+			// it isn't necessary to break run_loop into its own function,
+			// but it's convenient to separate the concerns in this way
+			job.run_loop().await
+		}
+		.boxed()
+	}
+}
+
+impl ProvisioningJob {
+	pub fn new(
+		relay_parent: Hash,
+		sender: mpsc::Sender<FromJob>,
+		receiver: mpsc::Receiver<ToJob>,
+	) -> Self {
+		Self {
+			relay_parent,
+			sender,
+			receiver,
+			provisionable_data_channels: Vec::new(),
+			backed_candidates: Vec::new(),
+			signed_bitfields: Vec::new(),
+		}
+	}
+
+	async fn run_loop(mut self) -> Result<(), Error> {
+		while let Some(msg) = self.receiver.next().await {
+			use ProvisionerMessage::{
+				ProvisionableData, RequestBlockAuthorshipData, RequestInherentData,
+			};
+
+			match msg {
+				ToJob::Provisioner(RequestInherentData(_, return_sender)) => {
+					if let Err(err) = send_inherent_data(
+						self.relay_parent,
+						&self.signed_bitfields,
+						&self.backed_candidates,
+						return_sender,
+						self.sender.clone(),
+					)
+					.await
+					{
+						log::warn!(target: "provisioner", "failed to send inherent data: {:?}", err);
+					}
+				}
+				ToJob::Provisioner(RequestBlockAuthorshipData(_, sender)) => {
+					self.provisionable_data_channels.push(sender)
+				}
+				ToJob::Provisioner(ProvisionableData(data)) => {
+					let mut bad_indices = Vec::new();
+					for (idx, channel) in self.provisionable_data_channels.iter_mut().enumerate() {
+						match channel.send(data.clone()).await {
+							Ok(_) => {}
+							Err(_) => bad_indices.push(idx),
+						}
+					}
+					self.note_provisionable_data(data);
+
+					// clean up our list of channels by removing the bad indices
+					// start by reversing it for efficient pop
+					bad_indices.reverse();
+					// Vec::retain would be nicer here, but it doesn't provide
+					// an easy API for retaining by index, so we re-collect instead.
+					self.provisionable_data_channels = self
+						.provisionable_data_channels
+						.into_iter()
+						.enumerate()
+						.filter(|(idx, _)| {
+							if bad_indices.is_empty() {
+								return true;
+							}
+							let tail = bad_indices[bad_indices.len() - 1];
+							let retain = *idx != tail;
+							if *idx >= tail {
+								bad_indices.pop();
+							}
+							retain
+						})
+						.map(|(_, item)| item)
+						.collect();
+				}
+				ToJob::Stop => break,
+			}
+		}
+
+		Ok(())
+	}
+
+	fn note_provisionable_data(&mut self, provisionable_data: ProvisionableData) {
+		match provisionable_data {
+			ProvisionableData::Bitfield(_, signed_bitfield) => {
+				self.signed_bitfields.push(signed_bitfield)
+			}
+			ProvisionableData::BackedCandidate(backed_candidate) => {
+				self.backed_candidates.push(backed_candidate)
+			}
+			_ => {}
+		}
+	}
+}
+
+type CoreAvailability = BitVec<bitvec::order::Lsb0, u8>;
+
+// The provisioner is the subsystem best suited to choosing which specific
+// backed candidates and availability bitfields should be assembled into the
+// block. To engage this functionality, a
+// `ProvisionerMessage::RequestInherentData` is sent; the response is a set of
+// non-conflicting candidates and the appropriate bitfields. Non-conflicting
+// means that there are never two distinct parachain candidates included for
+// the same parachain and that new parachain candidates cannot be included
+// until the previous one either gets declared available or expired.
+//
+// The main complication here is going to be around handling
+// occupied-core-assumptions. We might have candidates that are only
+// includable when some bitfields are included. And we might have candidates
+// that are not includable when certain bitfields are included.
+//
+// When we're choosing bitfields to include, the rule should be simple:
+// maximize availability. So basically, include all bitfields. And then
+// choose a coherent set of candidates along with that.
+async fn send_inherent_data(
+	relay_parent: Hash,
+	bitfields: &[SignedAvailabilityBitfield],
+	candidates: &[BackedCandidate],
+	return_sender: oneshot::Sender<ProvisionerInherentData>,
+	mut from_job: mpsc::Sender<FromJob>,
+) -> Result<(), Error> {
+	let availability_cores = match request_availability_cores(relay_parent, &mut from_job)
+		.await?
+		.await?
+	{
+		Ok(cores) => cores,
+		Err(runtime_err) => {
+			// Don't take down the node on runtime API errors.
+			log::warn!(target: "provisioner", "Encountered a runtime API error: {:?}", runtime_err);
+			return Ok(());
+		}
+	};
+
+	let bitfields = select_availability_bitfields(&availability_cores, bitfields);
+	let candidates = select_candidates(
+		&availability_cores,
+		&bitfields,
+		candidates,
+		relay_parent,
+		&mut from_job,
+	)
+	.await?;
+
+	return_sender
+		.send((bitfields, candidates))
+		.map_err(|_| Error::OneshotSend)?;
+	Ok(())
+}
+
+// in general, we want to pick all the bitfields. However, we have the following constraints:
+//
+// - not more than one per validator
+// - each must correspond to an occupied core
+//
+// If we have too many, an arbitrary selection policy is fine. For purposes of maximizing availability,
+// we pick the one with the greatest number of 1 bits.
+//
+// note: this does not enforce any sorting precondition on the output; the ordering there will be unrelated
+// to the sorting of the input.
+fn select_availability_bitfields(
+	cores: &[CoreState],
+	bitfields: &[SignedAvailabilityBitfield],
+) -> Vec<SignedAvailabilityBitfield> {
+	let mut fields_by_core: HashMap<_, Vec<_>> = HashMap::new();
+	for bitfield in bitfields.iter() {
+		let core_idx = bitfield.validator_index() as usize;
+		if let CoreState::Occupied(_) = cores[core_idx] {
+			fields_by_core
+				.entry(core_idx)
+				// there cannot be a value list in field_by_core with len < 1
+				.or_default()
+				.push(bitfield.clone());
+		}
+	}
+
+	let mut out = Vec::with_capacity(fields_by_core.len());
+	for (_, core_bitfields) in fields_by_core.iter_mut() {
+		core_bitfields.sort_by_key(|bitfield| bitfield.payload().0.count_ones());
+		out.push(
+			core_bitfields
+				.pop()
+				.expect("every core bitfield has at least 1 member; qed"),
+		);
+	}
+
+	out
+}
+
+// determine which cores are free, and then to the degree possible, pick a candidate appropriate to each free core.
+//
+// follow the candidate selection algorithm from the guide
+async fn select_candidates(
+	availability_cores: &[CoreState],
+	bitfields: &[SignedAvailabilityBitfield],
+	candidates: &[BackedCandidate],
+	relay_parent: Hash,
+	sender: &mut mpsc::Sender<FromJob>,
+) -> Result<Vec<BackedCandidate>, Error> {
+	let block_number = get_block_number_under_construction(relay_parent, sender).await?;
+
+	let global_validation_data = request_global_validation_data(relay_parent, sender)
+		.await?
+		.await??;
+
+	let mut selected_candidates =
+		Vec::with_capacity(candidates.len().min(availability_cores.len()));
+
+	for (core_idx, core) in availability_cores.iter().enumerate() {
+		let (scheduled_core, assumption) = match core {
+			CoreState::Scheduled(scheduled_core) => (scheduled_core, OccupiedCoreAssumption::Free),
+			CoreState::Occupied(occupied_core) => {
+				if bitfields_indicate_availability(core_idx, bitfields, &occupied_core.availability)
+				{
+					if let Some(ref scheduled_core) = occupied_core.next_up_on_available {
+						(scheduled_core, OccupiedCoreAssumption::Included)
+					} else {
+						continue;
+					}
+				} else {
+					if occupied_core.time_out_at != block_number {
+						continue;
+					}
+					if let Some(ref scheduled_core) = occupied_core.next_up_on_time_out {
+						(scheduled_core, OccupiedCoreAssumption::TimedOut)
+					} else {
+						continue;
+					}
+				}
+			}
+			_ => continue,
+		};
+
+		let local_validation_data = match request_local_validation_data(
+			relay_parent,
+			scheduled_core.para_id,
+			assumption,
+			sender,
+		)
+		.await?
+		.await??
+		{
+			Some(local_validation_data) => local_validation_data,
+			None => continue,
+		};
+
+		let computed_validation_data_hash =
+			validation_data_hash(&global_validation_data, &local_validation_data);
+
+		// we arbitrarily pick the first of the backed candidates which match the appropriate selection criteria
+		if let Some(candidate) = candidates.iter().find(|backed_candidate| {
+			let descriptor = &backed_candidate.candidate.descriptor;
+			descriptor.para_id == scheduled_core.para_id
+				&& descriptor.validation_data_hash == computed_validation_data_hash
+		}) {
+			selected_candidates.push(candidate.clone());
+		}
+	}
+
+	Ok(selected_candidates)
+}
+
+// produces a block number 1 higher than that of the relay parent
+// in the event of an invalid `relay_parent`, returns `Ok(0)`
+async fn get_block_number_under_construction(
+	relay_parent: Hash,
+	sender: &mut mpsc::Sender<FromJob>,
+) -> Result<BlockNumber, Error> {
+	let (tx, rx) = oneshot::channel();
+	sender
+		.send(FromJob::ChainApi(ChainApiMessage::BlockNumber(
+			relay_parent,
+			tx,
+		)))
+		.await
+		.map_err(|_| Error::OneshotSend)?;
+	match rx.await? {
+		Ok(Some(n)) => Ok(n + 1),
+		Ok(None) => Ok(0),
+		Err(err) => Err(err.into()),
+	}
+}
+
+// the availability bitfield for a given core is the transpose
+// of a set of signed availability bitfields. It goes like this:
+//
+//   - construct a transverse slice along `core_idx`
+//   - bitwise-or it with the availability slice
+//   - count the 1 bits, compare to the total length; true on 2/3+
+fn bitfields_indicate_availability(
+	core_idx: usize,
+	bitfields: &[SignedAvailabilityBitfield],
+	availability: &CoreAvailability,
+) -> bool {
+	let mut availability = availability.clone();
+	// we need to pre-compute this to avoid a borrow-immutable-while-borrowing-mutable error in the error message
+	let availability_len = availability.len();
+
+	for bitfield in bitfields {
+		let validator_idx = bitfield.validator_index() as usize;
+		match availability.get_mut(validator_idx) {
+			None => {
+				// in principle, this function might return a `Result<bool, Error>` so that we can more clearly express this error condition
+				// however, in practice, that would just push off an error-handling routine which would look a whole lot like this one.
+				// simpler to just handle the error internally here.
+				log::warn!(target: "provisioner", "attempted to set a transverse bit at idx {} which is greater than bitfield size {}", validator_idx, availability_len);
+				return false;
+			}
+			Some(mut bit_mut) => *bit_mut |= bitfield.payload().0[core_idx],
+		}
+	}
+	3 * availability.count_ones() >= 2 * availability.len()
+}
+
+delegated_subsystem!(ProvisioningJob(()) <- ToJob as ProvisioningSubsystem);
+
+#[cfg(test)]
+mod tests {
+	use super::*;
+	use bitvec::bitvec;
+	use polkadot_primitives::v1::{OccupiedCore, ScheduledCore};
+
+	pub fn occupied_core(para_id: u32) -> CoreState {
+		CoreState::Occupied(OccupiedCore {
+			para_id: para_id.into(),
+			group_responsible: para_id.into(),
+			next_up_on_available: None,
+			occupied_since: 100_u32,
+			time_out_at: 200_u32,
+			next_up_on_time_out: None,
+			availability: default_bitvec(),
+		})
+	}
+
+	pub fn build_occupied_core<Builder>(para_id: u32, builder: Builder) -> CoreState
+	where
+		Builder: FnOnce(&mut OccupiedCore),
+	{
+		let mut core = match occupied_core(para_id) {
+			CoreState::Occupied(core) => core,
+			_ => unreachable!(),
+		};
+
+		builder(&mut core);
+
+		CoreState::Occupied(core)
+	}
+
+	pub fn default_bitvec() -> CoreAvailability {
+		bitvec![bitvec::order::Lsb0, u8; 0; 32]
+	}
+
+	pub fn scheduled_core(id: u32) -> ScheduledCore {
+		ScheduledCore {
+			para_id: id.into(),
+			..Default::default()
+		}
+	}
+
+	mod select_availability_bitfields {
+		use super::super::*;
+		use super::{default_bitvec, occupied_core};
+		use lazy_static::lazy_static;
+		use polkadot_primitives::v1::{SigningContext, ValidatorIndex, ValidatorPair};
+		use sp_core::crypto::Pair;
+		use std::sync::Mutex;
+
+		lazy_static! {
+			// we can use a normal mutex here, not a futures-aware one, because we don't use any futures-based
+			// concurrency when accessing this. The risk of contention is that multiple tests are run in parallel,
+			// in independent threads, in which case a standard mutex suffices.
+			static ref VALIDATORS: Mutex<HashMap<ValidatorIndex, ValidatorPair>> = Mutex::new(HashMap::new());
+		}
+
+		fn signed_bitfield(
+			field: CoreAvailability,
+			validator_idx: ValidatorIndex,
+		) -> SignedAvailabilityBitfield {
+			let mut lock = VALIDATORS.lock().unwrap();
+			let validator = lock
+				.entry(validator_idx)
+				.or_insert_with(|| ValidatorPair::generate().0);
+			SignedAvailabilityBitfield::sign(
+				field.into(),
+				&<SigningContext<Hash>>::default(),
+				validator_idx,
+				validator,
+			)
+		}
+
+		#[test]
+		fn not_more_than_one_per_validator() {
+			let bitvec = default_bitvec();
+
+			let cores = vec![occupied_core(0), occupied_core(1)];
+
+			// we pass in three bitfields with two validators
+			// this helps us check the postcondition that we get two bitfields back, for which the validators differ
+			let bitfields = vec![
+				signed_bitfield(bitvec.clone(), 0),
+				signed_bitfield(bitvec.clone(), 1),
+				signed_bitfield(bitvec, 1),
+			];
+
+			let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields);
+			selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index());
+
+			assert_eq!(selected_bitfields.len(), 2);
+			assert_eq!(selected_bitfields[0], bitfields[0]);
+			// we don't know which of the (otherwise equal) bitfields will be selected
+			assert!(selected_bitfields[1] == bitfields[1] || selected_bitfields[1] == bitfields[2]);
+		}
+
+		#[test]
+		fn each_corresponds_to_an_occupied_core() {
+			let bitvec = default_bitvec();
+
+			let cores = vec![CoreState::Free, CoreState::Scheduled(Default::default())];
+
+			let bitfields = vec![
+				signed_bitfield(bitvec.clone(), 0),
+				signed_bitfield(bitvec.clone(), 1),
+				signed_bitfield(bitvec, 1),
+			];
+
+			let mut selected_bitfields = select_availability_bitfields(&cores, &bitfields);
+			selected_bitfields.sort_by_key(|bitfield| bitfield.validator_index());
+
+			// bitfields not corresponding to occupied cores are not selected
+			assert!(selected_bitfields.is_empty());
+		}
+
+		#[test]
+		fn more_set_bits_win_conflicts() {
+			let bitvec_zero = default_bitvec();
+			let bitvec_one = {
+				let mut bitvec = bitvec_zero.clone();
+				bitvec.set(0, true);
+				bitvec
+			};
+
+			let cores = vec![occupied_core(0)];
+
+			let bitfields = vec![
+				signed_bitfield(bitvec_zero, 0),
+				signed_bitfield(bitvec_one.clone(), 0),
+			];
+
+			// this test is probablistic: chances are excellent that it does what it claims to.
+			// it cannot fail unless things are broken.
+			// however, there is a (very small) chance that it passes when things are broken.
+			for _ in 0..64 {
+				let selected_bitfields = select_availability_bitfields(&cores, &bitfields);
+				assert_eq!(selected_bitfields.len(), 1);
+				assert_eq!(selected_bitfields[0].payload().0, bitvec_one);
+			}
+		}
+	}
+
+	mod select_candidates {
+		use super::super::*;
+		use super::{build_occupied_core, default_bitvec, occupied_core, scheduled_core};
+		use polkadot_node_subsystem::messages::RuntimeApiRequest::{
+			AvailabilityCores, GlobalValidationData, LocalValidationData,
+		};
+		use polkadot_primitives::v1::{
+			BlockNumber, CandidateDescriptor, CommittedCandidateReceipt,
+		};
+		use FromJob::{ChainApi, Runtime};
+
+		const BLOCK_UNDER_PRODUCTION: BlockNumber = 128;
+
+		fn test_harness<OverseerFactory, Overseer, TestFactory, Test>(
+			overseer_factory: OverseerFactory,
+			test_factory: TestFactory,
+		) where
+			OverseerFactory: FnOnce(mpsc::Receiver<FromJob>) -> Overseer,
+			Overseer: Future<Output = ()>,
+			TestFactory: FnOnce(mpsc::Sender<FromJob>) -> Test,
+			Test: Future<Output = ()>,
+		{
+			let (tx, rx) = mpsc::channel(64);
+			let overseer = overseer_factory(rx);
+			let test = test_factory(tx);
+
+			futures::pin_mut!(overseer, test);
+
+			tokio::runtime::Runtime::new()
+				.unwrap()
+				.block_on(future::select(overseer, test));
+		}
+
+		// For test purposes, we always return this set of availability cores:
+		//
+		//   [
+		//      0: Free,
+		//      1: Scheduled(default),
+		//      2: Occupied(no next_up set),
+		//      3: Occupied(next_up_on_available set but not available),
+		//      4: Occupied(next_up_on_available set and available),
+		//      5: Occupied(next_up_on_time_out set but not timeout),
+		//      6: Occupied(next_up_on_time_out set and timeout but available),
+		//      7: Occupied(next_up_on_time_out set and timeout and not available),
+		//      8: Occupied(both next_up set, available),
+		//      9: Occupied(both next_up set, not available, no timeout),
+		//     10: Occupied(both next_up set, not available, timeout),
+		//     11: Occupied(next_up_on_available and available, but different successor para_id)
+		//   ]
+		fn mock_availability_cores() -> Vec<CoreState> {
+			use std::ops::Not;
+			use CoreState::{Free, Scheduled};
+
+			vec![
+				// 0: Free,
+				Free,
+				// 1: Scheduled(default),
+				Scheduled(scheduled_core(1)),
+				// 2: Occupied(no next_up set),
+				occupied_core(2),
+				// 3: Occupied(next_up_on_available set but not available),
+				build_occupied_core(3, |core| {
+					core.next_up_on_available = Some(scheduled_core(3));
+				}),
+				// 4: Occupied(next_up_on_available set and available),
+				build_occupied_core(4, |core| {
+					core.next_up_on_available = Some(scheduled_core(4));
+					core.availability = core.availability.clone().not();
+				}),
+				// 5: Occupied(next_up_on_time_out set but not timeout),
+				build_occupied_core(5, |core| {
+					core.next_up_on_time_out = Some(scheduled_core(5));
+				}),
+				// 6: Occupied(next_up_on_time_out set and timeout but available),
+				build_occupied_core(6, |core| {
+					core.next_up_on_time_out = Some(scheduled_core(6));
+					core.time_out_at = BLOCK_UNDER_PRODUCTION;
+					core.availability = core.availability.clone().not();
+				}),
+				// 7: Occupied(next_up_on_time_out set and timeout and not available),
+				build_occupied_core(7, |core| {
+					core.next_up_on_time_out = Some(scheduled_core(7));
+					core.time_out_at = BLOCK_UNDER_PRODUCTION;
+				}),
+				// 8: Occupied(both next_up set, available),
+				build_occupied_core(8, |core| {
+					core.next_up_on_available = Some(scheduled_core(8));
+					core.next_up_on_time_out = Some(scheduled_core(8));
+					core.availability = core.availability.clone().not();
+				}),
+				// 9: Occupied(both next_up set, not available, no timeout),
+				build_occupied_core(9, |core| {
+					core.next_up_on_available = Some(scheduled_core(9));
+					core.next_up_on_time_out = Some(scheduled_core(9));
+				}),
+				// 10: Occupied(both next_up set, not available, timeout),
+				build_occupied_core(10, |core| {
+					core.next_up_on_available = Some(scheduled_core(10));
+					core.next_up_on_time_out = Some(scheduled_core(10));
+					core.time_out_at = BLOCK_UNDER_PRODUCTION;
+				}),
+				// 11: Occupied(next_up_on_available and available, but different successor para_id)
+				build_occupied_core(11, |core| {
+					core.next_up_on_available = Some(scheduled_core(12));
+					core.availability = core.availability.clone().not();
+				}),
+			]
+		}
+
+		async fn mock_overseer(mut receiver: mpsc::Receiver<FromJob>) {
+			use ChainApiMessage::BlockNumber;
+			use RuntimeApiMessage::Request;
+
+			while let Some(from_job) = receiver.next().await {
+				match from_job {
+					ChainApi(BlockNumber(_relay_parent, tx)) => {
+						tx.send(Ok(Some(BLOCK_UNDER_PRODUCTION - 1))).unwrap()
+					}
+					Runtime(Request(_parent_hash, GlobalValidationData(tx))) => {
+						tx.send(Ok(Default::default())).unwrap()
+					}
+					Runtime(Request(
+						_parent_hash,
+						LocalValidationData(_para_id, _assumption, tx),
+					)) => tx.send(Ok(Some(Default::default()))).unwrap(),
+					Runtime(Request(_parent_hash, AvailabilityCores(tx))) => {
+						tx.send(Ok(mock_availability_cores())).unwrap()
+					}
+					// non-exhaustive matches are fine for testing
+					_ => unimplemented!(),
+				}
+			}
+		}
+
+		#[test]
+		fn handles_overseer_failure() {
+			let overseer = |rx: mpsc::Receiver<FromJob>| async move {
+				// drop the receiver so it closes and the sender can't send, then just sleep long enough that
+				// this is almost certainly not the first of the two futures to complete
+				std::mem::drop(rx);
+				tokio::time::delay_for(std::time::Duration::from_secs(1)).await;
+			};
+
+			let test = |mut tx: mpsc::Sender<FromJob>| async move {
+				// wait so that the overseer can drop the rx before we attempt to send
+				tokio::time::delay_for(std::time::Duration::from_millis(50)).await;
+				let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await;
+				println!("{:?}", result);
+				assert!(std::matches!(result, Err(Error::OneshotSend)));
+			};
+
+			test_harness(overseer, test);
+		}
+
+		#[test]
+		fn can_succeed() {
+			test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJob>| async move {
+				let result = select_candidates(&[], &[], &[], Default::default(), &mut tx).await;
+				println!("{:?}", result);
+				assert!(result.is_ok());
+			})
+		}
+
+		// this tests that only the appropriate candidates get selected.
+		// To accomplish this, we supply a candidate list containing one candidate per possible core;
+		// the candidate selection algorithm must filter them to the appropriate set
+		#[test]
+		fn selects_correct_candidates() {
+			let mock_cores = mock_availability_cores();
+
+			let empty_hash =
+				validation_data_hash::<BlockNumber>(&Default::default(), &Default::default());
+			dbg!(empty_hash);
+
+			let candidate_template = BackedCandidate {
+				candidate: CommittedCandidateReceipt {
+					descriptor: CandidateDescriptor {
+						validation_data_hash: empty_hash,
+						..Default::default()
+					},
+					..Default::default()
+				},
+				validity_votes: Vec::new(),
+				validator_indices: default_bitvec(),
+			};
+
+			let candidates: Vec<_> = std::iter::repeat(candidate_template)
+				.take(mock_cores.len())
+				.enumerate()
+				.map(|(idx, mut candidate)| {
+					candidate.candidate.descriptor.para_id = idx.into();
+					candidate
+				})
+				.cycle()
+				.take(mock_cores.len() * 3)
+				.enumerate()
+				.map(|(idx, mut candidate)| {
+					if idx < mock_cores.len() {
+						// first go-around: use candidates which should work
+						candidate
+					} else if idx < mock_cores.len() * 2 {
+						// for the second repetition of the candidates, give them the wrong hash
+						candidate.candidate.descriptor.validation_data_hash = Default::default();
+						candidate
+					} else {
+						// third go-around: right hash, wrong para_id
+						candidate.candidate.descriptor.para_id = idx.into();
+						candidate
+					}
+				})
+				.collect();
+
+			// why those particular indices? see the comments on mock_availability_cores()
+			let expected_candidates: Vec<_> = [1, 4, 7, 8, 10]
+				.iter()
+				.map(|&idx| candidates[idx].clone())
+				.collect();
+
+			test_harness(mock_overseer, |mut tx: mpsc::Sender<FromJob>| async move {
+				let result =
+					select_candidates(&mock_cores, &[], &candidates, Default::default(), &mut tx)
+						.await;
+
+				if result.is_err() {
+					println!("{:?}", result);
+				}
+				assert_eq!(result.unwrap(), expected_candidates);
+			})
+		}
+	}
+}
diff --git a/polkadot/node/primitives/src/lib.rs b/polkadot/node/primitives/src/lib.rs
index 201522abb5065cff915b0db5170135bf7cbdc855..2e5f993a7e770bc8002ec10c6dbb1cbf1ff00b31 100644
--- a/polkadot/node/primitives/src/lib.rs
+++ b/polkadot/node/primitives/src/lib.rs
@@ -83,7 +83,7 @@ impl EncodeAs<CompactStatement> for Statement {
 pub type SignedFullStatement = Signed<Statement, CompactStatement>;
 
 /// A misbehaviour report.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum MisbehaviorReport {
 	/// These validator nodes disagree on this candidate's validity, please figure it out
 	///
diff --git a/polkadot/node/subsystem/src/messages.rs b/polkadot/node/subsystem/src/messages.rs
index e3a4a419a53efcd296d2bd11182bf4010b5c5967..29014c55a887d59d8f34f8fe93208b28485b1e7f 100644
--- a/polkadot/node/subsystem/src/messages.rs
+++ b/polkadot/node/subsystem/src/messages.rs
@@ -407,7 +407,8 @@ impl StatementDistributionMessage {
 }
 
 /// This data becomes intrinsics or extrinsics which should be included in a future relay chain block.
-#[derive(Debug)]
+// It needs to be cloneable because multiple potential block authors can request copies.
+#[derive(Debug, Clone)]
 pub enum ProvisionableData {
 	/// This bitfield indicates the availability of various candidate blocks.
 	Bitfield(Hash, SignedAvailabilityBitfield),
@@ -488,8 +489,6 @@ pub enum AllMessages {
 	CandidateBacking(CandidateBackingMessage),
 	/// Message for the candidate selection subsystem.
 	CandidateSelection(CandidateSelectionMessage),
-	/// Message for the Chain API subsystem.
-	ChainApi(ChainApiMessage),
 	/// Message for the statement distribution subsystem.
 	StatementDistribution(StatementDistributionMessage),
 	/// Message for the availability distribution subsystem.
@@ -508,6 +507,8 @@ pub enum AllMessages {
 	AvailabilityStore(AvailabilityStoreMessage),
 	/// Message for the network bridge subsystem.
 	NetworkBridge(NetworkBridgeMessage),
+	/// Message for the Chain API subsystem
+	ChainApi(ChainApiMessage),
 	/// Test message
 	///
 	/// This variant is only valid while testing, but makes the process of testing the
diff --git a/polkadot/node/subsystem/src/util.rs b/polkadot/node/subsystem/src/util.rs
index 14f6f96ed2b2c0e8260b89c6f23d6b94f7ce52d5..7472ebeb8aa03e88b83f54a1f63c8c3842ed4c74 100644
--- a/polkadot/node/subsystem/src/util.rs
+++ b/polkadot/node/subsystem/src/util.rs
@@ -21,10 +21,8 @@
 //! this module.
 
 use crate::{
-	messages::{
-		AllMessages, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender,
-	},
 	errors::{ChainApiError, RuntimeApiError},
+	messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender},
 	FromOverseer, SpawnedSubsystem, Subsystem, SubsystemContext, SubsystemError, SubsystemResult,
 };
 use futures::{
@@ -40,13 +38,12 @@ use keystore::KeyStorePtr;
 use parity_scale_codec::Encode;
 use pin_project::{pin_project, pinned_drop};
 use polkadot_primitives::v1::{
-	EncodeAs, Hash, Signed, SigningContext, SessionIndex,
-	ValidatorId, ValidatorIndex, ValidatorPair, GroupRotationInfo,
-};
-use sp_core::{
-	Pair,
-	traits::SpawnNamed,
+	CandidateEvent, CommittedCandidateReceipt, CoreState, EncodeAs, GlobalValidationData,
+	GroupRotationInfo, Hash, Id as ParaId, LocalValidationData, OccupiedCoreAssumption,
+	SessionIndex, Signed, SigningContext, ValidationCode, ValidatorId, ValidatorIndex,
+	ValidatorPair,
 };
+use sp_core::Pair;
 use std::{
 	collections::HashMap,
 	convert::{TryFrom, TryInto},
@@ -56,6 +53,11 @@ use std::{
 };
 use streamunordered::{StreamUnordered, StreamYield};
 
+/// This reexport is required so that external crates can use the `delegated_subsystem` macro properly.
+///
+/// Otherwise, downstream crates might have to modify their `Cargo.toml` to ensure `sp-core` appeared there.
+pub use sp_core::traits::SpawnNamed;
+
 /// Duration a job will wait after sending a stop signal before hard-aborting.
 pub const JOB_GRACEFUL_STOP_DURATION: Duration = Duration::from_secs(1);
 /// Capacity of channels to and from individual jobs
@@ -119,42 +121,67 @@ where
 	Ok(rx)
 }
 
-/// Request a validator set from the `RuntimeApi`.
-pub async fn request_validators<FromJob>(
-	parent: Hash,
-	s: &mut mpsc::Sender<FromJob>,
-) -> Result<RuntimeApiReceiver<Vec<ValidatorId>>, Error>
-where
-	FromJob: TryFrom<AllMessages>,
-	<FromJob as TryFrom<AllMessages>>::Error: std::fmt::Debug,
-{
-	request_from_runtime(parent, s, |tx| RuntimeApiRequest::Validators(tx)).await
-}
+/// Construct specialized request functions for the runtime.
+///
+/// These would otherwise get pretty repetitive.
+macro_rules! specialize_requests {
+	// expand return type name for documentation purposes
+	(fn $func_name:ident( $( $param_name:ident : $param_ty:ty ),* ) -> $return_ty:ty ; $request_variant:ident;) => {
+		specialize_requests!{
+			named stringify!($request_variant) ; fn $func_name( $( $param_name : $param_ty ),* ) -> $return_ty ; $request_variant;
+		}
+	};
 
-/// Request the validator groups.
-pub async fn request_validator_groups<FromJob>(
-	parent: Hash,
-	s: &mut mpsc::Sender<FromJob>,
-) -> Result<RuntimeApiReceiver<(Vec<Vec<ValidatorIndex>>, GroupRotationInfo)>, Error>
-where
-	FromJob: TryFrom<AllMessages>,
-	<FromJob as TryFrom<AllMessages>>::Error: std::fmt::Debug,
-{
-	request_from_runtime(parent, s, |tx| RuntimeApiRequest::ValidatorGroups(tx)).await
+	// create a single specialized request function
+	(named $doc_name:expr ; fn $func_name:ident( $( $param_name:ident : $param_ty:ty ),* ) -> $return_ty:ty ; $request_variant:ident;) => {
+		#[doc = "Request `"]
+		#[doc = $doc_name]
+		#[doc = "` from the runtime"]
+		pub async fn $func_name<FromJob>(
+			parent: Hash,
+			$(
+				$param_name: $param_ty,
+			)*
+			sender: &mut mpsc::Sender<FromJob>,
+		) -> Result<RuntimeApiReceiver<$return_ty>, Error>
+		where
+			FromJob: TryFrom<AllMessages>,
+			<FromJob as TryFrom<AllMessages>>::Error: std::fmt::Debug,
+		{
+			request_from_runtime(parent, sender, |tx| RuntimeApiRequest::$request_variant(
+				$( $param_name, )* tx
+			)).await
+		}
+	};
+
+	// recursive decompose
+	(
+		fn $func_name:ident( $( $param_name:ident : $param_ty:ty ),* ) -> $return_ty:ty ; $request_variant:ident;
+		$(
+			fn $t_func_name:ident( $( $t_param_name:ident : $t_param_ty:ty ),* ) -> $t_return_ty:ty ; $t_request_variant:ident;
+		)+
+	) => {
+		specialize_requests!{
+			fn $func_name( $( $param_name : $param_ty ),* ) -> $return_ty ; $request_variant ;
+		}
+		specialize_requests!{
+			$(
+				fn $t_func_name( $( $t_param_name : $t_param_ty ),* ) -> $t_return_ty ; $t_request_variant ;
+			)+
+		}
+	};
 }
 
-/// Request the session index of the child block.
-pub async fn request_session_index_for_child<FromJob>(
-	parent: Hash,
-	s: &mut mpsc::Sender<FromJob>,
-) -> Result<RuntimeApiReceiver<SessionIndex>, Error>
-where
-	FromJob: TryFrom<AllMessages>,
-	<FromJob as TryFrom<AllMessages>>::Error: std::fmt::Debug,
-{
-	request_from_runtime(parent, s, |tx| {
-		RuntimeApiRequest::SessionIndexForChild(tx)
-	}).await
+specialize_requests! {
+	fn request_validators() -> Vec<ValidatorId>; Validators;
+	fn request_validator_groups() -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo); ValidatorGroups;
+	fn request_availability_cores() -> Vec<CoreState>; AvailabilityCores;
+	fn request_global_validation_data() -> GlobalValidationData; GlobalValidationData;
+	fn request_local_validation_data(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<LocalValidationData>; LocalValidationData;
+	fn request_session_index_for_child() -> SessionIndex; SessionIndexForChild;
+	fn request_validation_code(para_id: ParaId, assumption: OccupiedCoreAssumption) -> Option<ValidationCode>; ValidationCode;
+	fn request_candidate_pending_availability(para_id: ParaId) -> Option<CommittedCandidateReceipt>; CandidatePendingAvailability;
+	fn request_candidate_events() -> Vec<CandidateEvent>; CandidateEvents;
 }
 
 /// From the given set of validators, find the first key we can sign with, if any.
@@ -405,8 +432,13 @@ impl<Spawner: SpawnNamed, Job: 'static + JobTrait> Jobs<Spawner, Job> {
 	/// the error is forwarded onto the provided channel.
 	///
 	/// Errors if the error channel already exists.
-	pub fn forward_errors(&mut self, tx: mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>) -> Result<(), Error> {
-		if self.errors.is_some() { return Err(Error::AlreadyForwarding) }
+	pub fn forward_errors(
+		&mut self,
+		tx: mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>,
+	) -> Result<(), Error> {
+		if self.errors.is_some() {
+			return Err(Error::AlreadyForwarding);
+		}
 		self.errors = Some(tx);
 		Ok(())
 	}
@@ -510,13 +542,12 @@ where
 
 	fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context) -> task::Poll<Option<Self::Item>> {
 		// pin-project the outgoing messages
-		self.project()
-			.outgoing_msgs
-			.poll_next(cx)
-			.map(|opt| opt.and_then(|(stream_yield, _)| match stream_yield {
+		self.project().outgoing_msgs.poll_next(cx).map(|opt| {
+			opt.and_then(|(stream_yield, _)| match stream_yield {
 				StreamYield::Item(msg) => Some(msg),
 				StreamYield::Finished(_) => None,
-		}))
+			})
+		})
 	}
 }
 
@@ -559,8 +590,13 @@ where
 	/// the error is forwarded onto the provided channel.
 	///
 	/// Errors if the error channel already exists.
-	pub fn forward_errors(&mut self, tx: mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>) -> Result<(), Error> {
-		if self.errors.is_some() { return Err(Error::AlreadyForwarding) }
+	pub fn forward_errors(
+		&mut self,
+		tx: mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>,
+	) -> Result<(), Error> {
+		if self.errors.is_some() {
+			return Err(Error::AlreadyForwarding);
+		}
 		self.errors = Some(tx);
 		Ok(())
 	}
@@ -576,10 +612,16 @@ where
 	///
 	/// If `err_tx` is not `None`, errors are forwarded onto that channel as they occur.
 	/// Otherwise, most are logged and then discarded.
-	pub async fn run(mut ctx: Context, run_args: Job::RunArgs, spawner: Spawner, mut err_tx: Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>) {
+	pub async fn run(
+		mut ctx: Context,
+		run_args: Job::RunArgs,
+		spawner: Spawner,
+		mut err_tx: Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>,
+	) {
 		let mut jobs = Jobs::new(spawner.clone());
 		if let Some(ref err_tx) = err_tx {
-			jobs.forward_errors(err_tx.clone()).expect("we never call this twice in this context; qed");
+			jobs.forward_errors(err_tx.clone())
+				.expect("we never call this twice in this context; qed");
 		}
 
 		loop {
@@ -592,7 +634,11 @@ where
 	}
 
 	// if we have a channel on which to forward errors, do so
-	async fn fwd_err(hash: Option<Hash>, err: JobsError<Job::Error>, err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>) {
+	async fn fwd_err(
+		hash: Option<Hash>,
+		err: JobsError<Job::Error>,
+		err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>,
+	) {
 		if let Some(err_tx) = err_tx {
 			// if we can't send on the error transmission channel, we can't do anything useful about it
 			// still, we can at least log the failure
@@ -607,14 +653,17 @@ where
 		incoming: SubsystemResult<FromOverseer<Context::Message>>,
 		jobs: &mut Jobs<Spawner, Job>,
 		run_args: &Job::RunArgs,
-		err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>
+		err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>,
 	) -> bool {
-		use crate::FromOverseer::{Communication, Signal};
 		use crate::ActiveLeavesUpdate;
-		use crate::OverseerSignal::{BlockFinalized, Conclude, ActiveLeaves};
+		use crate::FromOverseer::{Communication, Signal};
+		use crate::OverseerSignal::{ActiveLeaves, BlockFinalized, Conclude};
 
 		match incoming {
-			Ok(Signal(ActiveLeaves(ActiveLeavesUpdate { activated, deactivated }))) => {
+			Ok(Signal(ActiveLeaves(ActiveLeavesUpdate {
+				activated,
+				deactivated,
+			}))) => {
 				for hash in activated {
 					if let Err(e) = jobs.spawn_job(hash, run_args.clone()) {
 						log::error!("Failed to spawn a job: {:?}", e);
@@ -638,10 +687,11 @@ where
 				// Forwarding the stream to a drain means we wait until all of the items in the stream
 				// have completed. Contrast with `into_future`, which turns it into a future of `(head, rest_stream)`.
 				use futures::sink::drain;
-				use futures::stream::StreamExt;
 				use futures::stream::FuturesUnordered;
+				use futures::stream::StreamExt;
 
-				if let Err(e) = jobs.running
+				if let Err(e) = jobs
+					.running
 					.drain()
 					.map(|(_, handle)| handle.stop())
 					.collect::<FuturesUnordered<_>>()
@@ -686,7 +736,11 @@ where
 	}
 
 	// handle an outgoing message. return true if we should break afterwards.
-	async fn handle_outgoing(outgoing: Option<Job::FromJob>, ctx: &mut Context, err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>) -> bool {
+	async fn handle_outgoing(
+		outgoing: Option<Job::FromJob>,
+		ctx: &mut Context,
+		err_tx: &mut Option<mpsc::Sender<(Option<Hash>, JobsError<Job::Error>)>>,
+	) -> bool {
 		match outgoing {
 			Some(msg) => {
 				if let Err(e) = ctx.send_message(msg.into()).await {
@@ -713,7 +767,6 @@ where
 		let run_args = self.run_args.clone();
 		let errors = self.errors;
 
-
 		let future = Box::pin(async move {
 			Self::run(ctx, run_args, spawner, errors).await;
 		});
@@ -725,41 +778,107 @@ where
 	}
 }
 
+/// Create a delegated subsystem
+///
+/// It is possible to create a type which implements `Subsystem` by simply doing:
+///
+/// ```ignore
+/// pub type ExampleSubsystem<Spawner, Context> = util::JobManager<Spawner, Context, ExampleJob>;
+/// ```
+///
+/// However, doing this requires that job itself and all types which comprise it (i.e. `ToJob`, `FromJob`, `Error`, `RunArgs`)
+/// are public, to avoid exposing private types in public interfaces. It's possible to delegate instead, which
+/// can reduce the total number of public types exposed, i.e.
+///
+/// ```ignore
+/// type Manager<Spawner, Context> = util::JobManager<Spawner, Context, ExampleJob>;
+/// pub struct ExampleSubsystem {
+/// 	manager: Manager<Spawner, Context>,
+/// }
+///
+/// impl<Spawner, Context> Subsystem<Context> for ExampleSubsystem<Spawner, Context> { ... }
+/// ```
+///
+/// This dramatically reduces the number of public types in the crate; the only things which must be public are now
+///
+/// - `struct ExampleSubsystem` (defined by this macro)
+/// - `type ToJob` (because it appears in a trait bound)
+/// - `type RunArgs` (because it appears in a function signature)
+///
+/// Implementing this all manually is of course possible, but it's tedious; why bother? This macro exists for
+/// the purpose of doing it automatically:
+///
+/// ```ignore
+/// delegated_subsystem!(ExampleJob(ExampleRunArgs) <- ExampleToJob as ExampleSubsystem);
+/// ```
+#[macro_export]
+macro_rules! delegated_subsystem {
+	($job:ident($run_args:ty) <- $to_job:ty as $subsystem:ident) => {
+		delegated_subsystem!($job($run_args) <- $to_job as $subsystem; stringify!($subsystem));
+	};
+
+	($job:ident($run_args:ty) <- $to_job:ty as $subsystem:ident; $subsystem_name:expr) => {
+		#[doc = "Manager type for the "]
+		#[doc = $subsystem_name]
+		type Manager<Spawner, Context> = $crate::util::JobManager<Spawner, Context, $job>;
+
+		#[doc = "An implementation of the "]
+		#[doc = $subsystem_name]
+		pub struct $subsystem<Spawner, Context> {
+			manager: Manager<Spawner, Context>,
+		}
+
+		impl<Spawner, Context> $subsystem<Spawner, Context>
+		where
+			Spawner: Clone + $crate::util::SpawnNamed + Send + Unpin,
+			Context: $crate::SubsystemContext,
+			<Context as $crate::SubsystemContext>::Message: Into<$to_job>,
+		{
+			#[doc = "Creates a new "]
+			#[doc = $subsystem_name]
+			pub fn new(spawner: Spawner, run_args: $run_args) -> Self {
+				$subsystem {
+					manager: $crate::util::JobManager::new(spawner, run_args)
+				}
+			}
+
+			/// Run this subsystem
+			pub async fn run(ctx: Context, run_args: $run_args, spawner: Spawner) {
+				<Manager<Spawner, Context>>::run(ctx, run_args, spawner, None).await
+			}
+		}
+
+		impl<Spawner, Context> $crate::Subsystem<Context> for $subsystem<Spawner, Context>
+		where
+			Spawner: $crate::util::SpawnNamed + Send + Clone + Unpin + 'static,
+			Context: $crate::SubsystemContext,
+			<Context as $crate::SubsystemContext>::Message: Into<$to_job>,
+		{
+			fn start(self, ctx: Context) -> $crate::SpawnedSubsystem {
+				self.manager.start(ctx)
+			}
+		}
+	};
+}
+
 #[cfg(test)]
 mod tests {
-	use assert_matches::assert_matches;
 	use crate::{
 		messages::{AllMessages, CandidateSelectionMessage},
 		test_helpers::{self, make_subsystem_context},
-		util::{
-			self,
-			JobsError,
-			JobManager,
-			JobTrait,
-			ToJobTrait,
-		},
-		ActiveLeavesUpdate,
-		FromOverseer,
-		OverseerSignal,
-		SpawnedSubsystem,
-		Subsystem,
+		util::{self, JobManager, JobTrait, JobsError, ToJobTrait},
+		ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, Subsystem,
 	};
+	use assert_matches::assert_matches;
 	use futures::{
 		channel::mpsc,
 		executor,
-		Future,
-		FutureExt,
 		stream::{self, StreamExt},
-		SinkExt,
+		Future, FutureExt, SinkExt,
 	};
 	use futures_timer::Delay;
 	use polkadot_primitives::v1::Hash;
-	use std::{
-		collections::HashMap,
-		convert::TryFrom,
-		pin::Pin,
-		time::Duration,
-	};
+	use std::{collections::HashMap, convert::TryFrom, pin::Pin, time::Duration};
 
 	// basic usage: in a nutshell, when you want to define a subsystem, just focus on what its jobs do;
 	// you can leave the subsystem itself to the job manager.
@@ -803,7 +922,7 @@ mod tests {
 		fn try_from(msg: AllMessages) -> Result<Self, Self::Error> {
 			match msg {
 				AllMessages::CandidateSelection(csm) => Ok(ToJob::CandidateSelection(csm)),
-				_ => Err(())
+				_ => Err(()),
 			}
 		}
 	}
@@ -839,7 +958,7 @@ mod tests {
 	#[derive(Debug, derive_more::From)]
 	enum Error {
 		#[from]
-		Sending(mpsc::SendError)
+		Sending(mpsc::SendError),
 	}
 
 	impl JobTrait for FakeCandidateSelectionJob {
@@ -867,9 +986,7 @@ mod tests {
 			mut sender: mpsc::Sender<FromJob>,
 		) -> Pin<Box<dyn Future<Output = Result<(), Self::Error>> + Send>> {
 			async move {
-				let job = FakeCandidateSelectionJob {
-					receiver,
-				};
+				let job = FakeCandidateSelectionJob { receiver };
 
 				// most jobs will have a request-response cycle at the heart of their run loop.
 				// however, in this case, we never receive valid messages, so we may as well
@@ -881,7 +998,8 @@ mod tests {
 				// it isn't necessary to break run_loop into its own function,
 				// but it's convenient to separate the concerns in this way
 				job.run_loop().await
-			}.boxed()
+			}
+			.boxed()
 		}
 	}
 
@@ -901,12 +1019,16 @@ mod tests {
 	}
 
 	// with the job defined, it's straightforward to get a subsystem implementation.
-	type FakeCandidateSelectionSubsystem<Spawner, Context> = JobManager<Spawner, Context, FakeCandidateSelectionJob>;
+	type FakeCandidateSelectionSubsystem<Spawner, Context> =
+		JobManager<Spawner, Context, FakeCandidateSelectionJob>;
 
 	// this type lets us pretend to be the overseer
 	type OverseerHandle = test_helpers::TestSubsystemContextHandle<CandidateSelectionMessage>;
 
-	fn test_harness<T: Future<Output=()>>(run_args: HashMap<Hash, Vec<FromJob>>, test: impl FnOnce(OverseerHandle, mpsc::Receiver<(Option<Hash>, JobsError<Error>)>) -> T) {
+	fn test_harness<T: Future<Output = ()>>(
+		run_args: HashMap<Hash, Vec<FromJob>>,
+		test: impl FnOnce(OverseerHandle, mpsc::Receiver<(Option<Hash>, JobsError<Error>)>) -> T,
+	) {
 		let pool = sp_core::testing::TaskExecutor::new();
 		let (context, overseer_handle) = make_subsystem_context(pool.clone());
 		let (err_tx, err_rx) = mpsc::channel(16);
@@ -933,15 +1055,26 @@ mod tests {
 		let relay_parent: Hash = [0; 32].into();
 		let mut run_args = HashMap::new();
 		let test_message = format!("greetings from {}", relay_parent);
-		run_args.insert(relay_parent.clone(), vec![FromJob::Test(test_message.clone())]);
+		run_args.insert(
+			relay_parent.clone(),
+			vec![FromJob::Test(test_message.clone())],
+		);
 
 		test_harness(run_args, |mut overseer_handle, err_rx| async move {
-			overseer_handle.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(relay_parent)))).await;
+			overseer_handle
+				.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(
+					ActiveLeavesUpdate::start_work(relay_parent),
+				)))
+				.await;
 			assert_matches!(
 				overseer_handle.recv().await,
 				AllMessages::Test(msg) if msg == test_message
 			);
-			overseer_handle.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::stop_work(relay_parent)))).await;
+			overseer_handle
+				.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(
+					ActiveLeavesUpdate::stop_work(relay_parent),
+				)))
+				.await;
 
 			let errs: Vec<_> = err_rx.collect().await;
 			assert_eq!(errs.len(), 0);
@@ -954,7 +1087,11 @@ mod tests {
 		let run_args = HashMap::new();
 
 		test_harness(run_args, |mut overseer_handle, err_rx| async move {
-			overseer_handle.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::stop_work(relay_parent)))).await;
+			overseer_handle
+				.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(
+					ActiveLeavesUpdate::stop_work(relay_parent),
+				)))
+				.await;
 
 			let errs: Vec<_> = err_rx.collect().await;
 			assert_eq!(errs.len(), 1);
@@ -971,10 +1108,8 @@ mod tests {
 		let pool = sp_core::testing::TaskExecutor::new();
 		let (context, _) = make_subsystem_context::<CandidateSelectionMessage, _>(pool.clone());
 
-		let SpawnedSubsystem { name, .. } = FakeCandidateSelectionSubsystem::new(
-			pool,
-			HashMap::new(),
-		).start(context);
+		let SpawnedSubsystem { name, .. } =
+			FakeCandidateSelectionSubsystem::new(pool, HashMap::new()).start(context);
 		assert_eq!(name, "FakeCandidateSelection");
 	}
 }
diff --git a/polkadot/primitives/src/v1.rs b/polkadot/primitives/src/v1.rs
index 67926f2ab336951be0e0e9eefe2260efc761e3aa..781f6f771195a9ef8e6682c5f48e8703534d4124 100644
--- a/polkadot/primitives/src/v1.rs
+++ b/polkadot/primitives/src/v1.rs
@@ -585,7 +585,7 @@ pub struct OccupiedCore<N = BlockNumber> {
 
 /// Information about a core which is currently occupied.
 #[derive(Clone, Encode, Decode)]
-#[cfg_attr(feature = "std", derive(PartialEq, Debug))]
+#[cfg_attr(feature = "std", derive(PartialEq, Debug, Default))]
 pub struct ScheduledCore {
 	/// The ID of a para scheduled.
 	pub para_id: Id,
@@ -613,8 +613,19 @@ pub enum CoreState<N = BlockNumber> {
 	Free,
 }
 
+impl<N> CoreState<N> {
+	/// If this core state has a `para_id`, return it.
+	pub fn para_id(&self) -> Option<Id> {
+		match self {
+			Self::Occupied(OccupiedCore { para_id, ..}) => Some(*para_id),
+			Self::Scheduled(ScheduledCore { para_id, .. }) => Some(*para_id),
+			Self::Free => None,
+		}
+	}
+}
+
 /// An assumption being made about the state of an occupied core.
-#[derive(Clone, Encode, Decode)]
+#[derive(Clone, Copy, Encode, Decode)]
 #[cfg_attr(feature = "std", derive(PartialEq, Debug))]
 pub enum OccupiedCoreAssumption {
 	/// The candidate occupying the core was made available and included to free the core.
diff --git a/polkadot/roadmap/implementers-guide/src/SUMMARY.md b/polkadot/roadmap/implementers-guide/src/SUMMARY.md
index 59df20db2135500d36232cc395aa87c947b2446b..fa364ac490f13688e5dfec276503acc905732da7 100644
--- a/polkadot/roadmap/implementers-guide/src/SUMMARY.md
+++ b/polkadot/roadmap/implementers-guide/src/SUMMARY.md
@@ -15,6 +15,15 @@
   - [Validity Module](runtime/validity.md)
   - [Router Module](runtime/router.md)
 - [Runtime APIs](runtime-api/README.md)
+  - [Validators](runtime-api/validators.md)
+  - [Validator Groups](runtime-api/validator-groups.md)
+  - [Availability Cores](runtime-api/availability-cores.md)
+  - [Global Validation Data](runtime-api/global-validation-data.md)
+  - [Local Validation Data](runtime-api/local-validation-data.md)
+  - [Session Index](runtime-api/session-index.md)
+  - [Validation Code](runtime-api/validation-code.md)
+  - [Candidate Pending Availability](runtime-api/candidate-pending-availability.md)
+  - [Candidate Events](runtime-api/candidate-events.md)
 - [Node Architecture](node/README.md)
   - [Subsystems and Jobs](node/subsystems-and-jobs.md)
   - [Overseer](node/overseer.md)
diff --git a/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md b/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md
index 7692cd885d11280bd2300e963a9187a3cfb16ab7..8e6afc9d31f9febf85b036ffc101350b9aded05b 100644
--- a/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md
+++ b/polkadot/roadmap/implementers-guide/src/node/utility/provisioner.md
@@ -40,7 +40,54 @@ Note that block authors must re-send a `ProvisionerMessage::RequestBlockAuthorsh
 
 ## Block Production
 
-When a validator is selected by BABE to author a block, it becomes a block producer. The provisioner is the subsystem best suited to choosing which specific backed candidates and availability bitfields should be assembled into the block. To engage this functionality, a `ProvisionerMessage::RequestInherentData` is sent; the response is a set of non-conflicting candidates and the appropriate bitfields. Non-conflicting means that there are never two distinct parachain candidates included for the same parachain and that new parachain candidates cannot be included until the previous one either gets declared available or expired.
+When a validator is selected by BABE to author a block, it becomes a block producer. The provisioner is the subsystem best suited to choosing which specific backed candidates and availability bitfields should be assembled into the block. To engage this functionality, a `ProvisionerMessage::RequestInherentData` is sent; the response is a set of non-conflicting candidates and the appropriate bitfields. Non-conflicting means that there are never two distinct parachain candidates included for the same parachain and that new parachain candidates cannot be backed until the previous one either gets declared available or expired.
+
+### Bitfield Selection
+
+Our goal with respect to bitfields is simple: maximize availability. However, it's not quite as simple as always including all bitfields; there are constraints which still need to be met:
+
+- We cannot choose more than one bitfield per validator.
+- Each bitfield must correspond to an occupied core.
+
+Beyond that, a semi-arbitrary selection policy is fine. In order to meet the goal of maximizing availability, a heuristic of picking the bitfield with the greatest number of 1 bits set in the event of conflict is useful.
+
+### Candidate Selection
+
+The goal of candidate selection is to determine which cores are free, and then to the degree possible, pick a candidate appropriate to each free core.
+
+To determine availability:
+
+- Get the list of core states from the runtime API
+- For each core state:
+  - On `CoreState::Scheduled`, then we can make an `OccupiedCoreAssumption::Free`.
+  - On `CoreState::Occupied`, then we may be able to make an assumption:
+    - If the bitfields indicate availability and there is a scheduled `next_up_on_available`, then we can make an `OccupiedCoreAssumption::Included`.
+    - If the bitfields do not indicate availability, and there is a scheduled `next_up_on_time_out`, and `occupied_core.time_out_at == block_number_under_production`, then we can make an `OccupiedCoreAssumption::TimedOut`.
+  - If we did not make an `OccupiedCoreAssumption`, then continue on to the next core.
+  - Now compute the core's `validation_data_hash`: get the `LocalValidationData` from the runtime, given the known `ParaId` and `OccupiedCoreAssumption`; this can be combined with a cached `GlobalValidationData` to compute the hash.
+  - Find an appropriate candidate for the core.
+    - There are two constraints: `backed_candidate.candidate.descriptor.para_id == scheduled_core.para_id && candidate.candidate.descriptor.validation_data_hash == computed_validation_data_hash`.
+    - In the event that more than one candidate meets the constraints, selection between the candidates is arbitrary. However, not more than one candidate can be selected per core.
+
+The end result of this process is a vector of `BackedCandidate`s, sorted in order of their core index.
+
+### Determining Bitfield Availability
+
+An occupied core has a `CoreAvailability` bitfield. We also have a list of `SignedAvailabilityBitfield`s. We need to determine from these whether or not a core at a particular index has become available.
+
+The key insight required is that `CoreAvailability` is transverse to the `SignedAvailabilityBitfield`s: if we conceptualize the list of bitfields as many rows, each bit of which is its own column, then `CoreAvailability` for a given core index is the vertical slice of bits in the set at that index.
+
+To compute bitfield availability, then:
+
+- Start with a copy of `OccupiedCore.availability`
+- For each bitfield in the list of `SignedAvailabilityBitfield`s:
+  - Get the bitfield's `validator_index`
+  - Update the availability. Conceptually, assuming bit vectors: `availability[validator_index] |= bitfield[core_idx]`
+- Availability has a 2/3 threshold. Therefore: `3 * availability.count_ones() >= 2 * availability.len()`
+
+### Notes
+
+See also: [Scheduler Module: Availability Cores](../../runtime/scheduler.md#availability-cores).
 
 One might ask: given `ProvisionerMessage::RequestInherentData`, what's the point of `ProvisionerMessage::RequestBlockAuthorshipData`? The answer is that the block authorship data includes more information than is present in the inherent data; disputes, for example.
 
@@ -51,10 +98,10 @@ The subsystem should maintain a set of handles to Block Authorship Provisioning
 ### On Overseer Signal
 
 - `ActiveLeavesUpdate`:
-	- For each `activated` head:
-		- spawn a Block Authorship Provisioning Job with the given relay parent, storing a bidirectional channel with that job.
-	- For each `deactivated` head:
-		- terminate the Block Authorship Provisioning Job for the given relay parent, if any.
+  - For each `activated` head:
+    - spawn a Block Authorship Provisioning Job with the given relay parent, storing a bidirectional channel with that job.
+  - For each `deactivated` head:
+    - terminate the Block Authorship Provisioning Job for the given relay parent, if any.
 - `Conclude`: Forward `Conclude` to all jobs, waiting a small amount of time for them to join, and then hard-exiting.
 
 ### On `ProvisionerMessage`
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/README.md b/polkadot/roadmap/implementers-guide/src/runtime-api/README.md
index cb8998230d44934fbe62fcdb2b97b2eeb837e230..ce1d3948f043a243639a346560349e682bd3af10 100644
--- a/polkadot/roadmap/implementers-guide/src/runtime-api/README.md
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/README.md
@@ -7,17 +7,19 @@ Every block in the relay-chain contains a *state root* which is the root hash of
 Although Runtime APIs are often used for simple storage access, they are actually empowered to do arbitrary computation. The implementation of the Runtime APIs lives within the Runtime as Wasm code and exposes extern functions that can be invoked with arguments and have a return value. Runtime APIs have access to a variety of host functions, which are contextual functions provided by the Wasm execution context, that allow it to carry out many different types of behaviors.
 
 Abilities provided by host functions includes:
-  * State Access
-  * Offchain-DB Access
-  * Submitting transactions to the transaction queue
-  * Optimized versions of cryptographic functions
-  * More
+
+* State Access
+* Offchain-DB Access
+* Submitting transactions to the transaction queue
+* Optimized versions of cryptographic functions
+* More
 
 So it is clear that Runtime APIs are a versatile and powerful tool to leverage the state of the chain. In general, we will use Runtime APIs for these purposes:
-  * Access of a storage item
-  * Access of a bundle of related storage items
-  * Deriving a value from storage based on arguments
-  * Submitting misbehavior reports
+
+* Access of a storage item
+* Access of a bundle of related storage items
+* Deriving a value from storage based on arguments
+* Submitting misbehavior reports
 
 More broadly, we have the goal of using Runtime APIs to write Node-side code that fulfills the requirements set by the Runtime. In particular, the constraints set forth by the [Scheduler](../runtime/scheduler.md) and [Inclusion](../runtime/inclusion.md) modules. These modules are responsible for advancing paras with a two-phase protocol where validators are first chosen to validate and back a candidate and then required to ensure availability of referenced data. In the second phase, validators are meant to attest to those para-candidates that they have their availability chunk for. As the Node-side code needs to generate the inputs into these two phases, the runtime API needs to transmit information from the runtime that is aware of the Availability Cores model instantiated by the Scheduler and Inclusion modules.
 
@@ -35,180 +37,3 @@ The next sections will contain information on specific runtime APIs. The format
 /// best for the implementation to return an error indicating the failure mode.
 fn some_runtime_api(at: Block, arg1: Type1, arg2: Type2, ...) -> ReturnValue;
 ```
-
-## Validators
-
-Yields the validator-set at the state of a given block. This validator set is always the one responsible for backing parachains in the child of the provided block.
-
-```rust
-fn validators(at: Block) -> Vec<ValidatorId>;
-```
-
-## Validator Groups
-
-Yields the validator groups used during the current session. The validators in the groups are referred to by their index into the validator-set.
-
-```rust
-/// A helper data-type for tracking validator-group rotations.
-struct GroupRotationInfo {
-	session_start_block: BlockNumber,
-	group_rotation_frequency: BlockNumber,
-	now: BlockNumber,
-}
-
-impl GroupRotationInfo {
-	/// Returns the index of the group needed to validate the core at the given index,
-	/// assuming the given amount of cores/groups.
-	fn group_for_core(&self, core_index, cores) -> GroupIndex;
-
-	/// Returns the block number of the next rotation after the current block. If the current block
-	/// is 10 and the rotation frequency is 5, this should return 15.
-	///
-	/// If the group rotation frequency is 0, returns 0.
-	fn next_rotation_at(&self) -> BlockNumber;
-
-	/// Returns the block number of the last rotation before or including the current block. If the
-	/// current block is 10 and the rotation frequency is 5, this should return 10.
-	///
-	/// If the group rotation frequency is 0, returns 0.
-	fn last_rotation_at(&self) -> BlockNumber;
-}
-
-/// Returns the validator groups and rotation info localized based on the block whose state
-/// this is invoked on. Note that `now` in the `GroupRotationInfo` should be the successor of
-/// the number of the block.
-fn validator_groups(at: Block) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo);
-```
-
-## Availability Cores
-
-Yields information on all availability cores. Cores are either free or occupied. Free cores can have paras assigned to them. Occupied cores don't, but they can become available part-way through a block due to bitfields and then have something scheduled on them. To allow optimistic validation of candidates, the occupied cores are accompanied by information on what is upcoming. This information can be leveraged when validators perceive that there is a high likelihood of a core becoming available based on bitfields seen, and then optimistically validate something that would become scheduled based on that, although there is no guarantee on what the block producer will actually include in the block.
-
-```rust
-fn availability_cores(at: Block) -> Vec<CoreState>;
-```
-
-This is all the information that a validator needs about scheduling for the current block. It includes all information on [Scheduler](../runtime/scheduler.md) core-assignments and [Inclusion](../runtime/inclusion.md) state of blocks occupying availability cores. It includes data necessary to determine not only which paras are assigned now, but which cores are likely to become freed after processing bitfields, and exactly which bitfields would be necessary to make them so.
-
-```rust
-struct OccupiedCore {
-	/// The ID of the para occupying the core.
-	para_id: ParaId,
-	/// If this core is freed by availability, this is the assignment that is next up on this
-	/// core, if any. None if there is nothing queued for this core.
-	next_up_on_available: Option<ScheduledCore>,
-	/// The relay-chain block number this began occupying the core at.
-	occupied_since: BlockNumber,
-	/// The relay-chain block this will time-out at, if any.
-	time_out_at: BlockNumber,
-	/// If this core is freed by being timed-out, this is the assignment that is next up on this
-	/// core. None if there is nothing queued for this core or there is no possibility of timing
-	/// out.
-	next_up_on_time_out: Option<ScheduledCore>,
-	/// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
-	/// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
-	/// this will be available.
-	availability: Bitfield,
-	/// The group assigned to distribute availability pieces of this candidate.
-	group_responsible: GroupIndex,
-}
-
-struct ScheduledCore {
-	/// The ID of a para scheduled.
-	para_id: ParaId,
-	/// The collator required to author the block, if any.
-	collator: Option<CollatorId>,
-}
-
-enum CoreState {
-	/// The core is currently occupied.
-	Occupied(OccupiedCore),
-	/// The core is currently free, with a para scheduled and given the opportunity
-	/// to occupy.
-	///
-	/// If a particular Collator is required to author this block, that is also present in this
-	/// variant.
-	Scheduled(ScheduledCore),
-	/// The core is currently free and there is nothing scheduled. This can be the case for parathread
-	/// cores when there are no parathread blocks queued. Parachain cores will never be left idle.
-	Free,
-}
-```
-
-## Global Validation Schedule
-
-Yields the [`GlobalValidationData`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.
-
-```rust
-fn global_validation_data(at: Block) -> GlobalValidationData;
-```
-
-## Local Validation Data
-
-Yields the [`LocalValidationData`](../types/candidate.md#localvalidationdata) for the given [`ParaId`](../types/candidate.md#paraid) along with an assumption that should be used if the para currently occupies a core: whether the candidate occupying that core should be assumed to have been made available and included or timed out and discarded, along with a third option to assert that the core was not occupied. This choice affects everything from the parent head-data, the validation code, and the state of message-queues. Typically, users will take the assumption that either the core was free or that the occupying candidate was included, as timeouts are expected only in adversarial circumstances and even so, only in a small minority of blocks directly following validator set rotations.
-
-The documentation of [`LocalValidationData`](../types/candidate.md#localvalidationdata) has more information on this dichotomy.
-
-```rust
-/// An assumption being made about the state of an occupied core.
-enum OccupiedCoreAssumption {
-	/// The candidate occupying the core was made available and included to free the core.
-	Included,
-	/// The candidate occupying the core timed out and freed the core without advancing the para.
-	TimedOut,
-	/// The core was not occupied to begin with.
-	Free,
-}
-
-/// Returns the local validation data for the given para and occupied core assumption.
-///
-/// Returns `None` if either the para is not registered or the assumption is `Freed`
-/// and the para already occupies a core.
-fn local_validation_data(at: Block, ParaId, OccupiedCoreAssumption) -> Option<LocalValidationData>;
-```
-
-## Session Index
-
-Get the session index that is expected at the child of a block.
-
-In the [`Initializer`](../runtime/initializer.md) module, session changes are buffered by one block. The session index of the child of any relay block is always predictable by that block's state.
-
-This session index can be used to derive a [`SigningContext`](../types/candidate.md#signing-context).
-
-```rust
-/// Returns the session index expected at a child of the block.
-fn session_index_for_child(at: Block) -> SessionIndex;
-```
-
-## Validation Code
-
-Fetch the validation code used by a para, making the given `OccupiedCoreAssumption`.
-
-```rust
-fn validation_code(at: Block, ParaId, OccupiedCoreAssumption) -> Option<ValidationCode>;
-```
-
-## Candidate Pending Availability
-
-Get the receipt of a candidate pending availability. This returns `Some` for any paras assigned to occupied cores in `availability_cores` and `None` otherwise.
-
-```rust
-fn candidate_pending_availability(at: Block, ParaId) -> Option<CommittedCandidateReceipt>;
-```
-
-## Candidate Events
-
-Yields a vector of events concerning candidates that occurred within the given block.
-
-```rust
-enum CandidateEvent {
-	/// This candidate receipt was backed in the most recent block.
-	CandidateBacked(CandidateReceipt, HeadData),
-	/// This candidate receipt was included and became a parablock at the most recent block.
-	CandidateIncluded(CandidateReceipt, HeadData),
-	/// This candidate receipt was not made available in time and timed out.
-	CandidateTimedOut(CandidateReceipt, HeadData),
-}
-
-fn candidate_events(at: Block) -> Vec<CandidateEvent>;
-```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/availability-cores.md b/polkadot/roadmap/implementers-guide/src/runtime-api/availability-cores.md
new file mode 100644
index 0000000000000000000000000000000000000000..561e817cca3f147c8ad7baa9b1c7d08a3380037e
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/availability-cores.md
@@ -0,0 +1,56 @@
+# Availability Cores
+
+Yields information on all availability cores. Cores are either free or occupied. Free cores can have paras assigned to them. Occupied cores don't, but they can become available part-way through a block due to bitfields and then have something scheduled on them. To allow optimistic validation of candidates, the occupied cores are accompanied by information on what is upcoming. This information can be leveraged when validators perceive that there is a high likelihood of a core becoming available based on bitfields seen, and then optimistically validate something that would become scheduled based on that, although there is no guarantee on what the block producer will actually include in the block.
+
+See also the [Scheduler Module](../runtime/scheduler.md) for a high-level description of what an availability core is and why it exists.
+
+```rust
+fn availability_cores(at: Block) -> Vec<CoreState>;
+```
+
+This is all the information that a validator needs about scheduling for the current block. It includes all information on [Scheduler](../runtime/scheduler.md) core-assignments and [Inclusion](../runtime/inclusion.md) state of blocks occupying availability cores. It includes data necessary to determine not only which paras are assigned now, but which cores are likely to become freed after processing bitfields, and exactly which bitfields would be necessary to make them so.
+
+```rust
+struct OccupiedCore {
+    /// The ID of the para occupying the core.
+    para_id: ParaId,
+    /// If this core is freed by availability, this is the assignment that is next up on this
+    /// core, if any. None if there is nothing queued for this core.
+    next_up_on_available: Option<ScheduledCore>,
+    /// The relay-chain block number this began occupying the core at.
+    occupied_since: BlockNumber,
+    /// The relay-chain block this will time-out at, if any.
+    time_out_at: BlockNumber,
+    /// If this core is freed by being timed-out, this is the assignment that is next up on this
+    /// core. None if there is nothing queued for this core or there is no possibility of timing
+    /// out.
+    next_up_on_time_out: Option<ScheduledCore>,
+    /// A bitfield with 1 bit for each validator in the set. `1` bits mean that the corresponding
+    /// validators has attested to availability on-chain. A 2/3+ majority of `1` bits means that
+    /// this will be available.
+    availability: Bitfield,
+    /// The group assigned to distribute availability pieces of this candidate.
+    group_responsible: GroupIndex,
+}
+
+struct ScheduledCore {
+    /// The ID of a para scheduled.
+    para_id: ParaId,
+    /// The collator required to author the block, if any.
+    collator: Option<CollatorId>,
+}
+
+enum CoreState {
+    /// The core is currently occupied.
+    Occupied(OccupiedCore),
+    /// The core is currently free, with a para scheduled and given the opportunity
+    /// to occupy.
+    ///
+    /// If a particular Collator is required to author this block, that is also present in this
+    /// variant.
+    Scheduled(ScheduledCore),
+    /// The core is currently free and there is nothing scheduled. This can be the case for parathread
+    /// cores when there are no parathread blocks queued. Parachain cores will never be left idle.
+    Free,
+}
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-events.md b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-events.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ebdcd04917bdf1686b9d2a549a1f922a3c2cc03
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-events.md
@@ -0,0 +1,16 @@
+# Candidate Events
+
+Yields a vector of events concerning candidates that occurred within the given block.
+
+```rust
+enum CandidateEvent {
+	/// This candidate receipt was backed in the most recent block.
+	CandidateBacked(CandidateReceipt, HeadData),
+	/// This candidate receipt was included and became a parablock at the most recent block.
+	CandidateIncluded(CandidateReceipt, HeadData),
+	/// This candidate receipt was not made available in time and timed out.
+	CandidateTimedOut(CandidateReceipt, HeadData),
+}
+
+fn candidate_events(at: Block) -> Vec<CandidateEvent>;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md
new file mode 100644
index 0000000000000000000000000000000000000000..9c8969f6a958b2cce360ad83c1dc1e57d91e207e
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/candidate-pending-availability.md
@@ -0,0 +1,7 @@
+# Candidate Pending Availability
+
+Get the receipt of a candidate pending availability. This returns `Some` for any paras assigned to occupied cores in `availability_cores` and `None` otherwise.
+
+```rust
+fn candidate_pending_availability(at: Block, ParaId) -> Option<CommittedCandidateReceipt>;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/global-validation-data.md b/polkadot/roadmap/implementers-guide/src/runtime-api/global-validation-data.md
new file mode 100644
index 0000000000000000000000000000000000000000..249f9b9a060ad277b670dc77963b0baa479de779
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/global-validation-data.md
@@ -0,0 +1,7 @@
+# Global Validation Data
+
+Yields the [`GlobalValidationData`](../types/candidate.md#globalvalidationschedule) at the state of a given block. This applies to all para candidates with the relay-parent equal to that block.
+
+```rust
+fn global_validation_data(at: Block) -> GlobalValidationData;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/local-validation-data.md b/polkadot/roadmap/implementers-guide/src/runtime-api/local-validation-data.md
new file mode 100644
index 0000000000000000000000000000000000000000..10f5d78ede1eb4f5b691fd193b695871adc28995
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/local-validation-data.md
@@ -0,0 +1,23 @@
+# Local Validation Data
+
+Yields the [`LocalValidationData`](../types/candidate.md#localvalidationdata) for the given [`ParaId`](../types/candidate.md#paraid) along with an assumption that should be used if the para currently occupies a core: whether the candidate occupying that core should be assumed to have been made available and included or timed out and discarded, along with a third option to assert that the core was not occupied. This choice affects everything from the parent head-data, the validation code, and the state of message-queues. Typically, users will take the assumption that either the core was free or that the occupying candidate was included, as timeouts are expected only in adversarial circumstances and even so, only in a small minority of blocks directly following validator set rotations.
+
+The documentation of [`LocalValidationData`](../types/candidate.md#localvalidationdata) has more information on this dichotomy.
+
+```rust
+/// An assumption being made about the state of an occupied core.
+enum OccupiedCoreAssumption {
+    /// The candidate occupying the core was made available and included to free the core.
+    Included,
+    /// The candidate occupying the core timed out and freed the core without advancing the para.
+    TimedOut,
+    /// The core was not occupied to begin with.
+    Free,
+}
+
+/// Returns the local validation data for the given para and occupied core assumption.
+///
+/// Returns `None` if either the para is not registered or the assumption is `Freed`
+/// and the para already occupies a core.
+fn local_validation_data(at: Block, ParaId, OccupiedCoreAssumption) -> Option<LocalValidationData>;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/session-index.md b/polkadot/roadmap/implementers-guide/src/runtime-api/session-index.md
new file mode 100644
index 0000000000000000000000000000000000000000..1baf6a167dbb2543a1b6080dbe22dac266827fd5
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/session-index.md
@@ -0,0 +1,12 @@
+# Session Index
+
+Get the session index that is expected at the child of a block.
+
+In the [`Initializer`](../runtime/initializer.md) module, session changes are buffered by one block. The session index of the child of any relay block is always predictable by that block's state.
+
+This session index can be used to derive a [`SigningContext`](../types/candidate.md#signing-context).
+
+```rust
+/// Returns the session index expected at a child of the block.
+fn session_index_for_child(at: Block) -> SessionIndex;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/validation-code.md b/polkadot/roadmap/implementers-guide/src/runtime-api/validation-code.md
new file mode 100644
index 0000000000000000000000000000000000000000..908e3bfbd1450c81d83ed3a8a0729269f6fb85ef
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/validation-code.md
@@ -0,0 +1,7 @@
+# Validation Code
+
+Fetch the validation code used by a para, making the given `OccupiedCoreAssumption`.
+
+```rust
+fn validation_code(at: Block, ParaId, OccupiedCoreAssumption) -> Option<ValidationCode>;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/validator-groups.md b/polkadot/roadmap/implementers-guide/src/runtime-api/validator-groups.md
new file mode 100644
index 0000000000000000000000000000000000000000..42b39f976d19977df6e882127dd759fe9596d370
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/validator-groups.md
@@ -0,0 +1,35 @@
+# Validator Groups
+
+Yields the validator groups used during the current session. The validators in the groups are referred to by their index into the validator-set.
+
+```rust
+/// A helper data-type for tracking validator-group rotations.
+struct GroupRotationInfo {
+    session_start_block: BlockNumber,
+    group_rotation_frequency: BlockNumber,
+    now: BlockNumber,
+}
+
+impl GroupRotationInfo {
+    /// Returns the index of the group needed to validate the core at the given index,
+    /// assuming the given amount of cores/groups.
+    fn group_for_core(&self, core_index, cores) -> GroupIndex;
+
+    /// Returns the block number of the next rotation after the current block. If the current block
+    /// is 10 and the rotation frequency is 5, this should return 15.
+    ///
+    /// If the group rotation frequency is 0, returns 0.
+    fn next_rotation_at(&self) -> BlockNumber;
+
+    /// Returns the block number of the last rotation before or including the current block. If the
+    /// current block is 10 and the rotation frequency is 5, this should return 10.
+    ///
+    /// If the group rotation frequency is 0, returns 0.
+    fn last_rotation_at(&self) -> BlockNumber;
+}
+
+/// Returns the validator groups and rotation info localized based on the block whose state
+/// this is invoked on. Note that `now` in the `GroupRotationInfo` should be the successor of
+/// the number of the block.
+fn validator_groups(at: Block) -> (Vec<Vec<ValidatorIndex>>, GroupRotationInfo);
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime-api/validators.md b/polkadot/roadmap/implementers-guide/src/runtime-api/validators.md
new file mode 100644
index 0000000000000000000000000000000000000000..b7f1d964754755c2224dc89a3503835398a581a2
--- /dev/null
+++ b/polkadot/roadmap/implementers-guide/src/runtime-api/validators.md
@@ -0,0 +1,7 @@
+# Validators
+
+Yields the validator-set at the state of a given block. This validator set is always the one responsible for backing parachains in the child of the provided block.
+
+```rust
+fn validators(at: Block) -> Vec<ValidatorId>;
+```
diff --git a/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md b/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md
index 96641484e456c24d393ce07e66cc2ad9b21ed7b7..fa78237089fa61bcd8cb89192b4bcf221739f4c9 100644
--- a/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md
+++ b/polkadot/roadmap/implementers-guide/src/runtime/scheduler.md
@@ -14,82 +14,88 @@ It aims to achieve these tasks with these goals in mind:
 - Validator assignments should not be gameable. Malicious cartels should not be able to manipulate the scheduler to assign themselves as desired.
 - High or close to optimal throughput of parachains and parathreads. Work among validator groups should be balanced.
 
+## Availability Cores
+
 The Scheduler manages resource allocation using the concept of "Availability Cores". There will be one availability core for each parachain, and a fixed number of cores used for multiplexing parathreads. Validators will be partitioned into groups, with the same number of groups as availability cores. Validator groups will be assigned to different availability cores over time.
 
 An availability core can exist in either one of two states at the beginning or end of a block: free or occupied. A free availability core can have a parachain or parathread assigned to it for the potential to have a backed candidate included. After backing, the core enters the occupied state as the backed candidate is pending availability. There is an important distinction: a core is not considered occupied until it is in charge of a block pending availability, although the implementation may treat scheduled cores the same as occupied ones for brevity. A core exits the occupied state when the candidate is no longer pending availability - either on timeout or on availability. A core starting in the occupied state can move to the free state and back to occupied all within a single block, as availability bitfields are processed before backed candidates. At the end of the block, there is a possible timeout on availability which can move the core back to the free state if occupied.
 
+Cores are treated as an ordered list and are typically referred to by their index in that list.
+
 ```dot process
 digraph {
-	label = "Availability Core State Machine\n\n\n";
-	labelloc = "t";
+  label = "Availability Core State Machine\n\n\n";
+  labelloc = "t";
 
-	{ rank=same vg1 vg2 }
+  { rank=same vg1 vg2 }
 
-	vg1 [label = "Free" shape=rectangle]
-	vg2 [label = "Occupied" shape=rectangle]
+  vg1 [label = "Free" shape=rectangle]
+  vg2 [label = "Occupied" shape=rectangle]
 
-	vg1 -> vg2 [label = "Assignment & Backing" ]
-	vg2 -> vg1 [label = "Availability or Timeout" ]
+  vg1 -> vg2 [label = "Assignment & Backing" ]
+  vg2 -> vg1 [label = "Availability or Timeout" ]
 }
 ```
 
 ```dot process
 digraph {
-	label = "Availability Core Transitions within Block\n\n\n";
-	labelloc = "t";
-	splines="line";
-
-	subgraph cluster_left {
-		label = "";
-		labelloc = "t";
-
-		fr1 [label = "Free" shape=rectangle]
-		fr2 [label = "Free" shape=rectangle]
-		occ [label = "Occupied" shape=rectangle]
-
-		fr1 -> fr2 [label = "No Backing"]
-		fr1 -> occ [label = "Backing"]
-
-		{ rank=same fr2 occ }
-	}
-
-	subgraph cluster_right {
-		label = "";
-		labelloc = "t";
-
-		occ2 [label = "Occupied" shape=rectangle]
-		fr3 [label = "Free" shape=rectangle]
-		fr4 [label = "Free" shape=rectangle]
-		occ3 [label = "Occupied" shape=rectangle]
-		occ4 [label = "Occupied" shape=rectangle]
-
-		occ2 -> fr3 [label = "Availability"]
-		occ2 -> occ3 [label = "No availability"]
-		fr3 -> fr4 [label = "No backing"]
-		fr3 -> occ4 [label = "Backing"]
-		occ3 -> occ4 [label = "(no change)"]
-		occ3 -> fr3 [label = "Availability Timeout"]
-
-		{ rank=same; fr3[group=g1]; occ3[group=g2] }
-		{ rank=same; fr4[group=g1]; occ4[group=g2] }
-	}
+  label = "Availability Core Transitions within Block\n\n\n";
+  labelloc = "t";
+  splines="line";
+
+  subgraph cluster_left {
+    label = "";
+    labelloc = "t";
+
+    fr1 [label = "Free" shape=rectangle]
+    fr2 [label = "Free" shape=rectangle]
+    occ [label = "Occupied" shape=rectangle]
+
+    fr1 -> fr2 [label = "No Backing"]
+    fr1 -> occ [label = "Backing"]
+
+    { rank=same fr2 occ }
+  }
+
+  subgraph cluster_right {
+    label = "";
+    labelloc = "t";
+
+    occ2 [label = "Occupied" shape=rectangle]
+    fr3 [label = "Free" shape=rectangle]
+    fr4 [label = "Free" shape=rectangle]
+    occ3 [label = "Occupied" shape=rectangle]
+    occ4 [label = "Occupied" shape=rectangle]
+
+    occ2 -> fr3 [label = "Availability"]
+    occ2 -> occ3 [label = "No availability"]
+    fr3 -> fr4 [label = "No backing"]
+    fr3 -> occ4 [label = "Backing"]
+    occ3 -> occ4 [label = "(no change)"]
+    occ3 -> fr3 [label = "Availability Timeout"]
+
+    { rank=same; fr3[group=g1]; occ3[group=g2] }
+    { rank=same; fr4[group=g1]; occ4[group=g2] }
+  }
 }
 ```
 
+## Validator Groups
+
 Validator group assignments do not need to change very quickly. The security benefits of fast rotation is redundant with the challenge mechanism in the [Validity module](validity.md). Because of this, we only divide validators into groups at the beginning of the session and do not shuffle membership during the session. However, we do take steps to ensure that no particular validator group has dominance over a single parachain or parathread-multiplexer for an entire session to provide better guarantees of liveness.
 
 Validator groups rotate across availability cores in a round-robin fashion, with rotation occurring at fixed intervals. The i'th group will be assigned to the `(i+k)%n`'th core at any point in time, where `k` is the number of rotations that have occurred in the session, and `n` is the number of cores. This makes upcoming rotations within the same session predictable.
 
 When a rotation occurs, validator groups are still responsible for distributing availability chunks for any previous cores that are still occupied and pending availability. In practice, rotation and availability-timeout frequencies should be set so this will only be the core they have just been rotated from. It is possible that a validator group is rotated onto a core which is currently occupied. In this case, the validator group will have nothing to do until the previously-assigned group finishes their availability work and frees the core or the availability process times out. Depending on if the core is for a parachain or parathread, a different timeout `t` from the [`HostConfiguration`](../types/runtime.md#host-configuration) will apply. Availability timeouts should only be triggered in the first `t-1` blocks after the beginning of a rotation.
 
+## Claims
+
 Parathreads operate on a system of claims. Collators participate in auctions to stake a claim on authoring the next block of a parathread, although the auction mechanism is beyond the scope of the scheduler. The scheduler guarantees that they'll be given at least a certain number of attempts to author a candidate that is backed. Attempts that fail during the availability phase are not counted, since ensuring availability at that stage is the responsibility of the backing validators, not of the collator. When a claim is accepted, it is placed into a queue of claims, and each claim is assigned to a particular parathread-multiplexing core in advance. Given that the current assignments of validator groups to cores are known, and the upcoming assignments are predictable, it is possible for parathread collators to know who they should be talking to now and how they should begin establishing connections with as a fallback.
 
 With this information, the Node-side can be aware of which parathreads have a good chance of being includable within the relay-chain block and can focus any additional resources on backing candidates from those parathreads. Furthermore, Node-side code is aware of which validator group will be responsible for that thread. If the necessary conditions are reached for core reassignment, those candidates can be backed within the same block as the core being freed.
 
 Parathread claims, when scheduled onto a free core, may not result in a block pending availability. This may be due to collator error, networking timeout, or censorship by the validator group. In this case, the claims should be retried a certain number of times to give the collator a fair shot.
 
-Cores are treated as an ordered list of cores and are typically referred to by their index in that list.
-
 ## Storage
 
 Utility structs:
diff --git a/polkadot/roadmap/implementers-guide/src/types/availability.md b/polkadot/roadmap/implementers-guide/src/types/availability.md
index 3afa8d8df546eb91a66d456336582b062bf2f7db..241314067a9475cb1765332eb6ad70a7451757a0 100644
--- a/polkadot/roadmap/implementers-guide/src/types/availability.md
+++ b/polkadot/roadmap/implementers-guide/src/types/availability.md
@@ -14,6 +14,16 @@ type SignedAvailabilityBitfield = Signed<Bitvec>;
 struct Bitfields(Vec<(SignedAvailabilityBitfield)>), // bitfields sorted by validator index, ascending
 ```
 
+### Semantics
+
+A `SignedAvailabilityBitfield` represents the view from a particular validator's perspective. Each bit in the bitfield corresponds to a single [availability core](../runtime-api/availability-cores.md). A `1` bit indicates that the validator believes the following statements to be true for a core:
+
+- the availability core is occupied
+- there exists a [`CommittedCandidateReceipt`](candidate.html#committed-candidate-receipt) corresponding to that core. In other words, that para has a block in progress.
+- the validator's [Availability Store](../node/utility/availability-store.md) contains a chunk of that parablock's PoV.
+
+In other words, it is the transpose of [`OccupiedCore::availability`](../runtime-api/availability-cores.md).
+
 ## Proof-of-Validity
 
 Often referred to as PoV, this is a type-safe wrapper around bytes (`Vec<u8>`) when referring to data that acts as a stateless-client proof of validity of a candidate, when used as input to the validation function of the para.