Phase 2 — Runtime Engine Scope

Phase 1 (built) renders the 19-model schema as an AI-Safe CRUD register: requests, matters, issues, tasks, risks, decisions, approvals, engagements, spend, documents, SLAs and knowledge — browsable, with a dashboard. But the states are static: the seed shows a request already converted_to_matter, an SLA already on_track, spend already invoiced. Nothing is computed, timed or transitioned by the system.

Phase 2 is the runtime that makes it live — running the SLA clocks, enforcing the state machines, controlling spend against budget, and applying the intake / privilege / approval rules the pack specifies. Same shape as the (built, cluster-proven) orchestrator engine, but single-app.

Everything below is drawn from the pack's workflows.yaml, rules.yaml, the DSL derived {} blocks and the LegalOperationsDashboard. It is a scope, not a design.


A. Derived-field & rollup engine

Seeded statically today; Phase 2 computes them:

Field Rule
LegalRequest.days_to_required / overdue vs required_by, excluding closed/cancelled/converted
LegalMatter.days_open opened_dateclosed_date (or today)
LegalMatter.net_exposure estimated_exposure − estimated_recovery
MatterTask.overdue past due_date and not completed/cancelled

Plus spend rollups: total SpendEntry.total_amount per matter (by status) vs LegalMatter.budget — the input to over-budget detection and the dashboard.

B. State machines & guarded actions

The pack defines five workflows with transitions, guards and side-effects. Today a user free-edits status; Phase 2 exposes each as a guarded action.

Workflow Notable guards & effects
legal_request (draft→submitted→triage→accepted→converted_to_matter) submit requires the core fields and creates the SLA; accept assigns the legal owner; convert_to_matter creates the matter and links it
legal_matter (draft→open→active→…→resolved→closed) resolve requires outcome + outcome_summary; close closes open tasks and prompts knowledge capture
counsel_engagement (proposed→conflict_check→approved→engaged→active→completed) approve requires conflict check complete + approver + budget; engage requires scope + fee arrangement
legal_decision (draft→under_review→approved→implemented) approve stamps the decision date + emits; implement stamps implemented
approval (pending→approved/rejected/withdrawn) stamps responded_at, emits the result

Decisions: UI buttons vs API; role → PIN/RBAC; auto-actions (create SLA/matter) vs manual.

C. The SLA engine

The headline runtime — turns ServiceLevel targets into live, pausable clocks (SLAInstance), the thing that makes a legal-ops function measurable:

  • On request submit, create an SLAInstance from the matching ServiceLevel and compute first_response_due_at / triage_due_at / resolution_due_at.
  • A tick moves the instance through on_track → due_soon → overdue, marks first_response_at / triage_completed_at / resolved_at, and sets met / breached.
  • Pause when awaiting the business (ServiceLevel.pause_when_awaiting_business) — accumulate paused_minutes while the matter/request is awaiting_business.

Decisions: the business-calendar / working-hours model (targets are in hours/days — the pack ships business_calendars.yaml); tick cadence; which events stop the clock.

D. Rules / cross-record automation

From rules.yaml — intake, spend and governance guardrails:

  • Intake — a critical request → notify GC (legal.request.critical); an urgent/critical submitted request → auto-assign a triage owner; a request past required_by (hourly) → emit overdue + notify the assigned lawyer.
  • Spend control — matter spend over budget → emit budget_exceeded + notify owners; an invoiced entry ≥ $10,000 requires approval.
  • Counsel — an external proposed engagement requires approval; a non-panel firm requires an exception approval.
  • Privilege gate — a MatterDocument moving to issued while privilege is unresolved is blocked ("resolve privilege before external issue").
  • Governance — a matter cannot close while a critical residual risk is open; an approved decision past its implementation date (daily) notifies the owner; a KnowledgeItem within 30 days of review_date (daily) notifies the owner.
  • Critical matter — a critical matter notifies the GC.

Decisions: event bus (in-app vs orchestrator); block vs flag; notify channel; how "auto-assign triage" picks an owner.

The DSL LegalOperationsDashboard, wired via the repo KPI framework (kpi_engine.py + kpis.py):

  • Metrics: open requests; open matters; critical matters; overdue tasks; pending approvals; outside-counsel spend YTD; matters over budget; overdue requests.
  • Sections: intake queue; high-risk matters; workload by lawyer; spend by firm; spend by matter type; SLA performance; pending decisions; upcoming deadlines.

How it would be built

  1. A per-app engine module (mirror server/lib/orchestrator_engine/): compute pass + spend rollups (A), the SLA engine/tick (C), guarded actions (B), rules evaluator (D). Opt-in env flag, single worker.
  2. KPIs (E) via the existing framework — quick, independent win (and the spend/SLA metrics are the ones a GC actually wants).
  3. Business-calendar / working-hours underpins the SLA clocks and deadlines.
  4. Tests per workstream (test_orchestrator_flows.py is the template).

Suggested sequencing

  1. KPIs / dashboard (E) — visible, no engine; the spend + SLA numbers are the headline.
  2. Derived fields + spend rollups (A) — makes over-budget / overdue real.
  3. SLA engine (C) — the measurable-service core; needs the working-hours calendar.
  4. Spend & governance rules (D) — budget, approval and privilege guardrails.
  5. State machines + guards (B) — governed intake → matter → resolution.

Each slice is independently shippable and demoable. The orchestrator (built and run live this session) is the working template for the engine, scheduler and tests.