@open-agent-toolkit/cli 0.2.72 → 0.2.74
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/assets/agents/oat-reviewer.md +4 -3
- package/assets/agents/recon-worker.md +42 -8
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +3 -56
- package/assets/docs/cli-utilities/tool-packs.md +4 -4
- package/assets/docs/contributing/code.md +2 -2
- package/assets/docs/contributing/index.md +0 -1
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/reference/troubleshooting.md +17 -40
- package/assets/docs/workflows/projects/artifacts.md +16 -34
- package/assets/docs/workflows/projects/lifecycle.md +20 -19
- package/assets/docs/workflows/skills/explainer-kit.md +180 -440
- package/assets/docs/workflows/skills/index.md +1 -2
- package/assets/docs/workflows/skills/recon.md +114 -23
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +96 -171
- package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
- package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
- package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
- package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
- package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
- package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
- package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
- package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
- package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
- package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
- package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
- package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
- package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
- package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
- package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
- package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
- package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
- package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
- package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
- package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
- package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
- package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
- package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
- package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
- package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
- package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
- package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
- package/assets/skills/oat-project-complete/SKILL.md +168 -349
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
- package/assets/skills/oat-project-document/SKILL.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-lite/SKILL.md +1 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-plan/SKILL.md +5 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
- package/assets/skills/oat-project-summary/SKILL.md +35 -25
- package/assets/skills/oat-wave-execute/SKILL.md +11 -36
- package/assets/skills/oat-wave-program/SKILL.md +13 -37
- package/assets/skills/recon/SKILL.md +103 -40
- package/assets/skills/recon/references/packet-contract.md +150 -67
- package/assets/skills/recon/references/profiles.md +38 -16
- package/assets/skills/recon/references/worker-contract.md +39 -8
- package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
- package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
- package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
- package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
- package/assets/skills/recon/scripts/render-packet.mjs +69 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
- package/assets/skills/subagent-orchestration/SKILL.md +18 -9
- package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +9 -113
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +32 -184
- package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
- package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
- package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
- package/dist/config/oat-config.d.ts +0 -11
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +0 -34
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +0 -8
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +2 -10
- package/dist/validation/project-state.d.ts.map +1 -1
- package/dist/validation/project-state.js +42 -3
- package/package.json +2 -2
- package/assets/docs/contributing/explainer-kit-verification.md +0 -133
- package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
- package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
- package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
- package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
- package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
- package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
- package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
- package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
- package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
- package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
- package/assets/skills/explainer-kit/references/contracts.md +0 -324
- package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
- package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
- package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
- package/assets/skills/explainer-kit/references/visual-review.md +0 -71
- package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
- package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
- package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
- package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
- package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
- package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
- package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
- package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
- package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
- package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
- package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
- package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
- package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
- package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
- package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
- package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
- package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
- package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
- package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
- package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
- package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
- package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
- package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
- package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
- package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
- package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
- package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
- package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
- package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
- package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
- package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
- package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
- package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
- package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
- package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
- package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
- package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
- package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
- package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
- package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
- package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
- package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
- package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
|
@@ -1,400 +0,0 @@
|
|
|
1
|
-
import { createHash } from 'node:crypto';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
canonicalHash,
|
|
7
|
-
validateContract,
|
|
8
|
-
visualReviewRequestId,
|
|
9
|
-
} from './contracts.mjs';
|
|
10
|
-
import { decodeBrowserPng } from './png.mjs';
|
|
11
|
-
|
|
12
|
-
const REQUIRED_VIEWPORTS = Object.freeze(['mobile', 'tablet', 'desktop']);
|
|
13
|
-
|
|
14
|
-
export async function runVisualReview({
|
|
15
|
-
plan,
|
|
16
|
-
rendered,
|
|
17
|
-
evidence,
|
|
18
|
-
visualCritic,
|
|
19
|
-
runRoot,
|
|
20
|
-
}) {
|
|
21
|
-
if (typeof visualCritic !== 'function') {
|
|
22
|
-
throw visualReviewError(
|
|
23
|
-
'Independent visual review requires a visualCritic callback.',
|
|
24
|
-
);
|
|
25
|
-
}
|
|
26
|
-
const { request, snapshots } = await buildVisualReviewRequest({
|
|
27
|
-
plan,
|
|
28
|
-
rendered,
|
|
29
|
-
evidence,
|
|
30
|
-
runRoot,
|
|
31
|
-
});
|
|
32
|
-
const evidenceInput = Object.freeze({
|
|
33
|
-
read: async (path) => {
|
|
34
|
-
const snapshot = snapshots.get(path);
|
|
35
|
-
if (!snapshot) {
|
|
36
|
-
throw visualReviewError(
|
|
37
|
-
`Visual critic requested unbound evidence path ${path}.`,
|
|
38
|
-
);
|
|
39
|
-
}
|
|
40
|
-
return Buffer.from(snapshot.bytes);
|
|
41
|
-
},
|
|
42
|
-
});
|
|
43
|
-
let result;
|
|
44
|
-
try {
|
|
45
|
-
result = await visualCritic(structuredClone(request), evidenceInput);
|
|
46
|
-
} catch {
|
|
47
|
-
throw visualReviewError('Visual critic provider failed.', {
|
|
48
|
-
request,
|
|
49
|
-
kind: 'provider-failure',
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
try {
|
|
53
|
-
await assertSnapshotsUnchanged(runRoot, snapshots);
|
|
54
|
-
} catch {
|
|
55
|
-
throw visualReviewError('Visual review evidence changed during review.', {
|
|
56
|
-
request,
|
|
57
|
-
kind: 'pipeline-failure',
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
const validation = validateContract('visual-review-result', result, {
|
|
61
|
-
visualReviewRequest: request,
|
|
62
|
-
});
|
|
63
|
-
if (!validation.valid) {
|
|
64
|
-
throw visualReviewError('Visual critic returned an invalid result.', {
|
|
65
|
-
request,
|
|
66
|
-
kind: 'provider-failure',
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
return {
|
|
70
|
-
request,
|
|
71
|
-
result: structuredClone(result),
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export async function buildVisualReviewRequest({
|
|
76
|
-
plan,
|
|
77
|
-
rendered,
|
|
78
|
-
evidence,
|
|
79
|
-
runRoot,
|
|
80
|
-
}) {
|
|
81
|
-
if (!Array.isArray(rendered) || !Array.isArray(evidence)) {
|
|
82
|
-
throw visualReviewError(
|
|
83
|
-
'Visual review requires rendered artifacts and browser evidence arrays.',
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
if (typeof runRoot !== 'string' || runRoot.length === 0) {
|
|
87
|
-
throw visualReviewError('Visual review requires a confined run root.');
|
|
88
|
-
}
|
|
89
|
-
const browserBinding = browserBindingFor(evidence);
|
|
90
|
-
const paths = [
|
|
91
|
-
...rendered.map(({ renderedPath }) => renderedPath),
|
|
92
|
-
...evidence.flatMap(({ screenshotPath, metricsPath }) => [
|
|
93
|
-
screenshotPath,
|
|
94
|
-
metricsPath,
|
|
95
|
-
]),
|
|
96
|
-
];
|
|
97
|
-
const snapshots = new Map(
|
|
98
|
-
await Promise.all(
|
|
99
|
-
[...new Set(paths)].map(async (path) => [
|
|
100
|
-
path,
|
|
101
|
-
await captureSnapshot(runRoot, path),
|
|
102
|
-
]),
|
|
103
|
-
),
|
|
104
|
-
);
|
|
105
|
-
for (const item of evidence) {
|
|
106
|
-
const snapshot = snapshots.get(item.screenshotPath);
|
|
107
|
-
let decoded;
|
|
108
|
-
try {
|
|
109
|
-
decoded = decodeBrowserPng(snapshot.bytes);
|
|
110
|
-
} catch (error) {
|
|
111
|
-
throw visualReviewError(
|
|
112
|
-
`Visual review screenshot ${item.screenshotPath} is not a decodable browser PNG: ${error.message}`,
|
|
113
|
-
);
|
|
114
|
-
}
|
|
115
|
-
if (
|
|
116
|
-
typeof item.decodedScreenshotHash !== 'string' ||
|
|
117
|
-
decoded.decodedHash !== item.decodedScreenshotHash
|
|
118
|
-
) {
|
|
119
|
-
throw visualReviewError(
|
|
120
|
-
`Visual review screenshot ${item.screenshotPath} does not match its decoded screenshot hash.`,
|
|
121
|
-
);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
const payload = {
|
|
125
|
-
schemaVersion: 'explainer-kit.visual-review-request/v1',
|
|
126
|
-
browserRuntime: browserBinding.runtime,
|
|
127
|
-
captureIdentity: browserBinding.captureIdentity,
|
|
128
|
-
plan: structuredClone(plan),
|
|
129
|
-
renderedArtifacts: rendered.map(({ artifactId, renderedPath }) => ({
|
|
130
|
-
artifactId,
|
|
131
|
-
renderedPath,
|
|
132
|
-
renderedHash: snapshots.get(renderedPath).hash,
|
|
133
|
-
cohesionObservations: cohesionObservationsFromLedger({
|
|
134
|
-
artifactId,
|
|
135
|
-
content: snapshots.get(renderedPath).bytes,
|
|
136
|
-
contentHash: snapshots.get(renderedPath).hash,
|
|
137
|
-
ledger: plan?.ledger,
|
|
138
|
-
}),
|
|
139
|
-
evidence: evidence
|
|
140
|
-
.filter((item) => item.artifactId === artifactId)
|
|
141
|
-
.map(({ viewport, screenshotPath, metricsPath, captureIdentity }) => ({
|
|
142
|
-
viewport,
|
|
143
|
-
screenshotPath,
|
|
144
|
-
screenshotHash: snapshots.get(screenshotPath).hash,
|
|
145
|
-
metricsPath,
|
|
146
|
-
metricsHash: snapshots.get(metricsPath).hash,
|
|
147
|
-
captureIdentity,
|
|
148
|
-
})),
|
|
149
|
-
})),
|
|
150
|
-
};
|
|
151
|
-
const requestHash = canonicalHash(payload);
|
|
152
|
-
const request = {
|
|
153
|
-
...payload,
|
|
154
|
-
requestId: visualReviewRequestId(requestHash),
|
|
155
|
-
requestHash,
|
|
156
|
-
};
|
|
157
|
-
const validation = validateContract('visual-review-request', request);
|
|
158
|
-
if (!validation.valid) {
|
|
159
|
-
throw visualReviewError(
|
|
160
|
-
`Visual review request is incomplete: ${formatErrors(validation.errors)}`,
|
|
161
|
-
);
|
|
162
|
-
}
|
|
163
|
-
for (const artifact of request.renderedArtifacts) {
|
|
164
|
-
const viewports = new Set(
|
|
165
|
-
artifact.evidence.map(({ viewport }) => viewport),
|
|
166
|
-
);
|
|
167
|
-
if (
|
|
168
|
-
viewports.size !== REQUIRED_VIEWPORTS.length ||
|
|
169
|
-
REQUIRED_VIEWPORTS.some((viewport) => !viewports.has(viewport))
|
|
170
|
-
) {
|
|
171
|
-
throw visualReviewError(
|
|
172
|
-
`Visual review requires mobile, tablet, and desktop evidence for ${artifact.artifactId}.`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
return { request, snapshots };
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
function browserBindingFor(evidence) {
|
|
180
|
-
if (evidence.length === 0) {
|
|
181
|
-
throw visualReviewError(
|
|
182
|
-
'Visual review requires trusted browser runtime evidence.',
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
const first = evidence[0];
|
|
186
|
-
if (
|
|
187
|
-
!validBrowserRuntime(first.runtime) ||
|
|
188
|
-
!/^sha256:[a-f0-9]{64}$/.test(first.captureIdentity ?? '')
|
|
189
|
-
) {
|
|
190
|
-
throw visualReviewError(
|
|
191
|
-
'Visual review evidence is missing trusted browser runtime identity.',
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
const runtimeHash = canonicalHash(first.runtime);
|
|
195
|
-
for (const item of evidence) {
|
|
196
|
-
if (
|
|
197
|
-
!validBrowserRuntime(item.runtime) ||
|
|
198
|
-
canonicalHash(item.runtime) !== runtimeHash ||
|
|
199
|
-
item.captureIdentity !== first.captureIdentity
|
|
200
|
-
) {
|
|
201
|
-
throw visualReviewError(
|
|
202
|
-
'Visual review browser runtime or capture identity does not match across evidence records.',
|
|
203
|
-
);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return {
|
|
207
|
-
runtime: structuredClone(first.runtime),
|
|
208
|
-
captureIdentity: first.captureIdentity,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function validBrowserRuntime(runtime) {
|
|
213
|
-
return (
|
|
214
|
-
runtime &&
|
|
215
|
-
typeof runtime === 'object' &&
|
|
216
|
-
['launched', 'fixture'].includes(runtime.kind) &&
|
|
217
|
-
runtime.name === 'chromium' &&
|
|
218
|
-
typeof runtime.version === 'string' &&
|
|
219
|
-
runtime.version.length > 0
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
export function cohesionEvidenceFromLedger(artifacts, plan) {
|
|
224
|
-
if (!plan?.ledger) return artifacts;
|
|
225
|
-
return artifacts.map((artifact) => {
|
|
226
|
-
const text = visibleText(artifact.html);
|
|
227
|
-
return {
|
|
228
|
-
...artifact,
|
|
229
|
-
cohesion: {
|
|
230
|
-
terminology: observedClaims(
|
|
231
|
-
plan.ledger.terminology,
|
|
232
|
-
({ term }) => term,
|
|
233
|
-
({ term }) => term,
|
|
234
|
-
text,
|
|
235
|
-
),
|
|
236
|
-
numericClaims: observedClaims(
|
|
237
|
-
plan.ledger.numbers,
|
|
238
|
-
({ subject }) => subject,
|
|
239
|
-
({ value }) => value,
|
|
240
|
-
text,
|
|
241
|
-
true,
|
|
242
|
-
),
|
|
243
|
-
statuses: observedClaims(
|
|
244
|
-
plan.ledger.statuses,
|
|
245
|
-
({ subject }) => subject,
|
|
246
|
-
({ value }) => value,
|
|
247
|
-
text,
|
|
248
|
-
),
|
|
249
|
-
},
|
|
250
|
-
};
|
|
251
|
-
});
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export function cohesionObservationsFromLedger({
|
|
255
|
-
artifactId,
|
|
256
|
-
content,
|
|
257
|
-
contentHash,
|
|
258
|
-
ledger,
|
|
259
|
-
}) {
|
|
260
|
-
const text = visibleText(
|
|
261
|
-
Buffer.isBuffer(content) ? content.toString() : content,
|
|
262
|
-
);
|
|
263
|
-
return [
|
|
264
|
-
...observedEntries(
|
|
265
|
-
ledger?.terminology,
|
|
266
|
-
'terminology',
|
|
267
|
-
({ term }) => term,
|
|
268
|
-
({ term }) => term,
|
|
269
|
-
text,
|
|
270
|
-
),
|
|
271
|
-
...observedEntries(
|
|
272
|
-
ledger?.statuses,
|
|
273
|
-
'statuses',
|
|
274
|
-
({ subject }) => subject,
|
|
275
|
-
({ value }) => value,
|
|
276
|
-
text,
|
|
277
|
-
),
|
|
278
|
-
...observedEntries(
|
|
279
|
-
ledger?.numbers,
|
|
280
|
-
'numericClaims',
|
|
281
|
-
({ subject }) => subject,
|
|
282
|
-
({ value }) => value,
|
|
283
|
-
text,
|
|
284
|
-
true,
|
|
285
|
-
),
|
|
286
|
-
].map((observation) => ({
|
|
287
|
-
artifactId,
|
|
288
|
-
contentHash,
|
|
289
|
-
...observation,
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function observedClaims(entries, keyOf, valueOf, text, numeric = false) {
|
|
294
|
-
return Object.fromEntries(
|
|
295
|
-
(entries ?? [])
|
|
296
|
-
.filter((entry) => claimObserved(text, valueOf(entry), numeric))
|
|
297
|
-
.map((entry) => [keyOf(entry), valueOf(entry)]),
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
function observedEntries(
|
|
302
|
-
entries,
|
|
303
|
-
group,
|
|
304
|
-
keyOf,
|
|
305
|
-
valueOf,
|
|
306
|
-
text,
|
|
307
|
-
numeric = false,
|
|
308
|
-
) {
|
|
309
|
-
return (entries ?? [])
|
|
310
|
-
.filter((entry) => claimObserved(text, valueOf(entry), numeric))
|
|
311
|
-
.map((entry) => ({
|
|
312
|
-
group,
|
|
313
|
-
claim: keyOf(entry),
|
|
314
|
-
value: valueOf(entry),
|
|
315
|
-
}));
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
function claimObserved(text, value, numeric) {
|
|
319
|
-
const normalized = String(value)
|
|
320
|
-
.replaceAll(/\s+/g, ' ')
|
|
321
|
-
.trim()
|
|
322
|
-
.toLocaleLowerCase();
|
|
323
|
-
if (!normalized) return false;
|
|
324
|
-
const escaped = normalized.replaceAll(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
325
|
-
const startsWithDigit = numeric && /^\p{N}/u.test(normalized);
|
|
326
|
-
const endsWithDigit = numeric && /\p{N}$/u.test(normalized);
|
|
327
|
-
const leftBoundary = startsWithDigit
|
|
328
|
-
? '[^\\p{L}\\p{N}.,]'
|
|
329
|
-
: '[^\\p{L}\\p{N}]';
|
|
330
|
-
const rightBoundary = endsWithDigit
|
|
331
|
-
? '(?![\\p{L}\\p{N}]|[.,]\\p{N})'
|
|
332
|
-
: '(?![\\p{L}\\p{N}])';
|
|
333
|
-
return new RegExp(
|
|
334
|
-
`(?:^|${leftBoundary})${escaped}${rightBoundary}`,
|
|
335
|
-
'u',
|
|
336
|
-
).test(text);
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function visibleText(value) {
|
|
340
|
-
return String(value)
|
|
341
|
-
.replaceAll(/<[^>]+>/g, ' ')
|
|
342
|
-
.replaceAll(/\s+/g, ' ')
|
|
343
|
-
.trim()
|
|
344
|
-
.toLocaleLowerCase();
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
function formatErrors(errors) {
|
|
348
|
-
return errors
|
|
349
|
-
.slice(0, 3)
|
|
350
|
-
.map(({ code, message }) => `${code}: ${message}`)
|
|
351
|
-
.join('; ');
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function visualReviewError(message, { request, kind } = {}) {
|
|
355
|
-
const error = new Error(message);
|
|
356
|
-
error.code = 'E_VISUAL_REVIEW';
|
|
357
|
-
if (request) {
|
|
358
|
-
error.visualReviewRequest = structuredClone(request);
|
|
359
|
-
error.evidenceKind = kind;
|
|
360
|
-
}
|
|
361
|
-
return error;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
async function captureSnapshot(runRoot, path) {
|
|
365
|
-
const absolutePath = confinedPath(runRoot, path);
|
|
366
|
-
const bytes = await readFile(absolutePath);
|
|
367
|
-
return { bytes, hash: hashBytes(bytes) };
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
async function assertSnapshotsUnchanged(runRoot, snapshots) {
|
|
371
|
-
for (const [path, snapshot] of snapshots) {
|
|
372
|
-
const current = await captureSnapshot(runRoot, path);
|
|
373
|
-
if (current.hash !== snapshot.hash) {
|
|
374
|
-
throw visualReviewError(
|
|
375
|
-
`Visual review evidence changed while the critic inspected ${path}.`,
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
function confinedPath(runRoot, path) {
|
|
382
|
-
const root = resolve(runRoot);
|
|
383
|
-
const absolutePath = resolve(root, path);
|
|
384
|
-
const relativePath = relative(root, absolutePath);
|
|
385
|
-
if (
|
|
386
|
-
relativePath === '' ||
|
|
387
|
-
relativePath === '..' ||
|
|
388
|
-
relativePath.startsWith(`..${process.platform === 'win32' ? '\\' : '/'}`) ||
|
|
389
|
-
isAbsolute(relativePath)
|
|
390
|
-
) {
|
|
391
|
-
throw visualReviewError(
|
|
392
|
-
`Visual review evidence path is not confined: ${path}.`,
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
return absolutePath;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
function hashBytes(bytes) {
|
|
399
|
-
return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;
|
|
400
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
|
|
6
|
-
import { publishS3Static } from './lib/s3-static.mjs';
|
|
7
|
-
|
|
8
|
-
export async function runPublishCli(argv, dependencies = {}) {
|
|
9
|
-
const log = dependencies.log ?? console.log;
|
|
10
|
-
try {
|
|
11
|
-
const options = parseArguments(argv);
|
|
12
|
-
if (!options.confirmPublish) {
|
|
13
|
-
throw Object.assign(
|
|
14
|
-
new Error(
|
|
15
|
-
'Publishing requires --confirm-publish after human approval.',
|
|
16
|
-
),
|
|
17
|
-
{ code: 'E_PUBLISH_APPROVAL' },
|
|
18
|
-
);
|
|
19
|
-
}
|
|
20
|
-
const request = JSON.parse(await readFile(options.request, 'utf8'));
|
|
21
|
-
const receipt = await publishS3Static(request, {
|
|
22
|
-
approved: true,
|
|
23
|
-
receiptPath: options.receipt,
|
|
24
|
-
...dependencies.connector,
|
|
25
|
-
});
|
|
26
|
-
log(JSON.stringify({ ok: true, receiptPath: options.receipt, receipt }));
|
|
27
|
-
return 0;
|
|
28
|
-
} catch (caught) {
|
|
29
|
-
log(
|
|
30
|
-
JSON.stringify({
|
|
31
|
-
ok: false,
|
|
32
|
-
error: {
|
|
33
|
-
code: caught?.code ?? 'E_PUBLISH',
|
|
34
|
-
message: caught instanceof Error ? caught.message : String(caught),
|
|
35
|
-
},
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
return 1;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function parseArguments(argv) {
|
|
43
|
-
const options = {
|
|
44
|
-
request: null,
|
|
45
|
-
receipt: null,
|
|
46
|
-
confirmPublish: false,
|
|
47
|
-
};
|
|
48
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
49
|
-
const argument = argv[index];
|
|
50
|
-
if (argument === '--confirm-publish') {
|
|
51
|
-
options.confirmPublish = true;
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
if (argument === '--request' || argument === '--receipt') {
|
|
55
|
-
const value = argv[index + 1];
|
|
56
|
-
if (!value || value.startsWith('--')) {
|
|
57
|
-
throw new Error(`${argument} requires a path.`);
|
|
58
|
-
}
|
|
59
|
-
options[argument.slice(2)] = value;
|
|
60
|
-
index += 1;
|
|
61
|
-
continue;
|
|
62
|
-
}
|
|
63
|
-
throw new Error(`Unknown argument: ${argument}`);
|
|
64
|
-
}
|
|
65
|
-
if (!options.request || !options.receipt) {
|
|
66
|
-
throw new Error('--request and --receipt are required.');
|
|
67
|
-
}
|
|
68
|
-
return options;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (
|
|
72
|
-
process.argv[1] &&
|
|
73
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
74
|
-
) {
|
|
75
|
-
process.exitCode = await runPublishCli(process.argv.slice(2));
|
|
76
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { readFile } from 'node:fs/promises';
|
|
4
|
-
import { pathToFileURL } from 'node:url';
|
|
5
|
-
|
|
6
|
-
import { recordDurability } from './lib/durability.mjs';
|
|
7
|
-
|
|
8
|
-
export async function runRecordDurabilityCli(
|
|
9
|
-
argv = process.argv.slice(2),
|
|
10
|
-
io = console,
|
|
11
|
-
) {
|
|
12
|
-
const [requestPath] = argv;
|
|
13
|
-
if (!requestPath || argv.length !== 1) {
|
|
14
|
-
io.log(
|
|
15
|
-
JSON.stringify(
|
|
16
|
-
{
|
|
17
|
-
durable: false,
|
|
18
|
-
outcome: 'built-not-durable',
|
|
19
|
-
errors: [
|
|
20
|
-
{
|
|
21
|
-
code: 'usage',
|
|
22
|
-
message: 'Usage: record-durability.mjs <request-json-path>',
|
|
23
|
-
},
|
|
24
|
-
],
|
|
25
|
-
},
|
|
26
|
-
null,
|
|
27
|
-
2,
|
|
28
|
-
),
|
|
29
|
-
);
|
|
30
|
-
return 1;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const request = JSON.parse(await readFile(requestPath, 'utf8'));
|
|
35
|
-
const result = await recordDurability(request);
|
|
36
|
-
io.log(JSON.stringify(result, null, 2));
|
|
37
|
-
return result.durable ? 0 : 1;
|
|
38
|
-
} catch (error) {
|
|
39
|
-
io.log(
|
|
40
|
-
JSON.stringify(
|
|
41
|
-
{
|
|
42
|
-
durable: false,
|
|
43
|
-
outcome: 'built-not-durable',
|
|
44
|
-
errors: [
|
|
45
|
-
{
|
|
46
|
-
code: 'input',
|
|
47
|
-
message: error instanceof Error ? error.message : String(error),
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
},
|
|
51
|
-
null,
|
|
52
|
-
2,
|
|
53
|
-
),
|
|
54
|
-
);
|
|
55
|
-
return 1;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
process.argv[1] &&
|
|
61
|
-
pathToFileURL(process.argv[1]).href === import.meta.url
|
|
62
|
-
) {
|
|
63
|
-
process.exitCode = await runRecordDurabilityCli();
|
|
64
|
-
}
|