> ## 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.

# Building deep

> Wire megan-tk into a real control loop — the change-doer pattern, rate limits, error handling, and composition.

This page is for going past "hello world" — integrating megan-tk into a real
system. The mental model: **megan-tk decides, your change-doer acts.** The token
never touches actuators; it hands you a verdict, and your code owns the change.

## The change-doer pattern

Keep three responsibilities separate:

<Steps>
  <Step title="Sense">
    Your perception produces the state megan-tk needs — which milestones are
    reached, what's blocking, or raw position/goal/obstacles.
  </Step>

  <Step title="Govern (megan-tk)">
    `step` / `perceive` returns a `Decision`. This is the *only* thing megan-tk
    does — it does not move the robot.
  </Step>

  <Step title="Change (you)">
    On `adapt`, your **change-doer** intervenes at `frontier` — re-plan, switch
    skill, correct a trajectory. On `continue`, let the policy run.
  </Step>
</Steps>

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

tk = MeganTK()

with tk.session("assemble part", ["align", "insert", "fasten"]) as gov:
    while not done():
        obs = sense()
        d = gov.step(reached=reached_from(obs), obstacles=blocking(obs))

        if d.adapt:
            change_doer.intervene(at=d.frontier)   # your logic owns the "how"
        else:
            policy.act(obs)                          # continue as planned
```

<Tip>
  Treat `frontier` as **where to focus**, not a command. It's the milestone label
  you chose; your change-doer maps it to an actual corrective behaviour.
</Tip>

## Server-held state and the control loop

The server holds the live token object — its knowledge graph **accumulates across
steps**. Two consequences:

* **One session per task attempt.** Open it once, step it every cycle, close it at
  the end. Don't open a fresh session per tick.
* **Close what you open.** Sessions and anticipators persist server-side until
  deleted. The `with` form guarantees cleanup; if you hold one by hand, close it in
  a `finally`.

```python theme={null}
s = tk.session("goal", ["a", "b"])
try:
    run_loop(s)
finally:
    s.close()
```

## Rate limits

megan-tk routes are **rate-limited per account** (default **120 requests/minute**).
This is deliberate — it keeps the decision boundary from being reconstructed by
mass probing. Design your loop to stay under it.

<Warning>
  Exceeding the limit returns **HTTP 429**, surfaced as `MeganTKError` with a
  `Retry-After` hint in the message. Don't hot-loop calls: for a fast control loop,
  call megan-tk on a **decimated cadence** (e.g. every N ticks, or only when sensed
  state changes) rather than every single tick.
</Warning>

```python theme={null}
STRIDE = 5                      # govern every 5th control tick
for i, obs in enumerate(loop()):
    if i % STRIDE == 0:
        last = gov.step(reached=reached_from(obs), obstacles=blocking(obs))
    apply(last)                 # reuse the last decision between calls
```

## Error handling

Two exception types, one narrower than the other:

| Catch          | For                                                                                                 |
| -------------- | --------------------------------------------------------------------------------------------------- |
| `AuthRequired` | Missing/invalid/revoked key, deactivated account (401/403). **Not** retryable — fix the credential. |
| `MeganTKError` | Everything else: bad input (422), rate limit (429), not found (404), unreachable API.               |

```python theme={null}
import time
from cadenza_cli import MeganTK, AuthRequired, MeganTKError

def govern(session, **kw):
    for attempt in range(3):
        try:
            return session.step(**kw)
        except AuthRequired:
            raise                                   # credential problem — stop
        except MeganTKError as e:
            if "rate limit" in str(e).lower() and attempt < 2:
                time.sleep(2 ** attempt)            # back off, then retry
                continue
            raise
```

<Note>
  A 422 means your **input** is wrong (an out-of-range milestone index, `perceive`
  on a non-perception session, an unregistered action name). These won't fix
  themselves on retry — correct the call.
</Note>

## Composing sessions and anticipators

The two primitives are independent and compose in a single loop: a **session**
governs *task progress* while an **anticipator** guards against a *periodic
disturbance*.

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

tk = MeganTK()

with tk.session("carry tray", ["lift", "traverse", "place"]) as gov, \
     tk.anticipator(actions=["brace", "steady"], min_events=2) as guard:

    for i, ev in enumerate(control_loop()):
        # 1) periodic-disturbance protection
        if ev.bump_detected:
            guard.disturbance(t=ev.t)
        p = guard.protect(t=ev.t)
        if p.should_protect and p.ready:
            saved = actuators.engage(p.best_action)
            guard.outcome(p.best_action, saved=saved)

        # 2) task governance (decimated to respect the rate limit)
        if i % 5 == 0:
            d = gov.step(reached=reached_from(ev), obstacles=blocking(ev))
            if d.adapt:
                change_doer.intervene(at=d.frontier)
```

## Becoming more efficient

Efficiency is Megan's **on-device self-improvement layer** — the third of the
[three ways a robot self-learns](/megantk/introduction#the-three-ways-a-robot-self-learns).
Once a task reliably succeeds, it makes that task **faster, one committed step at a
time**, without ever destabilising the outcome.

<Note>
  This layer runs on the robot as part of the change-doer — it is **not** a hosted
  API route you call each tick. It surfaces as your repeated task simply getting
  faster while staying stable. You observe it in your own outcome metrics (task
  duration trending down, the result staying within tolerance).
</Note>

How it self-improves a succeeding task:

<Steps>
  <Step title="Set a golden reference">
    The first, deliberately-slow run of the task settles into a reference outcome —
    the state that must stay stable (object positions, an end pose, a metric).
  </Step>

  <Step title="Change one thing per repetition">
    Each later rep nudges a single thing a little: speed up the **slowest** step, or
    drop a step your rule marks redundant.
  </Step>

  <Step title="Commit or revert">
    Keep the change only if the outcome still matches golden **within tolerance** and
    the rep was **faster**; otherwise revert and stop pushing that step. Learned
    speeds persist, so the robot keeps its gains across sessions.
  </Step>
</Steps>

The result: over repetitions the robot sheds the caution it needed while learning
and settles into a smooth, efficient routine — the metric to watch is **task
duration falling while stability holds**.

## Deployment checklist

<Steps>
  <Step title="Key via environment">
    Set `CADENZA_API_KEY` in the deploy environment; don't hard-code the token.
  </Step>

  <Step title="Reuse one client">
    Construct `MeganTK` once at startup and share it — it holds no live
    connection but does resolve the key at construction.
  </Step>

  <Step title="Bound your call rate">
    Decimate governance calls to stay under 120/min per account.
  </Step>

  <Step title="Handle both error types">
    Fail fast on `AuthRequired`; back off + retry transient `MeganTKError`.
  </Step>

  <Step title="Close sessions">
    Use `with`, or `close()` in `finally`, so server-side state doesn't leak.
  </Step>

  <Step title="Watch your usage">
    `cadenza usage` / `tk.usage()` to confirm call volume and catch runaway loops.
  </Step>
</Steps>

## Full example

A complete, runnable end-to-end script that exercises every route lives in the
repo at [`examples/megantk_e2e.py`](https://github.com/aparekh02/cadenza-cli/blob/main/examples/megantk_e2e.py),
and a compact quickstart at
[`examples/megantk_quickstart.py`](https://github.com/aparekh02/cadenza-cli/blob/main/examples/megantk_quickstart.py).

## Next

<CardGroup cols={2}>
  <Card title="SDK reference" icon="book" href="/megantk/sdk-reference">
    Every method and type in one place.
  </Card>

  <Card title="Usage & metering" icon="chart-line" href="/megantk/usage">
    Track what your loop is consuming.
  </Card>
</CardGroup>
