debug-tend-run · git:20260929.087f5f1 · 2026-09-29 · sha256 0687eb74173489db

debug-tend-run git:20260929.087f5f1A

Immutable. This exact content never changes and is served at /api/v1/blob/0687eb74173489db.

---
name: debug-tend-run
description: Debugs a tend CI run from its session-log artifacts. Use for run IDs, PR numbers, or questions about what tend did and why.
---

# Debug Tend Run

Investigate what tend did during a GitHub Actions run by downloading and
parsing its session log artifacts. Works for both supported harnesses
(Claude and Codex); the artifact name distinguishes them.

## Identify the run

If the user provides a run ID or URL, extract the numeric run ID. Otherwise,
list recent tend runs:

```bash
REPO=$(gh repo view --json nameWithOwner --jq '.nameWithOwner')
gh run list -R "$REPO" --limit 20 \
  --json databaseId,workflowName,conclusion,createdAt,headBranch,event \
  --jq '.[] | select(.workflowName | startswith("tend-")) |
    "\(.databaseId)\t\(.conclusion)\t\(.createdAt)\t\(.workflowName)\t\(.headBranch)\t\(.event)"'
```

Narrow by branch (`--branch`), event type, or workflow name as needed. To find
the run associated with a specific PR:

```bash
PR_NUMBER=<number>
HEAD=$(gh pr view "$PR_NUMBER" -R "$REPO" --json headRefName --jq '.headRefName')
gh run list -R "$REPO" --branch "$HEAD" --limit 10 \
  --json databaseId,workflowName,conclusion,createdAt,event \
  --jq '.[] | select(.workflowName | startswith("tend-")) |
    "\(.databaseId)\t\(.conclusion)\t\(.workflowName)\t\(.event)"'
```

## Download session logs

The artifact name identifies the harness:

- `claude-session-logs*` — Claude harness (headless `claude -p` behind the proxy)
- `codex-session-logs-*` — Codex harness (`max-sixty/tend/codex`)

```bash
RUN_ID=<run-id>
DEST=${TMPDIR:-/tmp}/session-logs/$RUN_ID
gh run download "$RUN_ID" -R "$REPO" --pattern '*session-logs*' --dir "$DEST"
ls "$DEST"  # confirm claude- or codex-
FILE=$(find "$DEST" -name '*.jsonl' | head -1)
echo "$FILE"
```

Claude artifacts hold flat JSONL files, one per agent session. Codex
artifacts store the rollout at `sessions/YYYY/MM/DD/rollout-*.jsonl`
plus `projects/token-usage.json`; one rollout per session.

If no artifacts exist, the run either had no agent session or ended before
logs were uploaded. Fall back to console output:

```bash
gh run view "$RUN_ID" -R "$REPO" --log-failed
```

## Parse session logs

The JSONL schema differs by harness. Open the reference matching the
artifact name and follow its recipes:

- `claude-session-logs*` → [`references/claude-logs.md`](references/claude-logs.md)
- `codex-session-logs-*` → [`references/codex-logs.md`](references/codex-logs.md)

Each reference covers the line schema plus copy-paste jq for an overview
trace, targeted queries (commands, tool results, files, gh calls), and
keyword search. Both assume `$FILE` from the download step.

## Diagnose the problem

After extracting the session trace, reconstruct the decision chain:

1. **What triggered the run?** Check the event type and triggering context
   (PR comment, push, schedule).
2. **What did the bot see?** Look at system/user messages and tool results.
3. **What did it decide?** Follow assistant/agent text for reasoning.
4. **Where did it go wrong?** Compare intended behavior against actual tool
   calls and outputs.

Common failure modes:
- **Wrong skill loaded** (or skill not loaded) — inspect skill invocations or
  reads of a `SKILL.md` path in the matching harness log
- **Stale context** — bot acted on outdated PR state or missed recent commits
- **Tool error ignored** — a command failed but the bot continued
- **Hallucinated file/function** — bot referenced something that doesn't exist
- **CI polling timeout** — bot ran out of time waiting for checks

## Cross-reference with PR state

For review runs, compare the bot's actions against the PR timeline:

```bash
PR_NUMBER=<number>
gh pr view "$PR_NUMBER" -R "$REPO" --json title,state,reviews,comments,commits \
  --jq '{title, state, reviews: [.reviews[] | {author: .author.login, state: .state}],
    comments: (.comments | length), commits: (.commits | length)}'
```

Check whether subsequent commits undid something the bot approved, or whether
human reviewers flagged issues the bot missed.