|
1 | | -# Agent Spec field-change requirements |
| 1 | +# st2 Agent Spec field-change conformance requirements |
2 | 2 |
|
3 | | -## Technical terms |
| 3 | +## Context |
4 | 4 |
|
5 | | -- A **VRS node** is one part of the st2 requirements and specification. |
6 | | -- A **normalized Agent Spec** is the result after st2 parses a declaration and |
7 | | - applies default values. |
8 | | -- A **core field** is an Agent Spec field that core st2 uses. |
9 | | -- **Lower** means change a field into one or more core fields. |
10 | | -- A **semantic projection** is the set of effects from a normalized Agent Spec. |
11 | | -- A **render plan** lists the final files and bytes that st2 must write. |
12 | | -- A **catalog** is the set of Agent Spec declarations that st2 reads. |
13 | | -- **Runtime state** contains the local processes, files, and ownership records. |
14 | | -- A **local component** is a group of agents that share an owner, a rendered |
15 | | - file, or another conflict. |
16 | | -- An **incarnation** is one live generation of a task. |
17 | | -- **Attribution** is proof that binds an object to its exact catalog, host, |
18 | | - owner, task ID, and incarnation. |
19 | | -- A **launch fingerprint** identifies all inputs that start one version of a |
20 | | - task. |
21 | | -- A **host projection** is the set of declarations that one host selects. |
22 | | -- **Catalog skew** occurs when two hosts have different catalog versions. |
23 | | -- A **host migration** is a coordinated move of a process between hosts. |
24 | | -- **Last-known-good state (LKG)** is the latest local state for which st2 proved |
25 | | - ownership. |
26 | | -- A **survivor** is a healthy incarnation that st2 does not replace. |
27 | | -- A **fence** prevents st2 from starting an ID while st2 changes that ID. |
28 | | -- **Quiesce** means stop an incarnation and release its ports, locks, and |
29 | | - ownership. |
30 | | -- A **rollback** restores the state that existed before a failed change. |
31 | | -- A **snapshot** is the complete catalog and runtime input for one st2 pass. |
32 | | -- A **Resource** is a typed binding in an Agent Spec declaration. |
33 | | -- A **provider** changes provider fields into concrete core fields. |
34 | | -- A **materializer** is the st2 component that writes desired files. |
35 | | -- A **sidecar** is a support task that st2 creates from compact syntax. |
36 | | -- A **parser** reads a catalog declaration and creates a normalized Agent Spec. |
37 | | -- A **watcher** detects a catalog file change. |
38 | | -- A **reconcile pass** compares desired state with actual state and then applies |
39 | | - allowed changes. |
40 | | -- A **reconciler** runs a reconcile pass. |
41 | | -- An **executor** applies planned actions. |
42 | | -- **Replacement authority** is explicit permission to replace one exact |
43 | | - incarnation. |
44 | | -- A **destructive action** removes a file or stops, retires, or replaces work. |
45 | | -- **DING** is a best-effort delivery attempt. |
46 | | - It tells an agent to read its inbox. |
47 | | - st2 does not guarantee delivery. |
48 | | - A configured adapter can use terminal input or a post-turn hook. |
49 | | - This contract does not require terminal input or harness-specific behavior. |
50 | | -- A **PTY task** is an interactive terminal task. |
51 | | -- An **exec task** is a non-interactive task. |
52 | | -- The **CLI** is the st2 command-line interface. |
53 | | -- **KDL** is one catalog declaration format. |
54 | | -- An **anchor** is the declaration folder that owns state or Resources. |
55 | | -- An **idempotency ID** identifies one event. A repeated event with this ID has |
56 | | - no additional effect. |
57 | | -- **Moved intent** is a possible future map from one old local address to one |
58 | | - new local address. |
59 | | -- An **address** identifies one Agent Spec object without ambiguity. |
60 | | -- A **no-op** reports that st2 found no normalized change. |
61 | | -- A **hold** waits and makes no requested lifecycle change. |
62 | | -- A **refusal** makes no change because validation or proof failed. |
63 | | -- **Drifted** reports that a launch fingerprint differs. |
64 | | -- **Unknown** reports that st2 cannot prove actual state. |
65 | | -- **Materialize** means write the final desired files. |
66 | | -- **Compare-and-swap (CAS)** is a write rule. The write succeeds only when the |
67 | | - current value equals an expected value. |
| 5 | +The canonical Agent Spec and its proof corpus live in |
| 6 | +[compoundingtech/evals at commit `b3cd5fbd98c11179a4555d0f9bbccfe98351a734`](https://github.com/compoundingtech/evals/blob/b3cd5fbd98c11179a4555d0f9bbccfe98351a734/AGENT-SPEC.md). |
| 7 | +st2 is one implementation. Another implementation, including a future st3, can |
| 8 | +target the same contract and proofs. |
68 | 9 |
|
69 | | -## Context |
| 10 | +This sub-VRS defines desired st2 conformance when a normalized Agent Spec |
| 11 | +changes work on one host. It records st2-specific gaps; it does not own or copy |
| 12 | +the Agent Spec. General field-change behavior that the canonical specification |
| 13 | +does not yet define is proposed until a matching evals specification and proof |
| 14 | +change adopts it. |
| 15 | + |
| 16 | +This work supports root [R01, R06, R11, and R13 through R19](../requirements.md). |
| 17 | +It applies to every st2 tool that runs an agent or test. A valid local catalog |
| 18 | +and host-local runtime state are sufficient. It requires no compare-and-swap |
| 19 | +(CAS), lock service, cross-host call, or external registry. |
| 20 | + |
| 21 | +[spec.md](./spec.md) defines the proposed field-change rules and current st2 |
| 22 | +implementation gaps. The root st2 VRS has authority for st2 behavior; the |
| 23 | +canonical evals Agent Spec remains the authoring authority. |
70 | 24 |
|
71 | | -This VRS node defines how normalized Agent Spec changes affect work on one |
72 | | -host. |
73 | | -It supports root |
74 | | -[R01, R06, R11, and R13 through R19](../requirements.md). |
75 | | -The same contract applies to all tools that run an agent or test. |
76 | | -A valid local catalog and host-local runtime state provide all required input. |
77 | | -The contract does not require CAS, a lock service, a cross-host call, or an |
78 | | -external registry. |
79 | | -[spec.md](./spec.md) lists all field rules and current gaps. |
80 | | -If this node conflicts with the root VRS, the root VRS has authority. |
| 25 | +Field lookup: [F01](./spec.md#f01), [F02](./spec.md#f02), |
| 26 | +[F03](./spec.md#f03), [F04](./spec.md#f04), [F05](./spec.md#f05), |
| 27 | +[F06](./spec.md#f06), [F07](./spec.md#f07), [F08](./spec.md#f08), |
| 28 | +[F09](./spec.md#f09), [F10](./spec.md#f10), [F11](./spec.md#f11), |
| 29 | +[F12](./spec.md#f12), [F13](./spec.md#f13), [F14](./spec.md#f14), |
| 30 | +[F15](./spec.md#f15), and [F16](./spec.md#f16). |
81 | 31 |
|
82 | | -## Normative requirements |
| 32 | +## Shared invariants |
83 | 33 |
|
84 | | -- **SPEC-R01 Normalize and classify before an action.** st2 must compare |
85 | | - normalized effects before it acts. |
86 | | - - st2 must compare the normalized semantic projection. |
87 | | - - st2 must compare the render plan and all resolved addresses and paths. |
88 | | - - st2 must compare the task set and each launch fingerprint. |
89 | | - - st2 must compare policy, retirement state, and host membership. |
90 | | - - st2 must not use source bytes as the only comparison. |
91 | | - - st2 can report `no-op` for format, order, comment, or source-path changes |
92 | | - only when all normalized effects match. |
93 | | - - A source `no-op` must not authorize a write. |
94 | | - - A source `no-op` must not authorize a notification. |
95 | | - - A source `no-op` must not prevent st2 from healing independently absent or |
96 | | - dead work. |
97 | | - - Provider fields do not create a core change until a provider lowers them |
98 | | - into core fields. |
| 34 | +- **SPEC-R01 Normalize and classify before action.** Compare the normalized |
| 35 | + effects, not source bytes alone. The comparison includes the render plan, |
| 36 | + resolved addresses and paths, task set, launch fingerprints, policy, |
| 37 | + retirement state, and host membership. Formatting, order, comments, or a |
| 38 | + source path change are `no-op` only when every normalized effect is equal. |
| 39 | + A source-only `no-op` authorizes no write or notification. It does not block |
| 40 | + healing of independently absent or dead work. A provider field has no core |
| 41 | + effect until a provider lowers it into core fields. |
99 | 42 |
|
100 | | -- **SPEC-R02 Stop changes when local proof is incomplete.** st2 must divide |
101 | | - local work into local components. |
102 | | - - Before st2 changes a component, it must validate all input for that |
103 | | - component. |
104 | | - - st2 must prove the current owner of each affected process and file. |
105 | | - - If st2 cannot complete this proof, it must not change that component. |
106 | | - - st2 can change an independent component when proof for that component is |
107 | | - complete. |
108 | | - - If input is partial or unreadable, st2 must keep LKG ownership. |
109 | | - - If input is ambiguous or conflicting, st2 must keep LKG ownership. |
110 | | - - Incomplete input must not prove that st2 can remove an ID. |
111 | | - - Removal proof must bind the catalog, host, owner, task ID, and current |
112 | | - incarnation. |
113 | | - - st2 must put work from an earlier version in `hold` when it has no |
114 | | - attribution. |
115 | | - - st2 must put other unattributed work in `hold`. |
116 | | - - st2 must not require a previous synchronized snapshot. |
117 | | - - st2 must not require a deletion record. |
118 | | - - st2 must not require CAS. |
| 43 | +- **SPEC-R02 Fail closed at the smallest unproven local scope.** Before a |
| 44 | + change, validate the complete local owner, render dependency, and conflict |
| 45 | + component. Prove the current owner of each affected process and file. Partial, |
| 46 | + unreadable, ambiguous, or conflicting input retains the latest locally proved |
| 47 | + ownership. It never proves removal. Destructive action requires proof that |
| 48 | + binds the exact catalog, host, owner, task ID, and live incarnation (one task |
| 49 | + generation). Work without this proof stays in `hold`. Independent proved |
| 50 | + components can proceed. No prior synchronized snapshot, deletion record, or |
| 51 | + CAS is required. |
119 | 52 |
|
120 | | -- **SPEC-R03 Preserve live work unless an explicit action changes it.** A role |
121 | | - change must not change a healthy task. |
122 | | - - A future `keep`, restart, or `lifecycle` policy must not change a healthy |
123 | | - task. |
124 | | - - The agent `workspace` field supplies live context. |
125 | | - - If a workspace change is visible to a survivor, st2 must notify that |
126 | | - survivor. |
127 | | - - If an incarnation is absent or dead, st2 must boot it with the latest |
128 | | - workspace. |
129 | | - - A boot for an absent or dead incarnation must not send this notification. |
130 | | - - st2 must write changed render or Resource state only when the desired bytes |
131 | | - differ. |
132 | | - - If a changed render or Resource is visible to a survivor, st2 must notify |
133 | | - that survivor. |
134 | | - - F11 spawn inputs must form the launch fingerprint. |
135 | | - - If a healthy launch fingerprint differs, st2 must report `drifted` or |
136 | | - `unknown`. |
137 | | - - A fingerprint difference must not authorize replacement. |
138 | | - - Unrelated work must receive no action. |
| 53 | +- **SPEC-R03 Preserve live work unless the field rule changes it.** Metadata, |
| 54 | + live context, render data, Resource data, and future policy do not restart a |
| 55 | + healthy task. A changed workspace, render target, or Resource that is visible |
| 56 | + to a surviving task causes one post-commit notification. Absent or dead work |
| 57 | + boots with the latest desired data and gets no change notification. All spawn |
| 58 | + inputs form a versioned launch fingerprint. A healthy fingerprint mismatch is |
| 59 | + visible as `drifted` or `unknown`, but it does not authorize replacement. |
| 60 | + Unrelated work receives no action. |
139 | 61 |
|
140 | | -- **SPEC-R04 Change membership and lifecycle only for exact IDs.** st2 must add |
141 | | - only missing IDs. |
142 | | - - st2 must remove only old IDs with exact attribution. |
143 | | - - Retirement must stop the declared set. |
144 | | - - Retirement must prevent a new launch. |
145 | | - - An identity change must remove the old ID. |
146 | | - - An identity change must add the new ID. |
147 | | - - A task name or task ID change must remove the old ID. |
148 | | - - A task name or task ID change must add the new ID. |
149 | | - - st2 must not infer a rename from these changes. |
150 | | - - Moved intent is provisional. |
151 | | - - st2 does not support moved intent. |
152 | | - - Moved intent is limited to one host. |
153 | | - - The agent `host` field defines host projection membership. |
154 | | - - A `host` change is not a migration. |
155 | | - - The old host must remove its local member after a complete |
156 | | - present-to-absent change. |
157 | | - - The new host must add its local member after a complete |
158 | | - absent-to-present change. |
159 | | - - The two hosts must not require a shared transition, order, receipt, or |
160 | | - proof. |
161 | | - - Catalog skew can cause a temporary overlap or absence. |
162 | | - - During catalog skew, each host must use its local LKG state. |
| 62 | +- **SPEC-R04 Change membership and lifecycle only for exact IDs.** Add only a |
| 63 | + missing ID. Remove only an exactly attributed old ID. Retirement stops the |
| 64 | + declared set and prevents relaunch. An identity, task name, or task ID change |
| 65 | + is remove-old then add-new, not an inferred rename. Each host acts only on its |
| 66 | + local projection. A host change is independent removal on the old host and |
| 67 | + addition on the new host, never process migration. The hosts require no shared |
| 68 | + order, receipt, or proof. Catalog skew can cause temporary overlap or absence; |
| 69 | + each host retains its local last-known-good ownership. |
163 | 70 |
|
164 | | -- **SPEC-R05 Plan and run each local component in phases.** st2 must plan all |
165 | | - actions and refusals before it changes a component. |
166 | | - - The plan must include all proof, conflicts, and rollback conditions. |
167 | | - - st2 must use this order: `FENCE`, `REMOVE/QUIESCE`, `MATERIALIZE`, |
168 | | - `ADD/BOOT`, `NOTIFY`, and `VERIFY/REPORT`. |
169 | | - - st2 must omit an empty phase. |
170 | | - - If an add conflicts with an old incarnation, st2 must first prove that the |
171 | | - old incarnation is quiescent. |
172 | | - - A conflicting add must use the final desired bytes. |
173 | | - - A deletion must have explicit desired state. |
174 | | - - A deletion must have ownership proof. |
175 | | - - A deletion must not remove a source declaration from the catalog. |
176 | | - - If st2 proves a rollback, it can use that rollback after a failure. |
177 | | - - If st2 cannot prove a rollback, it must `hold` or `refuse` before a |
178 | | - dependent phase. |
179 | | - - An independent component must not wait for another component. |
180 | | - - A `drifted` incarnation needs explicit replacement authority. |
| 71 | +- **SPEC-R05 Plan and execute each local component in phases.** Plan every |
| 72 | + action, refusal, proof, conflict, and rollback condition before mutation. Use |
| 73 | + `FENCE`, `REMOVE/QUIESCE`, `MATERIALIZE`, `ADD/BOOT`, `NOTIFY`, then |
| 74 | + `VERIFY/REPORT`, and omit empty phases. Fence and remove an exact conflicting |
| 75 | + old incarnation before an add. Use final desired bytes for the add. A deletion |
| 76 | + requires explicit desired state and ownership proof, and never removes a |
| 77 | + catalog source declaration. Roll back only when rollback is proved; otherwise |
| 78 | + hold or refuse before a dependent phase. Explicit replacement authority is |
| 79 | + required for a drifted live incarnation. |
181 | 80 |
|
182 | | -- **SPEC-R06 Send one quiet event after a commit.** This rule applies when a |
183 | | - committed workspace, render, or Resource change is visible to a survivor. |
184 | | - - st2 must write one durable inbox event. |
185 | | - - st2 must combine related changes into this one event. |
186 | | - - After st2 writes the event, it must try DING. |
187 | | - - The event must contain a stable idempotency ID. |
188 | | - - The event must contain the agent, host, changed path, and change class. |
189 | | - - A workspace event can contain the old path and the new path. |
190 | | - - The event must not contain file contents or secrets. |
191 | | - - A DING failure must not remove the event. |
192 | | - - A DING failure must not roll back the commit. |
193 | | - - st2 must not send an event for `no-op` or unchanged work. |
194 | | - - st2 must not send an event for a failed or rolled-back change. |
195 | | - - st2 must not send an event for a periodic pass. |
196 | | - - st2 must not send an event for new, replaced, or retired work. |
197 | | - - A notification must not cause a restart. |
198 | | - - A notification must not cause another notification. |
| 81 | +- **SPEC-R06 Send one quiet event after commit.** When a committed workspace, |
| 82 | + render, or Resource change is visible to a survivor, write one coalesced |
| 83 | + durable inbox event and then try DING. DING is best-effort delivery through the |
| 84 | + configured adapter; this contract requires no terminal input or harness |
| 85 | + behavior. The event has a stable ID, and repeating that ID has no additional |
| 86 | + effect. It identifies the agent, host, changed path, and change class, but |
| 87 | + contains no file contents or secrets. Delivery failure does not remove the |
| 88 | + event or roll back the commit. Do not notify for unchanged, failed, |
| 89 | + rolled-back, periodic, new, replaced, or retired work. A notification cannot |
| 90 | + restart work or cause another notification. |
199 | 91 |
|
200 | 92 | ## Evidence boundary |
201 | 93 |
|
202 | | -After approval, an external test matrix must test every |
203 | | -[field rule and acceptance case](./spec.md). |
204 | | -The matrix must use public CLI behavior and isolated PTY and exec state. |
205 | | -Unit tests alone do not prove this contract. |
| 94 | +After approval, an external matrix must prove every |
| 95 | +[field rule](./spec.md#field-rules) and |
| 96 | +[acceptance case](./spec.md#acceptance-cases) through public CLI behavior and |
| 97 | +isolated PTY and exec state. Unit tests alone do not prove this contract. |
0 commit comments