Lattice Grid Buy a licence

demo D292

AI Project Risk Summary: Grounded Delivery Risk From Gantt and Kanban in React

Turn a project plan and a board into the one line a delivery lead wants: how many tasks are at risk on the critical path, how the schedule and cost are tracking, and how many service limits have slipped, each figure computed by the plan and the board and reconciled so no number is invented, with task names and money held back unless you opt in

createAI · ai.riskSummary(sources)

This turns a project plan and a board into the one line a delivery lead wants to read: how many tasks are at risk on the critical path, how the schedule and cost are tracking, and how many service limits have slipped. Every figure is computed by the plan and the board, then reconciled so a number the model did not ground is stripped before you see it.

Building…
Loading a live grid…

This is the React version. The grid mounts through the createLatticeGrid adapter, which takes its configuration as ordinary props and hands back the live grid through a ref. The grid below is the same one every other tab runs.

The configuration

<link rel="stylesheet" href="https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.min.css">

<div id="app"></div>

<script type="module">
  import React from 'https://esm.sh/react@18';
  import { createRoot } from 'https://esm.sh/react-dom@18/client';
  import { createHeadlessGrid } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/lattice-grid.esm.min.js';
  import { createAI } from 'https://cdn.latticegrid.dev/lattice-grid@1.107.0/modules/ai.esm.min.js';

  // The Gantt and Kanban modules are separate; the AI layer imports neither.
  // You pass their already-computed outputs and it reads them duck-typed, so
  // this sample needs no grid or viewer component of its own.

  // Your model, your key, your network call. The module makes none: it hands
  // ask() only the aggregate figures below and validates the text that comes
  // back.
  const ask = async ({ system, messages }) => {
    const reply = await myProvider.chat({ system, messages });
    return { text: reply.text };
  };

  function App() {
    const [text, setText] = React.useState('');

    React.useEffect(() => {
      let cancelled = false;
      // headlessGrid: a no-DOM grid, exactly like the vendored createHeadlessGrid,
      // large enough for ai.riskSummary()'s own bookkeeping.
      const headlessGrid = createHeadlessGrid({ rowKey: 'id' });
      const ai = createAI(headlessGrid, { ask, tools: false });

      // The plan and the board have already done the maths: the schedule
      // reports its critical path, earned value reports SPI/CPI, the board
      // reports its own SLA breach and warning counts. None of this is asked
      // of the model.
      const schedule = gantt.schedule;
      const earnedValue = gantt.earnedValue({ statusDate });
      const breaches = board.sla.breaches();
      const warnings = board.sla.warnings();

      // One grounded line: "3 tasks at risk on the critical path, SPI 0.66, 2
      // SLA breaches." Every figure is reconciled against the computed values
      // above, so a number the modules did not produce is stripped before it
      // is shown.
      ai.riskSummary({ schedule, earnedValue, breaches, warnings }).then(({ text }) => {
        if (!cancelled) setText(text);
      });

      return () => { cancelled = true; headlessGrid.destroy(); };
    }, []);

    return <div>{text}</div>;
  }

  createRoot(document.getElementById('app')).render(<App />);
</script>

The status update, without the status meeting

A plan and a board hold everything you need to know whether delivery is on track, but reading it means opening two tools and doing the arithmetic in your head. This writes the summary instead: one plain line that leads with the tasks still open on the critical path, the schedule and cost performance, and the service limits that have already slipped. It is the sentence a delivery lead would say out loud, produced from the numbers rather than a feeling about them.

The point is where the numbers come from. The plan is scheduled by the project module, which reports its critical path and its earned-value indices; the board is aged by the board module, which reports its own breach and warning counts. Those computed outputs are the ground the summary must stand on. Every figure in the line is checked back against them, so a number that was not computed is removed before it reaches you. The model writes the sentence; it does not invent the arithmetic.

Trust is the default, not a setting. A summary carries aggregates only unless you say otherwise: the counts and the indices, never a task name or a money figure. When the audience is right for more, you add the at-risk task names or the cost figures on purpose, and the summary reports exactly what it exposed.

How do I summarise delivery risk?

Hand the AI layer the outputs the plan and the board have already produced: the schedule, the earned value for a status date, and the board’s breach and warning lists. Call ai.riskSummary({ schedule, earnedValue, breaches, warnings }), or the { kind: 'risk' } target of explain, and you get the grounded line back. Your own model, wrapped in ask, writes the words; the grid holds no key and makes no call of its own, and hands the model only the aggregate figures. To widen what the summary may say, pass includeTaskNames or includeCost, each reported back in meta.exposed so the exposure is never silent.