result

package
v0.37.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 8, 2026 License: MIT Imports: 11 Imported by: 0

README

result

Posts an agent.Result back to the Rensei platform when a session finishes, in the order locked by F.1.1 §6:

  1. POST /api/sessions/<id>/completion — canonical "session done" hook. The platform side resolves the Linear comment text from the runner-supplied summary and posts it via the platform's getLinearClient resolver.
  2. POST /api/sessions/<id>/status — FSM status transition (completed | failed | stopped) with cost snapshot, provider session id, and worktree path. Triggers the cleanup chain (release claim, archive inbox, release issue lock, promote next pending work) and the lifecycle-hook chain on the platform side.

Both endpoints are worker-auth (runtime_jwt | registration_token | legacy WORKER_API_KEY). The Bearer token is sent unmodified.

Usage

import (
    "context"

    "github.com/RenseiAI/donmai/agent"
    "github.com/RenseiAI/donmai/result"
)

p, err := result.NewPoster(result.Options{
    PlatformURL: "https://app.rensei.ai",
    AuthToken:   runtimeJWT,
    WorkerID:    "wkr_xxx",
})
if err != nil { /* ... */ }

err = p.Post(ctx, sessionID, agent.Result{
    Status:            "completed",
    ProviderName:      agent.ProviderClaude,
    ProviderSessionID: "claude-sess-uuid",
    WorktreePath:      "/tmp/wt/REN-123-DEV",
    PullRequestURL:    "https://github.com/owner/repo/pull/42",
    Summary:           "Implemented X, opened PR.",
    WorkResult:        "passed",
    Cost: &agent.CostData{
        InputTokens:  1234,
        OutputTokens: 567,
        TotalCostUsd: 0.0123,
    },
})

Retry semantics

Both calls are wrapped by a 3-attempt exponential-backoff helper — baseDelay << (attempt-1) defaults to 1s, 2s, 4s. The shape is the verbatim port of the legacy apiRequestWithError from ../agentfactory/packages/cli/src/lib/worker-runner.ts.

Failure Behavior
Network error / DNS / connect refused / timeout Retry up to MaxAttempts, then TransientError.
5xx response Retry up to MaxAttempts, then TransientError.
4xx response Return PermanentError immediately, no retry.
Caller ctx.Cancel/ctx.Deadline Return the context error immediately.

A PermanentError on /completion does NOT prevent the /status post — the runner still wants to release the session lock so the next worker can pick up. When both calls fail, errors.Join combines them so downstream logs see the full picture.

Boundaries

  • Posts to the platform only. Does NOT post directly to Linear; the platform's completion + status handlers own the Linear-side reflection (REN-1399 tenant scoping requires it).
  • Knows nothing about how the agent.Result was produced. The runner hands it a fully-populated value; this package only translates that to the wire shape.

References

  • F.1.1 §6 (Result Posting) — locked endpoint + retry contract
  • F.0.1 §5 — legacy result shape
  • Live platform routes verified at platform/src/app/api/sessions/[id]/{completion,status}/route.ts (Phase 2a port of the legacy donmai-platform handlers).

Documentation

Overview

Package result posts an agent.Result back to the Rensei platform at the end of a session.

Per F.1.1 §6, the wire contract is:

  1. POST /api/sessions/<id>/completion — the canonical "session done" endpoint. The platform side resolves the Linear comment text from the runner-supplied summary and posts it via the platform's getLinearClient resolver. Worker auth: runtime_jwt | registration token | legacy WORKER_API_KEY.

  2. POST /api/sessions/<id>/status — the FSM status transition ("completed" | "failed" | "stopped") with the cost snapshot, provider session id, and worktree path. Drives Linear status transitions, fleet quota release, governor phase tracking, file reservation release, and the lifecycle hook chain (audit + session-status + LLM-billing + event publisher) on the platform side.

Both calls are wrapped by the legacy `apiRequestWithError` retry pattern: 3 attempts with exponential backoff (1s, 2s, 4s) on transient failures. 4xx responses are treated as permanent — retrying a 401 / 403 / 404 / 422 just papers over a programmer error.

Boundaries

  • Posts to the platform only. Does NOT post directly to Linear; the platform's completion + status handlers own the Linear-side reflection (REN-1399 tenant scoping requires it).
  • Knows nothing about how the Result was produced. The runner hands it a fully-populated agent.Result; this package only translates that to the wire shape.

Retry semantics

Transient errors (network failures, 5xx responses, context-deadline timeouts) trigger the 3-attempt exponential backoff. Permanent errors (4xx) return immediately wrapped in a PermanentError. The caller (runner) typically logs and moves on either way — a failed completion is operationally bad but not worth crashing the daemon over.

Source: F.1.1 §6, F.0.1 §5 ("Result"), legacy ../agentfactory/packages/cli/src/lib/worker-runner.ts::reportStatus + ../agentfactory/packages/core/src/orchestrator/orchestrator.ts (completion fetch).

Index

Constants

View Source
const DefaultBaseDelay = time.Second

DefaultBaseDelay is the base delay for exponential backoff between attempts. Backoff is `BaseDelay * 2^(n-1)` — same shape as the legacy 1s / 2s / 4s sequence.

View Source
const DefaultMaxAttempts = 3

DefaultMaxAttempts is the legacy 3-attempt pattern from `apiRequestWithError` in worker-runner.ts. Exposed for tests that want to assert "did we retry" semantics.

Variables

This section is empty.

Functions

This section is empty.

Types

type CredentialProvider

type CredentialProvider func(context.Context) (RuntimeCredentials, error)

CredentialProvider returns the freshest worker runtime credentials available to the caller. Implementations should be cheap and concurrency-safe.

type Options

type Options struct {
	// PlatformURL is the base URL of the platform. Required.
	PlatformURL string

	// AuthToken is the worker bearer token. Required for all
	// non-localhost platform deployments.
	AuthToken string

	// WorkerID identifies the calling worker; required because the
	// platform's status endpoint validates it against the session's
	// owner before accepting transitions.
	WorkerID string

	// CredentialProvider returns the latest worker id + auth token. Empty
	// fields fall back to WorkerID/AuthToken.
	CredentialProvider CredentialProvider

	// HTTPClient overrides the default 30s-timeout client. Optional.
	HTTPClient *http.Client

	// MaxAttempts overrides DefaultMaxAttempts. Values < 1 fall back to
	// DefaultMaxAttempts.
	MaxAttempts int

	// BaseDelay overrides DefaultBaseDelay. Values < 0 fall back to
	// DefaultBaseDelay; a zero value disables sleep between retries
	// (handy in tests).
	BaseDelay time.Duration

	// Now overrides time.Now for deterministic tests. Optional.
	Now func() time.Time
}

Options configure a Poster.

type PermanentError

type PermanentError struct {
	// StatusCode is the HTTP status the platform returned.
	StatusCode int
	// Body is up to the first 4 KiB of the response body, trimmed.
	Body string
}

PermanentError is returned by Poster.Post when the platform rejected the request with a 4xx status code. Permanent errors are not retried — a 401 / 403 / 404 / 422 indicates a programmer error (wrong session id, expired auth token, invalid wire shape) that retrying will not fix.

func (*PermanentError) Error

func (e *PermanentError) Error() string

Error implements the error interface.

type Poster

type Poster struct {
	// contains filtered or unexported fields
}

Poster posts an agent.Result back to the Rensei platform. The zero value is not usable — use NewPoster.

Posters are safe for concurrent use; all fields are read-only after construction.

func NewPoster

func NewPoster(opts Options) (*Poster, error)

NewPoster constructs a Poster from opts. Returns an error when the required PlatformURL is missing or unparseable. Optional fields fall through to their defaults.

func (*Poster) CreateIssueComment

func (p *Poster) CreateIssueComment(ctx context.Context, issueID, body string) error

CreateIssueComment posts a comment to the issue identified by issueID. Used by the post-session unknown-WORK_RESULT diagnostic path (sdlc.go::diagnosticCommentBody) and any other runner-side best-effort comments.

Errors mirror UpdateIssueStatus.

func (*Poster) Post

func (p *Poster) Post(ctx context.Context, sessionID string, r agent.Result) error

Post sends the runner's terminal agent.Result to the platform.

Order matters: completion first, then status. The completion endpoint posts the human-readable Linear comment; the status endpoint transitions the FSM and triggers the cleanup chain (release claim, archive inbox, release issue lock, promote next pending work). Both are wrapped by the retry helper; a permanent (4xx) failure on completion does NOT block the status post — the runner still wants to release the session lock so the next worker can pick up.

Errors:

  • returns nil when both calls succeed.
  • returns a wrapped PermanentError when a 4xx response is seen on either call.
  • returns a wrapped TransientError when retries are exhausted on a transient failure (5xx, network, timeout). Caller should log and treat as a soft failure.
  • returns ctx.Err() when the context is cancelled.

When both calls return errors, errors.Join combines them so downstream logs see the full picture.

func (*Poster) UpdateIssueStatus

func (p *Poster) UpdateIssueStatus(ctx context.Context, issueID, targetStatus string) error

UpdateIssueStatus transitions the issue identified by issueID to the named workflow status (e.g. "Finished", "Rejected"). The platform's issue-tracker proxy resolves the named status to the team's stateId via getTeamStatuses + matches the name to a workflow state.

Errors:

  • returns nil when the proxy responds 200 with success=true
  • returns a wrapped PermanentError on a 4xx response or success=false with retryable=false
  • returns a wrapped TransientError when retries are exhausted on a transient failure (5xx, network timeout)
  • returns ctx.Err() when the context is cancelled

type RuntimeCredentials

type RuntimeCredentials struct {
	WorkerID  string
	AuthToken string
}

RuntimeCredentials are the bearer-token credentials needed for platform result-post requests. Empty fields fall back to the Poster defaults.

type TransientError

type TransientError struct {
	// Attempts is the number of attempts made before giving up.
	Attempts int
	// Last is the last error observed.
	Last error
}

TransientError is returned by Poster.Post when retries were exhausted on a transient failure (5xx, connection refused, DNS, context-deadline timeout that exceeded the per-attempt window).

func (*TransientError) Error

func (e *TransientError) Error() string

Error implements the error interface.

func (*TransientError) Unwrap

func (e *TransientError) Unwrap() error

Unwrap returns the last underlying error so errors.Is / errors.As callers can match the inner cause.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL