From 8646c280bea228fef57e0baf2b0030c845efaa7f Mon Sep 17 00:00:00 2001 From: Ben Alkov Date: Wed, 15 Apr 2026 22:06:13 +0000 Subject: [PATCH 1/5] docs: code agent post-script and provider design spec Automation layer that takes the code agent's local commit from the sandbox and turns it into a pushed branch and PR. Three files: harness YAML, provider YAML, and a post-script shell script. Depends on repo extraction (in-flight) and PR #231 (fullsend run CLI). Assisted-by: Claude Code (Opus 4.6) --- ...026-04-15-code-agent-post-script-design.md | 171 ++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-15-code-agent-post-script-design.md diff --git a/docs/superpowers/specs/2026-04-15-code-agent-post-script-design.md b/docs/superpowers/specs/2026-04-15-code-agent-post-script-design.md new file mode 100644 index 0000000000..824901a1b2 --- /dev/null +++ b/docs/superpowers/specs/2026-04-15-code-agent-post-script-design.md @@ -0,0 +1,171 @@ +# Code Agent Post-Script and Provider + +Date: 2026-04-15 + +## Problem + +The code agent runs inside an OpenShell sandbox, reads an issue, implements a +fix, and commits to a feature branch. When the sandbox is destroyed, the commit +is lost. The automation layer that turns the agent's local commit into a pushed +branch and PR does not exist yet. + +This is the post-script component of the code agent harness -- the credentialed +write-back step described in [ADR-0017][adr-0017]. It runs on the host after +sandbox cleanup, outside the agent's isolation boundary. + +## Dependencies + +- **Repo extraction** (Marta): Modified repo must be extracted from the sandbox + into `runDir/repo/` before teardown. The post-script assumes this contract. +- **PR #231** (`fullsend run` CLI): The `Harness` struct and `run.go` execution + flow already support `post_script`, `providers`, and `runner_env` fields. This + work produces configuration that plugs into that machinery. +- **`code.yml` workflow**: Must export `ISSUE_NUMBER` and `REPO_FULL_NAME` as + environment variables before calling `fullsend run`. + +## Design + +### File layout + +``` +.fullsend/ + harness/code.yaml # harness definition for the code agent + providers/github.yaml # GH_TOKEN provider + scripts/post-code.sh # post-script: push branch + open PR +``` + +### Provider: `.fullsend/providers/github.yaml` + +```yaml +name: github +type: generic +credentials: + GH_TOKEN: "${FULLSEND_CODE_BOT_TOKEN}" +``` + +Maps the workflow-level `FULLSEND_CODE_BOT_TOKEN` (set by `setup-agent-env.sh` +after prefix-stripping) to `GH_TOKEN` for the post-script. The token is +available only on the host side; it never enters the sandbox (per ADR-0017). + +### Harness: `.fullsend/harness/code.yaml` + +```yaml +agent: agents/code.md +image: quay.io/manonru/fullsend-exp:latest +policy: policies/code.yaml + +providers: + - github + +post_script: scripts/post-code.sh + +runner_env: + ISSUE_NUMBER: "${ISSUE_NUMBER}" + REPO_FULL_NAME: "${REPO_FULL_NAME}" +``` + +- `agents/code.md` and `policies/code.yaml` are out of scope; the harness + references them for completeness. +- `ISSUE_NUMBER` and `REPO_FULL_NAME` originate from the dispatch workflow's + GitHub event payload. The workflow must export them before calling + `fullsend run`. +- No `pre_script`, `skills`, `host_files`, or `validation_loop` -- those can be + added as the code agent matures. + +### Post-script: `.fullsend/scripts/post-code.sh` + +```bash +#!/usr/bin/env bash +set -euo pipefail + +REPO_DIR="repo" + +if [ ! -d "${REPO_DIR}" ]; then + echo "error: extracted repo not found at ${REPO_DIR}" >&2 + exit 1 +fi + +cd "${REPO_DIR}" + +BRANCH="$(git branch --show-current)" + +if [ -z "${BRANCH}" ] || [ "${BRANCH}" = "main" ]; then + echo "error: agent did not create a feature branch" >&2 + exit 1 +fi + +git remote set-url origin \ + "https://x-access-token:${GH_TOKEN}@github.com/${REPO_FULL_NAME}.git" + +git push -u origin "${BRANCH}" + +gh pr create \ + --repo "${REPO_FULL_NAME}" \ + --head "${BRANCH}" \ + --title "fix: resolve #${ISSUE_NUMBER}" \ + --body "Closes #${ISSUE_NUMBER}" +``` + +#### Behavior + +1. Verifies the extracted repo exists at `repo/` under `runDir` (the working + directory set by `run.go`). +2. Verifies the agent created a feature branch (not `main`, not detached HEAD). +3. Rewrites the git remote to use the GitHub App token for push auth. +4. Pushes the branch. +5. Creates a PR that references and auto-closes the originating issue. + +#### Error cases + +| Condition | Behavior | +|---|---| +| `repo/` missing | Exit 1 with message. Indicates extraction did not run. | +| No branch or on `main` | Exit 1. Agent did not do its job. | +| Push fails | `set -euo pipefail` exits immediately. | +| PR creation fails | Same -- exits with `gh` error. | + +#### Environment contract + +| Variable | Source | Purpose | +|---|---|---| +| `GH_TOKEN` | Provider (github.yaml) | Push auth and `gh` CLI auth | +| `REPO_FULL_NAME` | runner_env (code.yaml) | `owner/repo` for remote URL and PR target | +| `ISSUE_NUMBER` | runner_env (code.yaml) | Issue reference in PR title/body | + +### Execution flow (in context of `run.go`) + +``` +fullsend run code + -> load harness/code.yaml + -> ensure provider "github" (GH_TOKEN available on host) + -> create sandbox, bootstrap agent, copy repo + -> run agent (agent commits to feature branch inside sandbox) + -> extract output files + -> [Marta] extract repo to runDir/repo/ + -> delete sandbox + -> run post-script (scripts/post-code.sh) in runDir + -> push branch, create PR +``` + +The post-script runs in the deferred cleanup in `run.go`, after sandbox +deletion but with access to the extracted repo and host environment. + +## What this does NOT cover + +- The `agents/code.md` agent definition +- The `policies/code.yaml` sandbox network policy +- Repo extraction from the sandbox (Marta's work) +- Review agent integration (separate harness + post-script) +- Retry/failure handling beyond immediate exit +- PR labels, assignees, or reviewer assignment + +## Testing + +End-to-end testing requires repo extraction (in-flight). The post-script can be +validated independently by: + +1. Creating a mock `runDir/repo/` with a git repo on a feature branch +2. Setting `GH_TOKEN`, `REPO_FULL_NAME`, `ISSUE_NUMBER` in the environment +3. Running `post-code.sh` and verifying push + PR creation against a test repo + +[adr-0017]: ../../ADRs/0017-credential-isolation-for-sandboxed-agents.md From 5cdd5ce21ace4b9edc5b969d3c186f9dce4cce70 Mon Sep 17 00:00:00 2001 From: Ben Alkov Date: Wed, 15 Apr 2026 22:29:16 +0000 Subject: [PATCH 2/5] feat(harness): add code agent post-script Pushes the agent feature branch and opens a PR that auto-closes the originating issue. Runs on the host after sandbox teardown with access to the extracted repo and GH_TOKEN. Assisted-by: Claude Code (Sonnet 4.6) --- .fullsend/scripts/post-code.sh | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100755 .fullsend/scripts/post-code.sh diff --git a/.fullsend/scripts/post-code.sh b/.fullsend/scripts/post-code.sh new file mode 100755 index 0000000000..3e0548c0ee --- /dev/null +++ b/.fullsend/scripts/post-code.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Contract: repo extraction (run.go) places the modified repo here +# before the sandbox is deleted. +REPO_DIR="repo" + +if [ ! -d "${REPO_DIR}" ]; then + echo "error: extracted repo not found at ${REPO_DIR}" >&2 + exit 1 +fi + +cd "${REPO_DIR}" + +BRANCH="$(git branch --show-current)" + +if [ -z "${BRANCH}" ] || [ "${BRANCH}" = "main" ]; then + echo "error: agent did not create a feature branch" >&2 + exit 1 +fi + +# Rewrite remote to use the GitHub App token for push auth. +git remote set-url origin \ + "https://x-access-token:${GH_TOKEN}@github.com/${REPO_FULL_NAME}.git" + +git push -u origin "${BRANCH}" + +# Create PR linking to the originating issue. +gh pr create \ + --repo "${REPO_FULL_NAME}" \ + --head "${BRANCH}" \ + --title "fix: resolve #${ISSUE_NUMBER}" \ + --body "Closes #${ISSUE_NUMBER}" From 144b16450e4b269b136ab4a6799ba671fa7ab0ab Mon Sep 17 00:00:00 2001 From: Ben Alkov Date: Wed, 15 Apr 2026 22:29:26 +0000 Subject: [PATCH 3/5] feat(harness): add code agent harness definition Wires the code agent to the GitHub provider, post-script, and runner env vars (ISSUE_NUMBER, REPO_FULL_NAME). References agents/code.md and policies/code.yaml which are out of scope for this change. Assisted-by: Claude Code (Sonnet 4.6) --- .fullsend/harness/code.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 .fullsend/harness/code.yaml diff --git a/.fullsend/harness/code.yaml b/.fullsend/harness/code.yaml new file mode 100644 index 0000000000..d1fac29a14 --- /dev/null +++ b/.fullsend/harness/code.yaml @@ -0,0 +1,12 @@ +agent: agents/code.md +image: quay.io/manonru/fullsend-exp:latest +policy: policies/code.yaml + +providers: + - github + +post_script: scripts/post-code.sh + +runner_env: + ISSUE_NUMBER: "${ISSUE_NUMBER}" + REPO_FULL_NAME: "${REPO_FULL_NAME}" From 816e75d1efd66216126717ce8ebe593c3fb09633 Mon Sep 17 00:00:00 2001 From: Ben Alkov Date: Wed, 15 Apr 2026 22:29:46 +0000 Subject: [PATCH 4/5] feat(harness): add GitHub credential provider for code agent Maps FULLSEND_CODE_BOT_TOKEN to GH_TOKEN on the host side. The token is used by the post-script for push and PR creation and never enters the sandbox (ADR-0017). Assisted-by: Claude Code (Opus 4.6) --- .fullsend/providers/github.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .fullsend/providers/github.yaml diff --git a/.fullsend/providers/github.yaml b/.fullsend/providers/github.yaml new file mode 100644 index 0000000000..a437806985 --- /dev/null +++ b/.fullsend/providers/github.yaml @@ -0,0 +1,4 @@ +name: github +type: generic +credentials: + GH_TOKEN: "${FULLSEND_CODE_BOT_TOKEN}" From 60a334cbbf6c519904c960a3c9763e4dfd43ddaa Mon Sep 17 00:00:00 2001 From: Ben Alkov Date: Wed, 15 Apr 2026 22:32:47 +0000 Subject: [PATCH 5/5] fix(harness): guard against both main and master default branches Assisted-by: Claude Code (Opus 4.6) --- .fullsend/scripts/post-code.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.fullsend/scripts/post-code.sh b/.fullsend/scripts/post-code.sh index 3e0548c0ee..e95e0bff15 100755 --- a/.fullsend/scripts/post-code.sh +++ b/.fullsend/scripts/post-code.sh @@ -14,7 +14,7 @@ cd "${REPO_DIR}" BRANCH="$(git branch --show-current)" -if [ -z "${BRANCH}" ] || [ "${BRANCH}" = "main" ]; then +if [ -z "${BRANCH}" ] || [ "${BRANCH}" = "main" ] || [ "${BRANCH}" = "master" ]; then echo "error: agent did not create a feature branch" >&2 exit 1 fi