api

package
v0.38.0 Latest Latest
Warning

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

Go to latest
Published: Nov 5, 2026 License: MIT Imports: 30 Imported by: 0

Documentation

Overview

Package api wraps the Hadron GraphQL endpoint: a genqlient client for typed operations, a raw escape hatch for `hadron api`, and the mapping from transport/GraphQL errors to exit codes.

Index

Constants

View Source
const (
	MaxMemoryKeys      = 8
	MaxMemoryKeysBytes = 16 * 1024
)

The server's bounds (memoryUnlock.ts MAX_HEADER_ENTRIES / MAX_HEADER_BYTES), checked locally so an over-limit invocation is refused before any request.

View Source
const EnvAllowHTTP = urlsec.EnvAllowHTTP

EnvAllowHTTP opts out of the HTTPS-enforcement guard for a trusted local or self-hosted server (set to "1").

View Source
const InternalServerErrorCode = "INTERNAL_SERVER_ERROR"

InternalServerErrorCode is the one generic code the server attaches to an internal fault (cor:api:260:01, hadron-server#2486). The fault's detail stays in the server log under the request id; a client keys on this code and the id, never on the text, and never echoes what the server wrote.

View Source
const InventoryPageSize = 20

InventoryPageSize follows the platform's default list limit; inventory commands have no user-requested larger page size.

View Source
const MCPOnlyRemedy = "This key was issued for MCP clients only, and the CLI needs one with the `account` scope. " +
	"Sign in again with `hadron auth logout && hadron auth login` (hadron v0.15.0 or later requests `account`), " +
	"or create a key on the portal's API keys page (/app/account/api-keys) and run `hadron auth login --with-token` with it. " +
	"If the key comes from HADRON_TOKEN, replace that variable instead."

MCPOnlyRemedy is appended wherever the CLI meets an MCP-only key. It names both ways back, because either may be the one that works: the browser login only helps from a CLI that requests `account` (v0.15.0+), and a portal key is the one route that needs no CLI upgrade.

View Source
const MemoryKeysHeader = "X-Hadron-Memory-Keys"

MemoryKeysHeader carries caller-held passphrases for encrypted memories (hadron-server src/lib/memoryUnlock.ts, stageRequestMemoryKeys). The value is a JSON array of {memory, passphrase}: at most MaxMemoryKeys entries and MaxMemoryKeysBytes bytes. cor:sec:040:04: a key is never a field on a query or mutation; it travels beside the operation and lasts that one request.

View Source
const NodeBatchCap = 200

NodeBatchCap mirrors hadron-server's BATCH_READ_MAX_NODES (cor:api:040): a single nodeBatch call accepts at most 200 refs and fails loud above that, so bulk reads fan out in fixed-size chunks. The server also enforces a ~1 MB response cap that can return a partial page (truncated=true) with the spillover in `omitted`; CollectNodeBatch re-requests those.

View Source
const OperationKeyHeader = "X-Hadron-Operation-Key"

OperationKeyHeader supplies a memory data key for one App operation (x-hadron-operation-key, hadron-server#1817, the first #1796 slice). cor:sec:040:04 — "a key is never a field on a query or a mutation" — is the rule it implements: a key in GraphQL variables travels in the request body, where any body logging, APM capture or proxy log would hold key material. The server stages the header request-scoped (outside GraphQL Context), authenticates before parsing, and consumes it exactly once; installing it as a persistent client default is refused by design, so it must ride per call.

View Source
const OversizedBodyNote = "(response body too large to show)"

OversizedBodyNote stands in for a body the CLI refused to read whole.

View Source
const PageLimit = 200

PageLimit is the server's hard cap on a single page of the uniform paginated list queries (hadron-server#473: cap 200; the server's own default is not relied on, cli#967).

View Source
const ReviewNodeRole = "review"

ReviewNodeRole is the root of the governed review role family. #1201 put `review` in the register for a SECURITY reason rather than a tidiness one: a review may be a safety check, so being able to remove one silently is the attack.

View Source
const SessionHeader = "X-Hadron-Session"

SessionHeader binds a request to a worker session (hadron-server#934, amending cor:aut:020:02). The server honors it only for a session the caller's own principal started and that is still active, and treats it as ATTRIBUTION, never a credential: it cannot widen what the caller may do.

View Source
const SpecNodeRole = "spec"

SpecNodeRole is the root of the governed spec role family. The server also governs its dotted descendants; the role is what the gate reads.

View Source
const UnsupportedScopeRemedy = "Sign in again with `hadron auth logout && hadron auth login`, " +
	"or create a key on the portal's API keys page (/app/account/api-keys) and run `hadron auth login --with-token` with it. " +
	"If the key comes from HADRON_TOKEN, replace that variable instead."

UnsupportedScopeRemedy is appended for a key whose OAuth grant carries a scope this server does not support: it is refused everywhere, so the only fix is a new credential (server#1306).

Variables

View Source
var ErrRedirectPolicy = errors.New("redirect refused by policy")

ErrRedirectPolicy marks a refusal WE made about a redirect, as opposed to a network failure. net/http wraps a CheckRedirect error in *url.Error, which satisfies net.Error — so without this sentinel the transport classifier (#394) would read our own HTTPS-downgrade refusal as a lost request and report it as retryable exit 7. The server answered; retrying cannot help.

Functions

func AppMemberWriteRefused added in v0.29.0

func AppMemberWriteRefused(err error) bool

AppMemberWriteRefused reports whether err is a DEFINITE refusal of an ensureAppMember / removeAppMember call: the server threw before the write, inside the authorized transaction, so nothing changed. Anything else — transport failures, a data+errors partial, a masked INTERNAL_SERVER_ERROR — is outcome unknown, because ensureAppMember re-reads the row AFTER commit and a failure there is reported although the upsert committed (#956).

The typed refusals are InvalidRoleError, MaxMembersExceededError and OrphanAppError; the server's `Forbidden: …` arrives as plain text under INTERNAL_SERVER_ERROR, so it is recognised by its message prefix. EVERY error in the list must qualify; one unrecognized entry keeps the answer ambiguous.

An error is a root refusal only when its path names no field below the mutation itself: a nested-field error (path ["ensureAppMember","user",…]) means the resolver RAN and returned a row, so the write may have committed whatever code the nested error carries. Callers must also treat a populated result next to an error as unknown (see memberWriteError) — this function sees only errors. Like EndRefusedBeforeCommit, call it BEFORE MapError wraps.

func BulkTemplateApplyRefusedBeforeWrite added in v0.38.0

func BulkTemplateApplyRefusedBeforeWrite(err error) bool

BulkTemplateApplyRefusedBeforeWrite recognizes only documented root-call refusals. A nested projection error can happen after per-target commits.

func Capabilities added in v0.24.0

func Capabilities(ctx context.Context, baseURL string) (map[string]bool, error)

Capabilities reads GET /capabilities — hadron-server#1925, mounted by #1930: which deployment switches are ON, readable before sign-in. The answer is `{"capabilities":[...]}` with ruled token words ("node-signing", "passkey-registration"); an off switch is OMITTED, never false, so a deployment with the switches off reads exactly like a build without the features. Unauthenticated, ETag-cached, rate-limited, and OUTSIDE GraphQL.

Within one process the response is cached per server for its stated max-age; past the window it is revalidated with If-None-Match, where a 304 means unchanged (and renews the window). Callers treat every failure — non-200 (an older server's plain 404 is the expected one), transport error, unparsable body — as "no capabilities": no caller may pay a refused follow-up round trip (e.g. a signature query an older server would reject) just because discovery failed.

func CheckUpdateKinds added in v0.17.0

func CheckUpdateKinds(input *gen.UpdateNodeInput, cur NodeKindState) error

CheckUpdateKinds is UpdateNodeByKind's refusal without the write, for a caller that must know before it reports a plan or prompts (node import's --dry-run and overwrite prompt): nil when some door can make the update.

func CloneAgentBlockerLines added in v0.34.0

func CloneAgentBlockerLines(err error) []string

CloneAgentBlockerLines renders one line per blocker, in the order received: its code, its count when one was sent, then each subject as a quoted name with its position, or the bare position when the server sent no name (a hidden subject never gets one). position is printed literally, as received.

func CloneAgentDetailText added in v0.29.0

func CloneAgentDetailText(err error) string

CloneAgentDetailText is the human one-line suffix naming the answer: the documented label, then code, reason and "retryable: rerun by hand" in the order a reader needs them (cli#1264, Uma's wording). retryable=false is omitted, never printed as a negative. Blockers follow on their own lines (CloneAgentBlockerLines). Every server-supplied string goes through output.SafeCell, so it can neither forge a line nor drive the terminal. Text mode only: --json carries CloneAgentDetails unchanged.

func CloneAgentDetails added in v0.29.0

func CloneAgentDetails(err error) map[string]any

CloneAgentDetails forwards only the clone contract, never arbitrary extensions. A missing subject name remains absent: the CLI never resolves it itself.

func CloneOutcomeUnknownError added in v0.32.0

func CloneOutcomeUnknownError(what string) error

CloneOutcomeUnknownError is the result of a clone whose answer is unusable: the request may have been applied, so the one thing to do is inspect state. Exit 7, like a lost response; never a success and never retried.

func CollectAll added in v0.5.1

func CollectAll[T any](fetch func(limit, offset int) ([]T, int, error)) ([]T, error)

CollectAll drains a uniform paginated { items, total } list query (hadron-server#473) to exhaustion, so "whole scope" commands never silently truncate at one server page. fetch receives (limit, offset) and returns one page's items plus the envelope total.

func CollectHasMore added in v0.38.0

func CollectHasMore[T any](fetch func(offset int) ([]T, int, bool, error), identity func(T) *string) ([]T, error)

CollectHasMore reads a complete offset inventory. Only hasMore ends traversal; neither total nor a short page proves that the inventory is complete. Total changes and repeated identities detect some concurrent drift, not a snapshot: equal-count changes without a repeated identity remain undetectable. A nil identity lets the caller retain its existing nullable-row handling. Do not return partial rows on failure: callers must not print a partial list.

func CollectNodeBatch added in v0.2.0

CollectNodeBatch fetches full nodes for refs in cap-sized chunks, re-queuing the spillover the server drops under its response-size cap. A ref is a node PK or a fully-qualified node URN (hadron-server#813) — `omitted` and `unavailable` come back as the refs were sent, so re-queuing and reporting both stay in the caller's own vocabulary. fetch is injected rather than calling gen.NodeBatch directly so the chunking/truncation loop is unit-testable without a server and so each caller controls the field projection. Returns the nodes (input order is not preserved across chunks), the union of refs the server reported unavailable, and the first error.

Shared by the whole-corpus fan-outs (`memory export`, `spec get --prefix`).

func CollectPages added in v0.27.0

func CollectPages[T any](pageSize int, fetch func(limit, offset int) ([]T, *int, error)) ([]T, error)

CollectPages drains a list whose total may be unknown (findNodes returns a nullable total; sessions and revisions return none). fetch receives (limit, offset) and returns one page plus the total when the server gave one. It stops only on an empty page or when a known total is reached, and advances by the rows actually received, so a server that clamps pages below pageSize can neither end the scan early nor skip rows (cor:api:120:04). A fetch error on any page fails the whole collection.

func CollectUntil added in v0.5.1

func CollectUntil[T any](fetch func(limit, offset int) ([]T, int, error), done func(items []T) bool) ([]T, error)

CollectUntil is CollectAll with an early exit: after each page, done receives the items accumulated so far, and returning true stops paging (the accumulated items are returned as-is). Use it when a caller can tell mid-scan that it already has what it needs — e.g. an exact match in a sorted list — without draining the remaining pages.

func CommentRecoveryDetails added in v0.21.0

func CommentRecoveryDetails(err error) map[string]any

CommentRecoveryDetails returns the bounded recovery fields published by comment refusals. Keep unrelated or arbitrary extensions out of curated output.

func DescendantCount added in v0.8.0

func DescendantCount(err error) int

DescendantCount returns the descendant count carried by a NODE_HAS_DESCENDANTS error (server #661: its extensions.count), or -1 when err is not that error or carries no non-negative numeric count. JSON numbers decode to float64, but a few other numeric shapes are tolerated. A negative or non-numeric value is treated as "no count" (-1) so callers never render a nonsensical "-N descendant(s)". Call it BEFORE MapError wraps the error.

func Detached added in v0.33.0

func Detached(err error, redact func(string) string) error

Detached returns err as a copy that renders and classifies the same, with redact applied to every rendered string, and that retains no part of err's chain. Use it where err's graph may hold a secret that Error() does not show (a wrapper with a safe message over a secret-bearing cause, a joined list): redacting only when the outer text changes leaves that cause reachable.

Preserved: Error(), HumanMessage() and ServerCode() (all redacted), the exit code, the internal-fault detail (redacted), and errors.Is for context.Canceled, context.DeadlineExceeded and exitcode.ErrSilent. Not preserved: errors.As to a concrete raw type (a GraphQL error list, an HTTP error, a *url.Error). A nil err stays nil.

func DocumentFromBatchNode added in v0.3.0

func DocumentFromBatchNode(n *batchNode) *nodedoc.Document

DocumentFromBatchNode maps a bulk-read node into the neutral nodedoc.Document the markdown/JSON codecs consume: `memory export`'s. `node export` does not use it: the server renders that file with its own nodeExport, which does not yet emit role or isRunnable (cli#714). Type "" carries the server default (info), so it never serializes and a re-import defaults correctly. The projection only carries the memory id, not the URN, so callers that emit a standalone file resolve and set Document.MemoryURN themselves.

func DocumentHasMutation added in v0.9.0

func DocumentHasMutation(doc string) bool

DocumentHasMutation reports whether a GraphQL document contains a mutation operation, so `hadron api` can warn about idempotency only when a write was actually at stake.

String literals are skipped so a query selecting a field whose *value* mentions mutations is not misread. Beyond that the check is deliberately generous — a false positive prints a warning that is merely unnecessary, while a false negative withholds the one sentence that prevents a double-applied write. It errs toward warning.

func EndRefusedBeforeCommit added in v0.13.0

func EndRefusedBeforeCommit(err error) bool

EndRefusedBeforeCommit reports whether an endSession failure PROVABLY changed nothing — the only case in which a client may tell its caller that a handoff was definitely not recorded.

It is deliberately ONE code, and not a taxonomy. `cor:agt:020:10` guarantees that a failed handoff write REFUSES the end, so `HANDOFF_WRITE_FAILED` is a SPEC-BACKED statement that nothing committed. Every other outcome is treated as unknown, including other GraphQL errors, because there is no way from here to tell a pre-commit refusal from a post-commit one:

  • GraphQL returns `data` AND `errors` when a nested resolver fails after the mutation ran, and
  • null-bubbling from a non-null child NULLS `data.endSession` even though the write happened,

so neither the presence of an error nor the absence of a payload proves a refusal (PR #528 review, Codex, twice).

The asymmetry is the whole design. Saying "may not have been recorded" when it definitely was not is harmless — the remedy is identical, since one stint records one handoff. Saying "was NOT recorded" when it WAS is the worst sentence this command can print, and it would make a later retry failure look like confirmation of data loss. So the definite wording is earned by a guarantee, not inferred from a shape.

Call it BEFORE MapError wraps.

func Endpoint

func Endpoint(serverURL string) string

Endpoint joins the server base URL with the GraphQL path.

func ExitCodeForExtension added in v0.18.0

func ExitCodeForExtension(code string) int

ExitCodeForExtension also classifies typed refusal codes carried in a successful transfer preview's blockers, where there is no GraphQL error for MapError to inspect. The preview and apply paths must exit consistently.

func ExtensionCount added in v0.34.0

func ExtensionCount(err error, code string) int

ExtensionCount is DescendantCount for any typed error that carries extensions.count — CROSS_MEMORY_EDGES (deleteNode, cli#1341) counts the edges linking the node to other memories. -1 when absent or not a count.

func ExtensionInt added in v0.35.0

func ExtensionInt(err error, key string) (int, bool)

ExtensionInt returns the first integer extensions[key] of err's GraphQL errors (a batch refusal's 1-based extensions.position, cli#1565). Call it on the RAW error. ok is false when no error carries the key as a whole number.

func ExtensionString added in v0.35.0

func ExtensionString(err error, key string) (string, bool)

ExtensionString returns the first non-empty string extensions[key] of err's GraphQL errors (TASK_MATCH_FAILED's extensions.reason, cli#1567). Call it on the RAW error; the value is server text, so a caller echoing it bounds it.

func FromNon200Envelope added in v0.38.0

func FromNon200Envelope(err error) bool

FromNon200Envelope reports whether err is a GraphQL error envelope that arrived inside a non-200 HTTP response of status 5xx. graphQLErrors reads that envelope exactly as it reads a 200 one, but a 5xx is not proof the operation was refused: a gateway can answer 502/504 with a JSON body that carries an `errors` list, after the server committed the write (cli#1495). A caller deciding "was this refused, definitely?" must exclude it.

func GovernedKindConflict added in v0.15.0

func GovernedKindConflict(role *string, isRunnable bool) []string

GovernedKindConflict reports the governed kinds a write would produce when there is MORE THAN ONE, and nil otherwise.

No door can write such a node. Each is exempt from its OWN kind only and stays fully subject to the others, so `isRunnable: true` + `role: "spec"` is refused by `createTaskNode` (for the role) and by `createSpecNode` (for the capability) alike. The server's refusal names one of them, which makes it actively misleading: it sends the caller to a door that will also refuse.

THIS IS ROUTING, NOT VALIDATION, and the distinction is the one @Ada drew on #615 — the CLI must not refuse a role VALUE, because the register is a server-side closed list that changes without this client knowing. It says nothing here about whether a value is governed in general; it reports that the mapping this client holds, which it needs anyway to pick a mutation, yields two doors and therefore none.

The degradation is safe and loud: if the server later un-governs a kind, this refuses a write that would now succeed — a visible Usage error naming `hadron api` as the way through, rather than a silent wrong result.

func HasErrorCode added in v0.5.1

func HasErrorCode(err error, code string) bool

HasErrorCode reports whether err carries a GraphQL error whose extensions.code equals code, including a non-200 HTTPError with a parsed GraphQL response. It also follows MapError's wrapping, though callers that need a special recovery path should branch before mapping the error — e.g. a guarded spec edit keeping its proposal on NODE_WRITE_CONFLICT.

func HasMemoryKeys added in v0.36.0

func HasMemoryKeys(ctx context.Context) bool

HasMemoryKeys reports whether ctx carries --unlock passphrases.

func InternalFaultMessage added in v0.32.0

func InternalFaultMessage(requestID string) string

InternalFaultMessage is the one sentence the CLI writes for an internal fault. There is no blank placeholder when the id is absent.

func IsGraphQLValidationFor added in v0.18.0

func IsGraphQLValidationFor(err error, name string) bool

IsGraphQLValidationFor reports a schema-validation refusal naming one field or argument. It reads the parsed GraphQL envelope, including the one inside an HTTPError, rather than matching HTTPError.Error's JSON-escaped rendering. Callers use it only when retrying a compatible operation is safe.

func IsGraphQLValidationOnlyFor added in v0.34.0

func IsGraphQLValidationOnlyFor(err error, names ...string) bool

IsGraphQLValidationOnlyFor reports whether err is a GraphQL error envelope in which EVERY error is a validation rejection of one of names — an older server that lacks exactly those fields and nothing else wrong. It is the predicate for tolerating that absence: IsGraphQLValidationFor answers "is one of the errors this?", so a mixed envelope (an unknown new field plus a FORBIDDEN) would be swallowed with it (cli#1529 LOCAL P2). An envelope with no parsed errors is not tolerated.

func IsMCPOnlyCredential added in v0.16.0

func IsMCPOnlyCredential(err error) bool

IsMCPOnlyCredential reports whether err is the server refusing an MCP-only key (#681). Call it on the RAW error, before MapError wraps it.

func IsNodeBatchEdgeCapRefusal added in v0.34.0

func IsNodeBatchEdgeCapRefusal(err error) bool

IsNodeBatchEdgeCapRefusal identifies the sanitized terminal cap refusal, even after a caller maps its exit code. No raw provider prose enters relint DTOs.

func IsSchemaSkew added in v0.29.0

func IsSchemaSkew(err error) bool

IsSchemaSkew reports whether err is the server rejecting a query this build sends (a GraphQL validation failure) — the server lacks an operation or field. A command that needs a more specific sentence than MapError's generic skew hint (and a different exit code) asks this before mapping.

func IsUnknownGraphQLArgument added in v0.20.0

func IsUnknownGraphQLArgument(err error, argument, field string) bool

IsUnknownGraphQLArgument permits a compatibility retry only when every parsed error is the specific schema refusal, never a mixed business failure. #817/server#1633 pin gqlgen's unknown-argument wording; Diego also observed that exact envelope on production during the #819 blind review. A wording change fails closed rather than silently dropping session attribution.

func ListNotAdvancing added in v0.27.0

func ListNotAdvancing(offset int) error

ListNotAdvancing is the error returned when a non-empty page brings nothing the scan has not already read: the server is not honouring the offset. The list cannot be certified complete, so this is a failed read, never a successful partial.

func MapCloneAgentError added in v0.29.0

func MapCloneAgentError(err error) error

func MapError

func MapError(err error) error

MapError converts transport and GraphQL errors into CodedErrors so the root command can derive the documented exit code. Codes come from hadron-server's Apollo resolvers (extensions.code).

It is also the ONE place the server's refusal is rendered for a human (#566). Every `exitcode.New` below wraps through `cleaned`, so the message that reaches `hadron: %s` — and the `--json` error envelope, which renders the same string — is what the server said, with none of genqlient's document-location scaffolding around it.

func MapErrorKeepingCode added in v0.33.0

func MapErrorKeepingCode(err error) error

MapErrorKeepingCode is MapError that also keeps the server's extensions.code on the result, for the --json envelope's serverCode.

func MapPRProtocolError added in v0.37.0

func MapPRProtocolError(err error) error

MapPRProtocolError preserves recognized protocol conflicts' provider message and code and exposes only the typed PR_NOT_READY slot fields. Those conflict chains omit raw errors/extensions; other errors retain the existing mapper.

func MapWorkerReportingError added in v0.21.0

func MapWorkerReportingError(err error) error

func NewClient

func NewClient(serverURL, token string, httpClient *http.Client, opts ...ClientOption) (graphql.Client, error)

NewClient returns a genqlient client for the given server, authenticating with token (may be empty for anonymous calls).

func NewInternalFaultError added in v0.32.0

func NewInternalFaultError(requestID string, cause error) error

NewInternalFaultError builds the exit-1 error for an internal fault. The message is CLI-written; cause stays on the chain for errors.As readers but is never rendered.

func OpenThreadConflictID added in v0.38.0

func OpenThreadConflictID(err error) (string, bool)

OpenThreadConflictID reads a createComment refusal for `comment add` (cli#1690): it returns the existing thread's id only when the FIRST GraphQL error carries the typed code COMMENT_OPEN_THREAD_EXISTS and a non-empty string extensions.threadId. Anything else — another code, the conflict code without a usable threadId, the conflict as a later error, message text that merely mentions it — is not a recoverable conflict. A 5xx envelope is excluded too: it is not proof the write was refused (FromNon200Envelope). Call it on the error MapError returned or on the raw one; both keep the chain.

func PageNodes added in v0.27.0

func PageNodes(pageSize int, fetch func(limit, offset int) ([]*ListNode, error), visit func(*ListNode) bool) error

PageNodes drains a findNodes-shaped listing, calling visit once per node until visit returns false or the list is exhausted. fetch receives (limit, offset).

It stops only on an EMPTY page: the server may serve fewer rows than asked for (a clamp, a concurrent delete) and findNodes.total is nullable, so a short page proves nothing (cli#967, cor:api:120:04). It advances by the rows actually received, visits each node id once, and FAILS when a non-empty page brings nothing new (a server ignoring the offset) rather than looping or reporting a partial list as complete. A fetch error on any page is returned.

func ParseDeliveryPending added in v0.35.0

func ParseDeliveryPending(extensions map[string]any) (int64, bool)

ParseDeliveryPending reads the metadata of one response. It reports a count only for {v:1, count:<safe non-negative integer>, complete:true}; everything else, including a future incomplete (lower-bound) payload, is unavailable.

func PlainHTTPInternalFault added in v0.32.0

func PlainHTTPInternalFault(h http.Header, body []byte) error

PlainHTTPInternalFault inspects a non-2xx plain HTTP body (asset endpoints, OAuth) and, when it is an INTERNAL_SERVER_ERROR envelope (or an OAuth server_error with request_id), returns the CLI error for it. nil for every other body: typed refusals and gateway pages keep their own handling.

func ReadNodeBatch added in v0.34.0

func ReadNodeBatch(ctx context.Context, client graphql.Client, refs []string, memory, prefix *string) (*gen.NodeBatchResponse, error)

ReadNodeBatch preserves the NodeBatch projection when its selected-edge budget is exceeded. Only the provider's structured edge-cap refusal is retried, by bisecting explicit refs with the SAME query. A k-ref chunk costs at most 2k-1 requests, before CollectNodeBatch's existing byte-cap requeues. Prefixes and an indivisible ref refuse explicitly; partial data from any failed request is discarded. No ordinary edges() projection is substituted.

func ReadNodeBatchContent added in v0.34.0

func ReadNodeBatchContent(ctx context.Context, client graphql.Client, refs []string) (*gen.NodeBatchResponse, error)

ReadNodeBatchContent is the same stored-content projection without either edge direction. It adapts the generated edge-free type for existing content consumers; it is never used by callers whose output depends on edges.

func ReadPlainHTTPFault added in v0.32.0

func ReadPlainHTTPFault(h http.Header, r io.Reader, keep int) ([]byte, error)

ReadPlainHTTPFault reads a non-2xx plain HTTP body (bounded) and returns the leading bytes for the caller's own diagnostic (at most keep bytes) and the internal-fault error when the body is one (then no bytes).

A body over the cap is never classified and never echoed: the code could sit anywhere past the cap, so a marker in the prefix proves nothing (it may be a typed refusal quoting the word), and any prefix could be fault text. The caller gets OversizedBodyNote instead.

func RequireSecureURL added in v0.6.0

func RequireSecureURL(serverURL, token string) error

RequireSecureURL refuses to transmit the bearer token over a non-https server URL — cleartext credentials are trivially captured by an on-path attacker on the shared CI/dev machines this CLI runs on (#114). Carve-outs: a loopback host (local dev, incl. the test httptest servers) and HADRON_ALLOW_HTTP=1 (a trusted self-hosted backend). An empty token means no credential rides, so the check is a no-op — anonymous http is allowed.

func RoleInFamily added in v0.18.0

func RoleInFamily(role *string, base string) bool

RoleInFamily matches the server's governed role boundary: the base role or one of its dotted descendants, never an ordinary prefix such as "special".

func RosterGroupAdvisories added in v0.34.0

func RosterGroupAdvisories(err error, rows int, nullAt func(i int) bool) (map[int]string, bool)

RosterGroupAdvisories reports whether err is NOTHING BUT advisory field errors on the team roster's per-row model groups (cli#1485 S3, cor:agt:060:12), and if so returns the safe error code per affected row index.

It is the one place a GraphQL field error is tolerated, and it is deliberately narrow. Every error must be a bare GraphQL error (never a transport or non-200 failure, which carry no data to trust) whose path is exactly the unaliased response path teamRoster.workers[i].modelGroups for a row i that exists (0 <= i < rows) and whose membership is actually null (nullAt) — an error on any other field, a mixed list, a missing or malformed path, an out-of-range index, or an error next to a non-null value all return ok=false, and the caller keeps its whole-command failure. The caller must also have a successfully decoded, non-null core roster (it supplies rows/nullAt from it).

The returned code is extensions.code only when it is a plain token, else a fixed placeholder: raw message text and extensions are never surfaced.

func SanitizeRequestID added in v0.32.0

func SanitizeRequestID(id string) string

SanitizeRequestID returns id when it is safe to print, else "".

func ScopeRefusalRemedy added in v0.16.0

func ScopeRefusalRemedy(kind ScopeRefusal) string

ScopeRefusalRemedy is the recovery the CLI appends for a scope refusal.

func ServerCodeOf added in v0.33.0

func ServerCodeOf(err error) string

ServerCodeOf returns the first non-empty extensions.code of err's GraphQL errors, or "" when it carries none. Call it on the RAW error.

func ServerMessage added in v0.14.0

func ServerMessage(err error) string

ServerMessage returns the FIRST server message in err, or "" when there is none. For a caller rendering one line about one failure; MapError joins the whole list instead, because dropping the others would hide them.

func ServerMessages added in v0.14.0

func ServerMessages(err error) []string

ServerMessages returns the server's own message for each GraphQL error in err, with genqlient's location/path rendering left off. Empty when err carries no GraphQL errors at all — a transport failure or a raw HTTP body — which is the caller's signal to fall back to err.Error().

func TransportCause added in v0.35.0

func TransportCause(err error) (cause string, ok bool)

TransportCause names, in one bounded phrase, why a request got no answer: a dropped connection, a timeout, an unreachable server or a gateway's HTTP status. It never carries a response body or a retry hint, so a caller whose write may have landed can state the cause without inviting a blind retry (cli#1157). ok is false when err is not a transport failure.

func ValidateOperationKey added in v0.21.0

func ValidateOperationKey(key string) error

ValidateOperationKey refuses everything the server's operation-key parser would refuse — so a malformed key is a LOCAL usage error before any request, not a BAD_UNLOCK_INPUT after a round-trip. Mirrors the server exactly: charset and length first, then the decode-and-reencode round-trip that rejects non-canonical spellings of valid bytes (the decoder silently drops the final character's low padding bits; re-encoding does not reproduce them). The value itself is never echoed back.

func WithCode added in v0.35.0

func WithCode(mapped error, code string) error

WithCode carries a server code that arrived as DATA rather than as a GraphQL error (a claim result's refusalCode, cli#1524) on mapped, for the --json envelope's serverCode, exactly as WithServerCode does for a thrown one. A nil mapped stays nil; an empty code returns mapped as it was.

func WithMemoryKeys added in v0.36.0

func WithMemoryKeys(ctx context.Context, keys []MemoryKey) (context.Context, error)

WithMemoryKeys marks every GraphQL request made with ctx as carrying the invocation's --unlock passphrases. The root installs it once on the command context, so it rides the requests THIS invocation makes and nothing else; it is never persisted or cached (ruling 0129). Empty keys leave ctx unchanged.

func WithNoRedirect added in v0.35.0

func WithNoRedirect(ctx context.Context) context.Context

WithNoRedirect marks ONE call as non-replayable across a redirect (hadron-cli #1568, gate ruling #69494). net/http follows 307/308 by re-sending the POST body, so a one-send mutation such as `auth token delete` would reach the endpoint twice. A call whose context carries this marker refuses to follow ANY redirect hop, in the same CheckRedirect that already guards session and bearer-token replay. It is a per-call opt-in like WithSession: no client's default changes, and every other operation keeps following redirects.

func WithOperationKey added in v0.21.0

func WithOperationKey(ctx context.Context, key string) context.Context

WithOperationKey marks the GraphQL request made with ctx as carrying the memory data key in the OperationKeyHeader. Like WithSession it is deliberately per-CALL, never a client default: the key rides exactly the one mutation that needs it, and every other request a command makes — before or after — goes out without it. An empty key leaves ctx unchanged.

func WithServerCode added in v0.33.0

func WithServerCode(mapped, raw error) error

WithServerCode keeps the server's extensions.code of raw on mapped, for the --json envelope's serverCode. A nil mapped stays nil whatever raw carries; without a code mapped is returned as it was. Only mapped is wrapped, so neither Error() nor Unwrap() exposes raw.

func WithSession added in v0.18.0

func WithSession(ctx context.Context, sessionID string) context.Context

WithSession marks every GraphQL request made with ctx as driven by the worker session id. It is deliberately per-CALL, never a client default: the header changes server behaviour (a heartbeat, and under hadron-server#1353 the bound worker's own read state), so it rides only on the operations that ask for that, never every request a bound worktree makes. Chat reads use explicit advanceReadState:false (#817/server#1628); only the post-delivery mark may acknowledge what the CLI actually showed. Authored node/edge writes opt in through the command-local #821 hook; queries and maintenance operations keep their existing contexts. An empty id leaves ctx unchanged.

func WithThreadID added in v0.38.0

func WithThreadID(err error, thread string) error

WithThreadID wraps err so it names thread. A nil err stays nil.

func WorkerReportingDetails added in v0.21.0

func WorkerReportingDetails(err error) map[string]any

WorkerReportingDetails preserves the server's refusal code without forwarding arbitrary extension payloads. The numeric CLI exit code remains separate.

Types

type AuthoredNode added in v0.14.0

type AuthoredNode struct {
	Id         string
	MemoryId   string
	Loc        string
	Name       string
	NodeType   string
	Tags       []string
	Seq        *int
	IsRunnable *bool
	Role       *string
	UpdatedAt  string
}

AuthoredNode is the node an authoring write returns.

A CONCRETE struct rather than an alias onto one door's generated type, which is what it used to be. Six doors plus the two generic surfaces return eight structurally similar but DISTINCT genqlient types, and Go will not convert between them — so aliasing one of them forced every dispatcher to pick a winner and cast, which does not compile and should not. Mapping once here keeps the call sites off genqlient shapes, the same reason the command packages marshal their own DTOs.

func CreateNodeByKind added in v0.14.0

func CreateNodeByKind(ctx context.Context, client graphql.Client, input *gen.CreateNodeInput) (*AuthoredNode, error)

CreateNodeByKind writes a node through the door its KIND requires, or through the generic `createNode` when it is of no governed kind.

It exists because the gate is a property of the NODE, not of the command: any caller that can set `role` or `isRunnable` can produce a governed node, and `hadron node add --runnable` does exactly that. Before this, that command sent a runnable node to the generic surface and was refused — a live break wider than the authoring commands, found by @codex on #614 and reproduced against the server before being believed.

func CreateReviewNode added in v0.14.0

func CreateReviewNode(ctx context.Context, client graphql.Client, input *gen.CreateNodeInput) (*AuthoredNode, error)

CreateReviewNode writes a review check through the review door. As with CreateSpecNode, the caller sets `input.Role`.

func CreateSpecNode added in v0.14.0

func CreateSpecNode(ctx context.Context, client graphql.Client, input *gen.CreateNodeInput) (*AuthoredNode, error)

CreateSpecNode writes a spec node through the spec door.

The caller must set `input.Role` in the spec family — this wrapper does not do it, and that is on purpose: for a create the gate reads the state being written, so a node's kind is a property of the node being written, not of the function called to write it. Setting the role here would let a call site that forgot to think about the kind still produce a governed node, which is the wrong direction for a signal whose whole job is to be explicit.

func UpdateNodeByKind added in v0.14.0

func UpdateNodeByKind(ctx context.Context, client graphql.Client, input *gen.UpdateNodeInput, cur NodeKindState) (*AuthoredNode, error)

UpdateNodeByKind edits a node through the door that owns every governed kind the write TOUCHES — the kind it is now and the kind it will be.

THE SERVER GATE IS BEFORE ∪ AFTER, and that is the whole difficulty (hadron-server governedRole.ts, assertGovernedWrite). Two consequences:

  • An update that touches neither `role` nor `isRunnable` still edits a governed node when the stored one is governed, so a plain `--description` edit of a review check or a task needs that kind's door. Hence the node's current kind is an argument: an omitted field preserves the stored value.
  • REMOVING a kind needs that kind's door too. `isRunnable: false` on a task or a role change away from `spec` leaves an ungoverned node, but the generic surface refuses it ("removes"): the gate exists so the generic surface cannot quietly un-govern a node. Routing on the resulting state alone sent exactly those writes to updateNode (cli#714).

A write that touches two governed kinds — a task given `role: spec`, or a spec turned into a task — has no door, because each door is exempt from its OWN kind only. It is refused here as a Usage error rather than sent to be refused. WHO may use a door is hadron-server#1202, not this client's call.

func UpdateNodeClearingRoleByKind added in v0.25.0

func UpdateNodeClearingRoleByKind(ctx context.Context, client graphql.Client, input *gen.UpdateNodeInput, cur NodeKindState) (*AuthoredNode, error)

UpdateNodeClearingRoleByKind applies one role-null update, with all other explicit edits in the SAME mutation. A nil Role on the ordinary input means preserve, so use an empty role only in this local copy for routing the resulting ungoverned state; it is never sent on the wire. Server refusal is final: do not retry via an ordinary or generic writer.

func UpdateSpecNode added in v0.14.0

func UpdateSpecNode(ctx context.Context, client graphql.Client, input *gen.UpdateNodeInput) (*AuthoredNode, error)

UpdateSpecNode edits a spec node through the spec door — the counterpart the create half had to wait for (hadron-server #1192, then folded into #1201).

It is needed even when an edit touches neither `role` nor `isRunnable`, because the gate reads the kind the node is now as well as the kind it will be: editing a node that already carries `role: "spec.rule"` touches the spec kind, so the generic `updateNode` refuses it.

It keeps `updateNode`'s selectors and, crucially, its OMIT-TO-PRESERVE semantics — which is why routing edits through the create door with `upsert: true` was never the workaround it looked like: `CreateNodeInput` has no omit-to-preserve, and `spec edit` depends on it.

type ClientOption added in v0.35.0

type ClientOption func(*nullSafeGraphQLClient)

ClientOption adjusts NewClient.

func WithDeliveryPending added in v0.35.0

func WithDeliveryPending(r *DeliveryPendingRecorder) ClientOption

WithDeliveryPending records each successful response's deliveryPending metadata in r.

type DeliveryPendingRecorder added in v0.35.0

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

DeliveryPendingRecorder holds the latest valid extensions.hadron.deliveryPending snapshot (hadron-server#2473, cli#1084) seen on a successful response. It is filled in ONE place, the shared client, and read once by the entry point after the command has succeeded.

The CLI computes nothing here: a count is either exactly what a supported, complete provider payload said, or there is no count. Absent, malformed, unsupported or incomplete metadata records nothing (never a fabricated zero).

func (*DeliveryPendingRecorder) Count added in v0.35.0

func (r *DeliveryPendingRecorder) Count() (int64, bool)

Count returns the latest valid count; ok is false when none was seen.

type FindNodesPage added in v0.5.0

type FindNodesPage struct {
	Nodes    []*ListNode
	Total    *int
	Degraded *string
	Reason   *string
}

FindNodesPage is the flattened result of one findNodes call: the hit nodes (hits[].node hoisted to a bare slice, the shape every old `nodes`/`nodeSearch` caller expects), plus the envelope's total and the degraded/reason notes that `spec find` surfaces on a vector-less memory. Nodes is always non-nil.

func FindNodes added in v0.5.0

func FindNodes(
	ctx context.Context,
	client graphql.Client,
	query *string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sort *gen.NodeSort,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
) (*FindNodesPage, error)

FindNodes runs the unified node search/list (cor:api:090) and flattens the hits[].node envelope into a bare node slice. Omit query for a filtered list in deterministic order (the old `nodes` semantics); pass query + mode to rank (the old `nodeSearch`). sortProperty orders by a properties/data JSON path and overrides sort when set (#719). All args are optional; nil pointers are omitted from the wire so the server applies no constraint. The caller maps GraphQL errors through MapError as usual.

func FindNodesProjected added in v0.14.0

func FindNodesProjected(
	ctx context.Context,
	client graphql.Client,
	query *string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sort *gen.NodeSort,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
	withProperties, withData bool,
) (*FindNodesPage, error)

FindNodesProjected is FindNodes with the two JSONB columns opt-in (#602). Pass withProperties / withData to select `properties` / `data` on the hit nodes; each is independent, so a caller that wants only the data envelope does not pay for properties. Every other caller should use FindNodes, whose projection is deliberately thin — a listing is an index.

The columns are separate arguments rather than one flag because they are separate payloads: this is the read half of `--where`'s `field` key, which names exactly these two columns, and the symmetry is the point (#602).

type HeldDetail added in v0.11.0

type HeldDetail struct {
	WorkerID   string
	HolderID   string
	HolderName string
	HeldAt     string
}

HeldDetail is the payload a WORKER_HELD error carries (hadron-server#1050): whose name it is, and since when.

TWO server paths raise this code and they do NOT carry the same fields. The pre-transaction check resolves the holder and sends workerId, heldBy, heldByName and heldAt; the compare-and-set inside the session-creating transaction — the one that refuses the loser of a race — sends only workerId and heldBy, because it has no holder read to spare. So HolderName and HeldAt are absent on a perfectly ordinary refusal, and a caller that renders them unconditionally prints a half-empty sentence on the path a concurrent bind takes. Every field is best-effort; only ok is a promise.

func WorkerHeldDetail added in v0.11.0

func WorkerHeldDetail(err error) (HeldDetail, bool)

WorkerHeldDetail extracts the WORKER_HELD payload from err's extensions; ok is false when err is not that error. Rendered from the extensions, not the message wording (cor:agt:020:09 is the contract). Call it BEFORE MapError wraps the error.

HELD is not TAKEN and the difference is the whole point: a held name is somebody's until they release it, so this refusal has no --force. Never pair it with a takeover suggestion.

func (HeldDetail) Holder added in v0.11.0

func (d HeldDetail) Holder() string

Holder names the person holding the name, preferring the handle the server resolved and falling back to the raw user id — which is what the race path leaves us with. Empty only when the server sent neither.

type HoldStaleDetail added in v0.13.0

type HoldStaleDetail struct {
	WorkerID string
	HolderID string
	Held     bool
}

HoldStaleDetail is the payload a WORKER_HOLD_STALE error carries (hadron-server#1084): the hold found NOW, at the moment the guarded write refused the caller's assertion.

HolderID is EMPTY when the name is currently held by NOBODY — the server sends `heldByUserId: null` for that, and it is a real answer rather than a missing field: it is what a caller who asserted a specific holder gets when the name was released underneath them. `Held` reports which of the two it is, because "" alone cannot: a JSON null and an absent key both decode to "".

The server deliberately does NOT say whose hold it is relative to the caller, nor whether releasing would be a force-release. It throws before comparing the holder to the caller, and the account now holding the name may well BE the caller — a caller asserting expectUnheld who turns out to hold it themselves is the plain case. The client knows its own id and can decide; rendering the server's neutrality as an accusation is the mistake this comment exists to prevent.

func WorkerHoldStaleDetail added in v0.13.0

func WorkerHoldStaleDetail(err error) (HoldStaleDetail, bool)

WorkerHoldStaleDetail extracts the WORKER_HOLD_STALE payload from err's extensions; ok is false when err is not that error. Call it BEFORE MapError wraps the error, like WorkerHeldDetail.

type InternalFault added in v0.32.0

type InternalFault struct {
	ServerCode string
	RequestID  string
}

InternalFault is the additive detail an internal-fault error carries.

func InternalFaultOf added in v0.32.0

func InternalFaultOf(err error) (InternalFault, bool)

InternalFaultOf reports the detail of an internal-fault error built by NewInternalFaultError (the --json envelope adds it additively).

type ListNode added in v0.5.0

ListNode is the shallow node projection the unified `findNodes` field returns under hits[].node — id/loc/name/type/tags/seq/isRunnable/updatedAt. Aliased here so callers don't spell the deeply-nested genqlient type name (and so a future projection change is a one-line edit), matching the batchNode alias pattern in nodedoc.go.

Properties and Data are the two JSONB columns, and they are OPT-IN (#602): they arrive only when FindNodesProjected was asked for them, and are nil otherwise. A nil here therefore means "not requested", NEVER "the node has none" — the two are indistinguishable at this type, which is the whole defect #602 was filed for. Read them only on the path that asked for them.

type MemoryKey added in v0.36.0

type MemoryKey struct {
	Memory     string `json:"memory"`
	Passphrase string `json:"passphrase"`
}

MemoryKey is one --unlock entry (hadron-cli#1234). v1 is passphrase-only: the header's raw dataKey form is deliberately not offered (Victor delta 3).

type NodeKindState added in v0.14.0

type NodeKindState struct {
	Role       *string
	IsRunnable bool
}

NodeKindState is what a node ALREADY is, which an update needs because the gate reads the kind the node is now as well as the kind it will be, and an omitted field preserves the stored one.

type PRSlotFailure added in v0.37.0

type PRSlotFailure struct {
	Slot          string  `json:"slot"`
	CurrentTaskID *string `json:"currentTaskId"`
	State         string  `json:"state"`
	Verdict       *string `json:"verdict"`
	Head          *string `json:"head"`
	Counts        bool    `json:"counts"`
}

PRSlotFailure is the server's failing-slot projection, not a client readiness decision.

type RawResult

type RawResult struct {
	Body   json.RawMessage
	Errors []rawError
}

RawResult is the verbatim GraphQL response envelope.

func RawGraphQL

func RawGraphQL(ctx context.Context, serverURL, token, query string, variables map[string]any, httpClient *http.Client) (*RawResult, error)

RawGraphQL posts an arbitrary query/mutation to the server and returns the raw response body. Used by `hadron api`. The returned error carries the mapped exit code when the response contains GraphQL errors.

func (*RawResult) Err

func (r *RawResult) Err() error

Err returns a CodedError summarizing the first substantive GraphQL error, or nil when the response is error-free. A null error entry decodes to an empty rawError; it must not hide a later typed refusal.

type ResponseWarning added in v0.27.0

type ResponseWarning struct {
	Path     string
	Code     string
	Text     string
	Register *WarningRegister // nil when the server sent none (a caller with no Agent)
}

ResponseWarning is one entry of a GraphQL response's `extensions.warnings` (server#2198/#2207): advice on an operation that SUCCEEDED, filed under the operation's response key (Path). Code and Text are the server's, verbatim; the code list is closed per server release and a client tolerates unknown ones.

type ScopeRefusal added in v0.16.0

type ScopeRefusal string

ScopeRefusal names why the server refused a credential by its OAuth scope. Its values are also `auth status` / `auth token validate`'s rejectedReason.

const (
	// ScopeMCPOnly: the grant is `mcp` alone, valid for MCP clients only.
	ScopeMCPOnly ScopeRefusal = "mcp-only-scope"
	// ScopeUnsupported: the grant carries a scope this server does not
	// support, so it is refused everywhere.
	ScopeUnsupported ScopeRefusal = "unsupported-scope"
)

func OAuthScopeRefusal added in v0.16.0

func OAuthScopeRefusal(err error) ScopeRefusal

OAuthScopeRefusal classifies a FORBIDDEN that refuses the CREDENTIAL, not the caller's permission. extensions.reason decides whenever the server sends it (server#1306), and an unknown reason is NOT a scope refusal. Only a server that sends no reason at all (one that predates #1306) is recognised by the MCP-only sentence, so the CLI works in either deploy order. Anything else returns "", and the error keeps its ordinary FORBIDDEN mapping. Call it on the RAW error.

type SearchHit added in v0.6.0

type SearchHit struct {
	Score         *float64
	AbstractStale bool
	Node          *SearchNode
}

SearchHit is one scored search result. Score is nil when the server returned an unscored hit; AbstractStale reports the vector index's abstract-staleness flag (false when absent).

type SearchNode added in v0.6.0

SearchNode is the search-shaped node projection (abstract included).

type SearchPage added in v0.6.0

type SearchPage struct {
	Hits     []*SearchHit
	Passages []*SearchPassage
	Total    *int
	Degraded *string
	Reason   *string
	Scope    *SearchScope
}

SearchPage is the scored counterpart of FindNodesPage: it preserves per-hit scores rather than flattening to bare nodes. Passages carries the chunk results a granularity: chunk search returns; it is never nil.

func SearchNodes added in v0.6.0

func SearchNodes(
	ctx context.Context,
	client graphql.Client,
	query string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
	scope *string,
	orgID *string,
	appRef *string,
) (*SearchPage, error)

SearchNodes runs a ranked findNodes query (the `hadron search` backend), keeping per-hit score + vector metadata that FindNodes drops. sortProperty orders by a properties/data JSON path and overrides the mode ranking window when set (#719); nil pointers are omitted from the wire. scope names the search lens — a scope id, a bare name, `app` or `global` — and is resolved server-side; the resolution it reports comes back on SearchPage.Scope.

The two context arguments are for DIFFERENT scope forms and are not interchangeable: appRef is REQUIRED for `scope: "app"` and for a bare scope NAME (a name is unique only per owner), while orgID selects the member's "active organization" view (cor:api:100:01), which is what `scope: "global"` resolves against.

func SearchNodesProjected added in v0.14.0

func SearchNodesProjected(
	ctx context.Context,
	client graphql.Client,
	query string,
	mode *gen.FindNodesMode,
	filter *gen.NodeFilter,
	sortProperty *gqltypes.NodePropertySort,
	limit, offset *int,
	scope *string,
	orgID *string,
	appRef *string,
	withProperties, withData bool,
	ranking SearchRanking,
) (*SearchPage, error)

SearchNodesProjected is SearchNodes with the two JSONB columns opt-in (#602), so a ranked result can show the field a --where predicate selected on. Each column is independent; callers that want neither should use SearchNodes, whose projection is deliberately thin.

withProperties and withData are ADJACENT BOOLEANS, which is the same hazard the scope/appRef pair carries below: transposing them compiles and silently asks for the other column. The command tests assert each flag selects its own column and NOT its neighbour, which is what catches it.

type SearchPassage added in v0.34.0

SearchPassage is one chunk-granularity result (cor:api:090, spec 033): where in which node the matching chunk sits.

type SearchRanking added in v0.34.0

type SearchRanking struct {
	Expand      *int
	Fields      []gen.NodeField
	Granularity *gen.SearchGranularity
	MinScore    *float64
}

SearchRanking is findNodes' optional ranking arguments (cli#1341). The zero value asks for none of them, and each nil/empty one is omitted from the wire: graph neighbours appended after the hits, the lexical field weight-mask, node vs chunk granularity, and an absolute score floor.

type SearchScope added in v0.14.0

SearchScope is the scope a search actually ran under, as the SERVER resolved it (spec 049). Nil when no scope applied.

Source is which rung of the resolution ladder won, and DroppedCount is how many of the scope's memories the caller may not read. Both are reported rather than derived: the ladder is the server's, and a client that inferred either would be a second implementation of it.

type TakenDetail added in v0.9.0

type TakenDetail struct {
	WorkerID   string
	SessionID  string
	LastDriver string
	LastSeenAt string
}

TakenDetail is the informed-takeover payload a WORKER_TAKEN error carries (hadron-server#940): everything the takeover prompt needs, in one round trip. Fields are empty when the server omitted them (lastDriver is null for an unattributed session).

func WorkerTakenDetail added in v0.9.0

func WorkerTakenDetail(err error) (TakenDetail, bool)

WorkerTakenDetail extracts the WORKER_TAKEN payload from err's extensions; ok is false when err is not that error. The MESSAGE also narrates the payload today, but the extensions are the documented contract (cor:agt:020:03) — render from these, not from message wording. Call it BEFORE MapError wraps the error.

type WarningRegister added in v0.27.0

type WarningRegister struct {
	State   string
	TaskLoc *string
	TaskURN *string
}

WarningRegister says where the App's register maps a warning's code: State is mapped (TaskLoc, TaskURN set), unmapped or unresolved.

type WarningSink added in v0.27.0

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

WarningSink collects the warnings of the requests made with the context WithWarningSink returned. It belongs to that context alone: a request made with any other context never reaches it.

func WithWarningSink added in v0.27.0

func WithWarningSink(ctx context.Context) (context.Context, *WarningSink)

WithWarningSink returns a context whose successful requests file their response warnings in the returned sink. Commands that never install one see no change.

func WithWarningSinkForFields added in v0.29.0

func WithWarningSinkForFields(ctx context.Context, fields ...string) (context.Context, *WarningSink)

WithWarningSinkForFields collects only warnings whose response key belongs to one of the selected mutation fields. Typed CLI operations select direct root fields; unresolved root fragments fail closed rather than displaying advice for an operation we cannot identify. The unfiltered task sink is unchanged.

func (*WarningSink) All added in v0.29.0

func (s *WarningSink) All() []ResponseWarning

All returns a copy in response order, for commands that can perform several writes (imports, replacements). A sink is scoped to one command invocation.

func (*WarningSink) For added in v0.27.0

func (s *WarningSink) For(path string) []ResponseWarning

For returns the warnings filed under path (an operation's response key), in the server's order. It never returns nil.

Directories

Path Synopsis
Package gqltypes holds hand-authored Go types that the generated genqlient client binds to (see genqlient.yaml `bindings`).
Package gqltypes holds hand-authored Go types that the generated genqlient client binds to (see genqlient.yaml `bindings`).

Jump to

Keyboard shortcuts

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