Skill Runtime Lifecycle
AdaOS provisions an isolated runtime per skill with versioned A/B slots. Each installation produces a fully self-contained copy of the skill sources, dependencies, resolved manifest, and metadata that can be activated atomically.
Directory layout
Every skill lives under skills/<name> in the workspace. Runtime artefacts are stored separately:
skills/<name>/
skill.yaml
requirements.in # optional dependency input
handlers/
migrations/
data_migration.py # reserved optional bucket migration file
tests/
skills/.runtime/<name>/current_version
skills/.runtime/<name>/current_runtime.json
skills/.runtime/<name>/previous_runtime.json
skills/.runtime/<name>/v<major>.<minor>/
active # marker with the current slot (A or B)
previous # marker with the last healthy slot
meta.json # test results, timestamps, history
vendor/ # shared pip --target deps for this bucket
venv/ # shared service-skill interpreter for this bucket
data/
db/
skill_env.json # shared state for this compatibility bucket
files/
secrets.json # per-bucket secrets/artifacts
.skill_env.json # optional persisted environment snapshot
internal/ # schema-bound internal data for this bucket
slots/<A|B>/
src/ # snapshot of the skill sources
skills/<name>/
skill.yaml
handlers/
migrations/
data_migration.py # reserved optional bucket migration file
tests/
node_modules/
bin/
cache/
runtime/
logs/
tmp/
resolved.manifest.json
Runtime isolation is keyed by semantic major.minor, not full SemVer. For example, 0.14.0 and 0.14.3 share v0.14; 0.15.0 uses v0.15.
Slots are A/B code deployments inside the same bucket. Data, vendor/, and venv/ are not A/B-slotted inside a bucket.
Version policy
The default publication bump for skills is patch. A patch release stays in the same runtime bucket and uses the existing data/, vendor/, and venv/ trees.
If the skill has the reserved migration file, or a legacy manifest data migration hook, a requested/default patch bump is promoted to minor. A minor release creates a new v<major>.<minor> bucket and prepares a migrated copy of data there before activation.
Major releases are manual. They also land in a new bucket, but the decision to publish one is outside automatic CI/CD policy.
Install → test → activate
adaos skill install <name> performs the pipeline below:
- Select the inactive slot (A/B) for the target version and wipe any previous contents.
- Copy the current contents of
skills/<name>intoslots/<slot>/src. - Build bucket dependencies (either reusing the host interpreter with bucket
vendor/or creating the bucketvenv/for service skills). - Enrich
manifest.jsonintoresolved.manifest.json, resolving tool entry points, interpreter paths, timeouts, and policy defaults. - Prepare bucket data. Patch installs in the same
v<major>.<minor>bucket reuse the existing shareddata/tree without copying. A new bucket safely looks for the reserved data migration file and runs it when present. - Optionally run
src/skills/<name>/tests/(--test) from the prepared slot. Commands execute inside the staged environment (interpreter,PYTHONPATH,.skill_env.json), and logs are streamed toslots/<slot>/logs/tests.log. - Persist slot metadata (tests, timestamps, default tool, data migration result) for status and rollback operations.
adaos skill activate <name> switches the active version/slot markers atomically and records the previous version/slot for adaos skill rollback. Activation does not run data migration; migration belongs to prepare. Setup flows must run after activation so that secrets and runtime paths are stable.
After a successful activation, AdaOS keeps only the current runtime bucket and the previous rollback bucket. Older runtime buckets are pruned automatically because the runtime supports only one rollback step.
adaos skill rollback <name> rolls back the active version/slot marker. For a patch rollback this means old code over the same bucket data. For a minor rollback this points back to the previous bucket and therefore to that bucket's older data copy. AdaOS does not try to detect or block writes that happened after the minor activation.
Important architectural note:
- activation is a slot-pointer switch, not a generic live-memory migration
- in-process skills typically pick up new code on the next invocation from the active slot
- service skills are explicitly restarted by the runtime lifecycle
- durable migration authority belongs to persisted bucket data under
v<major>.<minor>/data, while derived caches/projections should be rebuilt after activation
For the target kernel-facing migration architecture, including rehydrate and rollback semantics for stateful skills, see AdaOS Supervisor.
Deactivate lifecycle
AdaOS may keep the core switch committed while quarantining a subset of skills. For that case the runtime lifecycle now includes explicit deactivation:
- a deactivated skill remains installed
- its prepared slot and metadata remain inspectable
- tool execution is blocked with a clear
skill is deactivatederror - ordinary
activateclears the deactivation marker and returns the skill to service
This is intended for post-commit checks where rolling back the whole core is unnecessary, but continuing to serve a broken skill would be unsafe.
Core-update orchestration may trigger this automatically after a successful runtime switch if post-commit skill checks fail.
When that happens, the deactivation record now persists the failure contract itself, including failure_kind, failed_stage, source, and whether the core switch was already committed.
Optional internal data migration
This feature is optional. A skill can ignore data/internal completely and continue using only:
data/db/skill_env.jsondata/files/*
Use data/internal only for state that must evolve together with runtime schema changes.
Default behavior
If a skill has no migration file, AdaOS does not copy data during patch prepare. The prepared slot uses the same bucket-level data/ directory as the currently active slot.
When preparing a new minor/major bucket without a migration file, AdaOS writes a warning to the AdaOS log and copies the previous bucket data/ tree into the target bucket without schema mutation.
Reserved migration file
The standard migration source is reserved at:
In the staged runtime this becomes:
The file should expose migrate(payload: dict) -> dict | None. During prepare of a new compatibility bucket, AdaOS runs it against the staged skill sources for the target slot. The migration file owns target data population: it should copy the source data it wants to preserve and mutate schema-bound state as needed.
Manifest-level data_migration_tool declarations are legacy-compatible, but LLM-authored skills should prefer the reserved file so core and generated code agree without extra manifest wiring.
The hook receives a payload with:
source_versiontarget_versionsource_runtime_buckettarget_runtime_bucketsource_data_roottarget_data_rootsource_internal_dirtarget_internal_dirdata_rootinternal_rootruntime_slotversion
AdaOS also exposes convenience environment variables while the hook runs:
ADAOS_SKILL_INTERNAL_DATA_ROOTADAOS_SKILL_INTERNAL_ACTIVE_PATHADAOS_SKILL_INTERNAL_TARGET_PATH
Important notes:
- the hook is optional
- if the reserved file is absent on a minor/major bucket change, AdaOS logs a warning and falls back to a plain data copy
- the hook is expected to populate the target bucket data it owns
- on migration failure, AdaOS clears the target bucket data and fails
prepare_runtime
Target direction
The target AdaOS migration model separates state classes:
- canonical durable state: must survive restart, rollback, and rebuild
- bucket-bound schema state:
belongs under
v<major>.<minor>/data/internal - derived runtime state: caches, indexes, projections, and similar rebuildable material
- live memory: in-flight objects and subscriptions that should be drained and recreated, not migrated implicitly
This means the reserved data migration file should be used for schema-sensitive persisted state, not as a platform promise that arbitrary process memory can be moved across activation.
After activation, stateful skills are expected to rebuild derived runtime state from durable truth.
Vendor vs venv
vendor/ and venv/ are separated because they solve different dependency problems:
vendor/is a bucket-levelpip --targetpackage overlay. It is added toPYTHONPATHfor ordinary Python skills that can run in the hub interpreter but need extra pure-Python packages.venv/is a bucket-level isolated Python environment. Service skills use it when they need their own interpreter process, ABI boundary, or dependencies that must not be installed into the hub runtime.
Both live under v<major>.<minor> so patch A/B deployments do not duplicate dependency environments. A minor or major bucket gives the skill a fresh dependency boundary when the migration model says compatibility changed.
Runtime lifecycle hooks
AdaOS now supports optional lifecycle hooks in the resolved skill manifest.
Preferred declaration shape:
lifecycle:
persist_before_switch: persist_state
after_activate: after_activate
rehydrate: rehydrate
drain: drain
dispose: dispose
before_deactivate: before_deactivate
The hook names resolve through the ordinary skill tools table. Data migration itself should use the reserved migrations/data_migration.py file for new skills.
Current behavior:
persist_before_switchruns against the currently active slot before pointer cutover when an active prepared runtime existsafter_activateruns after the new slot becomes activerehydrateruns after activation to rebuild derived runtime statedrainruns before rollback/deactivate or activation-failure cleanup when declareddisposeruns afterdrainand beforebefore_deactivatewhen declaredbefore_deactivateruns before explicit deactivate or rollback of the current slot- global runtime drain now reuses the same contract:
subnet.drainingtriggers active-skilldrain, andsubnet.stoppingtriggersdisposethenbefore_deactivateas best-effort shutdown hooks for active installed runtimes
Lifecycle diagnostics are persisted into slot metadata and surfaced by adaos skill status --json through runtime_status().lifecycle.
If activation already switched to a new version/slot and rehydrate then fails, AdaOS now attempts to restore:
- the previous active version marker
- the previous active slot selection
- the previous runtime bucket data, by restoring the previous active version/slot marker
- the previous deactivation state
The failed target slot keeps its lifecycle diagnostics so operators can inspect the failed rehydrate, shutdown hooks, and rollback result.
Post-commit migration checks now also consume these lifecycle diagnostics.
That means a skill may be marked failed or selectively deactivated because rehydrate / healthcheck is already unhealthy even before any explicit post-commit test suite runs.
Operator-facing migration reports also surface lifecycle failures separately from test failures, so a lifecycle/rehydrate failure is visible as a first-class shutdown/migration issue rather than only as a generic failed skill.
This same metadata is also written into the deactivation marker when a skill is selectively quarantined after a committed core switch.
Supervisor-facing validation status and operator projections now also surface a compact quarantine summary, so post-commit status can show which skill was quarantined and at which lifecycle/test stage.
Tool execution and setup
adaos skill run <name> [<tool>] reads the active slot’s resolved.manifest.json, adds the staged source directory to sys.path, and executes the tool callable with per-invocation timeouts. adaos skill test <name> reuses the same active slot to execute src/skills/<name>/tests without preparing a new build. If a skill declares a setup tool it is available via adaos skill setup <name> only after activation; attempting to run setup while the version is pending reports a clear error instructing the operator to activate first.
Secrets management
Secrets are stored under skills/.runtime/<name>/v<major>.<minor>/data/files/secrets.json and are never copied into the source tree. Runtime execution injects secrets at process start and keeps placeholders (${secret:NAME}) inside resolved.manifest.json.
Use the CLI to manage secrets either globally or per skill:
adaos secrets set WEATHER_API_KEY <value> --skill weather_skill
adaos secrets list # lists all skills with stored secrets
adaos secrets export > backup.json # exports secrets grouped by skill (values redacted)
adaos secrets import backup.json # restores secrets into installed skills
adaos secrets list --skill weather_skill
adaos secrets export --skill weather_skill --show
adaos secrets import dump.json --skill weather_skill
adaos skill setup weather_skill is a thin wrapper around the skill-defined setup tool that typically requests credentials and persists them via the per-skill secrets backend.
Observability
Every install/test/activate/run operation logs under slots/<slot>/logs/. adaos skill status --json surfaces runtime state (active version/slot/readiness/tests). For progress checks:
- Workspace:
adaos skill status <NAME> --fetch --diffcomparesskills/<NAME>against the workspace registry remote (adaos-registry.gitmain). - Dev:
adaos skill status --space dev <NAME> --fetch --diffcompares the dev folder against the hub draft state via Root API (requires hub mTLS keys from the bootstrapnode.yaml).
Workspace markers are split by state plane. git-dirty means local filesystem
changes are not committed, while git-ahead means path-level commits exist
locally and still need a registry push. git-behind means the registry base has
newer path-level commits, and git-different is the fallback when the path
differs but Git cannot classify the divergence. git-error means the CLI could
not compute the Git comparison. Runtime markers are separate: runtime-ahead
means the workspace skill version is ahead of the active runtime slot,
runtime-behind means the active slot is newer than the workspace source, and
runtime-different means the versions differ but cannot be ordered.
adaos skill push without a skill name is a batch release command, not a raw
Git transport command. It finds skills with dirty files, committed git-ahead
changes, or an explicit manifest-vs-registry version drift, then runs the same
version bump, registry update, commit, and push flow used by
adaos skill push <name> -m .... Batch release commits use the standard message
chore(<skill>): release workspace changes.
Weather skill reference
.adaos/skills/weather_skill/ demonstrates the complete lifecycle:
- Install the reference skill with tests:
adaos skill install weather_skill --test. - Activate the freshly prepared slot:
adaos skill activate weather_skill. - Run setup to capture the API key via secrets:
adaos skill setup weather_skill. - Execute the default tool:
adaos skill run weather_skill --json '{"city": "Paris"}'.
The repository contains smoke and contract tests under src/skills/<name>/tests/ and an optional health probe that can be used by the platform for readiness checks.