From aa444a71b1b39d417846843a33fcb01250267f06 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:22:10 -0700 Subject: [PATCH 01/11] chore: simplify env vars to empty-value shorthand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace ${JIRA_URL} with empty value — both read from host env, but the empty form is cleaner. The ${VAR} syntax is still supported for composition (e.g., JIRA_URL: https://${DOMAIN}). --- README.md | 4 ++-- SPEC.md | 4 ++-- profiles/README.md | 3 ++- profiles/agent-default.yaml | 4 ++-- profiles/agent-ocp.yaml | 4 ++-- profiles/agent-opencode.yaml | 4 ++-- test/configs/agent-all-providers.yaml | 4 ++-- test/configs/agent-atlassian.yaml | 4 ++-- test/configs/agent-multi-provider.yaml | 4 ++-- 9 files changed, 18 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index cd0b5f1..a2c09ac 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: env: ANTHROPIC_BASE_URL: https://inference.local diff --git a/SPEC.md b/SPEC.md index 8cb572c..84be430 100644 --- a/SPEC.md +++ b/SPEC.md @@ -27,8 +27,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: - profile: google-workspace env: diff --git a/profiles/README.md b/profiles/README.md index f768877..59e097b 100644 --- a/profiles/README.md +++ b/profiles/README.md @@ -23,7 +23,8 @@ providers: # credential providers to register - profile: google-vertex-ai - profile: atlassian env: # non-secret env vars for this provider - JIRA_URL: ${JIRA_URL} # ${VAR} reads from host environment + JIRA_URL: # empty = read from host env + JIRA_USERNAME: env: # additional env vars injected into sandbox ANTHROPIC_BASE_URL: https://inference.local diff --git a/profiles/agent-default.yaml b/profiles/agent-default.yaml index 5570c7b..48c160d 100644 --- a/profiles/agent-default.yaml +++ b/profiles/agent-default.yaml @@ -13,8 +13,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: - profile: google-workspace env: diff --git a/profiles/agent-ocp.yaml b/profiles/agent-ocp.yaml index 57ba467..0ca7061 100644 --- a/profiles/agent-ocp.yaml +++ b/profiles/agent-ocp.yaml @@ -16,8 +16,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: - profile: google-workspace env: diff --git a/profiles/agent-opencode.yaml b/profiles/agent-opencode.yaml index e9ba575..2976ab7 100644 --- a/profiles/agent-opencode.yaml +++ b/profiles/agent-opencode.yaml @@ -16,8 +16,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: - profile: google-workspace env: diff --git a/test/configs/agent-all-providers.yaml b/test/configs/agent-all-providers.yaml index 00b6be7..76f5ae0 100644 --- a/test/configs/agent-all-providers.yaml +++ b/test/configs/agent-all-providers.yaml @@ -9,8 +9,8 @@ providers: - profile: google-vertex-ai - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: - profile: google-workspace env: ANTHROPIC_BASE_URL: https://inference.local diff --git a/test/configs/agent-atlassian.yaml b/test/configs/agent-atlassian.yaml index 3c9fb4c..7fb6c5f 100644 --- a/test/configs/agent-atlassian.yaml +++ b/test/configs/agent-atlassian.yaml @@ -6,5 +6,5 @@ entrypoint: bash providers: - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: diff --git a/test/configs/agent-multi-provider.yaml b/test/configs/agent-multi-provider.yaml index 3dd15db..48da141 100644 --- a/test/configs/agent-multi-provider.yaml +++ b/test/configs/agent-multi-provider.yaml @@ -6,7 +6,7 @@ providers: - profile: github - profile: atlassian env: - JIRA_URL: ${JIRA_URL} - JIRA_USERNAME: ${JIRA_USERNAME} + JIRA_URL: + JIRA_USERNAME: env: TEST_MARKER: "multi-provider-test" From f715e61ae188ffce6dca902faf2357ae28bbb335 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:24:18 -0700 Subject: [PATCH 02/11] =?UTF-8?q?docs:=20reorder=20README=20=E2=80=94=20co?= =?UTF-8?q?ding=20agent=20first,=20trim=20getting-results=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 51 ++++++++++++--------------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index a2c09ac..29f0dcd 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,16 @@ harness doctor # check your environment harness apply -f harness.yaml # launch a sandbox ``` +### Coding agent + +Launch an interactive coding session with Claude Code or OpenCode. + +```bash +harness apply -f harness.yaml --attach # local Podman +harness apply -f harness.yaml --attach --gateway openshift # on OpenShift +harness apply -f harness.yaml --attach --entrypoint opencode # OpenCode +``` + ### One-shot tasks Run a task headlessly -- the agent executes in a sandbox and outputs results. @@ -23,9 +33,7 @@ harness apply -f harness.yaml --task @skills/cpp-pro/SKILL.md ### Clone a repo into the sandbox -The `repo` field clones a repository outside the sandbox and uploads it. Git credentials never enter the sandbox unless needed. - -Use `base_agent` to inherit providers, env, and payloads from an existing config — you only specify what's different: +Use `base_agent` to inherit providers and inference routing from an existing config. The `repo` field clones the repository outside the sandbox and uploads it -- OpenShell sandboxes have no host mounts by design. ```yaml name: reviewer @@ -34,46 +42,11 @@ repo: https://github.com/stackrox/collector task: "identify the highest-priority C++ remediation" ``` -This inherits everything from `agent-default.yaml` (providers, inference routing, payloads) and adds the repo and task. Without `base_agent`, you'd need to specify the inference provider and env vars yourself. - ```bash harness apply -f reviewer.yaml ``` -### Getting results out - -The agent runs in an isolated sandbox. To extract results: - -```bash -# Agent outputs to stdout (--task mode) -harness apply -f harness.yaml --task "summarize the codebase" > results.md - -# Pull a specific file from the sandbox -openshell sandbox exec -- cat /sandbox/collector/report.md > report.md - -# Extract a diff -openshell sandbox exec -- git -C /sandbox/collector diff > changes.patch - -# Download files -openshell sandbox exec -- tar czf - /sandbox/collector/output/ > output.tar.gz -``` - -If the `github` provider is attached, the agent can push directly -- the proxy provides a scoped `GITHUB_TOKEN` without exposing raw credentials. - -### Coding agent - -Launch an interactive coding session with Claude Code or OpenCode. - -```bash -# Local (Podman) -harness apply -f harness.yaml --attach - -# On OpenShift -harness apply -f harness.yaml --attach --gateway openshift - -# OpenCode instead of Claude -harness apply -f harness.yaml --attach --entrypoint opencode -``` +To get results out: `--task` mode outputs to stdout, `openshell sandbox exec` pulls files, or attach a `github` provider so the agent can push directly via the scoped proxy token. ## Why this exists From 772337d2f181476c2cf24518d5ec9623fc1bd0c4 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:24:50 -0700 Subject: [PATCH 03/11] docs: mention openshell term for policy management --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 29f0dcd..7d63422 100644 --- a/README.md +++ b/README.md @@ -127,13 +127,17 @@ harness apply -f config.yaml OpenShell provides the runtime isolation. The harness provides the workflow. -For runtime operations, use openshell directly: +For runtime operations and policy management, use openshell directly: ```bash openshell sandbox connect # interactive shell openshell sandbox exec -- ... # run commands openshell sandbox logs # view logs +openshell policy get # inspect active policy +openshell term # interactive policy terminal ``` +`openshell term` provides a live view of policy decisions -- which requests are allowed, denied, or pending review. This is how you audit and tune the deny-by-default L7 network policy while an agent is running. + ## Install ```bash From ff0555cfa8639fa9cf3ca82d22b5d67efd4218c0 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:26:02 -0700 Subject: [PATCH 04/11] docs: add future work section --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 7d63422..7f73e4a 100644 --- a/README.md +++ b/README.md @@ -211,6 +211,13 @@ make test-remote # full e2e on OCP (needs KUBECONFIG) Each integration target builds (and pushes, for remote) the sandbox image automatically. +## Future Work + +- **GitHub Action** -- run harness tasks in CI (review PRs, enforce standards, generate reports) +- **Observability** -- structured telemetry export (Langfuse, MLflow, OpenTelemetry) for agent tool calls, token usage, and policy decisions +- **Skills integration** -- first-class support for community skill packs (e.g., [awesome-omni-skills](https://github.com/diegosouzapw/awesome-omni-skills)) as task inputs +- **Linux validation** -- CI and local testing on Linux (currently macOS-only) + ## Documentation | Document | What it is | From 28c09514d48f12bb059e136080bf8d84547bbc10 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:31:02 -0700 Subject: [PATCH 05/11] Clarify harness commands and OpenShell features Updated README to clarify harness usage and OpenShell description. --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 7f73e4a..a0ff77e 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ harness apply -f harness.yaml # launch a sandbox Launch an interactive coding session with Claude Code or OpenCode. ```bash -harness apply -f harness.yaml --attach # local Podman -harness apply -f harness.yaml --attach --gateway openshift # on OpenShift +harness apply --attach # local Podman with built-in harness +harness apply -f harness.yaml --attach --gateway openshift # Agent config in harness.yaml on OpenShift harness apply -f harness.yaml --attach --entrypoint opencode # OpenCode ``` @@ -50,11 +50,11 @@ To get results out: `--task` mode outputs to stdout, `openshell sandbox exec` pu ## Why this exists -[OpenShell](https://github.com/NVIDIA/OpenShell) is a foundation layer -- sandboxed containers with deny-by-default L7 network policy, credential proxy, Landlock filesystem isolation, and inference routing. It is designed as a strict, secure base that other tooling builds workflows on top of. +[OpenShell](https://github.com/NVIDIA/OpenShell) is a sandbox managment layer with deny-by-default L7 network policy, credential proxy, filesystem isolation, and inference routing. It is designed as a strict, secure base that supports other workflows. -The harness is one such workflow layer. One YAML file defines the agent, providers, payloads, and policy. One command deploys it -- locally via Podman or remotely on Kubernetes. +One YAML file defines the agent, providers, payloads, and policy and one command deploys it via Podman or remotely on Kubernetes. -OpenShell's upstream direction is toward a [Kubernetes Operator](https://github.com/NVIDIA/OpenShell/issues/1719) where providers and sandboxes become CRDs and the gateway narrows to data-plane only. The harness explores what the workflow layer looks like above that -- and covers the local Podman development path that no operator will own. +OpenShell's upstream direction is toward a [Kubernetes Operator](https://github.com/NVIDIA/OpenShell/issues/1719) where providers and sandboxes become CRDs and the gateway narrows to data-plane only. The harness explores what the workflow layer looks like above that with a develper mindset from local machine to cluster. ## The Agent YAML From 0da716244500069d5fbc8296f51d16289f370306 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:32:50 -0700 Subject: [PATCH 06/11] fix: typos in README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a0ff77e..f198892 100644 --- a/README.md +++ b/README.md @@ -50,11 +50,11 @@ To get results out: `--task` mode outputs to stdout, `openshell sandbox exec` pu ## Why this exists -[OpenShell](https://github.com/NVIDIA/OpenShell) is a sandbox managment layer with deny-by-default L7 network policy, credential proxy, filesystem isolation, and inference routing. It is designed as a strict, secure base that supports other workflows. +[OpenShell](https://github.com/NVIDIA/OpenShell) is a sandbox management layer with deny-by-default L7 network policy, credential proxy, filesystem isolation, and inference routing. It is designed as a strict, secure base that supports other workflows. One YAML file defines the agent, providers, payloads, and policy and one command deploys it via Podman or remotely on Kubernetes. -OpenShell's upstream direction is toward a [Kubernetes Operator](https://github.com/NVIDIA/OpenShell/issues/1719) where providers and sandboxes become CRDs and the gateway narrows to data-plane only. The harness explores what the workflow layer looks like above that with a develper mindset from local machine to cluster. +OpenShell's upstream direction is toward a [Kubernetes Operator](https://github.com/NVIDIA/OpenShell/issues/1719) where providers and sandboxes become CRDs and the gateway narrows to data-plane only. The harness explores what the workflow layer looks like above that with a developer mindset from local machine to cluster. ## The Agent YAML From 2c305d64088ce60e4455e7d4ac675c7e5c7d6c77 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:39:29 -0700 Subject: [PATCH 07/11] docs: richer agent YAML examples with real default config Show the actual default config with all four providers and payloads. Multi-doc example uses base_agent, repo, inline CLAUDE.md, and L7 policy with git clone/push rules. Note harness apply -o yaml for the full resolved output. --- README.md | 59 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index f198892..280ffb5 100644 --- a/README.md +++ b/README.md @@ -58,61 +58,76 @@ OpenShell's upstream direction is toward a [Kubernetes Operator](https://github. ## The Agent YAML -A single file defines what runs, what credentials it gets, and what files are uploaded to the sandbox. +A single file defines the entrypoint, credential providers, inference routing, environment, and files uploaded to the sandbox. This is the default config (`profiles/agent-default.yaml`): ```yaml name: agent entrypoint: claude tty: true -repo: https://github.com/stackrox/collector # cloned outside sandbox, uploaded in providers: - - profile: github - - profile: google-vertex-ai - - profile: atlassian + - profile: github # scoped GITHUB_TOKEN via proxy + - profile: google-vertex-ai # inference routing through gateway + - profile: atlassian # Jira/Confluence via mcp-atlassian env: - JIRA_URL: + JIRA_URL: # empty = read from host env JIRA_USERNAME: + - profile: google-workspace # Gmail, Calendar, Drive via gws CLI env: - ANTHROPIC_BASE_URL: https://inference.local + ANTHROPIC_BASE_URL: https://inference.local # route inference through gateway proxy + ANTHROPIC_API_KEY: sk-ant-openshell-proxy-managed + CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1" payloads: - - sandbox_path: /sandbox/.claude/CLAUDE.md + - sandbox_path: /sandbox/.claude/CLAUDE.md # agent instructions local_path: profiles/images/sandbox-default/CLAUDE.md - - sandbox_path: /sandbox/.mcp.json + - sandbox_path: /sandbox/.claude.json # claude code settings + local_path: profiles/images/sandbox-default/claude.json + - sandbox_path: /sandbox/.claude/settings.json # permissions and defaults + local_path: profiles/images/sandbox-default/settings.json + - sandbox_path: /sandbox/.mcp.json # MCP server config (jira, confluence) local_path: profiles/images/sandbox-default/mcp.json ``` +Credentials never enter the sandbox -- the gateway proxy resolves placeholder tokens at the network boundary. Each provider also contributes its own L7 network policy endpoints and binary allowlists. + +Use `harness apply -o yaml` to see the fully resolved config -- providers expand to show credential definitions, endpoint policies, scopes, and refresh strategies. + ### Multi-document YAML -Bundle agent, providers, payloads, and policy in one file: +Bundle agent, providers, payloads, and policy in one self-contained file. Use `base_agent` to inherit from an existing config: ```yaml --- kind: agent -name: cpp-reviewer -entrypoint: claude -providers: - - profile: github ---- -kind: provider -name: github -type: github -credentials: [GITHUB_TOKEN] +name: security-reviewer +base_agent: default # inherits providers, env, payloads +repo: https://github.com/stackrox/collector +task: "review for memory safety issues" --- kind: payload sandbox_path: /sandbox/.claude/CLAUDE.md content: | - You are a C++ security review agent. + You are a C++ security review agent specializing in RAII, + move semantics, and concurrency safety. Focus on the + highest-priority remediation and explain the fix. --- kind: policy network_policies: - github: + github_git: endpoints: - - { host: "api.github.com", port: 443 } + - host: github.com + port: 443 + rules: + - allow: { method: GET, path: "/**/info/refs*" } + - allow: { method: POST, path: "/**/git-upload-pack" } + binaries: + - { path: /usr/bin/git } ``` +This inherits all four providers and inference routing from `agent-default.yaml`, adds a custom CLAUDE.md as the agent's instructions, and defines an L7 policy that allows git clone but blocks git push at the HTTP method level. + ## How It Works ``` From d2152f90369ecbd757ad06280552a5c98d6a1a07 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:43:44 -0700 Subject: [PATCH 08/11] chore: move module path to github.com/stackrox/harness-openshell Update go.mod module path, all import statements, README download URL, and release docs to reflect the new repo location under stackrox. --- README.md | 2 +- cmd/apply.go | 6 +++--- cmd/delete.go | 6 +++--- cmd/deploy.go | 6 +++--- cmd/deploy_test.go | 4 ++-- cmd/describe.go | 4 ++-- cmd/doctor.go | 4 ++-- cmd/doctor_test.go | 2 +- cmd/executor.go | 8 ++++---- cmd/executor_test.go | 2 +- cmd/get.go | 2 +- cmd/helpers_test.go | 2 +- cmd/init_cmd.go | 2 +- cmd/init_cmd_test.go | 2 +- cmd/providers.go | 6 +++--- cmd/providers_test.go | 2 +- cmd/resolve.go | 4 ++-- cmd/sandbox.go | 4 ++-- cmd/status_cmd.go | 4 ++-- cmd/status_cmd_test.go | 2 +- cmd/teardown.go | 6 +++--- cmd/teardown_test.go | 4 ++-- docs/release-plan.md | 2 +- go.mod | 2 +- internal/gateway/cli.go | 2 +- internal/k8s/kubectl.go | 2 +- main.go | 4 ++-- 27 files changed, 48 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 280ffb5..ae83d8e 100644 --- a/README.md +++ b/README.md @@ -160,7 +160,7 @@ openshell term # interactive policy terminal brew tap nvidia/openshell && brew install openshell && brew services start openshell # Download the harness binary -curl -L https://github.com/robbycochran/harness-openshell/releases/latest/download/harness_darwin_arm64 -o harness +curl -L https://github.com/stackrox/harness-openshell/releases/latest/download/harness_darwin_arm64 -o harness chmod +x harness ``` diff --git a/cmd/apply.go b/cmd/apply.go index 467a4fd..8a8ce4d 100644 --- a/cmd/apply.go +++ b/cmd/apply.go @@ -7,9 +7,9 @@ import ( "strings" "time" - "github.com/robbycochran/harness-openshell/internal/agent" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/delete.go b/cmd/delete.go index 0ada7e0..a9513cb 100644 --- a/cmd/delete.go +++ b/cmd/delete.go @@ -3,9 +3,9 @@ package cmd import ( "fmt" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/deploy.go b/cmd/deploy.go index 7ef884d..76ec8f0 100644 --- a/cmd/deploy.go +++ b/cmd/deploy.go @@ -9,9 +9,9 @@ import ( "strings" "time" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/deploy_test.go b/cmd/deploy_test.go index 1b57f3d..5a62279 100644 --- a/cmd/deploy_test.go +++ b/cmd/deploy_test.go @@ -6,8 +6,8 @@ import ( "path/filepath" "testing" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" ) func setupDeployHarnessDir(t *testing.T) string { diff --git a/cmd/describe.go b/cmd/describe.go index 1356254..4c0fd4f 100644 --- a/cmd/describe.go +++ b/cmd/describe.go @@ -3,8 +3,8 @@ package cmd import ( "fmt" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/doctor.go b/cmd/doctor.go index aad563c..4560c3a 100644 --- a/cmd/doctor.go +++ b/cmd/doctor.go @@ -7,8 +7,8 @@ import ( "path/filepath" "strings" - "github.com/robbycochran/harness-openshell/internal/agent" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/gateway" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) diff --git a/cmd/doctor_test.go b/cmd/doctor_test.go index ad4c7d0..517d42e 100644 --- a/cmd/doctor_test.go +++ b/cmd/doctor_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/robbycochran/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/agent" ) func TestCheckOpenShell_Found(t *testing.T) { diff --git a/cmd/executor.go b/cmd/executor.go index d63a5b9..5c8d265 100644 --- a/cmd/executor.go +++ b/cmd/executor.go @@ -9,10 +9,10 @@ import ( "strings" "time" - "github.com/robbycochran/harness-openshell/internal/agent" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/status" ) var Version = "dev" diff --git a/cmd/executor_test.go b/cmd/executor_test.go index b5c372b..0bcb15b 100644 --- a/cmd/executor_test.go +++ b/cmd/executor_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/gateway" ) func TestUpLocal_NoGateway(t *testing.T) { diff --git a/cmd/get.go b/cmd/get.go index 37db09f..4a815cc 100644 --- a/cmd/get.go +++ b/cmd/get.go @@ -3,7 +3,7 @@ package cmd import ( "fmt" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/gateway" "github.com/spf13/cobra" ) diff --git a/cmd/helpers_test.go b/cmd/helpers_test.go index d47ab58..386a7bc 100644 --- a/cmd/helpers_test.go +++ b/cmd/helpers_test.go @@ -6,7 +6,7 @@ import ( "path/filepath" "testing" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/gateway" ) func init() { diff --git a/cmd/init_cmd.go b/cmd/init_cmd.go index 5450312..63fa270 100644 --- a/cmd/init_cmd.go +++ b/cmd/init_cmd.go @@ -9,7 +9,7 @@ import ( "strconv" "strings" - "github.com/robbycochran/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/agent" "github.com/spf13/cobra" "gopkg.in/yaml.v3" ) diff --git a/cmd/init_cmd_test.go b/cmd/init_cmd_test.go index 8a27064..c9f4b9e 100644 --- a/cmd/init_cmd_test.go +++ b/cmd/init_cmd_test.go @@ -7,7 +7,7 @@ import ( "strings" "testing" - "github.com/robbycochran/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/agent" "gopkg.in/yaml.v3" ) diff --git a/cmd/providers.go b/cmd/providers.go index 0a803c1..b04eb08 100644 --- a/cmd/providers.go +++ b/cmd/providers.go @@ -8,9 +8,9 @@ import ( "path/filepath" "strings" - "github.com/robbycochran/harness-openshell/internal/agent" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/status" "gopkg.in/yaml.v3" ) diff --git a/cmd/providers_test.go b/cmd/providers_test.go index ed29917..effbe6c 100644 --- a/cmd/providers_test.go +++ b/cmd/providers_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - "github.com/robbycochran/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/agent" ) func setupProvidersTest(t *testing.T) string { diff --git a/cmd/resolve.go b/cmd/resolve.go index 73c70a3..e2af7fa 100644 --- a/cmd/resolve.go +++ b/cmd/resolve.go @@ -9,8 +9,8 @@ import ( "sort" "strings" - "github.com/robbycochran/harness-openshell/internal/agent" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/agent" + "github.com/stackrox/harness-openshell/internal/gateway" ) diff --git a/cmd/sandbox.go b/cmd/sandbox.go index d3e49bd..e59ae3b 100644 --- a/cmd/sandbox.go +++ b/cmd/sandbox.go @@ -6,8 +6,8 @@ import ( "path/filepath" "time" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/status" ) // sandboxOpts holds the parameters that vary between callers of diff --git a/cmd/status_cmd.go b/cmd/status_cmd.go index b8c3d27..ebc6071 100644 --- a/cmd/status_cmd.go +++ b/cmd/status_cmd.go @@ -3,8 +3,8 @@ package cmd import ( "fmt" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/status_cmd_test.go b/cmd/status_cmd_test.go index e61fdbd..bd9a50d 100644 --- a/cmd/status_cmd_test.go +++ b/cmd/status_cmd_test.go @@ -3,7 +3,7 @@ package cmd import ( "testing" - "github.com/robbycochran/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/gateway" ) type statusMockGW struct { diff --git a/cmd/teardown.go b/cmd/teardown.go index 8b11f22..d0ed5c8 100644 --- a/cmd/teardown.go +++ b/cmd/teardown.go @@ -6,9 +6,9 @@ import ( "strings" "time" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) diff --git a/cmd/teardown_test.go b/cmd/teardown_test.go index ae8c7c3..7a07a37 100644 --- a/cmd/teardown_test.go +++ b/cmd/teardown_test.go @@ -4,8 +4,8 @@ import ( "fmt" "testing" - "github.com/robbycochran/harness-openshell/internal/gateway" - "github.com/robbycochran/harness-openshell/internal/k8s" + "github.com/stackrox/harness-openshell/internal/gateway" + "github.com/stackrox/harness-openshell/internal/k8s" ) func TestTeardownK8s_NamespaceNotFound(t *testing.T) { diff --git a/docs/release-plan.md b/docs/release-plan.md index f6d698a..51025ae 100644 --- a/docs/release-plan.md +++ b/docs/release-plan.md @@ -99,7 +99,7 @@ GitHub Releases only (no Homebrew tap — private repo at Red Hat). **One-liner (requires `gh` auth):** ```bash -gh release download --repo robbycochran/harness-openshell -p '*darwin_arm64*' -O- | tar xz +gh release download --repo stackrox/harness-openshell -p '*darwin_arm64*' -O- | tar xz sudo mv harness /usr/local/bin/ harness init ``` diff --git a/go.mod b/go.mod index 6122204..1e8cdb0 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/robbycochran/harness-openshell +module github.com/stackrox/harness-openshell go 1.22.4 diff --git a/internal/gateway/cli.go b/internal/gateway/cli.go index 7316980..310501e 100644 --- a/internal/gateway/cli.go +++ b/internal/gateway/cli.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/status" ) var ansiRE = regexp.MustCompile(`\x1b\[[0-9;]*m`) diff --git a/internal/k8s/kubectl.go b/internal/k8s/kubectl.go index ed45093..db442cb 100644 --- a/internal/k8s/kubectl.go +++ b/internal/k8s/kubectl.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/internal/status" "gopkg.in/yaml.v3" ) diff --git a/main.go b/main.go index 7b0c897..b8c6efb 100644 --- a/main.go +++ b/main.go @@ -6,8 +6,8 @@ import ( "os" "path/filepath" - "github.com/robbycochran/harness-openshell/cmd" - "github.com/robbycochran/harness-openshell/internal/status" + "github.com/stackrox/harness-openshell/cmd" + "github.com/stackrox/harness-openshell/internal/status" "github.com/spf13/cobra" ) From edbd596ec12522bfd8994bbd07e3414bc284c99e Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:47:44 -0700 Subject: [PATCH 09/11] chore: add coderabbit review config --- .coderabbit.yaml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .coderabbit.yaml diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 0000000..20519ac --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,28 @@ +language: en-US +reviews: + profile: chill + request_changes_workflow: false + high_level_summary: true + poem: false + review_status: true + collapse_walkthrough: false + path_instructions: + - path: "**/*.go" + instructions: | + This is a Go CLI that wraps the OpenShell CLI. Focus on: + - Correct error handling (wrap with context, don't swallow) + - CLI argument construction (exec.Command, not shell) + - Credential handling (never log secrets, never pass via CLI args if avoidable) + - Gateway interface usage (code should go through the Gateway interface, not call openshell directly) + - path: "profiles/**/*.yaml" + instructions: | + These are OpenShell provider profiles and gateway configs. + Check that credential env_vars match what the Go code expects. + Check that endpoint hosts and ports are correct for the service. + - path: "test/**" + instructions: | + Integration test scripts. Check that gateway names match + the current naming (local-container, helm, openshift). + Check that test assertions match current CLI output format. +chat: + auto_reply: true From 9c3ac5ab480950a77f6fae25889a2013335f5be4 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:49:45 -0700 Subject: [PATCH 10/11] chore: enrich coderabbit config with project knowledge Add architecture context, file-specific review instructions for SPEC.md, AGENTS.md, agent configs, provider profiles, gateway profiles, sandbox image files, and CI workflows. Enable knowledge base with auto-scoped learnings. --- .coderabbit.yaml | 77 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 20519ac..4b8122e 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -6,23 +6,88 @@ reviews: poem: false review_status: true collapse_walkthrough: false + knowledge_base: + learnings: + scope: auto + issues: + scope: auto + jira: + project_keys: [] + linear: + team_keys: [] path_instructions: - path: "**/*.go" instructions: | - This is a Go CLI that wraps the OpenShell CLI. Focus on: + This is a Go CLI ("harness") that wraps the OpenShell CLI to deploy + AI agent sandboxes. The harness orchestrates gateway deployment, + provider registration, payload upload, and sandbox creation. + + Key architecture: + - cmd/executor.go: main apply flow (upLocal) + - cmd/providers.go: provider registration (registerStandard, registerADC, registerGWS) + - cmd/resolve.go: config resolution, base_agent merging, gateway auto-discovery + - internal/agent/agent.go: AgentConfig parsing, MergeOver, BuildEnvMap, RenderPayload + - internal/gateway/: Gateway interface + CLI implementation (shells out to openshell) + + Review focus: - Correct error handling (wrap with context, don't swallow) - CLI argument construction (exec.Command, not shell) - Credential handling (never log secrets, never pass via CLI args if avoidable) - Gateway interface usage (code should go through the Gateway interface, not call openshell directly) - - path: "profiles/**/*.yaml" + - base_agent merge semantics (overlay wins for scalars, additive for providers/env) + - repo clone (happens outside sandbox, git creds never enter unless needed) + - path: "SPEC.md" instructions: | - These are OpenShell provider profiles and gateway configs. - Check that credential env_vars match what the Go code expects. + This is the authoritative behavior spec for the harness CLI. + All field names, command behavior, and config semantics must + match the Go implementation. Flag changes in code that aren't + reflected here, or spec claims that don't match the code. + - path: "AGENTS.md" + instructions: | + Contributor guide. Check that coding conventions, test + instructions, and upstream references are current. + - path: "profiles/agent-*.yaml" + instructions: | + Agent config files. These define what runs in a sandbox. + Key fields: name, base_agent, entrypoint, repo, repo_ref, + providers (with profile refs and env overrides), env, payloads, + task, gateway, policy, image. Empty env values read from host + environment. Check consistency with SPEC.md field definitions. + - path: "profiles/providers/**" + instructions: | + OpenShell provider profile YAMLs imported to the gateway. + These define credential discovery (env_vars), auth style + (bearer/basic), refresh strategies (oauth2-refresh-token), + L7 network policy endpoints, and binary allowlists. + Check that credential env_vars match what cmd/providers.go expects. Check that endpoint hosts and ports are correct for the service. + - path: "profiles/gateways/**" + instructions: | + Gateway deployment profiles. Current targets: + - local-container.yaml (Podman, type: local) + - helm.yaml (kind cluster, type: remote, service: nodeport) + - openshift.yaml (OCP, type: remote, service: route) + New gateways are auto-discovered by scanning this directory. + - path: "profiles/images/sandbox-default/**" + instructions: | + Sandbox image files: Dockerfile, CLAUDE.md (agent instructions), + claude.json/settings.json (Claude Code config), mcp.json (MCP + server config for Jira/Confluence), opencode.json, policy.yaml + (L7 network policy with per-provider endpoint rules). + These are the defaults — overridable via payloads in agent configs. - path: "test/**" instructions: | - Integration test scripts. Check that gateway names match - the current naming (local-container, helm, openshift). + Integration test scripts. Gateway names must match current + naming (local-container, helm, openshift). test-flow.sh is + the primary e2e test. kind-lifecycle.sh creates its own cluster. Check that test assertions match current CLI output format. + - path: ".github/workflows/**" + instructions: | + CI workflows. images.yml pushes to quay.io/rcochran/openshell + using REGISTRY_QUAY_USER/REGISTRY_QUAY_PASSWORD secrets. + integration.yml runs test-local (Dockerfile path for image), + test-kind (Docker build + kind load), and config suite. + The local job uses HARNESS_OS_IMAGE=profiles/images/sandbox-default + so openshell builds from Dockerfile directly. chat: auto_reply: true From 64aa9dc2d42a0fafc9e6f3c8c9f04dab6199d3f0 Mon Sep 17 00:00:00 2001 From: Robby Cochran Date: Wed, 17 Jun 2026 16:51:34 -0700 Subject: [PATCH 11/11] docs: add openshell plugin to future work --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ae83d8e..b8cb17c 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,7 @@ Each integration target builds (and pushes, for remote) the sandbox image automa - **GitHub Action** -- run harness tasks in CI (review PRs, enforce standards, generate reports) - **Observability** -- structured telemetry export (Langfuse, MLflow, OpenTelemetry) for agent tool calls, token usage, and policy decisions - **Skills integration** -- first-class support for community skill packs (e.g., [awesome-omni-skills](https://github.com/diegosouzapw/awesome-omni-skills)) as task inputs +- **OpenShell plugin** -- register the harness as an `openshell` CLI plugin so `openshell harness apply` works natively alongside other openshell commands - **Linux validation** -- CI and local testing on Linux (currently macOS-only) ## Documentation