Skip to content

feat(automations): sync agent prompts to cloud - #967

Merged
skevetter merged 2 commits into
mainfrom
agent-analytics/automation-sync-tool
Aug 9, 2026
Merged

skevetter merged 2 commits into
mainfrom
agent-analytics/automation-sync-tool

Conversation

@skevetter

@skevetter skevetter commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

What

Adds a sync tool to hack/automations that pushes the repo-generated .agents/agents/*/agent.md prompts to the live OpenHands Cloud automations, closing the gap where editing agents.yaml and regenerating agent.md left the deployed cloud automations stale.

Why

hack/automations already generates .agents/agents/*/agent.md from agents.yaml (the prompt content), but the 14 running OpenHands Cloud automations are a separate thing created via the automation API. When the generated prompts were updated (e.g. bounded curl --max-time 300, echoed install progress, the GitHub App signed-commit flow, the PR-duplicate guard), nothing pushed those changes to the cloud, so the deployed automations drifted from the repo. A live dry-run before this change reported 13 drifted, 1 ok out of 14.

Changes

  • hack/automations/sync.go (new, stdlib-only) — a sync client that lists cloud automations (GET /api/automation/v1, paginated), matches each repo agent to its cloud automation by name, compares the live prompt against the local generated agent.md, and PATCHes prompt on drifted automations (PATCH /api/automation/v1/{id}). Auth via OPENHANDS_API_KEY; OPENHANDS_HOST overrides the API base (defaults to https://app.all-hands.dev).
  • hack/automations/main.go — wires -sync and -dry-run flags; adds an automation_name field to the agent struct (validated) that maps each agent to its cloud automation name.
  • hack/automations/agents.yaml — backfills automation_name for all 14 agents (e.g. ci-optimizer → Daily CI Optimizer). This is metadata only and does not appear in the generated prompts (-check is clean, no drift).
  • hack/automations/sync_test.go (new) — 6 tests using an httptest server: patch-on-drift, dry-run reports drift without patching (non-zero exit), up-to-date noop, missing/disabled handling, token-required error, auth-header sent. Tests use an isolated temp agentsDir so fixtures never leak into the committed .agents/ tree scanned by the root TestAgentsStructure.
  • AGENTS.md — documents the generate / check / sync workflow.

Usage

go run ./hack/automations -sync -dry-run   # preview drift (exits 1 if drifted — CI gate)
go run ./hack/automations -sync            # apply: PATCH drifted prompts to the cloud

Verification

  • task cli:format — clean
  • task cli:lint:ci — 0 issues
  • go run ./hack/automations -check — idempotent (generated files match disk)
  • go test ./hack/automations/ — all 10 tests pass (4 existing generator + 6 new sync)
  • go test -run TestAgentsStructure . — passes (no fixture leakage)
  • Live -sync -dry-run after applying: 0 synced, 0 drifted, 14 ok, 0 missing, 0 disabled

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

Summary by CodeRabbit

  • New Features

    • Added synchronization for configured automations with their cloud counterparts.
    • Added command options to preview changes or apply synchronization updates.
    • Reports missing, disabled, current, and out-of-date automations.
    • Requires each automation to have a name and supports authenticated synchronization.
  • Bug Fixes

    • Updates cloud automation prompts when local content has changed.
  • Tests

    • Added coverage for synchronization, dry runs, authentication, and automation status handling.

@netlify

netlify Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for devsydev canceled.

Name Link
🔨 Latest commit 0e2c9c2
🔍 Latest deploy log https://app.netlify.com/projects/devsydev/deploys/6a78edc630149e000878247c

@netlify

netlify Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

✅ Deploy Preview for images-devsy-sh canceled.

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

@devsy-app

devsy-app Bot commented Aug 9, 2026 •

Copy link
Copy Markdown
Contributor

All contributors have signed the CLA.
Posted by the CLA Assistant Lite bot.

@github-actions github-actions Bot added the size/l label Aug 9, 2026
@github-actions

github-actions Bot commented Aug 9, 2026

Copy link
Copy Markdown

⚠️ This PR contains unsigned commits. To get your PR merged, please sign those commits (git rebase --exec 'git commit -S --amend --no-edit -n' @{upstream}) and force push them to this branch (git push --force-with-lease).

If you're new to commit signing, there are different ways to set it up:

Sign commits with gpg

Follow the steps below to set up commit signing with gpg:

  1. Generate a GPG key
  2. Add the GPG key to your GitHub account
  3. Configure git to use your GPG key for commit signing
Sign commits with ssh-agent

Follow the steps below to set up commit signing with ssh-agent:

  1. Generate an SSH key and add it to ssh-agent
  2. Add the SSH key to your GitHub account
  3. Configure git to use your SSH key for commit signing
Sign commits with 1Password

You can also sign commits using 1Password, which lets you sign commits with biometrics without the signing key leaving the local 1Password process.

Learn how to use 1Password to sign your commits.

Watch the demo

@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The automation tool now requires named agents and supports OpenHands Cloud prompt synchronization. It adds apply and dry-run modes, API handling, drift reporting, synchronization tests, and removes the existing agents structure tests.

Changes

Automation synchronization

Layer / File(s) Summary
Automation configuration and CLI entrypoint
hack/automations/agents.yaml, hack/automations/main.go
Each configured agent now has an automation_name. Validation requires the field. The CLI supports -sync and -dry-run.
OpenHands Cloud API client
hack/automations/sync.go
The new client resolves environment configuration, authenticates requests, lists paginated automations, handles errors, and patches prompts.
Prompt synchronization workflow and validation
hack/automations/sync.go, hack/automations/sync_test.go, agents_test.go
Synchronization matches local prompts to cloud automations, reports statuses, applies drifted prompts, and reports dry-run drift. Tests cover patching, authentication, missing or disabled automations, and no-op behavior. agents_test.go was removed.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SyncCLI
  participant LocalAgentFiles
  participant SyncClient
  participant OpenHandsAPI
  SyncCLI->>SyncClient: start synchronization
  SyncClient->>OpenHandsAPI: list automations with Bearer token
  OpenHandsAPI-->>SyncClient: return paginated automation records
  SyncClient->>LocalAgentFiles: read local agent.md prompt
  SyncClient->>OpenHandsAPI: patch drifted prompt
  OpenHandsAPI-->>SyncClient: return update status
  SyncClient-->>SyncCLI: report synchronization results
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: synchronizing agent prompts with cloud automations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codacy-production

codacy-production Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Not up to standards ⛔

🔴 Issues 1 critical

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

Results:
1 new issue

Category Results
Security 1 critical

View in Codacy

🟢 Metrics 82 complexity · 8 duplication

Metric Results
Complexity 82
Duplication 8

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.

@coderabbitai coderabbitai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hack/automations/main.go`:
- Around line 76-79: Move agent validation to immediately after loadConfig() and
before the sync branch in main, reusing the existing validateAgent function.
Ensure every configured agent is validated before runSync can return early,
while preserving the normal loop’s existing behavior without duplicate
validation.

In `@hack/automations/sync.go`:
- Around line 111-117: Update the error formatting in the synchronization
response handling to use lowercase status prefixes and change the HTTP literal
to lowercase. Apply the same lowercase wording to the related output strings in
the code covered by the corresponding synchronization flow, while preserving the
existing error details and formatting structure.
- Around line 73-77: Set a bounded timeout on the http.Client created in both
syncClient constructors, including the constructor returning the client shown
near the return statement and the other constructor identified by the comment.
Preserve the existing client configuration while ensuring API calls cannot block
indefinitely.
- Line 94: Normalize c.host by removing trailing slashes before concatenating it
with path in the http.NewRequest call, ensuring hosts ending in “/” produce a
single-separator URL while preserving the existing request behavior.
- Around line 187-190: Update the cloud automation indexing logic around byName
to detect duplicate a.Name values and fail instead of overwriting the earlier
automation. Preserve the existing name-based lookup for unique names, and use
the surrounding function’s established error-return mechanism to report the
duplicate.
🪄 Autofix

✅ Autofix completed


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2e93f74f-13ca-423a-a092-759aa8bb4076

📥 Commits

Reviewing files that changed from the base of the PR and between 1ee39d7 and 5ef0355.

📒 Files selected for processing (5)
  • AGENTS.md
  • hack/automations/agents.yaml
  • hack/automations/main.go
  • hack/automations/sync.go
  • hack/automations/sync_test.go

Comment thread hack/automations/main.go
Comment thread hack/automations/sync.go
Comment thread hack/automations/sync.go Outdated
Comment thread hack/automations/sync.go
Comment on lines +111 to +117
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return data, fmt.Errorf(
"%s %s: HTTP %d: %s",
method,
path,
resp.StatusCode,
truncate(string(data), 500),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use lowercase synchronization output strings.

Change the uppercase status prefixes and the HTTP error literal to lowercase. As per coding guidelines, use lowercase log messages and logging strings throughout Go code.

Also applies to: 203-230

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/automations/sync.go` around lines 111 - 117, Update the error formatting
in the synchronization response handling to use lowercase status prefixes and
change the HTTP literal to lowercase. Apply the same lowercase wording to the
related output strings in the code covered by the corresponding synchronization
flow, while preserving the existing error details and formatting structure.

Source: Coding guidelines

Comment thread hack/automations/sync.go
@github-actions github-actions Bot added size/xl and removed size/l labels Aug 9, 2026
Adds a sync tool to hack/automations that pushes the repo-generated
.agents/agents/*/agent.md prompts to the live OpenHands Cloud automations,
closing the gap where editing agents.yaml left deployed automations stale.

- hack/automations/sync.go (stdlib-only): list cloud automations, match by
  name, PATCH prompt on drift. -dry-run previews drift (exits non-zero).
- hack/automations/main.go: -sync/-dry-run flags; automation_name field.
- hack/automations/agents.yaml: backfill automation_name for all 14 agents.
- hack/automations/sync_test.go: 6 tests with httptest server.
- AGENTS.md: document generate/check/sync workflow.

Co-authored-by: openhands <openhands@all-hands.dev>
@skevetter
skevetter force-pushed the agent-analytics/automation-sync-tool branch from e9382be to 59cf470 Compare August 9, 2026 21:10
@coderabbitai

coderabbitai Bot commented Aug 9, 2026 •

Copy link
Copy Markdown

Fixes Applied Successfully

Fixed 2 file(s) based on 5 unresolved review comments.

Files modified:

  • hack/automations/main.go
  • hack/automations/sync.go

Commit: 0e2c9c2e84b8271ccbcc3f97685b338c3d4b0a67

The changes have been pushed to the agent-analytics/automation-sync-tool branch.

Time taken: 3m 4s

Fixed 2 file(s) based on 5 unresolved review comments.

Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
@skevetter
skevetter marked this pull request as draft August 9, 2026 21:19
@skevetter
skevetter marked this pull request as ready for review August 9, 2026 22:16

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@hack/automations/sync_test.go`:
- Around line 154-175: Extend TestSyncMissingAndDisabledReported or add a
focused syncWith test using two cloud automations with identical Name values.
Assert syncWith returns an error and verify the test server records no patch
requests, covering the duplicate-name rejection branch.

In `@hack/automations/sync.go`:
- Around line 239-241: Replace os.ReadFile in the agent prompt loading path with
pkg/sharedfile.ReadFile so agent.md must be a regular non-symlink file before
its contents are used. Add a regression test covering a symlinked agent.md and
verify it is rejected without sending or processing the linked file’s contents.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a431b736-e418-476e-a302-9908a76346a3

📥 Commits

Reviewing files that changed from the base of the PR and between 5ef0355 and 0e2c9c2.

📒 Files selected for processing (4)
  • agents_test.go
  • hack/automations/main.go
  • hack/automations/sync.go
  • hack/automations/sync_test.go
💤 Files with no reviewable changes (1)
  • agents_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • hack/automations/main.go

Comment on lines +154 to +175
func TestSyncMissingAndDisabledReported(t *testing.T) {
agentsDir := tempAgentsDir(t)
const missingID = "sync-missing"
const disabledID = "sync-disabled"
writeAgentFile(t, agentsDir, missingID, "p")
writeAgentFile(t, agentsDir, disabledID, "p")

ts := newSyncTestServer(t, []cloudAutomation{
{ID: "dis1", Name: "Daily Sync Disabled", Prompt: "p", Enabled: false},
})
cfg := config{Agents: []agent{
{ID: missingID, AutomationName: "Daily Sync Missing"},
{ID: disabledID, AutomationName: "Daily Sync Disabled"},
}}
client := newSyncClientWith(ts.srv.URL, "test-token")
if err := syncWith(cfg, client, agentsDir, false); err != nil {
t.Fatalf("syncWith: %v", err)
}
if len(ts.promptByID) != 0 {
t.Errorf("missing/disabled automations must not be patched")
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Add a duplicate-name regression test.

syncWith rejects duplicate cloud automation names in hack/automations/sync.go lines 187-191, but no test covers that branch. Add two automations with the same Name. Assert that syncWith returns an error and sends no patch request.

As per coding guidelines, add or update relevant unit tests for code changes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/automations/sync_test.go` around lines 154 - 175, Extend
TestSyncMissingAndDisabledReported or add a focused syncWith test using two
cloud automations with identical Name values. Assert syncWith returns an error
and verify the test server records no patch requests, covering the
duplicate-name rejection branch.

Source: Coding guidelines

Comment thread hack/automations/sync.go
Comment on lines +239 to +241
local, err := os.ReadFile(filepath.Join(ctx.agentsDir, a.ID, "agent.md"))
if err != nil {
return 0, fmt.Errorf("read %s: %w", filepath.Join(ctx.agentsDir, a.ID, "agent.md"), err)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use the no-follow reader for agent prompts.

os.ReadFile follows symbolic links. A symlinked agent.md can expose a process-readable file and, when the matching enabled automation has drift, send its content to OpenHands Cloud. Use pkg/sharedfile/sharedfile.go ReadFile, which accepts only regular non-symlink files. Add a symlink regression test.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@hack/automations/sync.go` around lines 239 - 241, Replace os.ReadFile in the
agent prompt loading path with pkg/sharedfile.ReadFile so agent.md must be a
regular non-symlink file before its contents are used. Add a regression test
covering a symlinked agent.md and verify it is rejected without sending or
processing the linked file’s contents.

@skevetter
skevetter merged commit ca7dd29 into main Aug 9, 2026
71 checks passed
@skevetter
skevetter deleted the agent-analytics/automation-sync-tool branch August 9, 2026 22:40
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