@wave-ai-os/wave-cli 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +928 -0
- package/dist/index.cjs +15923 -0
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +15922 -0
- package/package.json +34 -0
- package/scripts/benchmark-cli.mjs +626 -0
- package/scripts/postinstall-local-bin.mjs +20 -0
- package/scripts/verify-dev-api.mjs +1878 -0
|
@@ -0,0 +1,1878 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { spawnSync } from "node:child_process";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
|
|
6
|
+
function loadDotEnv(filePath) {
|
|
7
|
+
const raw = readFileSync(filePath, "utf8");
|
|
8
|
+
const env = {};
|
|
9
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
10
|
+
const trimmed = line.trim();
|
|
11
|
+
if (!trimmed || trimmed.startsWith("#")) continue;
|
|
12
|
+
const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
13
|
+
if (!match) continue;
|
|
14
|
+
const [, key, value] = match;
|
|
15
|
+
env[key] = value;
|
|
16
|
+
}
|
|
17
|
+
return env;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function runWave(args, env) {
|
|
21
|
+
const child = spawnSync("node", ["dist/index.js", ...args], {
|
|
22
|
+
env: { ...process.env, ...env },
|
|
23
|
+
encoding: "utf8"
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const stdout = child.stdout?.trim() ?? "";
|
|
27
|
+
let parsed = null;
|
|
28
|
+
try {
|
|
29
|
+
parsed = JSON.parse(stdout);
|
|
30
|
+
} catch {
|
|
31
|
+
parsed = null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return {
|
|
35
|
+
args,
|
|
36
|
+
code: child.status ?? 1,
|
|
37
|
+
stdout,
|
|
38
|
+
stderr: child.stderr?.trim() ?? "",
|
|
39
|
+
parsed
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function firstId(parsed, field) {
|
|
44
|
+
const root = parsed?.data;
|
|
45
|
+
if (!root || typeof root !== "object") {
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const lookup = root;
|
|
50
|
+
const candidates = [];
|
|
51
|
+
if (field) {
|
|
52
|
+
candidates.push(field);
|
|
53
|
+
}
|
|
54
|
+
for (const key of Object.keys(lookup)) {
|
|
55
|
+
if (!candidates.includes(key)) {
|
|
56
|
+
candidates.push(key);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const key of candidates) {
|
|
61
|
+
const data = lookup[key];
|
|
62
|
+
if (data?.data?.[0]?.id) return String(data.data[0].id);
|
|
63
|
+
if (data?.id) return String(data.id);
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function firstListRow(parsed, fields) {
|
|
69
|
+
for (const field of fields) {
|
|
70
|
+
const row = parsed?.data?.[field]?.data?.[0];
|
|
71
|
+
if (row && typeof row === "object") {
|
|
72
|
+
return row;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function classify(result) {
|
|
79
|
+
if (!result.parsed) {
|
|
80
|
+
return { ok: false, reason: "non_json_output" };
|
|
81
|
+
}
|
|
82
|
+
const embeddedError = findFirstEmbeddedErrors(result.parsed);
|
|
83
|
+
if (result.parsed.ok === true) {
|
|
84
|
+
if (embeddedError) {
|
|
85
|
+
return {
|
|
86
|
+
ok: false,
|
|
87
|
+
reason: "embedded_errors",
|
|
88
|
+
details: { path: embeddedError.path, sample: embeddedError.sample }
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return { ok: true, reason: "ok" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const code = result.parsed?.error?.code ?? "unknown";
|
|
95
|
+
const message = result.parsed?.error?.message ?? "";
|
|
96
|
+
const details = JSON.stringify(result.parsed?.error?.details ?? {});
|
|
97
|
+
const output = `${result.stdout}\n${result.stderr}`;
|
|
98
|
+
if (/unpermitted parameter/i.test(output) || /unpermitted parameter/i.test(details)) {
|
|
99
|
+
return { ok: false, reason: "unpermitted_parameter" };
|
|
100
|
+
}
|
|
101
|
+
const hardFailures = [
|
|
102
|
+
"network_error",
|
|
103
|
+
"missing_auth",
|
|
104
|
+
"generic_error"
|
|
105
|
+
];
|
|
106
|
+
if (hardFailures.includes(code)) {
|
|
107
|
+
return { ok: false, reason: code };
|
|
108
|
+
}
|
|
109
|
+
if (
|
|
110
|
+
details.includes("missingRequiredArguments") ||
|
|
111
|
+
details.includes("argumentNotAccepted") ||
|
|
112
|
+
details.includes("undefinedField") ||
|
|
113
|
+
details.includes("variableNotUsed")
|
|
114
|
+
) {
|
|
115
|
+
return { ok: false, reason: "graphql_contract_error" };
|
|
116
|
+
}
|
|
117
|
+
if (code === "invalid_args" && message.includes("Missing required create fields")) {
|
|
118
|
+
return { ok: true, reason: "skipped_missing_parent_dependency" };
|
|
119
|
+
}
|
|
120
|
+
if (result.parsed?.status === 422 && code === "VALIDATION_ERROR") {
|
|
121
|
+
return { ok: true, reason: "domain_validation_error" };
|
|
122
|
+
}
|
|
123
|
+
if (code === "invalid_args") {
|
|
124
|
+
return { ok: false, reason: code };
|
|
125
|
+
}
|
|
126
|
+
return { ok: true, reason: `reachable_${code}` };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function isNonEmptyErrorsValue(value) {
|
|
130
|
+
if (value === null || value === undefined) return false;
|
|
131
|
+
if (Array.isArray(value)) {
|
|
132
|
+
if (value.length === 0) return false;
|
|
133
|
+
return value.some((entry) => {
|
|
134
|
+
if (entry === null || entry === undefined) return false;
|
|
135
|
+
if (typeof entry === "string") return entry.trim().length > 0;
|
|
136
|
+
if (typeof entry === "object") return Object.keys(entry).length > 0;
|
|
137
|
+
return true;
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (typeof value === "string") return value.trim().length > 0;
|
|
141
|
+
if (typeof value === "object") return Object.keys(value).length > 0;
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function summarizeValue(value) {
|
|
146
|
+
try {
|
|
147
|
+
if (typeof value === "string") return value.slice(0, 160);
|
|
148
|
+
const serialized = JSON.stringify(value);
|
|
149
|
+
if (typeof serialized !== "string") return String(value);
|
|
150
|
+
return serialized.slice(0, 300);
|
|
151
|
+
} catch {
|
|
152
|
+
return String(value);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function findFirstEmbeddedErrors(input) {
|
|
157
|
+
const queue = [{ value: input, path: "$", depth: 0 }];
|
|
158
|
+
const maxDepth = 12;
|
|
159
|
+
const maxNodes = 2000;
|
|
160
|
+
let visited = 0;
|
|
161
|
+
|
|
162
|
+
while (queue.length > 0) {
|
|
163
|
+
const current = queue.shift();
|
|
164
|
+
if (!current) break;
|
|
165
|
+
visited += 1;
|
|
166
|
+
if (visited > maxNodes) break;
|
|
167
|
+
|
|
168
|
+
const { value, path, depth } = current;
|
|
169
|
+
if (!value || typeof value !== "object") continue;
|
|
170
|
+
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
if (depth >= maxDepth) continue;
|
|
173
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
174
|
+
queue.push({ value: value[i], path: `${path}[${i}]`, depth: depth + 1 });
|
|
175
|
+
}
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
for (const [key, nested] of Object.entries(value)) {
|
|
180
|
+
const nextPath = `${path}.${key}`;
|
|
181
|
+
if (key === "errors" && isNonEmptyErrorsValue(nested)) {
|
|
182
|
+
return { path: nextPath, sample: summarizeValue(nested) };
|
|
183
|
+
}
|
|
184
|
+
if (depth < maxDepth && nested && typeof nested === "object") {
|
|
185
|
+
queue.push({ value: nested, path: nextPath, depth: depth + 1 });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function payload(root, attrs) {
|
|
194
|
+
return JSON.stringify({ [root]: attrs });
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function sleepMs(ms) {
|
|
198
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function extractListIds(parsed, field) {
|
|
202
|
+
const rows = parsed?.data?.[field]?.data;
|
|
203
|
+
if (!Array.isArray(rows)) return [];
|
|
204
|
+
return rows
|
|
205
|
+
.map((row) => (row?.id ? String(row.id) : ""))
|
|
206
|
+
.filter((id) => id.length > 0);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function readPath(input, segments) {
|
|
210
|
+
let current = input;
|
|
211
|
+
for (const segment of segments) {
|
|
212
|
+
if (!current || typeof current !== "object") {
|
|
213
|
+
return undefined;
|
|
214
|
+
}
|
|
215
|
+
current = current[segment];
|
|
216
|
+
}
|
|
217
|
+
return current;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function firstDefinedValue(input, paths) {
|
|
221
|
+
for (const path of paths) {
|
|
222
|
+
const value = readPath(input, path);
|
|
223
|
+
if (value !== undefined && value !== null) {
|
|
224
|
+
return value;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function verifySubissueDeleteWithPolling(env, issueId, subIssueId) {
|
|
231
|
+
const attempts = [];
|
|
232
|
+
for (let i = 1; i <= 5; i += 1) {
|
|
233
|
+
const listRes = runWave(["subissues", "list", "--issue-id", issueId], env);
|
|
234
|
+
const ids = extractListIds(listRes.parsed, "sub_issues");
|
|
235
|
+
const deleted = !ids.includes(String(subIssueId));
|
|
236
|
+
attempts.push({ index: i, ids, result: listRes });
|
|
237
|
+
if (deleted) {
|
|
238
|
+
return { ok: true, attempts, tries: i };
|
|
239
|
+
}
|
|
240
|
+
if (i < 5) {
|
|
241
|
+
sleepMs(500);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
return { ok: false, attempts, tries: 5 };
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function isValidationErrorResult(result) {
|
|
248
|
+
return result?.parsed?.status === 422 && result?.parsed?.error?.code === "VALIDATION_ERROR";
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function extractValidationMessages(result) {
|
|
252
|
+
const errors = result?.parsed?.error?.details?.errors?.data;
|
|
253
|
+
if (!Array.isArray(errors)) return [];
|
|
254
|
+
return errors
|
|
255
|
+
.map((entry) => (typeof entry === "string" ? entry.trim() : ""))
|
|
256
|
+
.filter((entry) => entry.length > 0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function startsWithArgs(args, prefix) {
|
|
260
|
+
if (!Array.isArray(args)) return false;
|
|
261
|
+
if (args.length < prefix.length) return false;
|
|
262
|
+
for (let i = 0; i < prefix.length; i += 1) {
|
|
263
|
+
if (args[i] !== prefix[i]) return false;
|
|
264
|
+
}
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function findFirstResultByPrefix(results, prefix) {
|
|
269
|
+
return results.find((entry) => startsWithArgs(entry.args, prefix));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function cloneArgs(args) {
|
|
273
|
+
return Array.isArray(args) ? [...args] : [];
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function parseDataJsonFromArgs(args) {
|
|
277
|
+
const idx = args.indexOf("--data-json");
|
|
278
|
+
if (idx < 0 || idx + 1 >= args.length) return null;
|
|
279
|
+
try {
|
|
280
|
+
const parsed = JSON.parse(String(args[idx + 1]));
|
|
281
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return null;
|
|
282
|
+
return parsed;
|
|
283
|
+
} catch {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function withUpdatedDataJson(args, nextPayload) {
|
|
289
|
+
const idx = args.indexOf("--data-json");
|
|
290
|
+
if (idx < 0 || idx + 1 >= args.length) return args;
|
|
291
|
+
const next = cloneArgs(args);
|
|
292
|
+
next[idx + 1] = JSON.stringify(nextPayload);
|
|
293
|
+
return next;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function getArgValue(args, flag) {
|
|
297
|
+
const idx = args.indexOf(flag);
|
|
298
|
+
if (idx < 0 || idx + 1 >= args.length) return undefined;
|
|
299
|
+
return args[idx + 1];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function isoDatePlusDays(isoDate, days) {
|
|
303
|
+
const date = new Date(`${isoDate}T00:00:00Z`);
|
|
304
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
305
|
+
date.setUTCDate(date.getUTCDate() + days);
|
|
306
|
+
return date.toISOString().slice(0, 10);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function summarizeStatus(result) {
|
|
310
|
+
const status = result?.parsed?.status ?? "n/a";
|
|
311
|
+
const err = result?.parsed?.error?.code ?? "none";
|
|
312
|
+
return `status=${status},err=${err}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function firstArrayValue(input, paths) {
|
|
316
|
+
for (const path of paths) {
|
|
317
|
+
const value = readPath(input, path);
|
|
318
|
+
if (Array.isArray(value)) {
|
|
319
|
+
return value;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
function recordExactCheck({ lines, label, result, ok, reason }) {
|
|
326
|
+
const status = result?.parsed?.status ?? "n/a";
|
|
327
|
+
const errCode = result?.parsed?.error?.code ?? "none";
|
|
328
|
+
lines.push(
|
|
329
|
+
`${ok ? "PASS" : "FAIL"} | status=${status} | err=${errCode} | reason=${
|
|
330
|
+
reason || (ok ? "ok" : "assertion_failed")
|
|
331
|
+
} | ${label} (${result?.args?.join(" ") ?? "n/a"})`
|
|
332
|
+
);
|
|
333
|
+
return ok ? 0 : 1;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function resolveAnnualStrategicObjectiveId(result) {
|
|
337
|
+
return firstDefinedValue(result?.parsed, [
|
|
338
|
+
["data", "annual_objective", "attributes", "strategicObjectiveId"],
|
|
339
|
+
["data", "annual_objective", "attributes", "strategic_objective_id"],
|
|
340
|
+
["data", "annual_objective", "strategicObjectiveId"],
|
|
341
|
+
["data", "annual_objective", "strategic_objective_id"]
|
|
342
|
+
]);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
const root = process.cwd();
|
|
346
|
+
const envFile = resolve(root, ".env");
|
|
347
|
+
const dot = loadDotEnv(envFile);
|
|
348
|
+
const env = {
|
|
349
|
+
WAVE_API_BASE_URL: dot.WAVE_API_BASE_URL,
|
|
350
|
+
WAVE_API_TOKEN: dot.WAVE_API_TOKEN,
|
|
351
|
+
WAVE_ORGANIZATION_ID: dot.WAVE_ORGANIZATION_ID,
|
|
352
|
+
WAVE_AGENT_NAME: dot.WAVE_AGENT_NAME || "cli-verifier",
|
|
353
|
+
WAVE_AGENT_RUN_ID: `verify-${Date.now()}`,
|
|
354
|
+
WAVE_TIMEOUT_MS: dot.WAVE_TIMEOUT_MS || "10000"
|
|
355
|
+
};
|
|
356
|
+
|
|
357
|
+
const idLookup = {};
|
|
358
|
+
const listSeeds = [
|
|
359
|
+
["projects", ["projects", "list", "--page", "1", "--per", "2"]],
|
|
360
|
+
["tasks", ["tasks", "list", "--page", "1", "--per", "2"]],
|
|
361
|
+
["rocks", ["rocks", "list", "--page", "1", "--per", "2"]],
|
|
362
|
+
["meetings", ["meetings", "list", "--per", "2"]],
|
|
363
|
+
["members", ["members", "list", "--page", "1", "--per", "2"]],
|
|
364
|
+
["teams", ["teams", "list", "--page", "1", "--per", "2"]],
|
|
365
|
+
["issues", ["issues", "list", "--page", "1", "--per", "2"]],
|
|
366
|
+
["lists", ["lists", "list", "--page", "1", "--per", "2"]],
|
|
367
|
+
["listItems", ["list-items", "list", "--page", "1", "--per", "2"]],
|
|
368
|
+
["issueGroups", ["issue-groups", "list", "--page", "1", "--per", "2"]],
|
|
369
|
+
["todoGroups", ["todo-groups", "list", "--page", "1", "--per", "2"]],
|
|
370
|
+
["todos", ["todos", "list", "--page", "1", "--per", "2"]],
|
|
371
|
+
["rockCollections", ["rock-collections", "list", "--page", "1", "--per", "2"]],
|
|
372
|
+
["knowledge", ["knowledge", "list", "--page", "1", "--per", "2"]],
|
|
373
|
+
["standUps", ["stand-ups", "list", "--page", "1", "--per", "2"]],
|
|
374
|
+
["news", ["news", "list", "--page", "1", "--per", "2"]],
|
|
375
|
+
["questions", ["questions", "list", "--page", "1", "--per", "2"]],
|
|
376
|
+
["pulse", ["pulse", "list", "--page", "1", "--per", "2"]],
|
|
377
|
+
["surveys", ["surveys", "list", "--page", "1", "--per", "2"]],
|
|
378
|
+
["feedbacks", ["feedbacks", "list", "--page", "1", "--per", "2"]],
|
|
379
|
+
["accountability", ["accountability", "list", "--page", "1", "--per", "2"]],
|
|
380
|
+
["kpis", ["kpis", "list", "--page", "1", "--per", "2"]],
|
|
381
|
+
["scorecardGroups", ["scorecard-groups", "list", "--page", "1", "--per", "2"]],
|
|
382
|
+
["scorecards", ["scorecards", "list", "--page", "1", "--per", "2"]],
|
|
383
|
+
["customers", ["customers", "list", "--page", "1", "--per", "2"]],
|
|
384
|
+
["contacts", ["contacts", "list", "--page", "1", "--per", "2"]],
|
|
385
|
+
["annualObjectives", ["foundation", "annual-objectives", "list", "--page", "1", "--per", "2"]],
|
|
386
|
+
["quarterlyObjectives", ["foundation", "quarterly-objectives", "list", "--page", "1", "--per", "2"]]
|
|
387
|
+
];
|
|
388
|
+
|
|
389
|
+
const results = [];
|
|
390
|
+
for (const [key, args] of listSeeds) {
|
|
391
|
+
const res = runWave(args, env);
|
|
392
|
+
results.push(res);
|
|
393
|
+
if (res.parsed?.ok) {
|
|
394
|
+
const field = args[0] === "foundation" ? args[1].replace(/-([a-z])/g, (_, c) => c.toUpperCase()) : args[0].replace(/-([a-z])/g, (_, c) => c.toUpperCase());
|
|
395
|
+
idLookup[key] = firstId(res.parsed, field);
|
|
396
|
+
if (key === "kpis") {
|
|
397
|
+
const firstKpi = firstListRow(res.parsed, ["smart_kpis", "smartKpis", "kpis"]);
|
|
398
|
+
if (firstKpi && typeof firstKpi === "object") {
|
|
399
|
+
const attributes = firstKpi.attributes;
|
|
400
|
+
if (attributes && typeof attributes === "object") {
|
|
401
|
+
const attr = attributes;
|
|
402
|
+
idLookup.kpiSmartKpiViewId =
|
|
403
|
+
typeof attr.smart_kpi_view_id === "string"
|
|
404
|
+
? attr.smart_kpi_view_id
|
|
405
|
+
: typeof attr.smartKpiViewId === "string"
|
|
406
|
+
? attr.smartKpiViewId
|
|
407
|
+
: undefined;
|
|
408
|
+
idLookup.kpiCreateName =
|
|
409
|
+
typeof attr.name === "string" && attr.name.trim().length > 0 ? attr.name : undefined;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
const orgId = env.WAVE_ORGANIZATION_ID;
|
|
417
|
+
const projectId = idLookup.projects;
|
|
418
|
+
const taskId = idLookup.tasks;
|
|
419
|
+
const rockId = idLookup.rocks;
|
|
420
|
+
const meetingId = idLookup.meetings;
|
|
421
|
+
const memberId = idLookup.members;
|
|
422
|
+
const teamId = idLookup.teams;
|
|
423
|
+
const issueId = idLookup.issues;
|
|
424
|
+
const issueGroupId = idLookup.issueGroups;
|
|
425
|
+
const listId = idLookup.lists;
|
|
426
|
+
const listItemId = idLookup.listItems;
|
|
427
|
+
const todoGroupId = idLookup.todoGroups;
|
|
428
|
+
const todoId = idLookup.todos;
|
|
429
|
+
const rockCollectionId = idLookup.rockCollections;
|
|
430
|
+
const contentId = idLookup.knowledge;
|
|
431
|
+
const standUpId = idLookup.standUps;
|
|
432
|
+
const headlineId = idLookup.news;
|
|
433
|
+
const questionId = idLookup.questions;
|
|
434
|
+
const pulseId = idLookup.pulse;
|
|
435
|
+
const surveyId = idLookup.surveys;
|
|
436
|
+
const feedbackId = idLookup.feedbacks;
|
|
437
|
+
const responsibilityId = idLookup.accountability;
|
|
438
|
+
const kpiId = idLookup.kpis;
|
|
439
|
+
const kpiSmartKpiViewId = idLookup.kpiSmartKpiViewId;
|
|
440
|
+
const kpiCreateName = idLookup.kpiCreateName;
|
|
441
|
+
const measurableGroupId = idLookup.scorecardGroups;
|
|
442
|
+
const measurableId = idLookup.scorecards;
|
|
443
|
+
const customerId = idLookup.customers;
|
|
444
|
+
const contactId = idLookup.contacts;
|
|
445
|
+
const annualObjectiveId = idLookup.annualObjectives;
|
|
446
|
+
const quarterlyObjectiveId = idLookup.quarterlyObjectives;
|
|
447
|
+
const verifyStamp = String(Date.now());
|
|
448
|
+
const orgWorkspaceNameVerify = `Wave Verify ${verifyStamp}`;
|
|
449
|
+
const orgMetaOneSentenceSummaryVerify = `Verify Title ${verifyStamp}`;
|
|
450
|
+
const keyMetricRevenueVerify = String(1000 + Number(verifyStamp.slice(-3)));
|
|
451
|
+
const questionNameVerify = `CLI Verify Question Update ${verifyStamp}`;
|
|
452
|
+
const surveyDueDateVerify = "2026-12-31";
|
|
453
|
+
const feedbackQuarterVerify = "q3";
|
|
454
|
+
const accountabilityNameVerify = `CLI Verify Responsibility Update ${verifyStamp}`;
|
|
455
|
+
const organizationMetaProfileSeed = runWave(
|
|
456
|
+
["organizations", "meta-profile", "show", "--id", orgId],
|
|
457
|
+
env
|
|
458
|
+
);
|
|
459
|
+
results.push(organizationMetaProfileSeed);
|
|
460
|
+
const organizationMetaProfileId =
|
|
461
|
+
firstId(organizationMetaProfileSeed.parsed, "organization_meta_profile") ?? orgId;
|
|
462
|
+
const keyMetricMetaProfileSeed = runWave(
|
|
463
|
+
["organizations", "key-metric-meta-profile", "show", "--id", orgId],
|
|
464
|
+
env
|
|
465
|
+
);
|
|
466
|
+
results.push(keyMetricMetaProfileSeed);
|
|
467
|
+
const keyMetricMetaProfileId =
|
|
468
|
+
firstId(keyMetricMetaProfileSeed.parsed, "key_metric_meta_profile") ?? orgId;
|
|
469
|
+
const meetingCreatePayload = teamId
|
|
470
|
+
? {
|
|
471
|
+
name: "CLI Verify Meeting",
|
|
472
|
+
type: "TeamMeeting",
|
|
473
|
+
member_id: memberId,
|
|
474
|
+
date: "2026-04-18",
|
|
475
|
+
start_time: 700,
|
|
476
|
+
end_time: 760,
|
|
477
|
+
repeats: "never",
|
|
478
|
+
status: "upcoming",
|
|
479
|
+
teams_ids: [teamId]
|
|
480
|
+
}
|
|
481
|
+
: {
|
|
482
|
+
name: "CLI Verify Meeting",
|
|
483
|
+
type: "OneOnOneMeeting",
|
|
484
|
+
member_id: memberId,
|
|
485
|
+
date: "2026-04-18",
|
|
486
|
+
start_time: 700,
|
|
487
|
+
repeats: "never",
|
|
488
|
+
status: "upcoming"
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const probes = [
|
|
492
|
+
["projects", "show", "--id", projectId],
|
|
493
|
+
["tasks", "show", "--id", taskId],
|
|
494
|
+
["rocks", "show", "--id", rockId],
|
|
495
|
+
["meetings", "show", "--id", meetingId],
|
|
496
|
+
["members", "show", "--id", memberId],
|
|
497
|
+
["teams", "show", "--id", teamId],
|
|
498
|
+
["issues", "show", "--id", issueId],
|
|
499
|
+
["lists", "show", "--id", listId],
|
|
500
|
+
["list-items", "show", "--id", listItemId],
|
|
501
|
+
["issue-groups", "show", "--id", issueGroupId],
|
|
502
|
+
["todo-groups", "show", "--id", todoGroupId],
|
|
503
|
+
["todos", "show", "--id", todoId],
|
|
504
|
+
["rock-collections", "show", "--id", rockCollectionId],
|
|
505
|
+
["knowledge", "show", "--id", contentId],
|
|
506
|
+
["stand-ups", "show", "--id", standUpId],
|
|
507
|
+
["news", "show", "--id", headlineId],
|
|
508
|
+
["questions", "show", "--id", questionId],
|
|
509
|
+
["pulse", "show", "--id", pulseId],
|
|
510
|
+
["surveys", "show", "--id", surveyId],
|
|
511
|
+
["feedbacks", "show", "--id", feedbackId],
|
|
512
|
+
["accountability", "show", "--id", responsibilityId],
|
|
513
|
+
["kpis", "show", "--id", kpiId],
|
|
514
|
+
["scorecard-groups", "show", "--id", measurableGroupId],
|
|
515
|
+
["scorecards", "show", "--id", measurableId],
|
|
516
|
+
["customers", "show", "--id", customerId],
|
|
517
|
+
["contacts", "show", "--id", contactId],
|
|
518
|
+
["organizations", "show", "--id", orgId],
|
|
519
|
+
["organizations", "meta-profile", "show", "--id", orgId],
|
|
520
|
+
["organizations", "key-metric-meta-profile", "show", "--id", orgId],
|
|
521
|
+
["foundation", "strategic-plans", "show", "--id", orgId],
|
|
522
|
+
["foundation", "strategic-objectives", "show", "--id", orgId],
|
|
523
|
+
["foundation", "annual-objectives", "show", "--id", annualObjectiveId],
|
|
524
|
+
["foundation", "quarterly-objectives", "show", "--id", quarterlyObjectiveId],
|
|
525
|
+
["projects", "create", "--data-json", payload("project", { name: "CLI Verify Project", status: "backlog", priority: "no_priority" })],
|
|
526
|
+
["projects", "update", "--id", projectId, "--data-json", payload("project", { description: "verify-update" })],
|
|
527
|
+
["tasks", "create", "--project-id", projectId, "--summary", "CLI verify task"],
|
|
528
|
+
["tasks", "update", "--id", taskId, "--summary", "CLI verify task update"],
|
|
529
|
+
["rocks", "update-status", "--id", rockId, "--status", "on_track"],
|
|
530
|
+
["rocks", "create", "--data-json", payload("rock", { name: "CLI Verify Rock", rock_collection_id: rockCollectionId, status: "on_track", priority: "no_priority", rock_type: "individual" })],
|
|
531
|
+
["rocks", "update", "--id", rockId, "--data-json", payload("rock", { description: "verify-update" })],
|
|
532
|
+
["meetings", "notes", "--id", meetingId, "--content", "CLI verify notes"],
|
|
533
|
+
["meetings", "create", "--data-json", payload("meeting", meetingCreatePayload)],
|
|
534
|
+
["meetings", "update", "--id", meetingId, "--data-json", payload("meeting", { notes: "verify-update" })],
|
|
535
|
+
["members", "create", "--data-json", payload("member", { email: `cli.verify.${Date.now()}@example.com` })],
|
|
536
|
+
["members", "update", "--id", memberId, "--data-json", payload("member", { first_name: "CLI" })],
|
|
537
|
+
["teams", "create", "--data-json", payload("team", { name: `CLI Verify Team ${Date.now()}` })],
|
|
538
|
+
["teams", "update", "--id", teamId, "--data-json", payload("team", { name: `CLI Verify Team Update ${Date.now()}` })],
|
|
539
|
+
["organizations", "update", "--id", orgId, "--data-json", payload("organization", { organization_detail_attributes: { workspace_name: orgWorkspaceNameVerify } })],
|
|
540
|
+
["organizations", "meta-profile", "update", "--id", organizationMetaProfileId, "--data-json", payload("organization_meta_profile", { profile: { company_identity: { one_sentence_summary: orgMetaOneSentenceSummaryVerify } } })],
|
|
541
|
+
["organizations", "key-metric-meta-profile", "update", "--id", keyMetricMetaProfileId, "--data-json", payload("key_metric_meta_profile", { profile: { financial: { revenue: keyMetricRevenueVerify } } })],
|
|
542
|
+
["issues", "create", "--issue-group-id", issueGroupId, "--name", "CLI Verify Issue", "--issue-type", "short_term"],
|
|
543
|
+
["issues", "update", "--id", issueId, "--data-json", payload("issue", { description: "verify-update" })],
|
|
544
|
+
["lists", "create", "--data-json", payload("list", { name: "CLI Verify List", status: "in_progress", priority: "no_priority" })],
|
|
545
|
+
["lists", "update", "--id", listId, "--data-json", payload("list", { description: "verify-update" })],
|
|
546
|
+
["list-items", "create", "--data-json", payload("list_item", { list_id: listId, summary: "CLI Verify List Item" })],
|
|
547
|
+
["list-items", "update", "--id", listItemId, "--data-json", payload("list_item", { summary: "CLI Verify List Item Update" })],
|
|
548
|
+
["issue-groups", "create", "--data-json", payload("issue_group", { name: "CLI Verify Issue Group", status: "in_progress", priority: "no_priority" })],
|
|
549
|
+
["issue-groups", "update", "--id", issueGroupId, "--data-json", payload("issue_group", { description: "verify-update" })],
|
|
550
|
+
["todo-groups", "create", "--data-json", payload("todo_group", { name: "CLI Verify Todo Group", status: "in_progress", priority: "no_priority" })],
|
|
551
|
+
["todo-groups", "update", "--id", todoGroupId, "--data-json", payload("todo_group", { description: "verify-update" })],
|
|
552
|
+
["todos", "create", "--data-json", payload("todo", { todo_group_id: todoGroupId, name: "CLI Verify Todo", status: "open", priority: "no_priority" })],
|
|
553
|
+
["todos", "update", "--id", todoId, "--data-json", payload("todo", { description: "verify-update" })],
|
|
554
|
+
["rock-collections", "create", "--data-json", payload("rock_collection", { name: "CLI Verify Rock Collection", status: "in_progress", priority: "no_priority" })],
|
|
555
|
+
["rock-collections", "update", "--id", rockCollectionId, "--data-json", payload("rock_collection", { description: "verify-update" })],
|
|
556
|
+
["knowledge", "create", "--data-json", payload("content", { content_type: "process", status: "draft", name: "CLI Verify Doc", member_id: memberId })],
|
|
557
|
+
["knowledge", "update", "--id", contentId, "--data-json", payload("content", { name: "CLI Verify Doc Update" })],
|
|
558
|
+
["stand-ups", "create", "--data-json", payload("stand_up", { member_id: memberId, completed_date: "2026-04-06" })],
|
|
559
|
+
["stand-ups", "update", "--id", standUpId, "--data-json", payload("stand_up", { blockers: "verify-update" })],
|
|
560
|
+
["news", "create", "--data-json", payload("headline", { summary: "CLI Verify Headline", member_id: memberId, status: "active", headline_type: "org_wide" })],
|
|
561
|
+
["news", "update", "--id", headlineId, "--data-json", payload("headline", { description: "verify-update" })],
|
|
562
|
+
["questions", "create", "--data-json", payload("question", { name: "CLI Verify Question", member_id: memberId })],
|
|
563
|
+
["questions", "update", "--id", questionId, "--data-json", payload("question", { name: questionNameVerify, description: "verify-update" })],
|
|
564
|
+
["pulse", "create", "--data-json", payload("health_update", { health_updatable_id: projectId, health_updatable_type: "Project", member_id: memberId, value: "on_track", status: "on_track" })],
|
|
565
|
+
["pulse", "update", "--id", pulseId, "--data-json", payload("health_update", { value: "verify-update" })],
|
|
566
|
+
["surveys", "create", "--data-json", payload("survey", { name: "engagement", recipient_type: "org_wide" })],
|
|
567
|
+
["surveys", "update", "--id", surveyId, "--data-json", payload("survey", { name: "engagement", recipient_type: "org_wide", due_date: surveyDueDateVerify })],
|
|
568
|
+
["feedbacks", "create", "--data-json", payload("feedback", { name: "company_culture", quarter: "q2", year: "2026" })],
|
|
569
|
+
["feedbacks", "update", "--id", feedbackId, "--data-json", payload("feedback", { name: "roadmap_and_strategy", quarter: feedbackQuarterVerify, year: "2026" })],
|
|
570
|
+
["accountability", "create", "--data-json", payload("responsibility", { name: "CLI Verify Responsibility", member_id: memberId })],
|
|
571
|
+
["accountability", "update", "--id", responsibilityId, "--data-json", payload("responsibility", { name: accountabilityNameVerify })],
|
|
572
|
+
["kpis", "create", "--data-json", payload("smart_kpi", { smart_kpi_view_id: kpiSmartKpiViewId, name: kpiCreateName })],
|
|
573
|
+
["kpis", "update", "--id", kpiId, "--data-json", payload("smart_kpi", { name: "CLI Verify KPI Update" })],
|
|
574
|
+
["scorecard-groups", "create", "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group" })],
|
|
575
|
+
["scorecard-groups", "update", "--id", measurableGroupId, "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group Update" })],
|
|
576
|
+
["scorecards", "create", "--data-json", payload("measurable", { measurable_group_id: measurableGroupId, name: "CLI Verify Scorecard", interval: "weekly", trend: "average" })],
|
|
577
|
+
["scorecards", "update", "--id", measurableId, "--data-json", payload("measurable", { name: "CLI Verify Scorecard Update" })],
|
|
578
|
+
["customers", "create", "--data-json", payload("customer", { name: "CLI Verify Customer" })],
|
|
579
|
+
["customers", "update", "--id", customerId, "--data-json", payload("customer", { name: "CLI Verify Customer Update" })],
|
|
580
|
+
["contacts", "create", "--data-json", payload("contact", { customer_id: customerId, name: "CLI Verify Contact" })],
|
|
581
|
+
["contacts", "update", "--id", contactId, "--data-json", payload("contact", { name: "CLI Verify Contact Update" })],
|
|
582
|
+
["foundation", "strategic-plans", "update", "--id", orgId, "--data-json", payload("strategic_plan", { published_at: "2026-04-18T17:00:00Z" })],
|
|
583
|
+
["foundation", "strategic-objectives", "update", "--id", orgId, "--data-json", payload("strategic_objective", { summary: "CLI Verify Strategic Objective" })],
|
|
584
|
+
["foundation", "annual-objectives", "create", "--data-json", payload("annual_objective", { strategic_objective_id: annualObjectiveId || orgId, name: "CLI Verify Annual Objective" })],
|
|
585
|
+
["foundation", "annual-objectives", "update", "--id", annualObjectiveId, "--data-json", payload("annual_objective", { name: "CLI Verify Annual Objective Update" })],
|
|
586
|
+
["foundation", "quarterly-objectives", "create", "--data-json", payload("quarterly_objective", { strategic_objective_id: annualObjectiveId || orgId, annual_objective_id: annualObjectiveId || orgId, name: "CLI Verify Quarterly Objective" })],
|
|
587
|
+
["foundation", "quarterly-objectives", "update", "--id", quarterlyObjectiveId, "--data-json", payload("quarterly_objective", { name: "CLI Verify Quarterly Objective Update" })]
|
|
588
|
+
];
|
|
589
|
+
|
|
590
|
+
for (const args of probes) {
|
|
591
|
+
if (args.includes(undefined)) {
|
|
592
|
+
results.push({
|
|
593
|
+
args,
|
|
594
|
+
code: 0,
|
|
595
|
+
stdout: JSON.stringify({
|
|
596
|
+
ok: false,
|
|
597
|
+
error: { code: "skipped_missing_id", details: {} }
|
|
598
|
+
}),
|
|
599
|
+
parsed: { ok: false, error: { code: "skipped_missing_id", details: {} } }
|
|
600
|
+
});
|
|
601
|
+
continue;
|
|
602
|
+
}
|
|
603
|
+
results.push(runWave(args, env));
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
const validationRemediationEntries = [];
|
|
607
|
+
let validationRemediationFailures = 0;
|
|
608
|
+
let feedbackQuarterExpected = feedbackQuarterVerify;
|
|
609
|
+
|
|
610
|
+
function recordValidationRemediation(entry) {
|
|
611
|
+
validationRemediationEntries.push(entry);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function remediateStandupsCreateDuplicateDate() {
|
|
615
|
+
const operation = "stand-ups.create";
|
|
616
|
+
const initial = findFirstResultByPrefix(results, ["stand-ups", "create"]);
|
|
617
|
+
if (!initial) {
|
|
618
|
+
recordValidationRemediation({
|
|
619
|
+
operation,
|
|
620
|
+
offendingFields: ["stand_up.completed_date"],
|
|
621
|
+
backendMessage: "initial probe missing",
|
|
622
|
+
initialResult: "missing",
|
|
623
|
+
remediation: "none",
|
|
624
|
+
finalResult: "missing"
|
|
625
|
+
});
|
|
626
|
+
validationRemediationFailures += 1;
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const messages = extractValidationMessages(initial);
|
|
631
|
+
const isDuplicateDateValidation =
|
|
632
|
+
isValidationErrorResult(initial) &&
|
|
633
|
+
messages.some((msg) => /completed date.*already been taken/i.test(msg));
|
|
634
|
+
|
|
635
|
+
if (!isDuplicateDateValidation) {
|
|
636
|
+
recordValidationRemediation({
|
|
637
|
+
operation,
|
|
638
|
+
offendingFields: ["stand_up.completed_date"],
|
|
639
|
+
backendMessage: messages[0] ?? "",
|
|
640
|
+
initialResult: summarizeStatus(initial),
|
|
641
|
+
remediation: "none_not_required",
|
|
642
|
+
finalResult: summarizeStatus(initial)
|
|
643
|
+
});
|
|
644
|
+
return;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
const initialPayload = parseDataJsonFromArgs(initial.args);
|
|
648
|
+
const standUpBody =
|
|
649
|
+
initialPayload && typeof initialPayload.stand_up === "object" ? initialPayload.stand_up : null;
|
|
650
|
+
const baseDate =
|
|
651
|
+
standUpBody && typeof standUpBody.completed_date === "string"
|
|
652
|
+
? standUpBody.completed_date
|
|
653
|
+
: "2026-04-06";
|
|
654
|
+
|
|
655
|
+
if (!standUpBody || typeof standUpBody.member_id !== "string") {
|
|
656
|
+
recordValidationRemediation({
|
|
657
|
+
operation,
|
|
658
|
+
offendingFields: ["stand_up.member_id", "stand_up.completed_date"],
|
|
659
|
+
backendMessage: messages[0] ?? "",
|
|
660
|
+
initialResult: summarizeStatus(initial),
|
|
661
|
+
remediation: "failed_missing_member_or_payload",
|
|
662
|
+
finalResult: summarizeStatus(initial)
|
|
663
|
+
});
|
|
664
|
+
validationRemediationFailures += 1;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const attemptedDates = [];
|
|
669
|
+
let finalResult = initial;
|
|
670
|
+
let remediation = "retry_create_with_next_date";
|
|
671
|
+
for (let offset = 1; offset <= 7; offset += 1) {
|
|
672
|
+
const candidateDate = isoDatePlusDays(baseDate, offset);
|
|
673
|
+
if (!candidateDate) continue;
|
|
674
|
+
attemptedDates.push(candidateDate);
|
|
675
|
+
const nextPayload = {
|
|
676
|
+
stand_up: {
|
|
677
|
+
...standUpBody,
|
|
678
|
+
completed_date: candidateDate
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
const retryArgs = withUpdatedDataJson(initial.args, nextPayload);
|
|
682
|
+
const retryResult = runWave(retryArgs, env);
|
|
683
|
+
results.push(retryResult);
|
|
684
|
+
finalResult = retryResult;
|
|
685
|
+
if (retryResult.parsed?.ok === true) {
|
|
686
|
+
remediation = `retry_create_with_next_date_success base=${baseDate} chosen=${candidateDate}`;
|
|
687
|
+
break;
|
|
688
|
+
}
|
|
689
|
+
const retryMessages = extractValidationMessages(retryResult);
|
|
690
|
+
const stillDuplicate = retryMessages.some((msg) =>
|
|
691
|
+
/completed date.*already been taken/i.test(msg)
|
|
692
|
+
);
|
|
693
|
+
if (!stillDuplicate) {
|
|
694
|
+
remediation = `retry_create_with_next_date_stopped_non_duplicate_error at=${candidateDate}`;
|
|
695
|
+
break;
|
|
696
|
+
}
|
|
697
|
+
remediation = `retry_create_with_next_date_exhausting base=${baseDate}`;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
recordValidationRemediation({
|
|
701
|
+
operation,
|
|
702
|
+
offendingFields: ["stand_up.completed_date"],
|
|
703
|
+
backendMessage: messages[0] ?? "",
|
|
704
|
+
initialResult: summarizeStatus(initial),
|
|
705
|
+
remediation: `${remediation};attempted_dates=${attemptedDates.join(",")}`,
|
|
706
|
+
finalResult: summarizeStatus(finalResult)
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
if (finalResult.parsed?.ok !== true) {
|
|
710
|
+
validationRemediationFailures += 1;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
function remediateFeedbacksUpdateDuplicateName() {
|
|
715
|
+
const operation = "feedbacks.update";
|
|
716
|
+
const initial = findFirstResultByPrefix(results, ["feedbacks", "update"]);
|
|
717
|
+
if (!initial) {
|
|
718
|
+
recordValidationRemediation({
|
|
719
|
+
operation,
|
|
720
|
+
offendingFields: ["feedback.name"],
|
|
721
|
+
backendMessage: "initial probe missing",
|
|
722
|
+
initialResult: "missing",
|
|
723
|
+
remediation: "none",
|
|
724
|
+
finalResult: "missing"
|
|
725
|
+
});
|
|
726
|
+
validationRemediationFailures += 1;
|
|
727
|
+
return;
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
const messages = extractValidationMessages(initial);
|
|
731
|
+
const isDuplicateNameValidation =
|
|
732
|
+
isValidationErrorResult(initial) &&
|
|
733
|
+
messages.some((msg) => /name has already been taken/i.test(msg));
|
|
734
|
+
|
|
735
|
+
if (!isDuplicateNameValidation) {
|
|
736
|
+
recordValidationRemediation({
|
|
737
|
+
operation,
|
|
738
|
+
offendingFields: ["feedback.name"],
|
|
739
|
+
backendMessage: messages[0] ?? "",
|
|
740
|
+
initialResult: summarizeStatus(initial),
|
|
741
|
+
remediation: "none_not_required",
|
|
742
|
+
finalResult: summarizeStatus(initial)
|
|
743
|
+
});
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
const initialPayload = parseDataJsonFromArgs(initial.args);
|
|
748
|
+
const feedbackBody =
|
|
749
|
+
initialPayload && typeof initialPayload.feedback === "object" ? initialPayload.feedback : null;
|
|
750
|
+
const originalName =
|
|
751
|
+
feedbackBody && typeof feedbackBody.name === "string" ? feedbackBody.name : "feedback";
|
|
752
|
+
|
|
753
|
+
if (!feedbackBody) {
|
|
754
|
+
recordValidationRemediation({
|
|
755
|
+
operation,
|
|
756
|
+
offendingFields: ["feedback.name"],
|
|
757
|
+
backendMessage: messages[0] ?? "",
|
|
758
|
+
initialResult: summarizeStatus(initial),
|
|
759
|
+
remediation: "failed_missing_feedback_payload",
|
|
760
|
+
finalResult: summarizeStatus(initial)
|
|
761
|
+
});
|
|
762
|
+
validationRemediationFailures += 1;
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
const feedbackId = getArgValue(initial.args, "--id");
|
|
767
|
+
if (!feedbackId) {
|
|
768
|
+
recordValidationRemediation({
|
|
769
|
+
operation,
|
|
770
|
+
offendingFields: ["feedback.name", "feedback.year"],
|
|
771
|
+
backendMessage: messages[0] ?? "",
|
|
772
|
+
initialResult: summarizeStatus(initial),
|
|
773
|
+
remediation: "failed_missing_feedback_id",
|
|
774
|
+
finalResult: summarizeStatus(initial)
|
|
775
|
+
});
|
|
776
|
+
validationRemediationFailures += 1;
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const showArgs = ["feedbacks", "show", "--id", String(feedbackId)];
|
|
781
|
+
const showResult = runWave(showArgs, env);
|
|
782
|
+
results.push(showResult);
|
|
783
|
+
|
|
784
|
+
const currentName = firstDefinedValue(showResult?.parsed, [
|
|
785
|
+
["data", "feedback", "attributes", "name"],
|
|
786
|
+
["data", "feedback", "name"]
|
|
787
|
+
]);
|
|
788
|
+
const currentQuarter = firstDefinedValue(showResult?.parsed, [
|
|
789
|
+
["data", "feedback", "attributes", "quarter"],
|
|
790
|
+
["data", "feedback", "quarter"]
|
|
791
|
+
]);
|
|
792
|
+
const currentYear = firstDefinedValue(showResult?.parsed, [
|
|
793
|
+
["data", "feedback", "attributes", "year"],
|
|
794
|
+
["data", "feedback", "year"]
|
|
795
|
+
]);
|
|
796
|
+
|
|
797
|
+
if (!showResult.parsed?.ok || !currentName || !currentQuarter) {
|
|
798
|
+
recordValidationRemediation({
|
|
799
|
+
operation,
|
|
800
|
+
offendingFields: ["feedback.name", "feedback.year"],
|
|
801
|
+
backendMessage: messages[0] ?? "",
|
|
802
|
+
initialResult: summarizeStatus(initial),
|
|
803
|
+
remediation: `failed_feedback_show_lookup id=${feedbackId};lookup=${summarizeStatus(showResult)}`,
|
|
804
|
+
finalResult: summarizeStatus(initial)
|
|
805
|
+
});
|
|
806
|
+
validationRemediationFailures += 1;
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
const parsedYear = Number.parseInt(String(currentYear ?? ""), 10);
|
|
811
|
+
const remediatedYear = Number.isFinite(parsedYear) ? parsedYear + 1 : 2030;
|
|
812
|
+
const nextPayload = {
|
|
813
|
+
feedback: {
|
|
814
|
+
...feedbackBody,
|
|
815
|
+
name: String(currentName),
|
|
816
|
+
quarter: String(currentQuarter),
|
|
817
|
+
year: String(remediatedYear)
|
|
818
|
+
}
|
|
819
|
+
};
|
|
820
|
+
const retryArgs = withUpdatedDataJson(initial.args, nextPayload);
|
|
821
|
+
const finalResult = runWave(retryArgs, env);
|
|
822
|
+
results.push(finalResult);
|
|
823
|
+
|
|
824
|
+
recordValidationRemediation({
|
|
825
|
+
operation,
|
|
826
|
+
offendingFields: ["feedback.name", "feedback.year"],
|
|
827
|
+
backendMessage: messages[0] ?? "",
|
|
828
|
+
initialResult: summarizeStatus(initial),
|
|
829
|
+
remediation:
|
|
830
|
+
`fallback_non_unique_field original_name=${originalName}` +
|
|
831
|
+
` remediated_name=${String(currentName)}` +
|
|
832
|
+
` remediated_quarter=${String(currentQuarter)}` +
|
|
833
|
+
` remediated_year=${String(remediatedYear)}`,
|
|
834
|
+
finalResult: summarizeStatus(finalResult)
|
|
835
|
+
});
|
|
836
|
+
|
|
837
|
+
if (finalResult.parsed?.ok !== true) {
|
|
838
|
+
validationRemediationFailures += 1;
|
|
839
|
+
} else {
|
|
840
|
+
feedbackQuarterExpected = String(currentQuarter);
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
function remediateQuarterlyObjectiveParentMismatch() {
|
|
845
|
+
const operation = "foundation.quarterly-objectives.create";
|
|
846
|
+
const initial = findFirstResultByPrefix(results, [
|
|
847
|
+
"foundation",
|
|
848
|
+
"quarterly-objectives",
|
|
849
|
+
"create"
|
|
850
|
+
]);
|
|
851
|
+
if (!initial) {
|
|
852
|
+
recordValidationRemediation({
|
|
853
|
+
operation,
|
|
854
|
+
offendingFields: ["quarterly_objective.annual_objective_id", "quarterly_objective.strategic_objective_id"],
|
|
855
|
+
backendMessage: "initial probe missing",
|
|
856
|
+
initialResult: "missing",
|
|
857
|
+
remediation: "none",
|
|
858
|
+
finalResult: "missing"
|
|
859
|
+
});
|
|
860
|
+
validationRemediationFailures += 1;
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
const messages = extractValidationMessages(initial);
|
|
865
|
+
const isParentMismatchValidation =
|
|
866
|
+
isValidationErrorResult(initial) &&
|
|
867
|
+
messages.some((msg) =>
|
|
868
|
+
/belongs to strategic_objective_id|matching strategic_objective_id/i.test(msg)
|
|
869
|
+
);
|
|
870
|
+
|
|
871
|
+
if (!isParentMismatchValidation) {
|
|
872
|
+
recordValidationRemediation({
|
|
873
|
+
operation,
|
|
874
|
+
offendingFields: ["quarterly_objective.annual_objective_id", "quarterly_objective.strategic_objective_id"],
|
|
875
|
+
backendMessage: messages[0] ?? "",
|
|
876
|
+
initialResult: summarizeStatus(initial),
|
|
877
|
+
remediation: "none_not_required",
|
|
878
|
+
finalResult: summarizeStatus(initial)
|
|
879
|
+
});
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
const initialPayload = parseDataJsonFromArgs(initial.args);
|
|
884
|
+
const quarterlyBody =
|
|
885
|
+
initialPayload && typeof initialPayload.quarterly_objective === "object"
|
|
886
|
+
? initialPayload.quarterly_objective
|
|
887
|
+
: null;
|
|
888
|
+
const annualId =
|
|
889
|
+
quarterlyBody && typeof quarterlyBody.annual_objective_id === "string"
|
|
890
|
+
? quarterlyBody.annual_objective_id
|
|
891
|
+
: "";
|
|
892
|
+
|
|
893
|
+
if (!quarterlyBody || annualId.length === 0) {
|
|
894
|
+
recordValidationRemediation({
|
|
895
|
+
operation,
|
|
896
|
+
offendingFields: ["quarterly_objective.annual_objective_id", "quarterly_objective.strategic_objective_id"],
|
|
897
|
+
backendMessage: messages[0] ?? "",
|
|
898
|
+
initialResult: summarizeStatus(initial),
|
|
899
|
+
remediation: "failed_missing_annual_objective_id",
|
|
900
|
+
finalResult: summarizeStatus(initial)
|
|
901
|
+
});
|
|
902
|
+
validationRemediationFailures += 1;
|
|
903
|
+
return;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
const annualShowArgs = ["foundation", "annual-objectives", "show", "--id", annualId];
|
|
907
|
+
const annualShowResult = runWave(annualShowArgs, env);
|
|
908
|
+
results.push(annualShowResult);
|
|
909
|
+
const resolvedStrategicObjectiveId = resolveAnnualStrategicObjectiveId(annualShowResult);
|
|
910
|
+
if (!annualShowResult.parsed?.ok || !resolvedStrategicObjectiveId) {
|
|
911
|
+
recordValidationRemediation({
|
|
912
|
+
operation,
|
|
913
|
+
offendingFields: ["quarterly_objective.annual_objective_id", "quarterly_objective.strategic_objective_id"],
|
|
914
|
+
backendMessage: messages[0] ?? "",
|
|
915
|
+
initialResult: summarizeStatus(initial),
|
|
916
|
+
remediation: `failed_parent_lookup annual_id=${annualId};lookup=${summarizeStatus(annualShowResult)}`,
|
|
917
|
+
finalResult: summarizeStatus(initial)
|
|
918
|
+
});
|
|
919
|
+
validationRemediationFailures += 1;
|
|
920
|
+
return;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
const nextPayload = {
|
|
924
|
+
quarterly_objective: {
|
|
925
|
+
...quarterlyBody,
|
|
926
|
+
strategic_objective_id: String(resolvedStrategicObjectiveId),
|
|
927
|
+
annual_objective_id: annualId
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
const retryArgs = withUpdatedDataJson(initial.args, nextPayload);
|
|
931
|
+
const retryResult = runWave(retryArgs, env);
|
|
932
|
+
results.push(retryResult);
|
|
933
|
+
|
|
934
|
+
recordValidationRemediation({
|
|
935
|
+
operation,
|
|
936
|
+
offendingFields: ["quarterly_objective.annual_objective_id", "quarterly_objective.strategic_objective_id"],
|
|
937
|
+
backendMessage: messages[0] ?? "",
|
|
938
|
+
initialResult: summarizeStatus(initial),
|
|
939
|
+
remediation: `parent_match annual_id=${annualId} strategic_objective_id=${String(resolvedStrategicObjectiveId)}`,
|
|
940
|
+
finalResult: summarizeStatus(retryResult)
|
|
941
|
+
});
|
|
942
|
+
|
|
943
|
+
if (retryResult.parsed?.ok !== true) {
|
|
944
|
+
validationRemediationFailures += 1;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
remediateStandupsCreateDuplicateDate();
|
|
949
|
+
remediateFeedbacksUpdateDuplicateName();
|
|
950
|
+
remediateQuarterlyObjectiveParentMismatch();
|
|
951
|
+
|
|
952
|
+
let deleteVerifyLine = "";
|
|
953
|
+
let deleteVerifyFailure = 0;
|
|
954
|
+
if (issueId) {
|
|
955
|
+
const subissueCreate = runWave(
|
|
956
|
+
[
|
|
957
|
+
"subissues",
|
|
958
|
+
"create",
|
|
959
|
+
"--data-json",
|
|
960
|
+
payload("sub_issue", {
|
|
961
|
+
issue_id: issueId,
|
|
962
|
+
name: `CLI Verify Subissue ${Date.now()}`
|
|
963
|
+
})
|
|
964
|
+
],
|
|
965
|
+
env
|
|
966
|
+
);
|
|
967
|
+
results.push(subissueCreate);
|
|
968
|
+
const subIssueId = subissueCreate?.parsed?.data?.create_sub_issue?.data?.data?.id
|
|
969
|
+
? String(subissueCreate.parsed.data.create_sub_issue.data.data.id)
|
|
970
|
+
: "";
|
|
971
|
+
if (subIssueId) {
|
|
972
|
+
const destroyRes = runWave(["subissues", "destroy", "--id", subIssueId], env);
|
|
973
|
+
results.push(destroyRes);
|
|
974
|
+
const verifyRes = verifySubissueDeleteWithPolling(env, issueId, subIssueId);
|
|
975
|
+
deleteVerifyLine = `${verifyRes.ok ? "PASS" : "FAIL"} | status=${verifyRes.ok ? 200 : 500} | err=${
|
|
976
|
+
verifyRes.ok ? "none" : "delete_not_observed"
|
|
977
|
+
} | subissues destroy verify --issue-id ${issueId} --id ${subIssueId} --tries ${verifyRes.tries}`;
|
|
978
|
+
if (!verifyRes.ok) {
|
|
979
|
+
deleteVerifyFailure = 1;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
const postUpdateVerificationLines = [];
|
|
985
|
+
let postUpdateVerificationFailures = 0;
|
|
986
|
+
const osmdVerificationLines = [];
|
|
987
|
+
let osmdVerificationFailures = 0;
|
|
988
|
+
|
|
989
|
+
function hasSuccessfulCommand(commandPrefix) {
|
|
990
|
+
return results.some((res) => {
|
|
991
|
+
if (!Array.isArray(res.args)) return false;
|
|
992
|
+
if (res.args.length < commandPrefix.length) return false;
|
|
993
|
+
for (let i = 0; i < commandPrefix.length; i += 1) {
|
|
994
|
+
if (res.args[i] !== commandPrefix[i]) {
|
|
995
|
+
return false;
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
return res.parsed?.ok === true;
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
function verifyFieldWrite({ label, showArgs, expectedValue, candidatePaths, requiredSuccessPrefix }) {
|
|
1003
|
+
if (requiredSuccessPrefix && !hasSuccessfulCommand(requiredSuccessPrefix)) {
|
|
1004
|
+
postUpdateVerificationLines.push(`PASS | status=n/a | err=skipped_prereq_not_ok | ${label}`);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
if (showArgs.includes(undefined)) {
|
|
1009
|
+
postUpdateVerificationLines.push(`PASS | status=n/a | err=skipped_missing_id | ${label}`);
|
|
1010
|
+
return;
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
const showRes = runWave(showArgs, env);
|
|
1014
|
+
const status = showRes.parsed?.status ?? "n/a";
|
|
1015
|
+
const errCode = showRes.parsed?.error?.code ?? "none";
|
|
1016
|
+
|
|
1017
|
+
if (!showRes.parsed?.ok) {
|
|
1018
|
+
postUpdateVerificationLines.push(
|
|
1019
|
+
`FAIL | status=${status} | err=${errCode} | ${label} (${showArgs.join(" ")})`
|
|
1020
|
+
);
|
|
1021
|
+
postUpdateVerificationFailures += 1;
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
const actual = firstDefinedValue(showRes.parsed, candidatePaths);
|
|
1026
|
+
const ok = String(actual ?? "") === String(expectedValue);
|
|
1027
|
+
postUpdateVerificationLines.push(
|
|
1028
|
+
`${ok ? "PASS" : "FAIL"} | status=${status} | err=${
|
|
1029
|
+
ok ? "none" : "read_after_write_mismatch"
|
|
1030
|
+
} | ${label} expected=${JSON.stringify(expectedValue)} actual=${JSON.stringify(actual ?? null)}`
|
|
1031
|
+
);
|
|
1032
|
+
if (!ok) {
|
|
1033
|
+
postUpdateVerificationFailures += 1;
|
|
1034
|
+
}
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
verifyFieldWrite({
|
|
1038
|
+
label: "organizations.update verify workspace_name",
|
|
1039
|
+
showArgs: ["organizations", "show", "--id", orgId],
|
|
1040
|
+
expectedValue: orgWorkspaceNameVerify,
|
|
1041
|
+
requiredSuccessPrefix: ["organizations", "update"],
|
|
1042
|
+
candidatePaths: [
|
|
1043
|
+
["data", "organization", "organizationDetail", "workspaceName"],
|
|
1044
|
+
["data", "organization", "organization_detail", "workspace_name"],
|
|
1045
|
+
["data", "organization", "attributes", "organizationDetail", "workspaceName"],
|
|
1046
|
+
["data", "organization", "attributes", "organization_detail_attributes", "workspace_name"]
|
|
1047
|
+
]
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
verifyFieldWrite({
|
|
1051
|
+
label: "organizations.meta-profile.update verify one_sentence_summary",
|
|
1052
|
+
showArgs: ["organizations", "meta-profile", "show", "--id", organizationMetaProfileId],
|
|
1053
|
+
expectedValue: orgMetaOneSentenceSummaryVerify,
|
|
1054
|
+
requiredSuccessPrefix: ["organizations", "meta-profile", "update"],
|
|
1055
|
+
candidatePaths: [
|
|
1056
|
+
[
|
|
1057
|
+
"data",
|
|
1058
|
+
"organization_meta_profile",
|
|
1059
|
+
"attributes",
|
|
1060
|
+
"profile",
|
|
1061
|
+
"companyIdentity",
|
|
1062
|
+
"oneSentenceSummary"
|
|
1063
|
+
],
|
|
1064
|
+
[
|
|
1065
|
+
"data",
|
|
1066
|
+
"organization_meta_profile",
|
|
1067
|
+
"attributes",
|
|
1068
|
+
"profile",
|
|
1069
|
+
"company_identity",
|
|
1070
|
+
"one_sentence_summary"
|
|
1071
|
+
],
|
|
1072
|
+
[
|
|
1073
|
+
"data",
|
|
1074
|
+
"organizationMetaProfile",
|
|
1075
|
+
"attributes",
|
|
1076
|
+
"profile",
|
|
1077
|
+
"companyIdentity",
|
|
1078
|
+
"oneSentenceSummary"
|
|
1079
|
+
]
|
|
1080
|
+
]
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
verifyFieldWrite({
|
|
1084
|
+
label: "organizations.key-metric-meta-profile.update verify financial.revenue",
|
|
1085
|
+
showArgs: ["organizations", "key-metric-meta-profile", "show", "--id", keyMetricMetaProfileId],
|
|
1086
|
+
expectedValue: keyMetricRevenueVerify,
|
|
1087
|
+
requiredSuccessPrefix: ["organizations", "key-metric-meta-profile", "update"],
|
|
1088
|
+
candidatePaths: [
|
|
1089
|
+
["data", "key_metric_meta_profile", "attributes", "profile", "financial", "revenue"],
|
|
1090
|
+
["data", "keyMetricMetaProfile", "attributes", "profile", "financial", "revenue"]
|
|
1091
|
+
]
|
|
1092
|
+
});
|
|
1093
|
+
|
|
1094
|
+
verifyFieldWrite({
|
|
1095
|
+
label: "questions.update verify name",
|
|
1096
|
+
showArgs: ["questions", "show", "--id", questionId],
|
|
1097
|
+
expectedValue: questionNameVerify,
|
|
1098
|
+
requiredSuccessPrefix: ["questions", "update"],
|
|
1099
|
+
candidatePaths: [
|
|
1100
|
+
["data", "question", "attributes", "name"],
|
|
1101
|
+
["data", "question", "attributes", "summary"]
|
|
1102
|
+
]
|
|
1103
|
+
});
|
|
1104
|
+
|
|
1105
|
+
verifyFieldWrite({
|
|
1106
|
+
label: "surveys.update verify due_date",
|
|
1107
|
+
showArgs: ["surveys", "show", "--id", surveyId],
|
|
1108
|
+
expectedValue: surveyDueDateVerify,
|
|
1109
|
+
requiredSuccessPrefix: ["surveys", "update"],
|
|
1110
|
+
candidatePaths: [
|
|
1111
|
+
["data", "survey", "attributes", "dueDate"],
|
|
1112
|
+
["data", "survey", "attributes", "due_date"]
|
|
1113
|
+
]
|
|
1114
|
+
});
|
|
1115
|
+
|
|
1116
|
+
verifyFieldWrite({
|
|
1117
|
+
label: "feedbacks.update verify quarter",
|
|
1118
|
+
showArgs: ["feedbacks", "show", "--id", feedbackId],
|
|
1119
|
+
expectedValue: feedbackQuarterExpected,
|
|
1120
|
+
requiredSuccessPrefix: ["feedbacks", "update"],
|
|
1121
|
+
candidatePaths: [["data", "feedback", "attributes", "quarter"]]
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
verifyFieldWrite({
|
|
1125
|
+
label: "accountability.update verify name",
|
|
1126
|
+
showArgs: ["accountability", "show", "--id", responsibilityId],
|
|
1127
|
+
expectedValue: accountabilityNameVerify,
|
|
1128
|
+
requiredSuccessPrefix: ["accountability", "update"],
|
|
1129
|
+
candidatePaths: [["data", "responsibility", "attributes", "name"]]
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1132
|
+
function resolveOsmdAgentParentRef() {
|
|
1133
|
+
const queries = ["Wave Tools", "Ted Martinez", "Ted"];
|
|
1134
|
+
for (const query of queries) {
|
|
1135
|
+
const findRes = runWave(["find", query, "--limit", "10"], env);
|
|
1136
|
+
results.push(findRes);
|
|
1137
|
+
const candidates = firstArrayValue(findRes.parsed, [["data", "candidates"]]);
|
|
1138
|
+
for (const candidate of candidates) {
|
|
1139
|
+
const canonicalPath =
|
|
1140
|
+
candidate && typeof candidate === "object" && typeof candidate.canonicalPath === "string"
|
|
1141
|
+
? candidate.canonicalPath
|
|
1142
|
+
: "";
|
|
1143
|
+
if (!canonicalPath) continue;
|
|
1144
|
+
|
|
1145
|
+
const statusRes = runWave(["osmd", "status", canonicalPath], env);
|
|
1146
|
+
results.push(statusRes);
|
|
1147
|
+
if (
|
|
1148
|
+
statusRes.parsed?.ok === true &&
|
|
1149
|
+
statusRes.parsed?.data?.source === "canonical_osmd" &&
|
|
1150
|
+
statusRes.parsed?.data?.exists === true &&
|
|
1151
|
+
statusRes.parsed?.data?.agentChildrenAllowed === true
|
|
1152
|
+
) {
|
|
1153
|
+
return String(statusRes.parsed.data.path || canonicalPath);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
function runOsmdLiveVerification() {
|
|
1161
|
+
const treeDepthZero = runWave(["osmd", "tree", "--depth", "0"], env);
|
|
1162
|
+
results.push(treeDepthZero);
|
|
1163
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1164
|
+
lines: osmdVerificationLines,
|
|
1165
|
+
label: "osmd.tree depth 0 returns metadata and normalized empty children",
|
|
1166
|
+
result: treeDepthZero,
|
|
1167
|
+
ok:
|
|
1168
|
+
treeDepthZero.parsed?.ok === true &&
|
|
1169
|
+
treeDepthZero.parsed?.data?.source === "canonical_osmd" &&
|
|
1170
|
+
Array.isArray(treeDepthZero.parsed?.data?.children) &&
|
|
1171
|
+
treeDepthZero.parsed.data.children.length === 0,
|
|
1172
|
+
reason: "depth_zero_contract"
|
|
1173
|
+
});
|
|
1174
|
+
|
|
1175
|
+
const treeDepthOne = runWave(["osmd", "tree", "--depth", "1"], env);
|
|
1176
|
+
results.push(treeDepthOne);
|
|
1177
|
+
const treeChildren = Array.isArray(treeDepthOne.parsed?.data?.children)
|
|
1178
|
+
? treeDepthOne.parsed.data.children
|
|
1179
|
+
: [];
|
|
1180
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1181
|
+
lines: osmdVerificationLines,
|
|
1182
|
+
label: "osmd.tree depth 1 returns organization root and restored display folders",
|
|
1183
|
+
result: treeDepthOne,
|
|
1184
|
+
ok:
|
|
1185
|
+
treeDepthOne.parsed?.ok === true &&
|
|
1186
|
+
treeDepthOne.parsed?.data?.source === "canonical_osmd" &&
|
|
1187
|
+
String(treeDepthOne.parsed?.data?.runtimeLabel ?? "").endsWith(".MD") &&
|
|
1188
|
+
String(treeDepthOne.parsed?.data?.path ?? "").endsWith(".MD") &&
|
|
1189
|
+
JSON.stringify(treeChildren.map((child) => child?.runtimeLabel)) ===
|
|
1190
|
+
JSON.stringify(["Org", "Members", "Teams", "Tools", "Agent Wiki"]) &&
|
|
1191
|
+
JSON.stringify(treeChildren.map((child) => child?.toolKey)) ===
|
|
1192
|
+
JSON.stringify(["org", "directory", "directory", "osmd_display", "agent_wiki"]) &&
|
|
1193
|
+
JSON.stringify(treeChildren.map((child) => child?.nodeKey)) ===
|
|
1194
|
+
JSON.stringify(["root", "members", "teams", "tools", "root"]),
|
|
1195
|
+
reason: "display_root_contract"
|
|
1196
|
+
});
|
|
1197
|
+
|
|
1198
|
+
const wikiStatus = runWave(["osmd", "wiki", "status", "index.md"], env);
|
|
1199
|
+
results.push(wikiStatus);
|
|
1200
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1201
|
+
lines: osmdVerificationLines,
|
|
1202
|
+
label: "osmd.wiki status index",
|
|
1203
|
+
result: wikiStatus,
|
|
1204
|
+
ok:
|
|
1205
|
+
wikiStatus.parsed?.ok === true &&
|
|
1206
|
+
wikiStatus.parsed?.data?.path === "Agent Wiki/index.md" &&
|
|
1207
|
+
wikiStatus.parsed?.data?.source === "agent_wiki" &&
|
|
1208
|
+
wikiStatus.parsed?.data?.wikiRole === "index" &&
|
|
1209
|
+
typeof wikiStatus.parsed?.data?.formatVersion === "string" &&
|
|
1210
|
+
(wikiStatus.parsed?.data?.exists !== false ||
|
|
1211
|
+
(wikiStatus.parsed?.data?.canCreate === true &&
|
|
1212
|
+
wikiStatus.parsed?.data?.canUpdate === false &&
|
|
1213
|
+
wikiStatus.parsed?.data?.suggestedAction === "create")),
|
|
1214
|
+
reason: "agent_wiki_index_status"
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
const wikiChildren = runWave(["osmd", "wiki", "children"], env);
|
|
1218
|
+
results.push(wikiChildren);
|
|
1219
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1220
|
+
lines: osmdVerificationLines,
|
|
1221
|
+
label: "osmd.wiki children returns an array",
|
|
1222
|
+
result: wikiChildren,
|
|
1223
|
+
ok: wikiChildren.parsed?.ok === true && Array.isArray(wikiChildren.parsed?.data),
|
|
1224
|
+
reason: "agent_wiki_children"
|
|
1225
|
+
});
|
|
1226
|
+
|
|
1227
|
+
const wikiLogStatus = runWave(["osmd", "wiki", "status", "log.md"], env);
|
|
1228
|
+
results.push(wikiLogStatus);
|
|
1229
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1230
|
+
lines: osmdVerificationLines,
|
|
1231
|
+
label: "osmd.wiki status append-only log",
|
|
1232
|
+
result: wikiLogStatus,
|
|
1233
|
+
ok:
|
|
1234
|
+
wikiLogStatus.parsed?.ok === true &&
|
|
1235
|
+
wikiLogStatus.parsed?.data?.path === "Agent Wiki/log.md" &&
|
|
1236
|
+
wikiLogStatus.parsed?.data?.source === "agent_wiki" &&
|
|
1237
|
+
wikiLogStatus.parsed?.data?.access === "append_only" &&
|
|
1238
|
+
wikiLogStatus.parsed?.data?.wikiRole === "log" &&
|
|
1239
|
+
typeof wikiLogStatus.parsed?.data?.formatVersion === "string" &&
|
|
1240
|
+
typeof wikiLogStatus.parsed?.data?.canAppend === "boolean",
|
|
1241
|
+
reason: "agent_wiki_log_status"
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
const wikiLogRead = runWave(["osmd", "wiki", "read", "Agent Wiki/log.md"], env);
|
|
1245
|
+
results.push(wikiLogRead);
|
|
1246
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1247
|
+
lines: osmdVerificationLines,
|
|
1248
|
+
label: "osmd.wiki read append-only log does not create",
|
|
1249
|
+
result: wikiLogRead,
|
|
1250
|
+
ok:
|
|
1251
|
+
wikiLogRead.parsed?.ok === true &&
|
|
1252
|
+
wikiLogRead.parsed?.data?.path === "Agent Wiki/log.md" &&
|
|
1253
|
+
wikiLogRead.parsed?.data?.access === "append_only" &&
|
|
1254
|
+
wikiLogRead.parsed?.data?.wikiRole === "log" &&
|
|
1255
|
+
typeof wikiLogRead.parsed?.data?.formatVersion === "string" &&
|
|
1256
|
+
wikiLogRead.parsed?.data?.exists === wikiLogStatus.parsed?.data?.exists,
|
|
1257
|
+
reason: "agent_wiki_log_read"
|
|
1258
|
+
});
|
|
1259
|
+
|
|
1260
|
+
if (wikiLogStatus.parsed?.data?.exists === false) {
|
|
1261
|
+
const missingWikiLogAppend = runWave(
|
|
1262
|
+
["osmd", "wiki", "append-log", "--content", `CLI live verify log ${verifyStamp}`],
|
|
1263
|
+
env
|
|
1264
|
+
);
|
|
1265
|
+
results.push(missingWikiLogAppend);
|
|
1266
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1267
|
+
lines: osmdVerificationLines,
|
|
1268
|
+
label: "osmd.wiki append-log missing log returns init guidance",
|
|
1269
|
+
result: missingWikiLogAppend,
|
|
1270
|
+
ok:
|
|
1271
|
+
missingWikiLogAppend.parsed?.ok === false &&
|
|
1272
|
+
missingWikiLogAppend.parsed?.status === 404 &&
|
|
1273
|
+
missingWikiLogAppend.parsed?.error?.code === "agent_file_not_found" &&
|
|
1274
|
+
missingWikiLogAppend.parsed?.error?.suggestedAction === "init" &&
|
|
1275
|
+
String(missingWikiLogAppend.parsed?.error?.message ?? "").includes("wave osmd wiki init") &&
|
|
1276
|
+
missingWikiLogAppend.parsed?.data?.path === "Agent Wiki/log.md" &&
|
|
1277
|
+
missingWikiLogAppend.parsed?.data?.wikiRole === "log" &&
|
|
1278
|
+
typeof missingWikiLogAppend.parsed?.data?.formatVersion === "string" &&
|
|
1279
|
+
missingWikiLogAppend.parsed?.data?.canAppend === false,
|
|
1280
|
+
reason: "agent_wiki_log_missing_init_guidance"
|
|
1281
|
+
});
|
|
1282
|
+
} else {
|
|
1283
|
+
osmdVerificationLines.push(
|
|
1284
|
+
"PASS | status=n/a | err=skipped_existing_log | reason=missing_log_init_guidance_skipped | osmd.wiki append-log missing log returns init guidance"
|
|
1285
|
+
);
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
const wikiInit = runWave(["osmd", "wiki", "init"], env);
|
|
1289
|
+
results.push(wikiInit);
|
|
1290
|
+
const wikiInitFiles = firstArrayValue(wikiInit.parsed, [["data", "files"]]);
|
|
1291
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1292
|
+
lines: osmdVerificationLines,
|
|
1293
|
+
label: "osmd.wiki init creates Agent Wiki system files",
|
|
1294
|
+
result: wikiInit,
|
|
1295
|
+
ok:
|
|
1296
|
+
wikiInit.parsed?.ok === true &&
|
|
1297
|
+
wikiInitFiles.some(
|
|
1298
|
+
(file) =>
|
|
1299
|
+
file?.path === "Agent Wiki/log.md" &&
|
|
1300
|
+
file?.access === "append_only" &&
|
|
1301
|
+
file?.wikiRole === "log" &&
|
|
1302
|
+
typeof file?.formatVersion === "string" &&
|
|
1303
|
+
file?.exists === true &&
|
|
1304
|
+
file?.canAppend === true
|
|
1305
|
+
),
|
|
1306
|
+
reason: "agent_wiki_init"
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
const wikiLogAppend = runWave(
|
|
1310
|
+
["osmd", "wiki", "append-log", "--content", `CLI live verify log ${verifyStamp}`],
|
|
1311
|
+
env
|
|
1312
|
+
);
|
|
1313
|
+
results.push(wikiLogAppend);
|
|
1314
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1315
|
+
lines: osmdVerificationLines,
|
|
1316
|
+
label: "osmd.wiki append-log succeeds after init",
|
|
1317
|
+
result: wikiLogAppend,
|
|
1318
|
+
ok:
|
|
1319
|
+
wikiLogAppend.parsed?.ok === true &&
|
|
1320
|
+
wikiLogAppend.parsed?.data?.path === "Agent Wiki/log.md" &&
|
|
1321
|
+
wikiLogAppend.parsed?.data?.access === "append_only" &&
|
|
1322
|
+
wikiLogAppend.parsed?.data?.wikiRole === "log" &&
|
|
1323
|
+
typeof wikiLogAppend.parsed?.data?.formatVersion === "string" &&
|
|
1324
|
+
wikiLogAppend.parsed?.data?.canAppend === true,
|
|
1325
|
+
reason: "agent_wiki_log_append_after_init"
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
if (wikiStatus.parsed?.data?.exists === false) {
|
|
1329
|
+
const missingWikiUpdate = runWave(
|
|
1330
|
+
["osmd", "wiki", "update", "index.md", "--content", "verify missing update"],
|
|
1331
|
+
env
|
|
1332
|
+
);
|
|
1333
|
+
results.push(missingWikiUpdate);
|
|
1334
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1335
|
+
lines: osmdVerificationLines,
|
|
1336
|
+
label: "osmd.wiki update missing file returns 404 metadata",
|
|
1337
|
+
result: missingWikiUpdate,
|
|
1338
|
+
ok:
|
|
1339
|
+
missingWikiUpdate.code === 5 &&
|
|
1340
|
+
missingWikiUpdate.parsed?.ok === false &&
|
|
1341
|
+
missingWikiUpdate.parsed?.status === 404 &&
|
|
1342
|
+
missingWikiUpdate.parsed?.error?.code === "agent_file_not_found" &&
|
|
1343
|
+
missingWikiUpdate.parsed?.error?.suggestedAction === "create" &&
|
|
1344
|
+
missingWikiUpdate.parsed?.data?.exists === false &&
|
|
1345
|
+
missingWikiUpdate.parsed?.data?.canCreate === true &&
|
|
1346
|
+
missingWikiUpdate.parsed?.data?.canUpdate === false &&
|
|
1347
|
+
missingWikiUpdate.parsed?.data?.suggestedAction === "create",
|
|
1348
|
+
reason: "missing_file_404"
|
|
1349
|
+
});
|
|
1350
|
+
} else {
|
|
1351
|
+
osmdVerificationLines.push(
|
|
1352
|
+
"PASS | status=n/a | err=skipped_existing_index | reason=missing_file_404_skipped | osmd.wiki update missing file returns 404 metadata"
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1356
|
+
const controlledWikiSearchTerm = `CLI live controlled wiki ${verifyStamp}`;
|
|
1357
|
+
const controlledWikiPages = [
|
|
1358
|
+
{
|
|
1359
|
+
path: `concepts/cli-live-concept-${verifyStamp}.md`,
|
|
1360
|
+
fullPath: `Agent Wiki/concepts/cli-live-concept-${verifyStamp}.md`,
|
|
1361
|
+
wikiRole: "concept",
|
|
1362
|
+
title: "concept"
|
|
1363
|
+
},
|
|
1364
|
+
{
|
|
1365
|
+
path: `playbooks/cli-live-playbook-${verifyStamp}.md`,
|
|
1366
|
+
fullPath: `Agent Wiki/playbooks/cli-live-playbook-${verifyStamp}.md`,
|
|
1367
|
+
wikiRole: "playbook",
|
|
1368
|
+
title: "playbook"
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
path: `decisions/cli-live-decision-${verifyStamp}.md`,
|
|
1372
|
+
fullPath: `Agent Wiki/decisions/cli-live-decision-${verifyStamp}.md`,
|
|
1373
|
+
wikiRole: "decision",
|
|
1374
|
+
title: "decision"
|
|
1375
|
+
},
|
|
1376
|
+
{
|
|
1377
|
+
path: `org-summaries/cli-live-org-summary-${verifyStamp}.md`,
|
|
1378
|
+
fullPath: `Agent Wiki/org-summaries/cli-live-org-summary-${verifyStamp}.md`,
|
|
1379
|
+
wikiRole: "org_summary",
|
|
1380
|
+
title: "org_summary"
|
|
1381
|
+
}
|
|
1382
|
+
];
|
|
1383
|
+
|
|
1384
|
+
for (const page of controlledWikiPages) {
|
|
1385
|
+
const controlledStatus = runWave(["osmd", "wiki", "status", page.path], env);
|
|
1386
|
+
results.push(controlledStatus);
|
|
1387
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1388
|
+
lines: osmdVerificationLines,
|
|
1389
|
+
label: `osmd.wiki status controlled ${page.title}`,
|
|
1390
|
+
result: controlledStatus,
|
|
1391
|
+
ok:
|
|
1392
|
+
controlledStatus.parsed?.ok === true &&
|
|
1393
|
+
controlledStatus.parsed?.data?.path === page.fullPath &&
|
|
1394
|
+
controlledStatus.parsed?.data?.source === "agent_wiki" &&
|
|
1395
|
+
controlledStatus.parsed?.data?.access === "read_write" &&
|
|
1396
|
+
controlledStatus.parsed?.data?.wikiRole === page.wikiRole &&
|
|
1397
|
+
controlledStatus.parsed?.data?.formatVersion === "agent_markdown_v1" &&
|
|
1398
|
+
controlledStatus.parsed?.data?.exists === false &&
|
|
1399
|
+
controlledStatus.parsed?.data?.canCreate === true &&
|
|
1400
|
+
controlledStatus.parsed?.data?.canUpdate === false &&
|
|
1401
|
+
controlledStatus.parsed?.data?.canAppend === false &&
|
|
1402
|
+
controlledStatus.parsed?.data?.suggestedAction === "create",
|
|
1403
|
+
reason: "agent_wiki_controlled_status"
|
|
1404
|
+
});
|
|
1405
|
+
|
|
1406
|
+
const createContent = `# CLI Live ${page.title}\n\n${controlledWikiSearchTerm} create ${page.title}`;
|
|
1407
|
+
const controlledCreate = runWave(["osmd", "wiki", "create", page.path, "--content", createContent], env);
|
|
1408
|
+
results.push(controlledCreate);
|
|
1409
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1410
|
+
lines: osmdVerificationLines,
|
|
1411
|
+
label: `osmd.wiki create controlled ${page.title}`,
|
|
1412
|
+
result: controlledCreate,
|
|
1413
|
+
ok:
|
|
1414
|
+
controlledCreate.parsed?.ok === true &&
|
|
1415
|
+
controlledCreate.parsed?.data?.path === page.fullPath &&
|
|
1416
|
+
controlledCreate.parsed?.data?.source === "agent_wiki" &&
|
|
1417
|
+
controlledCreate.parsed?.data?.wikiRole === page.wikiRole &&
|
|
1418
|
+
controlledCreate.parsed?.data?.formatVersion === "agent_markdown_v1" &&
|
|
1419
|
+
controlledCreate.parsed?.data?.exists === true &&
|
|
1420
|
+
controlledCreate.parsed?.data?.canUpdate === true &&
|
|
1421
|
+
controlledCreate.parsed?.data?.canAppend === false &&
|
|
1422
|
+
controlledCreate.parsed?.data?.content === createContent,
|
|
1423
|
+
reason: "agent_wiki_controlled_create"
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
const updateContent = `# CLI Live ${page.title}\n\n${controlledWikiSearchTerm} update ${page.title}`;
|
|
1427
|
+
const controlledUpdate = runWave(["osmd", "wiki", "update", page.path, "--content", updateContent], env);
|
|
1428
|
+
results.push(controlledUpdate);
|
|
1429
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1430
|
+
lines: osmdVerificationLines,
|
|
1431
|
+
label: `osmd.wiki update controlled ${page.title}`,
|
|
1432
|
+
result: controlledUpdate,
|
|
1433
|
+
ok:
|
|
1434
|
+
controlledUpdate.parsed?.ok === true &&
|
|
1435
|
+
controlledUpdate.parsed?.data?.path === page.fullPath &&
|
|
1436
|
+
controlledUpdate.parsed?.data?.source === "agent_wiki" &&
|
|
1437
|
+
controlledUpdate.parsed?.data?.wikiRole === page.wikiRole &&
|
|
1438
|
+
controlledUpdate.parsed?.data?.formatVersion === "agent_markdown_v1" &&
|
|
1439
|
+
controlledUpdate.parsed?.data?.exists === true &&
|
|
1440
|
+
controlledUpdate.parsed?.data?.canUpdate === true &&
|
|
1441
|
+
controlledUpdate.parsed?.data?.canAppend === false &&
|
|
1442
|
+
controlledUpdate.parsed?.data?.content === updateContent,
|
|
1443
|
+
reason: "agent_wiki_controlled_update"
|
|
1444
|
+
});
|
|
1445
|
+
|
|
1446
|
+
const indexSummary = `CLI live index summary ${verifyStamp} ${page.title}`;
|
|
1447
|
+
const controlledIndexUpsert = runWave(
|
|
1448
|
+
["osmd", "wiki", "index-upsert", page.path, "--summary", indexSummary],
|
|
1449
|
+
env
|
|
1450
|
+
);
|
|
1451
|
+
results.push(controlledIndexUpsert);
|
|
1452
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1453
|
+
lines: osmdVerificationLines,
|
|
1454
|
+
label: `osmd.wiki index-upsert controlled ${page.title}`,
|
|
1455
|
+
result: controlledIndexUpsert,
|
|
1456
|
+
ok:
|
|
1457
|
+
controlledIndexUpsert.parsed?.ok === true &&
|
|
1458
|
+
controlledIndexUpsert.parsed?.data?.path === "Agent Wiki/index.md" &&
|
|
1459
|
+
controlledIndexUpsert.parsed?.data?.source === "agent_wiki" &&
|
|
1460
|
+
controlledIndexUpsert.parsed?.data?.wikiRole === "index" &&
|
|
1461
|
+
controlledIndexUpsert.parsed?.data?.formatVersion === "agent_markdown_v1" &&
|
|
1462
|
+
controlledIndexUpsert.parsed?.data?.exists === true &&
|
|
1463
|
+
controlledIndexUpsert.parsed?.data?.canUpdate === true &&
|
|
1464
|
+
controlledIndexUpsert.parsed?.data?.canAppend === false &&
|
|
1465
|
+
String(controlledIndexUpsert.parsed?.data?.content ?? "").includes(indexSummary),
|
|
1466
|
+
reason: "agent_wiki_controlled_index_upsert"
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
const logIndexUpsertRejected = runWave(
|
|
1471
|
+
["osmd", "wiki", "index-upsert", "log.md", "--summary", "verify forbidden"],
|
|
1472
|
+
env
|
|
1473
|
+
);
|
|
1474
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1475
|
+
lines: osmdVerificationLines,
|
|
1476
|
+
label: "osmd.wiki index-upsert rejects log target",
|
|
1477
|
+
result: logIndexUpsertRejected,
|
|
1478
|
+
ok:
|
|
1479
|
+
logIndexUpsertRejected.parsed?.ok === false &&
|
|
1480
|
+
logIndexUpsertRejected.parsed?.status === 403 &&
|
|
1481
|
+
logIndexUpsertRejected.parsed?.error?.code === "append_only_violation",
|
|
1482
|
+
reason: "agent_wiki_index_upsert_log_reject"
|
|
1483
|
+
});
|
|
1484
|
+
|
|
1485
|
+
const indexIndexUpsertRejected = runWave(
|
|
1486
|
+
["osmd", "wiki", "index-upsert", "index.md", "--summary", "verify forbidden"],
|
|
1487
|
+
env
|
|
1488
|
+
);
|
|
1489
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1490
|
+
lines: osmdVerificationLines,
|
|
1491
|
+
label: "osmd.wiki index-upsert rejects index target",
|
|
1492
|
+
result: indexIndexUpsertRejected,
|
|
1493
|
+
ok:
|
|
1494
|
+
indexIndexUpsertRejected.parsed?.ok === false &&
|
|
1495
|
+
indexIndexUpsertRejected.parsed?.status === 400 &&
|
|
1496
|
+
indexIndexUpsertRejected.parsed?.error?.code === "invalid_args",
|
|
1497
|
+
reason: "agent_wiki_index_upsert_index_reject"
|
|
1498
|
+
});
|
|
1499
|
+
|
|
1500
|
+
const logCreateRejected = runWave(["osmd", "wiki", "create", "log.md", "--content", "verify forbidden"], env);
|
|
1501
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1502
|
+
lines: osmdVerificationLines,
|
|
1503
|
+
label: "osmd.wiki create rejects log locally",
|
|
1504
|
+
result: logCreateRejected,
|
|
1505
|
+
ok:
|
|
1506
|
+
logCreateRejected.parsed?.ok === false &&
|
|
1507
|
+
logCreateRejected.parsed?.status === 400 &&
|
|
1508
|
+
logCreateRejected.parsed?.error?.code === "invalid_args",
|
|
1509
|
+
reason: "agent_wiki_log_create_local_reject"
|
|
1510
|
+
});
|
|
1511
|
+
|
|
1512
|
+
const logUpdateRejected = runWave(["osmd", "wiki", "update", "log.md", "--content", "verify forbidden"], env);
|
|
1513
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1514
|
+
lines: osmdVerificationLines,
|
|
1515
|
+
label: "osmd.wiki update rejects log locally",
|
|
1516
|
+
result: logUpdateRejected,
|
|
1517
|
+
ok:
|
|
1518
|
+
logUpdateRejected.parsed?.ok === false &&
|
|
1519
|
+
logUpdateRejected.parsed?.status === 400 &&
|
|
1520
|
+
logUpdateRejected.parsed?.error?.code === "invalid_args",
|
|
1521
|
+
reason: "agent_wiki_log_update_local_reject"
|
|
1522
|
+
});
|
|
1523
|
+
|
|
1524
|
+
const arbitraryWikiRejected = runWave(
|
|
1525
|
+
["osmd", "wiki", "create", "random/cli-live-random.md", "--content", "verify forbidden"],
|
|
1526
|
+
env
|
|
1527
|
+
);
|
|
1528
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1529
|
+
lines: osmdVerificationLines,
|
|
1530
|
+
label: "osmd.wiki create rejects arbitrary wiki path locally",
|
|
1531
|
+
result: arbitraryWikiRejected,
|
|
1532
|
+
ok:
|
|
1533
|
+
arbitraryWikiRejected.parsed?.ok === false &&
|
|
1534
|
+
arbitraryWikiRejected.parsed?.status === 400 &&
|
|
1535
|
+
arbitraryWikiRejected.parsed?.error?.code === "invalid_args",
|
|
1536
|
+
reason: "agent_wiki_arbitrary_path_local_reject"
|
|
1537
|
+
});
|
|
1538
|
+
|
|
1539
|
+
const hardenedWikiRejectPaths = [
|
|
1540
|
+
"Customer Health.md",
|
|
1541
|
+
"customer_health.md",
|
|
1542
|
+
"customer.health.md",
|
|
1543
|
+
"concepts/Customer Health.md",
|
|
1544
|
+
"concepts/customer_health.md",
|
|
1545
|
+
"concepts/customer.health.md",
|
|
1546
|
+
"concepts/nested/file.md"
|
|
1547
|
+
];
|
|
1548
|
+
for (const path of hardenedWikiRejectPaths) {
|
|
1549
|
+
const hardenedWikiRejected = runWave(["osmd", "wiki", "read", path], env);
|
|
1550
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1551
|
+
lines: osmdVerificationLines,
|
|
1552
|
+
label: `osmd.wiki read rejects invalid wiki path ${path}`,
|
|
1553
|
+
result: hardenedWikiRejected,
|
|
1554
|
+
ok:
|
|
1555
|
+
hardenedWikiRejected.parsed?.ok === false &&
|
|
1556
|
+
hardenedWikiRejected.parsed?.status === 400 &&
|
|
1557
|
+
hardenedWikiRejected.parsed?.error?.code === "invalid_args",
|
|
1558
|
+
reason: "agent_wiki_hardened_path_local_reject"
|
|
1559
|
+
});
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
const parentRef = resolveOsmdAgentParentRef();
|
|
1563
|
+
if (!parentRef) {
|
|
1564
|
+
osmdVerificationLines.push(
|
|
1565
|
+
"FAIL | status=n/a | err=osmd_parent_not_found | reason=no_agent_children_allowed_parent | osmd agent parent resolution"
|
|
1566
|
+
);
|
|
1567
|
+
osmdVerificationFailures += 1;
|
|
1568
|
+
return;
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
const agentStatus = runWave(["osmd", "agent", "status", parentRef, "notes.md"], env);
|
|
1572
|
+
results.push(agentStatus);
|
|
1573
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1574
|
+
lines: osmdVerificationLines,
|
|
1575
|
+
label: `osmd.agent status notes parent=${parentRef}`,
|
|
1576
|
+
result: agentStatus,
|
|
1577
|
+
ok:
|
|
1578
|
+
agentStatus.parsed?.ok === true &&
|
|
1579
|
+
agentStatus.parsed?.data?.source === "agent_overlay" &&
|
|
1580
|
+
agentStatus.parsed?.data?.wikiRole === "notes" &&
|
|
1581
|
+
typeof agentStatus.parsed?.data?.formatVersion === "string" &&
|
|
1582
|
+
agentStatus.parsed?.data?.parentRef === parentRef,
|
|
1583
|
+
reason: "agent_overlay_status"
|
|
1584
|
+
});
|
|
1585
|
+
|
|
1586
|
+
const agentInit = runWave(["osmd", "agent", "init", parentRef], env);
|
|
1587
|
+
results.push(agentInit);
|
|
1588
|
+
const initFiles = firstArrayValue(agentInit.parsed, [["data", "files"]]);
|
|
1589
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1590
|
+
lines: osmdVerificationLines,
|
|
1591
|
+
label: `osmd.agent init parent=${parentRef}`,
|
|
1592
|
+
result: agentInit,
|
|
1593
|
+
ok:
|
|
1594
|
+
agentInit.parsed?.ok === true &&
|
|
1595
|
+
(initFiles.some(
|
|
1596
|
+
(file) =>
|
|
1597
|
+
file?.path?.endsWith("/.agent/notes.md") &&
|
|
1598
|
+
file?.wikiRole === "notes" &&
|
|
1599
|
+
typeof file?.formatVersion === "string"
|
|
1600
|
+
) ||
|
|
1601
|
+
agentInit.parsed?.data?.path?.endsWith("/.agent/notes.md")),
|
|
1602
|
+
reason: "agent_overlay_init"
|
|
1603
|
+
});
|
|
1604
|
+
|
|
1605
|
+
const notesContent = `CLI live verify notes ${verifyStamp}`;
|
|
1606
|
+
const agentUpdate = runWave(
|
|
1607
|
+
["osmd", "agent", "update", parentRef, "notes.md", "--content", notesContent],
|
|
1608
|
+
env
|
|
1609
|
+
);
|
|
1610
|
+
results.push(agentUpdate);
|
|
1611
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1612
|
+
lines: osmdVerificationLines,
|
|
1613
|
+
label: `osmd.agent update notes parent=${parentRef}`,
|
|
1614
|
+
result: agentUpdate,
|
|
1615
|
+
ok:
|
|
1616
|
+
agentUpdate.parsed?.ok === true &&
|
|
1617
|
+
agentUpdate.parsed?.data?.access === "read_write" &&
|
|
1618
|
+
agentUpdate.parsed?.data?.wikiRole === "notes" &&
|
|
1619
|
+
typeof agentUpdate.parsed?.data?.formatVersion === "string" &&
|
|
1620
|
+
agentUpdate.parsed?.data?.content === notesContent,
|
|
1621
|
+
reason: "agent_overlay_update"
|
|
1622
|
+
});
|
|
1623
|
+
|
|
1624
|
+
const overlayIndexSummary = `CLI live overlay index summary ${verifyStamp}`;
|
|
1625
|
+
const overlayIndexUpsert = runWave(
|
|
1626
|
+
[
|
|
1627
|
+
"osmd",
|
|
1628
|
+
"wiki",
|
|
1629
|
+
"index-upsert",
|
|
1630
|
+
".agent/notes.md",
|
|
1631
|
+
"--parent-ref",
|
|
1632
|
+
parentRef,
|
|
1633
|
+
"--summary",
|
|
1634
|
+
overlayIndexSummary
|
|
1635
|
+
],
|
|
1636
|
+
env
|
|
1637
|
+
);
|
|
1638
|
+
results.push(overlayIndexUpsert);
|
|
1639
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1640
|
+
lines: osmdVerificationLines,
|
|
1641
|
+
label: `osmd.wiki index-upsert overlay parent=${parentRef}`,
|
|
1642
|
+
result: overlayIndexUpsert,
|
|
1643
|
+
ok:
|
|
1644
|
+
overlayIndexUpsert.parsed?.ok === true &&
|
|
1645
|
+
overlayIndexUpsert.parsed?.data?.path === "Agent Wiki/index.md" &&
|
|
1646
|
+
overlayIndexUpsert.parsed?.data?.source === "agent_wiki" &&
|
|
1647
|
+
overlayIndexUpsert.parsed?.data?.wikiRole === "index" &&
|
|
1648
|
+
overlayIndexUpsert.parsed?.data?.formatVersion === "agent_markdown_v1" &&
|
|
1649
|
+
overlayIndexUpsert.parsed?.data?.exists === true &&
|
|
1650
|
+
overlayIndexUpsert.parsed?.data?.canUpdate === true &&
|
|
1651
|
+
overlayIndexUpsert.parsed?.data?.canAppend === false &&
|
|
1652
|
+
String(overlayIndexUpsert.parsed?.data?.content ?? "").includes(parentRef) &&
|
|
1653
|
+
String(overlayIndexUpsert.parsed?.data?.content ?? "").includes(overlayIndexSummary),
|
|
1654
|
+
reason: "agent_overlay_index_upsert"
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
const searchCandidates = (result) => firstArrayValue(result.parsed, [["data", "candidates"]]);
|
|
1658
|
+
const hasOwn = (candidate, key) => Object.prototype.hasOwnProperty.call(candidate ?? {}, key);
|
|
1659
|
+
const hasSearchMetadata = (candidate, expectedSource) =>
|
|
1660
|
+
candidate &&
|
|
1661
|
+
typeof candidate.path === "string" &&
|
|
1662
|
+
candidate.source === expectedSource &&
|
|
1663
|
+
typeof candidate.access === "string" &&
|
|
1664
|
+
typeof candidate.owner === "string" &&
|
|
1665
|
+
hasOwn(candidate, "wikiRole") &&
|
|
1666
|
+
hasOwn(candidate, "formatVersion") &&
|
|
1667
|
+
hasOwn(candidate, "parentRef") &&
|
|
1668
|
+
typeof candidate.exists === "boolean";
|
|
1669
|
+
|
|
1670
|
+
const canonicalSearch = runWave(["osmd", "search", "Wave Tools", "--scope", "os-only", "--limit", "5"], env);
|
|
1671
|
+
results.push(canonicalSearch);
|
|
1672
|
+
const canonicalSearchCandidates = searchCandidates(canonicalSearch);
|
|
1673
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1674
|
+
lines: osmdVerificationLines,
|
|
1675
|
+
label: "osmd.search canonical results include OS metadata",
|
|
1676
|
+
result: canonicalSearch,
|
|
1677
|
+
ok:
|
|
1678
|
+
canonicalSearch.parsed?.ok === true &&
|
|
1679
|
+
canonicalSearch.parsed?.command === "osmd.search" &&
|
|
1680
|
+
typeof canonicalSearch.parsed?.meta?.requestId === "string" &&
|
|
1681
|
+
canonicalSearch.parsed?.data?.scope === "canonical_osmd" &&
|
|
1682
|
+
canonicalSearchCandidates.some(
|
|
1683
|
+
(candidate) =>
|
|
1684
|
+
hasSearchMetadata(candidate, "canonical_osmd") &&
|
|
1685
|
+
candidate.access === "read_only" &&
|
|
1686
|
+
candidate.owner === "system"
|
|
1687
|
+
),
|
|
1688
|
+
reason: "osmd_search_canonical"
|
|
1689
|
+
});
|
|
1690
|
+
|
|
1691
|
+
const wikiSearch = runWave(["osmd", "search", "Agent Wiki", "--scope", "agent-wiki-only", "--limit", "10"], env);
|
|
1692
|
+
results.push(wikiSearch);
|
|
1693
|
+
const wikiSearchCandidates = searchCandidates(wikiSearch);
|
|
1694
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1695
|
+
lines: osmdVerificationLines,
|
|
1696
|
+
label: "osmd.search Agent Wiki scope returns agent wiki metadata",
|
|
1697
|
+
result: wikiSearch,
|
|
1698
|
+
ok:
|
|
1699
|
+
wikiSearch.parsed?.ok === true &&
|
|
1700
|
+
wikiSearch.parsed?.data?.scope === "agent_wiki" &&
|
|
1701
|
+
wikiSearchCandidates.length > 0 &&
|
|
1702
|
+
wikiSearchCandidates.every(
|
|
1703
|
+
(candidate) =>
|
|
1704
|
+
hasSearchMetadata(candidate, "agent_wiki") &&
|
|
1705
|
+
candidate.path.startsWith("Agent Wiki/") &&
|
|
1706
|
+
candidate.owner === "atlas" &&
|
|
1707
|
+
typeof candidate.toolKey === "string" &&
|
|
1708
|
+
typeof candidate.nodeKey === "string" &&
|
|
1709
|
+
typeof candidate.nodeKind === "string"
|
|
1710
|
+
),
|
|
1711
|
+
reason: "osmd_search_agent_wiki"
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1714
|
+
const controlledWikiSearch = runWave(
|
|
1715
|
+
["osmd", "search", controlledWikiSearchTerm, "--scope", "agent-wiki-only", "--limit", "10"],
|
|
1716
|
+
env
|
|
1717
|
+
);
|
|
1718
|
+
results.push(controlledWikiSearch);
|
|
1719
|
+
const controlledWikiSearchCandidates = searchCandidates(controlledWikiSearch);
|
|
1720
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1721
|
+
lines: osmdVerificationLines,
|
|
1722
|
+
label: "osmd.search Agent Wiki controlled pages",
|
|
1723
|
+
result: controlledWikiSearch,
|
|
1724
|
+
ok:
|
|
1725
|
+
controlledWikiSearch.parsed?.ok === true &&
|
|
1726
|
+
controlledWikiSearch.parsed?.data?.scope === "agent_wiki" &&
|
|
1727
|
+
controlledWikiPages.every((page) =>
|
|
1728
|
+
controlledWikiSearchCandidates.some(
|
|
1729
|
+
(candidate) =>
|
|
1730
|
+
hasSearchMetadata(candidate, "agent_wiki") &&
|
|
1731
|
+
candidate.path === page.fullPath &&
|
|
1732
|
+
candidate.wikiRole === page.wikiRole &&
|
|
1733
|
+
candidate.access === "read_write"
|
|
1734
|
+
)
|
|
1735
|
+
),
|
|
1736
|
+
reason: "osmd_search_agent_wiki_controlled_pages"
|
|
1737
|
+
});
|
|
1738
|
+
|
|
1739
|
+
const controlledCombinedSearch = runWave(
|
|
1740
|
+
["osmd", "search", controlledWikiSearchTerm, "--scope", "combined", "--limit", "10"],
|
|
1741
|
+
env
|
|
1742
|
+
);
|
|
1743
|
+
results.push(controlledCombinedSearch);
|
|
1744
|
+
const controlledCombinedSearchCandidates = searchCandidates(controlledCombinedSearch);
|
|
1745
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1746
|
+
lines: osmdVerificationLines,
|
|
1747
|
+
label: "osmd.search combined includes controlled Agent Wiki pages",
|
|
1748
|
+
result: controlledCombinedSearch,
|
|
1749
|
+
ok:
|
|
1750
|
+
controlledCombinedSearch.parsed?.ok === true &&
|
|
1751
|
+
controlledCombinedSearch.parsed?.data?.scope === "combined" &&
|
|
1752
|
+
controlledWikiPages.some((page) =>
|
|
1753
|
+
controlledCombinedSearchCandidates.some(
|
|
1754
|
+
(candidate) =>
|
|
1755
|
+
hasSearchMetadata(candidate, "agent_wiki") &&
|
|
1756
|
+
candidate.path === page.fullPath &&
|
|
1757
|
+
candidate.wikiRole === page.wikiRole
|
|
1758
|
+
)
|
|
1759
|
+
),
|
|
1760
|
+
reason: "osmd_search_combined_controlled_pages"
|
|
1761
|
+
});
|
|
1762
|
+
|
|
1763
|
+
const logSearch = runWave(
|
|
1764
|
+
["osmd", "search", `CLI live verify log ${verifyStamp}`, "--scope", "agent-wiki-only", "--limit", "5"],
|
|
1765
|
+
env
|
|
1766
|
+
);
|
|
1767
|
+
results.push(logSearch);
|
|
1768
|
+
const logSearchCandidates = searchCandidates(logSearch);
|
|
1769
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1770
|
+
lines: osmdVerificationLines,
|
|
1771
|
+
label: "osmd.search Agent Wiki log content result",
|
|
1772
|
+
result: logSearch,
|
|
1773
|
+
ok:
|
|
1774
|
+
logSearch.parsed?.ok === true &&
|
|
1775
|
+
logSearchCandidates.some(
|
|
1776
|
+
(candidate) =>
|
|
1777
|
+
hasSearchMetadata(candidate, "agent_wiki") &&
|
|
1778
|
+
candidate.path === "Agent Wiki/log.md" &&
|
|
1779
|
+
candidate.wikiRole === "log"
|
|
1780
|
+
),
|
|
1781
|
+
reason: "osmd_search_agent_wiki_log"
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
const notesSearch = runWave(
|
|
1785
|
+
["osmd", "search", notesContent, "--scope", "agent-overlay-only", "--limit", "5"],
|
|
1786
|
+
env
|
|
1787
|
+
);
|
|
1788
|
+
results.push(notesSearch);
|
|
1789
|
+
const notesSearchCandidates = searchCandidates(notesSearch);
|
|
1790
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1791
|
+
lines: osmdVerificationLines,
|
|
1792
|
+
label: "osmd.search agent overlay notes content result",
|
|
1793
|
+
result: notesSearch,
|
|
1794
|
+
ok:
|
|
1795
|
+
notesSearch.parsed?.ok === true &&
|
|
1796
|
+
notesSearch.parsed?.data?.scope === "agent_overlay" &&
|
|
1797
|
+
notesSearchCandidates.some(
|
|
1798
|
+
(candidate) =>
|
|
1799
|
+
hasSearchMetadata(candidate, "agent_overlay") &&
|
|
1800
|
+
candidate.path.endsWith("/.agent/notes.md") &&
|
|
1801
|
+
candidate.wikiRole === "notes" &&
|
|
1802
|
+
candidate.parentRef === parentRef
|
|
1803
|
+
),
|
|
1804
|
+
reason: "osmd_search_agent_overlay_notes"
|
|
1805
|
+
});
|
|
1806
|
+
|
|
1807
|
+
const combinedSearch = runWave(["osmd", "search", "Wave", "--scope", "combined", "--limit", "3"], env);
|
|
1808
|
+
results.push(combinedSearch);
|
|
1809
|
+
osmdVerificationFailures += recordExactCheck({
|
|
1810
|
+
lines: osmdVerificationLines,
|
|
1811
|
+
label: "osmd.search combined scope returns stable envelope",
|
|
1812
|
+
result: combinedSearch,
|
|
1813
|
+
ok:
|
|
1814
|
+
combinedSearch.parsed?.ok === true &&
|
|
1815
|
+
combinedSearch.parsed?.command === "osmd.search" &&
|
|
1816
|
+
combinedSearch.parsed?.data?.scope === "combined" &&
|
|
1817
|
+
Array.isArray(combinedSearch.parsed?.data?.candidates) &&
|
|
1818
|
+
typeof combinedSearch.parsed?.meta?.requestId === "string",
|
|
1819
|
+
reason: "osmd_search_combined"
|
|
1820
|
+
});
|
|
1821
|
+
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
runOsmdLiveVerification();
|
|
1825
|
+
|
|
1826
|
+
const lines = [];
|
|
1827
|
+
let failures = 0;
|
|
1828
|
+
for (const res of results) {
|
|
1829
|
+
const verdict = classify(res);
|
|
1830
|
+
const cmd = res.args.join(" ");
|
|
1831
|
+
const status = res.parsed?.status ?? "n/a";
|
|
1832
|
+
const errCode = res.parsed?.error?.code ?? "none";
|
|
1833
|
+
const reasonDetail = verdict.details ? ` (${JSON.stringify(verdict.details)})` : "";
|
|
1834
|
+
lines.push(
|
|
1835
|
+
`${verdict.ok ? "PASS" : "FAIL"} | status=${status} | err=${errCode} | reason=${verdict.reason}${reasonDetail} | ${cmd}`
|
|
1836
|
+
);
|
|
1837
|
+
if (!verdict.ok) failures += 1;
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
if (deleteVerifyLine) {
|
|
1841
|
+
lines.push(deleteVerifyLine);
|
|
1842
|
+
}
|
|
1843
|
+
failures += deleteVerifyFailure;
|
|
1844
|
+
if (postUpdateVerificationLines.length > 0) {
|
|
1845
|
+
lines.push(...postUpdateVerificationLines);
|
|
1846
|
+
}
|
|
1847
|
+
failures += postUpdateVerificationFailures;
|
|
1848
|
+
if (osmdVerificationLines.length > 0) {
|
|
1849
|
+
lines.push(...osmdVerificationLines);
|
|
1850
|
+
}
|
|
1851
|
+
failures += osmdVerificationFailures;
|
|
1852
|
+
failures += validationRemediationFailures;
|
|
1853
|
+
|
|
1854
|
+
if (validationRemediationEntries.length > 0) {
|
|
1855
|
+
lines.push("VALIDATION_REMEDIATION_SUMMARY_START");
|
|
1856
|
+
for (const entry of validationRemediationEntries) {
|
|
1857
|
+
lines.push(
|
|
1858
|
+
[
|
|
1859
|
+
"VALIDATION_REMEDIATION",
|
|
1860
|
+
`operation=${entry.operation}`,
|
|
1861
|
+
`offending_fields=${entry.offendingFields.join(",") || "n/a"}`,
|
|
1862
|
+
`backend_message=${JSON.stringify(entry.backendMessage ?? "")}`,
|
|
1863
|
+
`initial_result=${entry.initialResult}`,
|
|
1864
|
+
`remediation=${entry.remediation}`,
|
|
1865
|
+
`final_result=${entry.finalResult}`
|
|
1866
|
+
].join(" | ")
|
|
1867
|
+
);
|
|
1868
|
+
}
|
|
1869
|
+
lines.push("VALIDATION_REMEDIATION_SUMMARY_END");
|
|
1870
|
+
}
|
|
1871
|
+
|
|
1872
|
+
console.log(lines.join("\n"));
|
|
1873
|
+
const extraChecksCount =
|
|
1874
|
+
(deleteVerifyLine ? 1 : 0) +
|
|
1875
|
+
postUpdateVerificationLines.length +
|
|
1876
|
+
validationRemediationEntries.length;
|
|
1877
|
+
console.log(`\nSUMMARY total=${results.length + extraChecksCount} failures=${failures}`);
|
|
1878
|
+
if (failures > 0) process.exit(1);
|