Skip to content
Merged
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
4 changes: 3 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ runs:
RUN_ID: ${{ github.run_id }}
RUN_URL: ${{ inputs.run-url }}
JOB_STATUS: ${{ job.status }}
HARNESS_ROLE: ${{ steps.run.outputs.role }}
WAS_SKIPPED: ${{ steps.run.outputs.skipped }}
PR_HEAD_SHA_INPUT: ${{ inputs.pr-head-sha }}
FULLSEND_DIR: ${{ inputs.fullsend-dir }}
Expand Down Expand Up @@ -562,7 +563,8 @@ runs:

FULLSEND_DIR="${FULLSEND_DIR:-${GITHUB_WORKSPACE}}"
RECONCILE_FLAGS=(--repo "${STATUS_REPO}" --number "${STATUS_NUMBER}" --run-id "${RUN_ID}")
RECONCILE_FLAGS+=(--mint-url "${MINT_URL}" --role "${AGENT}")
RESOLVED_ROLE="${HARNESS_ROLE:-${AGENT}}"
RECONCILE_FLAGS+=(--mint-url "${MINT_URL}" --role "${RESOLVED_ROLE}")
RECONCILE_FLAGS+=(--fullsend-dir "${FULLSEND_DIR}")
RECONCILE_FLAGS+=(--job-status "${JOB_STATUS}")
if [[ "${WAS_SKIPPED}" == "true" ]]; then
Expand Down
15 changes: 12 additions & 3 deletions internal/cli/prescript_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ func TestRunAgent_PreScriptNoSkip_ProceedsToSandboxAndRelaysFalse(t *testing.T)

data, err := os.ReadFile(out)
require.NoError(t, err)
assert.Equal(t, "skipped=false\n", string(data))
// role=test is emitted right after the harness loads, before the
// pre-script relay, and must reflect the harness role ("test") rather
// than the agent name ("code") passed to runAgent above (#7000).
assert.Equal(t, "role=test\nskipped=false\n", string(data))
}

// A harness with no pre_script must still relay skipped=false, otherwise
Expand All @@ -137,7 +140,10 @@ func TestRunAgent_NoPreScript_StillRelaysSkippedFalse(t *testing.T) {

data, err := os.ReadFile(out)
require.NoError(t, err)
assert.Equal(t, "skipped=false\n", string(data))
// role=test is emitted right after the harness loads, before the
// pre-script relay, and must reflect the harness role ("test") rather
// than the agent name ("code") passed to runAgent above (#7000).
assert.Equal(t, "role=test\nskipped=false\n", string(data))
}

// The skip path relays skipped=true. Fast: it returns before sandbox
Expand All @@ -156,7 +162,10 @@ func TestRunAgent_PreScriptSkip_RelaysSkippedTrue(t *testing.T) {

data, err := os.ReadFile(out)
require.NoError(t, err)
assert.Equal(t, "skipped=true\nreason=open PR exists\n", string(data))
// role=test is emitted right after the harness loads, before the
// pre-script relay, and must reflect the harness role ("test") rather
// than the agent name ("code") passed to runAgent above (#7000).
assert.Equal(t, "role=test\nskipped=true\nreason=open PR exists\n", string(data))
}

// A relay target that cannot be written must fail the run rather than
Expand Down
43 changes: 43 additions & 0 deletions internal/cli/reconcilestatus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,49 @@ func TestNewReconcileStatusCmd_MintSuccess(t *testing.T) {
require.NoError(t, err)
}

// TestNewReconcileStatusCmd_MintSuccess_CustomRole verifies that a custom
// harness role (e.g., "review" declared by a "grillme" agent) is passed
// through to the MintRequest correctly, not the raw agent name. This is the
// scenario from #7000: grillme declares role: review, and the reconcile step
// must use "review" — not "grillme" — when minting tokens.
func TestNewReconcileStatusCmd_MintSuccess_CustomRole(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte("[]"))
}))
defer srv.Close()

var captured mintclient.MintRequest
origMint := reconcileMintToken
reconcileMintToken = func(_ context.Context, req mintclient.MintRequest) (*mintclient.MintResult, error) {
captured = req
return &mintclient.MintResult{Token: "ghs_minted_token"}, nil
}
defer func() { reconcileMintToken = origMint }()

origForge := reconcileNewForgeClient
reconcileNewForgeClient = func(token string) forge.Client {
return gh.New(token).WithBaseURL(srv.URL)
}
defer func() { reconcileNewForgeClient = origForge }()

t.Setenv("FULLSEND_MINT_URL", "")
t.Setenv("GITHUB_ACTIONS", "true")

cmd := newReconcileStatusCmd()
cmd.SetArgs([]string{
"--repo", "org/repo",
"--number", "7",
"--run-id", "run-1",
"--mint-url", srv.URL,
"--role", "review",
})

err := cmd.Execute()
require.NoError(t, err)
assert.Equal(t, "review", captured.Role, "custom harness role should pass through to MintRequest without aliasing")
}

func TestNewReconcileStatusCmd_MintSuccessCancelled(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
Expand Down
18 changes: 18 additions & 0 deletions internal/cli/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,24 @@ func runAgent(ctx context.Context, agentName, fullsendDir, outputBase, targetRep
return fmt.Errorf("loading harness: %w", err)
}

// Emit the harness-resolved role as a step output so the finalize step
// in action.yml can pass it to reconcile-status --role instead of using
// the raw agent name. Custom agents (e.g., "grillme") may declare a
// role different from their name (e.g., "role: review"), and the mint
// service rejects unrecognized role names. See #7000.
//
// Emitted immediately after the harness loads — h.Role is already
// validated non-empty at this point — rather than later in this
// function, so the output is still available if a subsequent
// validation step aborts the run before reaching the pre-script relay.
if h.Role != "" {
if ghOutput := os.Getenv("GITHUB_OUTPUT"); ghOutput != "" && os.Getenv("GITHUB_ACTIONS") == "true" {
if wErr := writeGitHubOutput(ghOutput, "role", h.Role); wErr != nil {
printer.StepWarn("Could not relay harness role to GITHUB_OUTPUT: " + wErr.Error())
}
}
}

allDeps := append(fetchDeps, baseDeps...)
for _, dep := range allDeps {
if dep.CacheHit {
Expand Down
Loading