Skip to main content

Squillify development is paused indefinitely. Read why, and discover what this experiment achieved. Read the story

MicroSkills Reference

OKR Progress Calculator

business.okr_progress

ProductionBounded custom (Class B)Deterministicstrictinstantv1.0.0
No network

Overview

You send the key results — a metric moving from a start toward a target, a milestone that is done or not, or a figure that is already a proportion — and we return each one's progress, the weighted objective progress, the percentage and the status band it falls in. Progress is defined rather than guessed: a metric is the distance travelled from start to target, which is why the two must differ, and a target below the start reads correctly for a metric you want to bring down. Overshoot and backsliding are clamped to nothing-to-fully-done by default and flagged where they happened, so a key result at 120 per cent cannot carry an objective past complete. Every contribution is shown and they add up to exactly the progress beside them.

Every OKR tool computes progress, and no two agree on how. The disagreements are always the same three: whether a metric's progress is measured from zero or from where it started, what a key result at 120 per cent does to the objective, and where 'behind' ends and 'on track' begins. Each of those is an explicit input here, and the answer comes back with the arithmetic and the boundaries attached, which is what makes two teams' numbers comparable.

Reliability

Fully deterministic: BigDecimal at 34 significant digits, HALF_UP, rounded to the scale you ask for. METRIC progress is (current − start) ÷ (target − start), which is why start and target must differ and why a target below the start reads correctly for a DECREASE key result. MILESTONE is the 0 or 1 you send; PERCENT is passed through after its range is checked. Contributions are each normalizedWeight × progress, rounded so they sum to exactly the objective's progress, residue assigned by largest remainder with ties going to the earlier key result. The remainders compared are those of the unrounded 34-digit contributions, not of the figures published beside them, so two contributions that print identically can still be ranked apart — recomputing the residue from the published numbers will not always reproduce it. Status bands are half-open — [from, to) — except the top band, whose upper bound is inclusive so progress of exactly 1.0 has a status. A key result carries at most one note, so where several apply the clamp is reported, then the direction mismatch, then the ignored weight. An enum value — weightMode, type, direction — is stripped of surrounding whitespace and upper-cased before it is matched, so ' weighted ' is WEIGHTED; ids are never folded. Failures: OKR_KEY_RESULTS_INVALID means a key result, a band or a flag cannot be read — no key results, a MILESTONE that is not 0 or 1, a repeated band id, a scale outside 0 to 12, or an unaccepted property. OKR_KEY_RESULT_ID_DUPLICATE means an id appears twice. OKR_TYPE_UNSUPPORTED means type is missing or not one of the three. OKR_RANGE_INVALID means a METRIC has an equal start and target, so there is no distance to measure. OKR_PERCENT_OUT_OF_RANGE means a PERCENT key result is outside 0 to 1. OKR_WEIGHTS_INVALID means a weight cannot be read, is negative — refused in either weight mode — or they all add to zero. OKR_BANDS_GAP and OKR_BANDS_OVERLAP mean the band ladder is not contiguous, or does not cover the progress that came out — which is what happens if you turn clamping off and leave the default ladder, since it stops at 1.0. OKR_LIMIT_EXCEEDED means more than 200 key results or 10 bands. None is worth retrying unchanged.

Context footprint

One line per key result plus the resolved bands, so the response tracks the size of your objective and nothing else. 200 key results is roughly 30 KB. A successful call costs 1 execution credit.

When to use it

When you have the current figures and need one progress number, one percentage and one status, computed the same way for everybody.

When the figures still have to be gathered, or when what you actually need is a score or a ranking rather than progress toward targets.

Scope

Note
Turn a set of key results into one objective progress figure, a percentage and a status band, with every contribution shown and reconciled.

Non-goals

  • Does not write the objectives or the key results, and does not judge whether they are good ones
  • Does not read a metric from anywhere: current is a number you send
  • Does not forecast, and does not compare this quarter with the last one
  • Does not store progress or notify anybody about it

Properties

IdempotentRetry safeStreaming supportedFile input supportedRequires external service

Capabilities

math

Cost & billing

Flat rate per invocation

MetercallsBillable onsuccessCost per call1 credit

File handling

Accepts file_refProduces file_refMax input1 MB

How we handle your data

Sensitive input, safe output
What this means

This skill may receive sensitive content, but it does not target sensitive data and returns only a structured non-sensitive result.

Squillify guarantees
  • Runs inside Squillify
  • This skill runs inside Squillify.
  • Payloads are not sent to an external service by this skill.
  • Squillify does not permanently store submitted payloads.
  • Telemetry records metadata only, not submitted content.
  • Submitted payloads are not used for training.
  • Submitted payloads are not reviewed by humans.
  • This skill does not target sensitive data.
  • The response does not contain submitted content.
Your responsibility

Squillify does not determine whether submitted content is sensitive. Your application is responsible for deciding what data it is allowed to submit. Your application is responsible for securing the response.

Read the Microskill Data Handling Policy

Input schema

FieldTypeRequiredDescription
keyResultsarrayYesBounded array of 1 through 200 closed key results with unique ids.
objectiveobjectNoClosed optional objective settings; omit the object for EQUAL weighting and no objectiveId.
statusBandsarrayNoBounded array of 1 through 10 contiguous bands; omit it for the default AT_RISK/BEHIND/ON_TRACK ladder.
clampProgressbooleanNoWhether each key result is clamped to 0 through 1; omit it for true.
scaleintegerNoPublished decimal places; omit it for 4.

Output schema

FieldTypeRequiredDescription
progressnumberYesWeighted objective progress, above 1 only when clamping is off.
progressPercentnumberYesThe same progress times 100 at two decimal places.
statusstringYesId of the status band the progress falls in.
statusLabelstringNoBand label, present only when the band carries one.
keyResultsarrayYesBounded array of 1 through 200 computed key results in request order.
weightModestringYesWeight mode actually applied.
statusBandsarrayYesBounded array of 1 through 10 status bands actually applied, including defaults.
objectiveIdstringNoEchoed objective.id, present only when you supplied one.

API contract

POST /v1/execute
curl
curl -sS -X POST "$BASE/v1/execute" -H "Authorization: Bearer $KEY" -H 'Content-Type: application/json' -d '{"skillKey":"business.okr_progress","input":{"keyResults":[{"id":"kr1","type":"METRIC","start":0,"target":100,"current":40}]}}'
Integrations
TypeScript SDK
import { Squillify } from "@squillify/sdk";

const sq = new Squillify({ apiKey: process.env.SQUILLIFY_API_KEY });

const { output } = await sq.execute("business.okr_progress", {"keyResults":[{"id":"kr1","type":"METRIC","start":0,"target":100,"current":40},{"current":1,"type":"MILESTONE","id":"kr2"},{"current":0.25,"type":"PERCENT","id":"kr3"}],"objective":{"id":"OBJ-1"}});

Errors

CodeHTTPRetryableDescription
VALIDATION_ERROR400NoThe request payload failed schema validation.
UNAUTHORIZED401NoMissing or invalid API key.
SKILL_NOT_ACCESSIBLE403NoThe API key is not granted access to this skill.
SKILL_NOT_AVAILABLE_FOR_PLAN403NoThis skill is not included in your current plan.
PLAN_ELIGIBILITY_UNAVAILABLE503YesPlan eligibility could not be decided. Retry after backoff.
SKILL_NOT_FOUND404NoThe requested skill or version does not exist.
TENANT_RATE_LIMITED429YesRequest rate limit exceeded. Retry after backoff.
QUOTA_EXCEEDED429YesPlan quota exhausted. Retryable once the quota period resets.
EXECUTION_TIMEOUT504YesExecution exceeded the allowed time budget.
OKR_KEY_RESULTS_INVALID400NoThe request payload failed this skill's input validation.
OKR_KEY_RESULT_ID_DUPLICATE400NoThe request payload failed this skill's input validation.
OKR_TYPE_UNSUPPORTED400NoThe request payload failed this skill's input validation.
OKR_RANGE_INVALID400NoThe request payload failed this skill's input validation.
OKR_PERCENT_OUT_OF_RANGE400NoThe request payload failed this skill's input validation.
OKR_WEIGHTS_INVALID400NoThe request payload failed this skill's input validation.
OKR_BANDS_GAP400NoThe request payload failed this skill's input validation.
OKR_BANDS_OVERLAP400NoThe request payload failed this skill's input validation.
OKR_LIMIT_EXCEEDED429NoA size, quota, rate, or time limit was exceeded.

Other limits

LimitValue
Recommended inline bytes32 KiB

Limitations

  • Does not write the objectives or the key results, and does not judge whether they are good ones
  • Does not read a metric from anywhere: current is a number you send
  • Does not forecast, and does not compare this quarter with the last one
  • Does not store progress or notify anybody about it
  • Deriving current from a data source — fetch it first, then send it
  • Scoring or ranking things, which is business.priority_score and business.decision_matrix
  • Objectives with more than 200 key results, which is a planning problem

How to call

POST /v1/execute (requires authentication)

Request bodyjson
{
  "skillKey": "business.okr_progress",
  "input": {
    "keyResults": [
      {
        "id": "kr1",
        "type": "METRIC",
        "start": 0,
        "target": 100,
        "current": 40
      },
      {
        "current": 1,
        "type": "MILESTONE",
        "id": "kr2"
      },
      {
        "current": 0.25,
        "type": "PERCENT",
        "id": "kr3"
      }
    ],
    "objective": {
      "id": "OBJ-1"
    }
  }
}

Examples

One objective, three key results of different types, counted equally

Inputjson
{
  "keyResults": [
    {
      "id": "kr1",
      "type": "METRIC",
      "start": 0,
      "target": 100,
      "current": 40
    },
    {
      "current": 1,
      "type": "MILESTONE",
      "id": "kr2"
    },
    {
      "current": 0.25,
      "type": "PERCENT",
      "id": "kr3"
    }
  ],
  "objective": {
    "id": "OBJ-1"
  }
}
Expected outputjson
{
  "progressPercent": 55,
  "weightMode": "EQUAL",
  "statusBands": [
    {
      "id": "AT_RISK",
      "label": "At risk",
      "from": 0,
      "to": 0.4
    },
    {
      "id": "BEHIND",
      "label": "Behind",
      "from": 0.4,
      "to": 0.7
    },
    {
      "id": "ON_TRACK",
      "label": "On track",
      "from": 0.7,
      "to": 1
    }
  ],
  "progress": 0.55,
  "status": "BEHIND",
  "statusLabel": "Behind",
  "objectiveId": "OBJ-1",
  "keyResults": [
    {
      "id": "kr1",
      "type": "METRIC",
      "progress": 0.4,
      "weight": 1,
      "normalizedWeight": 0.3333,
      "contribution": 0.1333,
      "clamped": false
    },
    {
      "id": "kr2",
      "type": "MILESTONE",
      "progress": 1,
      "weight": 1,
      "normalizedWeight": 0.3333,
      "contribution": 0.3333,
      "clamped": false
    },
    {
      "id": "kr3",
      "type": "PERCENT",
      "progress": 0.25,
      "weight": 1,
      "normalizedWeight": 0.3333,
      "contribution": 0.0834,
      "clamped": false
    }
  ]
}

Weighted mode with a metric that must come down, overshooting its target and clamped

Inputjson
{
  "keyResults": [
    {
      "id": "churn",
      "type": "METRIC",
      "start": 5,
      "target": 3,
      "current": 2.5,
      "weight": 3,
      "direction": "DECREASE"
    },
    {
      "id": "nps",
      "type": "METRIC",
      "start": 30,
      "target": 50,
      "current": 35,
      "weight": 1
    }
  ],
  "objective": {
    "id": "OBJ-CHURN",
    "weightMode": "WEIGHTED"
  }
}
Expected outputjson
{
  "progressPercent": 81.25,
  "weightMode": "WEIGHTED",
  "statusBands": [
    {
      "id": "AT_RISK",
      "label": "At risk",
      "from": 0,
      "to": 0.4
    },
    {
      "id": "BEHIND",
      "label": "Behind",
      "from": 0.4,
      "to": 0.7
    },
    {
      "id": "ON_TRACK",
      "label": "On track",
      "from": 0.7,
      "to": 1
    }
  ],
  "progress": 0.8125,
  "status": "ON_TRACK",
  "statusLabel": "On track",
  "objectiveId": "OBJ-CHURN",
  "keyResults": [
    {
      "id": "churn",
      "type": "METRIC",
      "progress": 1,
      "weight": 3,
      "normalizedWeight": 0.75,
      "contribution": 0.75,
      "clamped": true,
      "note": "CLAMPED_HIGH"
    },
    {
      "id": "nps",
      "type": "METRIC",
      "progress": 0.25,
      "weight": 1,
      "normalizedWeight": 0.25,
      "contribution": 0.0625,
      "clamped": false
    }
  ]
}
  • Compute objective progress the same way for every team, from figures they already have
  • Mix a metric, a milestone and an already-percentage key result under one objective
  • Weight a key result that carries most of the objective without hiding the arithmetic
  • Publish the status band and its boundaries together, so nobody argues about 'behind'

Avoid for

  • Deriving current from a data source — fetch it first, then send it
  • Scoring or ranking things, which is business.priority_score and business.decision_matrix
  • Objectives with more than 200 key results, which is a planning problem

Composable with

Safety & privacy

  • Runs without calling any external service.
  • Your payloads are never stored permanently.
  • Telemetry captures metadata only — never your payload contents.
  • We never train models on your payloads.
  • No human reviews your payloads.

Tags

analysisprogressokrbusiness