NAMMU

← Blog
ProductEngineeringLegal AI

Trayectorias: how Nammu renders the right procedural roadmap for each case

Once a case is classified and its documents are segmented, Trayectorias overlays the correct statutory roadmap for that specific jurisdiction and process type with actual evidence from the case. Here is how the templates, status inference, two-view split, and seven empty states all fit together.

IP

Inventum P5

Engineering · Galictis-Legal

July 10, 2026

10 min read

Every legal process type has its own statutory sequence of stages, its own article citations, its own deadlines, its own “what happens if X does not occur.” A civil ordinario's audiencia preliminar has nothing in common procedurally with a violencia doméstica case's plazo de vigencia window. Trayectorias exists to render the correct statutory roadmap for this specific case's jurisdiction and process type — and then overlay it with actual evidence from this case's own documents to show where it really stands today. This is not a generic five-stage progress bar.

Two views over one model

The same CaseTimeline data structure feeds two deliberately distinct renderings. Keeping them separate prevents two genuinely different questions from being conflated.

Trayectorias tab

What has actually happened

  • Filtered through _reached() — only COMPLETED and IN_PROGRESS milestones
  • Evidence-gated: a stage not yet proven in the documents simply does not appear
  • Dates attached when extraction succeeded
  • Never shows what hasn't happened — that would misrepresent the case
Proceso mini-view

What the process generally looks like

  • Full statutory template — all milestones including PENDING and UNKNOWN
  • Available the moment process_type is classified, regardless of documents
  • Dates and plazos intentionally omitted — they only mean something once reached
  • Answers: what does this type of case look like, not where is this one

The data model

Three dataclasses in timeline_model.py carry everything a builder produces:

Milestone

One procedural stage. Carries a bilingual label, a symbol (see below), a phase, a status (completed / in_progress / pending / unknown), the governing article and law, an optional statutory deadline in business days, and free-text notes — which often carry safety-critical legal guidance, not just descriptions.

JurisdictionalHinge

Populated only for a bisagra (jurisdictional transfer) case. Carries origin and receiving jurisdiction, court names, and the transfer document's date and title.

CaseTimeline

The case-level wrapper: process_type, has_bisagra flag, phase labels, the hinge if any, and the full milestone list. Every builder returns exactly one of these — the UI layer never touches jurisdiction-specific logic directly.

Milestone symbols

Each milestone carries a symbol that communicates its structural role in the procedural sequence at a glance:

Standard milestone

A regular procedural stage

Pivot

A stage that changes the case's direction or track

Terminal

An end state — the case can stop here

Conditional

Only applies under specific circumstances

One builder per process type — not per jurisdiction

load_case_timeline() reads process_type from case_index.json and routes to exactly one build_*_timeline() function. This is process-type-level dispatch, not jurisdiction-level: a jurisdiction being fully classified does not guarantee every one of its process types has a template. Civil's monitorio, penal's procedimiento_abreviado and flagrancia, and contencioso's proceso_de_lesividad are all classified correctly but have no builder yet — they show a purpose-built “template missing” state, not a generic error.

Each builder is self-contained. It owns its own milestone list, article citations, and status-inference rules — grounded in a specific legal-domain review, cited in-code. There is no shared generic milestone structure that jurisdictions customize. A civil ordinario's 13 milestones and a familia violencia-doméstica case's 8 milestones do not share a single stage name or structure, because they are governed by different codes with different procedural clocks.

Phases — communicating jurisdictional change mid-process

MilestonePhase has four values. Which ones a builder uses says something legally meaningful about whether the case's jurisdiction can change mid-process.

PhaseMeaningUsed by
SINGLEProcess never changes jurisdictionPenal, laboral, familia (all 6), notarial, tránsito
PHASE1Pre-transfer stages (muted styling) — contencioso-administrativo originCivil ordinario builder only, bisagra path
BISAGRAThe jurisdictional transfer act itself (gold-highlighted, distinct node)Civil ordinario builder only, bisagra path
PHASE2Post-transfer stages (active styling) — civil ordinario track that followsCivil ordinario builder only, bisagra path

build_ordinario_timeline() is the one builder that must handle all three possible shapes of a civil ordinario case. It picks between them explicitly: bisagra detected → emit both phases plus the gold-highlighted hinge node; non-civil jurisdiction with no bisagra yet → still show both phase templates so the mini-view roadmap is visible before a transfer; purely civil from the start → drop phase labels and stamp every milestone SINGLE.

How milestone status is inferred

Every builder inspects the pooled doc_kind set from document_index.json — the output of Smart Document Segmentation — and derives each milestone's status from which document types are present. The patterns range from simple to legally nuanced:

Simple presence check

Civil ordinario's notificación al demandado:

COMPLETED if "notificacion" in doc_kinds else UNKNOWN

Three-state waterfall with a meaningful middle state

Penal querella's admisibilidad:

COMPLETED    if a court admission resolution exists
IN_PROGRESS  if defects flagged and/or subsanación filed,
             but no court ruling yet
PENDING      if no signal at all

The IN_PROGRESS state is deliberate and legally meaningful: it distinguishes “the defect/cure cycle happened and we are waiting on the court” from “nothing has happened yet” — a real procedural distinction, not a progress-bar artifact.

Logically inferred from surrounding evidence

Civil sumario's admisibilidad and audiencia única:

Sumario has no dedicated admisibilidad filing type in the current taxonomy. Rather than leaving these stages permanently UNKNOWN, the builder infers them from what must legally be true given later evidence: “admisibilidad” is COMPLETED once a contestación or sentencia exists (a case cannot legally reach either without having cleared it), IN_PROGRESS once a demanda exists with nothing further, PENDING otherwise. This is explicitly commented in-code as a deliberate, legally-sound inference — not a guess.

Dual-terminal, computed window

Familia violencia doméstica:

This template has two TERMINAL milestones — sentencia_recursos and archivo_proceso — because a VD case can legitimately end either way: a final ruling with measures still in force, or an administrative archival after the protective measures simply expire unrenewed. The plazo_vigencia milestone (whether protective measures are still legally in force) has no doc_kind of its own — its status is derived entirely from whether a granting resolution exists and whether any terminal event has since fired.

Milestone copy carries legal-safety guidance

The notes field on a milestone is not decorative. In several builders, it carries direct, load-bearing legal-safety instructions embedded at exactly the point in the UI where they are operationally relevant.

From the familia violencia-doméstica builder:

  • Resolución de otorgamiento

    "NUNCA exigir estándar probatorio elevado en esta etapa — acceso rápido por diseño." Never demand a high evidentiary standard here. Rapid access is the legal design intent of the law.

  • Señalamiento de audiencia

    "PROHIBICIÓN DE CONCILIACIÓN (art. 9 CPF): relación desigual de poder se presume en violencia doméstica; NUNCA sugerir, preparar ni facilitar conciliación en esta audiencia." A hard legal prohibition, not a suggestion.

  • Control del plazo de vigencia

    Flagged as "ALERTA DE SEVERIDAD ALTA, NO COSMÉTICA" — a missed protective-measures expiration is a physical-safety risk, not an ordinary procedural deadline.

  • Levantamiento anticipado

    "NUNCA inducir, sugerir conveniencia, ni evaluar el mérito." The decision to request early lifting of protective measures is explicitly marked as the protected person's alone to make.

Honest confidence caveats travel to the individual citation. The appeal deadline on VD's sentencia milestone is marked “de confianza baja (SINALEVI-PENDING, solo fuente de prensa) — no usarlo en una alerta de producción sin verificación adicional.” The caveat does not stop at a blanket schema-level disclaimer — it lives at the specific milestone citing the unverified figure, the same “DRAFT pending attorney sign-off” discipline documented for the schemas themselves.

Date enrichment — best effort, never a status determinant

timeline_dates.py runs a separate later pass that tries to attach a real calendar date to milestones already marked reached by the status logic. It never determines whether a milestone happened — only when.

For each milestone ID, a lookup table says which doc_kind(s) to search; the first matching segment's anchor page is scanned with Spanish-date regexes covering both numeral dates (28/01/2026) and Costa Rica's canonical spelled-out judicial self-dating formula (“del veintiocho de enero de dos mil veintiséis”), including a word-to-number table for spelled-out days and years.

A date extraction failure just means a milestone shows as reached with no date — never a reason to downgrade or hide a status the doc_kind evidence already established.

Seven distinct empty states

Trayectorias deliberately does not collapse every “nothing visible” situation into one generic empty state. The reason a case has nothing to show is itself useful information, and conflating the reasons would mislead the lawyer about what is actually going on.

StateMeaningWhy it is not just “empty”
EmptyNothing processed yet at allThe true default baseline state.
ProcessingTransient — the ~7s gap between the fast-path read and the background rewrite finishingPurely momentary. A permanent-looking empty state here would look like a stall.
Quality gateManifest reports completed, but too few pages yielded usable text (below 80% extraction floor)Without this, a low-OCR case would misleadingly look "still processing" forever.
Out of scopeJurisdiction detected, but no loaded legal schema (agrario, constitucional)Tells the lawyer why — a real known jurisdiction with no template yet, not a processing failure.
Unbuilt (pending)Correctly classified as sumario — a real NCPC category — but no timeline builder exists yetDistinct from OUT_OF_SCOPE: classification succeeded, only the template is missing.
UnrecognizedClassified as abreviado — a label the current civil schema mapping does not recognizeRecommends manual attorney verification explicitly, rather than pretending confidence.
Manual classificationTier 3 of the waterfall — both carátula probe and doc_kind roll-up were ambiguousInteractive: presents jurisdiction / process_type dropdowns and persists the lawyer's pick.
No milestones reachedProcess type known, full template exists (visible in mini-view) — case is genuinely early-stageWithout this, a correctly-classified early-stage case rendered the same message as a genuine failure.
Why the last row was added specifically: a real 11-stage contencioso-administrativo case that classified perfectly but legitimately had no completed milestones yet was indistinguishable, under the old generic empty state, from a case where something had actually gone wrong. The fix was not a UI tweak — it was recognizing that “correctly classified but early” and “failed to classify” are different facts that deserve different messages.

Guiding principles

1

One statutory template per process type, hand-grounded in its own governing code

Never a shared generic progress structure reused across branches.

2

Two distinct views, deliberately not merged

What a process generally looks like (mini-view, always available once classified) vs. what this case has actually proven so far (Trayectorias tab, evidence-gated).

3

Every status is evidence-derived or logically inferred from other evidence — never guessed

Where no dedicated evidence exists yet (civil sumario), the fallback is an honest, legally-sound inference from surrounding milestones, explicitly reasoned about in-code, not a silent default.

4

A missing template is reported as a missing template

Distinct from an unclassifiable case, distinct from a genuinely early-stage case, distinct from a transient processing gap. Collapsing any of these into a generic empty would misinform the lawyer about what is actually true.

5

Legal-safety guidance lives at the exact milestone it applies to

Including honest confidence caveats on any citation that is not yet fully verified — the same DRAFT discipline carried from the schema level all the way down into individual UI copy.

6

Dates are a best-effort enrichment, never a status determinant

A date-extraction miss never downgrades a status the underlying doc_kind evidence already earned.

Want to see Trayectorias running on real expedientes?

We can walk through the timeline for your jurisdiction and process types — including which milestones exist, which statuses are live, and what the roadmap looks like for a case you know.

Request a demo →