## What tags are

**Tags are governed, hereditary metadata.** A tag records a compliance-relevant fact about an artifact — its license, whether it contains PII, its data-residency jurisdiction — and then *flows downstream on its own*. Tag a raw dataset `contains_pii=present`, and every model, checkpoint, and export derived from it inherits that tag through the lineage graph, without you re-stating it at each step.

That inheritance is the whole point. [Labels](/docs/labels) are freeform metadata *you* attach and maintain. Tags are a small, governed vocabulary that `roar` **propagates for you** — so a compliance fact established once at the source can't quietly fall off the record three transforms later.

Tags live under the reserved `tag.*` namespace inside the same versioned label document, but they are not freeform: they carry per-value **provenance** (who set each value, and which job it came from) and a **bind ledger** that controls how far they travel. You interact with them through the dedicated `roar tag` verb, never through `roar label`.

> **Availability.** Tags are a `roar` 0.4.0 feature.

## Quick use

```bash
roar tag add contains_pii=present   raw.csv        # tag an artifact by path or hash
roar tag add license=Apache-2.0     @2            # tag the output(s) of job step 2
roar tag show      model.pkl                       # show current tags
roar tag history   model.pkl                       # every revision, append-only
roar tag why       contains_pii  model.pkl         # explain how it got this tag
roar tag rm        license=Apache-2.0  @2          # remove one value
roar tag rm        license             @2          # remove the whole kind
roar tag bind      model.pkl                        # promote its tags across sessions
roar tag unbind    model.pkl                        # revoke that promotion
```

Targets are the same everywhere: `@N` is job step *N* in the active session; a path or hash prefix is an artifact.

## Canonical kinds

Tags are restricted to a small canonical vocabulary, because a typo'd kind (`licence`, `pii_present`) is a compliance fact that silently doesn't propagate and doesn't match a search. The built-in kinds are:

| Kind | Meaning |
|---|---|
| `license` | Distribution license of the artifact |
| `contains_pii` | Whether the artifact contains personal data |
| `jurisdiction` | Data-residency / legal jurisdiction |
| `classification` | Sensitivity classification |
| `special_category` | GDPR Art. 9 / special-category data |

`roar tag add` rejects an unknown kind by default and tells you exactly how to allow it. If your organization has its own vocabulary, opt in per-project with a `.roarconfig`:

```toml
[tags]
custom_kinds = ["export_control", "retention_class"]
```

Custom kinds are additive — they extend the canonical set rather than replace it, and the append-aware error message spells out the two lines to add so you don't have to guess the format.

## Heredity: how tags propagate

Within a session, tags propagate **automatically** from a job's inputs to its outputs. Run a training job whose input dataset is tagged `contains_pii=present`, and the model it produces is tagged `contains_pii=present` too — marked as **inherited** (`origin=system`) and stamped with the producing job, so the record shows the value wasn't a fresh human assertion but a consequence of the lineage.

Each value therefore carries where it came from:

- **`origin=user`** — a human act: a bare `roar tag add`, or `roar run --add-tag`.
- **`origin=system`** — inherited via propagation at job-record time, annotated with the job that carried it.

Propagation is deliberately over-approximate within a session (a false positive is cheap and safe; a false negative is a compliance miss). It is a **union**: an artifact accumulates every value it inherits, and `roar tag rm` never rewrites history — it just stops a value from propagating forward.

## Crossing sessions: bind

Automatic propagation stops at the session boundary. Crossing it requires an explicit **bind** — a human act naming the concrete artifact whose tags are of record. This is what stops a stray 0-byte or scratch artifact from one project *infecting* everyone else's work: tags don't leak across sessions, users, or organizations unless someone deliberately promotes them.

- `roar tag add` on a **named artifact** is *born bound* — naming a concrete, inspectable artifact is itself the human act, so it writes an implicit bind covering exactly the value you added.
- `roar run --add-tag` stays **session-scoped** until you bind the artifact — it stamps the output within the run but doesn't promise anything cross-session.
- `roar tag bind <artifact>` promotes an artifact's *current* tag set to cross-session scope; `roar tag unbind` revokes it.
- `roar register --bind` / `--no-bind` controls whether registering to GLaaS promotes the artifact's tags.

The bind ledger is **append-only**: `bind` and `unbind` write events, they never delete. One `unbind` heals the whole downstream cone — everything that inherited through a revoked bind becomes mechanically identifiable as superseded. `roar tag bind` echoes what it promotes (path, size, and the tag set) so binding junk means reading past a line telling you the artifact is empty.

## Explaining a tag: `roar tag why`

Compliance is about *accountability*, so every inherited tag can be traced back to the human act that originated it:

```bash
roar tag why contains_pii model.pkl
# Why does model.pkl have contains_pii?
#   model.pkl  contains_pii=present  (inherited from job train.py)
#     └─ raw.csv  contains_pii=present  (tag add — user)
```

`roar tag why` walks the inheritance path back to the originating `tag add` / `run --add-tag`, annotating any cross-session hop with the explicit bind that authorized it. It explains an *artifact's* tag; point it at an artifact (by path or hash), not a job step.

## Barriers on `roar run`

Sometimes inheritance shouldn't happen — a step genuinely strips PII, or produces a differently-licensed output. Two run modifiers shape what a job records:

```bash
roar run --block-tag contains_pii        -- python anonymize.py   # stop a kind from inheriting
roar run --block-tag license=GPL-3.0     -- python build.py       # stop one value
roar run --add-tag  contains_pii=absent  -- python anonymize.py   # assert a value on the outputs
```

`--block-tag KIND` blocks a whole kind from propagating into this job's outputs; `--block-tag KIND=VALUE` blocks a single value; `--add-tag KIND=VALUE` stamps a value.

These modifiers are **recorded on the job and replayed by `roar reproduce`**. A barrier is a property of the job, not a flag you have to remember — so reproducing a pipeline reproduces the *tag layer* faithfully, instead of silently re-inheriting a tag you had deliberately blocked.

## Seeing tags

Both `roar show` and `roar tag show` render tags the same way — a clean **Tags** section listing `kind: value1, value2`, with the internal bind ledger hidden. Jobs additionally show a **Barriers** section when they carried `--block-tag` modifiers. `roar tag history` shows every version of the tag document, append-only.

## Visibility

Tags follow the [scope](/docs/scopes) of the artifact they're attached to, exactly like labels:

| Scope of the artifact | Tag readable to … |
|---|---|
| `anonymous` | public |
| `public` | public |
| `private` | you only |
| `<owner>/<project>` | project members only |

A tag on a private artifact is invisible outside that scope — GLaaS never returns a tag to someone who can't already read the artifact it's on. There is no way to make an artifact private but its tags public; the tag inherits the artifact's visibility.

## Tags on glaas.ai

When you `roar register` a tagged artifact, its tags travel with it. On glaas.ai the artifact's record carries its tags — following the scope rules above — so the license and PII status you established at the source are visible on the registered artifact for anyone entitled to see it. This is what turns a private, per-repo convention into an organization-wide compliance record, and feeds the [audit / AI-BOM](/docs/audit) view of what a model is actually built from.

## Where to look next

- [Labels](/docs/labels) — freeform metadata; tags are the governed, hereditary subset under `tag.*`.
- [Scopes](/docs/scopes) — how tag visibility, and the bind boundary, follow scope.
- [Audit (AI-BOM)](/docs/audit) — tags as part of the bill of materials for a model.
- [roar Guide](/docs/roar-guide) — the full `roar tag` command reference.
