trackops 2.0.5 → 2.1.0

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.
@@ -0,0 +1,485 @@
1
+ "use strict";
2
+
3
+ function hasText(value) {
4
+ return Boolean(String(value || "").trim());
5
+ }
6
+
7
+ function hasSchema(value) {
8
+ if (!value) return false;
9
+ if (Array.isArray(value)) return value.length > 0;
10
+ if (typeof value === "object") return Object.keys(value).length > 0;
11
+ return hasText(value);
12
+ }
13
+
14
+ function nonEmptyList(value) {
15
+ return Array.isArray(value) ? value.filter((item) => hasText(item)) : [];
16
+ }
17
+
18
+ function latestVerification(state, scope) {
19
+ return state.latestVerification?.verification?.runs?.[scope] || null;
20
+ }
21
+
22
+ function makeBacklogIssueDetail(state) {
23
+ const parts = [];
24
+ if (state.derived.circularDeps.length) parts.push(`deps:${state.derived.circularDeps.length}`);
25
+ if (state.derived.phantomDeps.length) parts.push(`phantomDeps:${state.derived.phantomDeps.length}`);
26
+ if (state.derived.phantomParents.length) parts.push(`phantomParents:${state.derived.phantomParents.length}`);
27
+ if (state.derived.hierarchyCycles.length) parts.push(`hierarchy:${state.derived.hierarchyCycles.length}`);
28
+ return parts.join(", ");
29
+ }
30
+
31
+ const PHASE_DOD = {
32
+ O: {
33
+ id: "O",
34
+ titleKey: "quality.phase.O.title",
35
+ checks: [
36
+ {
37
+ id: "bootstrap-complete",
38
+ severity: "high",
39
+ titleKey: "quality.phase.check.bootstrapComplete.title",
40
+ recommendationKey: "quality.phase.check.bootstrapComplete.recommendation",
41
+ evaluate(state) {
42
+ if (!state.hasOpera) return { status: "skip" };
43
+ const status = String(state.control.meta?.opera?.bootstrap?.status || "").trim();
44
+ return {
45
+ status: status === "completed" ? "pass" : "fail",
46
+ detail: status || "unknown",
47
+ evidence: { status: status || null },
48
+ };
49
+ },
50
+ },
51
+ {
52
+ id: "discovery-core",
53
+ severity: "high",
54
+ titleKey: "quality.phase.check.discoveryCore.title",
55
+ recommendationKey: "quality.phase.check.discoveryCore.recommendation",
56
+ evaluate(state) {
57
+ const missing = ["problemStatement", "targetUser", "singularDesiredOutcome", "sourceOfTruth"]
58
+ .filter((field) => !hasText(state.bootstrapArtifacts.intake?.[field]));
59
+ return {
60
+ status: missing.length ? "fail" : "pass",
61
+ detail: missing.join(", "),
62
+ evidence: { missingFields: missing },
63
+ };
64
+ },
65
+ },
66
+ {
67
+ id: "bootstrap-artifacts",
68
+ severity: "high",
69
+ titleKey: "quality.phase.check.bootstrapArtifacts.title",
70
+ recommendationKey: "quality.phase.check.bootstrapArtifacts.recommendation",
71
+ evaluate(state) {
72
+ const missing = [];
73
+ if (!state.bootstrapArtifacts.intakeExists) missing.push("intake.json");
74
+ if (!state.bootstrapArtifacts.specExists) missing.push("spec-dossier.md");
75
+ if (!state.contract.exists || !state.contract.valid) missing.push("operating-contract.json");
76
+ if ((state.bootstrapArtifacts.qualityReport?.missingFields || []).length) {
77
+ missing.push(`quality:${state.bootstrapArtifacts.qualityReport.missingFields.join(",")}`);
78
+ }
79
+ return {
80
+ status: missing.length ? "fail" : "pass",
81
+ detail: missing.join(", "),
82
+ evidence: {
83
+ missing,
84
+ contractValid: state.contract.valid,
85
+ bootstrapQuality: state.bootstrapArtifacts.qualityReport || null,
86
+ },
87
+ };
88
+ },
89
+ },
90
+ {
91
+ id: "version-control-recorded",
92
+ severity: "medium",
93
+ titleKey: "quality.phase.check.versionControl.title",
94
+ recommendationKey: "quality.phase.check.versionControl.recommendation",
95
+ evaluate(state) {
96
+ const metadata = state.bootstrapArtifacts.intake?.versionControl || null;
97
+ return {
98
+ status: metadata ? "pass" : "warn",
99
+ detail: metadata ? state.bootstrapArtifacts.sources.versionControl : "",
100
+ evidence: { versionControl: metadata, source: state.bootstrapArtifacts.sources.versionControl || null },
101
+ };
102
+ },
103
+ },
104
+ {
105
+ id: "deployment-recorded",
106
+ severity: "medium",
107
+ titleKey: "quality.phase.check.deployment.title",
108
+ recommendationKey: "quality.phase.check.deployment.recommendation",
109
+ evaluate(state) {
110
+ const metadata = state.bootstrapArtifacts.intake?.deployment || null;
111
+ return {
112
+ status: metadata ? "pass" : "warn",
113
+ detail: metadata ? state.bootstrapArtifacts.sources.deployment : "",
114
+ evidence: { deployment: metadata, source: state.bootstrapArtifacts.sources.deployment || null },
115
+ };
116
+ },
117
+ },
118
+ {
119
+ id: "schemas-defined",
120
+ severity: "medium",
121
+ titleKey: "quality.phase.check.schemas.title",
122
+ recommendationKey: "quality.phase.check.schemas.recommendation",
123
+ evaluate(state) {
124
+ const inputReady = hasSchema(state.bootstrapArtifacts.intake?.inputSchema) || hasSchema(state.contract.data?.system?.inputSchema);
125
+ const outputReady = hasSchema(state.bootstrapArtifacts.intake?.outputSchema) || hasSchema(state.contract.data?.system?.outputSchema);
126
+ const missing = [];
127
+ if (!inputReady) missing.push("input");
128
+ if (!outputReady) missing.push("output");
129
+ return {
130
+ status: missing.length ? "warn" : "pass",
131
+ detail: missing.join(", "),
132
+ evidence: { missing },
133
+ };
134
+ },
135
+ },
136
+ {
137
+ id: "behavior-rules-documented",
138
+ severity: "low",
139
+ titleKey: "quality.phase.check.behaviorRules.title",
140
+ recommendationKey: "quality.phase.check.behaviorRules.recommendation",
141
+ evaluate(state) {
142
+ const rules = nonEmptyList(state.bootstrapArtifacts.intake?.behaviorRules || state.contract.data?.system?.behaviorRules || []);
143
+ return {
144
+ status: rules.length ? "pass" : "warn",
145
+ detail: String(rules.length),
146
+ evidence: { count: rules.length },
147
+ };
148
+ },
149
+ },
150
+ {
151
+ id: "task-plan-synced",
152
+ severity: "low",
153
+ titleKey: "quality.phase.check.taskPlanSynced.title",
154
+ recommendationKey: "quality.phase.check.taskPlanSynced.recommendation",
155
+ evaluate(state) {
156
+ const drifted = state.drift.includes("task_plan");
157
+ return {
158
+ status: drifted ? "warn" : "pass",
159
+ detail: drifted ? "task_plan" : "",
160
+ evidence: { drift: state.drift },
161
+ };
162
+ },
163
+ },
164
+ ],
165
+ },
166
+ P: {
167
+ id: "P",
168
+ titleKey: "quality.phase.P.title",
169
+ checks: [
170
+ {
171
+ id: "env-keys-present",
172
+ severity: "high",
173
+ titleKey: "quality.phase.check.envKeys.title",
174
+ recommendationKey: "quality.phase.check.envKeys.recommendation",
175
+ evaluate(state) {
176
+ return {
177
+ status: state.envState.missingKeys.length ? "fail" : "pass",
178
+ detail: state.envState.missingKeys.join(", "),
179
+ evidence: { missingKeys: state.envState.missingKeys },
180
+ };
181
+ },
182
+ },
183
+ {
184
+ id: "verification-evidence",
185
+ severity: "medium",
186
+ titleKey: "quality.phase.check.verificationEvidence.title",
187
+ recommendationKey: "quality.phase.check.verificationEvidence.recommendation",
188
+ evaluate(state) {
189
+ const latestTest = latestVerification(state, "test");
190
+ const latestSmoke = latestVerification(state, "smoke");
191
+ const hasEvidence = (latestTest && latestTest.status === "passed") || (latestSmoke && latestSmoke.status === "passed");
192
+ return {
193
+ status: hasEvidence ? "pass" : "warn",
194
+ detail: hasEvidence ? "verified" : "missing",
195
+ evidence: { test: latestTest || null, smoke: latestSmoke || null },
196
+ };
197
+ },
198
+ },
199
+ {
200
+ id: "critical-findings-closed",
201
+ severity: "high",
202
+ titleKey: "quality.phase.check.criticalFindings.title",
203
+ recommendationKey: "quality.phase.check.criticalFindings.recommendation",
204
+ evaluate(state) {
205
+ const critical = state.derived.openFindings.filter((finding) => state.normalizeFindingSeverity(finding) === "critical");
206
+ return {
207
+ status: critical.length ? "fail" : "pass",
208
+ detail: String(critical.length),
209
+ evidence: { findingIds: critical.map((finding) => finding.id) },
210
+ };
211
+ },
212
+ },
213
+ {
214
+ id: "blocked-tasks-documented",
215
+ severity: "medium",
216
+ titleKey: "quality.phase.check.blockedTasksDocumented.title",
217
+ recommendationKey: "quality.phase.check.blockedTasksDocumented.recommendation",
218
+ evaluate(state) {
219
+ const undocumented = state.derived.blockers.filter((task) => !hasText(task.blocker));
220
+ return {
221
+ status: undocumented.length ? "fail" : "pass",
222
+ detail: String(undocumented.length),
223
+ evidence: { taskIds: undocumented.map((task) => task.id) },
224
+ };
225
+ },
226
+ },
227
+ ],
228
+ },
229
+ E: {
230
+ id: "E",
231
+ titleKey: "quality.phase.E.title",
232
+ checks: [
233
+ {
234
+ id: "backlog-integrity",
235
+ severity: "high",
236
+ titleKey: "quality.phase.check.backlogIntegrity.title",
237
+ recommendationKey: "quality.phase.check.backlogIntegrity.recommendation",
238
+ evaluate(state) {
239
+ const hasIssues = state.derived.circularDeps.length || state.derived.phantomDeps.length || state.derived.phantomParents.length || state.derived.hierarchyCycles.length;
240
+ return {
241
+ status: hasIssues ? "fail" : "pass",
242
+ detail: makeBacklogIssueDetail(state),
243
+ evidence: {
244
+ circularDeps: state.derived.circularDeps,
245
+ phantomDeps: state.derived.phantomDeps,
246
+ phantomParents: state.derived.phantomParents,
247
+ hierarchyCycles: state.derived.hierarchyCycles,
248
+ },
249
+ };
250
+ },
251
+ },
252
+ {
253
+ id: "required-acceptance",
254
+ severity: "medium",
255
+ titleKey: "quality.phase.check.requiredAcceptance.title",
256
+ recommendationKey: "quality.phase.check.requiredAcceptance.recommendation",
257
+ evaluate(state) {
258
+ const missing = state.derived.actionableTasks.filter(
259
+ (task) => task.required !== false && (!Array.isArray(task.acceptance) || task.acceptance.length === 0),
260
+ );
261
+ return {
262
+ status: missing.length ? "fail" : "pass",
263
+ detail: String(missing.length),
264
+ evidence: { taskIds: missing.map((task) => task.id) },
265
+ };
266
+ },
267
+ },
268
+ {
269
+ id: "task-tracking",
270
+ severity: "medium",
271
+ titleKey: "quality.phase.check.taskTracking.title",
272
+ recommendationKey: "quality.phase.check.taskTracking.recommendation",
273
+ evaluate(state) {
274
+ const tracked = state.derived.actionableTasks.some((task) => Array.isArray(task.history) && task.history.some((entry) => entry.action && entry.action !== "create"));
275
+ return {
276
+ status: tracked ? "pass" : "warn",
277
+ detail: tracked ? "tracked" : "missing",
278
+ evidence: { tracked },
279
+ };
280
+ },
281
+ },
282
+ {
283
+ id: "docs-synced-structure",
284
+ severity: "medium",
285
+ titleKey: "quality.phase.check.docsSynced.title",
286
+ recommendationKey: "quality.phase.check.docsSynced.recommendation",
287
+ evaluate(state) {
288
+ return {
289
+ status: state.drift.length ? "fail" : "pass",
290
+ detail: state.drift.join(", "),
291
+ evidence: { drift: state.drift },
292
+ };
293
+ },
294
+ },
295
+ {
296
+ id: "repo-committed",
297
+ severity: "medium",
298
+ titleKey: "quality.phase.check.repoCommitted.title",
299
+ recommendationKey: "quality.phase.check.repoCommitted.recommendation",
300
+ evaluate(state) {
301
+ if (!state.repo.available) return { status: "skip" };
302
+ return {
303
+ status: state.repo.clean ? "pass" : "warn",
304
+ detail: state.repo.clean ? "" : `staged:${state.repo.staged},unstaged:${state.repo.unstaged},untracked:${state.repo.untracked}`,
305
+ evidence: { clean: state.repo.clean, ahead: state.repo.ahead, behind: state.repo.behind },
306
+ };
307
+ },
308
+ },
309
+ ],
310
+ },
311
+ R: {
312
+ id: "R",
313
+ titleKey: "quality.phase.R.title",
314
+ checks: [
315
+ {
316
+ id: "findings-high-closed",
317
+ severity: "high",
318
+ titleKey: "quality.phase.check.findingsHighClosed.title",
319
+ recommendationKey: "quality.phase.check.findingsHighClosed.recommendation",
320
+ evaluate(state) {
321
+ const open = state.derived.openFindings.filter((finding) => {
322
+ const severity = state.normalizeFindingSeverity(finding);
323
+ return severity === "critical" || severity === "high";
324
+ });
325
+ return {
326
+ status: open.length ? "fail" : "pass",
327
+ detail: String(open.length),
328
+ evidence: { findingIds: open.map((finding) => finding.id) },
329
+ };
330
+ },
331
+ },
332
+ {
333
+ id: "docs-synced-refinement",
334
+ severity: "medium",
335
+ titleKey: "quality.phase.check.docsSynced.title",
336
+ recommendationKey: "quality.phase.check.docsSynced.recommendation",
337
+ evaluate(state) {
338
+ return {
339
+ status: state.drift.length ? "fail" : "pass",
340
+ detail: state.drift.join(", "),
341
+ evidence: { drift: state.drift },
342
+ };
343
+ },
344
+ },
345
+ {
346
+ id: "contract-consistent",
347
+ severity: "high",
348
+ titleKey: "quality.phase.check.contractConsistent.title",
349
+ recommendationKey: "quality.phase.check.contractConsistent.recommendation",
350
+ evaluate(state) {
351
+ const contradictions = state.bootstrapArtifacts.qualityReport?.contradictions || [];
352
+ const invalidContract = !state.contract.exists || !state.contract.valid;
353
+ return {
354
+ status: invalidContract || contradictions.length ? "fail" : "pass",
355
+ detail: invalidContract ? "contract" : contradictions.join(", "),
356
+ evidence: { invalidContract, contradictions },
357
+ };
358
+ },
359
+ },
360
+ {
361
+ id: "review-evidence",
362
+ severity: "low",
363
+ titleKey: "quality.phase.check.reviewEvidence.title",
364
+ recommendationKey: "quality.phase.check.reviewEvidence.recommendation",
365
+ evaluate(state) {
366
+ if (!state.verificationConfig.reviewRequired) return { status: "skip" };
367
+ const latestReview = latestVerification(state, "review");
368
+ return {
369
+ status: latestReview?.status === "passed" ? "pass" : "warn",
370
+ detail: latestReview?.status || "",
371
+ evidence: { review: latestReview || null },
372
+ };
373
+ },
374
+ },
375
+ ],
376
+ },
377
+ A: {
378
+ id: "A",
379
+ titleKey: "quality.phase.A.title",
380
+ checks: [
381
+ {
382
+ id: "tmp-clean",
383
+ severity: "low",
384
+ titleKey: "quality.phase.check.tmpClean.title",
385
+ recommendationKey: "quality.phase.check.tmpClean.recommendation",
386
+ evaluate(state) {
387
+ const tmpDir = state.context.paths.tmpDir;
388
+ if (!tmpDir) return { status: "skip" };
389
+ const entries = state.fileExists(tmpDir)
390
+ ? state.fs.readdirSync(tmpDir).filter((entry) => entry !== "upgrade-backups")
391
+ : [];
392
+ return {
393
+ status: entries.length ? "warn" : "pass",
394
+ detail: entries.join(", "),
395
+ evidence: { entries },
396
+ };
397
+ },
398
+ },
399
+ {
400
+ id: "deployment-ready",
401
+ severity: "high",
402
+ titleKey: "quality.phase.check.deploymentReady.title",
403
+ recommendationKey: "quality.phase.check.deploymentReady.recommendation",
404
+ evaluate(state) {
405
+ const deployment = state.bootstrapArtifacts.intake?.deployment || null;
406
+ const missing = [];
407
+ if (!deployment?.mode) missing.push("mode");
408
+ if (!deployment?.smokeCommand) missing.push("smokeCommand");
409
+ return {
410
+ status: missing.length ? "fail" : "pass",
411
+ detail: missing.join(", "),
412
+ evidence: { deployment, missing },
413
+ };
414
+ },
415
+ },
416
+ {
417
+ id: "smoke-passed",
418
+ severity: "high",
419
+ titleKey: "quality.phase.check.smokePassed.title",
420
+ recommendationKey: "quality.phase.check.smokePassed.recommendation",
421
+ evaluate(state) {
422
+ const smokeConfigured = state.verificationConfig.smokeCommands.length > 0;
423
+ const latestSmoke = latestVerification(state, "smoke");
424
+ if (!smokeConfigured) {
425
+ return { status: "warn", detail: "not_configured", evidence: { smoke: null } };
426
+ }
427
+ return {
428
+ status: latestSmoke?.status === "passed" ? "pass" : "fail",
429
+ detail: latestSmoke?.status || "missing",
430
+ evidence: { smoke: latestSmoke || null },
431
+ };
432
+ },
433
+ },
434
+ {
435
+ id: "docs-synced-automation",
436
+ severity: "medium",
437
+ titleKey: "quality.phase.check.docsSynced.title",
438
+ recommendationKey: "quality.phase.check.docsSynced.recommendation",
439
+ evaluate(state) {
440
+ return {
441
+ status: state.drift.length ? "fail" : "pass",
442
+ detail: state.drift.join(", "),
443
+ evidence: { drift: state.drift },
444
+ };
445
+ },
446
+ },
447
+ {
448
+ id: "remote-pushed",
449
+ severity: "high",
450
+ titleKey: "quality.phase.check.remotePushed.title",
451
+ recommendationKey: "quality.phase.check.remotePushed.recommendation",
452
+ evaluate(state) {
453
+ const versionControl = state.bootstrapArtifacts.intake?.versionControl || null;
454
+ if (!versionControl?.remote) return { status: "skip" };
455
+ if (!state.repo.available || !state.repo.hasUpstream) {
456
+ return { status: "fail", detail: "no_upstream", evidence: { repo: state.repo } };
457
+ }
458
+ return {
459
+ status: state.repo.ahead > 0 ? "fail" : "pass",
460
+ detail: state.repo.ahead > 0 ? String(state.repo.ahead) : "",
461
+ evidence: { ahead: state.repo.ahead, behind: state.repo.behind },
462
+ };
463
+ },
464
+ },
465
+ {
466
+ id: "repo-clean-automation",
467
+ severity: "high",
468
+ titleKey: "quality.phase.check.repoClean.title",
469
+ recommendationKey: "quality.phase.check.repoClean.recommendation",
470
+ evaluate(state) {
471
+ if (!state.repo.available) return { status: "skip" };
472
+ return {
473
+ status: state.repo.clean ? "pass" : "fail",
474
+ detail: state.repo.clean ? "" : `staged:${state.repo.staged},unstaged:${state.repo.unstaged},untracked:${state.repo.untracked}`,
475
+ evidence: { clean: state.repo.clean },
476
+ };
477
+ },
478
+ },
479
+ ],
480
+ },
481
+ };
482
+
483
+ module.exports = {
484
+ PHASE_DOD,
485
+ };