PromptPack CRD
A PromptPack is a versioned collection of prompts for AI agents, sourced from a ConfigMap containing a pack.json file in the PromptPack format. The controller validates pack.json against the PromptPack JSON Schema before activating it, using the copy built into the operator unless the Helm value operator.promptPackSchema.source is network.
A live PromptPack (source.type: live) points at an authored Arena project instead. A PromptPackSource of type project creates it, and the runtime compiles the project’s current source when a session opens.
API version
Section titled “API version”apiVersion: omnia.altairalabs.ai/v1alpha1kind: PromptPackShort name: pp (e.g. kubectl get pp).
Spec fields
Section titled “Spec fields”A published PromptPack version is an immutable release: the whole spec is
frozen after create (CEL: self == oldSelf). To change anything, publish a new
version.
packName
Section titled “packName”Logical pack identity. Required. Versions of the same pack share a
packName; an AgentRuntime’s spec.promptPackRef.name references it.
spec: packName: customer-serviceversion
Section titled “version”Semantic version of this pack. Required, and must match
^(\d+)\.(\d+)\.(\d+)(-[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?(\+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*)?$
(e.g. 1.0.0, 2.1.0-beta.1), or the literal live. A leading v is
rejected. The deploy API and PromptPackSource strip one from the version they
are given. live is required when source.type is live and rejected
otherwise (CEL-validated).
An AgentRuntime pin matches version as an exact string, including build
metadata, so 1.0.0+gAAAA and 1.0.0+gBBBB are two different packs. For
channel ordering, though, semver gives the two versions equal precedence, so
the one created later serves the channel. The other becomes Superseded, with
DuplicateVersion True. To avoid the tie, give each publish a distinct
MAJOR.MINOR.PATCH or prerelease.
spec: version: "2.0.0"source
Section titled “source”Source of the compiled PromptPack content.
| Field | Type | Required | Description |
|---|---|---|---|
source.type | configmap | live | Yes | Source type |
source.configMapRef.name | string | When type is configmap | Name of the ConfigMap |
source.live.sourceRef | string | When type is live | Name of the PromptPackSource that created the pack, in the same namespace |
source.live.project.id | string | When type is live | ID of the Arena project whose source is the content |
source.live.project.user | string | When type is live | Pseudonym of the user whose working clone of the project (arena/workspaces/<id>/<user>) the agent mounts and compiles. A project has no shared copy, so a live pack always runs one user’s clone. |
source.live is required for type live, where configMapRef is rejected,
and is rejected for type configmap (CEL-validated). The ConfigMap must
contain a pack.json key with valid PromptPack JSON content.
A live pack is dev-only. The controller does not resolve the project, validate a
schema, compute contentDigest or place the pack on a channel: it sets
SourceValid True with reason LiveSource and the phase to Active. An
AgentRuntime may reference it only without version or track, and never from
a rollout (see
AgentRuntime promptPackRef).
spec: source: type: configmap configMapRef: name: my-prompts # ConfigMap must have pack.json keyskills
Section titled “skills”Skills to expose to agents using this pack. Every entry goes through a SkillSource; inline skill content is not accepted.
| Field | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Name of a SkillSource in the pack’s own namespace |
include | string[] | No | Keep only skills whose SKILL.md frontmatter name matches an entry. Empty keeps every skill the source synced |
mountAs | string | No | Group name the skills are exposed under (pattern ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$). Defaults to the basename of the source’s status.artifact.contentPath, which is the synced content version |
skillsConfig
Section titled “skillsConfig”| Field | Type | Default | Description |
|---|---|---|---|
maxActive | int32 (min 1) | Unlimited if omitted | Cap on concurrently active skills |
selector | model-driven | tag | model-driven | Which skills the model is offered in the Phase-1 index. model-driven offers all of them and lets the model activate what it needs. tag offers only skills whose SKILL.md metadata tags (comma-separated) shares an entry with tags |
tags | []string | — | Tags the tag selector matches. Required when selector is tag; ignored otherwise. A skill with no tags metadata is never offered by the tag selector |
Status fields
Section titled “Status fields”Current phase of the PromptPack.
| Value | Description |
|---|---|
Pending | Not validated |
Active | Valid, and the highest version of its packName on the stable or the prerelease channel |
Superseded | A newer validated version has been published on its channel, or a sibling of equal semver precedence was created later (see DuplicateVersion). A superseded version still serves any AgentRuntime pinned to it |
Failed | Source or schema validation failed |
activeVersion
Section titled “activeVersion”The pack’s spec.version while it is Active; cleared when it becomes
Superseded.
contentDigest
Section titled “contentDigest”sha256:<hex> of the resolved pack.json, recomputed every reconcile. The
operator stamps it onto agent pods, which record it on each session, so a stored
result names the content that produced it.
publishedDigest
Section titled “publishedDigest”sha256:<hex> of pack.json when the operator first saw this version. Set
once and never changed. ContentDrift compares contentDigest against it.
contentMetadata
Section titled “contentMetadata”The pack’s own metadata block, lifted out of pack.json verbatim.
governance
Section titled “governance”The typed reading of metadata.governance (see
below), with a synthesised declared list naming the
fields the pack actually stated. extensions is not carried. Absent when the
pack declares no governance block.
skillsManifestVersion
Section titled “skillsManifestVersion”Hash of the resolved skill manifest — identifies the skill content in force. Empty when the pack declares no skills.
lastUpdated
Section titled “lastUpdated”Timestamp of the last status update.
conditions
Section titled “conditions”| Type | Description |
|---|---|
Ready | Roll-up of SourceValid, SchemaValid and the three skill conditions; inherits the first failing one’s reason and message |
SourceValid | ConfigMap exists and contains pack.json key. For a live pack, True with reason LiveSource when source.live is set |
SchemaValid | pack.json content conforms to the PromptPack schema |
SkillsResolved | Every spec.skills[].source names an existing SkillSource (True with reason NoSkills when none are declared) |
SkillsValid | No skill-name collisions across sources after include |
SkillToolsResolved | Every resolved skill’s allowed-tools is declared by the pack |
ContentDrift | True is the bad state. True, with a Warning event, while contentDigest differs from publishedDigest: pack.json changed under a published version. Stays True until the content returns to the published digest. The pack keeps serving |
Superseded | True is the bad state. True when a newer version has been published on this version’s channel, or (reason DuplicateVersion) when a sibling of equal semver precedence won the publish-order tiebreak |
DuplicateVersion | True is the bad state. Set True, with a Warning event, when a sibling whose version differs only in build metadata (equal semver precedence) was created later and serves the channel instead. Absent unless a tie has occurred; False once it is resolved |
AgentsNotified | Reports how many AgentRuntimes reference this pack (reason NoAgentsToNotify when none) |
See PromptPack validation for how source and schema validation run, and how failures surface as Kubernetes events.
ConfigMap format
Section titled “ConfigMap format”The referenced ConfigMap must contain a pack.json key with a compiled PromptPack following the PromptPack specification:
apiVersion: v1kind: ConfigMapmetadata: name: my-promptsdata: pack.json: | { "id": "customer-service", "name": "Customer Service Assistant", "version": "1.0.0", "template_engine": { "version": "v1", "syntax": "{{variable}}" }, "prompts": { "support": { "id": "support", "name": "General Support", "version": "1.0.0", "system_template": "You are a helpful customer service agent for {{company_name}}. Be professional, empathetic, and solution-oriented.", "variables": [ { "name": "company_name", "type": "string", "required": true, "description": "Name of the company" } ], "parameters": { "temperature": 0.7, "max_tokens": 1024 }, "validators": { "banned_words": ["competitor", "lawsuit"] } } }, "metadata": { "domain": "customer-service", "language": "en", "tags": ["support", "helpdesk"] } }PromptPack structure
Section titled “PromptPack structure”| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Unique identifier (lowercase, hyphens allowed) |
name | string | Yes | Human-readable name |
version | string | Yes | Semantic version (MAJOR.MINOR.PATCH) |
template_engine | object | Yes | Template configuration |
prompts | object | Yes | Map of prompt definitions |
metadata | object | No | Domain, language, tags, and the governance block (below) |
tools | object | No | Tool definitions for function calling |
fragments | object | No | Reusable template fragments |
metadata.governance
Section titled “metadata.governance”Records what the agent is for and how far it may act alone (promptpack-spec RFC 0013). Values are asserted by the pack’s author: readers may use them to tighten a policy, never to relax one.
Every field is optional; an omitted field means undeclared, not a safe default. RFC 0013 forbids inferring one from absence.
| Field | Type | Description |
|---|---|---|
intended_purpose | string | What the agent is for |
foreseeable_misuse | string[] | Ways it could be misused |
autonomy_level | string | How far it acts without a human |
accountable_owner | string | Who answers for it |
operator_role | string | Provider or deployer |
risk_classification | string | The author’s risk position — a legal judgement, never derived |
intended_deployment_contexts | string[] | Where it is meant to run |
capabilities | string[] | What it can do |
approved_environments | string[] | Where it is approved to run |
requires_ai_disclosure | boolean | Whether a human counterparty must be told this is an AI |
vocabularies | object | Vocabulary URIs the values are drawn from |
extensions | object | Firm-specific additions |
Two of these have runtime behaviour:
requires_ai_disclosureis delivered to clients and recorded on each session. See Disclose That an Agent Is an AI.- The whole block is exposed to ToolPolicy CEL rules as
governance, so a rule can gate on what an agent declared.governance.declaredlists the fields the pack stated, distinguishing undeclared from empty.
A pack carries eval definitions in a pack-level evals array and in each prompt’s evals array, next to validators. A prompt-level eval overrides a pack-level eval with the same id. The eval worker runs the pack-level evals plus those of the prompt the runtime opens the pack at: workflow.entry, then agents.entry, then the only prompt, then default. Realtime evals run these definitions against live sessions. See Realtime evals for how.
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Identifier, unique within its scope |
type | string | Yes | Registered eval type, for example llm_judge, contains, regex or guardrail_triggered |
trigger | string | Yes | When the eval fires (below) |
params | object | No | Type-specific parameters |
sample_percentage | number | No | Percentage of turns or sessions to run, for sample_turns and sample_sessions. Default 5 |
enabled | boolean | No | Default true |
| Trigger | Fires |
|---|---|
every_turn | After each assistant message |
sample_turns | After a hash-sampled share of assistant messages |
on_session_complete | When the session completes |
sample_sessions | On a hash-sampled share of completed sessions |
on_conversation_complete and on_workflow_step are valid values that realtime evals do not fire.
| Eval type | Parameters | Cost |
|---|---|---|
llm_judge | criteria (required), rubric, model, provider (name of a spec.providers entry) | One LLM call |
contains | patterns (all must appear, case-insensitive). content_includes is an alias | None |
regex | pattern, expect_match | None |
guardrail_triggered | validator, should_trigger | None |
For the complete specification, see promptpack.org.
Example
Section titled “Example”apiVersion: omnia.altairalabs.ai/v1alpha1kind: PromptPackmetadata: name: customer-service namespace: agentsspec: packName: customer-service version: "2.0.0" source: type: configmap configMapRef: name: cs-prompts-v2Status after deployment:
status: phase: Active activeVersion: "2.0.0" conditions: - type: SourceValid status: "True" reason: SourceValid message: "Source configuration is valid" - type: SchemaValid status: "True" reason: SchemaValid message: "pack.json content is valid" - type: AgentsNotified status: "True" reason: AgentsNotified message: "Notified 3 AgentRuntime(s)"See Author and compile a PromptPack for
writing PromptPacks in YAML and compiling with packc.