yam-flow 2.2.1 → 2.3.1
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/AGENTS.md +18 -0
- package/CHANGELOG.md +36 -0
- package/COMMANDS.md +8 -1
- package/README.md +18 -12
- package/ROADMAP.md +27 -0
- package/dist/bin/yam.js +293 -111
- package/dist/lib/skill-installation.d.ts +76 -0
- package/dist/lib/skill-installation.js +554 -0
- package/dist/lib/trust-kernel.d.ts +54 -0
- package/dist/lib/trust-kernel.js +194 -0
- package/dist/lib/ueye-artifacts.d.ts +98 -0
- package/dist/lib/ueye-artifacts.js +317 -0
- package/package.json +3 -2
- package/references/final-report.md +2 -0
- package/references/honest-completion.md +1 -0
- package/references/mission.md +16 -0
- package/references/tool-trust-layer.md +3 -0
- package/references/trust-kernel.md +3 -0
- package/references/truth-matrix.md +2 -0
- package/references/ueye-proof.md +6 -0
- package/references/ueye.md +12 -0
- package/references/verification-levels.md +2 -1
- package/skills/mission/SKILL.md +19 -4
- package/skills/ueye/SKILL.md +11 -0
- package/src/bin/yam.ts +282 -106
- package/src/lib/skill-installation.ts +638 -0
- package/src/lib/trust-kernel.ts +239 -0
- package/src/lib/ueye-artifacts.ts +354 -0
- package/templates/mission-plan.md +25 -2
- package/templates/ueye-comparison.md +12 -0
- package/templates/ueye-review.md +26 -0
- package/yam.manifest.json +6 -5
package/src/bin/yam.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from 'node:child_process';
|
|
3
|
-
import crypto from 'node:crypto';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import fsp from 'node:fs/promises';
|
|
6
5
|
import { createRequire } from 'node:module';
|
|
@@ -12,7 +11,9 @@ import {
|
|
|
12
11
|
TRUTH_STATUSES,
|
|
13
12
|
buildLoopReport,
|
|
14
13
|
buildMediaGenerationProof,
|
|
14
|
+
buildMissionCompletionGate,
|
|
15
15
|
buildMissionPatchEnvelope,
|
|
16
|
+
buildMissionSubagentReceipt,
|
|
16
17
|
buildRollbackHint,
|
|
17
18
|
buildRuntimeBackendEvidence,
|
|
18
19
|
buildStudyNote,
|
|
@@ -25,6 +26,19 @@ import {
|
|
|
25
26
|
isTruthStatus
|
|
26
27
|
} from '../lib/trust-kernel.js';
|
|
27
28
|
import type { LoopEvidenceLevel, ReadinessState, ToolIntent } from '../lib/trust-kernel.js';
|
|
29
|
+
import {
|
|
30
|
+
archiveUeyeRevision,
|
|
31
|
+
inspectImageFile,
|
|
32
|
+
upsertUeyeAsset,
|
|
33
|
+
verifyUeyeAssetManifest,
|
|
34
|
+
verifyUeyeRevisionHistory
|
|
35
|
+
} from '../lib/ueye-artifacts.js';
|
|
36
|
+
import {
|
|
37
|
+
INSTALL_LOCK_NAME,
|
|
38
|
+
INSTALL_RECEIPT_NAME,
|
|
39
|
+
inspectSkillInstallation,
|
|
40
|
+
installSkillSetTransactional
|
|
41
|
+
} from '../lib/skill-installation.js';
|
|
28
42
|
|
|
29
43
|
type AnyRecord = Record<string, any>;
|
|
30
44
|
|
|
@@ -151,9 +165,13 @@ Usage:
|
|
|
151
165
|
yam ueye compare --reference ref.png --actual screenshot.png [--json]
|
|
152
166
|
yam ueye preflight [dir] [--json]
|
|
153
167
|
yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
|
|
168
|
+
yam ueye asset <add|verify> [options]
|
|
169
|
+
yam ueye revision <archive|verify> [options]
|
|
154
170
|
yam media proof [--requested] [--attempted] [--output file] [--json]
|
|
155
171
|
yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
|
|
156
172
|
yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
|
|
173
|
+
yam mission receipt [--thread-id id] [--role reviewer] [--lifecycle stopped] [--outcome passed] [--evidence text] [--json]
|
|
174
|
+
yam mission gate [--expected-thread id] [--receipt file] [--json]
|
|
157
175
|
yam benchmark report [--baseline n] [--current n] [--unit ms] [--target lower|higher] [--json]
|
|
158
176
|
yam release report [--json]
|
|
159
177
|
yam safety [text...]
|
|
@@ -187,75 +205,73 @@ async function rmrf(target) {
|
|
|
187
205
|
await fsp.rm(target, { recursive: true, force: true });
|
|
188
206
|
}
|
|
189
207
|
|
|
190
|
-
async function copyDir(source, target) {
|
|
191
|
-
await fsp.mkdir(target, { recursive: true });
|
|
192
|
-
const entries = await fsp.readdir(source, { withFileTypes: true });
|
|
193
|
-
for (const entry of entries) {
|
|
194
|
-
const from = path.join(source, entry.name);
|
|
195
|
-
const to = path.join(target, entry.name);
|
|
196
|
-
if (entry.isDirectory()) {
|
|
197
|
-
await copyDir(from, to);
|
|
198
|
-
} else if (entry.isFile()) {
|
|
199
|
-
await fsp.copyFile(from, to);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
async function installSkill(skill) {
|
|
205
|
-
const source = path.join(ROOT, 'skills', skill);
|
|
206
|
-
const target = path.join(DEST, skill);
|
|
207
|
-
const references = path.join(ROOT, 'references');
|
|
208
|
-
|
|
209
|
-
if (!await exists(path.join(source, 'SKILL.md'))) {
|
|
210
|
-
throw new Error(`missing skill source: ${source}`);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
await rmrf(target);
|
|
214
|
-
await fsp.mkdir(target, { recursive: true });
|
|
215
|
-
await fsp.copyFile(path.join(source, 'SKILL.md'), path.join(target, 'SKILL.md'));
|
|
216
|
-
await copyDir(references, path.join(target, 'references'));
|
|
217
|
-
}
|
|
218
|
-
|
|
219
208
|
async function install() {
|
|
220
|
-
await
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}
|
|
230
|
-
if (CODEX_MIRROR !== DEST && fs.existsSync(CODEX_MIRROR)) {
|
|
231
|
-
for (const skill of [...SKILLS, ...LEGACY_SKILLS, ...RETIRED_SKILLS]) {
|
|
232
|
-
await rmrf(path.join(CODEX_MIRROR, skill));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
209
|
+
const result = await installSkillSetTransactional({
|
|
210
|
+
sourceRoot: ROOT,
|
|
211
|
+
destination: DEST,
|
|
212
|
+
codexMirror: CODEX_MIRROR,
|
|
213
|
+
packageName: String(PACKAGE_JSON.name || 'yam-flow'),
|
|
214
|
+
version: VERSION,
|
|
215
|
+
skills: SKILLS,
|
|
216
|
+
legacySkills: LEGACY_SKILLS,
|
|
217
|
+
retiredSkills: RETIRED_SKILLS
|
|
218
|
+
});
|
|
235
219
|
console.log(`yam installed to ${DEST}`);
|
|
220
|
+
console.log(`install receipt: ${result.receiptPath}`);
|
|
221
|
+
console.log(`integrity: ${result.installedFiles} files, sha256:${result.sourceDigest}`);
|
|
222
|
+
for (const warning of result.cleanupWarnings) console.warn(`warning: ${warning}`);
|
|
236
223
|
console.log('Restart Codex to reload skills.');
|
|
237
224
|
}
|
|
238
225
|
|
|
239
226
|
async function uninstall() {
|
|
227
|
+
const lockPath = path.join(DEST, INSTALL_LOCK_NAME);
|
|
228
|
+
if (await exists(lockPath)) {
|
|
229
|
+
throw new Error(`cannot uninstall while an install lock exists: ${lockPath}`);
|
|
230
|
+
}
|
|
240
231
|
for (const skill of [...SKILLS, ...LEGACY_SKILLS, ...RETIRED_SKILLS]) {
|
|
241
232
|
await rmrf(path.join(DEST, skill));
|
|
242
233
|
if (CODEX_MIRROR !== DEST) {
|
|
243
234
|
await rmrf(path.join(CODEX_MIRROR, skill));
|
|
244
235
|
}
|
|
245
236
|
}
|
|
237
|
+
await rmrf(path.join(DEST, INSTALL_RECEIPT_NAME));
|
|
246
238
|
console.log(`yam removed from ${DEST}`);
|
|
247
239
|
if (CODEX_MIRROR !== DEST) console.log(`yam mirror entries removed from ${CODEX_MIRROR}`);
|
|
248
240
|
console.log('Restart Codex to unload skills.');
|
|
249
241
|
}
|
|
250
242
|
|
|
251
243
|
async function status({ quiet = false } = {}) {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
244
|
+
const report = await inspectSkillInstallation({
|
|
245
|
+
sourceRoot: ROOT,
|
|
246
|
+
destination: DEST,
|
|
247
|
+
packageName: String(PACKAGE_JSON.name || 'yam-flow'),
|
|
248
|
+
version: VERSION,
|
|
249
|
+
skills: SKILLS
|
|
250
|
+
});
|
|
251
|
+
if (!quiet) {
|
|
252
|
+
for (const skill of report.skills) {
|
|
253
|
+
const label = skill.status === 'ok' ? 'ok ' : skill.status === 'missing' ? 'missing' : 'drift ';
|
|
254
|
+
console.log(`${label} ${skill.skill}`);
|
|
255
|
+
}
|
|
256
|
+
if (report.receiptStatus === 'ok' && report.receipt) {
|
|
257
|
+
console.log(`ok install receipt ${report.receipt.package.name}@${report.receipt.package.version}`);
|
|
258
|
+
console.log(` sha256:${report.receipt.integrity.source_digest}`);
|
|
259
|
+
} else {
|
|
260
|
+
console.log(`${report.receiptStatus === 'missing' ? 'missing' : 'drift '} install receipt`);
|
|
261
|
+
}
|
|
262
|
+
if (report.issues.length) {
|
|
263
|
+
console.log('');
|
|
264
|
+
console.log('Install integrity issues:');
|
|
265
|
+
for (const issue of report.issues.slice(0, 12)) console.log(`- ${issue}`);
|
|
266
|
+
if (report.issues.length > 12) console.log(`- ... ${report.issues.length - 12} more issue(s)`);
|
|
267
|
+
if (report.recoveryArtifacts.length) {
|
|
268
|
+
console.log('Confirm no install is running, then inspect and preserve the transaction backup before retrying.');
|
|
269
|
+
} else {
|
|
270
|
+
console.log('Run `yam install` to restore the package-bundled skill set.');
|
|
271
|
+
}
|
|
272
|
+
}
|
|
257
273
|
}
|
|
258
|
-
return
|
|
274
|
+
return report.ok ? 0 : Math.max(1, report.issues.length);
|
|
259
275
|
}
|
|
260
276
|
|
|
261
277
|
async function list() {
|
|
@@ -304,6 +320,7 @@ async function verify({ quiet = false } = {}) {
|
|
|
304
320
|
|
|
305
321
|
if (!manifest) return failOrReport('yam verify', issues, quiet);
|
|
306
322
|
if (manifest.name !== 'yam') issues.push('manifest name must be yam');
|
|
323
|
+
if (manifest.version !== VERSION) issues.push(`manifest version must match package version: ${manifest.version || 'missing'} != ${VERSION}`);
|
|
307
324
|
if (manifest.defaultHooks !== false) issues.push('manifest defaultHooks must be false');
|
|
308
325
|
if (!Array.isArray(manifest.routes) || manifest.routes.length !== SKILLS.length) {
|
|
309
326
|
issues.push(`manifest routes must contain ${SKILLS.length} routes`);
|
|
@@ -367,7 +384,7 @@ async function verify({ quiet = false } = {}) {
|
|
|
367
384
|
for (const template of ['ueye-review.md', 'ueye-comparison.md', 'mission-plan.md', 'runtime-proof.md', 'tuning-log.md']) {
|
|
368
385
|
if (!await exists(path.join(ROOT, 'templates', template))) issues.push(`missing template: ${template}`);
|
|
369
386
|
}
|
|
370
|
-
for (const module of ['trust-kernel.js']) {
|
|
387
|
+
for (const module of ['trust-kernel.js', 'ueye-artifacts.js']) {
|
|
371
388
|
if (!await exists(path.join(ROOT, 'dist', 'lib', module))) issues.push(`missing dist lib module: ${module}`);
|
|
372
389
|
}
|
|
373
390
|
|
|
@@ -2251,6 +2268,8 @@ async function ueye(args = []) {
|
|
|
2251
2268
|
if (subcommand === 'compare') return ueyeCompare(args.slice(1));
|
|
2252
2269
|
if (subcommand === 'preflight') return ueyePreflight(args.slice(1));
|
|
2253
2270
|
if (subcommand === 'report') return ueyeReport(args.slice(1));
|
|
2271
|
+
if (subcommand === 'asset') return ueyeAsset(args.slice(1));
|
|
2272
|
+
if (subcommand === 'revision') return ueyeRevision(args.slice(1));
|
|
2254
2273
|
console.error(`unknown ueye command: ${subcommand}`);
|
|
2255
2274
|
return ueyeUsage();
|
|
2256
2275
|
}
|
|
@@ -2264,15 +2283,92 @@ Usage:
|
|
|
2264
2283
|
yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
|
|
2265
2284
|
yam ueye compare --reference ref.png --actual screenshot.png [--json]
|
|
2266
2285
|
yam ueye preflight [dir] [--json]
|
|
2267
|
-
yam ueye report [--reference ref.png] [--actual screenshot.png] [--preflight-id id] [--p0-risk text] [--quality-gate-note text] [--brief-dimension text] [--constraint text] [--anti-slop text] [--invented-metric] [--placeholder-copy] [--generic-visual] [--acceptance-criterion text] [--touched-file file] [--read-file file] [--verified-file file] [--skipped-check text] [--residual-risk text] [--stop-condition text] [--resume-hint text] [--deep-visual-check text] [--design-system-evidence text] [--implementation-evidence text] [--state-check default:pass] [--review-session-id id] [--provider-context local] [--execution-surface in-app-browser] [--app-surface codex-app] [--browser-surface in-app-browser] [--control-mode manual|automated] [--preserved-state] [--preserved-url URL] [--completion-claim draft|needs-polish|done] [--strict] [--design-score n] [--p0 text] [--p1 text] [--states-checked] [--mobile-checked] [--contrast-checked] [--similar text] [--different text] [--missing text] [--resolved text] [--new-finding text] [--still-open text] [--regression text] [--viewport 1440x900] [--state default] [--design-quality pass|needs-polish|fails|not-checked] [--json]
|
|
2286
|
+
yam ueye report [--reference ref.png] [--actual screenshot.png] [--asset-manifest file] [--revision-manifest file] [--preflight-id id] [--p0-risk text] [--quality-gate-note text] [--brief-dimension text] [--constraint text] [--anti-slop text] [--invented-metric] [--placeholder-copy] [--generic-visual] [--acceptance-criterion text] [--touched-file file] [--read-file file] [--verified-file file] [--skipped-check text] [--residual-risk text] [--stop-condition text] [--resume-hint text] [--deep-visual-check text] [--design-system-evidence text] [--implementation-evidence text] [--state-check default:pass] [--review-session-id id] [--provider-context local] [--execution-surface in-app-browser] [--app-surface codex-app] [--browser-surface in-app-browser] [--control-mode manual|automated] [--preserved-state] [--preserved-url URL] [--completion-claim draft|needs-polish|done] [--strict] [--design-score n] [--p0 text] [--p1 text] [--states-checked] [--mobile-checked] [--contrast-checked] [--similar text] [--different text] [--missing text] [--resolved text] [--new-finding text] [--still-open text] [--regression text] [--viewport 1440x900] [--state default] [--design-quality pass|needs-polish|fails|not-checked] [--json]
|
|
2287
|
+
yam ueye asset add [--manifest file] --id id --file image [--source-url URL] [--source-page-url URL] [--license-note text] [--operator-provided] [--do-not-replace] [--allowed-for-edit] [--json]
|
|
2288
|
+
yam ueye asset verify [--manifest file] [--json]
|
|
2289
|
+
yam ueye revision archive --file image --round n [--artifact-id id] [--root dir] [--manifest file] [--json]
|
|
2290
|
+
yam ueye revision verify [--root dir] [--manifest file] [--json]
|
|
2268
2291
|
|
|
2269
2292
|
Notes:
|
|
2270
2293
|
capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
|
|
2271
2294
|
compare uses local files only and reports sha256, dimensions, comparison_result, and proof-ready visual provenance.
|
|
2272
2295
|
report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
|
|
2296
|
+
asset records local reference provenance and protection flags; it never downloads remote content.
|
|
2297
|
+
revision copies the current local artifact into a non-overwriting round archive before the live file is edited.
|
|
2273
2298
|
`);
|
|
2274
2299
|
}
|
|
2275
2300
|
|
|
2301
|
+
async function ueyeAsset(args = []) {
|
|
2302
|
+
const subcommand = args[0] || 'help';
|
|
2303
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') return ueyeUsage();
|
|
2304
|
+
const flags = parseSimpleFlags(args.slice(1), new Set(['manifest', 'id', 'file', 'source-url', 'source-page-url', 'retrieved-at', 'license-note', 'operator-provided', 'do-not-replace', 'allowed-for-edit', 'replace', 'json']));
|
|
2305
|
+
const manifest = path.resolve(expandHome(flags.manifest || path.join(process.cwd(), '.yam', 'ueye', 'assets.json')));
|
|
2306
|
+
try {
|
|
2307
|
+
if (subcommand === 'add') {
|
|
2308
|
+
if (!flags.id || !flags.file) throw new Error('asset add requires --id and --file');
|
|
2309
|
+
const result = await upsertUeyeAsset({
|
|
2310
|
+
manifest_path: manifest,
|
|
2311
|
+
id: String(flags.id),
|
|
2312
|
+
file: path.resolve(expandHome(flags.file)),
|
|
2313
|
+
source_url: flags.source_url,
|
|
2314
|
+
source_page_url: flags.source_page_url,
|
|
2315
|
+
retrieved_at: flags.retrieved_at,
|
|
2316
|
+
license_note: flags.license_note,
|
|
2317
|
+
operator_provided: flags.operator_provided ? true : undefined,
|
|
2318
|
+
do_not_replace: flags.do_not_replace ? true : undefined,
|
|
2319
|
+
allowed_for_edit: flags.allowed_for_edit ? true : undefined,
|
|
2320
|
+
replace: Boolean(flags.replace)
|
|
2321
|
+
});
|
|
2322
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset');
|
|
2323
|
+
return;
|
|
2324
|
+
}
|
|
2325
|
+
if (subcommand === 'verify') {
|
|
2326
|
+
const result = await verifyUeyeAssetManifest(manifest);
|
|
2327
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset verification');
|
|
2328
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
2329
|
+
return;
|
|
2330
|
+
}
|
|
2331
|
+
throw new Error(`unknown ueye asset command: ${subcommand}`);
|
|
2332
|
+
} catch (error) {
|
|
2333
|
+
const result = { schema: 'yam.ueye-asset-error.v1', status: 'blocked', reason: errorMessage(error), truth_status: 'blocked' };
|
|
2334
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset');
|
|
2335
|
+
process.exitCode = 1;
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
async function ueyeRevision(args = []) {
|
|
2340
|
+
const subcommand = args[0] || 'help';
|
|
2341
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') return ueyeUsage();
|
|
2342
|
+
const flags = parseSimpleFlags(args.slice(1), new Set(['file', 'round', 'artifact-id', 'root', 'manifest', 'json']));
|
|
2343
|
+
const root = path.resolve(expandHome(flags.root || path.join(process.cwd(), '.yam', 'ueye', 'revisions')));
|
|
2344
|
+
const manifest = path.resolve(expandHome(flags.manifest || path.join(root, 'manifest.json')));
|
|
2345
|
+
try {
|
|
2346
|
+
if (subcommand === 'archive') {
|
|
2347
|
+
if (!flags.file || !flags.round) throw new Error('revision archive requires --file and --round');
|
|
2348
|
+
const result = await archiveUeyeRevision({
|
|
2349
|
+
file: path.resolve(expandHome(flags.file)),
|
|
2350
|
+
root,
|
|
2351
|
+
manifest_path: manifest,
|
|
2352
|
+
round: Number(flags.round),
|
|
2353
|
+
artifact_id: flags.artifact_id
|
|
2354
|
+
});
|
|
2355
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision');
|
|
2356
|
+
return;
|
|
2357
|
+
}
|
|
2358
|
+
if (subcommand === 'verify') {
|
|
2359
|
+
const result = await verifyUeyeRevisionHistory(manifest);
|
|
2360
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision verification');
|
|
2361
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
throw new Error(`unknown ueye revision command: ${subcommand}`);
|
|
2365
|
+
} catch (error) {
|
|
2366
|
+
const result = { schema: 'yam.ueye-revision-error.v1', status: 'blocked', reason: errorMessage(error), truth_status: 'blocked' };
|
|
2367
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision');
|
|
2368
|
+
process.exitCode = 1;
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
|
|
2276
2372
|
async function ueyePreflight(args = []) {
|
|
2277
2373
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
|
|
2278
2374
|
const parsed = parseDirJsonArgs(args);
|
|
@@ -2501,7 +2597,7 @@ async function ueyeCompare(args = []) {
|
|
|
2501
2597
|
|
|
2502
2598
|
async function ueyeReport(args = []) {
|
|
2503
2599
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
|
|
2504
|
-
const flags = parseSimpleFlags(args, new Set(['reference', 'ref', 'actual', 'screenshot', 'capture-backend', 'compare-backend', 'design-quality', 'blocked-reason', 'next-action', 'next-visual-action', 'review-session-id', 'preflight-id', 'p0-risk', 'quality-gate-note', 'brief-dimension', 'constraint', 'anti-slop', 'invented-metric', 'placeholder-copy', 'generic-visual', 'acceptance-criterion', 'touched-file', 'read-file', 'verified-file', 'skipped-check', 'residual-risk', 'stop-condition', 'resume-hint', 'deep-visual-check', 'design-system-evidence', 'implementation-evidence', 'state-check', 'reference-id', 'screenshot-id', 'previous-screenshot-id', 'current-screenshot-id', 'previous-report', 'comparison-notes', 'similar', 'different', 'missing', 'resolved', 'new-finding', 'still-open', 'regression', 'viewport', 'state', 'provider-context', 'provider-badge', 'execution-surface', 'app-surface', 'browser-surface', 'control-mode', 'preserved-state', 'preserved-url', 'url', 'evidence-id', 'completion-claim', 'completion-status', 'gate-mode', 'strict', 'design-score', 'min-design-score', 'p0', 'p1', 'open-p0', 'open-p1', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'comparison-result', 'json']));
|
|
2600
|
+
const flags = parseSimpleFlags(args, new Set(['reference', 'ref', 'actual', 'screenshot', 'capture-backend', 'compare-backend', 'design-quality', 'blocked-reason', 'next-action', 'next-visual-action', 'review-session-id', 'preflight-id', 'p0-risk', 'quality-gate-note', 'brief-dimension', 'constraint', 'anti-slop', 'invented-metric', 'placeholder-copy', 'generic-visual', 'acceptance-criterion', 'touched-file', 'read-file', 'verified-file', 'skipped-check', 'residual-risk', 'stop-condition', 'resume-hint', 'deep-visual-check', 'design-system-evidence', 'implementation-evidence', 'state-check', 'asset-manifest', 'revision-manifest', 'reference-id', 'screenshot-id', 'previous-screenshot-id', 'current-screenshot-id', 'previous-report', 'comparison-notes', 'similar', 'different', 'missing', 'resolved', 'new-finding', 'still-open', 'regression', 'viewport', 'state', 'provider-context', 'provider-badge', 'execution-surface', 'app-surface', 'browser-surface', 'control-mode', 'preserved-state', 'preserved-url', 'url', 'evidence-id', 'completion-claim', 'completion-status', 'gate-mode', 'strict', 'design-score', 'min-design-score', 'p0', 'p1', 'open-p0', 'open-p1', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'comparison-result', 'json']));
|
|
2505
2601
|
const reference = String(flags.reference || flags.ref || '');
|
|
2506
2602
|
const actual = String(flags.actual || flags.screenshot || '');
|
|
2507
2603
|
const referenceSources = [];
|
|
@@ -2580,6 +2676,12 @@ async function ueyeReport(args = []) {
|
|
|
2580
2676
|
blockedReason = errorMessage(error);
|
|
2581
2677
|
}
|
|
2582
2678
|
|
|
2679
|
+
const assetManifest = await optionalUeyeAssetVerification(flags.asset_manifest);
|
|
2680
|
+
const revisionHistory = await optionalUeyeRevisionVerification(flags.revision_manifest);
|
|
2681
|
+
const doneClaim = ['done', 'complete', 'completed'].includes(String(flags.completion_claim || flags.completion_status || '').toLowerCase());
|
|
2682
|
+
if (doneClaim && assetManifest?.truth_status === 'blocked') blockedReason = blockedReason || `asset manifest blocked: ${assetManifest.next_action || 'verification failed'}`;
|
|
2683
|
+
if (doneClaim && revisionHistory?.truth_status === 'blocked') blockedReason = blockedReason || `revision history blocked: ${revisionHistory.next_action || 'verification failed'}`;
|
|
2684
|
+
|
|
2583
2685
|
const report = buildUeyeRunReport({
|
|
2584
2686
|
reference_sources: referenceSources,
|
|
2585
2687
|
implementation_sources: implementationSources,
|
|
@@ -2662,12 +2764,32 @@ async function ueyeReport(args = []) {
|
|
|
2662
2764
|
capture_backend: String(flags.capture_backend || 'not-recorded'),
|
|
2663
2765
|
compare_backend: String(flags.compare_backend || 'local-file-hash'),
|
|
2664
2766
|
continuity: comparisonReport.continuity,
|
|
2665
|
-
comparison_report: comparisonReport
|
|
2767
|
+
comparison_report: comparisonReport,
|
|
2768
|
+
asset_manifest: assetManifest,
|
|
2769
|
+
revision_history: revisionHistory
|
|
2666
2770
|
};
|
|
2667
2771
|
printJsonOrHuman(result, Boolean(flags.json), 'Ueye report');
|
|
2668
2772
|
if (report.truth_status === 'blocked') process.exitCode = 1;
|
|
2669
2773
|
}
|
|
2670
2774
|
|
|
2775
|
+
async function optionalUeyeAssetVerification(file = '') {
|
|
2776
|
+
if (!file) return null;
|
|
2777
|
+
try {
|
|
2778
|
+
return await verifyUeyeAssetManifest(path.resolve(expandHome(file)));
|
|
2779
|
+
} catch (error) {
|
|
2780
|
+
return { schema: 'yam.ueye-asset-verification.v1', truth_status: 'blocked', reason: errorMessage(error), next_action: 'repair or recreate the asset manifest before a done claim' };
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2783
|
+
|
|
2784
|
+
async function optionalUeyeRevisionVerification(file = '') {
|
|
2785
|
+
if (!file) return null;
|
|
2786
|
+
try {
|
|
2787
|
+
return await verifyUeyeRevisionHistory(path.resolve(expandHome(file)));
|
|
2788
|
+
} catch (error) {
|
|
2789
|
+
return { schema: 'yam.ueye-revision-verification.v1', truth_status: 'blocked', reason: errorMessage(error), next_action: 'repair or recreate the revision manifest before a done claim' };
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
|
|
2671
2793
|
function buildUeyeDesignBrief(flags: AnyRecord = {}) {
|
|
2672
2794
|
const dimensions = arrayFlag(flags.brief_dimension);
|
|
2673
2795
|
const constraints = arrayFlag(flags.constraint);
|
|
@@ -2942,6 +3064,8 @@ async function mission(args = []) {
|
|
|
2942
3064
|
const subcommand = args[0] || 'help';
|
|
2943
3065
|
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') return missionUsage();
|
|
2944
3066
|
if (subcommand === 'queue' || subcommand === 'patch') return missionQueue(args.slice(1));
|
|
3067
|
+
if (subcommand === 'receipt') return missionReceipt(args.slice(1));
|
|
3068
|
+
if (subcommand === 'gate' || subcommand === 'close') return missionGate(args.slice(1));
|
|
2945
3069
|
console.error(`unknown mission command: ${subcommand}`);
|
|
2946
3070
|
return missionUsage();
|
|
2947
3071
|
}
|
|
@@ -2951,12 +3075,76 @@ function missionUsage() {
|
|
|
2951
3075
|
|
|
2952
3076
|
Usage:
|
|
2953
3077
|
yam mission queue [--lane-id id] [--agent-id id] [--status pending|applied|verified|blocked|reverted] [--scope text] [--changed file] [--depends-on lane] [--generated file] [--verification-hint text] [--rollback-hint text] [--before-check text] [--out file] [--truth status] [--json]
|
|
3078
|
+
yam mission receipt --thread-id id --role implementer|reviewer|ux-verifier|doctor --lifecycle pending|running|stopped|failed|cancelled --outcome passed|failed|blocked|ambiguous [--access-mode read-only|write] [--scope text] [--changed file] [--evidence text] [--remaining-risk text] [--out file] [--json]
|
|
3079
|
+
yam mission gate --expected-thread id --receipt file [--expected-thread id --receipt file] [--out file] [--json]
|
|
2954
3080
|
|
|
2955
3081
|
Notes:
|
|
2956
3082
|
Produces a patch queue item for mission handoff/review. It does not run workers; persistence is opt-in with --out.
|
|
3083
|
+
Reviewer and doctor receipts default to read-only and are blocked if they claim changed files or write access.
|
|
3084
|
+
A stopped lifecycle is not success by itself; a passed outcome needs explicit verification evidence.
|
|
2957
3085
|
`);
|
|
2958
3086
|
}
|
|
2959
3087
|
|
|
3088
|
+
async function missionReceipt(args = []) {
|
|
3089
|
+
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return missionUsage();
|
|
3090
|
+
const flags = parseSimpleFlags(args, new Set(['receipt-id', 'thread-id', 'lane-id', 'agent-id', 'role', 'access-mode', 'lifecycle', 'outcome', 'scope', 'changed', 'evidence', 'remaining-risk', 'out', 'json']));
|
|
3091
|
+
const receipt = buildMissionSubagentReceipt({
|
|
3092
|
+
receipt_id: String(flags.receipt_id || `receipt-${timestampId()}`),
|
|
3093
|
+
thread_id: String(flags.thread_id || ''),
|
|
3094
|
+
lane_id: String(flags.lane_id || flags.thread_id || ''),
|
|
3095
|
+
agent_id: String(flags.agent_id || ''),
|
|
3096
|
+
role: flags.role,
|
|
3097
|
+
access_mode: flags.access_mode,
|
|
3098
|
+
lifecycle_status: flags.lifecycle,
|
|
3099
|
+
outcome: flags.outcome,
|
|
3100
|
+
assigned_scope: String(flags.scope || ''),
|
|
3101
|
+
changed_files: arrayFlag(flags.changed),
|
|
3102
|
+
verification_evidence: arrayFlag(flags.evidence),
|
|
3103
|
+
remaining_risks: arrayFlag(flags.remaining_risk)
|
|
3104
|
+
});
|
|
3105
|
+
const result = {
|
|
3106
|
+
...receipt,
|
|
3107
|
+
persistence: flags.out ? 'written' : 'not-persisted',
|
|
3108
|
+
out: flags.out ? path.resolve(expandHome(flags.out)) : '',
|
|
3109
|
+
next_action: receipt.completion_eligible ? 'include this receipt in `yam mission gate`' : receipt.blockers[0] || 'complete the receipt evidence'
|
|
3110
|
+
};
|
|
3111
|
+
if (flags.out) await writeJsonArtifact(flags.out, result);
|
|
3112
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Mission receipt');
|
|
3113
|
+
if (receipt.truth_status === 'blocked') process.exitCode = 1;
|
|
3114
|
+
}
|
|
3115
|
+
|
|
3116
|
+
async function missionGate(args = []) {
|
|
3117
|
+
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return missionUsage();
|
|
3118
|
+
const flags = parseSimpleFlags(args, new Set(['expected-thread', 'receipt', 'out', 'json']));
|
|
3119
|
+
const receipts = [];
|
|
3120
|
+
const loadErrors: string[] = [];
|
|
3121
|
+
for (const file of arrayFlag(flags.receipt)) {
|
|
3122
|
+
const target = path.resolve(expandHome(file));
|
|
3123
|
+
try {
|
|
3124
|
+
receipts.push(await readJson(target));
|
|
3125
|
+
} catch (error) {
|
|
3126
|
+
loadErrors.push(`${target}: ${errorMessage(error)}`);
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
const gate = buildMissionCompletionGate({
|
|
3130
|
+
expected_thread_ids: arrayFlag(flags.expected_thread),
|
|
3131
|
+
receipts
|
|
3132
|
+
});
|
|
3133
|
+
const blockers = [...gate.blockers, ...loadErrors.map((error) => `receipt_load_failed: ${error}`)];
|
|
3134
|
+
const result = {
|
|
3135
|
+
...gate,
|
|
3136
|
+
blockers,
|
|
3137
|
+
ready_to_claim_complete: gate.ready_to_claim_complete && loadErrors.length === 0,
|
|
3138
|
+
next_action: blockers[0] || gate.next_action,
|
|
3139
|
+
truth_status: blockers.length ? 'blocked' : gate.truth_status,
|
|
3140
|
+
persistence: flags.out ? 'written' : 'not-persisted',
|
|
3141
|
+
out: flags.out ? path.resolve(expandHome(flags.out)) : ''
|
|
3142
|
+
};
|
|
3143
|
+
if (flags.out) await writeJsonArtifact(flags.out, result);
|
|
3144
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Mission completion gate');
|
|
3145
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
3146
|
+
}
|
|
3147
|
+
|
|
2960
3148
|
async function missionQueue(args = []) {
|
|
2961
3149
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return missionUsage();
|
|
2962
3150
|
const flags = parseSimpleFlags(args, new Set(['lane-id', 'agent-id', 'status', 'scope', 'changed', 'generated', 'depends-on', 'verification-hint', 'rollback-hint', 'before-check', 'safe-revert-note', 'truth', 'intent', 'out', 'json']));
|
|
@@ -3103,7 +3291,7 @@ function parseSimpleFlags(args = [], allowed = new Set<string>()) {
|
|
|
3103
3291
|
const key = rawKey.slice(2);
|
|
3104
3292
|
if (allowed.size && !allowed.has(key)) continue;
|
|
3105
3293
|
const normalizedKey = key.replace(/-/g, '_');
|
|
3106
|
-
if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked', 'cleanup-observed', 'left-running-intentionally', 'strict', 'preserved-state', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'invented-metric', 'placeholder-copy', 'generic-visual'].includes(key)) {
|
|
3294
|
+
if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked', 'cleanup-observed', 'left-running-intentionally', 'strict', 'preserved-state', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read', 'invented-metric', 'placeholder-copy', 'generic-visual', 'operator-provided', 'do-not-replace', 'allowed-for-edit', 'replace'].includes(key)) {
|
|
3107
3295
|
flags[normalizedKey] = true;
|
|
3108
3296
|
continue;
|
|
3109
3297
|
}
|
|
@@ -3143,53 +3331,7 @@ async function loadOptionalPlaywright() {
|
|
|
3143
3331
|
}
|
|
3144
3332
|
|
|
3145
3333
|
async function imageFileInfo(file) {
|
|
3146
|
-
|
|
3147
|
-
const buffer = await fsp.readFile(absolute);
|
|
3148
|
-
const stat = await fsp.stat(absolute);
|
|
3149
|
-
return {
|
|
3150
|
-
path: absolute,
|
|
3151
|
-
sha256: crypto.createHash('sha256').update(buffer).digest('hex'),
|
|
3152
|
-
bytes: stat.size,
|
|
3153
|
-
dimensions: imageDimensions(buffer)
|
|
3154
|
-
};
|
|
3155
|
-
}
|
|
3156
|
-
|
|
3157
|
-
function imageDimensions(buffer) {
|
|
3158
|
-
const png = pngDimensions(buffer);
|
|
3159
|
-
if (png) return png;
|
|
3160
|
-
const jpeg = jpegDimensions(buffer);
|
|
3161
|
-
if (jpeg) return jpeg;
|
|
3162
|
-
const gif = gifDimensions(buffer);
|
|
3163
|
-
if (gif) return gif;
|
|
3164
|
-
return 'unknown';
|
|
3165
|
-
}
|
|
3166
|
-
|
|
3167
|
-
function pngDimensions(buffer) {
|
|
3168
|
-
if (buffer.length < 24) return '';
|
|
3169
|
-
if (buffer.readUInt32BE(0) !== 0x89504e47 || buffer.readUInt32BE(4) !== 0x0d0a1a0a) return '';
|
|
3170
|
-
return `${buffer.readUInt32BE(16)}x${buffer.readUInt32BE(20)}`;
|
|
3171
|
-
}
|
|
3172
|
-
|
|
3173
|
-
function gifDimensions(buffer) {
|
|
3174
|
-
if (buffer.length < 10) return '';
|
|
3175
|
-
const signature = buffer.toString('ascii', 0, 6);
|
|
3176
|
-
if (signature !== 'GIF87a' && signature !== 'GIF89a') return '';
|
|
3177
|
-
return `${buffer.readUInt16LE(6)}x${buffer.readUInt16LE(8)}`;
|
|
3178
|
-
}
|
|
3179
|
-
|
|
3180
|
-
function jpegDimensions(buffer) {
|
|
3181
|
-
if (buffer.length < 4 || buffer[0] !== 0xff || buffer[1] !== 0xd8) return '';
|
|
3182
|
-
let offset = 2;
|
|
3183
|
-
while (offset < buffer.length) {
|
|
3184
|
-
if (buffer[offset] !== 0xff) return '';
|
|
3185
|
-
const marker = buffer[offset + 1];
|
|
3186
|
-
const length = buffer.readUInt16BE(offset + 2);
|
|
3187
|
-
if (marker >= 0xc0 && marker <= 0xc3 && offset + 8 < buffer.length) {
|
|
3188
|
-
return `${buffer.readUInt16BE(offset + 7)}x${buffer.readUInt16BE(offset + 5)}`;
|
|
3189
|
-
}
|
|
3190
|
-
offset += 2 + length;
|
|
3191
|
-
}
|
|
3192
|
-
return '';
|
|
3334
|
+
return inspectImageFile(path.resolve(expandHome(file)));
|
|
3193
3335
|
}
|
|
3194
3336
|
|
|
3195
3337
|
function printJsonOrHuman(result, json = false, label = 'yam') {
|
|
@@ -3208,6 +3350,12 @@ function printJsonOrHuman(result, json = false, label = 'yam') {
|
|
|
3208
3350
|
}
|
|
3209
3351
|
}
|
|
3210
3352
|
|
|
3353
|
+
async function writeJsonArtifact(file, value) {
|
|
3354
|
+
const target = path.resolve(expandHome(file));
|
|
3355
|
+
await fsp.mkdir(path.dirname(target), { recursive: true });
|
|
3356
|
+
await fsp.writeFile(target, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
3357
|
+
}
|
|
3358
|
+
|
|
3211
3359
|
function detectDbSafetyText(text = '') {
|
|
3212
3360
|
return detectTrustDbSafetyText(text);
|
|
3213
3361
|
}
|
|
@@ -3251,6 +3399,8 @@ async function proof(args = []) {
|
|
|
3251
3399
|
printProofList('Runtime backend evidence', summary.runtimeBackendEvidence || []);
|
|
3252
3400
|
printProofList('Visual provenance', summary.visualProvenance || []);
|
|
3253
3401
|
printProofList('Mission patch envelope', summary.missionEnvelope || []);
|
|
3402
|
+
printProofList('Mission subagent receipt', summary.missionReceipt || []);
|
|
3403
|
+
printProofList('Mission completion gate', summary.missionCompletion || []);
|
|
3254
3404
|
printProofList('Rollback hint', summary.rollbackHint || []);
|
|
3255
3405
|
printProofList('Media proof', summary.mediaProof || []);
|
|
3256
3406
|
printProofList('Design completion', summary.designCompletion || []);
|
|
@@ -3341,6 +3491,14 @@ function buildProofSummary(parsed, artifact) {
|
|
|
3341
3491
|
...structuredEvidenceList(artifact.missionEnvelope),
|
|
3342
3492
|
...structuredEvidenceList(parsed.flags.mission_envelope)
|
|
3343
3493
|
],
|
|
3494
|
+
missionReceipt: [
|
|
3495
|
+
...structuredEvidenceList(artifact.missionReceipt),
|
|
3496
|
+
...structuredEvidenceList(parsed.flags.mission_receipt)
|
|
3497
|
+
],
|
|
3498
|
+
missionCompletion: [
|
|
3499
|
+
...structuredEvidenceList(artifact.missionCompletion),
|
|
3500
|
+
...structuredEvidenceList(parsed.flags.mission_completion)
|
|
3501
|
+
],
|
|
3344
3502
|
rollbackHint: [
|
|
3345
3503
|
...structuredEvidenceList(artifact.rollbackHint),
|
|
3346
3504
|
...structuredEvidenceList(parsed.flags.rollback_hint)
|
|
@@ -3412,6 +3570,12 @@ function renderProofMarkdown(summary) {
|
|
|
3412
3570
|
'## Mission patch envelope',
|
|
3413
3571
|
...renderProofMarkdownList(summary.missionEnvelope || []),
|
|
3414
3572
|
'',
|
|
3573
|
+
'## Mission subagent receipt',
|
|
3574
|
+
...renderProofMarkdownList(summary.missionReceipt || []),
|
|
3575
|
+
'',
|
|
3576
|
+
'## Mission completion gate',
|
|
3577
|
+
...renderProofMarkdownList(summary.missionCompletion || []),
|
|
3578
|
+
'',
|
|
3415
3579
|
'## Rollback hint',
|
|
3416
3580
|
...renderProofMarkdownList(summary.rollbackHint || []),
|
|
3417
3581
|
'',
|
|
@@ -3453,7 +3617,7 @@ function renderProofMarkdownList(values) {
|
|
|
3453
3617
|
function parseProofArgs(args = []) {
|
|
3454
3618
|
const flags: AnyRecord = {};
|
|
3455
3619
|
const positionals: string[] = [];
|
|
3456
|
-
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'runtime-backend', 'runtime-claim', 'runtime-evidence-id', 'runtime-command', 'runtime-pid', 'runtime-port', 'runtime-exit-code', 'runtime-started-at', 'runtime-stopped-at', 'runtime-cleanup-method', 'cleanup-observed', 'left-running-intentionally', 'cleanup-checked', 'runtime-note', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'media-proof', 'design-completion', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
3620
|
+
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'runtime-backend', 'runtime-claim', 'runtime-evidence-id', 'runtime-command', 'runtime-pid', 'runtime-port', 'runtime-exit-code', 'runtime-started-at', 'runtime-stopped-at', 'runtime-cleanup-method', 'cleanup-observed', 'left-running-intentionally', 'cleanup-checked', 'runtime-note', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'mission-receipt', 'mission-completion', 'rollback-hint', 'media-proof', 'design-completion', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
3457
3621
|
for (let index = 0; index < args.length; index += 1) {
|
|
3458
3622
|
const arg = args[index];
|
|
3459
3623
|
if (arg.startsWith('--')) {
|
|
@@ -3467,7 +3631,7 @@ function parseProofArgs(args = []) {
|
|
|
3467
3631
|
}
|
|
3468
3632
|
const value = inlineValue ?? args[index + 1] ?? '';
|
|
3469
3633
|
if (inlineValue === undefined) index += 1;
|
|
3470
|
-
if (['command', 'evidence', 'visual', 'runtime', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'media-proof', 'design-completion', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
3634
|
+
if (['command', 'evidence', 'visual', 'runtime', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'mission-receipt', 'mission-completion', 'rollback-hint', 'media-proof', 'design-completion', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
3471
3635
|
flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
|
|
3472
3636
|
} else {
|
|
3473
3637
|
flags[normalizedKey] = value;
|
|
@@ -3508,6 +3672,8 @@ function emptyProofArtifact() {
|
|
|
3508
3672
|
runtimeBackendEvidence: [],
|
|
3509
3673
|
visualProvenance: [],
|
|
3510
3674
|
missionEnvelope: [],
|
|
3675
|
+
missionReceipt: [],
|
|
3676
|
+
missionCompletion: [],
|
|
3511
3677
|
rollbackHint: [],
|
|
3512
3678
|
mediaProof: [],
|
|
3513
3679
|
designCompletion: [],
|
|
@@ -3536,6 +3702,8 @@ async function parseProofJson(file, empty) {
|
|
|
3536
3702
|
runtimeBackendEvidence: structuredEvidenceList(data.runtimeBackendEvidence || data.runtime_backend_evidence),
|
|
3537
3703
|
visualProvenance: structuredEvidenceList(data.visualProvenance || data.visual_provenance),
|
|
3538
3704
|
missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
|
|
3705
|
+
missionReceipt: structuredEvidenceList(data.missionReceipt || data.mission_receipt),
|
|
3706
|
+
missionCompletion: structuredEvidenceList(data.missionCompletion || data.mission_completion),
|
|
3539
3707
|
rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
|
|
3540
3708
|
mediaProof: structuredEvidenceList(data.mediaProof || data.media_proof),
|
|
3541
3709
|
designCompletion: structuredEvidenceList(data.designCompletion || data.design_completion),
|
|
@@ -3567,6 +3735,8 @@ async function parseProofMarkdown(file, empty) {
|
|
|
3567
3735
|
runtimeBackendEvidence: readProofList(text, 'Runtime backend evidence'),
|
|
3568
3736
|
visualProvenance: readProofList(text, 'Visual provenance'),
|
|
3569
3737
|
missionEnvelope: readProofList(text, 'Mission patch envelope'),
|
|
3738
|
+
missionReceipt: readProofList(text, 'Mission subagent receipt'),
|
|
3739
|
+
missionCompletion: readProofList(text, 'Mission completion gate'),
|
|
3570
3740
|
rollbackHint: readProofList(text, 'Rollback hint'),
|
|
3571
3741
|
mediaProof: readProofList(text, 'Media proof'),
|
|
3572
3742
|
designCompletion: readProofList(text, 'Design completion'),
|
|
@@ -3651,6 +3821,12 @@ function formatStructuredEvidence(value) {
|
|
|
3651
3821
|
if (data.schema === 'yam.ueye-visual-provenance.v1' || data.source_kind || data.reference_id || data.screenshot_id) {
|
|
3652
3822
|
return JSON.stringify(buildUeyeVisualProvenance(data));
|
|
3653
3823
|
}
|
|
3824
|
+
if (data.schema === 'yam.mission-subagent-receipt.v1' || data.thread_id || data.lifecycle_status || data.outcome) {
|
|
3825
|
+
return JSON.stringify(buildMissionSubagentReceipt(data));
|
|
3826
|
+
}
|
|
3827
|
+
if (data.schema === 'yam.mission-completion-gate.v1' || data.expected_thread_ids || data.ready_to_claim_complete !== undefined) {
|
|
3828
|
+
return JSON.stringify(buildMissionCompletionGate(data));
|
|
3829
|
+
}
|
|
3654
3830
|
if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
|
|
3655
3831
|
return JSON.stringify(buildMissionPatchEnvelope(data));
|
|
3656
3832
|
}
|
|
@@ -3674,7 +3850,7 @@ function formatStructuredEvidence(value) {
|
|
|
3674
3850
|
|
|
3675
3851
|
function hasProofEvidence(parsed) {
|
|
3676
3852
|
const flags = parsed.flags || {};
|
|
3677
|
-
return ['command', 'evidence', 'visual', 'runtime', 'runtime_backend_evidence', 'visual_provenance', 'mission_envelope', 'rollback_hint', 'media_proof', 'design_completion', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup || flags.runtime_backend || flags.runtime_claim);
|
|
3853
|
+
return ['command', 'evidence', 'visual', 'runtime', 'runtime_backend_evidence', 'visual_provenance', 'mission_envelope', 'mission_receipt', 'mission_completion', 'rollback_hint', 'media_proof', 'design_completion', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup || flags.runtime_backend || flags.runtime_claim);
|
|
3678
3854
|
}
|
|
3679
3855
|
|
|
3680
3856
|
function printProofList(label, values) {
|