Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .claude/settings.ci.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"permissions": {
"deny": [
"Edit(file_path=.github/**)",
"Write(file_path=.github/**)",
"Edit(file_path=api/**)",
"Write(file_path=api/**)",
"Edit(file_path=Makefile)",
"Write(file_path=Makefile)",
"Edit(file_path=go.mod)",
"Edit(file_path=go.sum)"
]
}
}
42 changes: 42 additions & 0 deletions .github/workflows/claude-ci-fix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Claude CI Fix

# Triggered when the "Test" workflow fails on a claude/* branch — Claude
# attempts to fix the failure automatically. Also supports manual dispatch
# for fixing arbitrary failed runs.

on:
workflow_run:
workflows: ["Test"]
types: [completed]
workflow_dispatch:
inputs:
run_id:
description: 'Failed workflow run ID (from gh run list)'
required: true
branch:
description: 'PR branch name (e.g., claude/APP-1234)'
required: true

jobs:
call-ci-fix:
if: >-
github.event_name == 'workflow_dispatch' ||
(
github.event.workflow_run.conclusion == 'failure' &&
startsWith(github.event.workflow_run.head_branch, 'claude/')
)
# viamrobotics/claude-ci-workflows@v1.17.3
uses: viamrobotics/claude-ci-workflows/.github/workflows/claude-ci-fix.yml@3ad96b0ccbb5ee0d7e2cde98653fae0c453e68bb
with:
run_id: ${{ github.event.workflow_run.id || inputs.run_id }}
branch: ${{ github.event.workflow_run.head_branch || inputs.branch }}
install_command: 'go mod download'
container: 'golang:1.25'
allowed_tools: 'Edit,Read,Write,Glob,Grep,Bash(go *),Bash(gofmt *),Bash(make test-go*),Bash(make test-go-no-race*),Bash(make lint*),Bash(make cli*),Bash(make generate-go*),Bash(golangci-lint *),Bash(git config *),Bash(git add *),Bash(git commit *),Bash(git push *),Bash(git status*),Bash(git diff*),Bash(git -C * diff*),Bash(git log*),Bash(git checkout *),Bash(git branch *),Bash(git rev-parse *),Bash(git fetch *),Bash(gh api repos/*/pulls/*/comments*)'
team_mention: '@viamrobotics/core-rdk'
extra_prompt: |
- Run `go build ./...`, `make test-go-no-race`, and `make lint-go` to verify your fix before committing. Fix any errors.
- Do not edit `api/**`, `.github/**`, `Makefile`, `go.mod`, or `go.sum` — these are denied at the settings level.
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
114 changes: 114 additions & 0 deletions .github/workflows/claude-jira.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Claude Jira Implementation

# Triggered by Jira webhook via middleware that calls GitHub repository_dispatch API.
# Expected payload:
# curl -X POST -H "Authorization: token $GITHUB_PAT" \
# https://api.github.com/repos/viamrobotics/rdk/dispatches \
# -d '{
# "event_type": "jira-ticket",
# "client_payload": {
# "ticket_id": "APP-1234",
# "summary": "...",
# "description": "...",
# "scope": "viam-cli",
# "task_complexity": "medium",
# "assignee": "John Doe",
# "assignee_email": "jdoe@viam.com"
# }
# }'
#
# The "scope" field selects which area of the rdk monorepo Claude works in.
# Valid values: "viam-cli" (work in cli/), "gosdk" (work in Go SDK paths).

on:
repository_dispatch:
types: [jira-ticket]
workflow_dispatch:
inputs:
ticket_id:
description: 'Jira ticket ID (e.g., RSDK-1234, APP-5678)'
required: true
summary:
description: 'Ticket summary'
required: true
description:
description: 'Ticket description'
required: true
scope:
description: 'Ticket scope — determines which area of rdk Claude works in'
required: true
type: choice
options:
- viam-cli
- gosdk
task_complexity:
description: 'Task complexity: small (50 turns), medium (75), large (100)'
required: false
default: 'medium'
type: choice
options:
- small
- medium
- large
assignee:
description: 'Atlassian display name of the responsible engineer'
required: false
default: ''
assignee_email:
description: 'Atlassian email of the responsible engineer'
required: false
default: ''

jobs:
call-jira:
uses: viamrobotics/claude-ci-workflows/.github/workflows/claude-jira.yml@3ad96b0ccbb5ee0d7e2cde98653fae0c453e68bb
with:
ticket_id: ${{ github.event.client_payload.ticket_id || inputs.ticket_id }}
summary: ${{ github.event.client_payload.summary || inputs.summary }}
description: ${{ github.event.client_payload.description || inputs.description }}
install_command: 'go mod download'
container: 'golang:1.25'
allowed_tools: 'Edit,Read,Write,Glob,Grep,Bash(go *),Bash(gofmt *),Bash(make test-go*),Bash(make test-go-no-race*),Bash(make lint*),Bash(make cli*),Bash(make generate-go*),Bash(golangci-lint *),Bash(git config *),Bash(git add *),Bash(git commit *),Bash(git push *),Bash(git status*),Bash(git diff*),Bash(git -C * diff*),Bash(git log*),Bash(git checkout *),Bash(git branch *),Bash(git rev-parse *),Bash(git fetch *),Bash(gh pr create*),Bash(gh pr view*),Bash(gh pr diff*),Bash(gh api repos/*/pulls/*/comments*)'
task_complexity: ${{ github.event.client_payload.task_complexity || inputs.task_complexity || 'medium' }}
assignee: ${{ github.event.client_payload.assignee || inputs.assignee || '' }}
assignee_email: ${{ github.event.client_payload.assignee_email || inputs.assignee_email || '' }}
jira_base_url: 'https://viam.atlassian.net'
jira_user_email: 'ale.paredes@viam.com'
extra_prompt: |
This ticket has scope label: **${{ github.event.client_payload.scope || inputs.scope }}**

Apply ONLY the instructions for the matching scope label below. Do not mix scopes.

---

**If scope is `viam-cli`** — Viam CLI (`viam` command, `cli/` folder, module generator, CLI subcommands and flags):
- Read `cli/CLAUDE.md` for commands, conventions, and file organization.
- Work only in `cli/`. Do not modify packages outside `cli/` unless strictly required — if you must, state the reason in the PR description.
- Follow the typed-argument pattern: use `createActionCommandWithT[T]()` for new commands.
- For entity flags (org, location, machine, part, fragment), use `AliasStringFlag` with `-id` and `-name` variants. See `commonPartFlags` in `cli/app.go` as the canonical example.
- Verify with: `go build ./cli/...`, `TEST_TARGET=./cli/... make test-go-no-race`, `make lint-go`.

---

**If scope is `gosdk`** — Go SDK (client code for components/services/robot, interacting with the Viam app/machine APIs from Go):
- Read the root `CLAUDE.md` for repo-wide conventions.
- Relevant paths: `components/<type>/client.go`, `robot/client/`, `services/<type>/client.go`, `resource/`.
- Follow existing client patterns: methods take `ctx context.Context` as the first parameter after the receiver and return typed Go values converted from proto responses.
- Do not store `context.Context` in structs — it's a Go anti-pattern.
- Server-side implementations (`components/<type>/server.go`) are for the gRPC handler, not the Go SDK client. Only edit them if the ticket explicitly requires it.
- Verify with: `go build ./...`, `make test-go-no-race`, `make lint-go`.

---

**Scope rules that apply to both**:
- Do not edit `api/**` (generated proto stubs, auto-generated from the `viamrobotics/api` repo), `.github/**`, `Makefile`, `go.mod`, or `go.sum` — these are denied at the settings level.
- Add or update unit tests for changed behavior. Verify edge cases and error paths.
- Follow existing code patterns. Do not introduce new abstractions or refactor outside the ticket's scope.
- If the ticket is ambiguous or the scope label doesn't match what the ticket text asks for, state your interpretation in the PR description before making changes.
show_full_output: true
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
CI_GITHUB_APP_ID: ${{ secrets.CI_GITHUB_APP_ID }}
CI_GITHUB_APP_PRIVATE_KEY: ${{ secrets.CI_GITHUB_APP_PRIVATE_KEY }}
SLACK_AI_WORKFLOW_ALERT_WEBHOOK_URL: ${{ secrets.SLACK_AI_WORKFLOW_ALERT_WEBHOOK_URL }}
JIRA_API_TOKEN: ${{ secrets.JIRA_API_TOKEN }}
103 changes: 103 additions & 0 deletions .github/workflows/claude-pr-assistant.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Claude PR Assistant

# Two paths:
# 1. auto-review: after the "Test" workflow succeeds on a claude/* branch,
# Claude reviews its own PR and leaves inline comments.
# 2. on-demand: when a human types @claude in a PR comment or review comment,
# Claude responds with a code change or review.

on:
workflow_run:
workflows: ["Test"]
types: [completed]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]

jobs:
# --- Resolve PR data for auto-review ---
resolve-pr:
permissions:
contents: read
if: >-
github.repository_owner == 'viamrobotics' &&
github.event_name == 'workflow_run' &&
github.event.workflow_run.conclusion == 'success' &&
startsWith(github.event.workflow_run.head_branch, 'claude/')
runs-on: ubuntu-latest
outputs:
pr_found: ${{ steps.pr.outputs.found }}
pr_number: ${{ steps.pr.outputs.number }}
pr_title: ${{ steps.pr.outputs.title }}
branch: ${{ steps.pr.outputs.branch }}
steps:
- name: Find PR for branch
id: pr
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
with:
script: |
const branch = context.payload.workflow_run.head_branch;
const { data: pulls } = await github.rest.pulls.list({
owner: context.repo.owner,
repo: context.repo.repo,
head: `${context.repo.owner}:${branch}`,
state: 'open'
});

if (pulls.length === 0) {
core.info('No open PR found for branch ' + branch);
core.setOutput('found', 'false');
return;
}

const pr = pulls[0];
core.setOutput('found', 'true');
core.setOutput('number', String(pr.number));
core.setOutput('title', pr.title);
core.setOutput('branch', branch);

# --- Auto-review after CI passes on claude/* branch ---
auto-review:
needs: resolve-pr
if: needs.resolve-pr.outputs.pr_found == 'true'
# viamrobotics/claude-ci-workflows@v1.17.3
uses: viamrobotics/claude-ci-workflows/.github/workflows/claude-auto-review.yml@3ad96b0ccbb5ee0d7e2cde98653fae0c453e68bb
with:
pr_number: ${{ needs.resolve-pr.outputs.pr_number }}
pr_title: ${{ needs.resolve-pr.outputs.pr_title }}
branch: ${{ needs.resolve-pr.outputs.branch }}
install_command: 'go mod download'
container: 'golang:1.25'
allowed_tools: 'Edit,Read,Write,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(go *),Bash(gofmt *),Bash(make test-go*),Bash(make test-go-no-race*),Bash(make lint*),Bash(make cli*),Bash(make generate-go*),Bash(golangci-lint *),Bash(git config *),Bash(git add *),Bash(git commit *),Bash(git push *),Bash(git status*),Bash(git diff*),Bash(git -C * diff*),Bash(git log*),Bash(git checkout *),Bash(git branch *),Bash(git rev-parse *),Bash(git fetch *),Bash(gh pr review*),Bash(gh pr comment*),Bash(gh pr diff*),Bash(gh pr view*),Bash(gh api repos/*/pulls/*/comments*)'
extra_review_instructions: |
- Ensure `api/**` (generated proto stubs) was not modified.
- Ensure `.github/**`, `Makefile`, `go.mod`, and `go.sum` were not modified.
- For CLI changes, verify `cli/CLAUDE.md` conventions are followed (typed args via `createActionCommandWithT[T]()`, `AliasStringFlag` for entity flags).
- For Go SDK changes, verify methods take `ctx context.Context` as the first parameter after the receiver and do not store context in structs.
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
SLACK_AI_WORKFLOW_ALERT_WEBHOOK_URL: ${{ secrets.SLACK_AI_WORKFLOW_ALERT_WEBHOOK_URL }}

# --- On-demand review or fix via @claude mention ---
on-demand:
if: >-
github.repository_owner == 'viamrobotics' &&
(
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude'))
)
# viamrobotics/claude-ci-workflows@v1.17.3
uses: viamrobotics/claude-ci-workflows/.github/workflows/claude-pr-assistant.yml@3ad96b0ccbb5ee0d7e2cde98653fae0c453e68bb
with:
install_command: 'go mod download'
container: 'golang:1.25'
allowed_tools: 'Edit,Read,Write,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(go *),Bash(gofmt *),Bash(make test-go*),Bash(make test-go-no-race*),Bash(make lint*),Bash(make cli*),Bash(make generate-go*),Bash(golangci-lint *),Bash(git config *),Bash(git add *),Bash(git commit *),Bash(git push *),Bash(git status*),Bash(git diff*),Bash(git -C * diff*),Bash(git log*),Bash(git checkout *),Bash(git branch *),Bash(git rev-parse *),Bash(git fetch *),Bash(gh pr diff*),Bash(gh pr view*),Bash(gh api repos/*/pulls/*/comments*)'
extra_review_instructions: |
- Ensure `api/**` (generated proto stubs) was not modified.
- Ensure `.github/**`, `Makefile`, `go.mod`, and `go.sum` were not modified.
- For CLI changes, see `cli/CLAUDE.md` for conventions.
secrets:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GIT_ACCESS_TOKEN: ${{ secrets.GIT_ACCESS_TOKEN }}
70 changes: 70 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Viam RDK

Viam RDK monorepo — `viam-server`, the Go SDK, and the Viam CLI all live here. Go module path: `go.viam.com/rdk`.

## Codebase Structure

```
cli/ — Viam CLI (`viam` command). See cli/CLAUDE.md.
components/<name>/ — Hardware component types (arm, base, camera, etc.)
<name>.go — Interface, Named type, registration
client.go — gRPC client wrapper (Go SDK side)
server.go — gRPC service handler (server side)
fake/ — Fake implementation for tests
services/<name>/ — Service types (vision, motion, slam, navigation, etc.)
robot/ — Robot lifecycle
client/ — RobotClient (Go SDK entry point)
impl/ — Server-side robot implementation
resource/ — Resource registry, base interfaces (e.g., Shaped)
config/ — Config parsing and loading
motionplan/ — Motion planning and constraints
referenceframe/ — Frame system and kinematics
spatialmath/ — Geometries, poses, transforms
module/, modulegeninputs/ — Modular component support
web/cmd/server/ — `viam-server` entrypoint
api/ — Generated protobuf code (NEVER EDIT, regenerated from viamrobotics/api)
testutils/ — Test helpers and mocks
```

All component/service clients follow the same pattern: `client.go` wraps the gRPC stub, `server.go` implements the gRPC service handler, `fake/` provides a test implementation.

## Subpackages with their own CLAUDE.md

Always check for a package-level `CLAUDE.md` in the directory you're working in. If one exists, its conventions take precedence over this file.

- `cli/CLAUDE.md` — Viam CLI

## Go Conventions

- **Formatting**: `gofmt`. Run via `make lint-go`.
- **Linting**: `golangci-lint` via `make lint-go`. Config at `etc/.golangci.yaml`.
- **Method signatures** for component/service/robot client methods follow this pattern:
```go
func (c *client) MethodName(
ctx context.Context,
arg1 Type1,
extra map[string]interface{},
) (ReturnType, error) {
// build proto request, call stub, convert response, return Go type
}
```
- **Context**: `ctx context.Context` is always the first parameter after the receiver. **Never store `context.Context` in a struct** — Go anti-pattern.
- **Errors**: idiomatic `(value, error)`. Return errors, don't panic.
- **Tests**: same package, `*_test.go` naming. Use `go.viam.com/test` for assertions.

## Protected Paths

Enforced at the CI settings level via `.claude/settings.ci.json`:

- `api/**` — generated proto stubs.
- `.github/**` — CI workflows.
- `Makefile`, `go.mod`, `go.sum` — build glue and dependency manifests.

## Verification Commands

- Build everything: `go build ./...`
- Build CLI only: `go build ./cli/...` or `make cli`
- Full test suite (with race detection): `make test-go`
- Faster test suite: `make test-go-no-race`
- Single package: `TEST_TARGET=./path/to/pkg/... make test-go-no-race`
- Lint: `make lint-go`
Loading