Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/elenacarino-max/Pildora4_ext_StarWars/llms.txt

Use this file to discover all available pages before exploring further.

Scoring in MLflow Jedi is tracked per team in the score column of the teams table. Points are added and deducted by database functions in database.py in real time — every successful chamber completion, every hint requested, every mission run recorded, and the final defense submission all modify the score atomically within a single SQLite transaction. The scoreboard can be toggled visible or hidden by the teacher at any moment via the scoreboard_visible flag on the session.

Score events

Every event that changes a team’s score maps to a specific database function. The table below documents each event exactly as implemented in database.py:
EventPointsFunctionCondition
Chamber solved correctly+20complete_chamber()Once per chamber; idempotent — only fires when team.chamber_index == chamber_index
Hint used−5use_hint()Each hint level; up to 3 hints per challenge
Forced advance (5 failures)−10force_advance_chamber()Once per chamber; applied instead of +20
Mission completed+30save_mission_run()First time only per mission; UNIQUE(team_id, mission_id) prevents double-award
All 3 missions bonus+15save_mission_run()Awarded once when missions_completed >= 3 and phase == 'missions'
Defense submitted+25save_defense()First submission only; re-sealing the same defense does not re-award
All score arithmetic is performed with SQL score = score + N expressions inside the same transaction that updates the team’s state, so a crash mid-function cannot leave the database in a partially scored state.

Maximum score calculation

A team that solves every challenge on the first attempt, uses no hints, and submits a complete defense achieves the following breakdown:
ComponentCalculationPoints
6 Chambers × 20 ptscomplete_chamber() called 6 times120
3 Missions × 30 ptssave_mission_run() called 3 times (first time each)90
All-missions bonusAwarded once when the third mission is saved15
Defensesave_defense() called once25
Maximum total250
Any hint used or forced advance subtracts from this ceiling. There is no floor — a team that uses many hints across all six chambers and all three missions will have a lower but still valid score.

Phase progression

Each team moves through a linear state machine tracked in the phase column. The transitions are enforced by the database functions — no UI action can skip a phase.
1

chambers (start)

Initial state. chamber_index = 0, phase = 'chambers'. The team works through Chambers 1–6 sequentially. complete_chamber() increments chamber_index after each success. force_advance_chamber() does the same with a −10 penalty.
2

missions

Entered automatically when chamber_index reaches 6. phase is set to 'missions' inside the same UPDATE that writes chamber_index = 6. The team can now attempt the three MLflow missions (Tatooine → Coruscant → Mustafar) in any order.
3

comparator

Entered automatically inside save_mission_run() once COUNT(mission_runs WHERE team_id) >= 3. The team can now compare their three runs in the MLflow UI comparator view and choose which to defend.
db.execute(
    "UPDATE teams SET phase='comparator', score=score+15 WHERE id=? AND phase='missions'",
    (team_id,),
)
The AND phase='missions' guard ensures the +15 bonus fires exactly once.
4

defense

Entered automatically by assign_holocron(), which is called at the end of save_mission_run(). Once a holocron is assigned, phase is set to 'defense' and the team receives their specific debate scenario.
5

completed

Entered when save_defense() processes a valid first submission. phase is set to 'completed' and +25 points are awarded. The team’s journey is finished.

Hint system

Hints are progressive disclosures designed to guide without simply giving away the answer. The three levels for each chamber move from conceptual context to category to partial syntax:
LevelExample (Chamber 1 — experiment)Cost
1”Una campaña agrupa runs relacionadas.”−5 pts
2”La función pertenece a la categoría Experiment.”−5 pts
3mlflow.set_e_________("Biblioteca_Jedi_Digits")−5 pts
Key implementation details from use_hint() and the hint_usage table:
  • The current hint level is computed by counting existing hint_usage rows for (team_id, challenge_id) — no separate level column is needed in teams.
  • Once level >= 3, use_hint() returns early without inserting or deducting points — hints are capped at 3 per challenge.
  • The insert uses INSERT OR IGNORE against the UNIQUE(team_id, challenge_id, level) constraint. A race condition or double-click cannot deduct points twice for the same hint level.
  • The hints_used counter on the teams row is incremented alongside the score deduction in the same UPDATE for display on the scoreboard.

Forced advance

When a team is stuck and cannot solve a chamber, the teacher (or the automatic 5-failure trigger) can advance them past it:
Forced advance applies a −10 point penalty and reveals the correct solution. It replaces the +20 points a successful first-attempt solve would have earned — an effective swing of −30 points relative to the optimal path.
The relevant function is force_advance_chamber():
def force_advance_chamber(team_id: str, chamber_index: int) -> dict:
    """Evita bloqueos tras cinco fallos y aplica la penalización acordada."""
    with connect() as db:
        team = db.execute("SELECT chamber_index FROM teams WHERE id=?", (team_id,)).fetchone()
        if not team:
            raise ValueError("Equipo no encontrado")
        if team[0] == chamber_index:
            next_index = chamber_index + 1
            phase = "missions" if next_index >= 6 else "chambers"
            db.execute(
                "UPDATE teams SET chamber_index=?, phase=?, score=score-10 WHERE id=?",
                (next_index, phase, team_id),
            )
    return get_team(team_id) or {}
  • The if team[0] == chamber_index guard makes the operation idempotent — calling it twice for the same chamber does not double-penalise.
  • The teacher can trigger this manually via the dashboard (unlock_next_chamber() wraps complete_chamber()) or it fires automatically once attempt_count(team_id, challenge_id, valid=False) >= MAX_ATTEMPTS (where MAX_ATTEMPTS = 5, defined in views/student.py).

Holocron assignment

After all three missions are complete, each team is assigned a Holocron — a structured debate scenario that defines the analytical lens for their final defense presentation. The assignment logic in assign_holocron() distributes all seven scenario types fairly across teams in a session:
position = db.execute(
    "SELECT COUNT(*) FROM teams WHERE session_id=? AND joined_at<=?",
    (team[\"session_id\"], team[\"joined_at\"]),
).fetchone()[0]
chosen = holocron_id or HOLOCRONS[(position - 1) % len(HOLOCRONS)][\"id\"]
  • position is the team’s 1-based join order within the session.
  • (position - 1) % 7 cycles through the 7 available holocron types so no two consecutive teams in the same session receive the same scenario.
  • The teacher can override the assignment using reassign_holocron(team_id, holocron_id), which validates that the chosen holocron_id exists in HOLOCRONS before writing.
  • assign_holocron() is a no-op if the team already has a holocron_id — repeated calls do not reset the assignment.
The seven available holocrons are: consejo-desconfiado, recursos-limitados, auditoria-imperial, senado-galactico, lado-oscuro-metrica, rescate-prioritario, and mision-produccion.
The optimal score strategy is to solve all six chambers on the first attempt with no hints, complete all three missions, and submit a complete defense:6 × 20 + 3 × 30 + 15 + 25 = 120 + 90 + 15 + 25 = 250 pointsEvery hint used costs −5 pts and every forced advance costs −10 pts relative to a perfect run. Encourage teams to reason carefully before submitting rather than guess-and-retry.

Build docs developers (and LLMs) love