back to the three assignments

assignment 2 · EPD planning cadence

EPD Pulse

act before the block

screen recording · stepping through the six-step scenario

The assignment

How does an EPD org plan for 3 months? How do we commit? What checks and balances keep us on track? How do we flag delays proactively? How do we measure progress? Assume Slack + Linear.

What I shipped: A working offline prototype: a five-tab workspace, a synthetic quarter, and a six-step scenario replay.

Deliverable
Mock tool + video
Data
Synthetic, frozen, fully offline
Warning rules
14 implemented
01

What I built, in one sentence

I built a small workspace that lets me step through a quarter: a healthy plan slips, raises a warning, records an explicit decision, recovers, delivers, and then gets checked against the outcome it was meant to move.

I kept coming back to five questions: how do I plan a quarter, commit to it, keep the plan honest, surface delays early, and measure progress? A document can answer each one. I wanted a founder to watch the loop run, though, so I built the loop instead of asking them to trust a description.

synthetic datamock linearslack previews only

Nothing here is live, and I’d rather say exactly what stands in for what than let a demo imply access I don’t have:

  • Linearis a frozen snapshot I wrote by hand , sixty-three issues with statuses, dates, blocked timestamps and critical-path flags. Every row is labelled Mock Linear. There’s no API call and nothing is written back.
  • Slackis six messages rendered locally in Slack’s own layout. They carry a “preview only” label and the buttons are visibly disabled, because there is no token and no send path in the code.
  • Customer and outcome evidence is synthetic, in the shape Enterpret-style evidence would arrive in: a baseline, a target, a measured actual, and a note about how it was normalised.
  • Throughput historyis twelve invented weeks per pod. It’s what the forecast samples from, so I say plainly that the probabilities describe made-up history.

Structurally it’s one route with five tabs , overview, plan, risks, learning, evidence , one frozen data set, and six fixed scenario steps. The step controls sit above every tab, so advancing the quarter never makes you leave the view you’re looking at. There is no network call anywhere in it, and a test scans the source and fails the build if one sneaks in. It’s a working explanation of how the operating model behaves, not a production planning tool.

signal → context → action → verification

the loop I wanted someone to feel while watching
02

The frameworks I borrowed from, and what I left out

I didn’t pick one methodology and apply it. I took the parts that still work at thirty people and left the rest, because most of what makes planning heavy at a big company is exactly what a small org can’t afford.

OKRs, for the outcomes

Objectives and key results give me the reason each bet exists and a number to check at the end. I also took the commitment-versus-aspiration split from OKR practice , that’s where my three tiers come from.

SAFe, three parts of it

I kept the planning cadence, aggregate capacity, and dependencies committed to in the open. I left out the trains, the extra roles and the two-day planning event. Full SAFe is built for hundreds of people; this is thirty.

Shape Up, for scope

Fixed time, variable scope. Every committed bet carries slices I’ve marked cuttable in advance, so when something slips there’s a cheap move ready instead of a negotiation.

Kanban and Monte Carlo, for dates

Flow metrics , throughput, aging, work in progress, blocked time , are the early warnings. Running Monte Carlo over past throughput turns them into a probability instead of someone’s gut feel.

What the four have in common is the thing I actually wanted: a small set of numbers, computed the same way every week, that say something is wrong while it’s still cheap to fix.

03

How I decide what I can promise: three tiers and a visible cut line

I set the quarter from 5 October to 25 December: twelve execution weeks, with decision windows at weeks 1–4, 5–8 and 9–12. I treat those windows as moments to make a call, not as forced project durations.

I put twelve bets across four pods and split them into three tiers. Four are Committed, four Target, four Stretch. The distinction I wanted to make impossible to miss is that tier is a promise, not a probability. Committed says “this is what we told people.” The forecast separately says “here is how likely it is.” If I collapse those into one number, planning turns into theatre.

public commitment cut line

In the tool, I render Committed rows, then the literal cut line, then Target and Stretch below it. Anything below the line isn’t forecast; it’s conditional work. I’d rather say that plainly than print a confidence number for something nobody promised.

Committed

I promised this externally. I forecast it at every step. There are four bets. If confidence drops, I treat that as an event that needs a decision.

Target

I intend it, but I haven’t promised it. It stays below the cut line. I show demand and capacity, but deliberately leave out probability.

Stretch

I cut this first. Keeping it visible is what makes a cut cheap instead of political.
04

How I look at capacity: pod level, and nowhere else

I calculate capacity per pod, and only per pod. I deliberately leave out per-person throughput. The minute a planning surface can be read as an individual performance metric, people start managing the metric, and I didn’t want this tool to create that incentive.

planned  = floor(gross capacity × availability factor)
usable   = max(0, planned − interrupt reserve)
headroom = usable − committed demand
util     = committed demand / usable
over     = committed demand > usable
the capacity math I wanted to be inspectable
A · Core Product100%

23 committed of 23 usable units

B · Integrations77%

17 committed of 22 usable units

C · ML & Inference47%

8 committed of 17 usable units

D · Experience71%

15 committed of 21 usable units

four pods at quarter start, with Pod A exactly full

Pod A is exactly at 100% of usable capacity and has zero headroom. I wanted the tool to say that plainly without quietly demoting anything. Full is a planning fact, not an error to auto-correct; only a human decision moves committed work.

PodGrossAvail.ReserveWIP limitP85 cycle
A · Core Product320.90559 business days
B · Integrations300.90548 business days
C · ML & Inference240.8754312 business days
D · Experience280.90447 business days
I take the interrupt reserve out before I commit anything, and I don't borrow it back later. P85 cycle time is the slow tail: eighty-five per cent of that pod's work finishes faster than that.
05

The dependency contract I wanted to catch early

I kept thinking about the quiet failure mode: a cross-team dependency that everyone assumes is fine until it isn’t. I turned it into a contract with named fields, rather than leaving it as a line on a roadmap.

  • Direction.I name the direction: Pod B provides “Search Indexing API v2 available in staging” to Pod A.
  • Acceptance criteria.I write down three checkable things: schema published, staging endpoint reachable, contract tests passing. They’re agreed up front.
  • Both acknowledgements. Provider and consumer each acknowledge the same directed contract, with a date.
  • Two dates.I keep the provider’s expected-ready date next to the consumer’s latest safe start.
  • Slack. I calculate the business days between those dates. That single number is the early-warning signal.

At the start of the quarter the provider expects to be ready on 12 October and Pod A’s latest safe start is 16 October, so I have four business days of slack. Nothing is wrong yet. Then the provider’s date moves to 19 October and slack becomes −1.

Warnings before a downstream pod becomes blocked.

the warning I wanted to surface

Here’s the part I wanted a founder to see: Pod A is still notblocked. Nothing is on fire, and standup would still report green. But the arithmetic says a block is coming while there are still days to make a cheap decision.

  1. Provider date slips

    12 Oct → 19 Oct

  2. arithmetic

    Slack goes negative

    +4 → −1 business days

  3. trigger

    Two warnings raised

    dependency slack + forecast confidence

  4. not yet an outage

    Pod A still not blocked

    this is the whole window of action

  5. named cause + options

    Explicit decision recorded

    cut scope · approved, with a rationale

  6. human approves

    Decision applied

    1 unit cut · latest start 16 → 20 Oct

  7. recompute

    Slack +1 · confidence 84%

    critical warning resolves

what I show from a slipped date to a recovered forecast
06

The fourteen ways I let the plan tell on itself

Every warning carries the same six things: what caused it, in words; what it puts at risk; the records it was derived from; when it was first seen; the date by which someone has to decide; and the options available. Without a named cause and a deadline, a warning is just a colour.

RuleFires whenCritical when
Forecast confidenceProbability in the 65–80% watch band, or drops ≥15 pointsBelow 65%
Dependency slackSlack ≤ 2 business daysSlack is negative
Dependency cycleAny cycle in the provider→consumer graphAlways
Latest safe startCritical-path item unstarted at its latest safe startPast the date
Blocked durationBlocked ≥ 3 business days≥ 5 days
Item agingActive longer than the pod's P85 cycle timeBeyond 1.5× P85
Scope growthPost-commit additions > 5% of baseline≥ 15%
Plan churnCumulative absolute revisions > 10% of baseline≥ 20%
WIP limitActive items above the pod's WIP limitAbove 1.5× the limit
Throughput dropRecent throughput < 75% of historical median< 50%
Carry-overAn item carried over one cycleTwo or more cycles
Review latencyIn review ≥ 3 business days≥ 5 days
ML gateGate incomplete within 3 business days of dueOverdue
Outcome targetA measured key result misses its targetAlways warning
thirteen deterministic rules and one probabilistic one; I care about the arithmetic more than the colour

I also gave warnings a lifecycle. When one recurs, it keeps its original identity and first-seen timestamp while last-seen updates, so lead time stays measurable. Once the underlying condition disappears, I mark it resolved instead of deleting it. I want the record to say “this fired for six days and then stopped,” not just show the current list.

Deterministic

I made thirteen rules pure arithmetic over dates, counts and limits. They can’t be argued with, which is what makes them useful in a review.

Probabilistic

One rule reads the forecast. I made it fire on a watch band or on a material drop, because a fall from 95% to 79% matters even though 79% looks fine.

Never inferred

I don’t let any rule guess at intent, effort or individual performance. Every input is a recorded fact from the execution record.
07

How I forecast, and what I refuse to forecast

For a committed bet, I use a Monte Carlo simulation rather than a judgement call. I run ten thousand seeded simulations per bet so the number is reproducible. Each run walks the remaining weeks and samples one week of that pod’s historical throughput at a time.

I model the dependency inside the simulation rather than bolt it on afterwards. Units blocked by an unresolved dependency can’t be consumed until the week the dependency is expected ready; only independent work can absorb capacity before that. A run counts as a hit only when both the independent and blocked units reach zero.

80.8%

healthy baseline

61.8%

after the slip

84.3%

after the cut

100%

delivered

I keep the bands fixed: ≥80% healthy · 65–80% watch · below 65% at risk · zero missed

what happened to confidence on the same committed bet across six steps

There are two cases where I refuse to print a number at all: research work, and a pod with fewer than six weeks of history to sample from. And two where the honest answer is zero rather than nothing , the target date has already passed with work still open, or the history says that pod has never shipped anything. Research is the case where most planning tools start pretending.

The ML bet gets a gate, not a probability

One of my four committed bets is intent-clustering feasibility. It’s genuinely unknowable: I can’t forecast a research result from throughput history. So the tool never produces a number for it. Instead, I give it a timeboxed learning gate with three checkable criteria and a due date.

CriterionThresholdActual at verification
Macro F1≥ 0.780.81 ✓
P95 inference latency≤ 800 ms740 ms ✓
Labelled evaluation sample≥ 500 records620 ✓
gate due 6 November · outcome: production-shape

Passing the gate still doesn’t produce a delivery probability for the research timebox. It gives me permission to shape a separate, bounded production bet, which can then be forecast because it has scope. The gate turns uncertainty into a decision on a date; it doesn’t hide uncertainty behind a number.

Uncertain research · no delivery probability

the learning gate card I wanted to be unambiguous
08

How I let someone walk the six steps, end to end

The scenario replay is where I demonstrate the model. I made six fixed steps; each one replays from the frozen baseline and applies its recorded changes in order, so someone can walk forward and back and get the same numbers every time.

  1. S0

    Healthy baseline

    slack +4 · no warnings

  2. S1

    Dependency risk

    slack −1 · 61.8% · not blocked

  3. S2

    Decision approved

    recorded, not yet applied

  4. S3

    Forecast recovery

    1 unit cut · slack +1 · 84.3%

  5. S4

    Delivery verified

    evidence recorded · gate passed

  6. S5

    Outcome measured

    delivered ≠ target met

Sequence of 6 pegged cards.

six steps · each one is a state you can stop on and inspect

Two steps are easy to blur, so I kept them separate. S2 and S3 are separate on purpose.Approval and effect are different events. If I merge them, the plan can look healthier before anyone has actually applied the decision. In S2 the numbers stay exactly where they are: the decision is approved but pending. Only in S3, when the one-unit CSV-export slice is actually cut, do remaining units drop, the consumer’s latest safe start move from 16 to 20 October, slack turn positive, and the forecast recover.

I also made the scope cut deterministic rather than discretionary. Cuttable slices are ordered Stretch, then Target, then Committed; within a tier by descending priority, then a defined cut order, then id. The same pressure therefore proposes the same cut, and the argument is about whether to cut, not about what.

09

Where I put Linear and Slack

I kept the boundary between Slack and Linear deliberately simple. The division of labour matters more here than the integration detail.

Linear holds the execution truth

I treat issue status, dates, critical-path flags, scope slices, carried-over cycles and delivery evidence as the execution record. The planning surface derives from it; it never becomes a second place to update status. In this prototype, that record is an immutable mock snapshot labelled Mock Linear. There’s no API call and no write-back.

Slack is where the signal has to land

Nobody opens a planning dashboard to find out something is wrong. So I build six messages locally, laid out the way Slack lays out its own: weekly digest, dependency reminder, event alert, decision record, verification, outcome update. Every one is marked preview only · no message sent, and the action buttons render disabled.

The dependency reminder is the one I would show first. It lands in the provider pod’s channel while the consumer is still unblocked, and carries the need-by date, the current forecast, the explicit not-blocked-yet status, and the decision options. That gives someone an action to take, rather than a status to interpret.

Act before the block

the rule the scenario card states
10

How I measure progress: three clocks kept apart

When I ask “how do we measure progress,” I hear three different questions. I kept them separate because shipping and succeeding aren’t the same thing.

Delivery clock

I track say/do ratio, committed carry-over rate, original dependency on-time rate, aggregate WIP and flow. Did we do what we said, by when we said it.

Outcome clock

For each key result, I keep the baseline, target, actual, attainment, and the evidence it was measured from. Did the thing we shipped move the number.

Control-system clock

I track warning lead time, intervention response time, threatened-commitment recovery, warning precision, and forecast calibration. Is the early-warning system itself any good.

The third clock is the one I’d defend hardest. A warning system nobody can grade turns into noise inside a quarter. So I measure how much warning we actually got, in business days between a warning first appearing and the date it was warning about, and how long we took to answer it, in hours from that first sighting to an approved decision. For accuracy I use a Brier score , the standard way to score probability forecasts against what actually happened , and I leave it blank until there are at least five finished forecasts, rather than showing a number built on two.

What I wanted the final step to show

At the end of the quarter I show all four committed bets delivered and a say/do ratio of 1. Three of the four key results still miss their targets: weekly enterprise search adoption reaches 48% against a 52% target, search-related negative feedback falls to 34 against a target of 28, and sync success reaches 99.1% against 99.5%.

Shipping is not success; this bet is delivered but only partially validated.

what the outcome panel says at step six

I leave one more uncomfortable number visible in that step. Original dependency reliability is zero: the provider delivered after the date it originally committed to, even though the mitigation protected the consumer and the commitment was met. Both facts are true, so the tool refuses to average them into a single green tile. The intervention worked; the original promise still slipped.

11

What I refuse to claim

  • I made the four pods, the quarter dates, the throughput history and every key result synthetic. They are shaped to be plausible, not to describe any real org.
  • My business-day arithmetic ignores public holidays.
  • The thresholds I use, including the 80% commitment bar, the 65% critical bar and the warning trigger points, are demonstration values. Real ones have to be calibrated against real historical distributions.
  • I compute the forecast calibration score over synthetic outcomes, so it demonstrates that calibration is tracked, not that the forecasts are accurate.
  • I represent integrations locally. Refreshing the page resets the scenario because there is nothing to persist.

I’m not adding ceremony. I’m making truth visible while there’s still time to act.

the line I wanted the founder to leave with
the other two assignmentsA2 · synthetic throughout