Topolog

How Topolog works

Three system layers, three things you do. You author the graph; the scheduler lays it across your week; you execute the day. Each does exactly one job. The list and the completion spectrum are just the graph, read back to you.

Author

The dependency graph

Every goal has a plan. Every plan in Topolog is a rigorous Directed Acyclic Graph (DAG): a structure that knows what depends on what. Milestones contain tasks. Tasks have estimated hours. Edges express ordering: what must finish before what.

Topolog's AI proposes structure; you sign off. Click empty canvas to add a node, drag from one node onto another to draw an edge, click a node or edge to edit it in the inspector, press Delete to remove the selection, or drop into the Source tab to author the plan directly in our domain-specific language, Total Orchestration Language (TOL). Cycles at the task level are rejected automatically, so the structure stays a valid Directed Acyclic Graph (DAG) by construction.

IDE Graph tab: a kitchen-remodel goal as a left-to-right dependency graph
Same data, your choice of view

Prefer a list? It's the same graph.

A plan is a DAG, and every DAG has a topological order, so Topolog can flatten the exact same graph into the familiar nested list you already think in. Milestones expand into their tasks and iterations, each with its hour estimate, always in an order that respects every dependency.

Graph, list, and TOL source are three lenses on one data model: sketch on the canvas, scan the list, fine-tune the source. Whatever you change shows up in all three.

Plan explorer: the graph as a collapsible outline
Under the hood

Every plan is a program

The graph isn't a drawing; it's source code. Every plan is written in TOL, our Total Orchestration Language: a typed, executable description of the work, its uncertainty, and its dependencies.

It earns the word Total twice over: mathematically, every plan, gate, and script is guaranteed to terminate, because the grammar cannot express an infinite loop or anything the engine cannot reason about, so your schedule and odds are always computable; and in scope, one file holds the whole endeavour, its structure, its probabilities, and the logic of each task.

That logic is TOLScript: optional, sandboxed per-task scripts that compute a task's own outputs. Because they always terminate, the Monte Carlo engine can run your plan, not just approximate it from priors.

kitchen.tol
plan "Remodel the kitchen in 6 weeks" {
  agent contractor { type: internal }
  outcome on_budget: boolean

  milestone m_finish "Second fix + sign-off" {
    task t_tile "Paint + tile" {
      agent: contractor
      estimate: 4h cv 0.3
      produces: [on_budget]
      script: "let waste = 0.1; 18.0 * (1 + waste)"
    }
  }

  sentinel s_done { end_state: success }
  edge e_done m_finish -> s_done { carries: null }
}

Decidable by construction

This puts TOL in a deliberate tradition: total languages like Starlark, Dhall, and Lustre, in the lineage of Turner's Total Functional Programming, that give up Turing-completeness in exchange for decidability. You lose the ability to write a plan that might never halt; you gain a plan whose schedule, odds, and critical path are always computable.

Because of that trade, the validator can prove things about your plan before it ever runs. Five of the structural invariants it enforces, each on every edit:

  • Acyclicity SI-1No circular dependencies at the task level, once the plan is fully decomposed. Higher up, at the milestone level, cycles are allowed while you author; each resolves into a bounded iteration before execution.
  • Bounded loops Axiom 1Every iteration declares a max_count, so the fully expanded plan is always finite.
  • Typed carries SI-46Every edge carries a value of a known type, and consumers must accept what producers emit.
  • No dangling work SI-16Every deliverable a task consumes has an upstream task that produces it.
  • Reachable outcomes SI-6Every outcome the plan declares has a task that can actually produce it.

Loops that are guaranteed to end

A plan is more than a dependency graph with code bolted on. The clearest proof is the iteration: a loop that repeats a milestone until an outcome lands, like revising a paper until it's accepted.

But every iteration is bounded: it runs over a finite generator (a fixed count, a query over your deliverables, or an until-gate) and always declares a max_count cap on top. That is why a plan with a feedback loop is still a program that terminates instead of running forever, and why its schedule and odds stay computable.

iteration.tol
plan "Get the paper accepted" {
  agent author { type: internal }
  outcome accepted: boolean

  milestone m_publish "Submit, then revise to acceptance" {
    iteration it_revise "Revise until accepted" {
      over: until(accepted = true)
      max_count: 4
      template: milestone m_round "One revision round" {
        task t_revise "Address reviews + resubmit" {
          agent: author
          estimate: 3h cv 0.4
          produces: [accepted]
        }
      }
    }
  }

  sentinel s_done { end_state: success }
  edge e_done m_publish -> s_done { carries: null }
}
Constrained authoring

The AI never hands you slop

Most AI planners hand you prose: a wall of text that reads fluently and falls apart on inspection. Topolog's never does, because the AI never writes anything you act on directly. It proposes structured edits to a typed graph, and every edit is checked before it reaches you.

So the failure mode isn't "the AI wrote something wrong" that you have to catch; it's "the AI proposed an edit the validator rejected," which you never see. A SAT solver doesn't trust its heuristic and a compiler doesn't trust its parser; in the same way, Topolog doesn't trust the model. You still sign off on the substance, but the AI cannot hand you an invalid plan or a fabricated forecast.

  • Top-down, in a fixed order. The goal becomes milestones, then the dependencies between them, then the tasks inside each, then the dependencies inside those.
  • Each step sees only its slice. No single call sees the whole plan: each gets a bounded context of the parent, its neighbours, and the deliverables in scope.
  • The model emits data, not prose. Every step returns typed JSON that is parsed into TOL, so there is no free text to misread.
  • Every step is validated before it is kept. The same structural-invariant checker the canvas uses runs on each step; cycles, dangling edges, type mismatches and duplicate ids are rejected, and the model is re-prompted with the exact errors.
  • The result is the program everything else runs. A dedicated pass wires up the dependencies between milestones, and the finished plan is the same validated TOL the scheduler and Monte Carlo read. No AI touches the dates or the odds.
re-promptvalidGoalDecomposemilestones → deps → tasksDraftstructured JSON, not proseValidatestructural-invariant checksCommittyped TOL → your graph

Early on I tried what everyone tries: I handed the whole goal to the biggest model available, Mistral Large at the time, and asked for the complete plan in one shot. Nothing it produced passed the engine's structural checks. Not one valid plan.

So I wrote the authoring algorithm instead: break the goal down top-down, and validate every step before keeping it. Today that runs on a mid-size model (Mistral Medium 3.5) for the decomposition and a small one for the rest. The first real test produced a 37-milestone plan, fully wired with cross-dependencies and zero validation errors.

The fix was never a bigger model. It was a different shape for the problem, and that shape is what makes Topolog's AI reliable where others hallucinate.

Rohith B.V., founder
Read off the graph

The completion spectrum

Topolog runs a Monte Carlo over the whole graph (every task's estimate carries an uncertainty, every dependency is respected) and rolls the results into a completion spectrum: the probability you land on time, partially, or miss.

You get an honest forecast instead of one fake date: a P50 end, a budget vs. expected cost, and the full success / partial / failure split, not a single number you'll miss.

Forecast panel: deadline, P50 end, and the completion spectrum
Spend with intent

Money, and the odds it buys

Give the plan a budget and Topolog tracks the cash trajectory: expected spend, the P10-P90 band, and the worst cash position (max drawdown) over the life of the plan.

It also ties how you allocate that budget to whether the plan succeeds. The Pareto frontier plots the trade-off directly: for every level of spend, the best probability of success you can buy, so you pick the point that fits your appetite for risk.

Money panel: cash trajectory + max drawdown
Pareto frontier: spend vs P(success)
Schedule

The deterministic scheduler

The scheduler is pure TypeScript. No AI in the scheduling path. Dependency ordering, time-budgeted scheduling, deadline propagation. Fast, debuggable, free of hallucination.

Milestone→milestone edges are expanded into the cross-product of their atomic tasks before scheduling, hours are bin-packed into your real availability windows, and tasks are split across days when they exceed remaining capacity.

  • → Every dependency respected, every cycle blocked at edge-add
  • → Hours bin-packed into your real weekly availability
  • → Per-Plan and portfolio-level uncertainty bands on every done-by date
  • → Bayesian per-area + per-bucket learning layer that sharpens over time
  • → Layered DAG layout that keeps the canvas readable as plans grow
metaScheduler.ts
try {
  const schedulableNodes = params.nodes.filter((n) => isSchedulable(n));
  const expandedEdges = expandMilestoneEdges(
    params.nodes,
    params.edges,
  ) as unknown as Edge[];
  topologicalSort(schedulableNodes, expandedEdges);
} catch {
  const schedulableNodes = params.nodes.filter((n) => isSchedulable(n));
  const expandedEdges = expandMilestoneEdges(params.nodes, params.edges).map(
    (e) => ({ from: e.from, to: e.to }),
  );
  const cycle = findLeafCycle(schedulableNodes, expandedEdges);
  let cycleDiagnostics: MetaScheduleOutput["cycleDiagnostics"];
  if (cycle) {
    const workspacesByNode: Record<NodeId, WorkspaceId> = {};
    for (const id of cycle.nodeIds) {
      const goalId = params.nodeToGoal[id];
      if (!goalId) continue;
      const wsId = params.goalToWorkspace[goalId];
      if (!wsId) continue;
      workspacesByNode[id] = wsId;
    }
    cycleDiagnostics = {
      nodeIds: cycle.nodeIds,
      edges: cycle.edges,
      workspacesByNode,
    };
  }
  // return { error: "cycle_detected", cycleDiagnostics, ... };
}
Execute

Your day

The Execute board shows exactly what you can act on: a task turns Ready the moment its dependencies clear, you pick it up, and mark it done as you finish. Anything you don't get to carries forward silently and re-prioritises against what remains.

No streaks, no shaming, no "you're behind" pressure. Just a plan that adapts.

The Execute Kanban board: tasks in Blocked, Ready, In flight, and Done columns
Zoom out

Every plan at a glance

One goal is a graph; your life is a portfolio of them. The Plans dashboard shows each plan as a card with its own completion spectrum, P50 end date, budget vs. expected cost, and live status.

Active plans surface their spread at a glance; an overloaded plan (one whose schedule can't fit your availability) is flagged before its deadline slips, and paused plans sit quietly until you pick them back up.

Plans dashboard: a portfolio of plans, each with a completion spectrum

Ready to plan in graphs?

7-day free trial · 250 credits · No card required

Get Started →