summaryrefslogtreecommitdiff
path: root/lib/kernel/precedence.mli
diff options
context:
space:
mode:
authorLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 19:07:11 +0200
committerLukasz Kasprzak <lukas@labunix.xyz>2026-08-11 19:07:11 +0200
commit19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f (patch)
tree0dcc72d79de4b4f68b19dc1d2934fef04887e1e3 /lib/kernel/precedence.mli
parent8ab24da337a10993b98696df5d499706318beb8e (diff)
downloadcolitur-19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f.tar.gz
colitur-19d5bbaab8fbf40f6fa6de8906169e3bb7144e1f.zip
kernel(precedence): rite-parameterised resolver
Three rite-supplied functions, not one: band (who wins, RG 91), disposition (what happens to the loser, RG 92-95) and admit (how many commemorations are admitted, RG 111). The loser's fate depends on the loser's own rank, so conflating them would resist extension. resolve takes the temporal candidate separately from the sanctoral list, which makes it total by construction. Every candidate lands in exactly one of observed, commemorations, deferred or omitted -- nothing is dropped silently, which is what makes the no-celebration-lost invariant checkable.
Diffstat (limited to 'lib/kernel/precedence.mli')
-rw-r--r--lib/kernel/precedence.mli60
1 files changed, 60 insertions, 0 deletions
diff --git a/lib/kernel/precedence.mli b/lib/kernel/precedence.mli
new file mode 100644
index 0000000..d30e5c3
--- /dev/null
+++ b/lib/kernel/precedence.mli
@@ -0,0 +1,60 @@
+(** The rite-parameterised resolver: RG 91 says who wins, RG 92-95 says what
+ happens to the loser, RG 108-111 says how many commemorations are admitted.
+ Three separate rite-supplied functions, because the loser's fate depends on
+ the loser's own rank, not the winner's -- conflating them would resist
+ extension to a second rite. *)
+
+(** Which of the day's two office streams a candidate came from. *)
+type origin = Temporal | Sanctoral [@@deriving sexp]
+
+(** RG 111: an admitted commemoration's own standing, distinct from its rank. *)
+type privilege = Privileged | Ordinary [@@deriving sexp]
+
+(** What becomes of a losing candidate. *)
+type disposition =
+ | Omit (** yields with no trace in the day's celebration *)
+ | Commemorate of privilege (** kept as a commemoration of the observed day *)
+ | Transfer (** moved to the next free day (RG 92-95) *)
+ | Repose (** kept only in a votive/private sense; not commemorated today *)
+[@@deriving sexp]
+
+(** A celebration together with the office stream it was drawn from. Parameterised
+ by the rite's rank type only, matching {!Celebration.t}. *)
+type 'r candidate = { cel : 'r Celebration.t; origin : origin } [@@deriving sexp]
+
+(** The day a resolution is computed for. Parameterised by the rite's season
+ type only -- a context has no rank of its own. *)
+type 's context = { date : Date.t; season : 's; weekday : Date.weekday }
+
+(** The rite's three resolution functions. *)
+type ('s, 'r) rules = {
+ band : 's context -> 'r candidate -> int;
+ (** RG 91: orders candidates for the day; lower wins. *)
+ disposition : winner:'r candidate -> loser:'r candidate -> disposition;
+ (** RG 92-95: the loser's fate, which depends on the loser's own rank. *)
+ admit :
+ observed:'r candidate ->
+ ('r candidate * privilege) list ->
+ ('r candidate * privilege) list;
+ (** RG 108-111: how many commemorations are admitted, and in what order;
+ anything filtered out here is recorded in {!resolution.omitted}, not
+ dropped. *)
+}
+
+(** The outcome of resolving one day's candidates. *)
+type 'r resolution = {
+ observed : 'r candidate;
+ commemorations : ('r candidate * privilege) list;
+ deferred : 'r candidate list;
+ omitted : ('r candidate * string) list; (** each with a reason *)
+}
+
+(** Total: the temporal candidate is passed separately, so there is no
+ empty-candidate case. Ties break on slug, so the result never depends on
+ input order. A [Commemoration_only] celebration is held out of the contest
+ and can never be [observed]. Every input candidate appears exactly once in
+ [observed], [commemorations], [deferred] or [omitted] — nothing is dropped
+ silently. *)
+val resolve :
+ ('s, 'r) rules -> 's context -> temporal:'r candidate ->
+ sanctoral:'r candidate list -> 'r resolution