@ted-galago/wave-cli 0.1.5 → 0.1.7

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.
@@ -47,17 +47,39 @@ function firstId(parsed, field) {
47
47
  return undefined;
48
48
  }
49
49
 
50
+ function firstListRow(parsed, fields) {
51
+ for (const field of fields) {
52
+ const row = parsed?.data?.[field]?.data?.[0];
53
+ if (row && typeof row === "object") {
54
+ return row;
55
+ }
56
+ }
57
+ return undefined;
58
+ }
59
+
50
60
  function classify(result) {
51
61
  if (!result.parsed) {
52
62
  return { ok: false, reason: "non_json_output" };
53
63
  }
64
+ const embeddedError = findFirstEmbeddedErrors(result.parsed);
54
65
  if (result.parsed.ok === true) {
66
+ if (embeddedError) {
67
+ return {
68
+ ok: false,
69
+ reason: "embedded_errors",
70
+ details: { path: embeddedError.path, sample: embeddedError.sample }
71
+ };
72
+ }
55
73
  return { ok: true, reason: "ok" };
56
74
  }
57
75
 
58
76
  const code = result.parsed?.error?.code ?? "unknown";
59
77
  const message = result.parsed?.error?.message ?? "";
60
78
  const details = JSON.stringify(result.parsed?.error?.details ?? {});
79
+ const output = `${result.stdout}\n${result.stderr}`;
80
+ if (/unpermitted parameter/i.test(output) || /unpermitted parameter/i.test(details)) {
81
+ return { ok: false, reason: "unpermitted_parameter" };
82
+ }
61
83
  const hardFailures = [
62
84
  "network_error",
63
85
  "missing_auth",
@@ -83,10 +105,124 @@ function classify(result) {
83
105
  return { ok: true, reason: `reachable_${code}` };
84
106
  }
85
107
 
108
+ function isNonEmptyErrorsValue(value) {
109
+ if (value === null || value === undefined) return false;
110
+ if (Array.isArray(value)) {
111
+ if (value.length === 0) return false;
112
+ return value.some((entry) => {
113
+ if (entry === null || entry === undefined) return false;
114
+ if (typeof entry === "string") return entry.trim().length > 0;
115
+ if (typeof entry === "object") return Object.keys(entry).length > 0;
116
+ return true;
117
+ });
118
+ }
119
+ if (typeof value === "string") return value.trim().length > 0;
120
+ if (typeof value === "object") return Object.keys(value).length > 0;
121
+ return true;
122
+ }
123
+
124
+ function summarizeValue(value) {
125
+ try {
126
+ if (typeof value === "string") return value.slice(0, 160);
127
+ const serialized = JSON.stringify(value);
128
+ if (typeof serialized !== "string") return String(value);
129
+ return serialized.slice(0, 300);
130
+ } catch {
131
+ return String(value);
132
+ }
133
+ }
134
+
135
+ function findFirstEmbeddedErrors(input) {
136
+ const queue = [{ value: input, path: "$", depth: 0 }];
137
+ const maxDepth = 12;
138
+ const maxNodes = 2000;
139
+ let visited = 0;
140
+
141
+ while (queue.length > 0) {
142
+ const current = queue.shift();
143
+ if (!current) break;
144
+ visited += 1;
145
+ if (visited > maxNodes) break;
146
+
147
+ const { value, path, depth } = current;
148
+ if (!value || typeof value !== "object") continue;
149
+
150
+ if (Array.isArray(value)) {
151
+ if (depth >= maxDepth) continue;
152
+ for (let i = 0; i < value.length; i += 1) {
153
+ queue.push({ value: value[i], path: `${path}[${i}]`, depth: depth + 1 });
154
+ }
155
+ continue;
156
+ }
157
+
158
+ for (const [key, nested] of Object.entries(value)) {
159
+ const nextPath = `${path}.${key}`;
160
+ if (key === "errors" && isNonEmptyErrorsValue(nested)) {
161
+ return { path: nextPath, sample: summarizeValue(nested) };
162
+ }
163
+ if (depth < maxDepth && nested && typeof nested === "object") {
164
+ queue.push({ value: nested, path: nextPath, depth: depth + 1 });
165
+ }
166
+ }
167
+ }
168
+
169
+ return null;
170
+ }
171
+
86
172
  function payload(root, attrs) {
87
173
  return JSON.stringify({ [root]: attrs });
88
174
  }
89
175
 
176
+ function sleepMs(ms) {
177
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
178
+ }
179
+
180
+ function extractListIds(parsed, field) {
181
+ const rows = parsed?.data?.[field]?.data;
182
+ if (!Array.isArray(rows)) return [];
183
+ return rows
184
+ .map((row) => (row?.id ? String(row.id) : ""))
185
+ .filter((id) => id.length > 0);
186
+ }
187
+
188
+ function readPath(input, segments) {
189
+ let current = input;
190
+ for (const segment of segments) {
191
+ if (!current || typeof current !== "object") {
192
+ return undefined;
193
+ }
194
+ current = current[segment];
195
+ }
196
+ return current;
197
+ }
198
+
199
+ function firstDefinedValue(input, paths) {
200
+ for (const path of paths) {
201
+ const value = readPath(input, path);
202
+ if (value !== undefined && value !== null) {
203
+ return value;
204
+ }
205
+ }
206
+ return undefined;
207
+ }
208
+
209
+ function verifySubissueDeleteWithPolling(env, issueId, subIssueId) {
210
+ const attempts = [];
211
+ for (let i = 1; i <= 5; i += 1) {
212
+ const listRes = runWave(["subissues", "list", "--issue-id", issueId], env);
213
+ const ids = extractListIds(listRes.parsed, "sub_issues");
214
+ const deleted = !ids.includes(String(subIssueId));
215
+ attempts.push({ index: i, ids, result: listRes });
216
+ if (deleted) {
217
+ return { ok: true, attempts, tries: i };
218
+ }
219
+ if (i < 5) {
220
+ sleepMs(500);
221
+ }
222
+ }
223
+ return { ok: false, attempts, tries: 5 };
224
+ }
225
+
90
226
  const root = process.cwd();
91
227
  const envFile = resolve(root, ".env");
92
228
  const dot = loadDotEnv(envFile);
@@ -138,6 +274,23 @@ for (const [key, args] of listSeeds) {
138
274
  if (res.parsed?.ok) {
139
275
  const field = args[0] === "foundation" ? args[1].replace(/-([a-z])/g, (_, c) => c.toUpperCase()) : args[0].replace(/-([a-z])/g, (_, c) => c.toUpperCase());
140
276
  idLookup[key] = firstId(res.parsed, field);
277
+ if (key === "kpis") {
278
+ const firstKpi = firstListRow(res.parsed, ["smart_kpis", "smartKpis", "kpis"]);
279
+ if (firstKpi && typeof firstKpi === "object") {
280
+ const attributes = firstKpi.attributes;
281
+ if (attributes && typeof attributes === "object") {
282
+ const attr = attributes;
283
+ idLookup.kpiSmartKpiViewId =
284
+ typeof attr.smart_kpi_view_id === "string"
285
+ ? attr.smart_kpi_view_id
286
+ : typeof attr.smartKpiViewId === "string"
287
+ ? attr.smartKpiViewId
288
+ : undefined;
289
+ idLookup.kpiCreateName =
290
+ typeof attr.name === "string" && attr.name.trim().length > 0 ? attr.name : undefined;
291
+ }
292
+ }
293
+ }
141
294
  }
142
295
  }
143
296
 
@@ -164,12 +317,57 @@ const surveyId = idLookup.surveys;
164
317
  const feedbackId = idLookup.feedbacks;
165
318
  const responsibilityId = idLookup.accountability;
166
319
  const kpiId = idLookup.kpis;
320
+ const kpiSmartKpiViewId = idLookup.kpiSmartKpiViewId;
321
+ const kpiCreateName = idLookup.kpiCreateName;
167
322
  const measurableGroupId = idLookup.scorecardGroups;
168
323
  const measurableId = idLookup.scorecards;
169
324
  const customerId = idLookup.customers;
170
325
  const contactId = idLookup.contacts;
171
326
  const annualObjectiveId = idLookup.annualObjectives;
172
327
  const quarterlyObjectiveId = idLookup.quarterlyObjectives;
328
+ const verifyStamp = String(Date.now());
329
+ const orgWorkspaceNameVerify = `Wave Verify ${verifyStamp}`;
330
+ const orgMetaOneSentenceSummaryVerify = `Verify Title ${verifyStamp}`;
331
+ const keyMetricRevenueVerify = String(1000 + Number(verifyStamp.slice(-3)));
332
+ const questionNameVerify = `CLI Verify Question Update ${verifyStamp}`;
333
+ const surveyDueDateVerify = "2026-12-31";
334
+ const feedbackQuarterVerify = "q3";
335
+ const accountabilityNameVerify = `CLI Verify Responsibility Update ${verifyStamp}`;
336
+ const organizationMetaProfileSeed = runWave(
337
+ ["organizations", "meta-profile", "show", "--id", orgId],
338
+ env
339
+ );
340
+ results.push(organizationMetaProfileSeed);
341
+ const organizationMetaProfileId =
342
+ firstId(organizationMetaProfileSeed.parsed, "organization_meta_profile") ?? orgId;
343
+ const keyMetricMetaProfileSeed = runWave(
344
+ ["organizations", "key-metric-meta-profile", "show", "--id", orgId],
345
+ env
346
+ );
347
+ results.push(keyMetricMetaProfileSeed);
348
+ const keyMetricMetaProfileId =
349
+ firstId(keyMetricMetaProfileSeed.parsed, "key_metric_meta_profile") ?? orgId;
350
+ const meetingCreatePayload = teamId
351
+ ? {
352
+ name: "CLI Verify Meeting",
353
+ type: "TeamMeeting",
354
+ member_id: memberId,
355
+ date: "2026-04-18",
356
+ start_time: 700,
357
+ end_time: 760,
358
+ repeats: "never",
359
+ status: "upcoming",
360
+ teams_ids: [teamId]
361
+ }
362
+ : {
363
+ name: "CLI Verify Meeting",
364
+ type: "OneOnOneMeeting",
365
+ member_id: memberId,
366
+ date: "2026-04-18",
367
+ start_time: 700,
368
+ repeats: "never",
369
+ status: "upcoming"
370
+ };
173
371
 
174
372
  const probes = [
175
373
  ["projects", "show", "--id", projectId],
@@ -213,16 +411,16 @@ const probes = [
213
411
  ["rocks", "create", "--data-json", payload("rock", { name: "CLI Verify Rock", rock_collection_id: rockCollectionId, status: "on_track", priority: "no_priority", rock_type: "individual" })],
214
412
  ["rocks", "update", "--id", rockId, "--data-json", payload("rock", { description: "verify-update" })],
215
413
  ["meetings", "notes", "--id", meetingId, "--content", "CLI verify notes"],
216
- ["meetings", "create", "--data-json", payload("meeting", { name: "CLI Verify Meeting", status: "upcoming" })],
414
+ ["meetings", "create", "--data-json", payload("meeting", meetingCreatePayload)],
217
415
  ["meetings", "update", "--id", meetingId, "--data-json", payload("meeting", { notes: "verify-update" })],
218
416
  ["members", "create", "--data-json", payload("member", { email: `cli.verify.${Date.now()}@example.com` })],
219
417
  ["members", "update", "--id", memberId, "--data-json", payload("member", { first_name: "CLI" })],
220
418
  ["teams", "create", "--data-json", payload("team", { name: `CLI Verify Team ${Date.now()}` })],
221
419
  ["teams", "update", "--id", teamId, "--data-json", payload("team", { name: `CLI Verify Team Update ${Date.now()}` })],
222
- ["organizations", "update", "--id", orgId, "--data-json", payload("organization", { workspace_name: "Wave Verify" })],
223
- ["organizations", "meta-profile", "update", "--id", orgId, "--data-json", payload("organization_meta_profile", { title: "Verify Title" })],
224
- ["organizations", "key-metric-meta-profile", "update", "--id", orgId, "--data-json", payload("key_metric_meta_profile", { annual_revenue: "1000" })],
225
- ["issues", "create", "--issue-group-id", issueGroupId, "--name", "CLI Verify Issue", "--issue-type", "personal"],
420
+ ["organizations", "update", "--id", orgId, "--data-json", payload("organization", { organization_detail_attributes: { workspace_name: orgWorkspaceNameVerify } })],
421
+ ["organizations", "meta-profile", "update", "--id", organizationMetaProfileId, "--data-json", payload("organization_meta_profile", { profile: { company_identity: { one_sentence_summary: orgMetaOneSentenceSummaryVerify } } })],
422
+ ["organizations", "key-metric-meta-profile", "update", "--id", keyMetricMetaProfileId, "--data-json", payload("key_metric_meta_profile", { profile: { financial: { revenue: keyMetricRevenueVerify } } })],
423
+ ["issues", "create", "--issue-group-id", issueGroupId, "--name", "CLI Verify Issue", "--issue-type", "short_term"],
226
424
  ["issues", "update", "--id", issueId, "--data-json", payload("issue", { description: "verify-update" })],
227
425
  ["lists", "create", "--data-json", payload("list", { name: "CLI Verify List", status: "in_progress", priority: "no_priority" })],
228
426
  ["lists", "update", "--id", listId, "--data-json", payload("list", { description: "verify-update" })],
@@ -232,42 +430,42 @@ const probes = [
232
430
  ["issue-groups", "update", "--id", issueGroupId, "--data-json", payload("issue_group", { description: "verify-update" })],
233
431
  ["todo-groups", "create", "--data-json", payload("todo_group", { name: "CLI Verify Todo Group", status: "in_progress", priority: "no_priority" })],
234
432
  ["todo-groups", "update", "--id", todoGroupId, "--data-json", payload("todo_group", { description: "verify-update" })],
235
- ["todos", "create", "--data-json", payload("todo", { todo_group_id: todoGroupId, name: "CLI Verify Todo", status: "to_do", priority: "no_priority" })],
433
+ ["todos", "create", "--data-json", payload("todo", { todo_group_id: todoGroupId, name: "CLI Verify Todo", status: "open", priority: "no_priority" })],
236
434
  ["todos", "update", "--id", todoId, "--data-json", payload("todo", { description: "verify-update" })],
237
435
  ["rock-collections", "create", "--data-json", payload("rock_collection", { name: "CLI Verify Rock Collection", status: "in_progress", priority: "no_priority" })],
238
436
  ["rock-collections", "update", "--id", rockCollectionId, "--data-json", payload("rock_collection", { description: "verify-update" })],
239
- ["knowledge", "create", "--data-json", payload("content", { type: "normal", content_type: "knowledge", status: "draft", name: "CLI Verify Doc" })],
437
+ ["knowledge", "create", "--data-json", payload("content", { content_type: "process", status: "draft", name: "CLI Verify Doc", member_id: memberId })],
240
438
  ["knowledge", "update", "--id", contentId, "--data-json", payload("content", { name: "CLI Verify Doc Update" })],
241
439
  ["stand-ups", "create", "--data-json", payload("stand_up", { member_id: memberId, completed_date: "2026-04-06" })],
242
440
  ["stand-ups", "update", "--id", standUpId, "--data-json", payload("stand_up", { blockers: "verify-update" })],
243
- ["news", "create", "--data-json", payload("headline", { summary: "CLI Verify Headline", status: "draft", headline_type: "organizational" })],
441
+ ["news", "create", "--data-json", payload("headline", { summary: "CLI Verify Headline", member_id: memberId, status: "active", headline_type: "org_wide" })],
244
442
  ["news", "update", "--id", headlineId, "--data-json", payload("headline", { description: "verify-update" })],
245
- ["questions", "create", "--data-json", payload("question", { summary: "CLI Verify Question" })],
246
- ["questions", "update", "--id", questionId, "--data-json", payload("question", { summary: "CLI Verify Question Update" })],
247
- ["pulse", "create", "--data-json", payload("health_update", { updatable_id: projectId, updatable_type: "Project", value: "on_track", status: "on_track" })],
443
+ ["questions", "create", "--data-json", payload("question", { name: "CLI Verify Question", member_id: memberId })],
444
+ ["questions", "update", "--id", questionId, "--data-json", payload("question", { name: questionNameVerify, description: "verify-update" })],
445
+ ["pulse", "create", "--data-json", payload("health_update", { health_updatable_id: projectId, health_updatable_type: "Project", member_id: memberId, value: "on_track", status: "on_track" })],
248
446
  ["pulse", "update", "--id", pulseId, "--data-json", payload("health_update", { value: "verify-update" })],
249
- ["surveys", "create", "--data-json", payload("survey", { title: "CLI Verify Survey" })],
250
- ["surveys", "update", "--id", surveyId, "--data-json", payload("survey", { title: "CLI Verify Survey Update" })],
251
- ["feedbacks", "create", "--data-json", payload("feedback", { title: "CLI Verify Feedback" })],
252
- ["feedbacks", "update", "--id", feedbackId, "--data-json", payload("feedback", { title: "CLI Verify Feedback Update" })],
253
- ["accountability", "create", "--data-json", payload("responsibility", { summary: "CLI Verify Responsibility" })],
254
- ["accountability", "update", "--id", responsibilityId, "--data-json", payload("responsibility", { summary: "CLI Verify Responsibility Update" })],
255
- ["kpis", "create", "--data-json", payload("smart_kpi", { measurable_group_id: measurableGroupId, name: "CLI Verify KPI" })],
447
+ ["surveys", "create", "--data-json", payload("survey", { name: "engagement", recipient_type: "org_wide" })],
448
+ ["surveys", "update", "--id", surveyId, "--data-json", payload("survey", { name: "engagement", recipient_type: "org_wide", due_date: surveyDueDateVerify })],
449
+ ["feedbacks", "create", "--data-json", payload("feedback", { name: "company_culture", quarter: "q2", year: "2026" })],
450
+ ["feedbacks", "update", "--id", feedbackId, "--data-json", payload("feedback", { name: "roadmap_and_strategy", quarter: feedbackQuarterVerify, year: "2026" })],
451
+ ["accountability", "create", "--data-json", payload("responsibility", { name: "CLI Verify Responsibility", member_id: memberId })],
452
+ ["accountability", "update", "--id", responsibilityId, "--data-json", payload("responsibility", { name: accountabilityNameVerify })],
453
+ ["kpis", "create", "--data-json", payload("smart_kpi", { smart_kpi_view_id: kpiSmartKpiViewId, name: kpiCreateName })],
256
454
  ["kpis", "update", "--id", kpiId, "--data-json", payload("smart_kpi", { name: "CLI Verify KPI Update" })],
257
455
  ["scorecard-groups", "create", "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group" })],
258
456
  ["scorecard-groups", "update", "--id", measurableGroupId, "--data-json", payload("measurable_group", { name: "CLI Verify Scorecard Group Update" })],
259
- ["scorecards", "create", "--data-json", payload("measurable", { measurable_group_id: measurableGroupId, name: "CLI Verify Scorecard" })],
457
+ ["scorecards", "create", "--data-json", payload("measurable", { measurable_group_id: measurableGroupId, name: "CLI Verify Scorecard", interval: "weekly", trend: "average" })],
260
458
  ["scorecards", "update", "--id", measurableId, "--data-json", payload("measurable", { name: "CLI Verify Scorecard Update" })],
261
459
  ["customers", "create", "--data-json", payload("customer", { name: "CLI Verify Customer" })],
262
460
  ["customers", "update", "--id", customerId, "--data-json", payload("customer", { name: "CLI Verify Customer Update" })],
263
461
  ["contacts", "create", "--data-json", payload("contact", { customer_id: customerId, name: "CLI Verify Contact" })],
264
462
  ["contacts", "update", "--id", contactId, "--data-json", payload("contact", { name: "CLI Verify Contact Update" })],
265
- ["foundation", "strategic-plans", "update", "--id", orgId, "--data-json", payload("strategic_plan", { title: "CLI Verify Strategic Plan" })],
266
- ["foundation", "strategic-objectives", "update", "--id", orgId, "--data-json", payload("strategic_objective", { title: "CLI Verify Strategic Objective" })],
267
- ["foundation", "annual-objectives", "create", "--data-json", payload("annual_objective", { strategic_objective_id: annualObjectiveId || orgId, title: "CLI Verify Annual Objective" })],
268
- ["foundation", "annual-objectives", "update", "--id", annualObjectiveId, "--data-json", payload("annual_objective", { title: "CLI Verify Annual Objective Update" })],
269
- ["foundation", "quarterly-objectives", "create", "--data-json", payload("quarterly_objective", { strategic_objective_id: annualObjectiveId || orgId, annual_objective_id: annualObjectiveId || orgId, title: "CLI Verify Quarterly Objective" })],
270
- ["foundation", "quarterly-objectives", "update", "--id", quarterlyObjectiveId, "--data-json", payload("quarterly_objective", { title: "CLI Verify Quarterly Objective Update" })]
463
+ ["foundation", "strategic-plans", "update", "--id", orgId, "--data-json", payload("strategic_plan", { published_at: "2026-04-18T17:00:00Z" })],
464
+ ["foundation", "strategic-objectives", "update", "--id", orgId, "--data-json", payload("strategic_objective", { summary: "CLI Verify Strategic Objective" })],
465
+ ["foundation", "annual-objectives", "create", "--data-json", payload("annual_objective", { strategic_objective_id: annualObjectiveId || orgId, name: "CLI Verify Annual Objective" })],
466
+ ["foundation", "annual-objectives", "update", "--id", annualObjectiveId, "--data-json", payload("annual_objective", { name: "CLI Verify Annual Objective Update" })],
467
+ ["foundation", "quarterly-objectives", "create", "--data-json", payload("quarterly_objective", { strategic_objective_id: annualObjectiveId || orgId, annual_objective_id: annualObjectiveId || orgId, name: "CLI Verify Quarterly Objective" })],
468
+ ["foundation", "quarterly-objectives", "update", "--id", quarterlyObjectiveId, "--data-json", payload("quarterly_objective", { name: "CLI Verify Quarterly Objective Update" })]
271
469
  ];
272
470
 
273
471
  for (const args of probes) {
@@ -286,6 +484,184 @@ for (const args of probes) {
286
484
  results.push(runWave(args, env));
287
485
  }
288
486
 
487
+ let deleteVerifyLine = "";
488
+ let deleteVerifyFailure = 0;
489
+ if (issueId) {
490
+ const subissueCreate = runWave(
491
+ [
492
+ "subissues",
493
+ "create",
494
+ "--data-json",
495
+ payload("sub_issue", {
496
+ issue_id: issueId,
497
+ name: `CLI Verify Subissue ${Date.now()}`
498
+ })
499
+ ],
500
+ env
501
+ );
502
+ results.push(subissueCreate);
503
+ const subIssueId = subissueCreate?.parsed?.data?.create_sub_issue?.data?.data?.id
504
+ ? String(subissueCreate.parsed.data.create_sub_issue.data.data.id)
505
+ : "";
506
+ if (subIssueId) {
507
+ const destroyRes = runWave(["subissues", "destroy", "--id", subIssueId], env);
508
+ results.push(destroyRes);
509
+ const verifyRes = verifySubissueDeleteWithPolling(env, issueId, subIssueId);
510
+ deleteVerifyLine = `${verifyRes.ok ? "PASS" : "FAIL"} | status=${verifyRes.ok ? 200 : 500} | err=${
511
+ verifyRes.ok ? "none" : "delete_not_observed"
512
+ } | subissues destroy verify --issue-id ${issueId} --id ${subIssueId} --tries ${verifyRes.tries}`;
513
+ if (!verifyRes.ok) {
514
+ deleteVerifyFailure = 1;
515
+ }
516
+ }
517
+ }
518
+
519
+ const postUpdateVerificationLines = [];
520
+ let postUpdateVerificationFailures = 0;
521
+
522
+ function hasSuccessfulCommand(commandPrefix) {
523
+ return results.some((res) => {
524
+ if (!Array.isArray(res.args)) return false;
525
+ if (res.args.length < commandPrefix.length) return false;
526
+ for (let i = 0; i < commandPrefix.length; i += 1) {
527
+ if (res.args[i] !== commandPrefix[i]) {
528
+ return false;
529
+ }
530
+ }
531
+ return res.parsed?.ok === true;
532
+ });
533
+ }
534
+
535
+ function verifyFieldWrite({ label, showArgs, expectedValue, candidatePaths, requiredSuccessPrefix }) {
536
+ if (requiredSuccessPrefix && !hasSuccessfulCommand(requiredSuccessPrefix)) {
537
+ postUpdateVerificationLines.push(`PASS | status=n/a | err=skipped_prereq_not_ok | ${label}`);
538
+ return;
539
+ }
540
+
541
+ if (showArgs.includes(undefined)) {
542
+ postUpdateVerificationLines.push(`PASS | status=n/a | err=skipped_missing_id | ${label}`);
543
+ return;
544
+ }
545
+
546
+ const showRes = runWave(showArgs, env);
547
+ const status = showRes.parsed?.status ?? "n/a";
548
+ const errCode = showRes.parsed?.error?.code ?? "none";
549
+
550
+ if (!showRes.parsed?.ok) {
551
+ postUpdateVerificationLines.push(
552
+ `FAIL | status=${status} | err=${errCode} | ${label} (${showArgs.join(" ")})`
553
+ );
554
+ postUpdateVerificationFailures += 1;
555
+ return;
556
+ }
557
+
558
+ const actual = firstDefinedValue(showRes.parsed, candidatePaths);
559
+ const ok = String(actual ?? "") === String(expectedValue);
560
+ postUpdateVerificationLines.push(
561
+ `${ok ? "PASS" : "FAIL"} | status=${status} | err=${
562
+ ok ? "none" : "read_after_write_mismatch"
563
+ } | ${label} expected=${JSON.stringify(expectedValue)} actual=${JSON.stringify(actual ?? null)}`
564
+ );
565
+ if (!ok) {
566
+ postUpdateVerificationFailures += 1;
567
+ }
568
+ }
569
+
570
+ verifyFieldWrite({
571
+ label: "organizations.update verify workspace_name",
572
+ showArgs: ["organizations", "show", "--id", orgId],
573
+ expectedValue: orgWorkspaceNameVerify,
574
+ requiredSuccessPrefix: ["organizations", "update"],
575
+ candidatePaths: [
576
+ ["data", "organization", "organizationDetail", "workspaceName"],
577
+ ["data", "organization", "organization_detail", "workspace_name"],
578
+ ["data", "organization", "attributes", "organizationDetail", "workspaceName"],
579
+ ["data", "organization", "attributes", "organization_detail_attributes", "workspace_name"]
580
+ ]
581
+ });
582
+
583
+ verifyFieldWrite({
584
+ label: "organizations.meta-profile.update verify one_sentence_summary",
585
+ showArgs: ["organizations", "meta-profile", "show", "--id", organizationMetaProfileId],
586
+ expectedValue: orgMetaOneSentenceSummaryVerify,
587
+ requiredSuccessPrefix: ["organizations", "meta-profile", "update"],
588
+ candidatePaths: [
589
+ [
590
+ "data",
591
+ "organization_meta_profile",
592
+ "attributes",
593
+ "profile",
594
+ "companyIdentity",
595
+ "oneSentenceSummary"
596
+ ],
597
+ [
598
+ "data",
599
+ "organization_meta_profile",
600
+ "attributes",
601
+ "profile",
602
+ "company_identity",
603
+ "one_sentence_summary"
604
+ ],
605
+ [
606
+ "data",
607
+ "organizationMetaProfile",
608
+ "attributes",
609
+ "profile",
610
+ "companyIdentity",
611
+ "oneSentenceSummary"
612
+ ]
613
+ ]
614
+ });
615
+
616
+ verifyFieldWrite({
617
+ label: "organizations.key-metric-meta-profile.update verify financial.revenue",
618
+ showArgs: ["organizations", "key-metric-meta-profile", "show", "--id", keyMetricMetaProfileId],
619
+ expectedValue: keyMetricRevenueVerify,
620
+ requiredSuccessPrefix: ["organizations", "key-metric-meta-profile", "update"],
621
+ candidatePaths: [
622
+ ["data", "key_metric_meta_profile", "attributes", "profile", "financial", "revenue"],
623
+ ["data", "keyMetricMetaProfile", "attributes", "profile", "financial", "revenue"]
624
+ ]
625
+ });
626
+
627
+ verifyFieldWrite({
628
+ label: "questions.update verify name",
629
+ showArgs: ["questions", "show", "--id", questionId],
630
+ expectedValue: questionNameVerify,
631
+ requiredSuccessPrefix: ["questions", "update"],
632
+ candidatePaths: [
633
+ ["data", "question", "attributes", "name"],
634
+ ["data", "question", "attributes", "summary"]
635
+ ]
636
+ });
637
+
638
+ verifyFieldWrite({
639
+ label: "surveys.update verify due_date",
640
+ showArgs: ["surveys", "show", "--id", surveyId],
641
+ expectedValue: surveyDueDateVerify,
642
+ requiredSuccessPrefix: ["surveys", "update"],
643
+ candidatePaths: [
644
+ ["data", "survey", "attributes", "dueDate"],
645
+ ["data", "survey", "attributes", "due_date"]
646
+ ]
647
+ });
648
+
649
+ verifyFieldWrite({
650
+ label: "feedbacks.update verify quarter",
651
+ showArgs: ["feedbacks", "show", "--id", feedbackId],
652
+ expectedValue: feedbackQuarterVerify,
653
+ requiredSuccessPrefix: ["feedbacks", "update"],
654
+ candidatePaths: [["data", "feedback", "attributes", "quarter"]]
655
+ });
656
+
657
+ verifyFieldWrite({
658
+ label: "accountability.update verify name",
659
+ showArgs: ["accountability", "show", "--id", responsibilityId],
660
+ expectedValue: accountabilityNameVerify,
661
+ requiredSuccessPrefix: ["accountability", "update"],
662
+ candidatePaths: [["data", "responsibility", "attributes", "name"]]
663
+ });
664
+
289
665
  const lines = [];
290
666
  let failures = 0;
291
667
  for (const res of results) {
@@ -293,10 +669,24 @@ for (const res of results) {
293
669
  const cmd = res.args.join(" ");
294
670
  const status = res.parsed?.status ?? "n/a";
295
671
  const errCode = res.parsed?.error?.code ?? "none";
296
- lines.push(`${verdict.ok ? "PASS" : "FAIL"} | status=${status} | err=${errCode} | ${cmd}`);
672
+ const reasonDetail = verdict.details ? ` (${JSON.stringify(verdict.details)})` : "";
673
+ lines.push(
674
+ `${verdict.ok ? "PASS" : "FAIL"} | status=${status} | err=${errCode} | reason=${verdict.reason}${reasonDetail} | ${cmd}`
675
+ );
297
676
  if (!verdict.ok) failures += 1;
298
677
  }
299
678
 
679
+ if (deleteVerifyLine) {
680
+ lines.push(deleteVerifyLine);
681
+ }
682
+ failures += deleteVerifyFailure;
683
+ if (postUpdateVerificationLines.length > 0) {
684
+ lines.push(...postUpdateVerificationLines);
685
+ }
686
+ failures += postUpdateVerificationFailures;
687
+
300
688
  console.log(lines.join("\n"));
301
- console.log(`\nSUMMARY total=${results.length} failures=${failures}`);
689
+ const extraChecksCount =
690
+ (deleteVerifyLine ? 1 : 0) + postUpdateVerificationLines.length;
691
+ console.log(`\nSUMMARY total=${results.length + extraChecksCount} failures=${failures}`);
302
692
  if (failures > 0) process.exit(1);