Removing a live Agent Spec is not retirement: reconciliation iterates discovered
specs, so deletion can leave its PTY/exec tasks alive with no declaration left
to tear them down.
The correct source protocol is already retired #true, followed by
reconciliation. The remaining gap for an external publisher is a safe,
machine-readable transition from a fully retired declaration to removal of only
its executable desired-state file.
Scenario
A policy-aware publisher needs to decommission one dynamic seat while
preserving its native history:
active agent.kdl
-> CAS-publish same declaration with retired #true
-> st2 stops and collects every declared task
-> prove retirement complete
-> remove only agent.kdl
-> retain status and resources/
Implementing the last two steps in a Home Manager shell hook or downstream
controller is not authoritative:
- st2 owns both PTY and exec task backends;
- a downstream probe can accidentally inspect only one backend;
- unreadable runtime state must be
unknown, never absent;
- a stale remover can race a newer declaration generation; and
- history and executable declaration inputs have different retention rules.
Open issue #35 currently shows why the runtime fact must fail closed:
ExecBackend::list can collapse unreadable state to an empty set. That issue is
a prerequisite for treating any retirement-complete result as authoritative.
Proposed primitive
st2 agent unpublish <host.identity> \
--catalog <root> \
--expect-sha256 <retired-spec-digest> \
--require-retirement-complete \
--json
Suggested contract:
- Acquire the same writer lock used by Agent Spec publication.
- CAS the exact current declaration bytes.
- Parse and require
retired #true.
- Derive all task IDs from that declaration.
- Query every configured runtime backend.
- Fail closed on any inspection error or any live/dead retained task record.
- Atomically unlink only canonical
agent.kdl.
- Preserve
status, resources/, and unrelated per-seat history.
- Return stable
removed, unchanged, generation-conflict, or
retirement-pending status.
Because the declaration remains retired throughout the absence check, the
normal supervisor cannot respawn its tasks between proof and unlink.
Possible scope
This can be either:
- a companion to a low-level CAS
agent publish primitive; or
- a higher-level
agent retire --wait followed by guarded agent unpublish.
The source of truth should remain the canonical Agent Spec. st2 does not need a
second finalizer object or SeatIntent state machine.
Identity-reuse epochs/tombstones can remain downstream initially. If stale
create-after-delete becomes a demonstrated cross-client problem, a generic
incarnation guard could be proposed separately.
Related
- #35 must fail closed
before retirement completion is authoritative.
- #29 would provide the
bounded reconcile/inspection path.
- #18 originally tracked
retired-agent doctor behavior; current main has moved toward “retired means
every declared task absent”.
Version
- st2
0.1.0+a77776a
- source
a77776ac553af9b9b04149f2bc9f6c269a008b83
- Linux
Posted on behalf of @schickling
| field |
value |
agent_name |
co2-pine |
agent_session_id |
cdd15622-c01a-4731-bbfe-ab8cce0cad54 |
agent_tool |
Codex CLI |
agent_tool_version |
0.145.0 |
agent_runtime |
Codex CLI 0.145.0 |
agent_model |
unknown |
runtime_profile |
/nix/store/ph8rlhdj25mg71v81jsfzy6dq4xpcs9m-coding-agent-runtime-profile/share/coding-agents/profile.json |
skills_manifest |
/nix/store/lsykz8x5481xrpbgk280xh3pypk1c5jy-agent-skills-corpus/share/agent-skills/manifest.json |
worktree |
tmp |
machine |
dev3 |
tooling_profile |
dotfiles@3649b53 |
Removing a live Agent Spec is not retirement: reconciliation iterates discovered
specs, so deletion can leave its PTY/exec tasks alive with no declaration left
to tear them down.
The correct source protocol is already
retired #true, followed byreconciliation. The remaining gap for an external publisher is a safe,
machine-readable transition from a fully retired declaration to removal of only
its executable desired-state file.
Scenario
A policy-aware publisher needs to decommission one dynamic seat while
preserving its native history:
Implementing the last two steps in a Home Manager shell hook or downstream
controller is not authoritative:
unknown, neverabsent;Open issue #35 currently shows why the runtime fact must fail closed:
ExecBackend::listcan collapse unreadable state to an empty set. That issue isa prerequisite for treating any retirement-complete result as authoritative.
Proposed primitive
Suggested contract:
retired #true.agent.kdl.status,resources/, and unrelated per-seat history.removed,unchanged,generation-conflict, orretirement-pendingstatus.Because the declaration remains retired throughout the absence check, the
normal supervisor cannot respawn its tasks between proof and unlink.
Possible scope
This can be either:
agent publishprimitive; oragent retire --waitfollowed by guardedagent unpublish.The source of truth should remain the canonical Agent Spec. st2 does not need a
second finalizer object or SeatIntent state machine.
Identity-reuse epochs/tombstones can remain downstream initially. If stale
create-after-delete becomes a demonstrated cross-client problem, a generic
incarnation guard could be proposed separately.
Related
before retirement completion is authoritative.
bounded reconcile/inspection path.
retired-agent doctor behavior; current main has moved toward “retired means
every declared task absent”.
Version
0.1.0+a77776aa77776ac553af9b9b04149f2bc9f6c269a008b83Posted on behalf of @schickling
agent_nameagent_session_idagent_toolagent_tool_versionagent_runtimeagent_modelruntime_profileskills_manifestworktreemachinetooling_profile