The resolver is the component that turns a prompt ID (likeDocumentation 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.
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.
The ResolverChain class
The resolution chain is implemented in promptops/resolver/chain.py:
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:
.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.
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):
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:
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):
Pin formats for git ref
| Pin format | Example | Description |
|---|---|---|
| Commit SHA | abc123def456... | Pins to an exact commit |
| Git tag | v1.2.3 | Pins to a named tag |
| Semver range (local) | semver:^1.2.0 | Resolves to the highest matching local tag |
| Remote git URL | git+https://github.com/org/repo#v1.2.3 | Fetches from a remote repo at a specific ref |
| Remote semver | git+https://github.com/org/repo#semver:^1.0.0 | Resolves the highest matching tag on the remote |
consumption.yaml:
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 format | Example | Description |
|---|---|---|
| SHA-256 digest | sha256:<64 hex chars> | Content-addressed artifact (most reproducible) |
| HTTPS URL | https://example.com/promptops.json | Direct URL to a JSON artifact |
| OCI artifact | oci://registry.example.com/repo:tag | OCI image manifest (pulls first layer) |
| npm package | npm:@org/package@1.2.3 | npm registry package (reads package.json:promptops) |
| PyPI wheel | pypi:package==1.2.3 | PyPI wheel (reads first .json file with promptops in name) |
| GitHub Release | github-release:owner/repo@v1.2.3 | GitHub Release asset (first .json asset) |
consumption.yaml:
- If the pin is a
sha256:digest,PackagedResolverfetches the artifact, validates it againstprompt-package.schema.json, and finds the prompt spec with the matchingidinside the package’sspecsarray. - For all other URL/registry formats, the resolver fetches the artifact, validates it against
provider-artifact.schema.json, verifies the optional signature, then reads thepackage_digestfield and callsresolve()recursively with that digest (treating it as a level-4 sha256 lookup).
~/.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.
ResolverChain.resolve(prompt_id, manifest) call:
- Validates the manifest against
consumption-manifest.schema.json. - Looks up the resolution rules for
prompt_id(override,pin,idmapping). - Applies the four-level chain based on those rules.
Pin format decision guide
- Development
- Same-repo
- Pinned to commit
- Pinned to tag
- Semver range
- Governed release
Use a local override when you are iterating on a prompt in a checked-out repo and don’t want to publish or commit.