---
name: lr-qrm
description: >-
  Use this skill when working with lr-qrm, a Python CLI and client library for
  the Qestit QRM database. Activate when the user mentions QRM, Unit Under Test
  (UUT) status, production box management, or the qrm CLI command. Covers:
  authentication (qrm login); service health (qrm status); UUT test history
  (uut status); production box lifecycle (box list, get, create, update, delete);
  box-item management (box add-item, remove-item, find); and skill management
  (--install-skill, --show-skill). Key entities: UUTs (serial numbers like
  326115020010F2F1), production boxes (identifiers like BOX-12345), bearer tokens
  stored in login.json. Covers both CLI usage and programmatic Python API.
---

# lr-qrm

`lr-qrm` is a CLI and Python library for interacting with the Qestit QRM
(Quality Resource Management) service used at LumenRadio. It provides
authentication, health checking, UUT test history queries, and production box
management.

Package: `lr-qrm` · CLI: `qrm` · Python: `import qrm`

**Local install**: `lr-qrm` is typically installed in a project-local `.venv`.
Always prefer `.venv/bin/qrm` over a system-wide `qrm` when a `.venv` directory
exists in the working directory.

## Authentication

```bash
qrm login --base-url https://qrm.example.com/qrmapi
```

Prompts for username and password (or reads `QRM_USERNAME` / `QRM_PASSWORD`
environment variables). Stores session details at:

```
~/.config/qrm/login.json
```

Override the config location with `--config-path` or the `QRM_CONFIG` env var.
Pass `--insecure` to skip TLS verification.

Non-interactive (CI/CD):

```bash
export QRM_USERNAME="user"
export QRM_PASSWORD="pass"
qrm login --base-url https://qrm.example.com/qrmapi
```

## Commands

### Service health

```bash
qrm status
```

Queries `/Result/Status`. Supports `--output json` for machine-readable output.


### Query production test results (read-only SQL)

#### Results by date range

```bash
qrm query results --start "2026-03-01T00:00:00Z" --stop "2026-03-01T23:59:59Z"
qrm query results --output json
```

Retrieves all production test results in the given date range (defaults to today if not specified). Output is a rich table by default, or JSON with `--output json`. References (operator, station, test program, etc.) are resolved for readability.

#### Results by serial number

```bash
qrm query serial 326115020010F2F1
qrm query serial 326115020010F2F1 --output json
```

Retrieves all test results for a given serial number. Output is a rich table by default, or JSON with `--output json`. References are resolved.

All `query` subcommands are read-only and accept global options like `--config-path`, `--base-url`, `--insecure`, and `--output`.

---

### UUT (Unit Under Test) history

```bash
qrm uut status 326115020010F2F1
qrm uut status 326115020010F2F1 --start "2026-02-01T00:00:00Z" --stop "2026-02-06T23:59:00Z"
qrm uut status 326115020010F2F1 --max-results 500 --output json
```

Queries `/Uut/Status`. Timestamps are UTC ISO-8601 or `YYYY-MM-DD HH:MM:SS`.
Default start: `1970-01-01T00:00:00Z`. Default stop: QRM current time.
Default max-results: 1000 (range 1–5000).

### Production box management

#### List boxes

```bash
qrm box list
qrm box list --start "2026-01-01T00:00:00Z" --stop "2026-02-06T23:59:00Z"
qrm box list --output json
```

Queries `/Box/List`.

#### Get box details

```bash
qrm box get BOX-12345
qrm box get BOX-12345 --output json
```

Fetches box metadata from `/Box/Get/{id}` and its UUT contents from
`/Box/Content/{id}`.

#### Create or update a box

```bash
qrm box create BOX-12345 \
  --started "2026-02-01T00:00:00Z" \
  --finished "2026-02-06T23:59:00Z" \
  --units 10 \
  --shipped
```

Posts to `/Box/Add`. Idempotent: creates if absent, updates if present.

#### Update an existing box

```bash
qrm box update BOX-12345 --units 12 --finished "2026-02-07T12:00:00Z"
```

Same endpoint as `create`; passes only the fields provided.

#### Delete a box

```bash
qrm box delete BOX-12345
```

Posts to `/Box/Remove/{id}`. Idempotent (404 treated as success).

#### Add an item to a box

```bash
qrm box add-item BOX-12345 --serial-number "326115020010F2F1"
qrm box add-item BOX-12345 --type-name "LR5110" --tag "CUSTOM-TAG"
```

Posts to `/Box/Uut/Add/{boxId}`. Identify UUT by `--serial-number`,
`--type-name`, or `--tag`.

#### Remove an item from a box

```bash
qrm box remove-item BOX-12345 --serial-number "326115020010F2F1"
```

Posts to `/Box/Uut/Remove/{boxId}`.

#### Find which box contains a UUT

```bash
qrm box find --serial-number "326115020010F2F1"
qrm box find --type-name "LR5110"
```

Posts to `/Box/Find`.

### Global options

Every command accepts:

- `--config-path PATH` — override location of `login.json`
- `--base-url URL` — override the stored base URL
- `--insecure` — disable TLS verification
- `--output rich|json` — output format (default: rich)

## Skill management

```bash
qrm --install-skill   # install to ~/.claude/skills/, .github/instructions/, .vscode/
qrm --show-skill      # print this file to stdout
```

## Programmatic Python API

```python
from qrm.config import load_login_state
from qrm.client import QrmClient

state = load_login_state()
client = QrmClient(base_url=str(state.base_url), verify_tls=state.verify_tls)

# Service health
health = client.status(token=state.token)

# UUT history
runs = client.uut_status(
    token=state.token,
    serial_number="326115020010F2F1",
    start_datetime="2026-02-01T00:00:00Z",
    stop_datetime="2026-02-06T23:59:00Z",
    max_results=1000,
)

# Box operations
boxes = client.box_list(token=state.token, start_datetime="1970-01-01T00:00:00Z")
box = client.box_get(token=state.token, box_identifier="BOX-12345")
content = client.box_content(token=state.token, box_identifier="BOX-12345")

new_box = client.box_add(
    token=state.token,
    box_identifier="BOX-12345",
    started_datetime="2026-02-01T00:00:00Z",
    finished_datetime="2026-02-06T23:59:00Z",
    number_of_units=10,
    shipped=True,
)

client.box_remove(token=state.token, box_identifier="BOX-12345")

client.box_uut_add(
    token=state.token,
    box_identifier="BOX-12345",
    uut_serial_number="326115020010F2F1",
)

client.box_uut_remove(
    token=state.token,
    box_identifier="BOX-12345",
    uut_serial_number="326115020010F2F1",
)

found = client.box_find(token=state.token, uut_serial_number="326115020010F2F1")
```

## Data models

### LoginState (stored in `~/.config/qrm/login.json`)

| Field        | Type     | Description                     |
|--------------|----------|---------------------------------|
| `token`      | str      | Bearer token from `/User/Login` |
| `username`   | str      | Authenticated username          |
| `base_url`   | HttpUrl  | QRM API base URL                |
| `verify_tls` | bool     | Whether TLS is verified         |
| `stored_at`  | datetime | When the token was stored       |

### UUT status entry (returned by `uut_status`)

Fields depend on QRM server version. Typical fields: `serialNumber`,
`startDateTime`, `stopDateTime`, `result`, `typeName`.

### Box (returned by `box_get`, `box_list`)

Typical fields: `boxIdentifier`, `startedDateTime`, `finishedDateTime`,
`numberOfUnits`, `shipped`.

## Common workflows

### Check whether a unit passed test in a date range

```bash
qrm uut status 326115020010F2F1 \
  --start "2026-02-01T00:00:00Z" \
  --stop "2026-02-28T23:59:59Z" \
  --output json | python3 -c "
import json, sys
data = json.load(sys.stdin)
passed = [r for r in (data if isinstance(data, list) else []) if r.get('result') == 'Pass']
print(f'{len(passed)} passing runs')
"
```

### Create a box and populate it

```bash
qrm box create BATCH-2026-001 \
  --started "2026-03-01T08:00:00Z" \
  --units 50

for sn in 326115020010F2F1 326115020010F2F2 326115020010F2F3; do
  qrm box add-item BATCH-2026-001 --serial-number "$sn"
done

qrm box update BATCH-2026-001 --finished "2026-03-01T17:00:00Z" --shipped
```

### Export all boxes to JSON

```bash
qrm box list --output json > boxes.json
```

## Tips and gotchas

- The token stored in `login.json` expires. Re-run `qrm login` when you get
  HTTP 401 errors.
- `--insecure` disables TLS and suppresses urllib3 warnings.
- Timestamps must be UTC. The CLI accepts both `2026-02-01T00:00:00Z` and
  `2026-02-01 00:00:00` formats.
- `box create` and `box update` both call the same `/Box/Add` endpoint
  (idempotent upsert).
- `box delete` is idempotent: a 404 response is treated as success.
- Set `QRM_DEBUG=1` to print raw HTTP request/response details to stderr.
- Config path resolution order: `--config-path` flag → `QRM_CONFIG` env var →
  `~/.config/qrm/login.json`.
