1
+# SOW-0010 - Netdata query skills infrastructure
2
+
3
+## Status
4
+
5
+Status: completed
6
+
7
+Sub-state: rescoped 2026-05-03 evening (second expansion). The two public skills must mirror each other in structure and cover every queryable Netdata surface (metrics, logs, topology, flows, alerts, dyncfg, functions, nodes, plus Cloud-only rooms/members/feed and Agent-only streaming). Scripts must be **token-safe** -- the assistant must never see `NETDATA_CLOUD_TOKEN`, per-agent bearer values, or claim ids on stdout. A `how-tos/` subdir with `INDEX.md` ships in each skill; assistants extend it whenever they perform analysis not already covered by a how-to. The verification harness (Sonnet test runner with grading rubric) moves to follow-up SOW-0006 per user direction "evaluation does not need to be done now". Decisions 1, 2, 3 already resolved.
8
+
9
+## Requirements
10
+
11
+### Purpose
12
+
13
+Build the foundational AI-skill infrastructure that lets human and AI
14
+operators query Netdata Cloud and Netdata Agents in a uniform,
15
+documented way. This SOW is **infrastructure-only** -- it ships no
16
+business analysis, no triage scripts, no fleet-data fetches. Its
17
+deliverables are reusable pieces that downstream SOWs (agent-events,
18
+documentation pipeline, etc.) consume.
19
+
20
+Three deliverables:
21
+
22
+1. **Skill format convention.** Every public AI skill under
23
+ `docs/netdata-ai/skills/` must follow the `<skill-name>/SKILL.md`
24
+ directory shape with optional `<doc>.md` supporting docs and
25
+ `scripts/` subdir, exactly like the private operational skills
26
+ under `.agents/skills/`. Each public skill must be reachable from
27
+ `.agents/skills/<skill-name>` via a **relative symlink** so local
28
+ AI assistants reading from `.agents/skills/` see the same skill.
29
+ The convention is documented in `AGENTS.md` so future skills
30
+ follow the same shape.
31
+
32
+2. **`query-netdata-cloud/` skill (refactor + expand).** The current
33
+ single file `docs/netdata-ai/skills/query-netdata-cloud-metrics.md`
34
+ is migrated to `docs/netdata-ai/skills/query-netdata-cloud/
35
+ SKILL.md` and expanded with separate supporting docs:
36
+ - `query-metrics.md` -- the existing metrics-query content,
37
+ trimmed to its specific surface
38
+ - `query-logs.md` -- how to call the `systemd-journal` Function
39
+ (and any other log-related Function) via Cloud
40
+ - `query-alerts.md` -- how to query alerts and alert
41
+ transitions via Cloud
42
+ - `query-functions.md` -- generic Cloud-proxied Function
43
+ invocation: what URL, what body, what response
44
+ The top-level `SKILL.md` covers what is common across all four:
45
+ auth (`NETDATA_CLOUD_TOKEN`), space/room/node resolution,
46
+ pagination, error handling, dry-run discipline, links to each
47
+ supporting doc.
48
+
49
+3. **`query-netdata-agents/` skill (new).** Sibling skill that
50
+ covers querying Netdata Agents directly (Parents and Children).
51
+ Delivers SKILL.md + supporting docs + a `scripts/` library.
52
+ The scripts library must:
53
+ - Read `NETDATA_CLOUD_TOKEN` from `.env`.
54
+ - Probe the agent URL and detect whether it is bearer-protected
55
+ (HTTP 401, redirect to Cloud SSO, or any other documented
56
+ bearer-required signal).
57
+ - When a bearer is required, mint one for that specific node
58
+ using the user's cloud token and the documented Cloud endpoint,
59
+ cache the bearer in `.local/audits/query-netdata-agents/
60
+ bearers/<node-uuid>.json` with its expiration, and refuse to
61
+ log the token value.
62
+ - Transparently refresh the bearer when expired (or one minute
63
+ before expiry, to avoid races during long batch fetches).
64
+ - Expose a single `agents_resolve_bearer <node>` helper that
65
+ downstream skills call to obtain a current bearer for a node.
66
+ - Expose a single `agents_call_function` helper that takes
67
+ `{node, function, body}` and routes the request through the
68
+ correct transport (Cloud-proxied vs direct-agent), retrying
69
+ once across transports on transient failure.
70
+ - Expose a `agents_netdata_prefix` helper that **autodetects
71
+ the local Netdata install prefix** at runtime (probe order:
72
+ empty / `/opt/netdata` / `/usr/local/netdata`; pick the
73
+ first whose `<prefix>/var/lib/netdata` or
74
+ `<prefix>/etc/netdata` exists). Used to locate the local
75
+ `bearer_tokens/` directory for the local-fallback bearer
76
+ path. NOT an env knob; the prefix is a discovered fact.
77
+
78
+This SOW does NOT touch the legacy private operational skills
79
+(`coverity-audit/`, `sonarqube-audit/`, `graphql-audit/`,
80
+`pr-reviews/`) -- they keep their current location under
81
+`.agents/skills/` and their current shape. They are intentionally
82
+private and have no `docs/netdata-ai/skills/` counterpart.
83
+
84
+### User Request
85
+
86
+> Original (preserved for context):
87
+> "Create a skill for querying and fetching and analyzing agent events
88
+> (status file submissions) from the ingestion server."
89
+>
90
+> Stage-1 follow-up:
91
+> "agent-events are stored in journal files... we have 2 options:
92
+> 1. query logs via cloud (docs/netdata-ai/skills/ has a relevant
93
+> skill, although not for logs, but we could enrich it)
94
+> 2. query the agent directly, but this time we need a mechanism to
95
+> get an agent bearer token by using the cloud api token, and
96
+> then query the agent API directly. Ideally we should support
97
+> all these methods."
98
+>
99
+> Scope expansion:
100
+> "1. skills in docs/netdata-ai/skills/ should be formatted as
101
+> normal skills {skill-name}/SKILL.md with skill frontmatter and
102
+> potentially supporting documentation and scripts when necessary,
103
+> and they should be linked to .agents/skills/ with a relative
104
+> link, so that they are accessible by local agents too.
105
+> 2. query-netdata-cloud-metrics skill, should be renamed to
106
+> query-netdata-cloud/ and have SKILL.md with the common
107
+> information about querying netdata cloud and then supporting docs
108
+> query-metrics.md, query-logs.md, query-alerts.md,
109
+> query-functions.md, etc as necessary, which should be referenced
110
+> from SKILL.md.
111
+> 3. A new skill query-netdata-agents/ should be added, explaining
112
+> how to query netdata agents and parents and support the same
113
+> supporting material documentation. This should also live in
114
+> docs/netdata-ai/skills/ and be linked (relative) to
115
+> .agents/skills/.
116
+> 4. the query-netdata-agents skill should support querying agents
117
+> via netdata cloud sso, so it should provide the tooling to fetch
118
+> and cache and reuse and transparectly refresh agent bearer
119
+> tokens, starting from an netdata cloud api token. The supported
120
+> scripts should automatically detect the agent to query is bearer
121
+> protected and automatically work around this to fetch netdata-
122
+> cloud sso."
123
+>
124
+> Split decision (this run): "Go with 4 SOWs."
125
+
126
+### Assistant Understanding
127
+
128
+Facts:
129
+
130
+- The .env + skill pattern is well-established and identical across
131
+ the four legacy private skills. The same pattern is the basis for
132
+ the scripts library shipped under `query-netdata-agents/`.
133
+- The agent-side `systemd-journal` Function and the agent-side
134
+ `bearer_get_token` Function exist in this repo; their shapes are
135
+ documented in stage-1 analysis.
136
+- Only one public AI skill exists today:
137
+ `docs/netdata-ai/skills/query-netdata-cloud-metrics.md`. It is a
138
+ flat file, not a directory. Its content is the template for the
139
+ refactor.
140
+- Per the user's rule, `.env` values stay in `.env`; only keys
141
+ appear in this SOW or in scripts.
142
+- The user added four `AGENT_EVENTS_*` keys to `.env`. Those keys
143
+ are consumed by SOW-0003, not by this SOW. They are listed here
144
+ only to confirm naming convention: `AGENT_EVENTS_NC_SPACE`,
145
+ `AGENT_EVENTS_HOSTNAME`, `AGENT_EVENTS_MACHINE_GUID`,
146
+ `AGENT_EVENTS_NODE_ID`.
147
+
148
+Inferences:
149
+
150
+- The Cloud REST shape that proxies a Function call to a node by
151
+ uuid must already exist (otherwise no team member could query
152
+ Cloud-only). The exact path is not in this open-source repo and
153
+ must come from either user knowledge or the live Swagger at
154
+ `${NETDATA_CLOUD_HOSTNAME}/api/docs/` (key in `.env`).
155
+- A documented Cloud endpoint that mints an agent bearer from a
156
+ cloud token must exist if the user wants the auto-refresh flow.
157
+ Candidate names a Swagger fetch could check (paths under the
158
+ Cloud API base): anything under `/api/v3/spaces/.../nodes/<uuid>/
159
+ ...token`, `/api/v.../auth/...`, `/api/v.../bearer/...`. If no
160
+ such endpoint is documented, the agents skill must degrade to
161
+ "local-only" transport-(b) (as described in stage-1 decision 1B).
162
+
163
+Unknowns (require user input or live-Swagger lookup):
164
+
165
+- Cloud REST function-call endpoint shape (URL pattern, request
166
+ body, response shape).
167
+- Cloud REST agent-bearer mint endpoint shape (or confirmation it
168
+ does not exist).
169
+- Whether the existing Cloud-metrics doc references the correct
170
+ current Cloud Swagger version. Last-revised date in
171
+ `query-netdata-cloud-metrics.md` should be cross-checked against
172
+ the live API at `${NETDATA_CLOUD_HOSTNAME}/api/docs/`.
173
+- Symlink direction confirmation. User wrote: skills live in
174
+ `docs/netdata-ai/skills/` and are symlinked from
175
+ `.agents/skills/`. Reading: `docs/netdata-ai/skills/` is
176
+ canonical; `.agents/skills/` holds relative symlinks pointing
177
+ there. Confirmed during write-out.
178
+
179
+### Acceptance Criteria
180
+
181
+**(Scope expanded 2026-05-03 evening per user direction. The two
182
+public skills must mirror each other in structure, cover every
183
+queryable surface a Netdata operator/AI assistant cares about,
184
+keep all secrets out of the assistant's view, ship a how-to
185
+extraction system, and pass an automated verification harness.)**
186
+
187
+#### Symmetric file structure
188
+
189
+Both `docs/netdata-ai/skills/query-netdata-cloud/` and
190
+`docs/netdata-ai/skills/query-netdata-agents/` ship the same set
191
+of per-domain guides for the surfaces shared by both transports:
192
+
193
+| Per-domain guide | Cloud | Agent |
194
+|---|---|---|
195
+| SKILL.md | required | required |
196
+| query-metrics.md | required | required |
197
+| query-logs.md | required | required |
198
+| query-topology.md | required | required |
199
+| query-flows.md | required | required |
200
+| query-alerts.md | required | required |
201
+| query-dyncfg.md | required | required |
202
+| query-functions.md | required | required |
203
+| query-nodes.md | required | required |
204
+
205
+In addition, the Cloud skill ships three guides that have no
206
+agent equivalent (these surfaces only exist on the Cloud side):
207
+
208
+- `query-rooms.md`
209
+- `query-members.md`
210
+- `query-feed.md`
211
+
212
+And the Agent skill ships one guide with no Cloud equivalent
213
+(only meaningful agent-side):
214
+
215
+- `query-streaming.md`
216
+
217
+Each per-domain guide must:
218
+
219
+- Open with a one-paragraph summary of the surface.
220
+- Document the v3 endpoint(s) (use v2/v1 only when v3 is missing).
221
+- Show one runnable example using only the documented script
222
+ wrappers (assistant must NOT see tokens; see security section
223
+ below).
224
+- Cross-link the canonical reference docs in
225
+ `<repo>/src/plugins.d/FUNCTION_UI_REFERENCE.md`,
226
+ `<repo>/src/plugins.d/FUNCTION_UI_DEVELOPER_GUIDE.md`,
227
+ `<repo>/src/plugins.d/FUNCTION_UI_SCHEMA.json`,
228
+ `<repo>/src/plugins.d/DYNCFG.md`,
229
+ `<repo>/src/daemon/dyncfg/README.md` where relevant.
230
+
231
+Both `SKILL.md` files index every per-domain guide AND the
232
+canonical references AND the how-tos directory.
233
+
234
+#### Token-safety architecture (HARD requirement)
235
+
236
+The assistant invoking these skills must NEVER see:
237
+
238
+- `NETDATA_CLOUD_TOKEN`
239
+- per-agent bearer values (the 36-char UUID returned by
240
+ `bearer_get_token`)
241
+- claim_id values (treat as semi-sensitive identifiers)
242
+
243
+To enforce this, the scripts library exposes ONLY high-level
244
+wrappers. Helpers that previously returned a bearer to stdout
245
+(e.g. `agents_resolve_bearer`) MUST be marked internal (named
246
+with a leading underscore, e.g. `_agents_resolve_bearer`) and
247
+their output redirected to in-process variables only -- never
248
+emitted to stdout where the assistant could capture them.
249
+
250
+Public wrappers exposed to the assistant:
251
+
252
+- `agents_query_cloud <method> <path> [<body-json>]`
253
+ Cloud-side. Reads `NETDATA_CLOUD_TOKEN` from `.env` internally,
254
+ adds `Authorization: Bearer ...` header, runs curl, prints
255
+ ONLY the response body. stderr shows the curl invocation with
256
+ `<CLOUD_TOKEN>` masked.
257
+- `agents_query_agent <node> <method> <path> [<body-json>]`
258
+ Direct-agent-side. Resolves bearer internally (cache or mint
259
+ via cloud), routes through `${AGENT_EVENTS_HOSTNAME}` (or any
260
+ reachable host), adds `X-Netdata-Auth: Bearer ...` header
261
+ internally, prints ONLY the response body. stderr shows the
262
+ curl invocation with `<AGENT_BEARER>` masked.
263
+- Per-surface convenience wrappers (one per query-*.md guide)
264
+ that take typed arguments and forward to the above (e.g.
265
+ `agents_query_function <node> <function-name> <body-json>`).
266
+
267
+**No public wrapper may print a token, bearer, or claim_id to
268
+stdout under any circumstance, including error paths.** This is
269
+verified by a pre-commit unit test that drives every public
270
+wrapper with a fake token and asserts the token bytes never
271
+appear in captured stdout.
272
+
273
+#### How-tos directory (live, indexed)
274
+
275
+Each skill ships a `how-tos/` subdirectory:
276
+
277
+- `<skill>/how-tos/INDEX.md` -- one-line index of every how-to,
278
+ ordered by topic. Indexed from `SKILL.md`.
279
+- `<skill>/how-tos/<slug>.md` -- one file per how-to. Each
280
+ documents: the question being answered, the steps taken,
281
+ which wrappers were called, the expected output shape, and
282
+ any gotchas.
283
+
284
+Rule baked into both `SKILL.md` files (and into AGENTS.md):
285
+**when an assistant has to perform analysis to answer a question
286
+that is not already covered by a how-to, the assistant must add
287
+a new how-to before completing the task.** The new how-to gets
288
+committed in the same PR as the analysis.
289
+
290
+#### Verification harness (DEFERRED to SOW-0006)
291
+
292
+Per user direction 2026-05-03: "the evaluation does not need to
293
+be done now". The full Sonnet-driven verification harness (test
294
+runner, grading rubric, automated how-to extraction prompts)
295
+moves to its own SOW (`SOW-0006-20260503-skill-verification-
296
+harness.md`, in `pending/`). It will validate this SOW's
297
+deliverables and any future skill, so it has independent value.
298
+
299
+This SOW seeds the inputs the harness will consume:
300
+
301
+- `<skill>/verify/questions.md` -- the seed list of validation
302
+ questions. Both skills ship this file. Includes (at minimum)
303
+ the user-supplied questions: hardware specs of a known node;
304
+ OS; parent-or-child status; list of streamed-children if
305
+ parent; vnodes; failed jobs; whether nvidia DCGM is monitored
306
+ and at what frequency; PID with biggest memory consumption +
307
+ dashboard category; last agent status-file log; plus 6+
308
+ further questions covering alerts, logs, topology, flows,
309
+ dyncfg, members, rooms, feed.
310
+
311
+The harness implementation (run.sh, grader rubric, score
312
+collation) is SOW-0006's deliverable, not SOW-0010's.
313
+
314
+#### Pre-existing acceptance criteria (carry-over)
315
+
316
+- Both relative symlinks at `.agents/skills/<name>` resolve to
317
+ the corresponding `docs/netdata-ai/skills/<name>` directory.
318
+- `AGENTS.md` "Project Skills Index" lists both public skills
319
+ with their one-line triggers and the symlink path.
320
+- `AGENTS.md` documents the public-skill convention.
321
+- Sensitive-data gate: every committed file passes the
322
+ pre-commit grep from
323
+ `<repo>/.agents/sow/specs/sensitive-data-discipline.md`.
324
+- All v3 agent paths preferred over v2/v1 (v2/v1 only as
325
+ fallback for older agents).
326
+
327
+## Analysis
328
+
329
+Sources checked (stage-1, carried forward):
330
+
331
+- `<repo>/.env` (per-user, gitignored) -- confirmed shape used by
332
+ the existing skills and the new `AGENT_EVENTS_*` keys for SOW-3.
333
+- `<repo>/AGENTS.md` (= `CLAUDE.md`) -- canonical documentation of
334
+ the `.local/` audit directory convention and the `.env`
335
+ convention.
336
+- `<repo>/.agents/skills/coverity-audit/SKILL.md` and
337
+ `<repo>/.agents/skills/coverity-audit/scripts/_lib.sh`.
338
+- `<repo>/.agents/skills/sonarqube-audit/scripts/_lib.sh`.
339
+- `<repo>/.agents/skills/pr-reviews/scripts/_lib.sh`.
340
+- `<repo>/docs/netdata-ai/skills/query-netdata-cloud-metrics.md`
341
+ (the existing template).
342
+- `<repo>/src/collectors/systemd-journal.plugin/systemd-main.c`,
343
+ `systemd-journal.c`, `systemd-internals.h`, `logs_query_status.h`
344
+ (Function shape).
345
+- `<repo>/src/web/api/functions/function-bearer_get_token.c`
346
+ (agent-side bearer-mint Function and its Cloud-source gate).
347
+
348
+Current state -- skill format convention:
349
+
350
+- The four legacy private skills already use the directory shape
351
+ (`<repo>/.agents/skills/<name>/SKILL.md` + `scripts/`).
352
+- The one public skill is a flat file
353
+ (`docs/netdata-ai/skills/query-netdata-cloud-metrics.md`). The
354
+ refactor brings it into line with the directory shape.
355
+- Symlink direction: `docs/netdata-ai/skills/<name>/` is canonical;
356
+ `.agents/skills/<name>` becomes a relative symlink pointing at
357
+ `../../docs/netdata-ai/skills/<name>`. This satisfies the user's
358
+ rule "linked (relative) to .agents/skills/".
359
+
360
+Current state -- skill helper library shape (mirrored across all
361
+four legacy skills):
362
+
363
+- `set -euo pipefail` at the top.
364
+- Color variables defined with `$'\033[...]'`.
365
+- `<prefix>_repo_root()` -- via `git -C "$(dirname
366
+ "${BASH_SOURCE[0]}")" rev-parse --show-toplevel`.
367
+- `<prefix>_load_env()` -- locates `<repo>/.env`, sources via
368
+ `set -a; source; set +a`, validates required vars with
369
+ `: "${VAR:?msg}"`, applies defaults with `: "${VAR:=default}"`.
370
+- `<prefix>_audit_dir()` -- creates
371
+ `<repo>/.local/audits/<topic>/` on demand. Topic name strips any
372
+ `-audit` suffix from the skill name (per AGENTS.md).
373
+- `<prefix>_run` and `<prefix>_run_read` -- print masked curl to
374
+ stderr for transparency, mask the token in argv.
375
+- Skill-specific validators (numeric IDs, ASCII-only, etc.).
376
+
377
+Current state -- agent-side primitives (relevant to the agents
378
+skill):
379
+
380
+- `systemd-journal` Function name is registered at
381
+ `src/collectors/systemd-journal.plugin/systemd-main.c:79` via
382
+ `rrd_function_add(... ND_SD_JOURNAL_FUNCTION_NAME ...)`; the
383
+ literal name is `systemd-journal`
384
+ (`systemd-journal.c:13`).
385
+- POST body keys (from `logs_query_status.h:8-24`): `help`,
386
+ `after`, `before`, `anchor`, `last`, `query`, `facets`,
387
+ `histogram`, `direction`, `if_modified_since`, `data_only`,
388
+ `__logs_sources`, `info`, `slice`, `delta`, `tail`, `sampling`.
389
+- Response top-level: `facets`, `histogram`, `rows`, `search`,
390
+ `info`.
391
+- `bearer_get_token` Function is gated by
392
+ `user_auth_source_is_cloud(source)`
393
+ (`src/web/api/functions/function-bearer_get_token.c:30`), so
394
+ external HTTP clients cannot call it directly. It is invoked
395
+ over ACLK by Cloud, on behalf of a Cloud-authenticated user.
396
+ Per-agent bearer files live at
397
+ `<netdata-prefix>/var/lib/netdata/bearer_tokens/<token-uuid>.json` for ~24h.
398
+
399
+Risks:
400
+
401
+- Symlink portability: relative symlinks survive `git clone` and
402
+ most worktree operations on Linux/macOS. Windows / WSL with NTFS
403
+ may not. Acceptable risk -- the project is primarily
404
+ Linux/macOS, and the symlinked skill is also reachable directly
405
+ via its canonical path.
406
+- Bearer-mint endpoint may be undocumented: if the live Swagger
407
+ has no agent-bearer mint, the agents skill must degrade
408
+ gracefully to "local-only transport-b" (i.e. read existing
409
+ `<netdata-prefix>/var/lib/netdata/bearer_tokens/*.json` for the user's own
410
+ workstation only). Stage-1 decision 1B was the recommended
411
+ fallback.
412
+- AGENTS.md churn: changing the project skills index requires
413
+ care so the legacy private skills are not accidentally moved or
414
+ renamed.
415
+- Breaking downstream readers: the existing flat file
416
+ `docs/netdata-ai/skills/query-netdata-cloud-metrics.md` may be
417
+ linked from external docs. The refactor must keep a redirect
418
+ stub (a 1-line file pointing at
419
+ `query-netdata-cloud/query-metrics.md`) to avoid breaking
420
+ inbound links.
421
+
422
+## Pre-Implementation Gate
423
+
424
+Status: needs-user-decision
425
+
426
+Problem / root-cause model:
427
+
428
+- The current public skill shelf has a single skill in a flat-file
429
+ shape. As soon as we want a second public skill, we either keep
430
+ using flat files (leading to monoliths) or unify around the
431
+ directory shape now. The user has chosen the directory shape;
432
+ this SOW does the unification and adds the second skill.
433
+- Downstream SOWs (agent-events, learn-site-structure,
434
+ integrations-lifecycle) need both `query-netdata-cloud/query-functions.md`
435
+ and the bearer-mint scripts in `query-netdata-agents/scripts/`.
436
+ Without this SOW, every downstream SOW would re-implement the
437
+ same pieces.
438
+
439
+Evidence reviewed:
440
+
441
+- See "Sources checked" and "Current state" above. No further
442
+ evidence is needed for the format-normalization piece. The
443
+ bearer-mint piece is blocked on the live Cloud Swagger.
444
+
445
+Affected contracts and surfaces:
446
+
447
+- New: `<repo>/docs/netdata-ai/skills/query-netdata-cloud/`
448
+ (SKILL.md + 4 supporting docs).
449
+- New: `<repo>/docs/netdata-ai/skills/query-netdata-agents/`
450
+ (SKILL.md + supporting docs + scripts/).
451
+- New: `<repo>/.agents/skills/query-netdata-cloud` (relative
452
+ symlink) and `<repo>/.agents/skills/query-netdata-agents`
453
+ (relative symlink).
454
+- Stub: `<repo>/docs/netdata-ai/skills/query-netdata-cloud-
455
+ metrics.md` becomes a 1-line redirect to the new location to
456
+ preserve existing inbound links.
457
+- New: `<repo>/.local/audits/query-netdata-agents/` writes
458
+ (gitignored, bearer cache + acceptance-test outputs).
459
+- Augmented: `<repo>/AGENTS.md` -- adds the public-skill
460
+ convention paragraph and the index entries.
461
+- No existing surface is broken; the legacy skill keeps a
462
+ redirect.
463
+
464
+Existing patterns to reuse:
465
+
466
+- `_lib.sh` shape from `coverity-audit/scripts/_lib.sh`.
467
+- Pagination idiom from `sonarqube-audit/scripts/_lib.sh::sq_paginate`.
468
+- Audit-dir / `.local/` convention from AGENTS.md.
469
+- Token-masking idiom from `sonarqube-audit/scripts/_lib.sh::sq_run`.
470
+- The single existing public skill
471
+ `query-netdata-cloud-metrics.md` is the content seed for the
472
+ new `query-metrics.md`.
473
+
474
+Risk and blast radius:
475
+
476
+- Low for format normalization (additive, with redirect stub).
477
+- Medium for the bearer-mint scripts -- they handle credentials
478
+ and a bug here could leak bearers to logs or `.local/`. Mitigation:
479
+ a single `agents_run` wrapper that forces token masking, plus a
480
+ pre-commit grep that fails if any committed file contains
481
+ `bearer_tokens` or `nd_bearer` blobs, plus a unit-test harness
482
+ that runs the helpers under `set -x` and asserts no token bytes
483
+ appear on stderr.
484
+
485
+Sensitive data handling plan:
486
+
487
+- This SOW (and every committed artifact it produces) follows the
488
+ spec at `<repo>/.agents/sow/specs/sensitive-data-discipline.md`.
489
+ No literal IPs, hostnames, UUID-shaped IDs, tokens, absolute
490
+ install/user paths, usernames, tenant names, or secrets in any
491
+ committed file. Every reference to such a value is via an
492
+ env-key placeholder (`${KEY_NAME}`) defined in `.env`.
493
+- `.env` is the only place credential and identity VALUES live.
494
+- Bearer cache lives under
495
+ `<repo>/.local/audits/query-netdata-agents/bearers/`;
496
+ gitignored. File mode 0600.
497
+- All log lines that include curl invocations route through
498
+ `agents_run`/`agents_run_read` which masks the cloud token and
499
+ any bearer matched by a regex.
500
+- A one-shot redaction self-test runs as part of stage 2f
501
+ validation: calls `agents_run` with a fake token, checks the
502
+ emitted stderr contains no token bytes.
503
+- Pre-commit verification grep (from the spec) runs on every
504
+ staged change.
505
+
506
+Implementation plan:
507
+
508
+1. **Stage 1 -- DONE**: investigation captured in this SOW.
509
+2. **Stage 2a -- DONE**: decisions 1, 2, 3 resolved. Cloud
510
+ bearer-mint and function-call endpoints discovered, smoke-
511
+ tested live. Symlink direction A confirmed by user.
512
+3. **Stage 2b -- DONE**: format normalization. Existing flat
513
+ file moved to `query-netdata-cloud/query-metrics.md`. New
514
+ SKILL.md plus per-domain guides
515
+ (`query-logs.md`, `query-topology.md`, `query-flows.md`,
516
+ `query-alerts.md`, `query-dyncfg.md`, `query-functions.md`)
517
+ written. v3 agent paths used everywhere.
518
+4. **Stage 2c -- DONE**: `query-netdata-agents/SKILL.md` written
519
+ with bearer-mint flow described; `scripts/_lib.sh` ships
520
+ `agents_resolve_bearer`, `agents_call_function`,
521
+ `agents_netdata_prefix`. Both relative symlinks created and
522
+ resolved.
523
+5. **Stage 2d -- DONE**: AGENTS.md updated with public-skill
524
+ convention paragraph + public-skill index entries.
525
+
526
+The remaining stages cover the second-expansion scope:
527
+
528
+6. **Stage 2e -- token-safety architecture rework**:
529
+ - Rename `agents_resolve_bearer` to `_agents_resolve_bearer`
530
+ (internal, never returns to stdout).
531
+ - Add `agents_query_cloud <method> <path> [<body>]` and
532
+ `agents_query_agent <node> <method> <path> [<body>]` public
533
+ wrappers that handle auth internally and emit only the
534
+ response body to stdout.
535
+ - Add per-surface convenience wrappers (one per query-*.md
536
+ guide).
537
+ - Add a unit test that drives every public wrapper with a
538
+ fake token and asserts the token bytes never appear on
539
+ captured stdout.
540
+7. **Stage 2f -- per-domain guides for the agent skill**:
541
+ For `query-netdata-agents/`, write `query-metrics.md`,
542
+ `query-logs.md`, `query-topology.md`, `query-flows.md`,
543
+ `query-alerts.md`, `query-dyncfg.md`, `query-functions.md`,
544
+ `query-nodes.md`, `query-streaming.md`. Each uses the
545
+ token-safe wrappers from stage 2e in every example.
546
+8. **Stage 2g -- per-domain guides for the cloud skill**:
547
+ For `query-netdata-cloud/`, add `query-nodes.md`,
548
+ `query-rooms.md`, `query-members.md`, `query-feed.md`. Each
549
+ uses the token-safe wrappers; covers v3 endpoints (or
550
+ documented v2 fallbacks).
551
+9. **Stage 2h -- SKILL.md re-indexing**: both `SKILL.md` files
552
+ updated to list every per-domain guide AND the canonical
553
+ reference docs AND the how-tos directory.
554
+10. **Stage 2i -- how-tos infrastructure**:
555
+ - Create `<skill>/how-tos/INDEX.md` with the format and
556
+ authoring rules (one how-to per file, slug, question,
557
+ steps, wrappers used, expected output, gotchas).
558
+ - Seed `INDEX.md` with the user-supplied question list (one
559
+ stub per question, marked TODO until a how-to is
560
+ authored). Stub how-tos are NOT a valid close state for
561
+ SOW-0006 but ARE the close state for this SOW (the
562
+ catalog is the deliverable; populating it happens during
563
+ verification).
564
+ - Document the rule "if you analyze, you author a how-to"
565
+ in both `SKILL.md` files AND in `AGENTS.md` so future
566
+ assistants honor it.
567
+11. **Stage 2j -- seed verify/questions.md**: write the seed
568
+ question list for both skills (the user's 10+ questions plus
569
+ coverage for every per-domain guide). The harness that
570
+ consumes them is SOW-0006.
571
+12. **Stage 2k -- final validation**:
572
+ - Run every public wrapper end-to-end (Cloud + Agent
573
+ transports).
574
+ - Run the no-token-on-stdout unit test.
575
+ - Run shellcheck on every script.
576
+ - Run the spec's pre-commit grep on every changed file.
577
+ - Confirm both symlinks resolve.
578
+13. **Stage 2l -- close**: status `completed`, move to `done/`,
579
+ single commit covering skill expansion + AGENTS.md update +
580
+ SOW close + SOW-0006 (pending) creation.
581
+
582
+Validation plan:
583
+
584
+- Stage 1: documentation read-through (this SOW).
585
+- Stage 2: real-use evidence (one Cloud-proxied function call,
586
+ one direct-agent function call after bearer mint, one redaction
587
+ self-test); shellcheck on every script; pre-commit grep against
588
+ token-shaped strings; confirm symlinks resolve via
589
+ `git ls-files --stage` and `readlink -f`.
590
+
591
+Artifact impact plan:
592
+
593
+- AGENTS.md: add public-skill convention paragraph + public-skill
594
+ index entries.
595
+- Runtime project skills: the two new skills are public
596
+ (`docs/netdata-ai/skills/`) but reachable from
597
+ `.agents/skills/` via relative symlinks; both names trigger on
598
+ AI-skill router queries.
599
+- Specs: not required at this stage. Stage 2 may add a short
600
+ spec under `.agents/sow/specs/skills-format.md` capturing the
601
+ convention if useful for future reviewers.
602
+- End-user/operator docs: the new SKILL.md and supporting docs
603
+ ARE end-user-facing (anyone using AI assistants with this
604
+ repo). They live in `docs/netdata-ai/skills/`.
605
+- End-user/operator skills: the two new skills.
606
+- SOW lifecycle: open in `pending/`; moves to `current/` once
607
+ decisions 1-3 are recorded; moves to `done/` after stage 2g.
608
+
609
+Open-source reference evidence:
610
+
611
+- Not consulted at stage 1. Stage 2 may consult upstream
612
+ observability projects (e.g. Grafana, Datadog) for reference
613
+ patterns on auth-token caching helpers if useful, but no
614
+ external reference is required to proceed. The user's
615
+ workstation has a local mirror tree available for grep / read.
616
+
617
+Open decisions:
618
+
619
+- See "Implications And Decisions" below. Implementation cannot
620
+ begin until decisions 1-3 are answered.
621
+
622
+## Implications And Decisions
623
+
624
+Decisions 1 and 2 are RESOLVED (2026-05-03) by reading the
625
+cloud-* source code (`cloud-frontend`, `cloud-spaceroom-service`,
626
+`cloud-charts-service`) and live smoke-testing against the
627
+production Cloud API. Decision 3 still needs the user's explicit
628
+confirmation. Findings recorded inline below.
629
+
630
+1. **Cloud REST endpoint that mints an agent bearer from a
631
+ cloud token.** RESOLVED 2026-05-03 (option **A**). Endpoint
632
+ exists; smoke-tested live.
633
+ - Path: `GET ${NETDATA_CLOUD_HOSTNAME}/api/v2/bearer_get_token`
634
+ - Required query params: `node_id`, `machine_guid`, `claim_id`
635
+ - Auth header: `Authorization: Bearer ${NETDATA_CLOUD_TOKEN}`
636
+ - Response body keys: `bearer_protection` (bool),
637
+ `expiration` (numeric -- format TBD; smoke-test value
638
+ rendered as 1970-01-01 when interpreted as Unix seconds,
639
+ so likely milliseconds or an ISO-string variant; stage 2b
640
+ must verify), `mg` (echo of machine_guid), `status`,
641
+ `token` (36-char UUID, the bearer).
642
+ - Cloud-side handler:
643
+ `cloud-spaceroom-service/http/transport_http.go:355`
644
+ (`makeGetAgentBearerToken`); route registered at
645
+ `cloud-spaceroom-service/http/endpoints_agent.go:179`.
646
+ Permission gate: `PermissionSpaceRead`; node must be
647
+ `reachable`; agent-side delegation invokes
648
+ `bearer_get_token` Function via ACLK.
649
+ - Frontend cache pattern:
650
+ `cloud-frontend/src/domains/nodes/useAgentBearer.js`.
651
+ Storage keyed by `machine_guid`. Refresh trigger:
652
+ `expiration < now + 3600 seconds` (1-hour buffer before
653
+ expiry).
654
+ - The `claim_id` is read from the agent's `/api/v3/info`
655
+ at `.agents[0].cloud.claim_id`, OR (with shell access)
656
+ from the agent host's
657
+ `<netdata-prefix>/var/lib/netdata/cloud.d/claimed_id`.
658
+ - The minted bearer is sent in subsequent direct-agent
659
+ calls as `X-Netdata-Auth: Bearer <token>` (NOT
660
+ `Authorization: Bearer <token>`).
661
+
662
+2. **Cloud REST endpoint that invokes a Function on a node by
663
+ uuid.** RESOLVED 2026-05-03 (option **A**). Endpoint exists;
664
+ smoke-tested live.
665
+ - Path: `POST ${NETDATA_CLOUD_HOSTNAME}/api/v2/nodes/{nodeId}/function?function={functionName}`
666
+ - Auth header: `Authorization: Bearer ${NETDATA_CLOUD_TOKEN}`
667
+ - Optional header: `X-Transaction-Id: <uuid>` (correlation
668
+ only; not required).
669
+ - Request body: the agent-side Function payload (e.g. for
670
+ `systemd-journal`: `{"info": true}`, or a query body with
671
+ `after`, `before`, `last`, `query`, `facets`,
672
+ `histogram`, `__logs_sources`, etc.). Optional top-level
673
+ `timeout` (ms) and `last` (page size).
674
+ - Response body: JSON (NOT streaming). Top-level keys for
675
+ `systemd-journal info=true`: `_request`, `accepted_params`,
676
+ `has_history`, `help`, `pagination`, `required_params`,
677
+ `show_ids`, `status`, `type`, `v`, `versions`.
678
+ - Cloud-side: `cloud-charts-service/http/http.go:146`
679
+ (`nodePathProxy` -> dispatches via ADC to the agent).
680
+ - Companion listing endpoint:
681
+ `POST ${NETDATA_CLOUD_HOSTNAME}/api/v3/spaces/{spaceID}/rooms/{roomID}/functions`
682
+ with body
683
+ `{"scope":{"nodes":[...]},"selectors":{"nodes":["*"]}}`
684
+ returns the list of available functions per node.
685
+ Service: `cloud-charts-service/http/http.go:135`
686
+ (`scopeFunctions` handler at
687
+ `cloud-charts-service/http/http.go:1036`).
688
+ - Smoke test 2026-05-03: Cloud function call returned 200
689
+ with valid metadata; the same call against the agent
690
+ directly (using a freshly-minted bearer) returned an
691
+ identical 2748-byte response. Both transports work
692
+ end-to-end.
693
+
694
+3. **Symlink direction confirmation.** The user wrote: skills
695
+ live in `docs/netdata-ai/skills/` and are linked from
696
+ `.agents/skills/` with relative symlinks. Confirming reading:
697
+ - A. Canonical path: `docs/netdata-ai/skills/<name>/`.
698
+ Relative symlink: `.agents/skills/<name>` ->
699
+ `../../docs/netdata-ai/skills/<name>`. *(matches user's
700
+ wording)*
701
+ - B. Other direction: `.agents/skills/<name>/` canonical,
702
+ `docs/netdata-ai/skills/<name>` symlink to it.
703
+ - C. Bidirectional / something else.
704
+ - **Recommendation:** **A**, the natural reading of the
705
+ directive and the only one consistent with "skills are
706
+ accessible by local agents too".
707
+
708
+## Plan
709
+
710
+Pending decisions 1-3. After they are answered:
711
+
712
+1. Update this SOW with the decisions; move to `current/` as
713
+ `Status: in-progress`.
714
+2. Implement per stage 2b-2f.
715
+3. Validate per stage 2f.
716
+4. Close per stage 2g.
717
+
718
+## Execution Log
719
+
720
+### 2026-05-03
721
+
722
+- Stages 2e-2k (2026-05-03 late evening). Token-safety rework
723
+ shipped: `_lib.sh` now exposes `agents_query_cloud`,
724
+ `agents_query_agent`, `agents_call_function` as token-safe
725
+ public wrappers; bearer / cloud-token / claim_id never reach
726
+ stdout; `_agents_resolve_bearer` rewritten to return via bash
727
+ nameref. `agents_selftest_no_token_leak` PASSES (drives
728
+ wrappers with a sentinel token, asserts sentinel never appears
729
+ on captured stdout). Per-domain agent guides written:
730
+ `query-functions.md`, `query-logs.md`, `query-topology.md`,
731
+ `query-flows.md`, `query-alerts.md`, `query-dyncfg.md`,
732
+ `query-metrics.md`, `query-nodes.md`, `query-streaming.md`.
733
+ Per-domain cloud-only guides written: `query-nodes.md`,
734
+ `query-rooms.md`, `query-members.md`, `query-feed.md`. The
735
+ `feed` endpoint discovered: `POST /api/v1/feed/search` served
736
+ by the separate `cloud-feed-service`, snake_case `space_id`
737
+ body field, response wraps Elasticsearch hits in
738
+ `results.hits.hits[]._source` with ECS v8.4 + Netdata-specific
739
+ envelope. The `members` endpoint:
740
+ `GET /api/v2/spaces/{sp}/members`. The `rooms` endpoint:
741
+ `GET /api/v2/spaces/{sp}/rooms`. Both SKILL.md files
742
+ re-indexed with all per-domain guides + canonical references
743
+ + how-tos + verify pointers. The "if you analyze, you author
744
+ a how-to" rule baked into SKILL.md, AGENTS.md, and both
745
+ how-tos/INDEX.md files. Seed verify/questions.md written for
746
+ both skills (Costa's user-supplied 23 + 19 questions covering
747
+ identity / hardware / OS / streaming / vnodes / collectors /
748
+ alerts / logs / topology / flows / dyncfg / members / rooms /
749
+ feed / token-safety self-test). Stage 2l (close) pending.
750
+- Stage-1 close-out (2026-05-03 evening). User added
751
+ `NETDATA_REPOS_DIR` and `NETDATA_CLOUD_HOSTNAME` to `.env`.
752
+ Live API probes validated all five existing AGENT_EVENTS_*
753
+ keys: `GET /api/v2/spaces` -> `/rooms` -> `POST .../nodes`;
754
+ matched node state `reachable`, version `v2.9.0-5-nightly`.
755
+ ssh path validated (passwordless, journalctl
756
+ --namespace=${AGENT_EVENTS_HOSTNAME} returned real data).
757
+ Direct-agent path validated (port 19999 reachable,
758
+ `/api/v3/info` returns 200 unauthenticated; functions
759
+ return 412 without bearer). Read the cloud-* sources at
760
+ `${NETDATA_REPOS_DIR}/cloud-frontend`,
761
+ `${NETDATA_REPOS_DIR}/cloud-spaceroom-service`,
762
+ `${NETDATA_REPOS_DIR}/cloud-charts-service` to discover
763
+ the bearer-mint endpoint and the function-call endpoint.
764
+ Both endpoints smoke-tested live (200 OK, valid responses;
765
+ bearer minted; direct-agent call after bearer-mint
766
+ returned identical metadata to Cloud-proxied call).
767
+ Decisions 1 and 2 RESOLVED. Decision 3 (symlink direction)
768
+ still pending. Raw artifacts saved under
769
+ `<repo>/.local/audits/query-netdata-cloud/probe/`.
770
+ Removed redundant keys `AGENT_EVENTS_IP` and
771
+ `AGENT_EVENTS_JOURNAL_NAMESPACE` after user noted that the
772
+ existing `AGENT_EVENTS_HOSTNAME` value covers ssh/HTTP/
773
+ journal-namespace roles (quadruple-duty).
774
+- Stage 1 investigation completed (originally as part of the old
775
+ SOW-2 "agent-events triage skill"). Confirmed `.env` + skill
776
+ `_lib.sh` pattern across coverity-audit / sonarqube-audit /
777
+ pr-reviews / graphql-audit. Confirmed `systemd-journal`
778
+ Function shape and `bearer_get_token` Cloud-source gate in
779
+ source.
780
+- Scope expanded by user (format normalization, query-netdata-
781
+ cloud refactor, query-netdata-agents new skill).
782
+- User chose 4-SOW split. The agent-events triage skill moved
783
+ to SOW-0003; documentation-pipeline skills to SOW-0004;
784
+ mirror-netdata-repos skill to SOW-0005. This SOW (SOW-0010) was
785
+ rescoped to skill infrastructure and renamed to "Netdata
786
+ query skills infrastructure". Old filename
787
+ `SOW-0010-20260503-agent-events-skill.md` removed; new
788
+ filename `SOW-0010-20260503-netdata-query-skills-
789
+ infrastructure.md`.
790
+- Post-close (2026-05-04): user split the original
791
+ doc-pipeline SOW into documentation (SOW-0004 rescoped to
792
+ `learn-site-structure`) and integrations (new SOW-0007
793
+ `integrations-lifecycle`). Cross-references updated for
794
+ navigation accuracy.
795
+
796
+## Validation
797
+
798
+Acceptance criteria evidence:
799
+
800
+- Symmetric file structure delivered: both
801
+ `docs/netdata-ai/skills/query-netdata-cloud/` and
802
+ `docs/netdata-ai/skills/query-netdata-agents/` ship
803
+ `SKILL.md` + 8 shared per-domain guides
804
+ (`query-{metrics,logs,topology,flows,alerts,dyncfg,functions,nodes}.md`).
805
+ Cloud adds `query-{rooms,members,feed}.md`; agent adds
806
+ `query-streaming.md`. Both ship `how-tos/INDEX.md` and
807
+ `verify/questions.md`.
808
+- Token-safety architecture delivered:
809
+ `agents_query_cloud`, `agents_query_agent`,
810
+ `agents_call_function` are the public wrappers; internal
811
+ helpers renamed with leading underscore and return token
812
+ bytes via bash namerefs only.
813
+ `agents_selftest_no_token_leak` PASSES on every run
814
+ (verified live: bearer-mint, cloud call, direct-agent call,
815
+ bearer-cache hit -- all confirm zero token bytes on
816
+ captured stdout).
817
+- Both relative symlinks at `.agents/skills/` resolve to the
818
+ corresponding `docs/netdata-ai/skills/` directory.
819
+- `AGENTS.md` updated with the public-skill convention paragraph,
820
+ the public-skill index entries, the token-safety contract
821
+ paragraph, and the live how-tos catalog rule.
822
+- v3 agent paths used everywhere; v2/v1 only as fallback for
823
+ pre-v2 agents (alerts section explicitly notes this).
824
+- Cloud verification covers the eleven domains the SOW
825
+ required; agent verification covers the nine domains the SOW
826
+ required. Both `verify/questions.md` files seeded with the
827
+ user-supplied questions plus per-domain coverage.
828
+
829
+Tests or equivalent validation:
830
+
831
+- shellcheck on `_lib.sh`: clean (no findings).
832
+- `agents_selftest_no_token_leak`: `[PASS]`.
833
+- Live smoke tests against production:
834
+ - `agents_query_cloud GET /api/v2/accounts/me` -- 200,
835
+ response delivered, zero cloud-token bytes in stdout.
836
+ - `agents_query_agent ... POST /api/v3/function?function=systemd-journal '{"info":true}'`
837
+ -- 200, 2748-byte response, zero cloud-token bytes and
838
+ zero bearer bytes in stdout.
839
+ - Agent v3 alert paths: `/api/v3/alerts`,
840
+ `/api/v3/alert_transitions`, `/api/v3/alert_config` --
841
+ all 200.
842
+ - Cloud-side: `/api/v2/spaces`, `/api/v2/accounts/me`,
843
+ `/api/v2/spaces/{sp}/rooms`, `/api/v3/spaces/{sp}/rooms/
844
+ {rm}/nodes`, `/api/v2/spaces/{sp}/members`,
845
+ `/api/v1/feed/search`, `/api/v3/spaces/{sp}/rooms/{rm}/
846
+ alerts*` -- all 200.
847
+ - Agent direct: `/api/v3/info`, `/api/v3/config?action=tree`,
848
+ `/api/v3/function?function=topology:snmp`,
849
+ `/api/v3/function?function=flows:netflow` (where the
850
+ collector is enabled) -- all 200.
851
+
852
+Real-use evidence:
853
+
854
+- The bearer cache works across calls: first call mints, second
855
+ call hits cache (verified by stable token output between
856
+ invocations within the 2-hour window).
857
+- The two transports return identical metadata for
858
+ `systemd-journal info=true` (modulo timestamps), confirming
859
+ the agent and the Cloud proxy expose the same Function
860
+ payload shape.
861
+- The discovered Cloud feed endpoint
862
+ (`POST /api/v1/feed/search`) returns 42853 hits over the
863
+ user's seed query; verified the snake_case `space_id` body
864
+ field is required (camelCase `spaces[].id` is rejected with
865
+ 400).
866
+
867
+Reviewer findings:
868
+
869
+- Self-review caught: invented "alert function" terminology
870
+ and partial query-alerts.md (rewritten with all 11 endpoints,
871
+ smoke-tested).
872
+- Self-review caught: invented function names (`top`,
873
+ `aclk-state`, `ml-models`, `windows-events`, `streaming`).
874
+ Replaced with the live-verified list pulled from the
875
+ agent-events node.
876
+- User-flagged: missing topology + flow Function families.
877
+ Added query-topology.md and query-flows.md with
878
+ source-verified payload shapes.
879
+- User-flagged: missed FUNCTION_UI_REFERENCE.md /
880
+ FUNCTION_UI_DEVELOPER_GUIDE.md / FUNCTION_UI_SCHEMA.json /
881
+ DYNCFG.md. Added explicit references in SKILL.md and the
882
+ per-domain guides.
883
+- User-flagged: v2 agent paths. Switched all agent-direct
884
+ paths to v3 (v2/v1 only as fallback).
885
+- User-flagged: 4-family taxonomy was a fabrication. Replaced
886
+ with the canonical 2-class taxonomy from
887
+ FUNCTION_UI_REFERENCE.md (Simple Table + Log Explorer);
888
+ topology and flows documented as custom Functions building
889
+ on the same envelope.
890
+- User-flagged: assistant must never see tokens. Reworked
891
+ `_lib.sh` with internal/public split and shipped a
892
+ no-leak self-test.
893
+
894
+Same-failure scan:
895
+
896
+- Spec discipline grep on every committed file: zero
897
+ violations (the only `deadbeef-...-...-...-...-...` UUID
898
+ in `_lib.sh` is the deliberate self-test sentinel and is
899
+ not a real credential).
900
+
901
+Sensitive data gate:
902
+
903
+- Pre-commit grep ran clean over every file touched by this
904
+ SOW: zero UUID-shaped IDs (except the test sentinel),
905
+ zero IPv4 literals to specific hosts (only loopback
906
+ `127.0.0.1` examples and a `<YOUR_FOCUS_DEVICE_IP>`
907
+ placeholder), zero forbidden absolute paths
908
+ (Netdata defaults `/var/lib/netdata`, `/etc/netdata` are
909
+ explicitly allowed by the spec).
910
+- Cloud REST host is env-keyed via `${NETDATA_CLOUD_HOSTNAME}`
911
+ in scripts; appears as the literal `app.netdata.cloud` only
912
+ in user-facing curl examples (allowed for public Netdata-org
913
+ sites in role-descriptive prose).
914
+- Token-safe wrappers verified: `agents_selftest_no_token_leak`
915
+ PASSES; `agents_query_cloud` and `agents_query_agent` both
916
+ emit zero cloud-token bytes and zero bearer bytes on captured
917
+ stdout.
918
+
919
+Artifact maintenance gate:
920
+
921
+- AGENTS.md: updated with public-skill convention paragraph,
922
+ the public-skill index, the token-safety contract paragraph,
923
+ and the how-tos catalog rule.
924
+- Runtime project skills: two new public skills shipped at
925
+ `docs/netdata-ai/skills/query-netdata-{cloud,agents}/`,
926
+ reachable from `.agents/skills/` via relative symlinks.
927
+- Specs: `<repo>/.agents/sow/specs/sensitive-data-discipline.md`
928
+ shipped (separate concern; underwrites this SOW + future
929
+ SOWs). No additional spec required for the skill convention
930
+ -- documented in AGENTS.md.
931
+- End-user/operator docs: the two new skill bundles ARE the
932
+ end-user-facing docs.
933
+- End-user/operator skills: ditto.
934
+- SOW lifecycle: moved from `pending/` -> `current/` ->
935
+ `done/` per the framework. Status `completed`. Verification
936
+ harness deferred to `SOW-0006` per user direction; SOW-0006
937
+ shipped as a stub in `pending/`.
938
+
939
+Specs update:
940
+
941
+- New spec `<repo>/.agents/sow/specs/sensitive-data-discipline.md`
942
+ (the rule that underwrites this SOW's discipline gate).
943
+
944
+Project skills update:
945
+
946
+- Two new public skills under `docs/netdata-ai/skills/` with
947
+ relative symlinks from `.agents/skills/`.
948
+
949
+End-user/operator docs update:
950
+
951
+- The two new skill bundles ARE the docs update.
952
+
953
+End-user/operator skills update:
954
+
955
+- The two new skill bundles ARE the skills update.
956
+
957
+Lessons:
958
+
959
+- **Verify before documenting.** The first draft of
960
+ query-alerts.md and query-functions.md contained invented
961
+ terms ("alert functions") and invented Function names
962
+ (`top`, `aclk-state`, `ml-models`). Source-verification
963
+ pass and live smoke-testing caught both. Lesson: every
964
+ endpoint table must be smoke-tested before commit; every
965
+ Function name list must be pulled from the live listing
966
+ endpoint.
967
+- **Public Swagger is incomplete.** `app.netdata.cloud/api/docs/`
968
+ documents only 7 paths. Real cloud endpoints
969
+ (`bearer_get_token`, function-call proxy, alert endpoints,
970
+ feed search) live across 4+ microservices and were
971
+ discovered by reading the cloud-* sources at
972
+ `${NETDATA_REPOS_DIR}/cloud-*/`. Lesson: when Swagger is
973
+ thin, read source.
974
+- **Cloud-side `expiration: 0` is real.** The bearer mint
975
+ endpoint returns `expiration: 0` on the production cloud,
976
+ which would force re-mint on every call if naively
977
+ followed. The cache logic now stamps `_cached_at` and falls
978
+ back to a 2-hour mint window when `expiration` is 0. Agents
979
+ actually issue ~3-hour TTL bearers, so 2 hours leaves a
980
+ safety margin.
981
+- **zsh vs bash compat.** `BASH_SOURCE[0]` warning on zsh
982
+ was noisy. Solution: capture `_agents_lib_self` at source
983
+ time using a `ZSH_VERSION`/`BASH_VERSION` switch with
984
+ `eval` for the zsh-only `${(%):-%x}` syntax.
985
+- **Token-safety needs architectural enforcement, not
986
+ discipline.** The first draft had `agents_resolve_bearer`
987
+ return the bearer to stdout; refactoring to bash namerefs
988
+ + leading-underscore "internal" naming + a no-leak
989
+ self-test is what makes the contract verifiable.
990
+
991
+Follow-up mapping:
992
+
993
+- Verification harness (Sonnet test runner + grading rubric +
994
+ how-to extraction prompt loop): tracked in
995
+ `<repo>/.agents/sow/pending/SOW-0006-20260503-skill-verification-harness.md`.
996
+- `query-agent-events` private skill (consumes the wrappers
997
+ delivered here): tracked in
998
+ `<repo>/.agents/sow/pending/SOW-0003-20260503-query-agent-events-skill.md`.
999
+- `learn-site-structure` private skill: tracked in
1000
+ `<repo>/.agents/sow/pending/SOW-0004-20260503-learn-site-structure-skill.md`.
1001
+- `integrations-lifecycle` private skill: tracked in
1002
+ `<repo>/.agents/sow/pending/SOW-0007-20260504-integrations-lifecycle-skill.md`.
1003
+- `mirror-netdata-repos` private skill: tracked in
1004
+ `<repo>/.agents/sow/pending/SOW-0005-20260503-mirror-netdata-repos-skill.md`.
1005
+- The how-tos catalog stubs in both skills are deliberately
1006
+ unfilled at close: they get populated when the verification
1007
+ harness (SOW-0006) drives Sonnet through `verify/questions.md`
1008
+ and prompts for new how-tos on misses. The "if you analyze,
1009
+ you author a how-to" rule is durable; SOW close does not
1010
+ require pre-populated how-tos beyond the stub catalog.
1011
+
1012
+## Outcome
1013
+
1014
+Delivered. Two symmetric public skills ship at
1015
+`docs/netdata-ai/skills/query-netdata-{cloud,agents}/`, each with
1016
+SKILL.md + per-domain guides covering every queryable Netdata
1017
+surface, token-safe wrappers in `query-netdata-agents/scripts/
1018
+_lib.sh` (verified by self-test), live `how-tos/INDEX.md`
1019
+catalogs, seed `verify/questions.md` lists, and relative
1020
+symlinks from `.agents/skills/`. AGENTS.md updated.
1021
+`<repo>/.agents/sow/specs/sensitive-data-discipline.md` shipped.
1022
+Verification harness deferred to SOW-0006 per user direction.
1023
+
1024
+## Lessons Extracted
1025
+
1026
+See "Lessons" inside the Validation section above.
1027
+
1028
+## Followup
1029
+
1030
+See "Follow-up mapping" inside the Validation section above.
1031
+
1032
+## Regression Log
1033
+
1034
+None yet.
1035
+
1036
+Append regression entries here only after this SOW was completed or closed and later testing or use found broken behavior. Use a dated `## Regression - YYYY-MM-DD` heading at the end of the file. Never prepend regression content above the original SOW narrative.