Packages
Shared configuration bundles, dependency resolution, and merge rules
Overview
Packages are reusable configuration bundles published as OCI artifacts. They are the main unit of sharing in the stax ecosystem.
A package MAY contain any combination of:
- MCP
- Skills
- Rules
- Knowledge
- Surfaces
- Secrets
- Dependencies on other packages
Packages do not contain a prompt or persona in spec 1.0.0.
definePackage()
import { definePackage } from "stax";
export default definePackage({
name: "github-workflow",
version: "2.0.0",
description: "MCP servers and skills for GitHub workflows.",
author: "myorg",
license: "MIT",
tags: ["github", "pr", "code-review"],
mcp: "./mcp-servers.ts",
skills: "./skills/",
rules: "./rules/",
knowledge: "./knowledge/",
surfaces: "./surfaces/",
packages: ["ghcr.io/myorg/packages/git-utils:1.0.0"],
secrets: [{ key: "GITHUB_TOKEN", required: true }],
});Package artifact
Packages use the same OCI manifest shape as agents but with:
artifactType: application/vnd.stax.package.v1The config blob MUST contain kind: "package".
Dependency resolution
Allowed references
Package references follow the same rules as agent package references:
- relative local path
- exact OCI tag reference
- OCI digest reference
Resolution algorithm
Builders MUST resolve the dependency graph depth-first in declaration order.
Resolution rules:
- The current artifact's direct
packageslist is traversed in source order - Each package is resolved to an exact digest
- Transitive dependencies are resolved recursively
- A previously resolved
(ref, digest)pair MUST be deduplicated — if the same ref resolves to the same digest at a different point in the tree, it is visited only once - Circular dependencies MUST fail validation. The error message MUST include the cycle path (e.g.,
A → B → C → A) - The dependency graph MUST NOT exceed a depth of 32 levels. Builders MUST fail with a clear error if this limit is reached
Conflict policy
If the same package reference resolves to different digests in one dependency graph (diamond dependency), the builder MUST:
- Check
stax.lock— if the lockfile pins an exact digest for the conflicting reference and all declarations are tag-based (not digest-pinned to a different value), the locked digest wins - If no lockfile resolution is possible, fail with a dependency conflict error that identifies:
- the conflicting package reference
- the two (or more) digests it resolved to
- the dependency paths that produced each resolution
Builders SHOULD suggest resolution strategies in the error output, such as:
- pinning a specific digest in the agent's
packageslist - pinning an explicit digest in the consuming artifact until lockfile refresh support lands in the reference CLI
Floating version resolution
Package references in packages arrays in definePackage() follow the same rules as agent manifests: semver ranges and latest SHOULD NOT appear in committed source files. Builders MAY allow them interactively, and lockfile-capable implementations SHOULD resolve and pin them in stax.lock.
Merge semantics
Package layers are merged into the consuming agent or package in declaration order.
Precedence order:
- Transitive dependencies (lowest)
- Direct dependencies in declaration order
- The consuming artifact's own layers (highest)
Merge table
| Layer | Merge unit | Conflict key | Rule |
|---|---|---|---|
| MCP | server object | server name | Replace entire server object at the highest-precedence definition |
| Skills | top-level skill directory | skill name | Higher precedence replaces the entire skill directory |
| Rules | rule file | rule id if present, else archive path | Higher precedence replaces matching rule; otherwise append in precedence order |
| Knowledge | archive path | normalized path | Higher precedence replaces matching path |
| Surfaces | surface file | basename | Higher precedence replaces the entire file |
| Secrets | secret key | key | Higher precedence replaces entire declaration |
Rationale
stax uses replace, not deep merge, for structured conflicts. This avoids non-deterministic behavior and ambiguous list merging.
For example, if two packages define the github MCP server, the higher-precedence definition replaces the lower one entirely.
Rules ordering
After rule replacement/deduplication, remaining rules are ordered by:
- precedence level (lowest to highest)
- explicit
priorityascending - normalized archive path ascending
Consumers MAY apply additional runtime-specific ordering, but SHOULD preserve the canonical order when possible.
Skills conflict behavior
A skill is identified by its top-level directory name.
If two packages provide skills/fix-issue/, the higher-precedence package wins and the lower-precedence directory is discarded entirely.
Knowledge conflict behavior
Knowledge files are identified by normalized archive path.
If two sources provide the same path, the higher-precedence file wins.
Consumers SHOULD retain path provenance when materializing or indexing merged knowledge.
Secrets merging
Secrets are merged by key.
Higher precedence replaces the full declaration, including:
requireddescriptionkindexposeAs
Lock file
The spec model includes stax.lock for package resolution. The current reference CLI does not yet emit stax.lock automatically during stax build.
Lockfile goals
- pin exact digests
- make builds reproducible
- surface dependency conflicts early
Lockfile example
{
"lockVersion": 1,
"specVersion": "1.0.0",
"packages": {
"ghcr.io/myorg/packages/github-workflow:2.0.0": {
"digest": "sha256:abc...",
"dependencies": ["ghcr.io/myorg/packages/git-utils:1.0.0"]
},
"ghcr.io/myorg/packages/git-utils:1.0.0": {
"digest": "sha256:def...",
"dependencies": []
}
}
}Builders SHOULD fail in CI when stax.lock is out of date unless explicitly told to refresh it. Automatic refresh remains future work in the current reference CLI.
Package source directory structure
my-package/
├── package.ts
├── mcp-servers.ts
├── skills/
├── rules/
├── knowledge/
├── surfaces/
├── .staxignore
└── stax.lockPublishing guidance
Package authors SHOULD:
- publish immutable semver tags
- sign released artifacts
- pin transitive dependencies via
stax.lock - avoid breaking package behavior behind unchanged tags