quay enqueue --linear-issue expects the Linear ticket body to contain one
fenced quay-config block. The block gives Quay the metadata it cannot infer
from ordinary prose.
Implement the requested API validation.
```quay-config
repo: hermes-agent
base_branch: dev
tags:
- backend
- validation
slack_thread: https://example.slack.com/archives/C123456/p1712345678901234
authors:
- name: Ada Lovelace
slack_id: U06TDC56VJB
worker_execution: goal
```base_branch, slack_thread, and worker_execution are optional.
base_branch overrides the repo default for this task only; Quay branches from
origin/<base_branch> and instructs the worker to open the PR into that branch.
worker_execution defaults to oneshot; set it to goal when the task should
use durable goal mode and continue across worker attempts. authors is
required and must contain at least one entry.
For Linear-backed umbrella workflows, do not add umbrella membership or
ordering to quay-config. Linear's native parent/child hierarchy defines the
umbrella and its expected subtasks, and Linear's native blocked-by relations
define task ordering. The umbrella parent uses the ordinary repo and optional
base_branch fields; child tickets use ordinary execution metadata.
- The fence must be exactly
quay-config. - Exactly one block is allowed.
- Tabs are rejected in the block indentation.
tagsmust be a list of strings.base_branch, when present, must be a git branch name, not a full ref.authorsmust be a non-empty list of objects withnameandslack_id.slack_idmust be a bare Slack user id likeU06TDC56VJB.slack_thread, when present, must be a Slack permalink that can be converted to<channel>:<ts>.worker_execution, when present, must beoneshotorgoal.- Linear-backed tickets must not use an
umbrella:object. Linear enqueue rejects umbrella config metadata; use native Linear parent/child relations for umbrella membership and native Linear blocked-by relations for ordering.
Validate a ticket-shaped JSON payload:
echo '{"body":"Implement...","tags":["backend"],"authors":[{"name":"Ada","slack_id":"U06TDC56VJB"}],"external_ref":"ENG-1234"}' \
| quay validate-ticketValidate from a file:
quay validate-ticket --ticket-json ./ticket.jsonUse a custom schema:
quay validate-ticket --schema-file ./ticket_schema.toml --ticket-json ./ticket.jsonQuiet mode uses exit codes without stdout:
quay validate-ticket --ticket-json ./ticket.json --quiet| Code | Meaning |
|---|---|
0 |
Valid input. |
1 |
Validation errors. |
2 |
Schema or usage error. |
3 |
Input file or JSON error. |
Invalid ticket content is printed on stdout as:
{"valid":false,"errors":[...]}Schema and input errors are printed on stderr as:
{"error":"schema_error","message":"..."}The shipped default schema requires:
body: string, 10 to 50000 chars.tags: unique lowercase alphanumeric/dash strings, at least one.authors: at least one object withnameandslack_id.
Optional:
slack_thread:<channel>:<ts>external_ref: stringworker_execution:oneshotorgoalbase_branch: task-level PR base override
Override the schema with --schema-file or by placing ticket_schema.toml in
QUAY_CONFIG_DIR or $HOME/.quay.
When a ticket's target repo has any per-repo tag vocabulary configured (via
quay repo set-tags or quay repo apply-tags — see
Repositories), the validator enforces an
extra layer on top of the schema's tags field:
- Each tag is parsed as
<namespace>-<value>, splitting on the first-. Namespaces match[a-z0-9]+(no dashes); values may use[a-z0-9-]+. - Each
(namespace, value)pair must appear in the merged (deployment ∪ per-repo) vocab. - A namespace marked
required(in either layer; deployment-required wins) must have at least one matching tag in the list.
Repos with no per-repo vocab keep the legacy charset/min/unique-only
behavior; deployment-level required namespaces only bind opted-in repos.
Use quay tags list --repo <id> to inspect the merged vocab and the
enforced flag for a given repo.
Additional error codes the validator may return when enforcement is on:
| Code | Meaning |
|---|---|
TAG_UNKNOWN_NAMESPACE |
Tag is unparseable, or its namespace prefix isn't in the merged vocab. |
TAG_UNKNOWN_VALUE |
Namespace is known but the value isn't in its permitted set. |
TAG_REQUIRED_MISSING |
A required namespace has no representative tag in the list. |