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

# ci.mark

> Attach a named value to the innermost open scope.

Attach a named scalar value to the innermost open scope on the current
thread. Marks are how you log per-step loss, grad norm, learning rate,
accuracy: anything that should appear alongside the span in the
viewer.

## Signature

```python theme={null}
def mark(
    name: str,
    value: float | int | str | bool,
    *,
    kind: Literal["point", "summary"] = "point",
    **attrs,
) -> None
```

## Parameters

| Name      | Type                          | Default   | Purpose                                      |
| --------- | ----------------------------- | --------- | -------------------------------------------- |
| `name`    | `str`                         | -         | Mark name                                    |
| `value`   | `float \| int \| str \| bool` | -         | Scalar value                                 |
| `kind`    | `"point" \| "summary"`        | `"point"` | See [Kinds](#kinds) below                    |
| `**attrs` | any                           | -         | Extra attributes attached to the mark record |

Values coerce to float64 / int / string (256-byte cap) / bool. Complex
types (tensors, arrays) should use [snapshots](/sdk/reference/profile#snapshot-modes),
not marks.

## Kinds

| `kind=`     | Use for                                                                              | Viewer rendering               |
| ----------- | ------------------------------------------------------------------------------------ | ------------------------------ |
| `"point"`   | Time-series data recorded while the span is open (per-step loss, per-batch accuracy) | Chart                          |
| `"summary"` | A single canonical end-of-span value (epoch-final loss)                              | One value attached to the span |

## Attachment

The mark attaches to **the innermost open scope on the calling thread**
at the moment `mark()` is called:

* If a scope is open → the mark is recorded against that span.
* If no scope is open → the mark attaches to the `cirron.session`
  root opened by `ci.profile()`.
* If `ci.profile()` was never called (pre-profile imports, tests that
  don't open a session) → the mark falls through to the legacy
  `"root"` sentinel rather than a real span ID.

## Examples

### Point marks

```python theme={null}
for batch in loader:
    loss = train_step(batch)
    ci.mark("loss", loss.item())
    ci.mark("grad_norm", compute_grad_norm(model))
    ci.mark("learning_rate", scheduler.get_last_lr()[0])
```

### Summary marks

```python theme={null}
for epoch in ci.epochs(range(20)):
    train_one_epoch()
    val_acc = evaluate()
    ci.mark("epoch_accuracy", val_acc, kind="summary")
```

### With extra attributes

```python theme={null}
ci.mark("loss", loss.item(), step=global_step, phase="warmup")
```

## Buffer behavior

Marks are written to a lock-free per-thread ring buffer with a
default capacity of **64 k marks** (not bytes). When full, the oldest
mark is dropped and a drop counter is incremented.

Every thread has its own buffer, so the counter is per-thread; the
`mark_drop_count` field returned by `ci.health()` is the **sum across
all threads**. Drop counts also surface on the dashboard.

`ci.mark` is \~3.7 μs per call on x86\_64 and \~2.4 μs on arm64.
The buffer append is inlined into `mark()` itself. The hot path
never enters `MarkBuffer.append` or walks through a property
dispatch. Mark ids are generated via `os.urandom(16).hex()`, which
produces a globally unique 32-char hex value on each call; the
platform uses this id as the primary key for the mark row, so
uniqueness across concurrent runs matters.

## Related

<CardGroup cols={2}>
  <Card title="ci.scope" icon="brackets-curly" href="/sdk/reference/scope">
    Open the scope that `ci.mark` will attach to.
  </Card>

  <Card title="Mark schema" icon="code" href="/sdk/schemas#marks">
    How marks are serialized in the spool.
  </Card>
</CardGroup>
