> ## Documentation Index
> Fetch the complete documentation index at: https://docs.cadenzalabs.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Progress-guided governor

> Feed megan-tk a live progress signal and it returns a corrective action-frame rotation — on-device recovery from an execution shift, no labels, no retraining.

A **governor** is megan-tk repairing a policy that has stopped making progress
because of an **execution-level shift a feed-forward model can't self-correct** —
the classic case is an **actuator miscalibration**: every commanded direction is
applied *rotated* by a fixed unknown angle (miswired motors, a mounting offset, a
steady cross-wind). You feed it a scalar **progress** signal each control step, and
it hands back a correction **`phi`** — an action-frame rotation to apply to your
action's `(x, y)`.

This is how Megan does
[**learning to fix itself on-device**](/megantk/introduction#the-four-ways-a-robot-self-learns):
the whole bandit + gating runs **server-side as the real `megantk`**; your client
only ships the latest progress number and applies the returned `phi`.

## How it works

On a **stall** (progress flat for `patience` steps) the governor opens a **trial**:
it applies a candidate rotation, measures the **real progress gain** over an
evaluation window, credits that candidate, and keeps the best. The learned
correction is imposed at the start of every episode and **carried across
episodes** — competence accumulates with no labels and no retraining. When it finds
the rotation that cancels the shift, progress resumes.

## Opening a governor

```python theme={null}
from cadenzalabs import MeganTK

tk = MeganTK()

g = tk.governor(
    n_candidates=9,     # candidate action-frame rotations it searches
    patience=6,         # flat-progress steps before it adapts
    eval_window=6,      # steps it scores each trial over
)
print(g.governor_id)
```

`governor()` returns a `Governor`. It's a **context manager** — prefer `with` so it
is always closed on the server:

```python theme={null}
with tk.governor() as g:
    ...
# governor deleted automatically here
```

## The loop — `episode_start` + `step`

Call `episode_start()` once per episode (it imposes the correction learned so far),
then each control step send the live progress and apply the returned `phi`.

```python theme={null}
g.episode_start()
for step in episode:
    phi = g.step(progress_now)          # radians; the correction to apply THIS step
    env.step(rotate_xy(action, phi))    # rotate your action's (x, y) by phi
```

`step()` returns the rotation directly. Use `step_full()` if you also want whether a
trial is currently being scored:

```python theme={null}
r = g.step_full(progress_now)
r["phi"]            # float, radians
r["adapting"]       # True while a correction trial's window is open
r["governor_step"]  # monotonically increasing step counter
```

<Note>
  The governor uses only the **step-to-step gains** of `progress`, so its **scale and
  offset don't matter** — any signal that *rises as the task improves* works (e.g.
  `-distance_to_goal`, a task reward, or `-error`). You never tell it the shift angle;
  it discovers the correction purely from whether progress improves.
</Note>

## Inspecting and closing

```python theme={null}
g.status()   # {'governor_id', 'governor_step', 'best_phi', 'adapting'}
g.close()    # delete server-side; returns True (idempotent — second call False)
```

`best_phi` is the correction it has learned (carried across episodes). The server
holds the live governor object, so **close governors you're done with** — the `with`
form does this for you.

## A full recovery loop

```python theme={null}
from cadenzalabs import MeganTK

def rotate_xy(a, theta):
    import math
    c, s = math.cos(theta), math.sin(theta)
    a = list(a); a[0], a[1] = c*a[0] - s*a[1], s*a[0] + c*a[1]
    return a

tk = MeganTK()
with tk.governor() as g:
    for episode in episodes:                 # many episodes -> it learns phi
        g.episode_start()
        for obs in episode:                  # your sensor/control loop
            action = policy(obs)             # your frozen VLA / policy
            phi = g.step(progress(obs))      # progress = e.g. -distance_to_goal
            env.step(rotate_xy(action, phi))
    print("learned correction:", g.status()["best_phi"], "rad")
```

Over the episodes, `best_phi` converges on the rotation that cancels the shift and
the policy — untouched, frozen — starts completing the task again.

## Next

<CardGroup cols={2}>
  <Card title="Token sessions" icon="diagram-project" href="/megantk/sessions">
    Milestone / perception governance — continue vs. adapt.
  </Card>

  <Card title="SDK reference" icon="book" href="/megantk/sdk-reference">
    Every method, argument, and return type.
  </Card>
</CardGroup>
