Measuring Progress¶
An impact model says what you expect. Measuring says how far you have come. The measurement is a pure function of three inputs: the impact model, the observations, and a date. The same inputs always give the same answer.
Indicators¶
An indicator says how an outcome (or an assumption) is measured. It carries a
baseline, targets with dates, a collection frequency, and the direction that
counts as progress (increase or decrease). A quantitative indicator has
numbers; a qualitative one has ordered levels, such as Emerging to Embedded.
Indicators can use a shared metric from the library, so the same measure is
defined once. A metric says how values combine across pathways: sum, mean,
weighted_mean, median, mode, min, max, latest, or none for a count
that cannot be added up, such as distinct people. An indicator can also roll
up from other indicators; a direct observation always beats a roll-up.
An indicator's claim says what the organization can say about it:
attribution (we caused this) or contribution (we contributed to it). The
default is contribution.
Where an indicator sits in the chain¶
What an indicator measures decides its tier, which is never stored:
| Tier | The indicator measures | For example |
|---|---|---|
activity |
an intervention | hours delivered, dollars spent |
output |
an output-level outcome | people reached |
outcome |
a short-term, medium-term, or long-term outcome | skills gained, practice applied. These are the driver metrics |
impact |
an impact outcome | residents served, reported as a contribution |
Resources are measured as activity-tier indicators with a unit such as hours or
dollars. impact-model report metric-tree lays a pathway's indicators out in
this order.
North Star, guardrails, and headline lists¶
Three choices sit on top of the tiers, and they are different things:
- A North Star is the one number a program or product steers by. Set it in the
pathway's
focus, with an optionaloneMetricIdand theguardrailIdsthat watch what pursuing it could harm. It should measure an outcome at or below the ceiling of accountability, never an output or an activity. - A headline list is what an audience is told: a short, ordered list on the
audience, each entry an indicator with an optional
labelandnoteworded for that reader. Every audience sees the same figure for an indicator; only the label and note differ. A North Star may lead one list and not appear in another. - Purpose is
context.missionandcontext.vision, plain statements. They are not part of the causal chain.
impact-model report headline shows what each audience is told, and the story
report and deck use the same lists. impact-model lint checks all of it: the
TOC-NSM-*, TOC-HLN-*, IMP-AUD-002, and IMP-CTX-001 rules.
Working a metric out from others¶
Some of the numbers people ask for are arithmetic on others, such as the cost per
applied change. Give that indicator a composedOf:
composedOf:
op: ratio # ratio, difference, product, or sum
indicatorIds: [ind-cost, ind-changes-applied] # cost divided by changes
A ratio and a difference take exactly two indicators, in order. When the indicator has no observation of its own, its value is worked out from the latest value of each part, and it is marked as an estimate if any part is. If a part is missing, or a divisor is zero, there is no value and nothing is guessed. A composition is arithmetic, not a causal link, and an indicator is either composed or rolled up, not both.
Observations¶
Measurements go in an observation set, a separate file that refers to the Impact model by ID:
kind: ObservationSet
specVersion: "0.1"
modelId: riverbend-network
period: 2026-Q3
observations:
- indicatorId: ind-hours-learning
date: "2026-09-30"
value: 3650
source: Attendance sheets
quality: verified
An observation has a date and either a value (quantitative) or a level
(qualitative), plus a source, a quality (verified, reported, or estimated), and an
optional note. The same file also holds:
- Assumption assessments: whether an assumption is still holding.
- Stories: a person's account, with the consent that was given and a sensitivity. See consent.
- Learning reviews: what changed in a period, the surprises, and decisions.
- Notes: context that belongs with the numbers.
Check a set against the impact model it measures:
Load a spreadsheet¶
Most programs already keep their numbers in a spreadsheet. Describe its columns once in a mapping file, then import:
modelId: riverbend-network
period: 2026-Q4
dateFormat: 02/01/2006
columns:
indicator: Measure
date: Month end
value: Value
pathway: Program
quality: Quality
source: Source
indicatorAliases:
Leadership practice maturity: ind-practice-maturity
metricAliases:
Attendee hours: met-attendee-hours
impact-model import observations --csv q4.csv --map mapping.yaml --model impact-model.yaml \
--out obs-q4.yaml --unmatched q4-unmatched.csv
A row can name an indicator directly, through an alias, or through a metric plus
a pathway. Every row either becomes an observation or is reported as unmatched
with its row number and the reason; nothing is dropped silently. Unmatched rows
are written to --unmatched as a sheet you can fix and import again. The exit
code is 1 when some rows were unmatched (the rest are still written).
Status¶
impact-model status impact-model.yaml -O obs-q3.yaml -O obs-q4.yaml --as-of 2026-12-31 --out status-q4.json
status prints a summary. --format json prints the full snapshot, and --out
writes it to a file. The date defaults to the latest observation.
Indicator status¶
For each indicator the snapshot gives its progress against what the targets call for by the date:
| Status | Meaning |
|---|---|
achieved |
The latest value has reached the target |
on_track |
At or within tolerance of the expected progress |
at_risk |
Behind expected progress by more than the tolerance |
off_track |
Far behind, or moving the wrong way |
stale |
The newest observation is older than twice the collection frequency |
no_data |
Nothing observed, and the first target date has passed |
not_started |
Nothing yet, before the first target date |
baseline_only |
No targets to measure against |
--tolerance (default 0.10) sets how far behind still counts as on track.
Outcome and pathway status¶
An outcome's status combines its indicators (--rollup worst, the default, or
mean) with the state of what leads to it. Pathway status follows the
precondition logic: an all group takes its worst member, any its best, and
at_least k the k-th best. A negative link, an inhibitor, caps its target at
at_risk while its source is on track.
Assumptions and confidence¶
An assumption assessed as at risk caps every link and outcome that depends on
it at at_risk; a critical assumption that is invalidated caps them at
off_track. Each link gets a rule-based confidence score from its evidence,
its assumptions, and whether it has a rationale. Confidence for a route to
impact is derived from its links, so a weak link shows up where it matters.
What changed¶
Keep each snapshot and compare:
impact-model status impact-model.yaml -O obs-q4.yaml --prev status-q3.json
impact-model report exec impact-model.yaml -O obs-q4.yaml --prev status-q3.json
The comparison lists the indicators, outcomes, links, and assumptions whose status or confidence changed, and the elements that were added or removed.