play icon for videos
← Academy
SOPACT ACADEMY · GRANT INTELLIGENCE · ANALYZE

Monitor Progress Against Targets + Real-Time Risk Alerts

Read a grantee report for variance, not vibes — extract each committed target and actual, compute the gap, and classify on-track, at-risk, or off-track with the stated reason.

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> @keyframes pulseDot{0%,100%{opacity:1}50%{opacity:.35}} *{box-sizing:border-box} body{margin:0;font-family:'Hanken Grotesk',-apple-system,BlinkMacSystemFont,sans-serif;background:#EEF2F9;padding:26px;display:flex;justify-content:center} .card{width:600px;max-width:100%;background:#FFFFFF;border:1px solid #E3E9F4;border-radius:20px;box-shadow:0 26px 64px -32px rgba(15,30,70,.34),0 2px 4px rgba(15,30,70,.05);overflow:hidden} .hdr{display:flex;align-items:center;gap:11px;padding:16px 20px;border-bottom:1px solid #EEF1F7;background:#F5F8FE} .hdr .ic{display:inline-flex;width:30px;height:30px;align-items:center;justify-content:center;border:2px solid #155DFC;border-radius:50%;color:#155DFC;font-size:15px;font-weight:800;flex-shrink:0} .hdr .t{min-width:0} .hdr .t .n{font-weight:800;font-size:14.5px;color:#0F172A;letter-spacing:-.2px} .hdr .t .s{font-size:12px;color:#64748B} .hdr .pill{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-size:11.5px;font-weight:700;color:#155DFC;background:#E6EEFF;padding:5px 10px;border-radius:20px;white-space:nowrap} .hdr .pill .d{width:6px;height:6px;border-radius:50%;background:#155DFC;animation:pulseDot 2s infinite} .tabs{display:flex;gap:0;padding:12px 16px 0;background:#F5F8FE} .tab{flex:1;display:flex;flex-direction:column;align-items:center;gap:5px;cursor:pointer;font-family:inherit;font-size:11.5px;font-weight:700;color:#64748B;background:transparent;border:none;border-bottom:2px solid transparent;padding:8px 4px 10px} .tab.on{font-weight:800;color:#155DFC;border-bottom-color:#155DFC} .tab .num{display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;border-radius:50%;font-size:11px;font-weight:800;color:#64748B;background:#EEF1F7} .tab.on .num{color:#fff;background:#155DFC} .divider{height:1px;background:#EEF1F7} .body{padding:20px} .eyebrow{font-size:10.5px;font-weight:800;letter-spacing:1.2px;text-transform:uppercase;color:#155DFC;margin-bottom:5px} .eyebrow.g{color:#15884F} .headline{font-family:'Inter',Georgia,serif;font-size:18.5px;font-weight:600;line-height:1.25;color:#0F172A;margin-bottom:12px} .lede{font-size:12.5px;line-height:1.55;color:#475569;margin:0 0 14px} .rows{display:flex;flex-direction:column;gap:8px;margin-bottom:14px} .row{display:flex;align-items:center;gap:11px;background:#F5F8FE;border:1px solid #EEF1F7;border-radius:11px;padding:11px 13px} .row .emoji{font-size:17px} .row .rt{flex:1;min-width:0} .row .rt .n{font-size:13px;font-weight:700;color:#0F172A} .row .rt .s{font-size:12px;color:#64748B} .pillsm{font-size:11px;font-weight:800;color:#155DFC;background:#E6EEFF;padding:4px 9px;border-radius:6px;flex-shrink:0} .pillsm.gray{font-weight:700;color:#64748B;background:#EEF1F7} .chainwrap{position:relative;padding-left:26px} .chainline{position:absolute;left:8px;top:8px;bottom:8px;width:2px;background:#E3E9F4} .cnode{position:relative;margin-bottom:14px} .cdot{position:absolute;left:-26px;top:4px;width:16px;height:16px;border-radius:50%;background:#fff;border:3px solid #94A3B8} .clabel{font-size:9.5px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:#64748B;margin-bottom:4px} .cbox{background:#F5F8FE;border:1px solid #EEF1F7;border-radius:11px;padding:10px 12px;font-size:12px;line-height:1.5;color:#334155} .cbox.graded{display:flex;align-items:flex-start;gap:9px;border-left-width:3px;border-left-style:solid} .cnote{margin:0 0 14px 4px;font-size:11px;line-height:1.5} .cnote .plus{display:inline-flex;width:16px;height:16px;align-items:center;justify-content:center;background:#155DFC;color:#fff;border-radius:4px;font-size:10px;font-weight:800;flex-shrink:0;margin-right:6px} .csummary{display:flex;align-items:center;gap:12px;background:#0A1B3D;border-radius:13px;padding:11px 15px;margin-top:4px} .csummary .t{flex:1;font-size:11.5px;color:#CBD8F5} .csummary .t b{color:#F1F5FF} .mxwrap{border:1px solid #EEF1F7;border-radius:12px;overflow:hidden;margin-bottom:10px} .mxhead{display:grid;grid-template-columns:118px 1fr 1fr;background:#F5F8FE;border-bottom:1px solid #EEF1F7} .mxhead div{font-size:9px;font-weight:800;letter-spacing:.5px;text-transform:uppercase;color:#64748B;padding:7px 9px} .mxrow{display:grid;grid-template-columns:118px 1fr 1fr;border-bottom:1px solid #EEF1F7} .mxrow:last-child{border-bottom:none} .mxrow>div{padding:9px;font-size:11.5px;line-height:1.4;color:#334155;display:flex;align-items:center;flex-wrap:wrap;gap:5px} .mxrow .mxlabel{font-weight:700;color:#0F172A;background:#FAFBFE} .mxchip{font-size:9px;font-weight:800;letter-spacing:.3px;text-transform:uppercase;padding:3px 6px;border-radius:5px;white-space:nowrap} .promptwrap{padding:0 20px 18px} .promptlbl{font-size:10px;font-weight:800;letter-spacing:1px;text-transform:uppercase;color:#94A3B8;margin-bottom:7px} .prompt{display:flex;align-items:flex-start;gap:9px;font-family:ui-monospace,'SF Mono',Menlo,monospace;font-size:11px;line-height:1.5;color:#1E293B;background:#F5F8FE;border:1px solid #EEF1F7;border-left:3px solid #155DFC;border-radius:9px;padding:11px 12px} .prompt .car{color:#155DFC;font-weight:800;flex-shrink:0} .flagrow{display:flex;align-items:flex-start;gap:10px;background:#F5F8FE;border:1px solid #EEF1F7;border-radius:11px;padding:10px 12px;border-left-width:3px;border-left-style:solid;margin-bottom:8px} .flagtag{font-size:9.5px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;padding:4px 8px;border-radius:5px;white-space:nowrap;flex-shrink:0} .flagtxt{font-size:12px;line-height:1.45;color:#334155} .lp{display:flex;align-items:center;gap:12px;background:#0A1B3D;border-radius:13px;padding:14px 16px;margin-top:6px} .lp .ic{display:inline-flex;width:34px;height:34px;align-items:center;justify-content:center;background:#14295C;border:1px solid #26407A;border-radius:9px;color:#7FA8FF;font-size:16px;flex-shrink:0} .lp .rt{flex:1;min-width:0} .lp .rt .n{font-size:13px;font-weight:800;color:#F1F5FF} .lp .rt .s{font-size:11.5px;color:#9DB2D9} .lp .open{font-size:11px;font-weight:800;color:#0A1B3D;background:#7FA8FF;padding:6px 11px;border-radius:7px;white-space:nowrap} </style> </head> <body> <div class="card"> <div class="hdr"> <span class="ic">&#128200;</span> <div class="t"> <div class="n">Grantee · Variance Check</div> <div class="s">DEMO-05 · Grant Applications · Sopact Sense</div> </div> <span class="pill"><span class="d"></span>Tracking</span> </div> <div class="tabs" id="tabs"></div> <div class="divider"></div> <div class="body" id="body"></div> <div class="promptwrap"> <div class="promptlbl">The prompt behind this step</div> <div class="prompt"><span class="car">&gt;</span><span id="promptTxt"></span></div> </div> </div> <script> (function(){ var TABS = ["Setup", "Compute", "Fix", "Report"]; var CONTENT = [{"eyebrow": "Step 1 · Load the report", "headline": "The input is the grantee’s own numbers", "lede": "Point the Assistant at the real record — nothing inferred, nothing invented.", "prompt": "You are the Sopact Sense Assistant. Here is this grantee’s report against committed targets: [PASTE REPORT]. Use only what it states; mark anything you infer as [INFERRED]. Wait for my task."}, {"eyebrow": "Step 2 · Compute & grade", "headline": "Three targets, variance you can recompute", "lede": "Every target is compared and graded — <strong style=\"color:#15884F\">green</strong>, <strong style=\"color:#C9820A\">amber</strong>, <strong style=\"color:#C63E2B\">red</strong>.", "prompt": "Compute variance for this grantee against committed targets."}, {"eyebrow": "Step 3 · Close the gap", "headline": "One request closes the missing report", "lede": "Take the lowest-graded item and resolve it with the one thing that's realistically obtainable.", "prompt": "Take the lowest-graded target above and resolve it using only what’s realistically obtainable this period. Show the before → after status and the one action that clears it."}, {"eyebrow": "Step 4 · Missing & incomplete report", "headline": "What the program officer needs to chase", "lede": "Every amber or red item becomes one named ask — the report leads with the decision it informs.", "prompt": "Create a 'missing & incomplete' report from this analysis in Sopact branding. List every element graded amber or red, what is missing, and the one input that would fix each. Lead with the decision this report informs."}]; var BODIES = { setup: "<div class=\"row\"><span class=\"emoji\">&#128202;</span><div class=\"rt\"><div class=\"n\">Grantee report</div><div class=\"s\">Committed targets vs. reported figures</div></div><span class=\"pillsm\">Loaded</span></div><div class=\"row\"><span class=\"emoji\">&#128200;</span><div class=\"rt\"><div class=\"n\">Variance formula</div><div class=\"s\">(Reported - Target) / Target</div></div><span class=\"pillsm gray\">Applied</span></div><div class=\"row\"><span class=\"emoji\">&#128274;</span><div class=\"rt\"><div class=\"n\">Recomputable rule</div><div class=\"s\">Every variance shows its inputs</div></div><span class=\"pillsm gray\">Required</span></div>", build: "<div class=\"mxwrap\"><div class=\"mxhead\"><div></div><div>Reported</div><div>Variance / status</div></div><div class=\"mxrow\"><div class=\"mxlabel\">People served</div><div>210 / 200</div><div>+5% · on track<span class=\"mxchip\" style=\"background:#15884F;color:#fff\">GREEN</span></div></div><div class=\"mxrow\"><div class=\"mxlabel\">Sessions held</div><div>38 / 50</div><div>-24% · at risk (staffing)<span class=\"mxchip\" style=\"background:#C9820A;color:#fff\">AMBER</span></div></div><div class=\"mxrow\"><div class=\"mxlabel\">Outcome report</div><div>—</div><div>not submitted<span class=\"mxchip\" style=\"background:#C63E2B;color:#fff\">RED</span></div></div></div>", fix: "<div class=\"mxwrap\"><div class=\"mxhead\"><div></div><div>Reported</div><div>Variance / status</div></div><div class=\"mxrow\"><div class=\"mxlabel\">People served</div><div>210 / 200</div><div>+5% · on track<span class=\"mxchip\" style=\"background:#15884F;color:#fff\">GREEN</span></div></div><div class=\"mxrow\"><div class=\"mxlabel\">Sessions held</div><div>38 / 50</div><div>-24% · at risk (staffing)<span class=\"mxchip\" style=\"background:#C9820A;color:#fff\">AMBER</span></div></div><div class=\"mxrow\"><div class=\"mxlabel\">Outcome report</div><div>—</div><div>received<span class=\"mxchip\" style=\"background:#15884F;color:#fff\">GREEN</span></div></div></div><div class=\"cnote\" style=\"color:#15884F;margin-left:0\"><span class=\"plus\">+</span><strong>Fix applied:</strong> Request the outcome report before the next disbursement.</div>", reportFlags: "<div class=\"flagrow\" style=\"border-left-color:#C9820A\"><span class=\"flagtag\" style=\"color:#C9820A;background:#FBF0DA\">AMBER</span><div class=\"flagtxt\">Sessions held — 24% under target, attributed to staffing. Needs a recovery plan or a revised target.</div></div><div class=\"flagrow\" style=\"border-left-color:#C63E2B\"><span class=\"flagtag\" style=\"color:#C63E2B;background:#FCE9E7\">RED</span><div class=\"flagtxt\">Outcome report — not submitted this period. Needs to be requested before the next disbursement.</div></div>", lpTitle: "Missing & incomplete report · shareable link", lpSub: "Decision-first · Sopact branding · Grantee · Variance Check", summaryBefore: "Grade: 1 green &middot; 1 amber &middot; 1 red. Variance you can recompute.", summaryAfter: "Grade: 2 green &middot; 1 amber &middot; 0 red. Nothing left unexplained." }; var phase = 1; var tabsEl = document.getElementById('tabs'); var bodyEl = document.getElementById('body'); var promptEl = document.getElementById('promptTxt'); function renderTabs(){ tabsEl.innerHTML = ''; TABS.forEach(function(label, i){ var b = document.createElement('button'); b.type = 'button'; b.className = 'tab' + (i === phase ? ' on' : ''); b.innerHTML = '<span class="num">' + (i+1) + '</span><span>' + label + '</span>'; b.addEventListener('click', function(){ phase = i; render(); }); tabsEl.appendChild(b); }); } function renderBody(){ var c = CONTENT[phase]; var eyebrowClass = (phase===2) ? 'eyebrow g' : 'eyebrow'; var out = '<div class="' + eyebrowClass + '">' + c.eyebrow + '</div><div class="headline">' + c.headline + '</div><p class="lede">' + c.lede + '</p>'; if (phase === 0) { out += '<div class="rows">' + BODIES.setup + '</div>'; } else if (phase === 1) { out += BODIES.build; out += '<div class="csummary"><div class="t"><b>' + BODIES.summaryBefore + '</b></div></div>'; } else if (phase === 2) { out += BODIES.fix; out += '<div class="csummary"><div class="t"><b>' + BODIES.summaryAfter + '</b></div></div>'; } else { out += BODIES.reportFlags; out += '<div class="lp"><span class="ic">&#128196;</span><div class="rt"><div class="n">' + BODIES.lpTitle + '</div><div class="s">' + BODIES.lpSub + '</div></div><span class="open">Open &rarr;</span></div>'; } bodyEl.innerHTML = out; promptEl.textContent = c.prompt; } function render(){ renderTabs(); renderBody(); } render(); })(); </script> </body> </html>

In short: To monitor grantee progress against targets, read the grantee report for variance rather than vibes: extract each committed target and its actual, compute the variance, and classify the line as on-track, at-risk, or off-track — with the stated reason and the figures cited. Sopact Sense grades each line green, amber, or red so a reviewer sees exactly where a grant is slipping and why, before the next disbursement.

1 · Set up over your data

Start with the grantee's report loaded as clean data with persistent contact IDs, so every variance traces to a committed figure and an actual. Point the assistant at the dataset and have it read your Decision Brief first — the decision, audience, outcomes, indicators, and evidence standard.

You are the Sopact Sense Assistant working over the DEMO-05 · Grant Applications dataset (clean data + persistent contact IDs). Load my Decision Brief (decision, audience, outcomes, indicators, evidence standard) first, then wait for my task.

2 · Write the variance prompt

The prompt extracts the targets, computes the gap, and demands a reason. Paste this verbatim:

From the grantee report, extract each committed target, the actual, compute variance; classify on/at-risk/off-track with the reason. Cite figures. Grade green/amber/red.

The prompt works because of five elements: the dataset it reads over, the pairing of targets vs actuals, the demand for the stated reason behind each gap, the instruction to cite figures, and the call to grade green/amber/red so slipping lines are visible.

3 · What Sense produces

Run it against the Foundation grant round demo:

Run on the Grant Applications dataset (DEMO-05) already loaded in Sopact Sense.

GRADE: green | served | +5%; amber | sessions | -24%; red | outcome | report missing

The green line is people served, running 5% above target. The amber line is sessions delivered, down 24% — at-risk, with a reason worth chasing. The red line is the missing outcome report — no data submitted, so variance can't be computed at all.

4 · Turn a weak link green

Take the weakest line and fix it with the one input that resolves it. Sense shows the before → after grade.

Take the lowest-graded element above and fix it using only what the program could realistically measure. Show the before → after grade and the single indicator/edit that moves it to green.

5 · Make the report and share it

Turn the variance table into a report, then a link that opens with no login.

Create a 'missing & incomplete' report from this analysis in Sopact branding [or paste your website URL / brand guideline to apply your own]. List every element graded amber or red, what is missing, and the one input that fixes each. Lead with the decision this report informs.
Create a shareable link for this report and open it in a new tab.

Tricks, tips, and troubleshooting

Read for variance, not vibes. A glowing narrative can sit on top of a 24% shortfall. Force the target-vs-actual math so the story is checked against the numbers.

Always capture the reason. An off-track line without a reason is a dead end. Require the grantee's stated explanation so you can tell a one-off setback from a structural problem.

Missing data is red, not zero. Treat an unsubmitted outcome report as red — a gap to close — not as a zero that quietly drags the average down.

Cite every figure. Tie each variance to the committed target and the reported actual so the classification is auditable, not asserted.

For the sessions line, pull the grantee's stated reason for the -24% variance and flag whether it is a one-time or recurring cause.

Frequently asked questions

How do funders monitor grantee progress against targets?

Funders compare each committed target to the reported actual, compute the variance, and classify the line as on-track, at-risk, or off-track. Doing it with AI means extracting the figures from the grantee report, computing the gap, and capturing the stated reason — so monitoring rests on numbers and explanations, not on the tone of the narrative.

What counts as off-track for a grant?

Off-track is a material shortfall against a committed target, or a missing report that makes variance impossible to compute. The useful distinction is between at-risk lines that have a stated, recoverable reason and off-track lines that signal a structural problem — which is why every variance should carry its reason.

What makes grantee monitoring weak?

Monitoring is weak when it accepts narrative over numbers, treats missing data as a silent zero, or records a shortfall with no stated reason. The fix is to compute target-vs-actual variance, mark missing reports red, and require the reason behind every gap.

Ready to try it for yourself?

Open Sopact Sense, paste your program description, and put it to work.

Try in Sopact