Skip to content

Commit a1c5fc2

Browse files
committed
docs(vrs): add explicit moved intent
1 parent 6d5b078 commit a1c5fc2

2 files changed

Lines changed: 60 additions & 3 deletions

File tree

docs/vrs/02-agent-spec/requirements.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,20 @@ wrong.
113113
invalid input retains last-known-good ownership and never authorizes removal.
114114
Status and true no-write dry-run surfaces quietly report affected task IDs,
115115
action/refusal class, proof scope, drift state, and reasons before mutation.
116+
- **SPEC-R11 Make moved intent explicit and optional:** Ordinary agent identity,
117+
task name, task ID, or host edits remain retire/remove-old plus add-new; st2
118+
never infers a rename. A future catalog-native `moved` mapping may explicitly
119+
relate one fully qualified old address to one fully qualified new address as
120+
migration intent, not an alias, hidden history, or global identity authority.
121+
Preflight proves exact old catalog/host/owner/task/incarnation ownership, a
122+
one-to-one acyclic mapping, and no conflicting live or desired destination.
123+
The same live incarnation is preserved only when the backend can atomically
124+
re-address it without changing any process-visible identity or launch field;
125+
otherwise the mapping guides explicit scoped replacement or staged host
126+
migration. Cross-host execution is local-first and holds until old retirement
127+
is proven, without synchronous all-host availability, CAS, or an external
128+
registry. Quiet status and true dry-run report `pending`, `refused`, or
129+
`completed`; removing the mapping before completion fails closed.
116130

117131
## Evidence boundary
118132

docs/vrs/02-agent-spec/spec.md

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ normalized effects also remain equal.
5252
| Task `kind`, `command`/`argv`, resolved effective `cwd`, agent/task `env`, task `tags`, synthesized managed environment including supervisor routing | [normalized task fields](../../../crates/agent-spec/src/spec.rs), [effective task target](../../../src/reconcile.rs), [spawn construction](../../../src/run.rs) | Change the desired launch/backend fingerprint. Dead/absent launches use the latest desired definition. A healthy older or unproven incarnation remains alive and reports `drifted` or `unknown` until an explicit task-scoped replacement. A source-path edit that changes effective cwd also belongs here. | Gap tracked by [#40](https://github.com/compoundingtech/st2/issues/40): current reconciliation adopts any healthy matching task ID without a desired/observed fingerprint or incarnation binding. `run.rs` currently passes task tags as backend spawn arguments, so they cannot be classified as pure metadata. |
5353
| `keep`, `restart`, task `lifecycle` | [policy fields/defaults](../../../crates/agent-spec/src/spec.rs), [policy reconcile](../../../src/reconcile.rs), [restart cap](../../../src/flapping.rs) | Apply on the next relevant liveness/reconciliation decision. Do not replace a healthy process merely because policy changed. Unknown policy values make the snapshot invalid rather than silently selecting a default. | `keep` and restart policy are prospective. `adopt-only` holds dead/absent tasks; returning to `service` authorizes ordinary replacement. Healthy matching IDs remain adopted. Unknown lifecycle values fail parsing, but malformed restart duration/mode values currently fall back to defaults. |
5454
| `retired #true` | [retired lowering](../../../crates/agent-spec/src/spec.rs), [retired plan](../../../src/reconcile.rs), [plan execution](../../../src/run.rs) | Explicitly tear down only the declaration's exact live tasks, clean eligible dead state, and never relaunch. If the same edit also removes a child, retirement of the remaining declared IDs is not proof about the now-invisible child; that child still requires exact host-local removal attribution. | Implemented for still-declared tasks. Safe removal of the retired declaration after completion remains [#42](https://github.com/compoundingtech/st2/issues/42); simultaneous child removal is not currently detected. |
55-
| Agent `identity` | [identity/host resolution](../../../crates/agent-spec/src/discovery.rs), [bus/task IDs](../../../crates/agent-spec/src/spec.rs) | Treat as retire-old/add-new, never an in-place rename. Complete old retirement before removing the old declaration or activating a colliding new identity. | A staged old declaration with `retired #true` is supported. Editing identity in place does not retire the old runtime; the new ID can launch while old state remains. Historical naming is separately tracked by [#21](https://github.com/compoundingtech/st2/issues/21) and [#89](https://github.com/compoundingtech/st2/issues/89). |
56-
| Task `name` or explicit `id` | [task lowering](../../../crates/agent-spec/src/spec.rs), [runtime ID resolution](../../../src/reconcile.rs) | Treat as remove-old/add-new after complete valid-owner and runtime-attribution proof; never infer that differently addressed tasks are the same incarnation. | Adding the new ID works; exact old-child teardown has the same missing ownership/incarnation-attribution gap as task removal. |
57-
| Agent `host` | [placement resolution](../../../crates/agent-spec/src/discovery.rs), [host filter](../../../src/reconcile.rs) | Treat as an explicit cross-host migration: retire/verify old placement, then add/activate new placement. Each host acts from a complete local snapshot without synchronous coordination. | A direct edit makes the old host stop seeing the declaration and lets the new host launch; it does not prove old teardown. Exact staged publication/acknowledgement remains unresolved. |
55+
| Agent `identity` | [identity/host resolution](../../../crates/agent-spec/src/discovery.rs), [bus/task IDs](../../../crates/agent-spec/src/spec.rs) | Treat an ordinary edit as retire-old/add-new, never an inferred in-place rename. Complete old retirement before removing the old declaration or activating a colliding new identity. An optional explicit moved mapping may guide the migration under the stricter contract below. | A staged old declaration with `retired #true` is supported. Editing identity in place does not retire the old runtime; the new ID can launch while old state remains. Historical naming is separately tracked by [#21](https://github.com/compoundingtech/st2/issues/21) and [#89](https://github.com/compoundingtech/st2/issues/89). Moved mappings are not implemented. |
56+
| Task `name` or explicit `id` | [task lowering](../../../crates/agent-spec/src/spec.rs), [runtime ID resolution](../../../src/reconcile.rs) | Treat an ordinary edit as remove-old/add-new after complete valid-owner and runtime-attribution proof; never infer that differently addressed tasks are the same incarnation. An optional explicit moved mapping may guide exact re-address or replacement. | Adding the new ID works; exact old-child teardown has the same missing ownership/incarnation-attribution gap as task removal. Moved mappings are not implemented. |
57+
| Agent `host` | [placement resolution](../../../crates/agent-spec/src/discovery.rs), [host filter](../../../src/reconcile.rs) | Treat an ordinary edit as an explicit cross-host migration: retire/verify old placement, then add/activate new placement. A moved mapping may express intent but remains local-first and held until old retirement is proven. Each host acts from a complete local snapshot without synchronous coordination. | A direct edit makes the old host stop seeing the declaration and lets the new host launch; it does not prove old teardown. Exact staged publication/acknowledgement and moved mappings are not implemented. |
5858
| Agent `workspace` | [workspace/cwd model](../../../crates/agent-spec/src/spec.rs), [render target resolution](../../../src/materialize.rs), [spawn construction](../../../src/run.rs) | Ordinarily classify the edit as launch drift when it changes effective cwd, plus any independently resolved render or Resource delta. Preserve a healthy process and require explicit replacement for launch drift; do not infer a state-root move. | Current ID-only adoption hides the launch drift while materialization may target the newly resolved workspace. `adopt-only` ([#98](https://github.com/compoundingtech/st2/issues/98)) can fence replacement during a cutover but is not a mover. |
5959
| Catalog root; effective `PTY_ROOT`; another explicitly selected sensitive root | [catalog root selection](../../../src/catalog.rs), [effective PTY root](../../../src/run.rs) | Treat as a guided state-bearing migration: freeze the affected scope, prove old-location quiescence, move/update state, and resume exactly once. | Guided catalog/PTY-root migration is specified but unimplemented in [#85](https://github.com/compoundingtech/st2/issues/85). Ordinary workspace edits are outside this boundary. |
6060
| Invalid, partial, ambiguous, conflicting, or unreadable desired/actual state | [discovery diagnostics](../../../crates/agent-spec/src/discovery.rs), [validation](../../../src/validate.rs), [materialization failures](../../../src/materialize.rs), [runtime-list failure](../../../src/run.rs) | Block the smallest owner/render-dependency set whose completeness cannot be proved; broaden only when attribution/isolation is unprovable. Invalid remote-host input does not block valid isolated local work. Retain last-known-good ownership and never infer removal from partial/invalid input. Report affected identities, refusal, and proof scope. | Runtime-list failure skips the affected pass and render conflicts fail affected owners before writes. Discovery collects errors while continuing with valid specs, but no explicit last-known-good/removal-attribution layer yet proves safe destructive deltas. |
@@ -79,6 +79,47 @@ incarnation; a stale or absent binding is `unknown`, never converged.
7979
| retired | healthy or dead | retirement | teardown/clean declared IDs; never relaunch |
8080
| invalid, partial, ambiguous, conflicting, unreadable | any | refusal | no destructive action in the unproven scope; valid isolated scopes may proceed |
8181

82+
## Optional explicit moved intent
83+
84+
The default remains retire/remove-old plus add-new. st2 never guesses that two
85+
addresses are a rename. A future catalog-native mapping may make that intent
86+
explicit without requiring runtime support in this draft. Its exact syntax is
87+
provisional; an illustrative KDL shape is:
88+
89+
```kdl
90+
moved from="Silber.old.agent" to="Silber.new.agent"
91+
```
92+
93+
Both values are one fully qualified task address. The mapping is:
94+
95+
- migration intent only, never a runtime alias, hidden history record, or
96+
global identity authority;
97+
- one-to-one and acyclic across the complete valid affected mapping set;
98+
- accepted only after preflight proves the exact old catalog, host, owner, task
99+
ID, and current runtime incarnation, plus no conflicting live incarnation or
100+
competing desired owner at the destination; and
101+
- retained until status reports completion; removing it while `pending` or
102+
`refused` fails closed and preserves the old last-known-good ownership.
103+
104+
Preserving the live process is legal only when the selected backend can
105+
atomically re-address the exact same incarnation and that change leaves every
106+
process-visible identity and launch field unchanged. An agent identity,
107+
`ST_AGENT`, supervisor/environment, host, effective cwd/workspace, command,
108+
argv, or other launch/backend-fingerprint change cannot be relabeled as an
109+
in-place move. In those cases `moved` guides an explicit task-scoped replacement
110+
or staged host migration instead.
111+
112+
Cross-host intent remains local-first: the destination holds until the old host
113+
has proven retirement, and each host can make its local decision from a
114+
complete local catalog and runtime proof. The contract requires no synchronous
115+
all-host availability, CAS, or external registry. Quiet status and true dry-run
116+
expose the affected addresses, proof scope, and `pending`, `refused`, or
117+
`completed` state.
118+
119+
Core st2 does not currently parse or execute this mapping. The syntax above
120+
must not be treated as supported catalog input until a later runtime change and
121+
paired acceptance make it normative.
122+
82123
## Reporting and dry-run
83124

84125
Before mutation, quiet status and true dry-run must expose, per affected task or
@@ -125,6 +166,8 @@ machine-readable fingerprint state. A true no-write plan is tracked by
125166
uses proven remove-old/add-new. Workspace changes produce launch drift plus
126167
independently resolved render changes. Guided state migration is reserved
127168
for catalog root, PTY root, or another explicitly selected sensitive root.
169+
A future optional `moved` mapping may explicitly guide rename/re-address
170+
intent under SPEC-R11, but ordinary edits never infer it.
128171

129172
After these decisions and merge, external acceptance should execute a
130173
table-driven field matrix against both PTY and exec tasks, including compact

0 commit comments

Comments
 (0)