harness-everything 0.35.0 → 0.36.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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +2 -0
- package/AGENTS.md +3 -0
- package/CHANGELOG.md +17 -0
- package/ci/mechanism-36-verify-timeout.test.js +534 -0
- package/ci/mechanism-37-portability-lint.test.js +71 -0
- package/ci/mechanism-38-verify-all.test.js +112 -0
- package/ci/mechanism-39-opencode-verify-outcomes.test.js +374 -0
- package/ci/portability-lint.js +288 -0
- package/docs/repository-contract.md +1 -1
- package/opencode-plugin/README.md +33 -2
- package/opencode-plugin/hooks/verify.js +87 -11
- package/opencode-plugin/index.mjs +216 -25
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +3 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/sbom.cdx.json +7 -7
- package/scripts/verify-all.js +314 -0
|
@@ -21,12 +21,18 @@
|
|
|
21
21
|
* once locked) + failure tracking inside session.idle
|
|
22
22
|
* compliance.js -> folded into the same state writes, no separate stage
|
|
23
23
|
*
|
|
24
|
-
* The circuit breaker trips on repeated *verification* failures
|
|
25
|
-
*
|
|
24
|
+
* The circuit breaker trips on repeated *verification* failures of the same
|
|
25
|
+
* underlying check (same command + normalized failure evidence: the first
|
|
26
|
+
* test-level failing marker with its suite header as context, or a digest of
|
|
27
|
+
* the full captured stdout+stderr when no test-level marker exists), not on
|
|
28
|
+
* arbitrary tool failures: tool.execute.after
|
|
26
29
|
* gives back `{title, output, metadata}` for every tool with no normalized
|
|
27
30
|
* success/failure field, so there is no reliable cross-tool failure signal to
|
|
28
31
|
* key a signature on. Verification failures are self-produced and already
|
|
29
|
-
* normalized, so that is the honest signal to use.
|
|
32
|
+
* normalized, so that is the honest signal to use. Uncompleted verifications
|
|
33
|
+
* (timeout, execution error such as ENOBUFS/ENOENT) are reported as failures
|
|
34
|
+
* but never counted toward the same-check threshold, because no failing check
|
|
35
|
+
* was actually confirmed.
|
|
30
36
|
*/
|
|
31
37
|
|
|
32
38
|
import { homedir } from "node:os"
|
|
@@ -269,8 +275,84 @@ function defaultCompliance() {
|
|
|
269
275
|
}
|
|
270
276
|
}
|
|
271
277
|
|
|
272
|
-
|
|
273
|
-
|
|
278
|
+
// Test-level evidence of an actual failing check, in line order: TAP, Node
|
|
279
|
+
// test-runner symbols, Jest test blocks, indented Mocha numbering, and
|
|
280
|
+
// assertion/error lines (which may also be indented). `✖ failing tests:`
|
|
281
|
+
// (the node:test summary header) is skipped in favor of the specific test
|
|
282
|
+
// line that follows it, and Jest's `● Console` section is not failure
|
|
283
|
+
// evidence: neither the heading nor its indented payload (logged output,
|
|
284
|
+
// including deeper-indented lines beginning with "●") becomes a marker.
|
|
285
|
+
const FAILURE_MARKER_PATTERNS = [/^not ok\b.*$/m, /^\s*[✖✕]\s*(?!\s*failing\b).+$/m, /^\s*●\s+(?!Console\b|Test suite failed to run\b|Validation Error\b).+$/m, /^[ \t]*\d+\)[ \t]+.+$/m, /^[ \t]*[A-Za-z]*Error\b.*$/m];
|
|
286
|
+
|
|
287
|
+
// Suite/file-level context, never an identity on its own: Jest prints
|
|
288
|
+
// `FAIL <file>` (or `● Test suite failed to run`) above the failing test
|
|
289
|
+
// blocks, so different tests in one file would otherwise share a signature.
|
|
290
|
+
// A context line is carried into the signature only when a test-level marker
|
|
291
|
+
// follows it; without one, the full-evidence digest decides.
|
|
292
|
+
const CONTEXT_MARKER_PATTERNS = [/^FAIL\b.*$/m, /^\s*●\s+(?:Test suite failed to run|Validation Error)\b.*$/m];
|
|
293
|
+
|
|
294
|
+
function normalizeFailureLine(line) {
|
|
295
|
+
let text = String(line || "").replace(/\s+/g, " ").trim();
|
|
296
|
+
text = text.replace(/\s*\(\d+(?:\.\d+)?\s*m?s\)\s*$/, ""); // runner timing suffixes
|
|
297
|
+
text = text.replace(/^not ok \d+\b/, "not ok"); // TAP plan numbering
|
|
298
|
+
return text.slice(0, 160);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function firstFailureMarker(text) {
|
|
302
|
+
const lines = String(text || "").replace(/\u001b\[[0-9;]*m/g, "").split(/\r?\n/);
|
|
303
|
+
let context = null;
|
|
304
|
+
let consoleIndent = null;
|
|
305
|
+
for (const line of lines) {
|
|
306
|
+
const bullet = /^([ \t]*)●[ \t]+/.exec(line);
|
|
307
|
+
if (consoleIndent !== null) {
|
|
308
|
+
// Jest's Console block contains indented log payloads, which may
|
|
309
|
+
// themselves begin with "●". Only a peer/parent section bullet or a
|
|
310
|
+
// non-indented line may end the block; deeper bullets remain data.
|
|
311
|
+
if (bullet) {
|
|
312
|
+
if (bullet[1].length > consoleIndent) continue;
|
|
313
|
+
consoleIndent = null;
|
|
314
|
+
} else if (line.trim() === "" || /^[ \t]/.test(line)) {
|
|
315
|
+
continue;
|
|
316
|
+
} else {
|
|
317
|
+
consoleIndent = null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (bullet && /^Console\b/.test(line.slice(bullet[0].length))) {
|
|
321
|
+
consoleIndent = bullet[1].length;
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
if (FAILURE_MARKER_PATTERNS.some((pattern) => pattern.test(line))) {
|
|
325
|
+
const normalized = normalizeFailureLine(line);
|
|
326
|
+
if (normalized) return context ? `${context}: ${normalized}` : normalized;
|
|
327
|
+
}
|
|
328
|
+
if (CONTEXT_MARKER_PATTERNS.some((pattern) => pattern.test(line))) {
|
|
329
|
+
const normalized = normalizeFailureLine(line);
|
|
330
|
+
if (normalized) context = normalized;
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// A stable, normalized signature: command + outcome + exit condition + the
|
|
337
|
+
// first test-level failing marker (with a suite/file context line when the
|
|
338
|
+
// runner printed one). Without a test-level marker the signature falls back
|
|
339
|
+
// to a digest of the full captured evidence (stdout and stderr both), so
|
|
340
|
+
// suite-header-only or banner-only prefixes from *different* failures can
|
|
341
|
+
// never collide (identical byte-for-byte evidence still matches, which is
|
|
342
|
+
// the honest reading: the same observation repeated). The user-visible
|
|
343
|
+
// `error` is a bounded diagnostic and is only the fallback when no full
|
|
344
|
+
// evidence was kept.
|
|
345
|
+
function buildFailureSignature(result) {
|
|
346
|
+
const evidence = String((result && (result.evidence || result.error)) || "");
|
|
347
|
+
const marker = firstFailureMarker(evidence);
|
|
348
|
+
const exit =
|
|
349
|
+
result && Number.isInteger(result.exitCode) ? `exit=${result.exitCode}`
|
|
350
|
+
: result && result.signal ? `signal=${result.signal}`
|
|
351
|
+
: "exit=unknown";
|
|
352
|
+
const base = `${(result && result.command) || "verification"} [${(result && result.outcome) || "unknown"}] ${exit}`;
|
|
353
|
+
if (marker) return `${base}: ${marker}`;
|
|
354
|
+
const digest = createHash("sha256").update(evidence.replace(/\s+/g, " ").trim()).digest("hex").slice(0, 16);
|
|
355
|
+
return `${base}: unrecognized-output ${digest}`;
|
|
274
356
|
}
|
|
275
357
|
|
|
276
358
|
function getAvailableScripts(cwd) {
|
|
@@ -283,6 +365,81 @@ function getAvailableScripts(cwd) {
|
|
|
283
365
|
}
|
|
284
366
|
}
|
|
285
367
|
|
|
368
|
+
// Verification budget for the npm scripts run on session.idle. The Harness
|
|
369
|
+
// self-regression suite needs ~2 minutes on a laptop, so the original 60s cap
|
|
370
|
+
// killed healthy runs and reported them as failures (same truncated stdout as
|
|
371
|
+
// the failure signature, three times in a row -> forced reflection).
|
|
372
|
+
// Known limits of this synchronous runner, kept honest: execSync blocks the
|
|
373
|
+
// plugin's event loop for the duration of each command (worst case one budget
|
|
374
|
+
// per script), and a command whose output exceeds VERIFY_MAX_BUFFER is
|
|
375
|
+
// classified as an execution error - never as a failed check - so buffer
|
|
376
|
+
// pressure cannot impersonate a test failure.
|
|
377
|
+
const VERIFY_TIMEOUT_ENV = "HARNESS_VERIFY_TIMEOUT_MS"
|
|
378
|
+
const DEFAULT_VERIFY_TIMEOUT_MS = 600000
|
|
379
|
+
const VERIFY_MAX_BUFFER = 16 * 1024 * 1024
|
|
380
|
+
const VERIFY_OUTPUT_LIMIT = 2000
|
|
381
|
+
|
|
382
|
+
function verifyTimeoutMs() {
|
|
383
|
+
const raw = Number(process.env[VERIFY_TIMEOUT_ENV])
|
|
384
|
+
return Number.isFinite(raw) && raw > 0 ? raw : DEFAULT_VERIFY_TIMEOUT_MS
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Full failure evidence for signature analysis: both captured streams, in
|
|
388
|
+
// full. execSync already bounds each stream at VERIFY_MAX_BUFFER, and the old
|
|
389
|
+
// stdout-only 2 KiB prefix cut happened *before* buildFailureSignature() ran,
|
|
390
|
+
// so distinct failures behind a shared banner collapsed into one signature.
|
|
391
|
+
function verificationEvidence(stdout, stderr) {
|
|
392
|
+
const out = String(stdout || "")
|
|
393
|
+
const err = String(stderr || "")
|
|
394
|
+
if (out && err) return `${out}\nstderr:\n${err}`
|
|
395
|
+
return out || err
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// Bounded, human-readable excerpt that keeps both ends: the npm banner at the
|
|
399
|
+
// head and the failure marker/summary at the tail survive truncation.
|
|
400
|
+
function boundedDiagnostic(text, limit) {
|
|
401
|
+
const value = String(text || "")
|
|
402
|
+
if (value.length <= limit) return value
|
|
403
|
+
const half = Math.floor(limit / 2)
|
|
404
|
+
return `${value.slice(0, half)}\n... [${value.length - half * 2} chars omitted] ...\n${value.slice(-half)}`
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// User-visible diagnostic: both streams get their own budget, so a huge
|
|
408
|
+
// stdout cannot push a short stderr (where jest writes assertions) out of
|
|
409
|
+
// the report. A single non-empty stream stays label-free.
|
|
410
|
+
function verificationDiagnostic(stdout, stderr) {
|
|
411
|
+
const out = String(stdout || "")
|
|
412
|
+
const err = String(stderr || "")
|
|
413
|
+
if (out && err) {
|
|
414
|
+
const share = Math.floor(VERIFY_OUTPUT_LIMIT / 2)
|
|
415
|
+
return `stdout:\n${boundedDiagnostic(out, share)}\nstderr:\n${boundedDiagnostic(err, share)}`
|
|
416
|
+
}
|
|
417
|
+
return boundedDiagnostic(out || err, VERIFY_OUTPUT_LIMIT)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Outcome taxonomy:
|
|
421
|
+
// pass the command exited 0
|
|
422
|
+
// test_failure a real non-zero exit status: a check actually failed
|
|
423
|
+
// timeout the configured budget expired: ETIMEDOUT, or a kill with
|
|
424
|
+
// no exit status once the budget elapsed: no verdict
|
|
425
|
+
// execution_error spawn-level failure, or an external signal (SIGTERM/
|
|
426
|
+
// SIGINT/SIGKILL from the user or host) before the budget:
|
|
427
|
+
// no verdict
|
|
428
|
+
// A signal alone is not a budget timeout - only expiry is. Only
|
|
429
|
+
// test_failure feeds the same-check Rule-of-3 counter.
|
|
430
|
+
function classifyVerificationError(error, startedAt, timeout) {
|
|
431
|
+
const code = String((error && error.code) || "")
|
|
432
|
+
const signal = String((error && error.signal) || "")
|
|
433
|
+
const exitCode = error && Number.isInteger(error.status) ? error.status : null
|
|
434
|
+
const budgetExpired =
|
|
435
|
+
code === "ETIMEDOUT" || (exitCode === null && Date.now() - startedAt >= timeout)
|
|
436
|
+
if (budgetExpired) return { outcome: "timeout", exitCode, signal, timedOut: true }
|
|
437
|
+
if (exitCode === null || ["ENOBUFS", "ENOENT", "EACCES", "EAGAIN", "ENOMEM"].includes(code)) {
|
|
438
|
+
return { outcome: "execution_error", exitCode, signal, timedOut: false }
|
|
439
|
+
}
|
|
440
|
+
return { outcome: "test_failure", exitCode, signal, timedOut: false }
|
|
441
|
+
}
|
|
442
|
+
|
|
286
443
|
function runVerification(cwd) {
|
|
287
444
|
const scripts = getAvailableScripts(cwd)
|
|
288
445
|
const commands = [
|
|
@@ -295,13 +452,30 @@ function runVerification(cwd) {
|
|
|
295
452
|
return { allPassed: true, skipped: true, results: [] }
|
|
296
453
|
}
|
|
297
454
|
|
|
455
|
+
const timeout = verifyTimeoutMs()
|
|
298
456
|
const results = []
|
|
299
457
|
for (const { cmd } of commands) {
|
|
458
|
+
const startedAt = Date.now()
|
|
300
459
|
try {
|
|
301
|
-
execSync(cmd, { cwd, stdio: "pipe", timeout:
|
|
302
|
-
results.push({ command: cmd, success: true })
|
|
460
|
+
execSync(cmd, { cwd, stdio: "pipe", timeout, maxBuffer: VERIFY_MAX_BUFFER })
|
|
461
|
+
results.push({ command: cmd, success: true, outcome: "pass", exitCode: 0, signal: null, timedOut: false })
|
|
303
462
|
} catch (e) {
|
|
304
|
-
|
|
463
|
+
const stdout = (e.stdout || "").toString()
|
|
464
|
+
const stderr = (e.stderr || "").toString()
|
|
465
|
+
const output = verificationEvidence(stdout, stderr) || String((e && e.message) || "")
|
|
466
|
+
const { outcome, exitCode, signal, timedOut } = classifyVerificationError(e, startedAt, timeout)
|
|
467
|
+
let error
|
|
468
|
+
if (outcome === "timeout") {
|
|
469
|
+
error = `timed out after ${Math.round(timeout / 1000)}s (set ${VERIFY_TIMEOUT_ENV} to raise it); last output: ${output.slice(0, 160).replace(/\s+/g, " ").trim() || "(none)"}`
|
|
470
|
+
} else if (outcome === "execution_error") {
|
|
471
|
+
const reason = String((e && e.code) || (signal ? `signal ${signal}` : "no exit status"))
|
|
472
|
+
error = `could not run to completion (execution error: ${reason}); output: ${output.slice(0, 200).replace(/\s+/g, " ").trim() || "(none)"}`
|
|
473
|
+
} else {
|
|
474
|
+
error = verificationDiagnostic(stdout, stderr) || String((e && e.message) || "")
|
|
475
|
+
}
|
|
476
|
+
const result = { command: cmd, success: false, outcome, exitCode, signal, timedOut, error }
|
|
477
|
+
if (outcome === "test_failure" && output) result.evidence = output
|
|
478
|
+
results.push(result)
|
|
305
479
|
}
|
|
306
480
|
}
|
|
307
481
|
return { allPassed: results.every((r) => r.success), skipped: false, results }
|
|
@@ -697,29 +871,46 @@ export const HarnessEnforcement = async ({ client, directory }) => {
|
|
|
697
871
|
return
|
|
698
872
|
}
|
|
699
873
|
|
|
700
|
-
const failing = results.
|
|
701
|
-
const
|
|
702
|
-
const
|
|
874
|
+
const failing = results.filter((r) => !r.success)
|
|
875
|
+
const confirmed = failing.find((r) => r.outcome === "test_failure")
|
|
876
|
+
const primary = confirmed || failing[0]
|
|
703
877
|
state.followUpPending = true
|
|
704
878
|
saveJSON(editStateFile, state)
|
|
705
879
|
|
|
706
880
|
let text
|
|
707
|
-
if (
|
|
708
|
-
|
|
881
|
+
if (!confirmed) {
|
|
882
|
+
// Timeout or execution error: no check produced a verdict, so this
|
|
883
|
+
// must not advance the same-check Rule-of-3 counter. Report it as an
|
|
884
|
+
// uncompleted verification and stay honest about what is unknown.
|
|
885
|
+
const detail =
|
|
886
|
+
primary.outcome === "timeout"
|
|
887
|
+
? `timed out (budget ${Math.round(verifyTimeoutMs() / 1000)}s; set ${VERIFY_TIMEOUT_ENV} to raise it)`
|
|
888
|
+
: "could not run to completion (execution error, not a confirmed test failure)"
|
|
709
889
|
text =
|
|
710
|
-
`Harness: "${
|
|
711
|
-
`
|
|
712
|
-
|
|
713
|
-
`
|
|
714
|
-
`After the report is recorded, resume with a different approach. Failure: ${failing.error || "(no output captured)"}`
|
|
715
|
-
recordCompliance(complianceFile, (c) => {
|
|
716
|
-
c.circuitBreakerTrips++
|
|
717
|
-
c.reflectionsForced++
|
|
718
|
-
})
|
|
890
|
+
`Harness: verification did not complete - "${primary.command}" ${detail}. ` +
|
|
891
|
+
`No failing check was confirmed, so this does NOT count toward the repeated-failure reflection. ` +
|
|
892
|
+
`Diagnostics: ${primary.error || "(no output captured)"}. ` +
|
|
893
|
+
`Fix the cause (or raise the budget) and verify again; a new edit re-runs verification.`
|
|
719
894
|
} else {
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
895
|
+
const signature = buildFailureSignature(confirmed)
|
|
896
|
+
const trip = tripBreaker(breakerFile, signature)
|
|
897
|
+
if (trip.action === "force_reflection") {
|
|
898
|
+
const { stateDir } = pathsFor(activeSession)
|
|
899
|
+
text =
|
|
900
|
+
`Harness: "${confirmed.command}" has now failed 3 times with the same error. Stop and reflect ` +
|
|
901
|
+
`before retrying. Write a report to "${reflectionReportPath(stateDir)}" with sections ` +
|
|
902
|
+
`## Goal, ## Failed Attempts, ## Verified Facts, ## Diagnosis, and ## Decision. ` +
|
|
903
|
+
`The Decision must begin with RESUME: or ESCALATE:. Include reflection token: ${trip.reflectionToken}. ` +
|
|
904
|
+
`After the report is recorded, resume with a different approach. Failure: ${confirmed.error || "(no output captured)"}`
|
|
905
|
+
recordCompliance(complianceFile, (c) => {
|
|
906
|
+
c.circuitBreakerTrips++
|
|
907
|
+
c.reflectionsForced++
|
|
908
|
+
})
|
|
909
|
+
} else {
|
|
910
|
+
text =
|
|
911
|
+
`Harness: verification failed (${trip.remaining} retries before a forced reflection). ` +
|
|
912
|
+
`"${confirmed.command}" - ${confirmed.error || "(no output captured)"}. Fix it and this will re-run automatically.`
|
|
913
|
+
}
|
|
723
914
|
}
|
|
724
915
|
|
|
725
916
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "harness-everything",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"description": "Harness OS: An Orchestrated Agent Operating System for AI-driven Development",
|
|
5
5
|
"main": "harness-everything/scripts/bootstrap.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"test": "node self-evolve/scripts/self-regression.js",
|
|
11
11
|
"self-regression": "node self-evolve/scripts/self-regression.js",
|
|
12
|
+
"verify:all": "node scripts/verify-all.js",
|
|
12
13
|
"test:mechanism": "node ci/mechanism-test.js",
|
|
13
14
|
"test:opencode:loadability": "node ci/mechanism-30-opencode-plugin-loadability.test.js",
|
|
14
15
|
"test:mutations": "node ci/mutation-check.js",
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
"test:consistency": "node ci/consistency-check.js && node ci/doc-capability-consistency.test.js && node ci/repository-contract.test.js && node ci/mechanism-2ao-plugin-sync.test.js && node ci/prompt-contract-regressions.test.js",
|
|
21
22
|
"test:docs:capabilities": "node ci/doc-capability-consistency.test.js",
|
|
22
23
|
"test:references": "node ci/reference-check.js",
|
|
24
|
+
"test:portability": "node ci/portability-lint.js",
|
|
23
25
|
"test:release": "node ci/release-consistency-check.js && node ci/release-consistency-check.test.js && node ci/mechanism-2x-release-automation.test.js && node ci/mechanism-2x-release-commit-message.test.js && node ci/mechanism-2ap-sbom.test.js",
|
|
24
26
|
"test:release:semantics": "node ci/semantic-release-analyzer.test.mjs",
|
|
25
27
|
"test:install": "node scripts/verify-install.js",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
|
|
3
3
|
"name": "harness-everything",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.36.0",
|
|
5
5
|
"description": "Invariant-first engineering harness for routing, verification, and failure recovery while preserving agent autonomy.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Miya Daniel",
|
package/sbom.cdx.json
CHANGED
|
@@ -2,23 +2,23 @@
|
|
|
2
2
|
"$schema": "https://cyclonedx.org/schema/bom-1.7.schema.json",
|
|
3
3
|
"bomFormat": "CycloneDX",
|
|
4
4
|
"specVersion": "1.7",
|
|
5
|
-
"serialNumber": "urn:uuid:
|
|
5
|
+
"serialNumber": "urn:uuid:81cf2be0-d984-5cb9-9eee-bcd04849c7b9",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-18T23:18:17.342Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.36.0"
|
|
14
14
|
}
|
|
15
15
|
],
|
|
16
16
|
"component": {
|
|
17
17
|
"type": "application",
|
|
18
|
-
"bom-ref": "pkg:npm/harness-everything@0.
|
|
18
|
+
"bom-ref": "pkg:npm/harness-everything@0.36.0",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.
|
|
20
|
+
"version": "0.36.0",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.36.0"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -1999,7 +1999,7 @@
|
|
|
1999
1999
|
]
|
|
2000
2000
|
},
|
|
2001
2001
|
{
|
|
2002
|
-
"ref": "pkg:npm/harness-everything@0.
|
|
2002
|
+
"ref": "pkg:npm/harness-everything@0.36.0",
|
|
2003
2003
|
"dependsOn": [
|
|
2004
2004
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2005
2005
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|