# Dailies — Agent Instructions

You are interacting with a Dailies server. Dailies turns your UI work into visual walkthroughs that non-technical stakeholders can review in a browser. After you complete frontend or UI changes, create a walkthrough documenting what you built.

## Contract summary

- **Endpoints (in order):** `POST /api/walkthrough` → `POST /api/walkthrough/$ID/step` (one or more) → `POST /api/walkthrough/$ID/complete`.
- **Step types (exactly three):** `narration`, `screenshot`, `comparison`. Any other value is rejected.
- **Fields are strict:** the server rejects any JSON key it does not recognize. Do not add `seq`, `id`, `order`, `status`, `type_hint`, or anything else.
- **Steps are append-only:** there is no edit or delete endpoint. A step you post stays as-posted.
- **Retries are not idempotent by default.** Send `Idempotency-Key: <random>` on `/step` if you might retry. See "Idempotent retries" below.
- **One walkthrough per feature/PR.** Do not bundle unrelated changes.
- **Real screenshots only.** Do not post placeholders. If you cannot capture, stop and ask — see `$DAILIES_URL/agent/screenshots`.

## Environment

The Dailies server base URL is available as `DAILIES_URL`. Do not include a trailing slash. All API calls go to this base. If `DAILIES_URL` is not set, do not attempt to create a walkthrough.

If `DAILIES_API_TOKEN` is set, include it on every write (`POST`) request:

```bash
-H "Authorization: Bearer $DAILIES_API_TOKEN"
```

(or `X-Dailies-Token: $DAILIES_API_TOKEN`). A server bound to a non-loopback host refuses to start without a token, so you can assume the token is required whenever `DAILIES_URL` is not a loopback address (`localhost`, `127.0.0.1`, `::1`).

## Error responses

Every error returns JSON of the form:

```json
{"code": "<enum>", "field": "<dotted.path>", "message": "<debug string>"}
```

`field` and `message` are optional; `code` is always present. Branch on `code`.

| HTTP | `code`                  | What happened                                        | Recovery                                                              |
|------|-------------------------|------------------------------------------------------|-----------------------------------------------------------------------|
| 400  | `malformed_json`        | Request body is not valid JSON                       | Fix the JSON; do not retry the same body.                             |
| 400  | `unknown_field`         | You sent a field the server does not recognize       | Remove the field named in `field`.                                    |
| 400  | `missing_field`         | A required field was absent or empty                 | Add the field named in `field`.                                       |
| 400  | `invalid_value`         | A value failed enum/range validation                 | Fix the value at `field`.                                             |
| 401  | `unauthorized`          | Token missing or wrong                               | Set `DAILIES_API_TOKEN` or fix the header.                            |
| 404  | `walkthrough_not_found` | ID does not exist                                    | Do not retry — the walkthrough is gone.                               |
| 409  | `walkthrough_completed` | Trying to add a step after `/complete`               | Create a new walkthrough if more steps are needed.                    |
| 413  | `payload_too_large`     | Request body over 50 MB (sum of base64 images)       | Capture at ≤1600px wide; send one `/step` per image when possible.    |
| 500  | `internal_error`        | Server bug or disk failure                           | For `/step`, retry only with the **same** `Idempotency-Key` used on the original request. If the original request had no key, call `GET $DAILIES_URL/api/walkthrough/$ID` and check whether the step already appears before retrying — a blind retry may create a duplicate step. |

## Idempotent retries

`POST /api/walkthrough/$ID/step` is **not** idempotent without a key. If your request times out or errors mid-flight, retrying will create a duplicate step, and the API has no delete endpoint to clean it up.

To retry safely, include an `Idempotency-Key` header with a value you would regenerate identically on retry (UUID v4 generated before the request, stored alongside your step payload):

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9f2a7c91-14d4-4f6e-9a58-fd6b9e6a2f00" \
  -d '{ ... step body ... }'
```

- First successful call: `201 Created` with `{"seq": N}`.
- Any subsequent call with the same key for the same walkthrough: `200 OK` with the original `{"seq": N}`, plus the response header `Idempotency-Replayed: true`.
- Keys are scoped per walkthrough; reusing a key across different walkthroughs is fine.
- Max key length is 256 bytes.
- If you use the same key with a different request body, you still get the original response — the server does not compare bodies.

Do not retry without a key unless you are willing to accept duplicate steps.

## Screenshot capability gate

Dailies is for visual walkthroughs. For UI work, confirm you can capture or access real screenshots before creating the walkthrough.

Use `GET $DAILIES_URL/agent/screenshots` for the screenshot capture guide. It covers `agent-browser`, base64 encoding, before/after capture rules, and fallback capture paths.

Before posting screenshot or comparison steps:

- Confirm screenshot capture works, or confirm usable screenshot files already exist.
- Do not use placeholder screenshots.
- If you cannot capture screenshots, stop and ask the user for help before posting a walkthrough.
- Ask the user for explicit permission before creating a narration-only walkthrough.

Use this escalation when screenshot capture is blocked:

```text
Dailies needs screenshots to create a useful visual walkthrough. I do not have a working screenshot capture path because <reason>.

Please either:
1. Install agent-browser and make it available on PATH,
2. Provide screenshot file paths I can use,
3. Tell me the app URL and approve an alternate browser automation tool, or
4. Confirm explicitly that you want a narration-only walkthrough.
```

## Flow

The API workflow has exactly 3 phases, always in this order:

### 1. Create a walkthrough

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Short description of what you built",
    "agent": "your-agent-name",
    "repo": "owner/repo",
    "branch": "current-git-branch",
    "pr": {"url": "https://github.com/owner/repo/pull/123", "provider": "github"}
  }'
```

Response: `{"id": "uuid"}` — save this ID for subsequent calls.

- `title`: a human-readable summary like "Add filter dropdown to dashboard header"
- `agent`: your identifier, e.g. `claude-code`, `cursor`, `codex`
- `repo`: the repository you're working in (owner/name format)
- `branch`: the git branch name
- `pr`: optional — include if a PR exists

### 2. Add steps (one or more)

Add as many content steps as the walkthrough needs, in the order you want them narrated. Each content step is one `POST /api/walkthrough/$ID/step` request. A walkthrough with 2 screenshots and 1 narration uses 3 step requests; a walkthrough with 4 screenshots and 5 narrations uses 9 step requests.

There are 3 step types:

#### Narration — text summary

Use as the first step to provide an overview, or anywhere you need to explain context.

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "narration",
    "title": "Overview",
    "body": "Added a filter dropdown to the analytics dashboard. Users can now filter by date range, region, and category.",
    "facts": [
      {"key": "duration", "label": "Duration", "value": "4m 22s"},
      {"key": "files_changed", "label": "Files changed", "value": "3"}
    ]
  }'
```

- `title`: step heading
- `body`: the narration text
- `facts`: optional ordered key-value rows/cards shown in the info card.
- Narration steps must not include image, before, after, or caption fields. The server rejects those combinations.

### Choosing facts

`facts` are optional. Use them for short structured details that help a reviewer understand or verify the current step at a glance. If no useful structured details exist, omit `facts`.

Good fact candidates:

- `route`: app route or URL path, e.g. `/analytics/overview`
- `screen`: screen or page name, e.g. `Dashboard`
- `state`: UI state, e.g. `Filter menu open`
- `component`: component or area changed, e.g. `DateRangeFilter`
- `action`: user action shown, e.g. `Select region`
- `role`: permission or user role, e.g. `Admin`
- `data`: data condition, e.g. `Empty results`
- `test`: verification detail for this step, e.g. `Keyboard nav passes`
- `a11y`: accessibility result, e.g. `Focus trap verified`
- `flag`: feature flag, e.g. `dashboard_filters`

Do not use `facts` for final run stats such as files changed, lines added, lines removed, or tests added. Send final run stats to `/complete.stats`.

Keep facts small:

- Prefer 0-4 facts per step.
- Use stable snake_case for `key`.
- Add `label` when the key is not obvious to a human reader.
- `value` must be a non-empty string.

#### Screenshot — a single UI state

Use to show a specific state: empty, loading, error, open dropdown, completed form, etc.

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "screenshot",
    "title": "Dashboard with filter open",
    "body": "The dropdown shows all available filter options.",
    "image": "<base64-encoded-image>",
    "caption": "/analytics/overview - filter open",
    "image_alt": "Dashboard with filter menu open",
    "highlights": [
      {"target": "image", "x": 85, "y": 2, "w": 12, "h": 6, "label": "Filter button", "kind": "added"}
    ]
  }'
```

- `type`: must be exactly `screenshot`.
- `image`: base64-encoded PNG, JPG, GIF, or WebP. A data URI (`data:image/png;base64,...`) is also accepted.
- `caption`: optional display string for screenshot or comparison steps. Use it for route, branch, file path, or state labels.
- `image_alt`: optional. If omitted, the server falls back to step title, then walkthrough title.
- `highlights[].target`: defaults to `image` on screenshot steps; only `image` or empty is allowed.
- `highlights[].kind`: optional. If set, must be one of `added`, `changed`, `removed`.
- `highlights[].x`, `y`, `w`, `h`: percentages of the image, top-left origin. `x` and `y` must be `>= 0`; `w` and `h` must be `> 0`. Values over 100 are accepted but clipped visually.

#### Comparison — before/after

Use to show what changed. The viewer renders this as a draggable slider.

Dailies does not choose comparison images automatically. Pick the two captures you want in the slider and send them in `before.image` and `after.image`. Separate screenshot steps are independent and are not paired automatically.

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "comparison",
    "title": "Dashboard — Before & After",
    "body": "The filter button was added to the header.",
    "before": {
      "image": "<base64-encoded-image>",
      "label": "Without filters",
      "alt": "Dashboard before filters"
    },
    "after": {
      "image": "<base64-encoded-image>",
      "label": "With filter button",
      "alt": "Dashboard after filter button was added"
    },
    "highlights": [
      {"target": "after", "x": 85, "y": 2, "w": 12, "h": 6, "label": "Filter button", "kind": "added"}
    ]
  }'
```

- `type`: must be exactly `comparison`.
- `before.image`, `after.image`: both required, same encoding as `image` above.
- `before.label`, `after.label`: optional text shown under each side of the slider.
- `before.alt`, `after.alt`: optional. Fallback chain matches screenshot steps.
- `caption`: optional display string for screenshot or comparison steps. Use it for route, branch, file path, or state labels.
- `highlights[].target`: **required**, must be exactly `before` or `after`. Empty/missing target on a comparison step is rejected.
- `highlights[].kind`: optional, same enum as above.
- `highlights[].x`, `y`, `w`, `h`: same rules as above.

### 3. Complete the walkthrough

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/complete"
```

This marks the walkthrough as ready for viewing. You can include a stats block when final implementation numbers are known:

```bash
curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/complete" \
  -H "Content-Type: application/json" \
  -d '{"stats":{"files_changed":3,"lines_added":142,"lines_removed":18,"tests_added":4}}'
```

### Mini example — complete walkthrough with screenshots and slider

This creates a walkthrough with 1 narration, 2 screenshot steps, and 1 comparison slider. The slider uses the same two captures explicitly: `FIRST_IMAGE` as `before.image`, and `SECOND_IMAGE` as `after.image`.

```bash
FIRST_IMAGE="$(base64 -i /tmp/dailies-first-state.png | tr -d '\n')"
SECOND_IMAGE="$(base64 -i /tmp/dailies-second-state.png | tr -d '\n')"
AUTH_ARGS=()
if [ -n "${DAILIES_API_TOKEN:-}" ]; then
  AUTH_ARGS=(-H "Authorization: Bearer $DAILIES_API_TOKEN")
fi

ID="$(
  curl -s -X POST "$DAILIES_URL/api/walkthrough" \
    -H "Content-Type: application/json" \
    "${AUTH_ARGS[@]}" \
    -d '{
      "title": "Dashboard filters",
      "agent": "codex",
      "repo": "owner/repo",
      "branch": "feature/dashboard-filters"
    }' |
  sed -n 's/.*"id":"\([^"]*\)".*/\1/p'
)"

curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  "${AUTH_ARGS[@]}" \
  -d '{
    "type": "narration",
    "title": "Overview",
    "body": "Added filtering to the dashboard and verified the open and applied-filter states."
  }'

curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  "${AUTH_ARGS[@]}" \
  -d "$(cat <<JSON
{
  "type": "screenshot",
  "title": "Filter menu open",
  "body": "The filter menu is available from the dashboard header.",
  "image": "$FIRST_IMAGE",
  "caption": "/analytics/overview - filter open",
  "highlights": [
    {"target": "image", "x": 82, "y": 4, "w": 14, "h": 8, "label": "Filter control", "kind": "added"}
  ]
}
JSON
)"

curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  "${AUTH_ARGS[@]}" \
  -d "$(cat <<JSON
{
  "type": "screenshot",
  "title": "Filtered results",
  "body": "The table updates after applying a filter.",
  "image": "$SECOND_IMAGE"
}
JSON
)"

curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/step" \
  -H "Content-Type: application/json" \
  "${AUTH_ARGS[@]}" \
  -d "$(cat <<JSON
{
  "type": "comparison",
  "title": "Before / after",
  "body": "The slider compares the chosen captures. Dailies does not infer this pair from the screenshot steps.",
  "before": {
    "image": "$FIRST_IMAGE",
    "label": "Before filters",
    "alt": "Dashboard before filters"
  },
  "after": {
    "image": "$SECOND_IMAGE",
    "label": "After filters",
    "alt": "Dashboard after filters"
  }
}
JSON
)"

curl -s -X POST "$DAILIES_URL/api/walkthrough/$ID/complete" \
  -H "Content-Type: application/json" \
  "${AUTH_ARGS[@]}" \
  -d '{"stats":{"files_changed":3,"lines_added":142,"lines_removed":18,"tests_added":4}}'
```

## Screenshot capture details

Detailed capture instructions live at:

```text
$DAILIES_URL/agent/screenshots
```

## Recommended walkthrough structure

A good walkthrough typically has 3-5 steps:

1. **Narration** — overview of what was built and why
2. **Comparison** — before/after showing the main change
3. **Screenshot** — key UI states (open dropdown, error state, empty state, etc.)
4. **Narration** (optional) — implementation notes or caveats

## Viewing

After completing the walkthrough, the shareable URL is:

```
$DAILIES_URL/w/$ID
```

Include this URL in your PR description, Slack message, or completion report.

## Tips

- Capture the "before" screenshot at the start of your work, before making changes
- Keep narration concise — stakeholders want to see, not read
- Use highlights to draw attention to small UI changes that might be missed
- One walkthrough per feature/PR — don't bundle unrelated changes
