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/dist/bin/yam.js
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';
|
|
@@ -8,7 +7,9 @@ import os from 'node:os';
|
|
|
8
7
|
import path from 'node:path';
|
|
9
8
|
import process from 'node:process';
|
|
10
9
|
import { fileURLToPath } from 'node:url';
|
|
11
|
-
import { TRUTH_STATUSES, buildLoopReport, buildMediaGenerationProof, buildMissionPatchEnvelope, buildRollbackHint, buildRuntimeBackendEvidence, buildStudyNote, buildUeyeDesignCompletionGate, buildUeyeVisualProvenance, buildUeyeRunReport, buildUeyeSurfaceContext, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
|
|
10
|
+
import { TRUTH_STATUSES, buildLoopReport, buildMediaGenerationProof, buildMissionCompletionGate, buildMissionPatchEnvelope, buildMissionSubagentReceipt, buildRollbackHint, buildRuntimeBackendEvidence, buildStudyNote, buildUeyeDesignCompletionGate, buildUeyeVisualProvenance, buildUeyeRunReport, buildUeyeSurfaceContext, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
|
|
11
|
+
import { archiveUeyeRevision, inspectImageFile, upsertUeyeAsset, verifyUeyeAssetManifest, verifyUeyeRevisionHistory } from '../lib/ueye-artifacts.js';
|
|
12
|
+
import { INSTALL_LOCK_NAME, INSTALL_RECEIPT_NAME, inspectSkillInstallation, installSkillSetTransactional } from '../lib/skill-installation.js';
|
|
12
13
|
function errorMessage(error) {
|
|
13
14
|
return error instanceof Error ? error.message : String(error);
|
|
14
15
|
}
|
|
@@ -129,9 +130,13 @@ Usage:
|
|
|
129
130
|
yam ueye compare --reference ref.png --actual screenshot.png [--json]
|
|
130
131
|
yam ueye preflight [dir] [--json]
|
|
131
132
|
yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
|
|
133
|
+
yam ueye asset <add|verify> [options]
|
|
134
|
+
yam ueye revision <archive|verify> [options]
|
|
132
135
|
yam media proof [--requested] [--attempted] [--output file] [--json]
|
|
133
136
|
yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
|
|
134
137
|
yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
|
|
138
|
+
yam mission receipt [--thread-id id] [--role reviewer] [--lifecycle stopped] [--outcome passed] [--evidence text] [--json]
|
|
139
|
+
yam mission gate [--expected-thread id] [--receipt file] [--json]
|
|
135
140
|
yam benchmark report [--baseline n] [--current n] [--unit ms] [--target lower|higher] [--json]
|
|
136
141
|
yam release report [--json]
|
|
137
142
|
yam safety [text...]
|
|
@@ -163,73 +168,77 @@ async function exists(file) {
|
|
|
163
168
|
async function rmrf(target) {
|
|
164
169
|
await fsp.rm(target, { recursive: true, force: true });
|
|
165
170
|
}
|
|
166
|
-
async function copyDir(source, target) {
|
|
167
|
-
await fsp.mkdir(target, { recursive: true });
|
|
168
|
-
const entries = await fsp.readdir(source, { withFileTypes: true });
|
|
169
|
-
for (const entry of entries) {
|
|
170
|
-
const from = path.join(source, entry.name);
|
|
171
|
-
const to = path.join(target, entry.name);
|
|
172
|
-
if (entry.isDirectory()) {
|
|
173
|
-
await copyDir(from, to);
|
|
174
|
-
}
|
|
175
|
-
else if (entry.isFile()) {
|
|
176
|
-
await fsp.copyFile(from, to);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
async function installSkill(skill) {
|
|
181
|
-
const source = path.join(ROOT, 'skills', skill);
|
|
182
|
-
const target = path.join(DEST, skill);
|
|
183
|
-
const references = path.join(ROOT, 'references');
|
|
184
|
-
if (!await exists(path.join(source, 'SKILL.md'))) {
|
|
185
|
-
throw new Error(`missing skill source: ${source}`);
|
|
186
|
-
}
|
|
187
|
-
await rmrf(target);
|
|
188
|
-
await fsp.mkdir(target, { recursive: true });
|
|
189
|
-
await fsp.copyFile(path.join(source, 'SKILL.md'), path.join(target, 'SKILL.md'));
|
|
190
|
-
await copyDir(references, path.join(target, 'references'));
|
|
191
|
-
}
|
|
192
171
|
async function install() {
|
|
193
|
-
await
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
}
|
|
203
|
-
if (CODEX_MIRROR !== DEST && fs.existsSync(CODEX_MIRROR)) {
|
|
204
|
-
for (const skill of [...SKILLS, ...LEGACY_SKILLS, ...RETIRED_SKILLS]) {
|
|
205
|
-
await rmrf(path.join(CODEX_MIRROR, skill));
|
|
206
|
-
}
|
|
207
|
-
}
|
|
172
|
+
const result = await installSkillSetTransactional({
|
|
173
|
+
sourceRoot: ROOT,
|
|
174
|
+
destination: DEST,
|
|
175
|
+
codexMirror: CODEX_MIRROR,
|
|
176
|
+
packageName: String(PACKAGE_JSON.name || 'yam-flow'),
|
|
177
|
+
version: VERSION,
|
|
178
|
+
skills: SKILLS,
|
|
179
|
+
legacySkills: LEGACY_SKILLS,
|
|
180
|
+
retiredSkills: RETIRED_SKILLS
|
|
181
|
+
});
|
|
208
182
|
console.log(`yam installed to ${DEST}`);
|
|
183
|
+
console.log(`install receipt: ${result.receiptPath}`);
|
|
184
|
+
console.log(`integrity: ${result.installedFiles} files, sha256:${result.sourceDigest}`);
|
|
185
|
+
for (const warning of result.cleanupWarnings)
|
|
186
|
+
console.warn(`warning: ${warning}`);
|
|
209
187
|
console.log('Restart Codex to reload skills.');
|
|
210
188
|
}
|
|
211
189
|
async function uninstall() {
|
|
190
|
+
const lockPath = path.join(DEST, INSTALL_LOCK_NAME);
|
|
191
|
+
if (await exists(lockPath)) {
|
|
192
|
+
throw new Error(`cannot uninstall while an install lock exists: ${lockPath}`);
|
|
193
|
+
}
|
|
212
194
|
for (const skill of [...SKILLS, ...LEGACY_SKILLS, ...RETIRED_SKILLS]) {
|
|
213
195
|
await rmrf(path.join(DEST, skill));
|
|
214
196
|
if (CODEX_MIRROR !== DEST) {
|
|
215
197
|
await rmrf(path.join(CODEX_MIRROR, skill));
|
|
216
198
|
}
|
|
217
199
|
}
|
|
200
|
+
await rmrf(path.join(DEST, INSTALL_RECEIPT_NAME));
|
|
218
201
|
console.log(`yam removed from ${DEST}`);
|
|
219
202
|
if (CODEX_MIRROR !== DEST)
|
|
220
203
|
console.log(`yam mirror entries removed from ${CODEX_MIRROR}`);
|
|
221
204
|
console.log('Restart Codex to unload skills.');
|
|
222
205
|
}
|
|
223
206
|
async function status({ quiet = false } = {}) {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
207
|
+
const report = await inspectSkillInstallation({
|
|
208
|
+
sourceRoot: ROOT,
|
|
209
|
+
destination: DEST,
|
|
210
|
+
packageName: String(PACKAGE_JSON.name || 'yam-flow'),
|
|
211
|
+
version: VERSION,
|
|
212
|
+
skills: SKILLS
|
|
213
|
+
});
|
|
214
|
+
if (!quiet) {
|
|
215
|
+
for (const skill of report.skills) {
|
|
216
|
+
const label = skill.status === 'ok' ? 'ok ' : skill.status === 'missing' ? 'missing' : 'drift ';
|
|
217
|
+
console.log(`${label} ${skill.skill}`);
|
|
218
|
+
}
|
|
219
|
+
if (report.receiptStatus === 'ok' && report.receipt) {
|
|
220
|
+
console.log(`ok install receipt ${report.receipt.package.name}@${report.receipt.package.version}`);
|
|
221
|
+
console.log(` sha256:${report.receipt.integrity.source_digest}`);
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
console.log(`${report.receiptStatus === 'missing' ? 'missing' : 'drift '} install receipt`);
|
|
225
|
+
}
|
|
226
|
+
if (report.issues.length) {
|
|
227
|
+
console.log('');
|
|
228
|
+
console.log('Install integrity issues:');
|
|
229
|
+
for (const issue of report.issues.slice(0, 12))
|
|
230
|
+
console.log(`- ${issue}`);
|
|
231
|
+
if (report.issues.length > 12)
|
|
232
|
+
console.log(`- ... ${report.issues.length - 12} more issue(s)`);
|
|
233
|
+
if (report.recoveryArtifacts.length) {
|
|
234
|
+
console.log('Confirm no install is running, then inspect and preserve the transaction backup before retrying.');
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
console.log('Run `yam install` to restore the package-bundled skill set.');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
231
240
|
}
|
|
232
|
-
return
|
|
241
|
+
return report.ok ? 0 : Math.max(1, report.issues.length);
|
|
233
242
|
}
|
|
234
243
|
async function list() {
|
|
235
244
|
const manifest = await loadManifest();
|
|
@@ -275,6 +284,8 @@ async function verify({ quiet = false } = {}) {
|
|
|
275
284
|
return failOrReport('yam verify', issues, quiet);
|
|
276
285
|
if (manifest.name !== 'yam')
|
|
277
286
|
issues.push('manifest name must be yam');
|
|
287
|
+
if (manifest.version !== VERSION)
|
|
288
|
+
issues.push(`manifest version must match package version: ${manifest.version || 'missing'} != ${VERSION}`);
|
|
278
289
|
if (manifest.defaultHooks !== false)
|
|
279
290
|
issues.push('manifest defaultHooks must be false');
|
|
280
291
|
if (!Array.isArray(manifest.routes) || manifest.routes.length !== SKILLS.length) {
|
|
@@ -343,7 +354,7 @@ async function verify({ quiet = false } = {}) {
|
|
|
343
354
|
if (!await exists(path.join(ROOT, 'templates', template)))
|
|
344
355
|
issues.push(`missing template: ${template}`);
|
|
345
356
|
}
|
|
346
|
-
for (const module of ['trust-kernel.js']) {
|
|
357
|
+
for (const module of ['trust-kernel.js', 'ueye-artifacts.js']) {
|
|
347
358
|
if (!await exists(path.join(ROOT, 'dist', 'lib', module)))
|
|
348
359
|
issues.push(`missing dist lib module: ${module}`);
|
|
349
360
|
}
|
|
@@ -2251,6 +2262,10 @@ async function ueye(args = []) {
|
|
|
2251
2262
|
return ueyePreflight(args.slice(1));
|
|
2252
2263
|
if (subcommand === 'report')
|
|
2253
2264
|
return ueyeReport(args.slice(1));
|
|
2265
|
+
if (subcommand === 'asset')
|
|
2266
|
+
return ueyeAsset(args.slice(1));
|
|
2267
|
+
if (subcommand === 'revision')
|
|
2268
|
+
return ueyeRevision(args.slice(1));
|
|
2254
2269
|
console.error(`unknown ueye command: ${subcommand}`);
|
|
2255
2270
|
return ueyeUsage();
|
|
2256
2271
|
}
|
|
@@ -2263,14 +2278,97 @@ Usage:
|
|
|
2263
2278
|
yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
|
|
2264
2279
|
yam ueye compare --reference ref.png --actual screenshot.png [--json]
|
|
2265
2280
|
yam ueye preflight [dir] [--json]
|
|
2266
|
-
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]
|
|
2281
|
+
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]
|
|
2282
|
+
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]
|
|
2283
|
+
yam ueye asset verify [--manifest file] [--json]
|
|
2284
|
+
yam ueye revision archive --file image --round n [--artifact-id id] [--root dir] [--manifest file] [--json]
|
|
2285
|
+
yam ueye revision verify [--root dir] [--manifest file] [--json]
|
|
2267
2286
|
|
|
2268
2287
|
Notes:
|
|
2269
2288
|
capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
|
|
2270
2289
|
compare uses local files only and reports sha256, dimensions, comparison_result, and proof-ready visual provenance.
|
|
2271
2290
|
report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
|
|
2291
|
+
asset records local reference provenance and protection flags; it never downloads remote content.
|
|
2292
|
+
revision copies the current local artifact into a non-overwriting round archive before the live file is edited.
|
|
2272
2293
|
`);
|
|
2273
2294
|
}
|
|
2295
|
+
async function ueyeAsset(args = []) {
|
|
2296
|
+
const subcommand = args[0] || 'help';
|
|
2297
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h')
|
|
2298
|
+
return ueyeUsage();
|
|
2299
|
+
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']));
|
|
2300
|
+
const manifest = path.resolve(expandHome(flags.manifest || path.join(process.cwd(), '.yam', 'ueye', 'assets.json')));
|
|
2301
|
+
try {
|
|
2302
|
+
if (subcommand === 'add') {
|
|
2303
|
+
if (!flags.id || !flags.file)
|
|
2304
|
+
throw new Error('asset add requires --id and --file');
|
|
2305
|
+
const result = await upsertUeyeAsset({
|
|
2306
|
+
manifest_path: manifest,
|
|
2307
|
+
id: String(flags.id),
|
|
2308
|
+
file: path.resolve(expandHome(flags.file)),
|
|
2309
|
+
source_url: flags.source_url,
|
|
2310
|
+
source_page_url: flags.source_page_url,
|
|
2311
|
+
retrieved_at: flags.retrieved_at,
|
|
2312
|
+
license_note: flags.license_note,
|
|
2313
|
+
operator_provided: flags.operator_provided ? true : undefined,
|
|
2314
|
+
do_not_replace: flags.do_not_replace ? true : undefined,
|
|
2315
|
+
allowed_for_edit: flags.allowed_for_edit ? true : undefined,
|
|
2316
|
+
replace: Boolean(flags.replace)
|
|
2317
|
+
});
|
|
2318
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset');
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
if (subcommand === 'verify') {
|
|
2322
|
+
const result = await verifyUeyeAssetManifest(manifest);
|
|
2323
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset verification');
|
|
2324
|
+
if (result.truth_status === 'blocked')
|
|
2325
|
+
process.exitCode = 1;
|
|
2326
|
+
return;
|
|
2327
|
+
}
|
|
2328
|
+
throw new Error(`unknown ueye asset command: ${subcommand}`);
|
|
2329
|
+
}
|
|
2330
|
+
catch (error) {
|
|
2331
|
+
const result = { schema: 'yam.ueye-asset-error.v1', status: 'blocked', reason: errorMessage(error), truth_status: 'blocked' };
|
|
2332
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye asset');
|
|
2333
|
+
process.exitCode = 1;
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
2336
|
+
async function ueyeRevision(args = []) {
|
|
2337
|
+
const subcommand = args[0] || 'help';
|
|
2338
|
+
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h')
|
|
2339
|
+
return ueyeUsage();
|
|
2340
|
+
const flags = parseSimpleFlags(args.slice(1), new Set(['file', 'round', 'artifact-id', 'root', 'manifest', 'json']));
|
|
2341
|
+
const root = path.resolve(expandHome(flags.root || path.join(process.cwd(), '.yam', 'ueye', 'revisions')));
|
|
2342
|
+
const manifest = path.resolve(expandHome(flags.manifest || path.join(root, 'manifest.json')));
|
|
2343
|
+
try {
|
|
2344
|
+
if (subcommand === 'archive') {
|
|
2345
|
+
if (!flags.file || !flags.round)
|
|
2346
|
+
throw new Error('revision archive requires --file and --round');
|
|
2347
|
+
const result = await archiveUeyeRevision({
|
|
2348
|
+
file: path.resolve(expandHome(flags.file)),
|
|
2349
|
+
root,
|
|
2350
|
+
manifest_path: manifest,
|
|
2351
|
+
round: Number(flags.round),
|
|
2352
|
+
artifact_id: flags.artifact_id
|
|
2353
|
+
});
|
|
2354
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision');
|
|
2355
|
+
return;
|
|
2356
|
+
}
|
|
2357
|
+
if (subcommand === 'verify') {
|
|
2358
|
+
const result = await verifyUeyeRevisionHistory(manifest);
|
|
2359
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision verification');
|
|
2360
|
+
if (result.truth_status === 'blocked')
|
|
2361
|
+
process.exitCode = 1;
|
|
2362
|
+
return;
|
|
2363
|
+
}
|
|
2364
|
+
throw new Error(`unknown ueye revision command: ${subcommand}`);
|
|
2365
|
+
}
|
|
2366
|
+
catch (error) {
|
|
2367
|
+
const result = { schema: 'yam.ueye-revision-error.v1', status: 'blocked', reason: errorMessage(error), truth_status: 'blocked' };
|
|
2368
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye revision');
|
|
2369
|
+
process.exitCode = 1;
|
|
2370
|
+
}
|
|
2371
|
+
}
|
|
2274
2372
|
async function ueyePreflight(args = []) {
|
|
2275
2373
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
|
|
2276
2374
|
return ueyeUsage();
|
|
@@ -2504,7 +2602,7 @@ async function ueyeCompare(args = []) {
|
|
|
2504
2602
|
async function ueyeReport(args = []) {
|
|
2505
2603
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
|
|
2506
2604
|
return ueyeUsage();
|
|
2507
|
-
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']));
|
|
2605
|
+
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']));
|
|
2508
2606
|
const reference = String(flags.reference || flags.ref || '');
|
|
2509
2607
|
const actual = String(flags.actual || flags.screenshot || '');
|
|
2510
2608
|
const referenceSources = [];
|
|
@@ -2584,6 +2682,13 @@ async function ueyeReport(args = []) {
|
|
|
2584
2682
|
catch (error) {
|
|
2585
2683
|
blockedReason = errorMessage(error);
|
|
2586
2684
|
}
|
|
2685
|
+
const assetManifest = await optionalUeyeAssetVerification(flags.asset_manifest);
|
|
2686
|
+
const revisionHistory = await optionalUeyeRevisionVerification(flags.revision_manifest);
|
|
2687
|
+
const doneClaim = ['done', 'complete', 'completed'].includes(String(flags.completion_claim || flags.completion_status || '').toLowerCase());
|
|
2688
|
+
if (doneClaim && assetManifest?.truth_status === 'blocked')
|
|
2689
|
+
blockedReason = blockedReason || `asset manifest blocked: ${assetManifest.next_action || 'verification failed'}`;
|
|
2690
|
+
if (doneClaim && revisionHistory?.truth_status === 'blocked')
|
|
2691
|
+
blockedReason = blockedReason || `revision history blocked: ${revisionHistory.next_action || 'verification failed'}`;
|
|
2587
2692
|
const report = buildUeyeRunReport({
|
|
2588
2693
|
reference_sources: referenceSources,
|
|
2589
2694
|
implementation_sources: implementationSources,
|
|
@@ -2666,12 +2771,34 @@ async function ueyeReport(args = []) {
|
|
|
2666
2771
|
capture_backend: String(flags.capture_backend || 'not-recorded'),
|
|
2667
2772
|
compare_backend: String(flags.compare_backend || 'local-file-hash'),
|
|
2668
2773
|
continuity: comparisonReport.continuity,
|
|
2669
|
-
comparison_report: comparisonReport
|
|
2774
|
+
comparison_report: comparisonReport,
|
|
2775
|
+
asset_manifest: assetManifest,
|
|
2776
|
+
revision_history: revisionHistory
|
|
2670
2777
|
};
|
|
2671
2778
|
printJsonOrHuman(result, Boolean(flags.json), 'Ueye report');
|
|
2672
2779
|
if (report.truth_status === 'blocked')
|
|
2673
2780
|
process.exitCode = 1;
|
|
2674
2781
|
}
|
|
2782
|
+
async function optionalUeyeAssetVerification(file = '') {
|
|
2783
|
+
if (!file)
|
|
2784
|
+
return null;
|
|
2785
|
+
try {
|
|
2786
|
+
return await verifyUeyeAssetManifest(path.resolve(expandHome(file)));
|
|
2787
|
+
}
|
|
2788
|
+
catch (error) {
|
|
2789
|
+
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' };
|
|
2790
|
+
}
|
|
2791
|
+
}
|
|
2792
|
+
async function optionalUeyeRevisionVerification(file = '') {
|
|
2793
|
+
if (!file)
|
|
2794
|
+
return null;
|
|
2795
|
+
try {
|
|
2796
|
+
return await verifyUeyeRevisionHistory(path.resolve(expandHome(file)));
|
|
2797
|
+
}
|
|
2798
|
+
catch (error) {
|
|
2799
|
+
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' };
|
|
2800
|
+
}
|
|
2801
|
+
}
|
|
2675
2802
|
function buildUeyeDesignBrief(flags = {}) {
|
|
2676
2803
|
const dimensions = arrayFlag(flags.brief_dimension);
|
|
2677
2804
|
const constraints = arrayFlag(flags.constraint);
|
|
@@ -2925,6 +3052,10 @@ async function mission(args = []) {
|
|
|
2925
3052
|
return missionUsage();
|
|
2926
3053
|
if (subcommand === 'queue' || subcommand === 'patch')
|
|
2927
3054
|
return missionQueue(args.slice(1));
|
|
3055
|
+
if (subcommand === 'receipt')
|
|
3056
|
+
return missionReceipt(args.slice(1));
|
|
3057
|
+
if (subcommand === 'gate' || subcommand === 'close')
|
|
3058
|
+
return missionGate(args.slice(1));
|
|
2928
3059
|
console.error(`unknown mission command: ${subcommand}`);
|
|
2929
3060
|
return missionUsage();
|
|
2930
3061
|
}
|
|
@@ -2933,11 +3064,80 @@ function missionUsage() {
|
|
|
2933
3064
|
|
|
2934
3065
|
Usage:
|
|
2935
3066
|
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]
|
|
3067
|
+
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]
|
|
3068
|
+
yam mission gate --expected-thread id --receipt file [--expected-thread id --receipt file] [--out file] [--json]
|
|
2936
3069
|
|
|
2937
3070
|
Notes:
|
|
2938
3071
|
Produces a patch queue item for mission handoff/review. It does not run workers; persistence is opt-in with --out.
|
|
3072
|
+
Reviewer and doctor receipts default to read-only and are blocked if they claim changed files or write access.
|
|
3073
|
+
A stopped lifecycle is not success by itself; a passed outcome needs explicit verification evidence.
|
|
2939
3074
|
`);
|
|
2940
3075
|
}
|
|
3076
|
+
async function missionReceipt(args = []) {
|
|
3077
|
+
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
|
|
3078
|
+
return missionUsage();
|
|
3079
|
+
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']));
|
|
3080
|
+
const receipt = buildMissionSubagentReceipt({
|
|
3081
|
+
receipt_id: String(flags.receipt_id || `receipt-${timestampId()}`),
|
|
3082
|
+
thread_id: String(flags.thread_id || ''),
|
|
3083
|
+
lane_id: String(flags.lane_id || flags.thread_id || ''),
|
|
3084
|
+
agent_id: String(flags.agent_id || ''),
|
|
3085
|
+
role: flags.role,
|
|
3086
|
+
access_mode: flags.access_mode,
|
|
3087
|
+
lifecycle_status: flags.lifecycle,
|
|
3088
|
+
outcome: flags.outcome,
|
|
3089
|
+
assigned_scope: String(flags.scope || ''),
|
|
3090
|
+
changed_files: arrayFlag(flags.changed),
|
|
3091
|
+
verification_evidence: arrayFlag(flags.evidence),
|
|
3092
|
+
remaining_risks: arrayFlag(flags.remaining_risk)
|
|
3093
|
+
});
|
|
3094
|
+
const result = {
|
|
3095
|
+
...receipt,
|
|
3096
|
+
persistence: flags.out ? 'written' : 'not-persisted',
|
|
3097
|
+
out: flags.out ? path.resolve(expandHome(flags.out)) : '',
|
|
3098
|
+
next_action: receipt.completion_eligible ? 'include this receipt in `yam mission gate`' : receipt.blockers[0] || 'complete the receipt evidence'
|
|
3099
|
+
};
|
|
3100
|
+
if (flags.out)
|
|
3101
|
+
await writeJsonArtifact(flags.out, result);
|
|
3102
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Mission receipt');
|
|
3103
|
+
if (receipt.truth_status === 'blocked')
|
|
3104
|
+
process.exitCode = 1;
|
|
3105
|
+
}
|
|
3106
|
+
async function missionGate(args = []) {
|
|
3107
|
+
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
|
|
3108
|
+
return missionUsage();
|
|
3109
|
+
const flags = parseSimpleFlags(args, new Set(['expected-thread', 'receipt', 'out', 'json']));
|
|
3110
|
+
const receipts = [];
|
|
3111
|
+
const loadErrors = [];
|
|
3112
|
+
for (const file of arrayFlag(flags.receipt)) {
|
|
3113
|
+
const target = path.resolve(expandHome(file));
|
|
3114
|
+
try {
|
|
3115
|
+
receipts.push(await readJson(target));
|
|
3116
|
+
}
|
|
3117
|
+
catch (error) {
|
|
3118
|
+
loadErrors.push(`${target}: ${errorMessage(error)}`);
|
|
3119
|
+
}
|
|
3120
|
+
}
|
|
3121
|
+
const gate = buildMissionCompletionGate({
|
|
3122
|
+
expected_thread_ids: arrayFlag(flags.expected_thread),
|
|
3123
|
+
receipts
|
|
3124
|
+
});
|
|
3125
|
+
const blockers = [...gate.blockers, ...loadErrors.map((error) => `receipt_load_failed: ${error}`)];
|
|
3126
|
+
const result = {
|
|
3127
|
+
...gate,
|
|
3128
|
+
blockers,
|
|
3129
|
+
ready_to_claim_complete: gate.ready_to_claim_complete && loadErrors.length === 0,
|
|
3130
|
+
next_action: blockers[0] || gate.next_action,
|
|
3131
|
+
truth_status: blockers.length ? 'blocked' : gate.truth_status,
|
|
3132
|
+
persistence: flags.out ? 'written' : 'not-persisted',
|
|
3133
|
+
out: flags.out ? path.resolve(expandHome(flags.out)) : ''
|
|
3134
|
+
};
|
|
3135
|
+
if (flags.out)
|
|
3136
|
+
await writeJsonArtifact(flags.out, result);
|
|
3137
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Mission completion gate');
|
|
3138
|
+
if (result.truth_status === 'blocked')
|
|
3139
|
+
process.exitCode = 1;
|
|
3140
|
+
}
|
|
2941
3141
|
async function missionQueue(args = []) {
|
|
2942
3142
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
|
|
2943
3143
|
return missionUsage();
|
|
@@ -3095,7 +3295,7 @@ function parseSimpleFlags(args = [], allowed = new Set()) {
|
|
|
3095
3295
|
if (allowed.size && !allowed.has(key))
|
|
3096
3296
|
continue;
|
|
3097
3297
|
const normalizedKey = key.replace(/-/g, '_');
|
|
3098
|
-
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)) {
|
|
3298
|
+
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)) {
|
|
3099
3299
|
flags[normalizedKey] = true;
|
|
3100
3300
|
continue;
|
|
3101
3301
|
}
|
|
@@ -3137,58 +3337,7 @@ async function loadOptionalPlaywright() {
|
|
|
3137
3337
|
}
|
|
3138
3338
|
}
|
|
3139
3339
|
async function imageFileInfo(file) {
|
|
3140
|
-
|
|
3141
|
-
const buffer = await fsp.readFile(absolute);
|
|
3142
|
-
const stat = await fsp.stat(absolute);
|
|
3143
|
-
return {
|
|
3144
|
-
path: absolute,
|
|
3145
|
-
sha256: crypto.createHash('sha256').update(buffer).digest('hex'),
|
|
3146
|
-
bytes: stat.size,
|
|
3147
|
-
dimensions: imageDimensions(buffer)
|
|
3148
|
-
};
|
|
3149
|
-
}
|
|
3150
|
-
function imageDimensions(buffer) {
|
|
3151
|
-
const png = pngDimensions(buffer);
|
|
3152
|
-
if (png)
|
|
3153
|
-
return png;
|
|
3154
|
-
const jpeg = jpegDimensions(buffer);
|
|
3155
|
-
if (jpeg)
|
|
3156
|
-
return jpeg;
|
|
3157
|
-
const gif = gifDimensions(buffer);
|
|
3158
|
-
if (gif)
|
|
3159
|
-
return gif;
|
|
3160
|
-
return 'unknown';
|
|
3161
|
-
}
|
|
3162
|
-
function pngDimensions(buffer) {
|
|
3163
|
-
if (buffer.length < 24)
|
|
3164
|
-
return '';
|
|
3165
|
-
if (buffer.readUInt32BE(0) !== 0x89504e47 || buffer.readUInt32BE(4) !== 0x0d0a1a0a)
|
|
3166
|
-
return '';
|
|
3167
|
-
return `${buffer.readUInt32BE(16)}x${buffer.readUInt32BE(20)}`;
|
|
3168
|
-
}
|
|
3169
|
-
function gifDimensions(buffer) {
|
|
3170
|
-
if (buffer.length < 10)
|
|
3171
|
-
return '';
|
|
3172
|
-
const signature = buffer.toString('ascii', 0, 6);
|
|
3173
|
-
if (signature !== 'GIF87a' && signature !== 'GIF89a')
|
|
3174
|
-
return '';
|
|
3175
|
-
return `${buffer.readUInt16LE(6)}x${buffer.readUInt16LE(8)}`;
|
|
3176
|
-
}
|
|
3177
|
-
function jpegDimensions(buffer) {
|
|
3178
|
-
if (buffer.length < 4 || buffer[0] !== 0xff || buffer[1] !== 0xd8)
|
|
3179
|
-
return '';
|
|
3180
|
-
let offset = 2;
|
|
3181
|
-
while (offset < buffer.length) {
|
|
3182
|
-
if (buffer[offset] !== 0xff)
|
|
3183
|
-
return '';
|
|
3184
|
-
const marker = buffer[offset + 1];
|
|
3185
|
-
const length = buffer.readUInt16BE(offset + 2);
|
|
3186
|
-
if (marker >= 0xc0 && marker <= 0xc3 && offset + 8 < buffer.length) {
|
|
3187
|
-
return `${buffer.readUInt16BE(offset + 7)}x${buffer.readUInt16BE(offset + 5)}`;
|
|
3188
|
-
}
|
|
3189
|
-
offset += 2 + length;
|
|
3190
|
-
}
|
|
3191
|
-
return '';
|
|
3340
|
+
return inspectImageFile(path.resolve(expandHome(file)));
|
|
3192
3341
|
}
|
|
3193
3342
|
function printJsonOrHuman(result, json = false, label = 'yam') {
|
|
3194
3343
|
if (json) {
|
|
@@ -3207,6 +3356,11 @@ function printJsonOrHuman(result, json = false, label = 'yam') {
|
|
|
3207
3356
|
}
|
|
3208
3357
|
}
|
|
3209
3358
|
}
|
|
3359
|
+
async function writeJsonArtifact(file, value) {
|
|
3360
|
+
const target = path.resolve(expandHome(file));
|
|
3361
|
+
await fsp.mkdir(path.dirname(target), { recursive: true });
|
|
3362
|
+
await fsp.writeFile(target, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
3363
|
+
}
|
|
3210
3364
|
function detectDbSafetyText(text = '') {
|
|
3211
3365
|
return detectTrustDbSafetyText(text);
|
|
3212
3366
|
}
|
|
@@ -3250,6 +3404,8 @@ async function proof(args = []) {
|
|
|
3250
3404
|
printProofList('Runtime backend evidence', summary.runtimeBackendEvidence || []);
|
|
3251
3405
|
printProofList('Visual provenance', summary.visualProvenance || []);
|
|
3252
3406
|
printProofList('Mission patch envelope', summary.missionEnvelope || []);
|
|
3407
|
+
printProofList('Mission subagent receipt', summary.missionReceipt || []);
|
|
3408
|
+
printProofList('Mission completion gate', summary.missionCompletion || []);
|
|
3253
3409
|
printProofList('Rollback hint', summary.rollbackHint || []);
|
|
3254
3410
|
printProofList('Media proof', summary.mediaProof || []);
|
|
3255
3411
|
printProofList('Design completion', summary.designCompletion || []);
|
|
@@ -3341,6 +3497,14 @@ function buildProofSummary(parsed, artifact) {
|
|
|
3341
3497
|
...structuredEvidenceList(artifact.missionEnvelope),
|
|
3342
3498
|
...structuredEvidenceList(parsed.flags.mission_envelope)
|
|
3343
3499
|
],
|
|
3500
|
+
missionReceipt: [
|
|
3501
|
+
...structuredEvidenceList(artifact.missionReceipt),
|
|
3502
|
+
...structuredEvidenceList(parsed.flags.mission_receipt)
|
|
3503
|
+
],
|
|
3504
|
+
missionCompletion: [
|
|
3505
|
+
...structuredEvidenceList(artifact.missionCompletion),
|
|
3506
|
+
...structuredEvidenceList(parsed.flags.mission_completion)
|
|
3507
|
+
],
|
|
3344
3508
|
rollbackHint: [
|
|
3345
3509
|
...structuredEvidenceList(artifact.rollbackHint),
|
|
3346
3510
|
...structuredEvidenceList(parsed.flags.rollback_hint)
|
|
@@ -3411,6 +3575,12 @@ function renderProofMarkdown(summary) {
|
|
|
3411
3575
|
'## Mission patch envelope',
|
|
3412
3576
|
...renderProofMarkdownList(summary.missionEnvelope || []),
|
|
3413
3577
|
'',
|
|
3578
|
+
'## Mission subagent receipt',
|
|
3579
|
+
...renderProofMarkdownList(summary.missionReceipt || []),
|
|
3580
|
+
'',
|
|
3581
|
+
'## Mission completion gate',
|
|
3582
|
+
...renderProofMarkdownList(summary.missionCompletion || []),
|
|
3583
|
+
'',
|
|
3414
3584
|
'## Rollback hint',
|
|
3415
3585
|
...renderProofMarkdownList(summary.rollbackHint || []),
|
|
3416
3586
|
'',
|
|
@@ -3450,7 +3620,7 @@ function renderProofMarkdownList(values) {
|
|
|
3450
3620
|
function parseProofArgs(args = []) {
|
|
3451
3621
|
const flags = {};
|
|
3452
3622
|
const positionals = [];
|
|
3453
|
-
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']);
|
|
3623
|
+
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']);
|
|
3454
3624
|
for (let index = 0; index < args.length; index += 1) {
|
|
3455
3625
|
const arg = args[index];
|
|
3456
3626
|
if (arg.startsWith('--')) {
|
|
@@ -3466,7 +3636,7 @@ function parseProofArgs(args = []) {
|
|
|
3466
3636
|
const value = inlineValue ?? args[index + 1] ?? '';
|
|
3467
3637
|
if (inlineValue === undefined)
|
|
3468
3638
|
index += 1;
|
|
3469
|
-
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)) {
|
|
3639
|
+
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)) {
|
|
3470
3640
|
flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
|
|
3471
3641
|
}
|
|
3472
3642
|
else {
|
|
@@ -3508,6 +3678,8 @@ function emptyProofArtifact() {
|
|
|
3508
3678
|
runtimeBackendEvidence: [],
|
|
3509
3679
|
visualProvenance: [],
|
|
3510
3680
|
missionEnvelope: [],
|
|
3681
|
+
missionReceipt: [],
|
|
3682
|
+
missionCompletion: [],
|
|
3511
3683
|
rollbackHint: [],
|
|
3512
3684
|
mediaProof: [],
|
|
3513
3685
|
designCompletion: [],
|
|
@@ -3535,6 +3707,8 @@ async function parseProofJson(file, empty) {
|
|
|
3535
3707
|
runtimeBackendEvidence: structuredEvidenceList(data.runtimeBackendEvidence || data.runtime_backend_evidence),
|
|
3536
3708
|
visualProvenance: structuredEvidenceList(data.visualProvenance || data.visual_provenance),
|
|
3537
3709
|
missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
|
|
3710
|
+
missionReceipt: structuredEvidenceList(data.missionReceipt || data.mission_receipt),
|
|
3711
|
+
missionCompletion: structuredEvidenceList(data.missionCompletion || data.mission_completion),
|
|
3538
3712
|
rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
|
|
3539
3713
|
mediaProof: structuredEvidenceList(data.mediaProof || data.media_proof),
|
|
3540
3714
|
designCompletion: structuredEvidenceList(data.designCompletion || data.design_completion),
|
|
@@ -3566,6 +3740,8 @@ async function parseProofMarkdown(file, empty) {
|
|
|
3566
3740
|
runtimeBackendEvidence: readProofList(text, 'Runtime backend evidence'),
|
|
3567
3741
|
visualProvenance: readProofList(text, 'Visual provenance'),
|
|
3568
3742
|
missionEnvelope: readProofList(text, 'Mission patch envelope'),
|
|
3743
|
+
missionReceipt: readProofList(text, 'Mission subagent receipt'),
|
|
3744
|
+
missionCompletion: readProofList(text, 'Mission completion gate'),
|
|
3569
3745
|
rollbackHint: readProofList(text, 'Rollback hint'),
|
|
3570
3746
|
mediaProof: readProofList(text, 'Media proof'),
|
|
3571
3747
|
designCompletion: readProofList(text, 'Design completion'),
|
|
@@ -3651,6 +3827,12 @@ function formatStructuredEvidence(value) {
|
|
|
3651
3827
|
if (data.schema === 'yam.ueye-visual-provenance.v1' || data.source_kind || data.reference_id || data.screenshot_id) {
|
|
3652
3828
|
return JSON.stringify(buildUeyeVisualProvenance(data));
|
|
3653
3829
|
}
|
|
3830
|
+
if (data.schema === 'yam.mission-subagent-receipt.v1' || data.thread_id || data.lifecycle_status || data.outcome) {
|
|
3831
|
+
return JSON.stringify(buildMissionSubagentReceipt(data));
|
|
3832
|
+
}
|
|
3833
|
+
if (data.schema === 'yam.mission-completion-gate.v1' || data.expected_thread_ids || data.ready_to_claim_complete !== undefined) {
|
|
3834
|
+
return JSON.stringify(buildMissionCompletionGate(data));
|
|
3835
|
+
}
|
|
3654
3836
|
if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
|
|
3655
3837
|
return JSON.stringify(buildMissionPatchEnvelope(data));
|
|
3656
3838
|
}
|
|
@@ -3674,7 +3856,7 @@ function formatStructuredEvidence(value) {
|
|
|
3674
3856
|
}
|
|
3675
3857
|
function hasProofEvidence(parsed) {
|
|
3676
3858
|
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);
|
|
3859
|
+
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
3860
|
}
|
|
3679
3861
|
function printProofList(label, values) {
|
|
3680
3862
|
console.log(`- ${label}:`);
|