@@ -6,6 +6,256 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
66
77<!-- changelog:entries -->
88
9+ ## [0.1.117-rc.4] - 2026-07-24
10+
11+
12+ ### Added
13+
14+ - Feat(cli): health-aware list/stop, startup failure diagnostics, port retry, doctor --probe (#820)
15+
16+ * feat(cli): warn before af stop interrupts running executions
17+
18+ `af stop <node>` gracefully killed nodes with long-running executions in
19+ flight with no warning. Running executions are queryable from the control
20+ plane, so query them before signalling the process:
21+
22+ - running executions found + TTY → list count/ids/age and prompt to confirm
23+ - running executions found + no TTY → warn (count/ids/age) and proceed
24+ - --force → stop immediately, no query/warning/prompt
25+ - control plane unreachable → note it and proceed (best-effort, as before)
26+
27+ The check runs only once we have confirmed the process is genuinely ours and
28+ alive, so a dead/stale node still reconciles cleanly without spurious queries.
29+
30+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
31+
32+ * feat(cli): surface af run startup logs and retry once on strict-port bind conflict
33+
34+ Two operational failures in the `af run` path made a failed start opaque and
35+ non-recoverable:
36+
37+ - On any startup failure the CLI only printed "did not become ready within 30s";
38+ the real traceback/exit reason lived in the node log, reachable only via a
39+ separate `af logs`. Now the last ~15 lines of the node's log are printed
40+ inline on failure, plus a "Full logs: af logs <node>" pointer.
41+
42+ - When a node was assigned a port that looked free but lost the bind race (a
43+ just-stopped node's port lingering under mirrored networking), the SDK exited
44+ with AGENTFIELD_STRICT_PORT "assigned port N is unavailable" and nothing
45+ retried. The run path now detects that strict-port exit from the node log and
46+ retries exactly once on a fresh port (the failed port is reserved first so it
47+ is never reused), logging "Port <p> unavailable, retrying on a fresh port".
48+
49+ The port-alloc/start/wait section is refactored into attemptStart +
50+ startWithPortRetry so the retry decision and port-change logic are unit-testable
51+ without the real health-poll.
52+
53+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
54+
55+ * feat(cli): reconcile af list registry status with control-plane health
56+
57+ `af list` showed only the local registry's view, which is a claim, not a fact:
58+ a node the registry calls "running" can be dead on the control plane (or a
59+ registry "stopped" node can still be live). Add a HEALTH column that fetches the
60+ control plane's node view (GET /api/v1/nodes?show_all=true, so inactive nodes
61+ are included) and reconciles it with each node's registry status:
62+
63+ - statuses agree → plain health (e.g. "active")
64+ - statuses disagree → health + "(mismatch)" and a footer explaining it
65+ - node absent from CP → "not on control plane (mismatch)" when registry running
66+ - control plane unreachable → "unknown (control plane unreachable)", never an error
67+
68+ The same health/health_discrepancy fields are added to `af list --json` for the
69+ agent-driven flow. A missing control plane never fails the command.
70+
71+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
72+
73+ * feat(cli): add af doctor --probe to smoke-test detected provider CLIs
74+
75+ `af doctor` reported a provider as available whenever its binary was on PATH,
76+ but a present binary can still return instant empty completions (broken auth,
77+ model outage) — the doctor called it healthy while every real call failed.
78+
79+ Add an opt-in `--probe` flag that runs a minimal one-shot prompt against each
80+ DETECTED provider CLI (claude -p, codex exec, gemini -p, opencode run) with a
81+ per-provider 60s timeout and classifies the result:
82+
83+ - ok → non-empty completion
84+ - empty → exit 0 but no output (the silently-broken case a PATH check misses)
85+ - error → non-zero exit (stderr head captured)
86+ - timeout → no response within the timeout
87+
88+ Probes run only for providers doctor already detects; without --probe the
89+ command is unchanged. Output and help note that a probe consumes a trivial
90+ amount of provider quota. The classifier is a pure function so ok/empty/error/
91+ timeout are table-tested from (exit code, stdout, timed-out) tuples.
92+
93+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
94+
95+ * fix: preserve explicit agent ports on startup conflict
96+
97+ * chore(skills): sync embedded skill mirrors on branch
98+
99+ Realigns embedded mirrors with skills/ sources inherited from the main
100+ merge so skillkit drift tests pass branch-locally.
101+
102+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
103+
104+ ---------
105+
106+ Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (f11cb69)
107+
108+
109+
110+ ### Fixed
111+
112+ - Fix(cli): make af config write through to the secret store + pre-install requirements visibility (#819)
113+
114+ * fix(cli): make af config write through to the node-scoped secret store
115+
116+ `af config <node> --set K=V` (and interactive `af config <node>`) only ever
117+ wrote the package `.env`, but `af run` resolves a node's environment from the
118+ encrypted secret store, not that file — so configured values looked saved yet
119+ never reached the running process, and `af config` even told users to run
120+ `af run` with settings it would ignore.
121+
122+ Mirror every value set via `af config` into the node-scoped secret store (the
123+ same one `af secrets set K --node <node>` uses), keeping the `.env` for `af dev`
124+ and the web UI env editor. `--unset` now removes from both destinations so an
125+ unset value cannot linger and get re-injected. Success/interactive copy now
126+ states both destinations truthfully.
127+
128+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
129+
130+ * fix(cli): add actionable fix command to missing-secret startup errors
131+
132+ When a required secret is missing and stdin is not a TTY, `af run` hard-failed
133+ with an error that listed variable names but no way to fix them. Pair each unset
134+ variable (and every option of an unsatisfied require_one_of group) with the exact
135+ command that resolves it — `af secrets set <VAR> --node <name>` — and name the
136+ node in the message so the command is copy-pasteable.
137+
138+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
139+
140+ * feat(cli): add af show-requirements to inspect a node before installing
141+
142+ There was no way to see what environment variables a node needs until after
143+ installing it. `af show-requirements <path-or-git-url>` resolves the source
144+ WITHOUT installing — a local path is parsed in place; a Git URL (with optional
145+ @ref and //subdir selector) is shallow-cloned into a temp directory that is
146+ removed afterwards, so nothing is written under ~/.agentfield.
147+
148+ It prints the node name, required variables, optional variables with their
149+ defaults, and require_one_of groups, pairing each required variable with the
150+ exact `af secrets set <VAR> --node <name>` command. Supports `-o json`. The gap
151+ is now also called out in `af install --help`.
152+
153+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
154+
155+ * fix(cli): point post-install guidance at af secrets + explicit next steps
156+
157+ The post-install output told users to run `af config <node> --set VAR=...` for
158+ missing required variables — a command that only wrote the package .env, which
159+ `af run` ignores. Point it at `af secrets set <VAR> --node <name>` (which `af run`
160+ reads) for both missing required variables and unsatisfied require_one_of groups,
161+ and always print explicit next steps (`af run <name>`, `af list`) so a first-time
162+ user is never left guessing.
163+
164+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
165+
166+ * docs: document af show-requirements and write-through af config
167+
168+ Document the new `af show-requirements` command and the now write-through
169+ `af config` behavior in the agent-node installation guide, consistent with the
170+ existing `af secrets` guidance: `af config --set/--unset` mirrors into the
171+ node-scoped secret store `af run` reads (keeping the `.env` for `af dev` and the
172+ web UI), and the non-interactive missing-secret error now includes the fix
173+ command. Adds both commands to the lifecycle reference table.
174+
175+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
176+
177+ * fix(config): preserve env values and contain subdirs
178+
179+ * fix(packages): report missing --path subdirs as missing manifests again
180+
181+ The symlink-containment hardening made EvalSymlinks fail first on absent
182+ subdirectories, surfacing a raw lstat error instead of the documented
183+ 'no agentfield-package.yaml found (expected at <path>)' message and
184+ breaking four tests that pin that contract. Handle not-exist explicitly
185+ before the resolution error path.
186+
187+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
188+
189+ * chore(skills): sync embedded skill mirrors on branch
190+
191+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
192+
193+ * test(cli,packages): cover requirements CLI branches and subdir error paths
194+
195+ Closes the patch-coverage gap: no-configuration and unlabeled
196+ require_one_of rendering plus inspect-error propagation in
197+ af show-requirements, and the root-resolution / manifest-missing error
198+ branches of ResolvePackageSubdir.
199+
200+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
201+
202+ ---------
203+
204+ Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (4a64cde)
205+
206+
207+
208+ ### Other
209+
210+ - Replace N+1 execution fetches in batch-status handler with one batch fetch (#811)
211+
212+ * issue/batch-status-n-plus-1: replace N+1 execution fetches in batch-status handler with one batch fetch
213+
214+ handleBatchStatus now issues a single GetExecutionRecordsBatch query
215+ regardless of how many execution IDs are requested, instead of looping
216+ GetExecutionRecord per ID. Missing IDs preserve the prior per-ID
217+ 'not_found' response. Requests with more than 500 IDs are rejected with
218+ a 400 before hitting storage. Storage layer gains GetExecutionRecordsBatch
219+ on LocalStorage and the StorageProvider/ExecutionStore interfaces; test
220+ stubs updated to satisfy the new interface.
221+
222+ * fix: preserve batch status partial results
223+
224+ * chore(skills): sync embedded skill mirrors on branch
225+
226+ Realigns embedded mirrors with skills/ sources inherited from the main
227+ merge so skillkit drift tests pass branch-locally.
228+
229+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
230+
231+ ---------
232+
233+ Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (984996d)
234+
235+ - InMemoryBackend: deep-copy values on Set/Get to fix data race (#807)
236+
237+ * issue/inmemory-deepcopy: deep-copy map/slice values in InMemoryBackend Set/Get and SetVector/GetVector to prevent data races
238+
239+ * test: add deep-copy helper coverage for []any, []float64, nil/default paths
240+
241+ Exercises the previously-uncovered branches in deepCopyAny ([]any
242+ recursion, []float64 copying, default passthrough) and
243+ deepCopyFloat64Slice (nil input handling) to satisfy the patch-coverage
244+ gate at 80%. No production code changes.
245+
246+ * fix(sdk): reject cyclic in-memory values
247+
248+ * chore(skills): sync embedded skill mirrors on branch
249+
250+ Realigns embedded mirrors with skills/ sources inherited from the main
251+ merge so skillkit drift tests pass branch-locally.
252+
253+ Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
254+
255+ ---------
256+
257+ Co-authored-by: Claude Fable 5 <noreply@anthropic.com> (7793bc8)
258+
9259## [0.1.117-rc.3] - 2026-07-24
10260
11261
0 commit comments