Installation and release¶
How a package gets onto a deployment and how its version is released: the
installation file and sources (installation directory, path, git by tag), pinning
with packages.lock, a single plan with plan --out and applying exactly that
plan with apply --plan, console edits, upgrading with live instances,
retirement, and releasing a version with a tag. The page is for package authors
and installation administrators. Rationale: TAI-ADR-0062 (items 6–7),
CP-ADR-0074.
flowchart LR
R["release:<br/>version, tag"] --> I["installation:<br/>installation.yaml"]
I --> L["lock<br/>packages.lock"]
L --> P["plan --out<br/>plan without writing"]
P --> H{"a human<br/>reads the plan"}
H -- yes --> A["apply --plan<br/>exactly this plan"]
H -- no --> I
Installation file¶
An installation is a separate file in the git repository of the installation, not of the package: which packages are installed on a particular deployment and from where, which ontologies are enabled for workspaces, and what is retired.
apiVersion: taimen.ai/v1
kind: Installation
key: production
spec:
packages:
- notifications # installation directory
- {key: claims, path: ../claims} # path relative to the installation file
- {key: helpdesk, git: https://git.example.com/example/helpdesk.git, ref: v0.3.1}
- {key: billing, git: git@git.example.com:example/packs.git, ref: v1.2.0, path: billing}
knowledge:
- {workspace: "${CLAIMS_WORKSPACE_ID}", packs: ["default@1", "claims@1"]}
retire:
TaskType: [legacy-claim-review]
| Source | Form | When to use it |
|---|---|---|
| installation directory | the package key | the package lives in the same git repository as the installation: packages/<key> next to the file, or spec.packagesDir |
| path | {key, path} |
a neighboring checkout during development; key is checked against the manifest |
| git | {key, git, ref, path?} |
a released version of the package from its repository |
Rules for a git source:
- the address is
https://host/pathwithout credentials in the address, orgit@host:path; other git protocols (file,ext,git) are not accepted. Access to a private repository comes from the git credential helper, not from the installation file; refis a tag only (refs/tags/<ref>): branches and commits are not accepted;pathis the package subdirectory in the repository;- only regular files are allowed in a package from git: a symbolic link, or two paths that differ only in case, means a refusal.
The requires of packages are pulled into the installation automatically: it is
enough to name the package you install. An empty packages: [] is a valid
installation; only the system task type task remains on the deployment.
Installation variables¶
Everything that depends on the deployment, a package moves into ${NAME}
variables and declares in the manifest (see Package anatomy).
The installation sets the values:
- The
--envfile (.envin the current directory by default) and the process environment; the environment takes precedence over the file. planchecks that every required variable is set, that the value matches its kind, and that the UUID of a workspace, project, principal, or role exists on the deployment.- Values are not written into the plan, only their hash
variablesHash. Apply rejects values that changed after the plan. - There are no secrets among the variables: an executor secret is a name in the
agent's
placement.secrets, and the value lives on the node (see Package agents).
packages.lock: pinning sources¶
claims 0.1.0: ../claims sha256:…
helpdesk 0.3.1: https://git.example.com/example/helpdesk.git 4f2a9c1b3d5e sha256:…
записан packages.lock
lock writes packages.lock next to the installation file (format
package-sdk.lock/v1, schema schema/v1/lock.schema.json): for each package, the
source, the version, the tag commit (for git), and contentHash, the hash of the
package's canonical set of files. packages.lock is committed to the
installation's git repository.
plan is built only from the pinned contents:
| Refusal | When | What to do |
|---|---|---|
lock_required |
the package is from git, and there is no lock entry | package-sdk lock |
source_ref_moved |
the tag in the source was moved after pinning | find out who moved the tag; to accept the new commit, run lock again |
content_mismatch |
the contents diverged from contentHash: an edited cache, or a package by path changed |
commit the edit and run lock again |
lock_stale |
the lock does not match the installation: a missing package, a different source or version | package-sdk lock |
For packages from the installation directory and by path, a lock is optional: they are in the installation's git repository anyway. But if a lock exists, it covers all packages of the installation and is checked.
Git source cache¶
Git sources are cached in $PACKAGE_SDK_CACHE, otherwise in
$XDG_CACHE_HOME/package-sdk, otherwise in ~/.cache/package-sdk: a repository
mirror and commit exports. If a source is unavailable while the plan is built, the
commit is taken from the cache, but only if its hash matches the lock.
package-sdk cache prune # exports not referenced by any packages.lock under the current directory
package-sdk cache prune --lock deploy/prod/packages.lock --lock deploy/test/packages.lock
package-sdk cache prune --all # the whole cache
Without a single lock under the current directory, prune deletes nothing and
asks you to name lock files or pass --all.
Plan¶
export CP_TOKEN=<access token audience control-plane>
package-sdk plan --install installation.yaml \
--server https://platform.example.com --env claims.env --out plan.json
plan writes nothing to the deployment. It checks the packages (check),
compares the deployment's core version from its openapi.json with the engines
of each package, checks the variables, and builds one plan for all kinds,
package-sdk.plan/v1 (schema schema/v1/plan.schema.json):
| Section | What it contains |
|---|---|
catalog |
the kinds that the installer installs as core resources: roles, skills, artifact types, capabilities, project templates, workspace types; for a package without processes and calendars, also task types, agents, and work rules |
core |
for each package with processes or calendars, a core plan (POST /api/v1/packages:plan): the core itself plans and installs such a package's task types, agents, calendars, processes, and work rules; replay of processes and the fate of open instances |
knowledge |
registration of package ontologies and the resulting ontology sets of workspaces next to the current ones |
notification-rules |
the notification rules that the notification service accepted on :validate |
retire |
retirement; for processes and calendars, how many live instances will run to completion |
The plan document records the deployment address, the core version, the source
pinning hash lockHash, the variable values hash variablesHash, the
overwriteConsole flag, and planHash of the whole document.
plan flag |
What it does |
|---|---|
--install <file> |
the installation file (required) |
--server <address> |
the deployment (required) |
--out <file> |
where to save the plan (required) |
--env <file> |
variable values, .env by default |
--workspace <id> |
the workspace of the package's processes for the core plan |
--replay-limit <N> |
how many recent instances of each process to replay (0–200, 50 by default) |
--overwrite-console |
overwrite the fields that people edited in the console (below) |
--json |
the plan as a document on stdout |
For a human, the plan is printed by section in the order of application, and at
the end comes итого изменений: N (total changes: N) or изменений нет (no
changes). The structural diff of the core plan: + will be added, ~ will
change, → rename; lines with - in the retire section are retirement.
Console edits and --overwrite-console¶
A core object field that a person edited on the deployment after the previous
package apply (in the console or through the API) is owned by console, not by
the package. By default the plan keeps such fields: the published version
takes the value from the deployment, and the plan reports this in a separate
block:
правки консоли: сохраняются (перезаписать — plan --overwrite-console)
останутся как в консоли:
= Process/claim (claims): /spec/decisions
To overwrite them with the value from the package, build the plan with the flag:
package-sdk plan --install installation.yaml --server https://platform.example.com \
--out plan.json --overwrite-console
правки консоли: перезаписываются (overwriteConsole)
будут перезаписаны:
! Process/claim (claims): /spec/decisions
The flag is stored in the plan and is part of its hash: a plan without the flag
cannot be applied "with overwrite", and vice versa. If a console edit should be
kept permanently, move it into the package: package-sdk export exports the
object from the deployment into a package file (for processes and calendars, with
--workspace, to take console fields into account).
Apply¶
apply applies exactly the saved plan:
- the document's
planHashmatches: an edited plan file is rejected; --servermatches the deployment the plan was built for;- the sources (
lockHash), the variable values (variablesHash), and the core version are the same as when the plan was built; - the plan is shown, and a human answered
yin the terminal. Without a terminal, apply is cancelled:нужен ответ человека в терминале(a human answer in the terminal is required); - before the first write, each section is rebuilt and compared with the plan,
and the core plan of each package is requested again and compared by its
planHash. Any divergence givesplan_stale, and nothing is written.
Sections are applied in the order catalog → core → knowledge →
notification-rules → retire. They are not atomic with respect to each other:
each write is idempotent, and if apply was interrupted, a repeated plan shows
the remainder, and apply of the new plan delivers it.
Credential¶
| Where | Token | Permissions |
|---|---|---|
| core | CP_TOKEN (an access token for the control-plane audience) or the operator's IAM credential that the core client finds |
packages.plan; write permissions for the kinds being installed: task_types.manage, artifact_types.manage, project_templates.manage, workspaces.manage, org.manage (roles, capabilities, skills), agents.manage, rules.write, processes.write, calendars.write; all permissions that the package grants to its agents |
notification service (if the installation has a NotificationRule) |
NOTIFY_TOKEN, or an exchange of the same credential for the notification-service audience with the notifications:admin scope; the address is the NOTIFICATION_SERVICE_URL variable |
— |
The token is taken before each request to the core and the notification
service: an access token from an IAM credential is exchanged again before it
expires, and after 401 invalid_credentials once more, with the request
repeated. So a long apply does not fail with 401 halfway through the plan.
CP_TOKEN and NOTIFY_TOKEN are an explicit human choice: they are not
renewed, and their lifetime must cover the whole run.
The permissions that an agent description grants to the agent must be held by
whoever applies: otherwise 403 permission_escalation.
Upgrading with live instances¶
A new package version is installed the same way: a new source (tag), lock,
plan, apply. For objects with immutable versions, an upgrade is a new version
next to the old one:
| Kind | What happens to what already exists |
|---|---|
TaskType, ProjectTemplate |
a new version is published, the previous active ones → deprecated; tasks and projects stay on their version |
Process |
spec.version: N+1; open instances run to completion on their version (pin) or move according to the migrations map (migrate). A removed element with open instances and no map is a migration_required plan error |
Skill |
a contract change with the same version is an error: raise spec.version |
Agent |
a new revision if the description changed; the executor restarts on it by itself |
KnowledgePack |
different contents with the same version is an error; an edit is a new version |
The core plan for a package with processes shows a replay of the new version over the logs of recent instances: how many instances would have decided differently. Details are in Processes in a package and Scenarios and the core plan.
Retirement¶
No kind supports deletion. An object that is no longer needed is retired by the
retire list of the installation file: this is the history of the environment,
not of the package.
Kind in retire |
What happens |
|---|---|
TaskType, ProjectTemplate |
all active versions → deprecated; the created tasks and projects live on |
WorkRule |
the rule is archived; the work it created remains |
Agent |
the executor stops, the credential is revoked, the run history remains; the key is not reused (409 agent_retired) |
NotificationRule |
the rule is retired in the notification service; sent notifications remain |
Process |
new instances do not start, live ones run to completion; the plan shows how many |
Calendar |
only if no active process refers to it (calendar_in_use) |
ArtifactTypeis not retired.- The system task type
taskcannot be retired. - A key cannot be both declared in a package and retired by the same installation.
- A task type removed from a package is not retired by itself: add it to
retireonce its open tasks are closed. - A rename is neither a retirement nor a creation: use
renamesin the manifest for it (see Package anatomy).
Releasing a version¶
A package is released in its own repository with a tag. Installations take it from git by that tag.
-
Version.
spec.versionof the manifest is the package's SemVer:- major: an object is removed or renamed, fields or permissions are narrowed, a process behaves differently on the same inputs, a new required variable;
- minor: new objects, optional fields, variables with a
default; - patch: a fix that does not change behavior.
The process version (the process's
spec.version, an integer) is separate: new process behavior is alwaysN+1. 2. Compatibility.enginesis the range of core versions the package was tested against (initwrites the core minor that ships with the tool);requireslists packages with ranges.package-sdk describe .shows what an installation needs. 3. License and authors.licenseis an SPDX identifier (package-sdk init --license Apache-2.0), plusauthorsandhomepage.describeand a README section show them. 4. Documentation.package-sdk docs . --writeupdates the generated README section; the package changelog records what was added, changed, removed, and what to do when upgrading. 5. Testing.package-sdk test .: a green pyramid (see Package tests). 6. Tag.git tag v<version>on the release commit, and publish the tag. Tags are not moved: installations with a lock refuse withsource_ref_moved. A fix is a new patch version and a new tag. 7. Installation. In the installation file,{key, git, ref: v<version>}, thenlock,plan,apply.
Everyone who installs the package sees the tag
Publishing a tag is a decision, just like applying a plan. The author plugin in Claude Code asks for separate consent to the tag and separate consent to the apply (see Package author in Claude Code).
Deployment initialization¶
make bootstrap installs the default installation by itself, at step 5b, through
the same single plan as plan and apply: all kinds, and the plan is saved in the
initialization state directory. There is no separate confirmation: starting the
initialization is already the operator's decision, and the log marks it. A repeated
run builds the plan again and applies nothing if it is empty. For a running
deployment there is one path: plan --out, a human reviews the plan, then
apply --plan.
Common problems¶
| Symptom | Cause | What to do |
|---|---|---|
plan: версия ядра не прочитана … план не строится (the core version was not read … the plan is not built) |
the deployment is unreachable at --server |
check the address and the network |
plan: the core version is outside engines |
the package is not designed for this core version | upgrade the package or the core; adjust engines if the package has been tested |
lock_required, lock_stale, content_mismatch, source_ref_moved |
the lock does not match the sources | see the table above |
git source: нужен https://хост/путь без учётных данных (https://host/path without credentials is required) |
a token in the address or an unsupported protocol | move the credentials into the git credential helper |
ref: нужно имя тега (a tag name is required) |
a branch or a commit instead of a tag | release a tag |
plan_stale |
after the plan, the deployment, instances, sources, or variables changed | build the plan again and show it again |
план построен для …, а применяется к … (the plan was built for …, but is applied to …) |
a different --server for apply |
apply to the deployment the plan was built for |
apply: нужен ответ человека в терминале, применение отменено (a human answer in the terminal is required, apply cancelled) |
no terminal | run it in a terminal |
migration_required |
a removed process element with open instances and no map | a migrations map or the pin policy |
403 permission_escalation |
the token lacks the permissions that the package grants to agents | apply with a token that has these permissions |
apply breaks off with 401 invalid_credentials |
CP_TOKEN or NOTIFY_TOKEN expired: a token set by a variable is not renewed |
apply with an IAM credential (its token is renewed automatically) or set a fresh token; a new plan shows the remainder |
plan: в установке есть правила уведомлений — нужен сервис уведомлений (the installation has notification rules: the notification service is needed) |
no NOTIFICATION_SERVICE_URL or no token |
set the variable and NOTIFY_TOKEN |
See also¶
- Package anatomy: manifest, variables, versions, renames
- Package tests
- Processes in a package: versions and live instances
- Catalog packages: reference of kinds and how they are applied
- Package author in Claude Code:
pkg_planandpkg_apply - Package readiness checklist