yam-flow 0.1.0 → 0.1.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/COMMANDS.md +8 -0
- package/README.md +3 -0
- package/ROADMAP.md +110 -0
- package/dist/bin/yam.js +151 -4
- package/dist/lib/trust-kernel.d.ts +38 -0
- package/dist/lib/trust-kernel.js +69 -0
- package/package.json +1 -1
- package/references/mission.md +24 -0
- package/references/tool-trust-layer.md +13 -0
- package/references/ueye-proof.md +19 -0
- package/skills/mission/SKILL.md +10 -6
- package/skills/ueye/SKILL.md +6 -3
- package/src/bin/yam.ts +148 -3
- package/src/lib/trust-kernel.ts +111 -0
- package/templates/mission-plan.md +13 -0
- package/templates/ueye-comparison.md +15 -0
- package/templates/ueye-review.md +15 -0
- package/yam.manifest.json +2 -2
package/COMMANDS.md
CHANGED
|
@@ -135,12 +135,20 @@ yam tools doctor /path/to/project --json
|
|
|
135
135
|
yam proof /path/to/project
|
|
136
136
|
yam proof --route deep --truth verified --command "npm run build: pass"
|
|
137
137
|
yam proof --route ueye --truth verified --visual "reference image only"
|
|
138
|
+
yam proof --route ueye --truth partial --visual-provenance '{"source_kind":"reference","source_hash":"unknown","comparison_result":"not-verified","truth_status":"partial"}'
|
|
139
|
+
yam proof --route mission --mission-envelope '{"agent_id":"implementer","assigned_scope":"target component","changed_files":["src/file.ts"],"verification_hint":"npm run typecheck","truth_status":"partial"}'
|
|
138
140
|
yam proof --route deep --truth proven --require-runtime --runtime "mock server fixture"
|
|
139
141
|
yam proof write /path/to/project --route deep --truth partial --command "npm run build: pass"
|
|
140
142
|
yam proof write /path/to/project --format md --truth assumed --assumption "No runtime was started"
|
|
141
143
|
yam safety "supabase db reset --linked"
|
|
142
144
|
```
|
|
143
145
|
|
|
146
|
+
Release reporting intentionally runs the release checks and returns a machine-readable summary.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
yam release report --json
|
|
150
|
+
```
|
|
151
|
+
|
|
144
152
|
## Lite Hook
|
|
145
153
|
|
|
146
154
|
Advisory-only hook. It does not run checks or force routes.
|
package/README.md
CHANGED
|
@@ -126,6 +126,9 @@ yam tools doctor /path/to/project --json
|
|
|
126
126
|
yam proof /path/to/project
|
|
127
127
|
yam proof write /path/to/project --route quick --truth verified --command "npm run verify:self: pass"
|
|
128
128
|
yam proof --route ueye --truth verified --visual "reference image only"
|
|
129
|
+
yam proof --route ueye --truth partial --visual-provenance '{"source_kind":"reference","source_hash":"unknown","comparison_result":"not-verified","truth_status":"partial"}'
|
|
130
|
+
yam proof --route mission --mission-envelope '{"agent_id":"implementer","assigned_scope":"target component","changed_files":["src/file.ts"],"verification_hint":"npm run typecheck","truth_status":"partial"}'
|
|
131
|
+
yam release report --json
|
|
129
132
|
yam safety "supabase db reset"
|
|
130
133
|
yam detect /path/to/project
|
|
131
134
|
yam pack /path/to/project
|
package/ROADMAP.md
CHANGED
|
@@ -311,3 +311,113 @@ Tasks:
|
|
|
311
311
|
- Add `yam proof write`. Done.
|
|
312
312
|
- Prepare npm/npx package metadata and dry-run packaging. Done.
|
|
313
313
|
- Add small route-scoped trust kernel: truth caps, fake/real policy, runtime truth matrix, and completion proof object. Done.
|
|
314
|
+
|
|
315
|
+
### 16. Deferred Triggers
|
|
316
|
+
|
|
317
|
+
Goal: keep useful future trust-layer ideas visible without turning them into always-on gates too early.
|
|
318
|
+
|
|
319
|
+
Rules:
|
|
320
|
+
|
|
321
|
+
- Do not implement deferred items just because they are listed here.
|
|
322
|
+
- Mention an item again when its trigger appears in real use.
|
|
323
|
+
- Prefer lite metadata before runtime systems.
|
|
324
|
+
- Keep ordinary `$quick`, `$question`, and `$scout` flows free of these gates.
|
|
325
|
+
|
|
326
|
+
#### Mission Patch Queue Lite
|
|
327
|
+
|
|
328
|
+
Status: next candidate for `$mission` only.
|
|
329
|
+
|
|
330
|
+
Trigger:
|
|
331
|
+
|
|
332
|
+
- A mission uses two or more real subagent/lane outputs.
|
|
333
|
+
- Multiple lanes touch nearby files or the same subsystem.
|
|
334
|
+
- It becomes hard to tell who changed what, why, and how to revert it.
|
|
335
|
+
- Mission rollback or apply order starts to matter.
|
|
336
|
+
|
|
337
|
+
First shape:
|
|
338
|
+
|
|
339
|
+
- pending / applied / verified / reverted / blocked state.
|
|
340
|
+
- lane id and assigned scope.
|
|
341
|
+
- changed files.
|
|
342
|
+
- verification hint.
|
|
343
|
+
- rollback hint.
|
|
344
|
+
- truth status.
|
|
345
|
+
|
|
346
|
+
Keep out for now:
|
|
347
|
+
|
|
348
|
+
- automatic merge engine.
|
|
349
|
+
- persistent locks.
|
|
350
|
+
- parallel apply worker.
|
|
351
|
+
- broad runtime orchestration.
|
|
352
|
+
|
|
353
|
+
#### Tool Intent Label / Tool Trust Metadata
|
|
354
|
+
|
|
355
|
+
Status: near-term lightweight metadata.
|
|
356
|
+
|
|
357
|
+
Trigger:
|
|
358
|
+
|
|
359
|
+
- A route uses several tool classes together, such as browser, package manager, GitHub, filesystem, database, or deployment tools.
|
|
360
|
+
- Read-only and write actions need to be distinguished in proof.
|
|
361
|
+
- Destructive actions need a visible route recommendation before execution.
|
|
362
|
+
- Release or mission reports need clearer evidence about what kind of tool ran.
|
|
363
|
+
|
|
364
|
+
First shape:
|
|
365
|
+
|
|
366
|
+
- tool intent: read_only / write / destructive / runtime / visual.
|
|
367
|
+
- parallel safe: true / false / unknown.
|
|
368
|
+
- approval required: true / false / unknown.
|
|
369
|
+
- evidence kind and truth status.
|
|
370
|
+
|
|
371
|
+
Keep out for now:
|
|
372
|
+
|
|
373
|
+
- automatic scheduling.
|
|
374
|
+
- automatic permission mutation.
|
|
375
|
+
- mandatory tool graph.
|
|
376
|
+
|
|
377
|
+
#### MCP Scheduler Runtime
|
|
378
|
+
|
|
379
|
+
Status: later.
|
|
380
|
+
|
|
381
|
+
Trigger:
|
|
382
|
+
|
|
383
|
+
- Tool intent labels are no longer enough.
|
|
384
|
+
- The harness starts coordinating multiple external tools inside one route.
|
|
385
|
+
- Write or destructive actions could run near read-only checks.
|
|
386
|
+
- Parallel tool execution becomes common and needs ordering.
|
|
387
|
+
- A mission needs deterministic tool sequence proof.
|
|
388
|
+
|
|
389
|
+
First shape:
|
|
390
|
+
|
|
391
|
+
- scheduler proof object.
|
|
392
|
+
- read-only overlap allowed.
|
|
393
|
+
- write/destructive serialization.
|
|
394
|
+
- blocked/approved/skipped status.
|
|
395
|
+
|
|
396
|
+
Keep out for now:
|
|
397
|
+
|
|
398
|
+
- default scheduler for normal work.
|
|
399
|
+
- route-wide mandatory scheduler.
|
|
400
|
+
- background runtime service.
|
|
401
|
+
|
|
402
|
+
#### Appshots Attachment Gate
|
|
403
|
+
|
|
404
|
+
Status: deferred.
|
|
405
|
+
|
|
406
|
+
Trigger:
|
|
407
|
+
|
|
408
|
+
- Ueye visual provenance cannot answer which exact visual source was used.
|
|
409
|
+
- A team workflow needs thread-level or attachment-level audit trails.
|
|
410
|
+
- Reference and implementation screenshots are easy to confuse.
|
|
411
|
+
- External review requires source image lineage beyond path/hash/id.
|
|
412
|
+
|
|
413
|
+
First shape:
|
|
414
|
+
|
|
415
|
+
- optional attachment source fields.
|
|
416
|
+
- no mandatory gate.
|
|
417
|
+
- preserve Ueye visual provenance as the default.
|
|
418
|
+
|
|
419
|
+
Keep out for now:
|
|
420
|
+
|
|
421
|
+
- required thread attachment ids.
|
|
422
|
+
- app-specific visual gates.
|
|
423
|
+
- blocking ordinary Ueye work when attachment metadata is absent.
|
package/dist/bin/yam.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import fsp from 'node:fs/promises';
|
|
4
5
|
import os from 'node:os';
|
|
5
6
|
import path from 'node:path';
|
|
6
7
|
import process from 'node:process';
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import { TRUTH_STATUSES, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
|
|
9
|
+
import { TRUTH_STATUSES, buildMissionPatchEnvelope, buildRollbackHint, buildUeyeVisualProvenance, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
|
|
9
10
|
function errorMessage(error) {
|
|
10
11
|
return error instanceof Error ? error.message : String(error);
|
|
11
12
|
}
|
|
@@ -117,6 +118,7 @@ Usage:
|
|
|
117
118
|
yam tools doctor [dir]
|
|
118
119
|
yam proof [dir|--from file] [--route route] [--truth status] [--command text] [--evidence text]
|
|
119
120
|
yam proof write [dir] [--format json|md] [--out file] [--route route] [--truth status] [--command text]
|
|
121
|
+
yam release report [--json]
|
|
120
122
|
yam safety [text...]
|
|
121
123
|
yam memory <init|add|list|summary|resolve> [dir] [options]
|
|
122
124
|
yam hook <status|enable|disable|run> [lite] [--global|--project dir]
|
|
@@ -927,6 +929,86 @@ async function safety(args = []) {
|
|
|
927
929
|
const result = detectDbSafetyText(text);
|
|
928
930
|
printSafetyResult(result);
|
|
929
931
|
}
|
|
932
|
+
async function release(args = []) {
|
|
933
|
+
const subcommand = args[0] || 'help';
|
|
934
|
+
if (subcommand !== 'report') {
|
|
935
|
+
console.error('usage: yam release report [--json]');
|
|
936
|
+
process.exitCode = 1;
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
const asJson = args.includes('--json');
|
|
940
|
+
const report = runReleaseReport();
|
|
941
|
+
if (asJson) {
|
|
942
|
+
console.log(JSON.stringify(report, null, 2));
|
|
943
|
+
if (!report.ok)
|
|
944
|
+
process.exitCode = 1;
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
console.log('yam release report');
|
|
948
|
+
console.log(`- Package: ${report.packageName}@${report.version}`);
|
|
949
|
+
console.log(`- Truth status: ${report.truth_status}`);
|
|
950
|
+
for (const check of report.checks) {
|
|
951
|
+
console.log(`- ${check.id}: ${check.status} (${check.duration_ms}ms)`);
|
|
952
|
+
if (check.note)
|
|
953
|
+
console.log(` ${check.note}`);
|
|
954
|
+
}
|
|
955
|
+
if (report.failed.length) {
|
|
956
|
+
console.log('- Failed:');
|
|
957
|
+
for (const id of report.failed)
|
|
958
|
+
console.log(` - ${id}`);
|
|
959
|
+
}
|
|
960
|
+
if (!report.ok)
|
|
961
|
+
process.exitCode = 1;
|
|
962
|
+
}
|
|
963
|
+
function runReleaseReport() {
|
|
964
|
+
const startedAt = new Date().toISOString();
|
|
965
|
+
const checks = [
|
|
966
|
+
['typecheck', ['npm', ['run', 'typecheck']]],
|
|
967
|
+
['forbidden_names', ['npm', ['run', 'forbidden-names:check']]],
|
|
968
|
+
['package_boundary', ['npm', ['run', 'package-boundary:check']]],
|
|
969
|
+
['registry_status', ['npm', ['run', 'registry:check']]],
|
|
970
|
+
['cli_smoke', ['npm', ['run', 'cli-smoke']]],
|
|
971
|
+
['dist_freshness', ['npm', ['run', 'dist:freshness']]]
|
|
972
|
+
].map(([id, command]) => runReleaseCheck(id, command));
|
|
973
|
+
const failed = checks.filter((check) => check.status !== 'passed').map((check) => check.id);
|
|
974
|
+
return {
|
|
975
|
+
schema: 'yam.release-report.v1',
|
|
976
|
+
generated_at: startedAt,
|
|
977
|
+
packageName: PACKAGE_JSON.name,
|
|
978
|
+
version: VERSION,
|
|
979
|
+
ok: failed.length === 0,
|
|
980
|
+
truth_status: failed.length === 0 ? 'verified' : 'blocked',
|
|
981
|
+
checks,
|
|
982
|
+
failed
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
function runReleaseCheck(id, commandSpec) {
|
|
986
|
+
const [command, args] = commandSpec;
|
|
987
|
+
const start = Date.now();
|
|
988
|
+
const result = spawnSync(command, args, {
|
|
989
|
+
cwd: ROOT,
|
|
990
|
+
encoding: 'utf8',
|
|
991
|
+
env: { ...process.env, npm_config_cache: path.join(os.tmpdir(), 'yam-npm-cache') },
|
|
992
|
+
timeout: 120000
|
|
993
|
+
});
|
|
994
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
995
|
+
const timedOut = result.error?.message?.includes('ETIMEDOUT') || result.signal === 'SIGTERM';
|
|
996
|
+
return {
|
|
997
|
+
id,
|
|
998
|
+
command: [command, ...args].join(' '),
|
|
999
|
+
status: result.status === 0 ? 'passed' : timedOut ? 'blocked' : 'failed',
|
|
1000
|
+
exit_code: typeof result.status === 'number' ? result.status : null,
|
|
1001
|
+
duration_ms: Date.now() - start,
|
|
1002
|
+
note: summarizeCheckOutput(output || result.error?.message || '')
|
|
1003
|
+
};
|
|
1004
|
+
}
|
|
1005
|
+
function summarizeCheckOutput(output = '') {
|
|
1006
|
+
const lines = String(output || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1007
|
+
if (!lines.length)
|
|
1008
|
+
return '';
|
|
1009
|
+
const highSignal = lines.filter((line) => !line.startsWith('>')).slice(-4);
|
|
1010
|
+
return highSignal.join(' | ').slice(0, 600);
|
|
1011
|
+
}
|
|
930
1012
|
async function readStdinTextIfAvailable() {
|
|
931
1013
|
if (process.stdin.isTTY)
|
|
932
1014
|
return '';
|
|
@@ -975,6 +1057,9 @@ async function proof(args = []) {
|
|
|
975
1057
|
printProofList('Evidence', summary.evidence);
|
|
976
1058
|
printProofList('Visual evidence', summary.visual);
|
|
977
1059
|
printProofList('Runtime evidence', summary.runtime);
|
|
1060
|
+
printProofList('Visual provenance', summary.visualProvenance || []);
|
|
1061
|
+
printProofList('Mission patch envelope', summary.missionEnvelope || []);
|
|
1062
|
+
printProofList('Rollback hint', summary.rollbackHint || []);
|
|
978
1063
|
printProofList('Changed surfaces', summary.changed);
|
|
979
1064
|
printProofList('Skipped', summary.skipped);
|
|
980
1065
|
printProofList('Blocked', summary.blocked);
|
|
@@ -1050,6 +1135,18 @@ function buildProofSummary(parsed, artifact) {
|
|
|
1050
1135
|
evidence: [...artifact.evidence, ...arrayFlag(parsed.flags.evidence)],
|
|
1051
1136
|
visual: [...artifact.visual, ...arrayFlag(parsed.flags.visual)],
|
|
1052
1137
|
runtime: [...artifact.runtime, ...arrayFlag(parsed.flags.runtime)],
|
|
1138
|
+
visualProvenance: [
|
|
1139
|
+
...structuredEvidenceList(artifact.visualProvenance),
|
|
1140
|
+
...structuredEvidenceList(parsed.flags.visual_provenance)
|
|
1141
|
+
],
|
|
1142
|
+
missionEnvelope: [
|
|
1143
|
+
...structuredEvidenceList(artifact.missionEnvelope),
|
|
1144
|
+
...structuredEvidenceList(parsed.flags.mission_envelope)
|
|
1145
|
+
],
|
|
1146
|
+
rollbackHint: [
|
|
1147
|
+
...structuredEvidenceList(artifact.rollbackHint),
|
|
1148
|
+
...structuredEvidenceList(parsed.flags.rollback_hint)
|
|
1149
|
+
],
|
|
1053
1150
|
cleanup: parsed.flags.cleanup || artifact.cleanup || '',
|
|
1054
1151
|
changed: [...artifact.changed, ...arrayFlag(parsed.flags.changed)],
|
|
1055
1152
|
skipped: [...artifact.skipped, ...arrayFlag(parsed.flags.skipped)],
|
|
@@ -1099,6 +1196,15 @@ function renderProofMarkdown(summary) {
|
|
|
1099
1196
|
'## Runtime evidence',
|
|
1100
1197
|
...renderProofMarkdownList(summary.runtime),
|
|
1101
1198
|
'',
|
|
1199
|
+
'## Visual provenance',
|
|
1200
|
+
...renderProofMarkdownList(summary.visualProvenance || []),
|
|
1201
|
+
'',
|
|
1202
|
+
'## Mission patch envelope',
|
|
1203
|
+
...renderProofMarkdownList(summary.missionEnvelope || []),
|
|
1204
|
+
'',
|
|
1205
|
+
'## Rollback hint',
|
|
1206
|
+
...renderProofMarkdownList(summary.rollbackHint || []),
|
|
1207
|
+
'',
|
|
1102
1208
|
'## Changed surfaces',
|
|
1103
1209
|
...renderProofMarkdownList(summary.changed),
|
|
1104
1210
|
'',
|
|
@@ -1129,7 +1235,7 @@ function renderProofMarkdownList(values) {
|
|
|
1129
1235
|
function parseProofArgs(args = []) {
|
|
1130
1236
|
const flags = {};
|
|
1131
1237
|
const positionals = [];
|
|
1132
|
-
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
1238
|
+
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
1133
1239
|
for (let index = 0; index < args.length; index += 1) {
|
|
1134
1240
|
const arg = args[index];
|
|
1135
1241
|
if (arg.startsWith('--')) {
|
|
@@ -1145,7 +1251,7 @@ function parseProofArgs(args = []) {
|
|
|
1145
1251
|
const value = inlineValue ?? args[index + 1] ?? '';
|
|
1146
1252
|
if (inlineValue === undefined)
|
|
1147
1253
|
index += 1;
|
|
1148
|
-
if (['command', 'evidence', 'visual', 'runtime', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
1254
|
+
if (['command', 'evidence', 'visual', 'runtime', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
1149
1255
|
flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
|
|
1150
1256
|
}
|
|
1151
1257
|
else {
|
|
@@ -1184,6 +1290,9 @@ function emptyProofArtifact() {
|
|
|
1184
1290
|
evidence: [],
|
|
1185
1291
|
visual: [],
|
|
1186
1292
|
runtime: [],
|
|
1293
|
+
visualProvenance: [],
|
|
1294
|
+
missionEnvelope: [],
|
|
1295
|
+
rollbackHint: [],
|
|
1187
1296
|
cleanup: '',
|
|
1188
1297
|
changed: [],
|
|
1189
1298
|
skipped: [],
|
|
@@ -1205,6 +1314,9 @@ async function parseProofJson(file, empty) {
|
|
|
1205
1314
|
evidence: arrayFlag(data.evidence),
|
|
1206
1315
|
visual: arrayFlag(data.visual || data.visualEvidence),
|
|
1207
1316
|
runtime: arrayFlag(data.runtime || data.runtimeEvidence),
|
|
1317
|
+
visualProvenance: structuredEvidenceList(data.visualProvenance || data.visual_provenance),
|
|
1318
|
+
missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
|
|
1319
|
+
rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
|
|
1208
1320
|
cleanup: String(data.cleanup || data.cleanupStatus || ''),
|
|
1209
1321
|
changed: arrayFlag(data.changed || data.files),
|
|
1210
1322
|
skipped: arrayFlag(data.skipped),
|
|
@@ -1230,6 +1342,9 @@ async function parseProofMarkdown(file, empty) {
|
|
|
1230
1342
|
evidence: readProofList(text, 'Evidence'),
|
|
1231
1343
|
visual: readProofList(text, 'Visual evidence'),
|
|
1232
1344
|
runtime: firstProofList(text, ['Runtime evidence', 'Processes', 'tmux']),
|
|
1345
|
+
visualProvenance: readProofList(text, 'Visual provenance'),
|
|
1346
|
+
missionEnvelope: readProofList(text, 'Mission patch envelope'),
|
|
1347
|
+
rollbackHint: readProofList(text, 'Rollback hint'),
|
|
1233
1348
|
cleanup: readProofField(text, 'Cleanup') || readProofField(text, 'Cleanup status'),
|
|
1234
1349
|
changed: firstProofList(text, ['Changed surfaces', 'Files']),
|
|
1235
1350
|
skipped: readProofList(text, 'Skipped'),
|
|
@@ -1274,9 +1389,39 @@ function arrayFlag(value) {
|
|
|
1274
1389
|
return [];
|
|
1275
1390
|
return Array.isArray(value) ? value.filter(Boolean) : [value].filter(Boolean);
|
|
1276
1391
|
}
|
|
1392
|
+
function structuredEvidenceList(value) {
|
|
1393
|
+
return arrayFlag(value).map((item) => formatStructuredEvidence(item)).filter(Boolean);
|
|
1394
|
+
}
|
|
1395
|
+
function formatStructuredEvidence(value) {
|
|
1396
|
+
if (!value)
|
|
1397
|
+
return '';
|
|
1398
|
+
if (typeof value === 'object')
|
|
1399
|
+
return JSON.stringify(value);
|
|
1400
|
+
const text = String(value).trim();
|
|
1401
|
+
if (!text)
|
|
1402
|
+
return '';
|
|
1403
|
+
if (!text.startsWith('{'))
|
|
1404
|
+
return text;
|
|
1405
|
+
try {
|
|
1406
|
+
const data = JSON.parse(text);
|
|
1407
|
+
if (data.schema === 'yam.ueye-visual-provenance.v1' || data.source_kind || data.reference_id || data.screenshot_id) {
|
|
1408
|
+
return JSON.stringify(buildUeyeVisualProvenance(data));
|
|
1409
|
+
}
|
|
1410
|
+
if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
|
|
1411
|
+
return JSON.stringify(buildMissionPatchEnvelope(data));
|
|
1412
|
+
}
|
|
1413
|
+
if (data.schema === 'yam.rollback-hint.v1' || data.touched_files || data.safe_revert_note) {
|
|
1414
|
+
return JSON.stringify(buildRollbackHint(data));
|
|
1415
|
+
}
|
|
1416
|
+
return JSON.stringify(data);
|
|
1417
|
+
}
|
|
1418
|
+
catch {
|
|
1419
|
+
return text;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1277
1422
|
function hasProofEvidence(parsed) {
|
|
1278
1423
|
const flags = parsed.flags || {};
|
|
1279
|
-
return ['command', 'evidence', 'visual', 'runtime', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup);
|
|
1424
|
+
return ['command', 'evidence', 'visual', 'runtime', 'visual_provenance', 'mission_envelope', 'rollback_hint', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup);
|
|
1280
1425
|
}
|
|
1281
1426
|
function printProofList(label, values) {
|
|
1282
1427
|
console.log(`- ${label}:`);
|
|
@@ -1967,6 +2112,8 @@ async function main() {
|
|
|
1967
2112
|
return tools(process.argv.slice(3));
|
|
1968
2113
|
if (command === 'proof')
|
|
1969
2114
|
return proof(process.argv.slice(3));
|
|
2115
|
+
if (command === 'release')
|
|
2116
|
+
return release(process.argv.slice(3));
|
|
1970
2117
|
if (command === 'safety')
|
|
1971
2118
|
return safety(process.argv.slice(3));
|
|
1972
2119
|
if (command === 'memory')
|
|
@@ -29,6 +29,35 @@ export interface RuntimeTruthMatrix {
|
|
|
29
29
|
rows: RuntimeTruthRow[];
|
|
30
30
|
blockers: string[];
|
|
31
31
|
}
|
|
32
|
+
export interface UeyeVisualProvenance {
|
|
33
|
+
schema: 'yam.ueye-visual-provenance.v1';
|
|
34
|
+
source_kind: string;
|
|
35
|
+
source_path: string;
|
|
36
|
+
source_hash: string;
|
|
37
|
+
reference_id: string;
|
|
38
|
+
screenshot_id: string;
|
|
39
|
+
local_only: boolean;
|
|
40
|
+
redacted: boolean;
|
|
41
|
+
operator_provided: boolean;
|
|
42
|
+
comparison_result: string;
|
|
43
|
+
truth_status: TruthStatus;
|
|
44
|
+
}
|
|
45
|
+
export interface RollbackHint {
|
|
46
|
+
schema: 'yam.rollback-hint.v1';
|
|
47
|
+
touched_files: string[];
|
|
48
|
+
generated_files: string[];
|
|
49
|
+
before_check: string;
|
|
50
|
+
safe_revert_note: string;
|
|
51
|
+
}
|
|
52
|
+
export interface MissionPatchEnvelope {
|
|
53
|
+
schema: 'yam.mission-patch-envelope.v1';
|
|
54
|
+
agent_id: string;
|
|
55
|
+
assigned_scope: string;
|
|
56
|
+
changed_files: string[];
|
|
57
|
+
verification_hint: string;
|
|
58
|
+
rollback_hint: RollbackHint;
|
|
59
|
+
truth_status: TruthStatus;
|
|
60
|
+
}
|
|
32
61
|
export interface FakeRealPolicyResult {
|
|
33
62
|
schema: 'yam.fake-real-policy.v1';
|
|
34
63
|
ok: boolean;
|
|
@@ -52,6 +81,9 @@ export interface ProofSummary {
|
|
|
52
81
|
blocked?: unknown;
|
|
53
82
|
assumptions?: unknown;
|
|
54
83
|
unverified?: unknown;
|
|
84
|
+
visualProvenance?: unknown;
|
|
85
|
+
missionEnvelope?: unknown;
|
|
86
|
+
rollbackHint?: unknown;
|
|
55
87
|
}
|
|
56
88
|
export interface ProofOptions {
|
|
57
89
|
requireRealRuntime?: boolean;
|
|
@@ -79,6 +111,9 @@ export interface YamCompletionProof {
|
|
|
79
111
|
evidence: string[];
|
|
80
112
|
visual: string[];
|
|
81
113
|
runtime: string[];
|
|
114
|
+
visualProvenance: string[];
|
|
115
|
+
missionEnvelope: string[];
|
|
116
|
+
rollbackHint: string[];
|
|
82
117
|
cleanup: unknown;
|
|
83
118
|
changed: string[];
|
|
84
119
|
skipped: string[];
|
|
@@ -90,6 +125,9 @@ export interface YamCompletionProof {
|
|
|
90
125
|
fakeReal: FakeRealPolicyResult;
|
|
91
126
|
runtimeTruth: RuntimeTruthMatrix;
|
|
92
127
|
}
|
|
128
|
+
export declare function buildUeyeVisualProvenance(input?: Partial<UeyeVisualProvenance>): UeyeVisualProvenance;
|
|
129
|
+
export declare function buildRollbackHint(input?: Partial<RollbackHint>): RollbackHint;
|
|
130
|
+
export declare function buildMissionPatchEnvelope(input?: Partial<MissionPatchEnvelope>): MissionPatchEnvelope;
|
|
93
131
|
export declare function isTruthStatus(value?: unknown): value is TruthStatus;
|
|
94
132
|
export declare function weakestTruth(...statuses: unknown[]): TruthStatus;
|
|
95
133
|
export declare function detectDbSafetyText(text?: unknown): DbSafetyScanResult;
|
package/dist/lib/trust-kernel.js
CHANGED
|
@@ -10,6 +10,45 @@ export const TRUTH_STATUSES = Object.freeze([
|
|
|
10
10
|
'blocked',
|
|
11
11
|
'assumed'
|
|
12
12
|
]);
|
|
13
|
+
export function buildUeyeVisualProvenance(input = {}) {
|
|
14
|
+
const truth = isTruthStatus(input.truth_status) ? input.truth_status : 'partial';
|
|
15
|
+
return {
|
|
16
|
+
schema: 'yam.ueye-visual-provenance.v1',
|
|
17
|
+
source_kind: String(input.source_kind || ''),
|
|
18
|
+
source_path: String(input.source_path || ''),
|
|
19
|
+
source_hash: String(input.source_hash || 'unknown'),
|
|
20
|
+
reference_id: String(input.reference_id || ''),
|
|
21
|
+
screenshot_id: String(input.screenshot_id || ''),
|
|
22
|
+
local_only: Boolean(input.local_only),
|
|
23
|
+
redacted: Boolean(input.redacted),
|
|
24
|
+
operator_provided: Boolean(input.operator_provided),
|
|
25
|
+
comparison_result: String(input.comparison_result || 'not-verified'),
|
|
26
|
+
truth_status: truth
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function buildRollbackHint(input = {}) {
|
|
30
|
+
return {
|
|
31
|
+
schema: 'yam.rollback-hint.v1',
|
|
32
|
+
touched_files: asList(input.touched_files),
|
|
33
|
+
generated_files: asList(input.generated_files),
|
|
34
|
+
before_check: String(input.before_check || ''),
|
|
35
|
+
safe_revert_note: String(input.safe_revert_note || '')
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
export function buildMissionPatchEnvelope(input = {}) {
|
|
39
|
+
const rollback = input.rollback_hint && typeof input.rollback_hint === 'object'
|
|
40
|
+
? buildRollbackHint(input.rollback_hint)
|
|
41
|
+
: buildRollbackHint();
|
|
42
|
+
return {
|
|
43
|
+
schema: 'yam.mission-patch-envelope.v1',
|
|
44
|
+
agent_id: String(input.agent_id || ''),
|
|
45
|
+
assigned_scope: String(input.assigned_scope || ''),
|
|
46
|
+
changed_files: asList(input.changed_files),
|
|
47
|
+
verification_hint: String(input.verification_hint || ''),
|
|
48
|
+
rollback_hint: rollback,
|
|
49
|
+
truth_status: isTruthStatus(input.truth_status) ? input.truth_status : 'partial'
|
|
50
|
+
};
|
|
51
|
+
}
|
|
13
52
|
const TRUTH_RANK = Object.freeze({
|
|
14
53
|
blocked: 0,
|
|
15
54
|
real_required_missing: 0,
|
|
@@ -81,6 +120,15 @@ export function classifyEvidenceTruth(summary = {}, options = {}) {
|
|
|
81
120
|
for (const visual of asList(summary.visual)) {
|
|
82
121
|
rows.push(evidenceRow('visual', visual, classifyVisualEvidence(visual)));
|
|
83
122
|
}
|
|
123
|
+
for (const provenance of asList(summary.visualProvenance)) {
|
|
124
|
+
rows.push(evidenceRow('visual', provenance, metadataTruth(provenance, 'partial')));
|
|
125
|
+
}
|
|
126
|
+
for (const envelope of asList(summary.missionEnvelope)) {
|
|
127
|
+
rows.push(evidenceRow('evidence', envelope, metadataTruth(envelope, 'partial')));
|
|
128
|
+
}
|
|
129
|
+
for (const rollback of asList(summary.rollbackHint)) {
|
|
130
|
+
rows.push(evidenceRow('evidence', rollback, metadataTruth(rollback, 'partial')));
|
|
131
|
+
}
|
|
84
132
|
for (const runtime of asList(summary.runtime)) {
|
|
85
133
|
rows.push(evidenceRow('runtime', runtime, classifyRuntimeEvidence(runtime, options)));
|
|
86
134
|
}
|
|
@@ -166,6 +214,9 @@ export function buildYamCompletionProof(summary = {}, options = {}) {
|
|
|
166
214
|
evidence: asList(summary.evidence),
|
|
167
215
|
visual: asList(summary.visual),
|
|
168
216
|
runtime: asList(summary.runtime),
|
|
217
|
+
visualProvenance: asList(summary.visualProvenance),
|
|
218
|
+
missionEnvelope: asList(summary.missionEnvelope),
|
|
219
|
+
rollbackHint: asList(summary.rollbackHint),
|
|
169
220
|
cleanup: summary.cleanup || '',
|
|
170
221
|
changed: asList(summary.changed),
|
|
171
222
|
skipped: asList(summary.skipped),
|
|
@@ -223,6 +274,24 @@ function classifyCleanupEvidence(value = '') {
|
|
|
223
274
|
function commandPassed(value = '') {
|
|
224
275
|
return /pass|passed|ok|success|exit\s*0|green/i.test(String(value || '')) && !/fail|failed|error|blocked/i.test(String(value || ''));
|
|
225
276
|
}
|
|
277
|
+
function metadataTruth(value = '', fallback = 'partial') {
|
|
278
|
+
return weakestTruth(readStructuredTruth(value, fallback), 'partial');
|
|
279
|
+
}
|
|
280
|
+
function readStructuredTruth(value = '', fallback = 'partial') {
|
|
281
|
+
if (typeof value === 'object' && value) {
|
|
282
|
+
const candidate = value.truth_status || value.truth;
|
|
283
|
+
return isTruthStatus(candidate) ? candidate : fallback;
|
|
284
|
+
}
|
|
285
|
+
const text = String(value || '');
|
|
286
|
+
try {
|
|
287
|
+
const data = JSON.parse(text);
|
|
288
|
+
const candidate = data.truth_status || data.truth;
|
|
289
|
+
return isTruthStatus(candidate) ? candidate : fallback;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return fallback;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
226
295
|
function strongestSupportedTruth(rows = []) {
|
|
227
296
|
if (rows.some((row) => row.truth === 'blocked'))
|
|
228
297
|
return 'blocked';
|
package/package.json
CHANGED
package/references/mission.md
CHANGED
|
@@ -36,6 +36,30 @@ Subagent policy:
|
|
|
36
36
|
- If the user explicitly insists on mission despite missing subagents, mark the result `partial` or `blocked`; do not treat self-review as team execution.
|
|
37
37
|
- The final proof must record the subagent decision: `used`, `downgraded_to_deep`, `unavailable_partial`, or `blocked`, with the reason.
|
|
38
38
|
|
|
39
|
+
Patch envelope:
|
|
40
|
+
|
|
41
|
+
Use a small envelope when a real mission lane changes code:
|
|
42
|
+
|
|
43
|
+
- `agent_id`: who or which lane made the change.
|
|
44
|
+
- `assigned_scope`: the bounded work area.
|
|
45
|
+
- `changed_files`: files the lane touched.
|
|
46
|
+
- `verification_hint`: the smallest relevant check for the lane.
|
|
47
|
+
- `rollback_hint`: what to inspect or revert if the change is wrong.
|
|
48
|
+
- `truth_status`: the lane's honest status.
|
|
49
|
+
|
|
50
|
+
This is only a record shape. It is not a queue, lock manager, or parallel apply engine.
|
|
51
|
+
|
|
52
|
+
Rollback hint:
|
|
53
|
+
|
|
54
|
+
Record:
|
|
55
|
+
|
|
56
|
+
- touched files
|
|
57
|
+
- generated files
|
|
58
|
+
- before check
|
|
59
|
+
- safe revert note
|
|
60
|
+
|
|
61
|
+
Do not claim rollback safety unless the relevant check or revert path was actually inspected.
|
|
62
|
+
|
|
39
63
|
Runtime use:
|
|
40
64
|
|
|
41
65
|
- Mission may use deep runtime verification when needed, but runtime proof alone does not make a mission if subagents were not used.
|
|
@@ -110,6 +110,19 @@ On demand:
|
|
|
110
110
|
|
|
111
111
|
- command evidence
|
|
112
112
|
- browser/screenshot evidence
|
|
113
|
+
- visual provenance for reference-based UI work
|
|
114
|
+
- mission patch envelopes for real team lanes
|
|
115
|
+
- rollback hints for risky changes
|
|
113
116
|
- runtime/tmux/process cleanup evidence
|
|
114
117
|
- truth status
|
|
115
118
|
- skipped/blocked/assumed items
|
|
119
|
+
|
|
120
|
+
`yam release report --json` should collect release readiness into a machine-readable artifact:
|
|
121
|
+
|
|
122
|
+
- typecheck
|
|
123
|
+
- forbidden names
|
|
124
|
+
- package boundary
|
|
125
|
+
- registry status
|
|
126
|
+
- CLI smoke
|
|
127
|
+
- dist freshness
|
|
128
|
+
- final truth status
|
package/references/ueye-proof.md
CHANGED
|
@@ -41,6 +41,25 @@ Bounds:
|
|
|
41
41
|
- Record missing dimensions or hashes as `unknown`, not as failure.
|
|
42
42
|
- Use generated annotations only as derivative aids.
|
|
43
43
|
|
|
44
|
+
### Visual Provenance
|
|
45
|
+
|
|
46
|
+
Purpose: prove the visual source path from reference reading to implementation recheck.
|
|
47
|
+
|
|
48
|
+
Record these fields when a reference or screenshot materially affects the claim:
|
|
49
|
+
|
|
50
|
+
- `source_kind`
|
|
51
|
+
- `source_path`
|
|
52
|
+
- `source_hash`
|
|
53
|
+
- `reference_id`
|
|
54
|
+
- `screenshot_id`
|
|
55
|
+
- `local_only`
|
|
56
|
+
- `redacted`
|
|
57
|
+
- `operator_provided`
|
|
58
|
+
- `comparison_result`
|
|
59
|
+
- `truth_status`
|
|
60
|
+
|
|
61
|
+
Use `unknown` for missing hashes and `not-verified` for comparisons that were not actually rechecked. Do not upgrade a visual claim only because a reference was supplied.
|
|
62
|
+
|
|
44
63
|
### Reference Read Proof
|
|
45
64
|
|
|
46
65
|
Purpose: separate reading the reference from judging the implementation.
|
package/skills/mission/SKILL.md
CHANGED
|
@@ -49,12 +49,14 @@ Do not use for:
|
|
|
49
49
|
- UX/browser verifier: checks screen behavior when relevant.
|
|
50
50
|
- Doctor/scanner: checks direction fit, scope control, verification, cleanup, stale context, and false-completion risk.
|
|
51
51
|
5. Execute the implementation in bounded steps.
|
|
52
|
-
6.
|
|
53
|
-
7.
|
|
54
|
-
8.
|
|
55
|
-
9.
|
|
56
|
-
10.
|
|
57
|
-
11.
|
|
52
|
+
6. Record a compact patch envelope for each real lane that changes code.
|
|
53
|
+
7. Add rollback hints for touched files, generated files, before checks, and safe revert notes.
|
|
54
|
+
8. Use `$deep`-style runtime verification when the mission needs dev server, tmux, test watcher, browser QA, cleanup, or before/after evidence.
|
|
55
|
+
9. Cross-check findings and resolve contradictions.
|
|
56
|
+
10. Run the smallest honest final verification set.
|
|
57
|
+
11. Run doctor scan with `references/doctor-scan.md`.
|
|
58
|
+
12. Confirm cleanup or explicitly report intentionally running processes.
|
|
59
|
+
13. Produce final proof summary, truth status, remaining tasks, and fix-first items.
|
|
58
60
|
|
|
59
61
|
## Proof Summary
|
|
60
62
|
|
|
@@ -64,6 +66,8 @@ Include:
|
|
|
64
66
|
- Role work completed.
|
|
65
67
|
- Subagent decision: used / downgraded_to_deep / unavailable_partial / blocked, with reason.
|
|
66
68
|
- Files or surfaces changed.
|
|
69
|
+
- Patch envelope for each real code-changing lane.
|
|
70
|
+
- Rollback hint for risky or multi-file changes.
|
|
67
71
|
- Runtime/tmux/browser evidence when used.
|
|
68
72
|
- Cross-verification result.
|
|
69
73
|
- Doctor/scanner result.
|
package/skills/ueye/SKILL.md
CHANGED
|
@@ -77,13 +77,16 @@ Use these artifacts for reference-led implementation or serious visual review. K
|
|
|
77
77
|
- reference, before, and after evidence when available.
|
|
78
78
|
- path or URL, source type, state, viewport, and visual verification cap.
|
|
79
79
|
- sha256 and dimensions when an image file is locally available.
|
|
80
|
-
2.
|
|
80
|
+
2. Visual Provenance:
|
|
81
|
+
- source kind, path, hash, reference id, screenshot id, redaction/locality flags, operator-provided flag, comparison result, and truth status.
|
|
82
|
+
- use this to prove the route from reference reading to implementation recheck.
|
|
83
|
+
3. Reference Read Proof:
|
|
81
84
|
- concrete observations from the reference before implementation.
|
|
82
85
|
- no implementation claim should depend on an unrecorded reference observation.
|
|
83
|
-
|
|
86
|
+
4. Reference vs Implementation Matrix:
|
|
84
87
|
- compare the implemented result against the reference by aspect.
|
|
85
88
|
- record matched, similar, different, not-applicable, or not-verified.
|
|
86
|
-
|
|
89
|
+
5. Design Quality Review:
|
|
87
90
|
- judge whether the result is good UI/UX/design after reference comparison.
|
|
88
91
|
- use pass, needs-polish, or fails for each relevant design dimension.
|
|
89
92
|
|
package/src/bin/yam.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
2
3
|
import fs from 'node:fs';
|
|
3
4
|
import fsp from 'node:fs/promises';
|
|
4
5
|
import os from 'node:os';
|
|
@@ -7,6 +8,9 @@ import process from 'node:process';
|
|
|
7
8
|
import { fileURLToPath } from 'node:url';
|
|
8
9
|
import {
|
|
9
10
|
TRUTH_STATUSES,
|
|
11
|
+
buildMissionPatchEnvelope,
|
|
12
|
+
buildRollbackHint,
|
|
13
|
+
buildUeyeVisualProvenance,
|
|
10
14
|
buildYamCompletionProof,
|
|
11
15
|
detectDbSafetyText as detectTrustDbSafetyText,
|
|
12
16
|
isTruthStatus
|
|
@@ -128,6 +132,7 @@ Usage:
|
|
|
128
132
|
yam tools doctor [dir]
|
|
129
133
|
yam proof [dir|--from file] [--route route] [--truth status] [--command text] [--evidence text]
|
|
130
134
|
yam proof write [dir] [--format json|md] [--out file] [--route route] [--truth status] [--command text]
|
|
135
|
+
yam release report [--json]
|
|
131
136
|
yam safety [text...]
|
|
132
137
|
yam memory <init|add|list|summary|resolve> [dir] [options]
|
|
133
138
|
yam hook <status|enable|disable|run> [lite] [--global|--project dir]
|
|
@@ -919,6 +924,85 @@ async function safety(args = []) {
|
|
|
919
924
|
printSafetyResult(result);
|
|
920
925
|
}
|
|
921
926
|
|
|
927
|
+
async function release(args = []) {
|
|
928
|
+
const subcommand = args[0] || 'help';
|
|
929
|
+
if (subcommand !== 'report') {
|
|
930
|
+
console.error('usage: yam release report [--json]');
|
|
931
|
+
process.exitCode = 1;
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
934
|
+
const asJson = args.includes('--json');
|
|
935
|
+
const report = runReleaseReport();
|
|
936
|
+
if (asJson) {
|
|
937
|
+
console.log(JSON.stringify(report, null, 2));
|
|
938
|
+
if (!report.ok) process.exitCode = 1;
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
console.log('yam release report');
|
|
942
|
+
console.log(`- Package: ${report.packageName}@${report.version}`);
|
|
943
|
+
console.log(`- Truth status: ${report.truth_status}`);
|
|
944
|
+
for (const check of report.checks) {
|
|
945
|
+
console.log(`- ${check.id}: ${check.status} (${check.duration_ms}ms)`);
|
|
946
|
+
if (check.note) console.log(` ${check.note}`);
|
|
947
|
+
}
|
|
948
|
+
if (report.failed.length) {
|
|
949
|
+
console.log('- Failed:');
|
|
950
|
+
for (const id of report.failed) console.log(` - ${id}`);
|
|
951
|
+
}
|
|
952
|
+
if (!report.ok) process.exitCode = 1;
|
|
953
|
+
}
|
|
954
|
+
|
|
955
|
+
function runReleaseReport() {
|
|
956
|
+
const startedAt = new Date().toISOString();
|
|
957
|
+
const checks = [
|
|
958
|
+
['typecheck', ['npm', ['run', 'typecheck']]],
|
|
959
|
+
['forbidden_names', ['npm', ['run', 'forbidden-names:check']]],
|
|
960
|
+
['package_boundary', ['npm', ['run', 'package-boundary:check']]],
|
|
961
|
+
['registry_status', ['npm', ['run', 'registry:check']]],
|
|
962
|
+
['cli_smoke', ['npm', ['run', 'cli-smoke']]],
|
|
963
|
+
['dist_freshness', ['npm', ['run', 'dist:freshness']]]
|
|
964
|
+
].map(([id, command]) => runReleaseCheck(id, command));
|
|
965
|
+
const failed = checks.filter((check) => check.status !== 'passed').map((check) => check.id);
|
|
966
|
+
return {
|
|
967
|
+
schema: 'yam.release-report.v1',
|
|
968
|
+
generated_at: startedAt,
|
|
969
|
+
packageName: PACKAGE_JSON.name,
|
|
970
|
+
version: VERSION,
|
|
971
|
+
ok: failed.length === 0,
|
|
972
|
+
truth_status: failed.length === 0 ? 'verified' : 'blocked',
|
|
973
|
+
checks,
|
|
974
|
+
failed
|
|
975
|
+
};
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
function runReleaseCheck(id, commandSpec) {
|
|
979
|
+
const [command, args] = commandSpec;
|
|
980
|
+
const start = Date.now();
|
|
981
|
+
const result = spawnSync(command, args, {
|
|
982
|
+
cwd: ROOT,
|
|
983
|
+
encoding: 'utf8',
|
|
984
|
+
env: { ...process.env, npm_config_cache: path.join(os.tmpdir(), 'yam-npm-cache') },
|
|
985
|
+
timeout: 120000
|
|
986
|
+
});
|
|
987
|
+
const output = [result.stdout, result.stderr].filter(Boolean).join('\n').trim();
|
|
988
|
+
const timedOut = result.error?.message?.includes('ETIMEDOUT') || result.signal === 'SIGTERM';
|
|
989
|
+
return {
|
|
990
|
+
id,
|
|
991
|
+
command: [command, ...args].join(' '),
|
|
992
|
+
status: result.status === 0 ? 'passed' : timedOut ? 'blocked' : 'failed',
|
|
993
|
+
exit_code: typeof result.status === 'number' ? result.status : null,
|
|
994
|
+
duration_ms: Date.now() - start,
|
|
995
|
+
note: summarizeCheckOutput(output || result.error?.message || '')
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
function summarizeCheckOutput(output = '') {
|
|
1000
|
+
const lines = String(output || '').split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
|
|
1001
|
+
if (!lines.length) return '';
|
|
1002
|
+
const highSignal = lines.filter((line) => !line.startsWith('>')).slice(-4);
|
|
1003
|
+
return highSignal.join(' | ').slice(0, 600);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
922
1006
|
async function readStdinTextIfAvailable() {
|
|
923
1007
|
if (process.stdin.isTTY) return '';
|
|
924
1008
|
const chunks = [];
|
|
@@ -966,6 +1050,9 @@ async function proof(args = []) {
|
|
|
966
1050
|
printProofList('Evidence', summary.evidence);
|
|
967
1051
|
printProofList('Visual evidence', summary.visual);
|
|
968
1052
|
printProofList('Runtime evidence', summary.runtime);
|
|
1053
|
+
printProofList('Visual provenance', summary.visualProvenance || []);
|
|
1054
|
+
printProofList('Mission patch envelope', summary.missionEnvelope || []);
|
|
1055
|
+
printProofList('Rollback hint', summary.rollbackHint || []);
|
|
969
1056
|
printProofList('Changed surfaces', summary.changed);
|
|
970
1057
|
printProofList('Skipped', summary.skipped);
|
|
971
1058
|
printProofList('Blocked', summary.blocked);
|
|
@@ -1040,6 +1127,18 @@ function buildProofSummary(parsed, artifact) {
|
|
|
1040
1127
|
evidence: [...artifact.evidence, ...arrayFlag(parsed.flags.evidence)],
|
|
1041
1128
|
visual: [...artifact.visual, ...arrayFlag(parsed.flags.visual)],
|
|
1042
1129
|
runtime: [...artifact.runtime, ...arrayFlag(parsed.flags.runtime)],
|
|
1130
|
+
visualProvenance: [
|
|
1131
|
+
...structuredEvidenceList(artifact.visualProvenance),
|
|
1132
|
+
...structuredEvidenceList(parsed.flags.visual_provenance)
|
|
1133
|
+
],
|
|
1134
|
+
missionEnvelope: [
|
|
1135
|
+
...structuredEvidenceList(artifact.missionEnvelope),
|
|
1136
|
+
...structuredEvidenceList(parsed.flags.mission_envelope)
|
|
1137
|
+
],
|
|
1138
|
+
rollbackHint: [
|
|
1139
|
+
...structuredEvidenceList(artifact.rollbackHint),
|
|
1140
|
+
...structuredEvidenceList(parsed.flags.rollback_hint)
|
|
1141
|
+
],
|
|
1043
1142
|
cleanup: parsed.flags.cleanup || artifact.cleanup || '',
|
|
1044
1143
|
changed: [...artifact.changed, ...arrayFlag(parsed.flags.changed)],
|
|
1045
1144
|
skipped: [...artifact.skipped, ...arrayFlag(parsed.flags.skipped)],
|
|
@@ -1090,6 +1189,15 @@ function renderProofMarkdown(summary) {
|
|
|
1090
1189
|
'## Runtime evidence',
|
|
1091
1190
|
...renderProofMarkdownList(summary.runtime),
|
|
1092
1191
|
'',
|
|
1192
|
+
'## Visual provenance',
|
|
1193
|
+
...renderProofMarkdownList(summary.visualProvenance || []),
|
|
1194
|
+
'',
|
|
1195
|
+
'## Mission patch envelope',
|
|
1196
|
+
...renderProofMarkdownList(summary.missionEnvelope || []),
|
|
1197
|
+
'',
|
|
1198
|
+
'## Rollback hint',
|
|
1199
|
+
...renderProofMarkdownList(summary.rollbackHint || []),
|
|
1200
|
+
'',
|
|
1093
1201
|
'## Changed surfaces',
|
|
1094
1202
|
...renderProofMarkdownList(summary.changed),
|
|
1095
1203
|
'',
|
|
@@ -1122,7 +1230,7 @@ function renderProofMarkdownList(values) {
|
|
|
1122
1230
|
function parseProofArgs(args = []) {
|
|
1123
1231
|
const flags: AnyRecord = {};
|
|
1124
1232
|
const positionals: string[] = [];
|
|
1125
|
-
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
1233
|
+
const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
|
|
1126
1234
|
for (let index = 0; index < args.length; index += 1) {
|
|
1127
1235
|
const arg = args[index];
|
|
1128
1236
|
if (arg.startsWith('--')) {
|
|
@@ -1136,7 +1244,7 @@ function parseProofArgs(args = []) {
|
|
|
1136
1244
|
}
|
|
1137
1245
|
const value = inlineValue ?? args[index + 1] ?? '';
|
|
1138
1246
|
if (inlineValue === undefined) index += 1;
|
|
1139
|
-
if (['command', 'evidence', 'visual', 'runtime', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
1247
|
+
if (['command', 'evidence', 'visual', 'runtime', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
1140
1248
|
flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
|
|
1141
1249
|
} else {
|
|
1142
1250
|
flags[normalizedKey] = value;
|
|
@@ -1174,6 +1282,9 @@ function emptyProofArtifact() {
|
|
|
1174
1282
|
evidence: [],
|
|
1175
1283
|
visual: [],
|
|
1176
1284
|
runtime: [],
|
|
1285
|
+
visualProvenance: [],
|
|
1286
|
+
missionEnvelope: [],
|
|
1287
|
+
rollbackHint: [],
|
|
1177
1288
|
cleanup: '',
|
|
1178
1289
|
changed: [],
|
|
1179
1290
|
skipped: [],
|
|
@@ -1196,6 +1307,9 @@ async function parseProofJson(file, empty) {
|
|
|
1196
1307
|
evidence: arrayFlag(data.evidence),
|
|
1197
1308
|
visual: arrayFlag(data.visual || data.visualEvidence),
|
|
1198
1309
|
runtime: arrayFlag(data.runtime || data.runtimeEvidence),
|
|
1310
|
+
visualProvenance: structuredEvidenceList(data.visualProvenance || data.visual_provenance),
|
|
1311
|
+
missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
|
|
1312
|
+
rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
|
|
1199
1313
|
cleanup: String(data.cleanup || data.cleanupStatus || ''),
|
|
1200
1314
|
changed: arrayFlag(data.changed || data.files),
|
|
1201
1315
|
skipped: arrayFlag(data.skipped),
|
|
@@ -1221,6 +1335,9 @@ async function parseProofMarkdown(file, empty) {
|
|
|
1221
1335
|
evidence: readProofList(text, 'Evidence'),
|
|
1222
1336
|
visual: readProofList(text, 'Visual evidence'),
|
|
1223
1337
|
runtime: firstProofList(text, ['Runtime evidence', 'Processes', 'tmux']),
|
|
1338
|
+
visualProvenance: readProofList(text, 'Visual provenance'),
|
|
1339
|
+
missionEnvelope: readProofList(text, 'Mission patch envelope'),
|
|
1340
|
+
rollbackHint: readProofList(text, 'Rollback hint'),
|
|
1224
1341
|
cleanup: readProofField(text, 'Cleanup') || readProofField(text, 'Cleanup status'),
|
|
1225
1342
|
changed: firstProofList(text, ['Changed surfaces', 'Files']),
|
|
1226
1343
|
skipped: readProofList(text, 'Skipped'),
|
|
@@ -1267,9 +1384,36 @@ function arrayFlag(value) {
|
|
|
1267
1384
|
return Array.isArray(value) ? value.filter(Boolean) : [value].filter(Boolean);
|
|
1268
1385
|
}
|
|
1269
1386
|
|
|
1387
|
+
function structuredEvidenceList(value) {
|
|
1388
|
+
return arrayFlag(value).map((item) => formatStructuredEvidence(item)).filter(Boolean);
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
function formatStructuredEvidence(value) {
|
|
1392
|
+
if (!value) return '';
|
|
1393
|
+
if (typeof value === 'object') return JSON.stringify(value);
|
|
1394
|
+
const text = String(value).trim();
|
|
1395
|
+
if (!text) return '';
|
|
1396
|
+
if (!text.startsWith('{')) return text;
|
|
1397
|
+
try {
|
|
1398
|
+
const data = JSON.parse(text);
|
|
1399
|
+
if (data.schema === 'yam.ueye-visual-provenance.v1' || data.source_kind || data.reference_id || data.screenshot_id) {
|
|
1400
|
+
return JSON.stringify(buildUeyeVisualProvenance(data));
|
|
1401
|
+
}
|
|
1402
|
+
if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
|
|
1403
|
+
return JSON.stringify(buildMissionPatchEnvelope(data));
|
|
1404
|
+
}
|
|
1405
|
+
if (data.schema === 'yam.rollback-hint.v1' || data.touched_files || data.safe_revert_note) {
|
|
1406
|
+
return JSON.stringify(buildRollbackHint(data));
|
|
1407
|
+
}
|
|
1408
|
+
return JSON.stringify(data);
|
|
1409
|
+
} catch {
|
|
1410
|
+
return text;
|
|
1411
|
+
}
|
|
1412
|
+
}
|
|
1413
|
+
|
|
1270
1414
|
function hasProofEvidence(parsed) {
|
|
1271
1415
|
const flags = parsed.flags || {};
|
|
1272
|
-
return ['command', 'evidence', 'visual', 'runtime', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup);
|
|
1416
|
+
return ['command', 'evidence', 'visual', 'runtime', 'visual_provenance', 'mission_envelope', 'rollback_hint', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup);
|
|
1273
1417
|
}
|
|
1274
1418
|
|
|
1275
1419
|
function printProofList(label, values) {
|
|
@@ -1961,6 +2105,7 @@ async function main() {
|
|
|
1961
2105
|
if (command === 'measure') return measure(process.argv[3], process.argv.slice(4));
|
|
1962
2106
|
if (command === 'tools') return tools(process.argv.slice(3));
|
|
1963
2107
|
if (command === 'proof') return proof(process.argv.slice(3));
|
|
2108
|
+
if (command === 'release') return release(process.argv.slice(3));
|
|
1964
2109
|
if (command === 'safety') return safety(process.argv.slice(3));
|
|
1965
2110
|
if (command === 'memory') return memory(process.argv.slice(3));
|
|
1966
2111
|
if (command === 'hook') return hook(process.argv.slice(3));
|
package/src/lib/trust-kernel.ts
CHANGED
|
@@ -47,6 +47,38 @@ export interface RuntimeTruthMatrix {
|
|
|
47
47
|
blockers: string[];
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
export interface UeyeVisualProvenance {
|
|
51
|
+
schema: 'yam.ueye-visual-provenance.v1';
|
|
52
|
+
source_kind: string;
|
|
53
|
+
source_path: string;
|
|
54
|
+
source_hash: string;
|
|
55
|
+
reference_id: string;
|
|
56
|
+
screenshot_id: string;
|
|
57
|
+
local_only: boolean;
|
|
58
|
+
redacted: boolean;
|
|
59
|
+
operator_provided: boolean;
|
|
60
|
+
comparison_result: string;
|
|
61
|
+
truth_status: TruthStatus;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface RollbackHint {
|
|
65
|
+
schema: 'yam.rollback-hint.v1';
|
|
66
|
+
touched_files: string[];
|
|
67
|
+
generated_files: string[];
|
|
68
|
+
before_check: string;
|
|
69
|
+
safe_revert_note: string;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface MissionPatchEnvelope {
|
|
73
|
+
schema: 'yam.mission-patch-envelope.v1';
|
|
74
|
+
agent_id: string;
|
|
75
|
+
assigned_scope: string;
|
|
76
|
+
changed_files: string[];
|
|
77
|
+
verification_hint: string;
|
|
78
|
+
rollback_hint: RollbackHint;
|
|
79
|
+
truth_status: TruthStatus;
|
|
80
|
+
}
|
|
81
|
+
|
|
50
82
|
export interface FakeRealPolicyResult {
|
|
51
83
|
schema: 'yam.fake-real-policy.v1';
|
|
52
84
|
ok: boolean;
|
|
@@ -71,6 +103,9 @@ export interface ProofSummary {
|
|
|
71
103
|
blocked?: unknown;
|
|
72
104
|
assumptions?: unknown;
|
|
73
105
|
unverified?: unknown;
|
|
106
|
+
visualProvenance?: unknown;
|
|
107
|
+
missionEnvelope?: unknown;
|
|
108
|
+
rollbackHint?: unknown;
|
|
74
109
|
}
|
|
75
110
|
|
|
76
111
|
export interface ProofOptions {
|
|
@@ -101,6 +136,9 @@ export interface YamCompletionProof {
|
|
|
101
136
|
evidence: string[];
|
|
102
137
|
visual: string[];
|
|
103
138
|
runtime: string[];
|
|
139
|
+
visualProvenance: string[];
|
|
140
|
+
missionEnvelope: string[];
|
|
141
|
+
rollbackHint: string[];
|
|
104
142
|
cleanup: unknown;
|
|
105
143
|
changed: string[];
|
|
106
144
|
skipped: string[];
|
|
@@ -113,6 +151,48 @@ export interface YamCompletionProof {
|
|
|
113
151
|
runtimeTruth: RuntimeTruthMatrix;
|
|
114
152
|
}
|
|
115
153
|
|
|
154
|
+
export function buildUeyeVisualProvenance(input: Partial<UeyeVisualProvenance> = {}): UeyeVisualProvenance {
|
|
155
|
+
const truth = isTruthStatus(input.truth_status) ? input.truth_status : 'partial';
|
|
156
|
+
return {
|
|
157
|
+
schema: 'yam.ueye-visual-provenance.v1',
|
|
158
|
+
source_kind: String(input.source_kind || ''),
|
|
159
|
+
source_path: String(input.source_path || ''),
|
|
160
|
+
source_hash: String(input.source_hash || 'unknown'),
|
|
161
|
+
reference_id: String(input.reference_id || ''),
|
|
162
|
+
screenshot_id: String(input.screenshot_id || ''),
|
|
163
|
+
local_only: Boolean(input.local_only),
|
|
164
|
+
redacted: Boolean(input.redacted),
|
|
165
|
+
operator_provided: Boolean(input.operator_provided),
|
|
166
|
+
comparison_result: String(input.comparison_result || 'not-verified'),
|
|
167
|
+
truth_status: truth
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function buildRollbackHint(input: Partial<RollbackHint> = {}): RollbackHint {
|
|
172
|
+
return {
|
|
173
|
+
schema: 'yam.rollback-hint.v1',
|
|
174
|
+
touched_files: asList(input.touched_files),
|
|
175
|
+
generated_files: asList(input.generated_files),
|
|
176
|
+
before_check: String(input.before_check || ''),
|
|
177
|
+
safe_revert_note: String(input.safe_revert_note || '')
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export function buildMissionPatchEnvelope(input: Partial<MissionPatchEnvelope> = {}): MissionPatchEnvelope {
|
|
182
|
+
const rollback = input.rollback_hint && typeof input.rollback_hint === 'object'
|
|
183
|
+
? buildRollbackHint(input.rollback_hint)
|
|
184
|
+
: buildRollbackHint();
|
|
185
|
+
return {
|
|
186
|
+
schema: 'yam.mission-patch-envelope.v1',
|
|
187
|
+
agent_id: String(input.agent_id || ''),
|
|
188
|
+
assigned_scope: String(input.assigned_scope || ''),
|
|
189
|
+
changed_files: asList(input.changed_files),
|
|
190
|
+
verification_hint: String(input.verification_hint || ''),
|
|
191
|
+
rollback_hint: rollback,
|
|
192
|
+
truth_status: isTruthStatus(input.truth_status) ? input.truth_status : 'partial'
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
116
196
|
const TRUTH_RANK: Readonly<Record<TruthStatus, number>> = Object.freeze({
|
|
117
197
|
blocked: 0,
|
|
118
198
|
real_required_missing: 0,
|
|
@@ -184,6 +264,15 @@ export function classifyEvidenceTruth(summary: ProofSummary = {}, options: Proof
|
|
|
184
264
|
for (const visual of asList(summary.visual)) {
|
|
185
265
|
rows.push(evidenceRow('visual', visual, classifyVisualEvidence(visual)));
|
|
186
266
|
}
|
|
267
|
+
for (const provenance of asList(summary.visualProvenance)) {
|
|
268
|
+
rows.push(evidenceRow('visual', provenance, metadataTruth(provenance, 'partial')));
|
|
269
|
+
}
|
|
270
|
+
for (const envelope of asList(summary.missionEnvelope)) {
|
|
271
|
+
rows.push(evidenceRow('evidence', envelope, metadataTruth(envelope, 'partial')));
|
|
272
|
+
}
|
|
273
|
+
for (const rollback of asList(summary.rollbackHint)) {
|
|
274
|
+
rows.push(evidenceRow('evidence', rollback, metadataTruth(rollback, 'partial')));
|
|
275
|
+
}
|
|
187
276
|
for (const runtime of asList(summary.runtime)) {
|
|
188
277
|
rows.push(evidenceRow('runtime', runtime, classifyRuntimeEvidence(runtime, options)));
|
|
189
278
|
}
|
|
@@ -267,6 +356,9 @@ export function buildYamCompletionProof(summary: ProofSummary = {}, options: Pro
|
|
|
267
356
|
evidence: asList(summary.evidence),
|
|
268
357
|
visual: asList(summary.visual),
|
|
269
358
|
runtime: asList(summary.runtime),
|
|
359
|
+
visualProvenance: asList(summary.visualProvenance),
|
|
360
|
+
missionEnvelope: asList(summary.missionEnvelope),
|
|
361
|
+
rollbackHint: asList(summary.rollbackHint),
|
|
270
362
|
cleanup: summary.cleanup || '',
|
|
271
363
|
changed: asList(summary.changed),
|
|
272
364
|
skipped: asList(summary.skipped),
|
|
@@ -317,6 +409,25 @@ function commandPassed(value: unknown = ''): boolean {
|
|
|
317
409
|
return /pass|passed|ok|success|exit\s*0|green/i.test(String(value || '')) && !/fail|failed|error|blocked/i.test(String(value || ''));
|
|
318
410
|
}
|
|
319
411
|
|
|
412
|
+
function metadataTruth(value: unknown = '', fallback: TruthStatus = 'partial'): TruthStatus {
|
|
413
|
+
return weakestTruth(readStructuredTruth(value, fallback), 'partial');
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function readStructuredTruth(value: unknown = '', fallback: TruthStatus = 'partial'): TruthStatus {
|
|
417
|
+
if (typeof value === 'object' && value) {
|
|
418
|
+
const candidate = (value as { truth_status?: unknown; truth?: unknown }).truth_status || (value as { truth?: unknown }).truth;
|
|
419
|
+
return isTruthStatus(candidate) ? candidate : fallback;
|
|
420
|
+
}
|
|
421
|
+
const text = String(value || '');
|
|
422
|
+
try {
|
|
423
|
+
const data = JSON.parse(text);
|
|
424
|
+
const candidate = data.truth_status || data.truth;
|
|
425
|
+
return isTruthStatus(candidate) ? candidate : fallback;
|
|
426
|
+
} catch {
|
|
427
|
+
return fallback;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
320
431
|
function strongestSupportedTruth(rows: EvidenceRow[] = []): TruthStatus {
|
|
321
432
|
if (rows.some((row) => row.truth === 'blocked')) return 'blocked';
|
|
322
433
|
if (rows.some((row) => row.truth === 'real_required_missing')) return 'real_required_missing';
|
|
@@ -29,6 +29,19 @@ Subagent 판단:
|
|
|
29
29
|
- 이유:
|
|
30
30
|
- subagent가 불가능하거나 불필요하면 기본적으로 $deep으로 전환:
|
|
31
31
|
|
|
32
|
+
Patch envelope:
|
|
33
|
+
- agent_id:
|
|
34
|
+
- assigned_scope:
|
|
35
|
+
- changed_files:
|
|
36
|
+
- verification_hint:
|
|
37
|
+
- truth_status: verified / partial / skipped / blocked / assumed
|
|
38
|
+
|
|
39
|
+
Rollback hint:
|
|
40
|
+
- touched_files:
|
|
41
|
+
- generated_files:
|
|
42
|
+
- before_check:
|
|
43
|
+
- safe_revert_note:
|
|
44
|
+
|
|
32
45
|
검증:
|
|
33
46
|
- 필요한 가장 작은 검증 명령을 우선 실행
|
|
34
47
|
- 필요하면 tmux/dev server/browser QA/process cleanup proof 사용
|
|
@@ -14,6 +14,21 @@
|
|
|
14
14
|
| implementation | | | unknown | unknown | | | proof |
|
|
15
15
|
| reference | | | unknown | unknown | | | reference direction |
|
|
16
16
|
|
|
17
|
+
## Visual Provenance
|
|
18
|
+
|
|
19
|
+
| Field | Value |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| source_kind | |
|
|
22
|
+
| source_path | |
|
|
23
|
+
| source_hash | unknown |
|
|
24
|
+
| reference_id | |
|
|
25
|
+
| screenshot_id | |
|
|
26
|
+
| local_only | true / false |
|
|
27
|
+
| redacted | true / false |
|
|
28
|
+
| operator_provided | true / false |
|
|
29
|
+
| comparison_result | matched / similar / different / not-verified / not-applicable |
|
|
30
|
+
| truth_status | verified / partial / skipped / blocked / assumed |
|
|
31
|
+
|
|
17
32
|
## Reference Read Proof
|
|
18
33
|
|
|
19
34
|
- Layout:
|
package/templates/ueye-review.md
CHANGED
|
@@ -25,6 +25,21 @@
|
|
|
25
25
|
- Role: proof / reference direction / annotation / partial evidence
|
|
26
26
|
- Visual verification cap:
|
|
27
27
|
|
|
28
|
+
## Visual Provenance
|
|
29
|
+
|
|
30
|
+
| Field | Value |
|
|
31
|
+
| --- | --- |
|
|
32
|
+
| source_kind | |
|
|
33
|
+
| source_path | |
|
|
34
|
+
| source_hash | unknown |
|
|
35
|
+
| reference_id | |
|
|
36
|
+
| screenshot_id | |
|
|
37
|
+
| local_only | true / false |
|
|
38
|
+
| redacted | true / false |
|
|
39
|
+
| operator_provided | true / false |
|
|
40
|
+
| comparison_result | matched / similar / different / not-verified / not-applicable |
|
|
41
|
+
| truth_status | verified / partial / skipped / blocked / assumed |
|
|
42
|
+
|
|
28
43
|
## Reference Read Proof
|
|
29
44
|
|
|
30
45
|
- Layout:
|
package/yam.manifest.json
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
{
|
|
23
23
|
"id": "ueye",
|
|
24
24
|
"stage": "v0.3",
|
|
25
|
-
"purpose": "Tight UI/UX/design implementation and visual review with reference-read proof, visual comparison, design quality review, and P0-P3 issue tracking."
|
|
25
|
+
"purpose": "Tight UI/UX/design implementation and visual review with visual provenance, reference-read proof, visual comparison, design quality review, and P0-P3 issue tracking."
|
|
26
26
|
},
|
|
27
27
|
{
|
|
28
28
|
"id": "question",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
{
|
|
43
43
|
"id": "mission",
|
|
44
44
|
"stage": "v0.2",
|
|
45
|
-
"purpose": "Explicit real-subagent/team execution route for approved plans, cross-verification, doctor scan, and final proof."
|
|
45
|
+
"purpose": "Explicit real-subagent/team execution route for approved plans, patch envelopes, rollback hints, cross-verification, doctor scan, and final proof."
|
|
46
46
|
}
|
|
47
47
|
]
|
|
48
48
|
}
|