yam-flow 0.1.4 → 0.1.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,34 @@
2
2
 
3
3
  All notable changes to yam-flow are documented here.
4
4
 
5
+ ## v0.1.6 - 2026-06-07
6
+
7
+ ### Added
8
+
9
+ - Added the Ueye Design Completion Gate so Ueye can own design/UI/UX completion quality instead of relying on a separate code-risk route to catch unfinished visual work.
10
+ - Added `yam ueye report --completion-claim`, `--strict`, `--design-score`, P0/P1, CTA, state, mobile/responsive, contrast/accessibility, direction, and reference-read gate fields.
11
+ - Added `yam proof --design-completion` so design completion evidence can cap proof truth status when a Ueye done claim is not supported.
12
+ - Added design completion sections to Ueye review and comparison templates.
13
+
14
+ ### Improved
15
+
16
+ - Ueye `done` claims now cap to partial or blocked when implementation evidence, design quality, P0/P1 status, state coverage, mobile/responsive checks, contrast/accessibility checks, CTA checks, or reference-read/comparison evidence is missing where relevant.
17
+ - Ueye report JSON now includes `design_completion_gate` in both the main run report and comparison report.
18
+ - Expanded CLI smoke coverage for Ueye completion gate success, partial, and blocked paths.
19
+
20
+ ## v0.1.5 - 2026-06-04
21
+
22
+ ### Added
23
+
24
+ - Added Ueye surface context metadata for provider context, provider badge, execution surface, app surface, browser surface, control mode, preserved URL, preserved state, and evidence id.
25
+ - Added surface context output to `yam ueye capture`, `yam ueye compare`, and `yam ueye report` so visual evidence can say where and how it was observed.
26
+ - Added provider and execution surface fields to Ueye visual provenance records.
27
+
28
+ ### Improved
29
+
30
+ - Strengthened `yam ueye report` for fast UI state preservation without making capture or compare an always-on gate.
31
+ - Updated Ueye docs and templates so visual proof can distinguish source files, local screenshots, in-app browser checks, and other execution surfaces.
32
+
5
33
  ## v0.1.4 - 2026-06-03
6
34
 
7
35
  ### Added
package/COMMANDS.md CHANGED
@@ -44,7 +44,8 @@ Opt-in visual evidence helpers:
44
44
  ```bash
45
45
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
46
46
  yam ueye compare --reference ./reference.png --actual .yam/screens/home.png
47
- yam ueye report --reference ./reference.png --actual .yam/screens/home.png --review-session-id pricing-card-v1 --similar "overall hierarchy" --different "button glow intensity" --missing "mobile state" --design-quality needs-polish --json
47
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --review-session-id pricing-card-v1 --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --similar "overall hierarchy" --different "button glow intensity" --missing "mobile state" --design-quality needs-polish --json
48
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --completion-claim done --design-quality pass --direction-locked --reference-read --states-checked --mobile-checked --contrast-checked --cta-checked --json
48
49
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
49
50
  yam proof --route ueye --truth partial --visual "browser/local screenshot comparison executed" --require-visual
50
51
  ```
@@ -149,7 +150,8 @@ yam proof --route ueye --truth verified --visual "reference image only"
149
150
  yam proof --route ueye --truth partial --visual-provenance '{"source_kind":"reference","source_hash":"unknown","comparison_result":"not-verified","truth_status":"partial"}'
150
151
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
151
152
  yam ueye compare --reference ./reference.png --actual .yam/screens/home.png --json
152
- yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality pass --json
153
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality pass --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --json
154
+ yam proof --route ueye --truth verified --visual "implementation screenshot evidence recorded" --design-completion '{"completion_claim":"done","has_implementation_screenshot":true,"design_quality":"pass","states_checked":true,"mobile_checked":true,"contrast_checked":true,"cta_checked":true,"direction_locked":true,"truth_status":"verified"}'
153
155
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
154
156
  yam runtime evidence --backend terminal --claim observed --evidence-id dev-server-1 --command "npm run dev" --json
155
157
  yam mission queue --agent-id implementer --scope "checkout form" --changed src/checkout.tsx --verification-hint "npm run typecheck" --rollback-hint "revert checkout form patch if typecheck or smoke fails" --json
package/README.md CHANGED
@@ -129,7 +129,8 @@ yam proof --route ueye --truth verified --visual "reference image only"
129
129
  yam proof --route ueye --truth partial --visual-provenance '{"source_kind":"reference","source_hash":"unknown","comparison_result":"not-verified","truth_status":"partial"}'
130
130
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
131
131
  yam ueye compare --reference ./reference.png --actual .yam/screens/home.png
132
- yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality needs-polish --json
132
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --completion-claim done --design-quality pass --direction-locked --reference-read --states-checked --mobile-checked --contrast-checked --cta-checked --provider-context local --execution-surface in-app-browser --app-surface codex-app --browser-surface in-app-browser --preserved-state --json
133
+ yam proof --route ueye --truth verified --visual "implementation screenshot evidence recorded" --design-completion '{"completion_claim":"done","has_implementation_screenshot":true,"design_quality":"pass","states_checked":true,"mobile_checked":true,"contrast_checked":true,"cta_checked":true,"direction_locked":true,"truth_status":"verified"}'
133
134
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
134
135
  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"}'
135
136
  yam release report --json
@@ -177,6 +178,8 @@ Publishing still requires confirming the final npm package name and account acce
177
178
  - runtime truth matrix
178
179
  - visual evidence caps
179
180
  - Ueye visual run reports
181
+ - Ueye surface context metadata
182
+ - Ueye design completion gate
180
183
  - runtime backend evidence
181
184
  - runtime evidence mini summaries
182
185
  - mission patch queue lite records
@@ -197,6 +200,8 @@ These shapes keep reports machine-readable without turning normal work into a he
197
200
  - Benchmark optimization loop lite: use `yam measure <route>` and `yam template tuning` to record baseline, one route wording change, rerun result, keep/revert decision, and stop condition.
198
201
  - Structured diagnostic next action: every doctor/release diagnostic should say what was observed, what evidence supports it, what to do next, owner route, severity, and truth status.
199
202
  - Ueye review continuity/comparison report: `$ueye` run reports can link a previous review, current evidence, comparison delta, design quality result, and next action.
203
+ - Ueye surface context: `$ueye` run reports can record provider context, provider badge, execution surface, app surface, browser surface, control mode, preserved URL/state, and evidence id.
204
+ - Ueye design completion gate: `$ueye` can stay fast for draft work, but a `done` claim is capped until implementation evidence, comparison/design-quality status, P0/P1 status, key states, mobile/responsive behavior, contrast/accessibility visuals, CTA affordance, and direction/reference-read proof are recorded when relevant.
200
205
 
201
206
  ## Ueye Capture And Compare
202
207
 
@@ -205,14 +210,14 @@ These shapes keep reports machine-readable without turning normal work into a he
205
210
  ```bash
206
211
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
207
212
  yam ueye compare --reference ./reference.png --actual .yam/screens/home.png --json
208
- yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality pass --json
213
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --completion-claim done --design-quality pass --direction-locked --reference-read --states-checked --mobile-checked --contrast-checked --cta-checked --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --preserved-state --json
209
214
  ```
210
215
 
211
216
  `capture` uses a Playwright install from the current project when present, then falls back to the package context. It does not install browsers or add runtime dependencies by itself. If capture is unavailable, the visual claim stays `partial` or `blocked` until a real screenshot is supplied.
212
217
 
213
218
  `compare` is local-only and dependency-free. It records file hashes, dimensions, comparison result, and proof-ready visual provenance. Exact image matches can be `verified`; different screenshots stay `partial` so visual parity is not overclaimed.
214
219
 
215
- `report` gathers reference sources, implementation screenshots, comparison result, continuity fields, design quality judgment, and next action into one compact proof-ready JSON object.
220
+ `report` gathers reference sources, implementation screenshots, comparison result, continuity fields, design quality judgment, design completion gate, surface context, and next action into one compact proof-ready JSON object. Draft and needs-polish work can stay fast; `--completion-claim done` turns on the stricter design gate.
216
221
 
217
222
  Generated media can guide visual direction, but it cannot prove the implemented UI by itself:
218
223
 
package/dist/bin/yam.js CHANGED
@@ -8,7 +8,7 @@ import os from 'node:os';
8
8
  import path from 'node:path';
9
9
  import process from 'node:process';
10
10
  import { fileURLToPath } from 'node:url';
11
- import { TRUTH_STATUSES, buildMediaGenerationProof, buildMissionPatchEnvelope, buildRollbackHint, buildRuntimeBackendEvidence, buildUeyeVisualProvenance, buildUeyeRunReport, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
11
+ import { TRUTH_STATUSES, buildMediaGenerationProof, buildMissionPatchEnvelope, buildRollbackHint, buildRuntimeBackendEvidence, buildUeyeDesignCompletionGate, buildUeyeVisualProvenance, buildUeyeRunReport, buildUeyeSurfaceContext, buildYamCompletionProof, detectDbSafetyText as detectTrustDbSafetyText, isTruthStatus } from '../lib/trust-kernel.js';
12
12
  function errorMessage(error) {
13
13
  return error instanceof Error ? error.message : String(error);
14
14
  }
@@ -122,7 +122,7 @@ Usage:
122
122
  yam proof write [dir] [--format json|md] [--out file] [--route route] [--truth status] [--command text]
123
123
  yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
124
124
  yam ueye compare --reference ref.png --actual screenshot.png [--json]
125
- yam ueye report [--reference ref.png] [--actual screenshot.png] [--json]
125
+ yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
126
126
  yam media proof [--requested] [--attempted] [--output file] [--json]
127
127
  yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
128
128
  yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
@@ -808,7 +808,7 @@ function toolsDoctorNextActionDetails({ pack, rows, riskNotes, frameworkChecklis
808
808
  if (frameworkChecklist?.detected && !commands?.build && !commands?.typecheck)
809
809
  actions.push(nextActionDetail('verification-command', 'warning', 'build/typecheck command not detected', 'add or document a small build/typecheck command for implementation verification', 'yam pack .'));
810
810
  if (frameworkChecklist?.uiLike)
811
- actions.push(nextActionDetail('ueye-evidence', 'info', 'UI-like project detected', 'for Ueye verified claims, record reference, actual screenshot, and comparison result', 'yam ueye report --reference ref.png --actual shot.png --json'));
811
+ actions.push(nextActionDetail('ueye-evidence', 'info', 'UI-like project detected', 'for Ueye verified claims, record reference, actual screenshot, comparison result, and surface context', 'yam ueye report --reference ref.png --actual shot.png --provider-context local --execution-surface in-app-browser --json'));
812
812
  return uniqueNextActionDetails(actions);
813
813
  }
814
814
  async function scanSqlFiles(rootDir, { maxFiles = 30, maxBytes = 200000, maxDepth = 5 } = {}) {
@@ -1366,18 +1366,18 @@ Opt-in visual evidence helpers. Ueye stays one skill: fast by default, capture/c
1366
1366
  Usage:
1367
1367
  yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
1368
1368
  yam ueye compare --reference ref.png --actual screenshot.png [--json]
1369
- yam ueye report [--reference ref.png] [--actual screenshot.png] [--review-session-id id] [--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]
1369
+ yam ueye report [--reference ref.png] [--actual screenshot.png] [--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]
1370
1370
 
1371
1371
  Notes:
1372
1372
  capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
1373
1373
  compare uses local files only and reports sha256, dimensions, comparison_result, and proof-ready visual provenance.
1374
- report produces a proof-ready Ueye visual run report and continuity/comparison record without requiring a new capture.
1374
+ report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
1375
1375
  `);
1376
1376
  }
1377
1377
  async function ueyeCapture(args = []) {
1378
1378
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1379
1379
  return ueyeUsage();
1380
- const flags = parseSimpleFlags(args, new Set(['url', 'out', 'viewport', 'wait-until', 'timeout', 'full-page', 'json']));
1380
+ const flags = parseSimpleFlags(args, new Set(['url', 'out', 'viewport', 'wait-until', 'timeout', 'full-page', 'provider-context', 'provider-badge', 'execution-surface', 'app-surface', 'browser-surface', 'control-mode', 'preserved-state', 'preserved-url', 'evidence-id', 'screenshot-id', 'json']));
1381
1381
  const url = String(flags.url || '');
1382
1382
  const out = String(flags.out || '');
1383
1383
  if (!url || !out) {
@@ -1401,6 +1401,24 @@ async function ueyeCapture(args = []) {
1401
1401
  await page.goto(url, { waitUntil, timeout });
1402
1402
  await page.screenshot({ path: target, fullPage: Boolean(flags.full_page) });
1403
1403
  const info = await imageFileInfo(target);
1404
+ const surfaceContext = buildUeyeSurfaceContext({
1405
+ provider_context: String(flags.provider_context || 'local-playwright'),
1406
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'local-playwright'),
1407
+ execution_surface: String(flags.execution_surface || 'browser-capture'),
1408
+ app_surface: String(flags.app_surface || 'local-project'),
1409
+ browser_surface: String(flags.browser_surface || 'playwright'),
1410
+ control_mode: String(flags.control_mode || 'automated-capture'),
1411
+ route: 'ueye',
1412
+ mode: 'capture',
1413
+ url,
1414
+ viewport: `${viewport.width}x${viewport.height}`,
1415
+ screenshot_id: String(flags.screenshot_id || path.basename(target)),
1416
+ evidence_id: String(flags.evidence_id || `ueye-capture-${timestampId()}`),
1417
+ preserved_state: Boolean(flags.preserved_state),
1418
+ preserved_url: String(flags.preserved_url || url),
1419
+ local_only: true,
1420
+ truth_status: 'verified'
1421
+ });
1404
1422
  const result = {
1405
1423
  schema: 'yam.ueye-capture.v1',
1406
1424
  status: 'verified',
@@ -1410,6 +1428,7 @@ async function ueyeCapture(args = []) {
1410
1428
  full_page: Boolean(flags.full_page),
1411
1429
  sha256: info.sha256,
1412
1430
  dimensions: info.dimensions,
1431
+ surface_context: surfaceContext,
1413
1432
  visual_evidence: `browser screenshot captured: ${target} (${info.dimensions}, sha256:${info.sha256})`
1414
1433
  };
1415
1434
  printJsonOrHuman(result, Boolean(flags.json), 'Ueye capture');
@@ -1435,7 +1454,7 @@ async function ueyeCapture(args = []) {
1435
1454
  async function ueyeCompare(args = []) {
1436
1455
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1437
1456
  return ueyeUsage();
1438
- const flags = parseSimpleFlags(args, new Set(['reference', 'ref', 'actual', 'screenshot', 'json', 'reference-id', 'screenshot-id']));
1457
+ const flags = parseSimpleFlags(args, new Set(['reference', 'ref', 'actual', 'screenshot', 'json', 'reference-id', 'screenshot-id', 'provider-context', 'provider-badge', 'execution-surface', 'app-surface', 'browser-surface', 'control-mode', 'preserved-state', 'preserved-url', 'url', 'viewport', 'evidence-id']));
1439
1458
  const reference = String(flags.reference || flags.ref || '');
1440
1459
  const actual = String(flags.actual || flags.screenshot || '');
1441
1460
  if (!reference || !actual) {
@@ -1455,12 +1474,35 @@ async function ueyeCompare(args = []) {
1455
1474
  const sameDimensions = referenceInfo.dimensions === actualInfo.dimensions;
1456
1475
  const comparisonResult = exactMatch ? 'matched' : 'different';
1457
1476
  const truthStatus = exactMatch ? 'verified' : 'partial';
1477
+ const surfaceContext = buildUeyeSurfaceContext({
1478
+ provider_context: String(flags.provider_context || 'local-file'),
1479
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'local-file'),
1480
+ execution_surface: String(flags.execution_surface || 'local-file-compare'),
1481
+ app_surface: String(flags.app_surface || 'local-project'),
1482
+ browser_surface: String(flags.browser_surface || 'not-used'),
1483
+ control_mode: String(flags.control_mode || 'local-compare'),
1484
+ route: 'ueye',
1485
+ mode: 'compare',
1486
+ url: String(flags.url || ''),
1487
+ viewport: String(flags.viewport || ''),
1488
+ screenshot_id: String(flags.screenshot_id || path.basename(actualPath)),
1489
+ evidence_id: String(flags.evidence_id || `ueye-compare-${timestampId()}`),
1490
+ preserved_state: Boolean(flags.preserved_state),
1491
+ preserved_url: String(flags.preserved_url || flags.url || ''),
1492
+ local_only: true,
1493
+ truth_status: truthStatus
1494
+ });
1458
1495
  const provenance = buildUeyeVisualProvenance({
1459
1496
  source_kind: 'implementation_screenshot',
1460
1497
  source_path: actualPath,
1461
1498
  source_hash: actualInfo.sha256,
1462
1499
  reference_id: String(flags.reference_id || path.basename(referencePath)),
1463
1500
  screenshot_id: String(flags.screenshot_id || path.basename(actualPath)),
1501
+ provider_context: surfaceContext.provider_context,
1502
+ provider_badge: surfaceContext.provider_badge,
1503
+ execution_surface: surfaceContext.execution_surface,
1504
+ app_surface: surfaceContext.app_surface,
1505
+ browser_surface: surfaceContext.browser_surface,
1464
1506
  local_only: true,
1465
1507
  redacted: false,
1466
1508
  operator_provided: false,
@@ -1475,6 +1517,7 @@ async function ueyeCompare(args = []) {
1475
1517
  same_dimensions: sameDimensions,
1476
1518
  reference: referenceInfo,
1477
1519
  actual: actualInfo,
1520
+ surface_context: surfaceContext,
1478
1521
  visual_evidence: `browser/local screenshot comparison executed: reference=${referencePath}, actual=${actualPath}, result=${comparisonResult}`,
1479
1522
  visual_provenance: provenance,
1480
1523
  proof_hint: `yam proof --route ueye --truth ${truthStatus} --visual "browser/local screenshot comparison executed: ${comparisonResult}" --visual-provenance '${JSON.stringify(provenance)}' --require-visual`
@@ -1496,7 +1539,7 @@ async function ueyeCompare(args = []) {
1496
1539
  async function ueyeReport(args = []) {
1497
1540
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h')
1498
1541
  return ueyeUsage();
1499
- 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', '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', 'json']));
1542
+ 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', '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']));
1500
1543
  const reference = String(flags.reference || flags.ref || '');
1501
1544
  const actual = String(flags.actual || flags.screenshot || '');
1502
1545
  const referenceSources = [];
@@ -1505,6 +1548,24 @@ async function ueyeReport(args = []) {
1505
1548
  let blockedReason = String(flags.blocked_reason || '');
1506
1549
  const reviewSessionId = String(flags.review_session_id || `ueye-${timestampId()}`);
1507
1550
  const previousReport = await readPreviousUeyeReport(flags.previous_report);
1551
+ const surfaceContext = buildUeyeSurfaceContext({
1552
+ provider_context: String(flags.provider_context || 'not-recorded'),
1553
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'not-recorded'),
1554
+ execution_surface: String(flags.execution_surface || flags.capture_backend || 'not-recorded'),
1555
+ app_surface: String(flags.app_surface || 'not-recorded'),
1556
+ browser_surface: String(flags.browser_surface || flags.capture_backend || 'not-recorded'),
1557
+ control_mode: String(flags.control_mode || 'not-recorded'),
1558
+ route: 'ueye',
1559
+ mode: 'report',
1560
+ url: String(flags.url || flags.preserved_url || ''),
1561
+ viewport: String(flags.viewport || ''),
1562
+ screenshot_id: String(flags.current_screenshot_id || flags.screenshot_id || ''),
1563
+ evidence_id: String(flags.evidence_id || reviewSessionId),
1564
+ preserved_state: Boolean(flags.preserved_state),
1565
+ preserved_url: String(flags.preserved_url || flags.url || ''),
1566
+ local_only: true,
1567
+ truth_status: actual ? 'partial' : 'assumed'
1568
+ });
1508
1569
  try {
1509
1570
  if (reference) {
1510
1571
  const info = await imageFileInfo(reference);
@@ -1513,6 +1574,11 @@ async function ueyeReport(args = []) {
1513
1574
  source_path: info.path,
1514
1575
  source_hash: info.sha256,
1515
1576
  reference_id: String(flags.reference_id || path.basename(info.path)),
1577
+ provider_context: surfaceContext.provider_context,
1578
+ provider_badge: surfaceContext.provider_badge,
1579
+ execution_surface: surfaceContext.execution_surface,
1580
+ app_surface: surfaceContext.app_surface,
1581
+ browser_surface: surfaceContext.browser_surface,
1516
1582
  local_only: true,
1517
1583
  operator_provided: true,
1518
1584
  comparison_result: 'reference-recorded',
@@ -1527,13 +1593,21 @@ async function ueyeReport(args = []) {
1527
1593
  source_hash: info.sha256,
1528
1594
  reference_id: String(flags.reference_id || referenceSources[0]?.reference_id || ''),
1529
1595
  screenshot_id: String(flags.current_screenshot_id || flags.screenshot_id || path.basename(info.path)),
1596
+ provider_context: surfaceContext.provider_context,
1597
+ provider_badge: surfaceContext.provider_badge,
1598
+ execution_surface: surfaceContext.execution_surface,
1599
+ app_surface: surfaceContext.app_surface,
1600
+ browser_surface: surfaceContext.browser_surface,
1530
1601
  local_only: true,
1531
1602
  operator_provided: false,
1532
1603
  comparison_result: 'implementation-recorded',
1533
1604
  truth_status: 'partial'
1534
1605
  }));
1535
1606
  }
1536
- if (reference && actual && referenceSources[0]?.source_hash === implementationSources[0]?.source_hash) {
1607
+ if (flags.comparison_result) {
1608
+ comparisonResult = normalizeUeyeComparisonResult(flags.comparison_result);
1609
+ }
1610
+ else if (reference && actual && referenceSources[0]?.source_hash === implementationSources[0]?.source_hash) {
1537
1611
  comparisonResult = 'matched';
1538
1612
  }
1539
1613
  else if (reference && actual) {
@@ -1546,8 +1620,29 @@ async function ueyeReport(args = []) {
1546
1620
  const report = buildUeyeRunReport({
1547
1621
  reference_sources: referenceSources,
1548
1622
  implementation_sources: implementationSources,
1623
+ surface_context: {
1624
+ ...surfaceContext,
1625
+ screenshot_id: surfaceContext.screenshot_id || String(flags.current_screenshot_id || flags.screenshot_id || implementationSources[0]?.screenshot_id || ''),
1626
+ truth_status: blockedReason ? 'blocked' : implementationSources.length ? 'partial' : 'assumed'
1627
+ },
1549
1628
  comparison_result: comparisonResult,
1550
1629
  design_quality: String(flags.design_quality || 'not-checked'),
1630
+ design_completion_gate: {
1631
+ mode: flags.strict || flags.gate_mode === 'strict' ? 'strict' : 'fast',
1632
+ completion_claim: flags.completion_claim || flags.completion_status || 'draft',
1633
+ design_score: numberOrNull(flags.design_score),
1634
+ min_design_score: numberOrNull(flags.min_design_score) ?? 8,
1635
+ p0: [...arrayFlag(flags.p0), ...arrayFlag(flags.open_p0)],
1636
+ p1: [...arrayFlag(flags.p1), ...arrayFlag(flags.open_p1)],
1637
+ states_checked: Boolean(flags.states_checked),
1638
+ mobile_checked: Boolean(flags.mobile_checked),
1639
+ responsive_checked: Boolean(flags.responsive_checked),
1640
+ contrast_checked: Boolean(flags.contrast_checked),
1641
+ accessibility_checked: Boolean(flags.accessibility_checked),
1642
+ cta_checked: Boolean(flags.cta_checked),
1643
+ direction_locked: Boolean(flags.direction_locked),
1644
+ reference_read: Boolean(flags.reference_read)
1645
+ },
1551
1646
  blocked_reason: blockedReason,
1552
1647
  next_action: String(flags.next_visual_action || flags.next_action || '')
1553
1648
  });
@@ -1556,6 +1651,8 @@ async function ueyeReport(args = []) {
1556
1651
  previousReport,
1557
1652
  referenceSources,
1558
1653
  implementationSources,
1654
+ surfaceContext: report.surface_context,
1655
+ designCompletionGate: report.design_completion_gate,
1559
1656
  comparisonResult,
1560
1657
  designQuality: String(flags.design_quality || 'not-checked'),
1561
1658
  similar: arrayFlag(flags.similar),
@@ -1609,7 +1706,7 @@ async function readPreviousUeyeReport(file = '') {
1609
1706
  };
1610
1707
  }
1611
1708
  }
1612
- function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceSources, implementationSources, comparisonResult, designQuality, similar, different, missing, notes, resolved, newFindings, stillOpen, regressions, previousScreenshotId, currentScreenshotId, viewport, state, nextAction, truthStatus }) {
1709
+ function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceSources, implementationSources, surfaceContext, designCompletionGate, comparisonResult, designQuality, similar, different, missing, notes, resolved, newFindings, stillOpen, regressions, previousScreenshotId, currentScreenshotId, viewport, state, nextAction, truthStatus }) {
1613
1710
  const hasReference = referenceSources.length > 0;
1614
1711
  const hasImplementation = implementationSources.length > 0;
1615
1712
  const continuityTruth = previousReport?.blocked_reason ? 'blocked' : previousReport ? 'partial' : 'assumed';
@@ -1633,6 +1730,8 @@ function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceS
1633
1730
  reference_count: referenceSources.length,
1634
1731
  implementation_count: implementationSources.length
1635
1732
  },
1733
+ surface_context: surfaceContext || buildUeyeSurfaceContext(),
1734
+ design_completion_gate: designCompletionGate || null,
1636
1735
  comparison_result: comparisonResult,
1637
1736
  design_quality: designQuality,
1638
1737
  similar,
@@ -1651,6 +1750,12 @@ function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceS
1651
1750
  truth_status: truthStatus
1652
1751
  };
1653
1752
  }
1753
+ function normalizeUeyeComparisonResult(value = '') {
1754
+ const text = String(value || '').toLowerCase();
1755
+ if (['matched', 'similar', 'different', 'not-verified', 'not-applicable'].includes(text))
1756
+ return text;
1757
+ return 'not-verified';
1758
+ }
1654
1759
  async function media(args = []) {
1655
1760
  const subcommand = args[0] || 'help';
1656
1761
  if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h')
@@ -1948,7 +2053,7 @@ function parseSimpleFlags(args = [], allowed = new Set()) {
1948
2053
  if (allowed.size && !allowed.has(key))
1949
2054
  continue;
1950
2055
  const normalizedKey = key.replace(/-/g, '_');
1951
- if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked'].includes(key)) {
2056
+ if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked', 'strict', 'preserved-state', 'states-checked', 'mobile-checked', 'responsive-checked', 'contrast-checked', 'accessibility-checked', 'cta-checked', 'direction-locked', 'reference-read'].includes(key)) {
1952
2057
  flags[normalizedKey] = true;
1953
2058
  continue;
1954
2059
  }
@@ -2105,6 +2210,7 @@ async function proof(args = []) {
2105
2210
  printProofList('Mission patch envelope', summary.missionEnvelope || []);
2106
2211
  printProofList('Rollback hint', summary.rollbackHint || []);
2107
2212
  printProofList('Media proof', summary.mediaProof || []);
2213
+ printProofList('Design completion', summary.designCompletion || []);
2108
2214
  printProofList('Changed surfaces', summary.changed);
2109
2215
  printProofList('Skipped', summary.skipped);
2110
2216
  printProofList('Blocked', summary.blocked);
@@ -2201,6 +2307,10 @@ function buildProofSummary(parsed, artifact) {
2201
2307
  ...structuredEvidenceList(artifact.mediaProof),
2202
2308
  ...structuredEvidenceList(parsed.flags.media_proof)
2203
2309
  ],
2310
+ designCompletion: [
2311
+ ...structuredEvidenceList(artifact.designCompletion),
2312
+ ...structuredEvidenceList(parsed.flags.design_completion)
2313
+ ],
2204
2314
  cleanup: parsed.flags.cleanup || artifact.cleanup || '',
2205
2315
  changed: [...artifact.changed, ...arrayFlag(parsed.flags.changed)],
2206
2316
  skipped: [...artifact.skipped, ...arrayFlag(parsed.flags.skipped)],
@@ -2265,6 +2375,9 @@ function renderProofMarkdown(summary) {
2265
2375
  '## Media proof',
2266
2376
  ...renderProofMarkdownList(summary.mediaProof || []),
2267
2377
  '',
2378
+ '## Design completion',
2379
+ ...renderProofMarkdownList(summary.designCompletion || []),
2380
+ '',
2268
2381
  '## Changed surfaces',
2269
2382
  ...renderProofMarkdownList(summary.changed),
2270
2383
  '',
@@ -2295,7 +2408,7 @@ function renderProofMarkdownList(values) {
2295
2408
  function parseProofArgs(args = []) {
2296
2409
  const flags = {};
2297
2410
  const positionals = [];
2298
- const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'runtime-backend', 'runtime-claim', 'runtime-evidence-id', 'runtime-command', 'cleanup-checked', 'runtime-note', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'media-proof', 'cleanup', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified', 'from', 'format', 'out', 'file', 'json', 'require-runtime', 'require-real-runtime', 'require-tmux', 'require-visual']);
2411
+ const aliases = new Set(['goal', 'route', 'truth', 'command', 'evidence', 'visual', 'runtime', 'runtime-backend', 'runtime-claim', 'runtime-evidence-id', 'runtime-command', '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']);
2299
2412
  for (let index = 0; index < args.length; index += 1) {
2300
2413
  const arg = args[index];
2301
2414
  if (arg.startsWith('--')) {
@@ -2311,7 +2424,7 @@ function parseProofArgs(args = []) {
2311
2424
  const value = inlineValue ?? args[index + 1] ?? '';
2312
2425
  if (inlineValue === undefined)
2313
2426
  index += 1;
2314
- if (['command', 'evidence', 'visual', 'runtime', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'media-proof', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
2427
+ 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)) {
2315
2428
  flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
2316
2429
  }
2317
2430
  else {
@@ -2355,6 +2468,7 @@ function emptyProofArtifact() {
2355
2468
  missionEnvelope: [],
2356
2469
  rollbackHint: [],
2357
2470
  mediaProof: [],
2471
+ designCompletion: [],
2358
2472
  cleanup: '',
2359
2473
  changed: [],
2360
2474
  skipped: [],
@@ -2381,6 +2495,7 @@ async function parseProofJson(file, empty) {
2381
2495
  missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
2382
2496
  rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
2383
2497
  mediaProof: structuredEvidenceList(data.mediaProof || data.media_proof),
2498
+ designCompletion: structuredEvidenceList(data.designCompletion || data.design_completion),
2384
2499
  cleanup: String(data.cleanup || data.cleanupStatus || ''),
2385
2500
  changed: arrayFlag(data.changed || data.files),
2386
2501
  skipped: arrayFlag(data.skipped),
@@ -2411,6 +2526,7 @@ async function parseProofMarkdown(file, empty) {
2411
2526
  missionEnvelope: readProofList(text, 'Mission patch envelope'),
2412
2527
  rollbackHint: readProofList(text, 'Rollback hint'),
2413
2528
  mediaProof: readProofList(text, 'Media proof'),
2529
+ designCompletion: readProofList(text, 'Design completion'),
2414
2530
  cleanup: readProofField(text, 'Cleanup') || readProofField(text, 'Cleanup status'),
2415
2531
  changed: firstProofList(text, ['Changed surfaces', 'Files']),
2416
2532
  skipped: readProofList(text, 'Skipped'),
@@ -2497,6 +2613,9 @@ function formatStructuredEvidence(value) {
2497
2613
  if (data.schema === 'yam.media-generation-proof.v1' || data.generation_requested || data.output_hash) {
2498
2614
  return JSON.stringify(buildMediaGenerationProof(data));
2499
2615
  }
2616
+ if (data.schema === 'yam.ueye-design-completion-gate.v1' || data.completion_claim || data.ready_to_claim_done !== undefined) {
2617
+ return JSON.stringify(buildUeyeDesignCompletionGate(data));
2618
+ }
2500
2619
  return JSON.stringify(data);
2501
2620
  }
2502
2621
  catch {
@@ -2505,7 +2624,7 @@ function formatStructuredEvidence(value) {
2505
2624
  }
2506
2625
  function hasProofEvidence(parsed) {
2507
2626
  const flags = parsed.flags || {};
2508
- return ['command', 'evidence', 'visual', 'runtime', 'runtime_backend_evidence', 'visual_provenance', 'mission_envelope', 'rollback_hint', 'media_proof', 'changed', 'skipped', 'blocked'].some((key) => arrayFlag(flags[key]).length > 0) || Boolean(flags.cleanup || flags.runtime_backend || flags.runtime_claim);
2627
+ 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);
2509
2628
  }
2510
2629
  function printProofList(label, values) {
2511
2630
  console.log(`- ${label}:`);
@@ -38,12 +38,58 @@ export interface UeyeVisualProvenance {
38
38
  source_hash: string;
39
39
  reference_id: string;
40
40
  screenshot_id: string;
41
+ provider_context: string;
42
+ provider_badge: string;
43
+ execution_surface: string;
44
+ app_surface: string;
45
+ browser_surface: string;
41
46
  local_only: boolean;
42
47
  redacted: boolean;
43
48
  operator_provided: boolean;
44
49
  comparison_result: string;
45
50
  truth_status: TruthStatus;
46
51
  }
52
+ export interface UeyeSurfaceContext {
53
+ schema: 'yam.ueye-surface-context.v1';
54
+ provider_context: string;
55
+ provider_badge: string;
56
+ execution_surface: string;
57
+ app_surface: string;
58
+ browser_surface: string;
59
+ control_mode: string;
60
+ route: string;
61
+ mode: string;
62
+ url: string;
63
+ viewport: string;
64
+ screenshot_id: string;
65
+ evidence_id: string;
66
+ preserved_state: boolean;
67
+ preserved_url: string;
68
+ local_only: boolean;
69
+ truth_status: TruthStatus;
70
+ }
71
+ export interface UeyeDesignGateCheck {
72
+ id: string;
73
+ label: string;
74
+ status: 'pass' | 'fail' | 'warning' | 'not_checked';
75
+ required: boolean;
76
+ next_action: string;
77
+ }
78
+ export interface UeyeDesignCompletionGate {
79
+ schema: 'yam.ueye-design-completion-gate.v1';
80
+ mode: 'fast' | 'strict';
81
+ completion_claim: 'draft' | 'needs-polish' | 'done';
82
+ ready_to_claim_done: boolean;
83
+ design_score: number | null;
84
+ min_design_score: number;
85
+ checks: UeyeDesignGateCheck[];
86
+ p0: string[];
87
+ p1: string[];
88
+ warnings: string[];
89
+ blockers: string[];
90
+ next_action: string;
91
+ truth_status: TruthStatus;
92
+ }
47
93
  export interface RollbackHint {
48
94
  schema: 'yam.rollback-hint.v1';
49
95
  touched_files: string[];
@@ -74,12 +120,17 @@ export interface UeyeRunReport {
74
120
  schema: 'yam.ueye-run-report.v1';
75
121
  reference_sources: UeyeVisualProvenance[];
76
122
  implementation_sources: UeyeVisualProvenance[];
123
+ surface_context: UeyeSurfaceContext;
124
+ design_completion_gate: UeyeDesignCompletionGate;
77
125
  comparison_result: string;
78
126
  design_quality: string;
79
127
  blocked_reason: string;
80
128
  next_action: string;
81
129
  truth_status: TruthStatus;
82
130
  }
131
+ export interface UeyeRunReportInput extends Partial<Omit<UeyeRunReport, 'design_completion_gate'>> {
132
+ design_completion_gate?: Partial<UeyeDesignCompletionGate> & Record<string, unknown>;
133
+ }
83
134
  export interface MediaGenerationProof {
84
135
  schema: 'yam.media-generation-proof.v1';
85
136
  tool_name: string;
@@ -120,6 +171,7 @@ export interface ProofSummary {
120
171
  rollbackHint?: unknown;
121
172
  runtimeBackendEvidence?: unknown;
122
173
  mediaProof?: unknown;
174
+ designCompletion?: unknown;
123
175
  }
124
176
  export interface ProofOptions {
125
177
  requireRealRuntime?: boolean;
@@ -152,6 +204,7 @@ export interface YamCompletionProof {
152
204
  rollbackHint: string[];
153
205
  runtimeBackendEvidence: string[];
154
206
  mediaProof: string[];
207
+ designCompletion: string[];
155
208
  cleanup: unknown;
156
209
  changed: string[];
157
210
  skipped: string[];
@@ -164,10 +217,12 @@ export interface YamCompletionProof {
164
217
  runtimeTruth: RuntimeTruthMatrix;
165
218
  }
166
219
  export declare function buildUeyeVisualProvenance(input?: Partial<UeyeVisualProvenance>): UeyeVisualProvenance;
220
+ export declare function buildUeyeSurfaceContext(input?: Partial<UeyeSurfaceContext>): UeyeSurfaceContext;
221
+ export declare function buildUeyeDesignCompletionGate(input?: Partial<UeyeDesignCompletionGate> & Record<string, unknown>): UeyeDesignCompletionGate;
167
222
  export declare function buildRollbackHint(input?: Partial<RollbackHint>): RollbackHint;
168
223
  export declare function buildMissionPatchEnvelope(input?: Partial<MissionPatchEnvelope>): MissionPatchEnvelope;
169
224
  export declare function buildRuntimeBackendEvidence(input?: Partial<RuntimeBackendEvidence>): RuntimeBackendEvidence;
170
- export declare function buildUeyeRunReport(input?: Partial<UeyeRunReport>): UeyeRunReport;
225
+ export declare function buildUeyeRunReport(input?: UeyeRunReportInput): UeyeRunReport;
171
226
  export declare function buildMediaGenerationProof(input?: Partial<MediaGenerationProof>): MediaGenerationProof;
172
227
  export declare function isTruthStatus(value?: unknown): value is TruthStatus;
173
228
  export declare function weakestTruth(...statuses: unknown[]): TruthStatus;