Multi-Criteria Lexicographic Next-Task Ranking #23

Closed
opened 2026-09-22 17:58:57 +00:00 by yuris · 0 comments
Owner

Summary

Implement the multi-criteria lexicographic ranking engine for next-task candidates, sorting policy-filtered tasks by explicit hard-deadline urgency, unmet weekday career floor status, Jev semantic scores (alignment.v1, consequence.v1, readiness.v1), and stable item IDs. Return at most three candidates with rigorously separated factual reasons, semantic reasons, unknowns, and model provenance.

Background & Technical Rationale

After candidate tasks are validated and filtered by the deterministic policy filter (#022), the eligible candidates must be ordered to produce a concise, actionable recommendation.

Rather than collapsing disparate dimensions into an opaque weighted numerical formula or prompting an LLM to "pick the best task", Life enforces an explicit lexicographical hierarchy:

  1. Strict Lexicographical Precedence Hierarchy:
    • Tier 1: Explicit Hard-Deadline Urgency (Strictly Dominates Downstream Criteria):
      • Sub-tier 1A: Overdue deadlines (deadline < current_time). Sorted by earliest deadline first.
      • Sub-tier 1B: Deadlines falling within the requested time window (current_time <= deadline <= current_time + window). Sorted by earliest deadline first.
      • Sub-tier 1C: Later known deadlines (deadline > current_time + window). Sorted by earliest deadline first.
      • Sub-tier 1D: No deadline (undated tasks).
      • Foundational Invariant: An overdue task strictly ranks ahead of a non-deadline task, regardless of how high the non-deadline task scores on Jev alignment or consequence. Objective commitments always supersede advisory suggestions.
    • Tier 2: Unmet Weekday Career Floor:
      • If today is a weekday and no qualifying career outcome has been recorded (career_floor_met == false), tasks categorized with area-career are prioritized among otherwise tied urgency candidates.
    • Tier 3: Jev Semantic Scores (Descending Order):
      • Evaluated via TypeSafe/Jev question runners:
        1. alignment.v1 score (weighted mean against legend levels, high to low).
        2. consequence.v1 score (weighted mean against legend levels, high to low).
        3. readiness.v1 score (weighted mean against legend levels, high to low).
    • Tier 4: Deterministic Tie-Breaker:
      • Stable item ID string comparison (item.id.cmp(&other.id)), ensuring absolute reproducibility across evaluations.
  2. Top-3 Truncation Invariant:
    • The engine returns at most three candidates. Presenting endless options induces decision fatigue; three well-justified options provide clarity while preserving autonomy.
  3. Rigorous Reason Segregation:
    • Recommendation explanations must never blend factual observations with model opinions.
    • factual_reasons: Objective calendar and policy facts (e.g. "Overdue by 1h 30m", "Estimated duration (45m) fits 90m window", "Matches unmet weekday career floor").
    • semantic_reasons: Jev score evaluations citing specific question versions and numeric ratings (e.g. "alignment.v1 score 8.4/10: Strongly aligns with active Q2 deliverables", "consequence.v1 score 7.2/10").
    • unknowns: Explicit list of missing attributes (e.g. ["estimated_minutes"]).
    • judgment_provenance: Model ID (jev-1.13.0), evaluation timestamps.
  4. Graceful Degradation to Factual Ranking:
    • If Jev is disabled or unavailable, Tier 3 (semantic scores) is bypassed entirely.
    • Candidates are ordered purely by Tiers 1, 2, and 4 (urgency, career floor, and ID).
    • The response includes warning judgment_unavailable. The resulting list must NEVER be presented as a semantic ranking (Scenario I07, W07).

Exact Technical References & Specifications

Normative Next-Task Policy Specification

From docs/technical/jev.md (lines 38–42):

"3. Rank by explicit hard-deadline urgency (overdue, then deadlines inside window, then later known deadlines, then no deadline; earlier deadline first), unmet weekday career floor among otherwise tied urgency candidates, then Jev alignment, consequence and readiness scores descending in that order, then stable item ID.
4. Return at most three candidates with factual reasons, semantic reasons, unknowns and model/question provenance separately. A user choice is not task completion; link later reported outcome to the recommendation if supplied.

This lexicographic heuristic is proposed policy, not a proven optimal scheduler. If the model is unavailable, return explicit judgment_unavailable and, if requested, a separately labeled deterministic eligibility/urgency list; never present it as semantic ranking. Live calendar incompleteness blocks confident capacity claims even if Jev answers confidently."

Workflow Contract

From docs/technical/workflows.md (lines 27, 44):

Workflow Input and reads Decision and approval Writes and observable result Failure path
Next task Available window and energy; live eligible tasks, deadlines and recorded qualifying outcomes Apply deterministic constraints before semantic ranking; policy order owned by Jev page At most three advisory choices, factual versus semantic reasons, no calendar writes Unknown duration/energy shown, not fabricated; Jev unavailable leaves explicit deterministic candidates, not claimed semantic ranking

"One meaningful career action per weekday. Success requires an explicit qualifying outcome linked to the action and day; allocating a calendar block is not success. Qualification must be user-approved or satisfy a reviewed deterministic rubric, never a model's unreviewed inference."

Verification Scenario W07

From docs/technical/verification.md (line 21):

ID Requirement and specification Input / action Observable acceptance
W07 Next action 90m, medium energy, deadlines and unmet career floor At most three eligible choices in documented order; factual/semantic reasons separated; unknown estimate not claimed to fit

What to Build

1. src/domain/policy/next_task_ranker.rs

  • Module implementing the 4-tier lexicographic ordering:
    #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
    pub enum DeadlineUrgencyTier {
        Overdue,        // Deadline < now
        InsideWindow,   // now <= Deadline <= now + window
        LaterKnown,     // Deadline > now + window
        NoDeadline,     // Undated
    }
    
    #[derive(Debug, Clone)]
    pub struct CandidateJevScores {
        pub alignment: Option<f64>,
        pub consequence: Option<f64>,
        pub readiness: Option<f64>,
    }
    
    #[derive(Debug, Clone)]
    pub struct RankedCandidate {
        pub item: Item,
        pub urgency_tier: DeadlineUrgencyTier,
        pub deadline_instant: Option<DateTime<Utc>>,
        pub is_career_priority: bool,
        pub jev_scores: Option<CandidateJevScores>,
        pub factual_reasons: Vec<String>,
        pub semantic_reasons: Vec<String>,
        pub unknowns: Vec<String>,
    }
    
    pub fn rank_candidates(
        mut candidates: Vec<RankedCandidate>,
    ) -> Vec<RankedCandidate> {
        candidates.sort_by(|a, b| {
            // Tier 1: Urgency tier
            a.urgency_tier.cmp(&b.urgency_tier)
                // Within same urgency tier, earlier deadline wins
                .then_with(|| a.deadline_instant.cmp(&b.deadline_instant))
                // Tier 2: Career floor priority (true ranks ahead of false)
                .then_with(|| b.is_career_priority.cmp(&a.is_career_priority))
                // Tier 3: Jev semantic scores (descending)
                .then_with(|| compare_jev_scores(&a.jev_scores, &b.jev_scores))
                // Tier 4: Stable item ID tie-breaker
                .then_with(|| a.item.id.cmp(&b.item.id))
        });
    
        // Truncate to at most 3 candidates
        candidates.truncate(3);
        candidates
    }
    
  • Sub-function compare_jev_scores:
    • Compares alignment (descending: higher score first).
    • Then compares consequence (descending).
    • Then compares readiness (descending).
    • If Jev scores are None (degraded mode), returns Ordering::Equal.

2. src/jev/scorer.rs — Batch Candidate Scorer

  • Evaluates eligible candidates against Jev in parallel:
    • Evaluates all eligible candidate tasks that can enter the final top three (avoiding undocumented silent pre-ranking truncation, per docs/technical/cli-api.md:73). If candidate limits are exceeded, disclose narrowing warnings.
    • Builds questions alignment.v1, consequence.v1, and readiness.v1 for each candidate task.
    • State contains task title, categories, and active project context.
    • Executes batch query to JevService.
    • Parses scores and formats semantic_reasons.
    • If JevService returns judgment_unavailable, attaches None for jev_scores and records warning.

3. Unit & Comparator Tests (tests/ranker_tests.rs)

  • Test 1: Overdue task strictly ranks ahead of a non-deadline task with perfect 10/10 Jev score.
  • Test 2: Two tasks with identical deadline urgency; the task matching an unmet weekday career floor ranks ahead.
  • Test 3: Two tasks with identical factual urgency and career status; the task with higher Jev alignment score ranks ahead.
  • Test 4: Two tasks tied on alignment; higher consequence score breaks the tie.
  • Test 5: Two tasks tied on all criteria; broken deterministically by item ID.
  • Test 6: A list of 10 eligible candidates is truncated to exactly 3 items.
  • Test 7: Degradation test: When Jev scores are absent, candidates are sorted purely by factual criteria and ID without panic.

Acceptance Criteria

  • An overdue task strictly ranks ahead of a non-deadline task, regardless of Jev semantic scores.
  • On weekdays with an unmet career floor, an area-career task breaks ties ahead of non-career tasks with identical deadline urgency.
  • Ties in factual criteria are broken by Jev alignment, consequence, and readiness scores descending (Scenario W07).
  • Ties across all factual and semantic criteria are broken deterministically by stable item ID comparison.
  • The engine truncates output to at most three candidates.
  • Candidates that can affect the top three are evaluated without undocumented silent pre-ranking truncation; explicit candidate limits disclose narrowing warnings.
  • Each candidate returned contains separate factual_reasons and semantic_reasons arrays.
  • When Jev is unavailable, candidates are ranked deterministically by factual criteria and item ID, accompanied by warning judgment_unavailable.

Notes

  • Scoring bound is MAX_SCORED_CANDIDATES = 20 applied to the factual order, disclosed as candidate_narrowed; all candidates are still returned, unjudged ones marked as such.
  • A degraded judgment discards the whole semantic pass (RankingBasis::Deterministic) rather than blending a partial ranking.
  • No dependency added, so the deferred Cargo.toml authors chore lands with #35.

Blocked by

  • #022: Next-Task Policy Filter & Candidate Window Slicing
## Summary Implement the multi-criteria lexicographic ranking engine for next-task candidates, sorting policy-filtered tasks by explicit hard-deadline urgency, unmet weekday career floor status, Jev semantic scores (`alignment.v1`, `consequence.v1`, `readiness.v1`), and stable item IDs. Return at most three candidates with rigorously separated factual reasons, semantic reasons, unknowns, and model provenance. ## Background & Technical Rationale After candidate tasks are validated and filtered by the deterministic policy filter (#022), the eligible candidates must be ordered to produce a concise, actionable recommendation. Rather than collapsing disparate dimensions into an opaque weighted numerical formula or prompting an LLM to "pick the best task", Life enforces an **explicit lexicographical hierarchy**: 1. **Strict Lexicographical Precedence Hierarchy:** - **Tier 1: Explicit Hard-Deadline Urgency (Strictly Dominates Downstream Criteria):** - Sub-tier 1A: Overdue deadlines (`deadline < current_time`). Sorted by earliest deadline first. - Sub-tier 1B: Deadlines falling within the requested time window (`current_time <= deadline <= current_time + window`). Sorted by earliest deadline first. - Sub-tier 1C: Later known deadlines (`deadline > current_time + window`). Sorted by earliest deadline first. - Sub-tier 1D: No deadline (undated tasks). - *Foundational Invariant:* An overdue task strictly ranks ahead of a non-deadline task, regardless of how high the non-deadline task scores on Jev alignment or consequence. Objective commitments always supersede advisory suggestions. - **Tier 2: Unmet Weekday Career Floor:** - If today is a weekday and no qualifying career outcome has been recorded (`career_floor_met == false`), tasks categorized with `area-career` are prioritized among otherwise tied urgency candidates. - **Tier 3: Jev Semantic Scores (Descending Order):** - Evaluated via TypeSafe/Jev question runners: 1. `alignment.v1` score (weighted mean against legend levels, high to low). 2. `consequence.v1` score (weighted mean against legend levels, high to low). 3. `readiness.v1` score (weighted mean against legend levels, high to low). - **Tier 4: Deterministic Tie-Breaker:** - Stable item ID string comparison (`item.id.cmp(&other.id)`), ensuring absolute reproducibility across evaluations. 2. **Top-3 Truncation Invariant:** - The engine returns at most **three** candidates. Presenting endless options induces decision fatigue; three well-justified options provide clarity while preserving autonomy. 3. **Rigorous Reason Segregation:** - Recommendation explanations must never blend factual observations with model opinions. - `factual_reasons`: Objective calendar and policy facts (e.g. `"Overdue by 1h 30m"`, `"Estimated duration (45m) fits 90m window"`, `"Matches unmet weekday career floor"`). - `semantic_reasons`: Jev score evaluations citing specific question versions and numeric ratings (e.g. `"alignment.v1 score 8.4/10: Strongly aligns with active Q2 deliverables"`, `"consequence.v1 score 7.2/10"`). - `unknowns`: Explicit list of missing attributes (e.g. `["estimated_minutes"]`). - `judgment_provenance`: Model ID (`jev-1.13.0`), evaluation timestamps. 4. **Graceful Degradation to Factual Ranking:** - If Jev is disabled or unavailable, Tier 3 (semantic scores) is bypassed entirely. - Candidates are ordered purely by Tiers 1, 2, and 4 (urgency, career floor, and ID). - The response includes warning `judgment_unavailable`. The resulting list must NEVER be presented as a semantic ranking (Scenario I07, W07). ## Exact Technical References & Specifications ### Normative Next-Task Policy Specification From `docs/technical/jev.md` (lines 38–42): > "3. Rank by explicit hard-deadline urgency (overdue, then deadlines inside window, then later known deadlines, then no deadline; earlier deadline first), unmet weekday career floor among otherwise tied urgency candidates, then Jev alignment, consequence and readiness scores descending in that order, then stable item ID. > 4. Return at most three candidates with factual reasons, semantic reasons, unknowns and model/question provenance separately. A user choice is not task completion; link later reported outcome to the recommendation if supplied. > > This lexicographic heuristic is proposed policy, not a proven optimal scheduler. If the model is unavailable, return explicit judgment_unavailable and, if requested, a separately labeled deterministic eligibility/urgency list; never present it as semantic ranking. Live calendar incompleteness blocks confident capacity claims even if Jev answers confidently." ### Workflow Contract From `docs/technical/workflows.md` (lines 27, 44): | Workflow | Input and reads | Decision and approval | Writes and observable result | Failure path | |---|---|---|---|---| | Next task | Available window and energy; live eligible tasks, deadlines and recorded qualifying outcomes | Apply deterministic constraints before semantic ranking; policy order owned by Jev page | At most three advisory choices, factual versus semantic reasons, no calendar writes | Unknown duration/energy shown, not fabricated; Jev unavailable leaves explicit deterministic candidates, not claimed semantic ranking | > "One meaningful career action per weekday. Success requires an explicit qualifying outcome linked to the action and day; allocating a calendar block is not success. Qualification must be user-approved or satisfy a reviewed deterministic rubric, never a model's unreviewed inference." ### Verification Scenario W07 From `docs/technical/verification.md` (line 21): | ID | Requirement and specification | Input / action | Observable acceptance | |---|---|---|---| | W07 | [Next action](jev.md#next-task-policy) | 90m, medium energy, deadlines and unmet career floor | At most three eligible choices in documented order; factual/semantic reasons separated; unknown estimate not claimed to fit | ## What to Build ### 1. `src/domain/policy/next_task_ranker.rs` - Module implementing the 4-tier lexicographic ordering: ```rust #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum DeadlineUrgencyTier { Overdue, // Deadline < now InsideWindow, // now <= Deadline <= now + window LaterKnown, // Deadline > now + window NoDeadline, // Undated } #[derive(Debug, Clone)] pub struct CandidateJevScores { pub alignment: Option<f64>, pub consequence: Option<f64>, pub readiness: Option<f64>, } #[derive(Debug, Clone)] pub struct RankedCandidate { pub item: Item, pub urgency_tier: DeadlineUrgencyTier, pub deadline_instant: Option<DateTime<Utc>>, pub is_career_priority: bool, pub jev_scores: Option<CandidateJevScores>, pub factual_reasons: Vec<String>, pub semantic_reasons: Vec<String>, pub unknowns: Vec<String>, } pub fn rank_candidates( mut candidates: Vec<RankedCandidate>, ) -> Vec<RankedCandidate> { candidates.sort_by(|a, b| { // Tier 1: Urgency tier a.urgency_tier.cmp(&b.urgency_tier) // Within same urgency tier, earlier deadline wins .then_with(|| a.deadline_instant.cmp(&b.deadline_instant)) // Tier 2: Career floor priority (true ranks ahead of false) .then_with(|| b.is_career_priority.cmp(&a.is_career_priority)) // Tier 3: Jev semantic scores (descending) .then_with(|| compare_jev_scores(&a.jev_scores, &b.jev_scores)) // Tier 4: Stable item ID tie-breaker .then_with(|| a.item.id.cmp(&b.item.id)) }); // Truncate to at most 3 candidates candidates.truncate(3); candidates } ``` - Sub-function `compare_jev_scores`: - Compares `alignment` (descending: higher score first). - Then compares `consequence` (descending). - Then compares `readiness` (descending). - If Jev scores are `None` (degraded mode), returns `Ordering::Equal`. ### 2. `src/jev/scorer.rs` — Batch Candidate Scorer - Evaluates eligible candidates against Jev in parallel: - Evaluates all eligible candidate tasks that can enter the final top three (avoiding undocumented silent pre-ranking truncation, per `docs/technical/cli-api.md:73`). If candidate limits are exceeded, disclose narrowing warnings. - Builds questions `alignment.v1`, `consequence.v1`, and `readiness.v1` for each candidate task. - State contains task title, categories, and active project context. - Executes batch query to `JevService`. - Parses scores and formats `semantic_reasons`. - If `JevService` returns `judgment_unavailable`, attaches `None` for `jev_scores` and records warning. ### 3. Unit & Comparator Tests (`tests/ranker_tests.rs`) - Test 1: Overdue task strictly ranks ahead of a non-deadline task with perfect 10/10 Jev score. - Test 2: Two tasks with identical deadline urgency; the task matching an unmet weekday career floor ranks ahead. - Test 3: Two tasks with identical factual urgency and career status; the task with higher Jev alignment score ranks ahead. - Test 4: Two tasks tied on alignment; higher consequence score breaks the tie. - Test 5: Two tasks tied on all criteria; broken deterministically by item ID. - Test 6: A list of 10 eligible candidates is truncated to exactly 3 items. - Test 7: Degradation test: When Jev scores are absent, candidates are sorted purely by factual criteria and ID without panic. ## Acceptance Criteria - [x] An overdue task strictly ranks ahead of a non-deadline task, regardless of Jev semantic scores. - [x] On weekdays with an unmet career floor, an `area-career` task breaks ties ahead of non-career tasks with identical deadline urgency. - [x] Ties in factual criteria are broken by Jev alignment, consequence, and readiness scores descending (Scenario W07). - [x] Ties across all factual and semantic criteria are broken deterministically by stable item ID comparison. - [x] The engine truncates output to at most three candidates. - [x] Candidates that can affect the top three are evaluated without undocumented silent pre-ranking truncation; explicit candidate limits disclose narrowing warnings. - [x] Each candidate returned contains separate `factual_reasons` and `semantic_reasons` arrays. - [x] When Jev is unavailable, candidates are ranked deterministically by factual criteria and item ID, accompanied by warning `judgment_unavailable`. ## Notes - Scoring bound is `MAX_SCORED_CANDIDATES = 20` applied to the factual order, disclosed as `candidate_narrowed`; all candidates are still returned, unjudged ones marked as such. - A degraded judgment discards the whole semantic pass (`RankingBasis::Deterministic`) rather than blending a partial ranking. - No dependency added, so the deferred `Cargo.toml` authors chore lands with #35. ## Blocked by - #022: Next-Task Policy Filter & Candidate Window Slicing
yuris changed title from Issue #023: Multi-Criteria Lexicographic Next-Task Ranking to Multi-Criteria Lexicographic Next-Task Ranking 2026-09-22 20:02:36 +00:00
yuris closed this issue 2026-09-23 00:33:30 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/life-service#23
No description provided.