Skip to content

Pin a version for a scope

View as Markdown
POST
/v1/agents/{agentId}/pinned/pin
curl --request POST \
--url https://example.com/v1/agents/example/pinned/pin \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "version": "example", "scope": { "kind": "tenant" }, "reason": "example", "testRunId": "example", "stopFollowingLatest": true }'

Pins version for scope: runs in that scope that don’t name a version use it, from the next run. Open conversations keep their version. The version must be registered and active. Every pin is recorded, with who asked and why. Needs pin on the agent.

The pin checks the version in each project whose running version it changes and that has active cases, on the project’s agent test set (testRuns): with the run testRunId names when that run completed there on the test set as it stands, whose outcome changes the corrections’ statuses at once (fixed, reopened); otherwise with a run it starts, correlation id pin:<changeId>, whose outcome changes them when it ends. A pin that waits for approval starts its runs at once, and its statuses change once it’s approved.

A pin that would take a project that follows Latest now off it (a project or segment pin there, or a tenant or org pin over it) is refused with 409 pin-stops-latest before any gate, whether or not its version changes, unless the body has stopFollowingLatest: true. The pin then records stoppedFollowingLatest, and the project runs the pinned version from then on, not each new Latest.

With a release gate for the scope (GET …/release-gate), the pin is checked first against the comparison named by testRunId: 201 pinned; 202 the gate passed and wants a reviewer’s approval (a HITL approval, subject agent-pin; the pinned version moves once it’s approved, if nothing changed meanwhile); 422 gate-failed with details.changeId, details.releaseGate and every check in details.checks. A refused pin is recorded too.

A pin in place skips the gate: when the scope has no pinned version of its own and already serves exactly version (from a scope above, or as the latest), pinning it there changes nothing any run gets, so it is pinned (201) with one passing pinInPlace check, no comparison and no approval, its reason starting pin-in-place. It is re-checked as it is written; if the scope moved meanwhile, 409 pin-superseded: check again.

Under kindgi dev, nothing is pinned, since the pack’s files are what runs: pinning a version writes the data blocks it changes into the pack’s own files (each block’s new version and content, and a prompt’s template into its template file), and the dev server reloads them. Only for the whole workspace (the tenant scope), which the pack’s files serve. If one of those files changed since the version was made (the changed block’s file holds another version than the one its suggested change changed, or other content than its suggested change was drafted from, even at that version, or another block’s file a version not older), or the block left the pack, 409 pin-superseded, and the message names the file; an approval’s decision settles it superseded the same way. Once the dev server reloads the files, the pinned version is what runs, and the pin’s test runs check it in the agent’s project (the default one) and change the corrections’ statuses as anywhere else.

agentId
required
string

AgentId — dotted namespace (acme.drafting).

Idempotency-Key
string
>= 1 characters

Caller-supplied idempotency key, scoped to the caller: the same key from someone else in the tenant is their own request (from 0.1.5). Retries with the same key return the original response byte-identical (per docs/API-ROUTE-CONVENTIONS.md §3.1). A retry sent while the first request still runs is answered 409 idempotency-key-in-flight with Retry-After, on a runtime whose store holds keys (from 0.1.5); retry after it, and you get the first request’s answer. An answer that carries a secret (a new API key or public run token, a session token, a generated signing secret) isn’t kept: a retry gets 409 idempotency-key-replay-withheld, with the first request’s status and when it succeeded (from 0.1.5). A request whose key store can’t be read before it runs is answered 503 idempotency-store-unavailable with Retry-After: nothing ran, so retry with the same key (from 0.3.0).

Media typeapplication/json
object
version
required

The agent version to pin (registered, active).

string
scope
required
One of: discriminator: kind

The whole tenant.

object
kind
required
string
Allowed values: tenant
reason
string
<= 2000 characters
testRunId

The comparison behind this pin.

string
stopFollowingLatest

Pin even when it takes a project that follows Latest now off it: the project then runs the pinned version, not each new Latest. Without it, such a pin is refused (409 pin-stops-latest). POST …/pinned/check ignores it.

boolean

Pinned, with the test runs that check it (testRuns), and the corrections a reused run changed.

Media typeapplication/json

A pin, and the test runs that check it.

object
pin
required

One change of a scope’s pinned version, kept for good: what was pinned before, what is after, who asked and why.

object
id
required
string format: uuid
agentId
required
string
scope
required
One of: discriminator: kind

The whole tenant.

object
kind
required
string
Allowed values: tenant
action
required
string
Allowed values: pin unpin
fromVersion
required

The scope’s own pin before; null when it had none.

string | null
toVersion
required

The scope’s own pin after; null after an unpin.

string | null
requestedBy
required
object
kind
required
string
Allowed values: user service
id
required
string
reason
string
testRunId

The comparison behind the change.

string
createdAt
required
string format: date-time
status

A pin row’s state: it changes once, from pending-approval to its final state. Absent on unpin rows, and on pins made before gates (pinned).

string
Allowed values: pinned pending-approval refused superseded rejected expired
releaseGate
One of:
object
id
required
string
version
required
string
checks
Array<object>

One of a gate’s checks, as the pin records it.

object
name
required

Stable: comparison.required, comparison.status, comparison.freshness, comparison.testSet, sameContents, baselineIsPinned, scopeCovered, evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight, metric.<metric>.k|minCandidate|maxDrop, replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped, cases.minPassing|noNewFailures, groupWeights.expertsOnly, and approval.recheck, which an approval adds to a pin it supersedes, to say what changed since the check.

string
passed
required
boolean
message
required

A plain sentence: what was found against what the gate asks.

string
value
number | string
threshold
number | string
caseIds

The cases it names: for cases.noNewFailures, the ones that passed before and fail now (empty when none does). Absent when it could not tell, and on other checks.

Array<string>
approvalId

The approval a pending-approval pin waits on (kept once decided).

string
resolvedAt

When a pending-approval pin reached its final state.

string format: date-time
stoppedFollowingLatest

A pin’s: true when it took a project that followed Latest off it, as its request asked (stopFollowingLatest) or as a promotion’s pin does. Absent otherwise.

boolean
testRuns
required

One per project whose running version the pin changes and that has active cases: a project pin has one, a tenant or org pin one per such project, a segment pin none. While the pin waits for approval, its runs are already started.

Array<object>

A test run a pin checks a project with: the one the pin named, reused, or one it started, with correlation id pin:<changeId>.

object
projectId
required
string format: uuid
testRunId
required
string
fixed
required

The corrections a reused run made fixed at pin time. Empty when the runs were started: their outcome changes the statuses when each ends.

Array<string>
reopened
required

The corrections a reused run reopened at pin time, as fixed’s.

Array<string>
Example
{
"pin": {
"scope": {
"kind": "tenant"
},
"action": "pin",
"requestedBy": {
"kind": "user"
},
"status": "pinned"
}
}

The gate passed; the pin waits for an approval (pin.approvalId). Its test runs are started, and fixed and reopened are empty.

Media typeapplication/json

A pin, and the test runs that check it.

object
pin
required

One change of a scope’s pinned version, kept for good: what was pinned before, what is after, who asked and why.

object
id
required
string format: uuid
agentId
required
string
scope
required
One of: discriminator: kind

The whole tenant.

object
kind
required
string
Allowed values: tenant
action
required
string
Allowed values: pin unpin
fromVersion
required

The scope’s own pin before; null when it had none.

string | null
toVersion
required

The scope’s own pin after; null after an unpin.

string | null
requestedBy
required
object
kind
required
string
Allowed values: user service
id
required
string
reason
string
testRunId

The comparison behind the change.

string
createdAt
required
string format: date-time
status

A pin row’s state: it changes once, from pending-approval to its final state. Absent on unpin rows, and on pins made before gates (pinned).

string
Allowed values: pinned pending-approval refused superseded rejected expired
releaseGate
One of:
object
id
required
string
version
required
string
checks
Array<object>

One of a gate’s checks, as the pin records it.

object
name
required

Stable: comparison.required, comparison.status, comparison.freshness, comparison.testSet, sameContents, baselineIsPinned, scopeCovered, evidence.<metric>.minCases|minWeight|baselineMinCases|baselineMinWeight, metric.<metric>.k|minCandidate|maxDrop, replay.maxDiverged|maxErrors|maxRefusedWrites|maxStopped, cases.minPassing|noNewFailures, groupWeights.expertsOnly, and approval.recheck, which an approval adds to a pin it supersedes, to say what changed since the check.

string
passed
required
boolean
message
required

A plain sentence: what was found against what the gate asks.

string
value
number | string
threshold
number | string
caseIds

The cases it names: for cases.noNewFailures, the ones that passed before and fail now (empty when none does). Absent when it could not tell, and on other checks.

Array<string>
approvalId

The approval a pending-approval pin waits on (kept once decided).

string
resolvedAt

When a pending-approval pin reached its final state.

string format: date-time
stoppedFollowingLatest

A pin’s: true when it took a project that followed Latest off it, as its request asked (stopFollowingLatest) or as a promotion’s pin does. Absent otherwise.

boolean
testRuns
required

One per project whose running version the pin changes and that has active cases: a project pin has one, a tenant or org pin one per such project, a segment pin none. While the pin waits for approval, its runs are already started.

Array<object>

A test run a pin checks a project with: the one the pin named, reused, or one it started, with correlation id pin:<changeId>.

object
projectId
required
string format: uuid
testRunId
required
string
fixed
required

The corrections a reused run made fixed at pin time. Empty when the runs were started: their outcome changes the statuses when each ends.

Array<string>
reopened
required

The corrections a reused run reopened at pin time, as fixed’s.

Array<string>
Example
{
"pin": {
"scope": {
"kind": "tenant"
},
"action": "pin",
"requestedBy": {
"kind": "user"
},
"status": "pinned"
}
}

Malformed request body, or the test run isn’t a completed comparison: a cancelled or failed one is refused, even with the summary of the cases it got through. Or reference-out-of-scope: a project scope whose project doesn’t reach the agent (details.issues).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

Missing / malformed / expired / revoked bearer token.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

The version is not registered, or was unregistered; or the test run is not found.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

pin-stops-latest: the pin would take a project that follows Latest now off it, and the body doesn’t have stopFollowingLatest: true; details.projects lists each one, { projectId, version }, with the version it runs now. Nothing is recorded. nothing-runs-in-scope: no version of the agent runs in the scope yet (no pin covers it, and it has no Latest), so there’s nothing to measure the version against; deploy or publish a version into the project first (into a later environment, promote a build that ships one). project-suspended: nothing runs there because the scope’s project is suspended. pin-superseded: the scope’s pinned version changed while the gate ran; check again. release-gate-descendant-unpinned: the change would also move a narrower scope a release gate applies to, which has no pinned version of its own and so follows this scope, without its gate; the message names each one: pin it at its current version first. pin-scope-spans-environments: the agent has a project in an environment order, and a tenant- or org-wide pin would act in every environment of the order at once; pin a project (or a segment of it) instead. Under kindgi dev, pin-superseded also when a pack file the version’s data blocks go into changed since the version was made, or the block left the pack; the message names the file. Or registry-read-only: under kindgi dev, a scope narrower than the whole tenant, since the pack’s files serve every scope.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

The request body is over the API’s limit (request-too-large; details.maxBytes says how much).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

gate-failed: the gate refused it. details.checks has every check; the refusal is recorded (details.changeId). With the gate’s cases rule, details.passing and details.needs come with minPassing, and details.newlyFailing (the cases that passed before and fail now) with noNewFailures.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

approvals-pending-limit: the gate wants an approval, and the tenant’s approvals waiting for a decision are at its limit (set by the runtime’s operator). Nothing is recorded.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

Server error (unmapped domain code or framework crash).

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

pin-gate-unsupported: a release gate applies, and the deployment can’t record a gated pin.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

The Idempotency-Key store couldn’t be read before the request ran (idempotency-store-unavailable), so nothing was changed; Retry-After (whole seconds) says when to try again with the same key.

Media typeapplication/json
object
error
required
object
code
required

Stable machine-readable discriminant. Values match domain error codes (see docs/API-ROUTE-CONVENTIONS.md §4.3).

string
message
required
string
details

Optional, kind-specific.

object
key
additional properties
any
requestId
required

Server-assigned request id; also echoed via X-Request-Id header.

string
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": {},
"requestId": "example"
}
}

See also