PPactDocs
Team & Org

OKRs

Cascading objectives and key results in Pact — with workstreams, tasks, and key results that sync their progress from live CRM signals.

OKRs

Pact's OKR engine gives your workspace a four-level goal structure — objectives → key results → workstreams → tasks — with two features that make it more than a spreadsheet: objectives cascade (a child objective rolls up to a parent), and key results can auto-sync their current value from live CRM signals instead of being hand-updated.

Live

Objectives, key results, workstreams, and tasks are backed by real tables (objectives, key_results, workstreams, workstream_tasks — alembic 0078) and the /v1/okrs API in api/routes/okrs.py. Progress and signal resolution live in core/okrs/.

The four levels

LevelTablePurpose
ObjectiveobjectivesA qualitative goal with an owner, a period (period_start/period_end), and an optional parent_objective_id for cascading.
Key resultkey_resultsA measurable outcome with target_value, starting_value, current_value, unit, and a signal_source.
WorkstreamworkstreamsA body of work under an objective, with its own owner and status.
Taskworkstream_tasksA concrete to-do under a workstream, optionally with a due_at.

Working with objectives

  • POST /v1/okrs/objectives — create an objective (optionally set parent_objective_id to cascade it under another).
  • PATCH /v1/okrs/objectives/{id} — update title, description, period, or status.
  • GET /v1/okrs/objectives / GET /v1/okrs/objectives/{id} — list or fetch, with caller-scoped visibility.
  • GET /v1/okrs/cascade — the objective tree, resolved through parent_objective_id edges so a manager can see how their team's objectives ladder up.

Key results, workstreams, and tasks have their own create/patch endpoints under the same /v1/okrs prefix.

Key results that sync themselves

Every key result declares a signal_source. manual means you update current_value yourself. The other sources pull a live number from the platform when you call POST /v1/okrs/key-results/{id}/refresh:

signal_sourceResolves to
manualThe stored current_value, unchanged.
pipeline_valueSum of in-flight opportunities.value for the owner.
win_rateWon / total opportunity rate for the owner.
account_healthOwner's account-health signal.
sequence_engagementSequence engagement for the owner.
custom_queryA tenant-defined query carried in signal_query_json.

The value is resolved by core/okrs/signal_resolver.py, and the refresh (core/okrs/progress_engine.py's refresh_kr) writes the new number and stamps last_synced_at. An unrecognized signal source is logged and leaves the value untouched rather than failing the request.

Cascade is validated

core/okrs/cascade_validator.py keeps the objective tree acyclic — an objective can't become its own ancestor — so roll-ups always terminate.