Skip to content

chore(automation): agent-fleet analytics pipeline - #947

Merged
skevetter merged 6 commits into
mainfrom
agent-analytics/fleet-monitor
Aug 9, 2026
Merged

skevetter merged 6 commits into
mainfrom
agent-analytics/fleet-monitor

Conversation

@devsy-app

@devsy-app devsy-app Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Summary

Add a daily agent-fleet analytics pipeline that monitors the previous day's agent runs, clusters failure modes with Python data-science tooling (scikit-learn KMeans), and ships one deterministic intervention to reduce recurring errors — lowering agent cognitive load so reasoning budget stays on the primary task.

Pipeline (hack/analytics/analyze_runs.py)

A PEP 723 self-contained uv script (always run via uv run, never pip/venv) with inline deps: typer, numpy, scikit-learn, pandas, seaborn, matplotlib, prettytable.

Step Output
Pull conversation event logs via OpenHands Cloud API runs.csv
KMeans-cluster failure signatures failure_clusters.csv
Rank top errors by frequency x agent top_errors.csv
seaborn failure-mode x agent heatmap failure_heatmap.png
prettytable human-readable summary daily_report.md

After writing outputs it zips the five files, uploads to https://temp.sh/upload (3-day expiry, plain-text share URL; set ANALYTICS_NO_UPLOAD=1 to skip), and appends a ## pr-gate verdict (ACTIONABLE / NOT-ACTIONABLE) to daily_report.md.

PR-gate logic: ACTIONABLE when the top failure signature recurs across >=2 runs at >=10% share of failed runs. The agent-analytics agent only opens a PR when the verdict is ACTIONABLE — otherwise it reports "no actionable failure mode found" and stops. Upload failures degrade gracefully (skip with reason; pipeline never crashes).

Generated agent

.agents/agents/agent-analytics/agent.md — generated via hack/automations/agents.yaml (regenerate with go run ./hack/automations; verify idempotency with go run ./hack/automations -check). The agent reviews the analytics output and ships ONE deterministic intervention per run (script fix, prompt clarification, or small code change) justified by a metric from the data.

Go toolchain changes

  • hack/automations/main.go — support uv (Python agents use PEP 723 self-contained scripts)
  • hack/automations/main_test.go — pythonAgents allowlist permits uv run for agent-analytics; bans pip install / venv globally for all generated agents

Verification

Check Result
mypy --strict clean
radon max complexity 8 (< 10)
arg count per function <= 4
leading-underscore identifiers none
inline comments / lazy imports none
go test ./hack/automations/ . pass
gofmt clean
go run ./hack/automations -check idempotent
Real temp.sh upload https://temp.sh/ibyJZ/tmp1_40mf6q.zip (HTTP 200, downloadable)
ANALYTICS_NO_UPLOAD=1 smoke pr-gate ACTIONABLE, clean skip reason
Commit signature verified (Devsy GitHub App, committer: GitHub)

Constraints met

  • uv only (no pip/venv) — PEP 723 inline deps
  • Typer CLI, mypy --strict clean
  • cyclomatic complexity < 10 (max 8)
  • <= 4 args per function
  • no leading underscores for identifiers
  • no inline comments, no lazy imports

This PR was created by an AI agent on behalf of the user.

Add a daily agent-fleet analytics pipeline that monitors the previous day's agent runs, clusters failure modes with Python data-science tooling (scikit-learn KMeans), and ships one deterministic intervention to reduce recurring errors.

Pipeline (hack/analytics/analyze_runs.py):
- PEP 723 self-contained uv script (deps: typer, numpy, scikit-learn, pandas, seaborn, matplotlib, prettytable)
- Pulls conversation event logs via OpenHands Cloud API, normalizes into runs.csv
- KMeans clusters failure signatures (failure_clusters.csv), ranks top errors (top_errors.csv)
- seaborn heatmap (failure_heatmap.png), prettytable daily report (daily_report.md)
- Uploads zipped outputs to temp.sh; appends a pr-gate verdict (ACTIONABLE/NOT-ACTIONABLE)
- Agent only opens a PR when verdict is ACTIONABLE; skips gracefully on upload failure

Generated agent (.agents/agents/agent-analytics/) via hack/automations/agents.yaml.
Go toolchain updated to use uv (never pip/venv); main_test.go bans pip/venv globally.

Constraints: mypy --strict clean, radon max complexity 8 (<10), max 4 args/function, no leading underscores, no inline comments, no lazy imports.

This PR was created by an AI agent on behalf of the user.
@netlify

netlify Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for images-devsy-sh canceled.

Name Link
🔨 Latest commit d67f07f
🔍 Latest deploy log https://app.netlify.com/projects/images-devsy-sh/deploys/6a78b7af52853400085477cc

@netlify

netlify Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit d67f07f
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a78b7af8d3ca80008455ac0

@codacy-production

codacy-production Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 3 medium

Alerts:
⚠ 3 issues (≤ 0 issues of at least minor severity)

Results:
3 new issues

Category Results
BestPractice 1 medium
Security 1 medium
Complexity 1 medium

View in Codacy

🟢 Metrics 269 complexity · 3 duplication

Metric Results
Complexity 269
Duplication 3

View in Codacy

AI Reviewer: run a review on demand. To trigger the first review automatically, go to your organization or repository integration settings. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

devsy-app Bot added 3 commits August 9, 2026 08:37
Reduce agent friction when committing by fixing the two most common
errors and simplifying the commit+PR flow to one command each.

Changes to hack/sign_commit (hack/sign_commit/main.go):
- changedFiles() now detects ALL working-tree changes (staged, unstaged,
  and untracked) vs origin/main, not just committed diffs. Previously
  agents with uncommitted changes got 'no changed files to commit'.
- ensureBranch() auto-creates the remote branch from origin/main when it
  does not yet exist. Previously agents got '404 Not Found' on new branches.
- Added -pr flag: commit + open draft PR in one step.
- Added -pr-only flag: open a draft PR without committing.
- Added -title and -draft flags for PR creation.

Agent prompt simplification (hack/automations/template.md.tmpl):
- Commit step: removed git add, the WARNING block, the Pre-PR check, and
  the reset/recovery flow. Now just: checkout branch + sign-commit.
- PR step: removed the 6-line TOKEN/curl/JSON dance. Now just:
  sign-commit --pr-only -title ... -b ...
- Status-check follow-up: removed git add (tool auto-detects changes).

All 14 agent.md files regenerated. Tests updated to match new flow.

Verification:
- go test ./hack/sign_commit/ ./hack/automations/ — pass
- gofmt — clean
- go vet — clean
- E2E smoke test: working-tree detection + branch auto-creation + verified
  commit all confirmed working end-to-end.

This PR was created by an AI agent on behalf of the user.
Move the duplicated verify step (mkdir, git fetch, lint:ci, test, and the
KNOWN PRE-EXISTING FAILURE note) from inline agents.yaml review_steps into a
generated verifyBlock() function in main.go. This eliminates ~8 lines of
boilerplate duplicated across 9 Go agents.

Changes:
- Removed unused git_add_glob field from the agent struct and all 14 YAML
  entries (sign_commit now auto-detects working-tree changes).
- Added verify_type field: set to 'go' for 9 agents, 'go+desktop' for
  lint-fixer. Agents with custom verify steps (docs-keeper, ci-optimizer,
  ui-polish, agent-analytics) leave it empty.
- Added verify_extras field for per-agent additions (gRPC build for
  pkg-tunnel-network, e2e suite for integration-test and devcontainer-spec,
  npm check for lint-fixer).
- verifyBlock() generates the standard verify step with the correct step
  number (commit_step_number - 1) and any extras.
- Template inserts {{.VerifyBlock}} between review_steps and FORMATTING GATE.
- Removed the inline verify block from 9 agents' review_steps in agents.yaml.

Result: agents.yaml dropped from 489 to 474 lines. The generated agent.md
files are identical in content — the verify text now comes from one place
instead of being copy-pasted 9 times.

Verification: go test ./hack/... pass, gofmt clean, go vet clean,
go run ./hack/automations -check idempotent.

This PR was created by an AI agent on behalf of the user.
Fix all 12 golangci-lint errors flagged by CI:

sign_commit/main.go:
- goconst: extract 'main' and 'body' string literals into constants
  (mainBranch, bodyKey)
- lll: shorten -all flag description, wrap -pr-only flag description
- cyclop: extract prOnly branch resolution into createPRFromOptions helper

sign_commit/main_test.go:
- cyclop: extract gitRun() and writeFile() helpers to reduce
  TestChangedFilesDetectsWorkingTree complexity from 12 to under 8
- errcheck: replace unchecked exec.Command().Run() with gitRun() helper
- goconst: use testMainBranch const instead of literal 'main'
- golines: writeFile helper eliminates long os.WriteFile lines

automations/main.go:
- lll: split knownGitFailure const into concatenated string parts

Verification: golangci-lint run --new-from-rev=origin/main reports 0 issues.
All tests pass. gofmt clean. go vet clean.

This PR was created by an AI agent on behalf of the user.
@skevetter skevetter changed the title feat(analytics): agent-fleet analytics pipeline chore(automation): agent-fleet analytics pipeline Aug 9, 2026
@skevetter
skevetter marked this pull request as ready for review August 9, 2026 09:27
@skevetter
skevetter enabled auto-merge (squash) August 9, 2026 09:27
@skevetter
skevetter disabled auto-merge August 9, 2026 09:32
Add a code quality directive to the FORMATTING GATE section of the
agent prompt template: write self-documenting code with clear names, small
functions, and obvious structure. Avoid wordy comments — prefer no comments
unless the code expresses something genuinely unintuitive (a non-obvious
invariant, workaround, or trade-off).

Regenerated all 14 agent.md files from the updated template.

This PR was created by an AI agent on behalf of the user.
@devsy-app

devsy-app Bot commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Blocking bug: events fetch uses limit=500 -> API returns HTTP 422

While validating this pipeline against the live OpenHands Cloud API, I found that analyze_runs.py cannot complete a real (non---sample) run. Every conversation triggers a 422 that is swallowed silently, so the pipeline exits with code 1 and no output.

Root cause

CloudClient.conversation_events() requests events with limit=500:

def conversation_events(self, conv_id: str, limit: int = 500) -> list[dict[str, Any]]:
    path = f"/api/v1/conversation/{conv_id}/events/search"
    return normalize_items(self.get(path, {"limit": limit}))

The /api/v1/conversation/{id}/events/search endpoint rejects limit > 100:

{"detail":[{"type":"less_than_equal","loc":["query","limit"],"msg":"Input should be less than or equal to 100","input":"500","ctx":{"le":100}}]}

So run_from_conv() aborts on the first conversation that has events.

Silent failure compounds it

CloudClient.get() does:

except urllib.error.HTTPError as exc:
    raise typer.Exit(1) from SystemExit(f"HTTP {exc.code} for {url}")

typer.Exit(1) suppresses the SystemExit message, so the 422 (and the offending URL) never print -- even with ANALYTICS_VERBOSE=1 you only see the two GET lines then a bare exit 1. Worth surfacing the error (e.g. typer.echo(f"http {exc.code} for {url}", err=True) before exiting) so future failures are diagnosable.

Verified fix

limit=100&page=1 returns HTTP 200 with 100 items and a next_page_id cursor. conversation_events should cap limit at 100 and page until exhausted:

  • limit=100, page=1 -> 200, 100 items, next_page_id=100
  • limit=100, page=2 -> 200, 100 items
  • limit=500 -> 422 (rejected)

Note: next_page_id is returned as a constant (100) across pages in my probes, so termination should key on an empty items page rather than the cursor value. The app-conversations/search list call also benefits from the same limit <= 100 + pagination treatment (it currently relies on a single limit=100 fetch and client-side in_window filtering, which silently truncates any day with >100 runs).

Impact

Without this fix, the daily agent-analytics job can only ever produce --sample (synthetic) output -- real fleet data is unreachable. This blocks the pipeline's stated purpose, so I'd consider it a merge blocker.


This review comment was posted by an AI agent (OpenHands) running the daily agent-analytics job on behalf of the repo maintainers. I did not open a separate PR because the full pipeline bootstrap is already in flight here; this comment aims to correct the in-flight work rather than duplicate it.

Signed-off-by: GitHub <noreply@github.com>
@skevetter
skevetter enabled auto-merge (squash) August 9, 2026 17:29
@skevetter
skevetter disabled auto-merge August 9, 2026 18:11
@skevetter
skevetter merged commit 1ec5f00 into main Aug 9, 2026
66 of 69 checks passed
@skevetter
skevetter deleted the agent-analytics/fleet-monitor branch August 9, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant