diff --git a/analysis/facts/sporadic/arrival_bound.v b/analysis/facts/sporadic/arrival_bound.v
new file mode 100644
index 0000000000000000000000000000000000000000..0be55b8e02cca73d07e212651a6ce6c45002517d
--- /dev/null
+++ b/analysis/facts/sporadic/arrival_bound.v
@@ -0,0 +1,147 @@
+Require Import prosa.util.all.
+Require Export prosa.model.task.arrival.sporadic.
+Require Export prosa.analysis.facts.model.task_arrivals.
+
+(** * Sporadic Arrival Bound *)
+
+(** In the following, we upper bound the number of jobs that can
+    arrive in any interval as constrained by the sporadic task model's
+    minimum inter-arrival time [task_min_inter_arrival_time]. *)
+Section SporadicArrivalBound.
+
+  (** Consider any sporadic tasks  ... *)
+  Context {Task : TaskType} `{SporadicModel Task}.
+
+  (** ... and their jobs. *)
+  Context {Job : JobType} `{JobTask Job Task} `{JobArrival Job}.
+
+  (** We define the classic "ceiling of the interval length divided by
+      minimum inter-arrival time", which we prove to be correct in the
+      following. *)
+  Definition max_sporadic_arrivals (tsk : Task) (delta : duration) :=
+    div_ceil delta (task_min_inter_arrival_time tsk).
+
+  (** To establish the bound's soundness, consider any well-formed
+      arrival sequence, ... *)
+  Variable arr_seq : arrival_sequence Job.
+  Hypothesis H_consistent_arrivals: consistent_arrival_times arr_seq.
+  Hypothesis H_uniq_arr_seq: arrival_sequence_uniq arr_seq.
+
+  (** ... and any valid sporadic task [tsk] to be analyzed. *)
+  Variable tsk : Task.
+  Hypothesis H_sporadic_model: respects_sporadic_task_model arr_seq tsk.
+  Hypothesis H_valid_inter_min_arrival: valid_task_min_inter_arrival_time tsk.
+
+  (** Before we can establish the bound, we require two auxiliary
+      bounds, which we derive next. First, we consider minimum offset
+      of the <<n-th>> job of the task that arrives in a given interval. *)
+  Section NthJob.
+
+    (** For technical reasons, we require a "dummy" job in scope to
+        use the [nth] function. In the proofs, we establish that the
+        [dummy] job is never used, i.e., it is an irrelevant artifact
+        induced by the ssreflect API. It may be safely ignored. *)
+    Variable dummy : Job.
+
+    (** We observe that the <<i-th>> job to arrive in an interval <<[t1,t2)>>
+        arrives no earlier than [(task_min_inter_arrival_time tsk) *i]
+        time units after the beginning of the interval due the minimum
+        inter-arrival time of the sporadic task. *)
+    Lemma arrival_of_nth_job :
+      forall t1 t2 n i j,
+        n = number_of_task_arrivals arr_seq tsk t1 t2 ->
+        i < n ->
+        j = nth dummy (task_arrivals_between arr_seq tsk t1 t2) i ->
+        job_arrival j >= t1 + (task_min_inter_arrival_time tsk) * i.
+    Proof.
+      move=> t1 t2 n i j. rewrite /number_of_task_arrivals.
+      case ARR : (task_arrivals_between arr_seq tsk t1 t2) => [|j' js'] -> // LIM JOB.
+      elim: i LIM j JOB => [LIM j JOB|i IH LIM j JOB].
+      { rewrite muln0 addn0.
+        apply: job_arrival_between_ge; eauto.
+        apply: (task_arrivals_between_subset _ tsk _ t2).
+        by rewrite JOB ARR; apply mem_nth. }
+      { rewrite mulnSr addnA.
+        pose prev_j := nth dummy (j' :: js') i.
+        have prev_LIM : t1 + task_min_inter_arrival_time tsk * i + task_min_inter_arrival_time tsk
+                        <= job_arrival prev_j + task_min_inter_arrival_time tsk
+          by rewrite leq_add2r; apply IH => //; lia.
+        apply: (leq_trans prev_LIM).
+        have IN_j : j \in task_arrivals_between arr_seq tsk t1 t2
+          by rewrite JOB ARR; apply mem_nth.
+        have IN_prev : prev_j \in task_arrivals_between arr_seq tsk t1 t2
+          by rewrite /prev_j ARR; apply mem_nth; lia.
+        apply: H_sporadic_model => //=.
+        { rewrite JOB /prev_j  => /eqP.
+          rewrite nth_uniq; try lia; rewrite -ARR.
+          by apply: task_arrivals_between_uniq. }
+        { by apply/in_arrivals_implies_arrived/(task_arrivals_between_subset _ tsk t1 t2). }
+        { by apply/in_arrivals_implies_arrived/(task_arrivals_between_subset _ tsk t1 t2). }
+        { apply: in_task_arrivals_between_implies_job_of_task.
+          exact IN_prev. }
+        { apply: in_task_arrivals_between_implies_job_of_task.
+          exact: IN_j. }
+        { rewrite /prev_j JOB.
+          have SORTED : sorted by_arrival_times (j' :: js')
+            by rewrite -ARR; apply task_arrivals_between_sorted.
+          eapply (sorted_leq_nth _ _ _ SORTED); try lia.
+          - rewrite unfold_in simpl_predE; lia.
+          - rewrite unfold_in simpl_predE; lia. } }
+      Unshelve. all: by rewrite /by_arrival_times/transitive/reflexive; lia.
+    Qed.
+
+  End NthJob.
+
+  (** As a second auxiliary lemma, we establish a minimum length on
+      the interval for a given number of arrivals by applying the
+      previous lemma to the last job in the interval. We consider only
+      the case of "many" jobs, i.e., [n >= 2], which ensures that the
+      interval <<[t1, t2)>> spans at least one inter-arrival time. *)
+  Lemma minimum_distance_for_n_sporadic_arrivals:
+    forall t1 t2 n,
+      number_of_task_arrivals arr_seq tsk t1 t2 = n ->
+      n >= 2 ->
+      t2 > t1 + (task_min_inter_arrival_time tsk) * n.-1.
+  Proof.
+    move=> t1 t2 n H_num_arrivals H_many_jobs.
+    (* First, let us get ourselves a job so we can discharge the dummy job parameter. *)
+    destruct (task_arrivals_between arr_seq tsk t1 t2) as [|j js] eqn:ARR;
+      first by move: ARR H_num_arrivals H_many_jobs; rewrite /number_of_task_arrivals => -> //= ->; lia.
+    (* Now that we can use [nth], let's proceed with the actual proof. *)
+    set j_last := (nth j (task_arrivals_between arr_seq tsk t1 t2) n.-1).
+    have LAST : job_arrival j_last  < t2.
+    { apply: job_arrival_between_lt; eauto.
+      apply: task_arrivals_between_subset.
+      apply mem_nth.
+      by move: H_num_arrivals; rewrite /number_of_task_arrivals => ->; lia. }
+    have DIST :  t1 + task_min_inter_arrival_time tsk * n.-1 <= job_arrival j_last.
+    { apply: arrival_of_nth_job; auto;
+        first by rewrite [number_of_task_arrivals arr_seq tsk t1 _]H_num_arrivals; lia.
+      by rewrite /j_last //=. }
+    by lia.
+  Qed.
+
+  (** Based on the above lemma, it is easy to see that
+      [max_sporadic_arrivals] is indeed a correct upper bound on the
+      maximum number of arrivals in a given interval. *)
+  Theorem sporadic_task_arrivals_bound:
+    forall t1 t2,
+      number_of_task_arrivals arr_seq tsk t1 t2 <= max_sporadic_arrivals tsk (t2 - t1).
+  Proof.
+    move=> t1 t2.
+    case COUNT: (number_of_task_arrivals arr_seq tsk t1 t2) => // [n'].
+    case COUNT: n' COUNT => // [|n] NARR.
+    { (* one arrival *)
+      apply div_ceil_gt0 => //; rewrite subn_gt0.
+      by eapply number_of_task_arrivals_nonzero; eauto. }
+    { (* two or more arrivals *)
+      clear n' COUNT.
+      move: NARR. set n' := n.+2 => NARR.
+      have SEP: t2 >  t1 + (task_min_inter_arrival_time tsk) * n'.-1
+        by apply: minimum_distance_for_n_sporadic_arrivals.
+      move: SEP. rewrite -ltn_subRL => SEP.
+      by apply: div_ceil_multiple. }
+  Qed.
+
+
+End SporadicArrivalBound.