Skip to main content

Documentation Index

Fetch the complete documentation index at: https://bintzgavin-apastra-14.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

The resolver is the component that turns a prompt ID (like summarize-v1) into actual prompt content your harness can use. It works through four levels in strict priority order — the first level to find a match wins.
local override → workspace → git ref → packaged artifact
This ordering means you can always override any pinned version locally for development without changing your consumption manifest or your CI configuration.

The ResolverChain class

The resolution chain is implemented in promptops/resolver/chain.py:
from promptops.resolver.local import LocalResolver
from promptops.resolver.workspace import WorkspaceResolver
from promptops.resolver.git_ref import GitRefResolver
from promptops.resolver.packaged import PackagedResolver
from promptops.manifests.consumption import validate_manifest

class ResolverChain:
    def resolve(self, prompt_id, manifest):
        if hasattr(manifest, 'data') and manifest.data:
            validate_manifest(manifest.data)

        rules = manifest.get_rules(prompt_id) if hasattr(manifest, 'get_rules') else {}
        target_id = rules.get('id', prompt_id)

        if rules and 'override' in rules:
            return LocalResolver().resolve(target_id, rules['override'])

        workspace_result = WorkspaceResolver().resolve(target_id)
        if workspace_result is not None:
            return workspace_result

        if rules and 'pin' in rules:
            pin = rules['pin']
            if pin.startswith('sha256:') or pin.startswith('https://') or pin.startswith('oci://') or pin.startswith('npm:') or pin.startswith('pypi:') or pin.startswith('github-release:'):
                return PackagedResolver().resolve(target_id, pin)
            else:
                return GitRefResolver().resolve(target_id, pin)

        raise NotImplementedError("Resolution fallback not yet implemented")
The chain reads resolution rules from the consumption manifest. If the manifest has an override for a prompt ID, it goes straight to LocalResolver. Otherwise it tries WorkspaceResolver first (no manifest entry needed), then falls back to GitRefResolver or PackagedResolver based on the pin format.

Level 1 — Local override

LocalResolver reads a prompt from a local file path. This is the highest-priority level and is always checked first when an override is present in the consumption manifest. When to use: local development against a separate prompt repo without publishing. Configure in consumption.yaml:
version: "1.0"
prompts:
  summarize-v1:
    id: summarize-v1
    override: "../prompt-repo/promptops/prompts/summarize-v1.yaml"
The resolver supports .yaml, .yml, and .json files. It caches results by mtime to avoid re-reading unchanged files. If the override path does not exist, LocalResolver raises FileNotFoundError. This prevents silent fallthrough to a stale pinned version when the override file is missing.
Use local overrides during rapid iteration. When you’re done, remove the override key and rely on the git ref pin for reproducibility.

Level 2 — Workspace

WorkspaceResolver finds prompts in the local promptops/ directory — no manifest entry needed. This is how same-repo prompts work without any publishing step. Paths tried (in order):
promptops/prompts/<prompt-id>.yaml
promptops/prompts/<prompt-id>.json
promptops/prompts/<prompt-id>/prompt.yaml
promptops/prompts/<prompt-id>/prompt.json
promptops/evals/<prompt-id>.yaml
promptops/evals/<prompt-id>.json
Minimal mode: if your project has three or fewer prompt files, WorkspaceResolver also checks the shorter prompts/ and evals/ paths (without the promptops/ prefix). This is auto-detected by counting files matching promptops/prompts/*.yaml and similar globs. Quick eval resolution: when the resolver finds a file in evals/, it checks for a prompt field. If present, it returns a synthetic prompt spec:
{"id": prompt_id, "template": data["prompt"], "variables": {}}
If WorkspaceResolver.resolve() returns None, the chain continues to level 3 or 4.

Level 3 — Git ref

GitRefResolver retrieves prompt content from a specific git commit, tag, or semver range. This is the default pinning mechanism for separate prompt repos — zero publishing overhead. Paths tried inside the git tree (in order):
promptops/prompts/<prompt-id>.yaml
promptops/prompts/<prompt-id>.json
promptops/prompts/<prompt-id>/prompt.yaml
promptops/prompts/<prompt-id>/prompt.json
promptops/evals/<prompt-id>.yaml
promptops/evals/<prompt-id>.json

Pin formats for git ref

Pin formatExampleDescription
Commit SHAabc123def456...Pins to an exact commit
Git tagv1.2.3Pins to a named tag
Semver range (local)semver:^1.2.0Resolves to the highest matching local tag
Remote git URLgit+https://github.com/org/repo#v1.2.3Fetches from a remote repo at a specific ref
Remote semvergit+https://github.com/org/repo#semver:^1.0.0Resolves the highest matching tag on the remote
Configure in consumption.yaml:
version: "1.0"
prompts:
  summarize-v1:
    id: summarize-v1
    pin: "v1.2.3"
For remote repos:
version: "1.0"
prompts:
  summarize-v1:
    id: summarize-v1
    pin: "git+https://github.com/your-org/prompt-repo#abc123"
How semver resolution works: GitRefResolver runs git tag --list (or git ls-remote --tags for remote repos) and finds all tags that satisfy the semver range using its own match_semver() implementation. It selects the highest matching stable version. Prerelease versions (e.g., v1.2.3-rc1) are excluded unless the range explicitly selects them. Fallback for git archive: if the remote server disables git archive, the resolver falls back to a full git clone + git checkout in a temporary directory and reads the file from there. All results are cached in memory by (prompt_id, pin) key.

Level 4 — Packaged artifact

PackagedResolver retrieves prompts from published artifacts — GitHub Releases, OCI registries, npm packages, or PyPI wheels. This level is triggered when the pin starts with a packaged-artifact prefix.

Pin formats for packaged artifacts

Pin formatExampleDescription
SHA-256 digestsha256:<64 hex chars>Content-addressed artifact (most reproducible)
HTTPS URLhttps://example.com/promptops.jsonDirect URL to a JSON artifact
OCI artifactoci://registry.example.com/repo:tagOCI image manifest (pulls first layer)
npm packagenpm:@org/package@1.2.3npm registry package (reads package.json:promptops)
PyPI wheelpypi:package==1.2.3PyPI wheel (reads first .json file with promptops in name)
GitHub Releasegithub-release:owner/repo@v1.2.3GitHub Release asset (first .json asset)
Configure in consumption.yaml:
version: "1.0"
prompts:
  summarize-v1:
    id: summarize-v1
    pin: "github-release:your-org/prompt-repo@v1.2.3"
Resolution flow for packaged artifacts:
  1. If the pin is a sha256: digest, PackagedResolver fetches the artifact, validates it against prompt-package.schema.json, and finds the prompt spec with the matching id inside the package’s specs array.
  2. For all other URL/registry formats, the resolver fetches the artifact, validates it against provider-artifact.schema.json, verifies the optional signature, then reads the package_digest field and calls resolve() recursively with that digest (treating it as a level-4 sha256 lookup).
Caching: fetched artifacts are cached to ~/.promptops/cache/ using a URL-safe filename derived from the ref. If a network fetch fails but a cached copy exists, the resolver uses the cache. If no cache is available, it raises RuntimeError.

How the consumption manifest drives resolution

The consumption manifest (promptops/manifests/consumption.yaml) is the single config file that controls how the resolver behaves for your app.
version: "1.0"
prompts:
  summarize-v1:
    id: summarize-v1
    pin: "abc123"           # commit SHA, tag, semver, or packaged ref
  classify-v2:
    id: classify-v2
    override: "./local-prompts/classify-v2.yaml"  # local dev override
  translate-v1:
    id: translate-v1
    pin: "github-release:org/prompts@v2.1.0"
    model: "claude-opus-4"  # override default model for this prompt
defaults:
  model: "claude-sonnet-4"
  provider: "anthropic"
The ResolverChain.resolve(prompt_id, manifest) call:
  1. Validates the manifest against consumption-manifest.schema.json.
  2. Looks up the resolution rules for prompt_id (override, pin, id mapping).
  3. Applies the four-level chain based on those rules.

Pin format decision guide

Use a local override when you are iterating on a prompt in a checked-out repo and don’t want to publish or commit.
pin: ~
override: "../prompt-repo/promptops/prompts/my-prompt.yaml"