harness-everything 0.36.4 → 0.38.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/CHANGELOG.md +24 -0
- package/ci/fixtures/doc-obligations/providers-s3/cases.json +535 -0
- package/ci/fixtures/doc-obligations/reconciliation-prep/cases.json +70 -11
- package/ci/fixtures/doc-obligations/reconciliation-prep/pending-red.json +24 -556
- package/ci/fixtures/doc-obligations/reconciliation-prep/provider-contract.json +1 -1
- package/ci/fixtures/doc-obligations/reconciliation-s1/cases.json +6842 -0
- package/ci/fixtures/doc-obligations/reconciliation-s1/fail-session-writes.js +116 -0
- package/ci/lib/doc-reconciliation-prep-fixtures.js +116 -10
- package/ci/mechanism-325-3b-preparation.test.js +8 -8
- package/ci/mechanism-325-3b-s1-review.test.js +831 -0
- package/ci/mechanism-325-3b-s1.test.js +188 -0
- package/ci/mechanism-325-3b-s3-providers.test.js +378 -0
- package/ci/prep/doc-reconciliation-red.js +2 -1
- package/docs/doc-workflow-reconciliation.md +333 -28
- package/docs/document-lifecycle.md +4 -4
- package/harness-everything/SKILL.md +1 -1
- package/harness-everything/scripts/doc-lifecycle.js +1 -1
- package/harness-everything/scripts/doc-providers.js +113 -0
- package/hooks/scripts/lib/doc-reconciliation-pilot.js +152 -0
- package/hooks/scripts/lib/workflow-obligations.js +44 -0
- package/hooks/scripts/workflow-disposition.js +2 -0
- package/hooks/scripts/workflow-stop-gate.js +24 -1
- package/opencode-plugin/plugin.json +1 -1
- package/package.json +1 -1
- package/plugins/harness-everything/.codex-plugin/plugin.json +1 -1
- package/plugins/harness-everything/hooks/scripts/lib/doc-reconciliation-pilot.js +152 -0
- package/plugins/harness-everything/hooks/scripts/lib/workflow-obligations.js +44 -0
- package/plugins/harness-everything/hooks/scripts/workflow-disposition.js +2 -0
- package/plugins/harness-everything/hooks/scripts/workflow-stop-gate.js +24 -1
- package/plugins/harness-everything/plugin.json +1 -1
- package/plugins/harness-everything/skills/harness-everything/SKILL.md +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-lifecycle.js +1 -1
- package/plugins/harness-everything/skills/harness-everything/scripts/doc-providers.js +113 -0
- package/sbom.cdx.json +7 -7
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Suggestions describe candidates only; they never select a provider or satisfy evidence.
|
|
4
|
+
const AVAILABLE = 'active-step capability and host requirements match';
|
|
5
|
+
const NO_METADATA = 'no available provider metadata for the active-step capability';
|
|
6
|
+
const compareText = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
7
|
+
const isRecord = value => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
+
const isText = value => typeof value === 'string' && value.trim().length > 0;
|
|
9
|
+
|
|
10
|
+
function validateInput(input) {
|
|
11
|
+
const diagnostics = [];
|
|
12
|
+
const diagnose = (path, message) => diagnostics.push({ code: 'invalid-input', path, message });
|
|
13
|
+
const text = (value, path) => {
|
|
14
|
+
if (!isText(value)) diagnose(path, 'Expected a nonempty string.');
|
|
15
|
+
};
|
|
16
|
+
const strings = (value, path, distinct = false) => {
|
|
17
|
+
const before = diagnostics.length;
|
|
18
|
+
if (!Array.isArray(value)) {
|
|
19
|
+
diagnose(path, 'Expected an array of nonempty strings.');
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
const seen = new Set();
|
|
23
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
24
|
+
text(value[i], `${path}[${i}]`);
|
|
25
|
+
if (distinct && seen.has(value[i])) diagnose(`${path}[${i}]`, 'Expected distinct strings.');
|
|
26
|
+
seen.add(value[i]);
|
|
27
|
+
}
|
|
28
|
+
return diagnostics.length === before;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
if (!isRecord(input)) {
|
|
32
|
+
diagnose('input', 'Expected an object.');
|
|
33
|
+
return { diagnostics, neededValid: false };
|
|
34
|
+
}
|
|
35
|
+
if (!isRecord(input.activeStep)) {
|
|
36
|
+
diagnose('activeStep', 'Expected an object.');
|
|
37
|
+
} else {
|
|
38
|
+
text(input.activeStep.id, 'activeStep.id');
|
|
39
|
+
text(input.activeStep.stepType, 'activeStep.stepType');
|
|
40
|
+
}
|
|
41
|
+
const neededValid = strings(input.neededCapabilities, 'neededCapabilities', true);
|
|
42
|
+
strings(input.hostCapabilities, 'hostCapabilities', true);
|
|
43
|
+
if (!Array.isArray(input.availableProviderMetadata)) {
|
|
44
|
+
diagnose('availableProviderMetadata', 'Expected an array of metadata objects.');
|
|
45
|
+
} else {
|
|
46
|
+
for (let i = 0; i < input.availableProviderMetadata.length; i += 1) {
|
|
47
|
+
const metadata = input.availableProviderMetadata[i];
|
|
48
|
+
const path = `availableProviderMetadata[${i}]`;
|
|
49
|
+
if (!isRecord(metadata)) {
|
|
50
|
+
diagnose(path, 'Expected a metadata object.');
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
text(metadata.providerId, `${path}.providerId`);
|
|
54
|
+
strings(metadata.capabilities, `${path}.capabilities`, true);
|
|
55
|
+
strings(metadata.stepTypes, `${path}.stepTypes`);
|
|
56
|
+
strings(metadata.requiresHostCapabilities, `${path}.requiresHostCapabilities`);
|
|
57
|
+
if (!isRecord(metadata.locator)) {
|
|
58
|
+
diagnose(`${path}.locator`, 'Expected a locator object.');
|
|
59
|
+
} else {
|
|
60
|
+
if (!['binding', 'tool'].includes(metadata.locator.kind)) {
|
|
61
|
+
diagnose(`${path}.locator.kind`, 'Expected binding or tool.');
|
|
62
|
+
}
|
|
63
|
+
text(metadata.locator.id, `${path}.locator.id`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
diagnostics.sort((a, b) => compareText(a.path, b.path) || compareText(a.message, b.message));
|
|
68
|
+
return { diagnostics, neededValid };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Pure advisory projection of caller-supplied active-step metadata. */
|
|
72
|
+
function suggestDocProviders(input) {
|
|
73
|
+
const { diagnostics, neededValid } = validateInput(input);
|
|
74
|
+
if (diagnostics.length) {
|
|
75
|
+
return {
|
|
76
|
+
suggestions: [],
|
|
77
|
+
unavailable: neededValid
|
|
78
|
+
? [...input.neededCapabilities].sort(compareText).map(capability => ({ capability, reason: 'invalid-input' }))
|
|
79
|
+
: [],
|
|
80
|
+
diagnostics
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const needed = new Set(input.neededCapabilities);
|
|
85
|
+
const host = new Set(input.hostCapabilities);
|
|
86
|
+
const suggestions = [];
|
|
87
|
+
for (const metadata of input.availableProviderMetadata) {
|
|
88
|
+
if (!metadata.stepTypes.includes(input.activeStep.stepType)) continue;
|
|
89
|
+
const missing = metadata.requiresHostCapabilities.filter(capability => !host.has(capability)).sort(compareText);
|
|
90
|
+
const availability = missing.length ? 'unavailable' : 'available';
|
|
91
|
+
const reason = missing.length ? `host lacks ${missing.join(', ')}` : AVAILABLE;
|
|
92
|
+
for (const capability of metadata.capabilities) {
|
|
93
|
+
if (needed.has(capability)) {
|
|
94
|
+
suggestions.push({ capability, providerId: metadata.providerId, availability, reason });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
suggestions.sort((a, b) => compareText(a.capability, b.capability)
|
|
99
|
+
|| compareText(a.providerId, b.providerId)
|
|
100
|
+
|| compareText(a.availability, b.availability)
|
|
101
|
+
|| compareText(a.reason, b.reason));
|
|
102
|
+
|
|
103
|
+
const unavailable = [];
|
|
104
|
+
for (const capability of [...needed].sort(compareText)) {
|
|
105
|
+
const candidates = suggestions.filter(suggestion => suggestion.capability === capability);
|
|
106
|
+
if (candidates.some(candidate => candidate.availability === 'available')) continue;
|
|
107
|
+
const reasons = [...new Set(candidates.map(candidate => candidate.reason))].sort(compareText);
|
|
108
|
+
unavailable.push({ capability, reason: reasons.length ? reasons.join('; ') : NO_METADATA });
|
|
109
|
+
}
|
|
110
|
+
return { suggestions, unavailable };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
module.exports = { suggestDocProviders };
|
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:69acfc0d-3ec2-541a-b60f-99d93b8cf29c",
|
|
6
6
|
"version": 1,
|
|
7
7
|
"metadata": {
|
|
8
|
-
"timestamp": "2026-10-
|
|
8
|
+
"timestamp": "2026-10-21T11:38:13.404Z",
|
|
9
9
|
"tools": [
|
|
10
10
|
{
|
|
11
11
|
"vendor": "Harness Everything",
|
|
12
12
|
"name": "generate-sbom.js",
|
|
13
|
-
"version": "0.
|
|
13
|
+
"version": "0.38.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.38.0",
|
|
19
19
|
"name": "harness-everything",
|
|
20
|
-
"version": "0.
|
|
21
|
-
"purl": "pkg:npm/harness-everything@0.
|
|
20
|
+
"version": "0.38.0",
|
|
21
|
+
"purl": "pkg:npm/harness-everything@0.38.0"
|
|
22
22
|
}
|
|
23
23
|
},
|
|
24
24
|
"components": [
|
|
@@ -2033,7 +2033,7 @@
|
|
|
2033
2033
|
]
|
|
2034
2034
|
},
|
|
2035
2035
|
{
|
|
2036
|
-
"ref": "pkg:npm/harness-everything@0.
|
|
2036
|
+
"ref": "pkg:npm/harness-everything@0.38.0",
|
|
2037
2037
|
"dependsOn": [
|
|
2038
2038
|
"pkg:npm/%40commitlint/cli@19.8.1",
|
|
2039
2039
|
"pkg:npm/%40commitlint/config-conventional@19.8.1",
|