yam-flow 0.1.4 → 0.1.5

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,19 @@
2
2
 
3
3
  All notable changes to yam-flow are documented here.
4
4
 
5
+ ## v0.1.5 - 2026-06-04
6
+
7
+ ### Added
8
+
9
+ - 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.
10
+ - 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.
11
+ - Added provider and execution surface fields to Ueye visual provenance records.
12
+
13
+ ### Improved
14
+
15
+ - Strengthened `yam ueye report` for fast UI state preservation without making capture or compare an always-on gate.
16
+ - Updated Ueye docs and templates so visual proof can distinguish source files, local screenshots, in-app browser checks, and other execution surfaces.
17
+
5
18
  ## v0.1.4 - 2026-06-03
6
19
 
7
20
  ### Added
package/COMMANDS.md CHANGED
@@ -44,7 +44,7 @@ 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
48
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
49
49
  yam proof --route ueye --truth partial --visual "browser/local screenshot comparison executed" --require-visual
50
50
  ```
@@ -149,7 +149,7 @@ yam proof --route ueye --truth verified --visual "reference image only"
149
149
  yam proof --route ueye --truth partial --visual-provenance '{"source_kind":"reference","source_hash":"unknown","comparison_result":"not-verified","truth_status":"partial"}'
150
150
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
151
151
  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
152
+ 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
153
153
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
154
154
  yam runtime evidence --backend terminal --claim observed --evidence-id dev-server-1 --command "npm run dev" --json
155
155
  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,7 @@ 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 --design-quality needs-polish --provider-context local --execution-surface in-app-browser --app-surface codex-app --browser-surface in-app-browser --preserved-state --json
133
133
  yam media proof --requested --attempted --output ./generated.png --wait-loop --json
134
134
  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
135
  yam release report --json
@@ -177,6 +177,7 @@ Publishing still requires confirming the final npm package name and account acce
177
177
  - runtime truth matrix
178
178
  - visual evidence caps
179
179
  - Ueye visual run reports
180
+ - Ueye surface context metadata
180
181
  - runtime backend evidence
181
182
  - runtime evidence mini summaries
182
183
  - mission patch queue lite records
@@ -197,6 +198,7 @@ These shapes keep reports machine-readable without turning normal work into a he
197
198
  - 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
199
  - 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
200
  - Ueye review continuity/comparison report: `$ueye` run reports can link a previous review, current evidence, comparison delta, design quality result, and next action.
201
+ - 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.
200
202
 
201
203
  ## Ueye Capture And Compare
202
204
 
@@ -205,14 +207,14 @@ These shapes keep reports machine-readable without turning normal work into a he
205
207
  ```bash
206
208
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
207
209
  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
210
+ 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 --preserved-state --json
209
211
  ```
210
212
 
211
213
  `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
214
 
213
215
  `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
216
 
215
- `report` gathers reference sources, implementation screenshots, comparison result, continuity fields, design quality judgment, and next action into one compact proof-ready JSON object.
217
+ `report` gathers reference sources, implementation screenshots, comparison result, continuity fields, design quality judgment, surface context, and next action into one compact proof-ready JSON object.
216
218
 
217
219
  Generated media can guide visual direction, but it cannot prove the implemented UI by itself:
218
220
 
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, 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,7 +1366,7 @@ 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] [--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.
@@ -1377,7 +1377,7 @@ Notes:
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', '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,6 +1593,11 @@ 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',
@@ -1546,6 +1617,11 @@ async function ueyeReport(args = []) {
1546
1617
  const report = buildUeyeRunReport({
1547
1618
  reference_sources: referenceSources,
1548
1619
  implementation_sources: implementationSources,
1620
+ surface_context: {
1621
+ ...surfaceContext,
1622
+ screenshot_id: surfaceContext.screenshot_id || String(flags.current_screenshot_id || flags.screenshot_id || implementationSources[0]?.screenshot_id || ''),
1623
+ truth_status: blockedReason ? 'blocked' : implementationSources.length ? 'partial' : 'assumed'
1624
+ },
1549
1625
  comparison_result: comparisonResult,
1550
1626
  design_quality: String(flags.design_quality || 'not-checked'),
1551
1627
  blocked_reason: blockedReason,
@@ -1556,6 +1632,7 @@ async function ueyeReport(args = []) {
1556
1632
  previousReport,
1557
1633
  referenceSources,
1558
1634
  implementationSources,
1635
+ surfaceContext: report.surface_context,
1559
1636
  comparisonResult,
1560
1637
  designQuality: String(flags.design_quality || 'not-checked'),
1561
1638
  similar: arrayFlag(flags.similar),
@@ -1609,7 +1686,7 @@ async function readPreviousUeyeReport(file = '') {
1609
1686
  };
1610
1687
  }
1611
1688
  }
1612
- function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceSources, implementationSources, comparisonResult, designQuality, similar, different, missing, notes, resolved, newFindings, stillOpen, regressions, previousScreenshotId, currentScreenshotId, viewport, state, nextAction, truthStatus }) {
1689
+ function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceSources, implementationSources, surfaceContext, comparisonResult, designQuality, similar, different, missing, notes, resolved, newFindings, stillOpen, regressions, previousScreenshotId, currentScreenshotId, viewport, state, nextAction, truthStatus }) {
1613
1690
  const hasReference = referenceSources.length > 0;
1614
1691
  const hasImplementation = implementationSources.length > 0;
1615
1692
  const continuityTruth = previousReport?.blocked_reason ? 'blocked' : previousReport ? 'partial' : 'assumed';
@@ -1633,6 +1710,7 @@ function buildUeyeComparisonReport({ reviewSessionId, previousReport, referenceS
1633
1710
  reference_count: referenceSources.length,
1634
1711
  implementation_count: implementationSources.length
1635
1712
  },
1713
+ surface_context: surfaceContext || buildUeyeSurfaceContext(),
1636
1714
  comparison_result: comparisonResult,
1637
1715
  design_quality: designQuality,
1638
1716
  similar,
@@ -38,12 +38,36 @@ 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
+ }
47
71
  export interface RollbackHint {
48
72
  schema: 'yam.rollback-hint.v1';
49
73
  touched_files: string[];
@@ -74,6 +98,7 @@ export interface UeyeRunReport {
74
98
  schema: 'yam.ueye-run-report.v1';
75
99
  reference_sources: UeyeVisualProvenance[];
76
100
  implementation_sources: UeyeVisualProvenance[];
101
+ surface_context: UeyeSurfaceContext;
77
102
  comparison_result: string;
78
103
  design_quality: string;
79
104
  blocked_reason: string;
@@ -164,6 +189,7 @@ export interface YamCompletionProof {
164
189
  runtimeTruth: RuntimeTruthMatrix;
165
190
  }
166
191
  export declare function buildUeyeVisualProvenance(input?: Partial<UeyeVisualProvenance>): UeyeVisualProvenance;
192
+ export declare function buildUeyeSurfaceContext(input?: Partial<UeyeSurfaceContext>): UeyeSurfaceContext;
167
193
  export declare function buildRollbackHint(input?: Partial<RollbackHint>): RollbackHint;
168
194
  export declare function buildMissionPatchEnvelope(input?: Partial<MissionPatchEnvelope>): MissionPatchEnvelope;
169
195
  export declare function buildRuntimeBackendEvidence(input?: Partial<RuntimeBackendEvidence>): RuntimeBackendEvidence;
@@ -19,6 +19,11 @@ export function buildUeyeVisualProvenance(input = {}) {
19
19
  source_hash: String(input.source_hash || 'unknown'),
20
20
  reference_id: String(input.reference_id || ''),
21
21
  screenshot_id: String(input.screenshot_id || ''),
22
+ provider_context: String(input.provider_context || 'not-recorded'),
23
+ provider_badge: String(input.provider_badge || 'not-recorded'),
24
+ execution_surface: String(input.execution_surface || 'not-recorded'),
25
+ app_surface: String(input.app_surface || 'not-recorded'),
26
+ browser_surface: String(input.browser_surface || 'not-recorded'),
22
27
  local_only: Boolean(input.local_only),
23
28
  redacted: Boolean(input.redacted),
24
29
  operator_provided: Boolean(input.operator_provided),
@@ -26,6 +31,27 @@ export function buildUeyeVisualProvenance(input = {}) {
26
31
  truth_status: truth
27
32
  };
28
33
  }
34
+ export function buildUeyeSurfaceContext(input = {}) {
35
+ return {
36
+ schema: 'yam.ueye-surface-context.v1',
37
+ provider_context: String(input.provider_context || 'not-recorded'),
38
+ provider_badge: String(input.provider_badge || input.provider_context || 'not-recorded'),
39
+ execution_surface: String(input.execution_surface || 'not-recorded'),
40
+ app_surface: String(input.app_surface || 'not-recorded'),
41
+ browser_surface: String(input.browser_surface || 'not-recorded'),
42
+ control_mode: String(input.control_mode || 'not-recorded'),
43
+ route: String(input.route || 'ueye'),
44
+ mode: String(input.mode || 'report'),
45
+ url: String(input.url || ''),
46
+ viewport: String(input.viewport || ''),
47
+ screenshot_id: String(input.screenshot_id || ''),
48
+ evidence_id: String(input.evidence_id || ''),
49
+ preserved_state: Boolean(input.preserved_state),
50
+ preserved_url: String(input.preserved_url || ''),
51
+ local_only: input.local_only !== undefined ? Boolean(input.local_only) : true,
52
+ truth_status: isTruthStatus(input.truth_status) ? input.truth_status : 'partial'
53
+ };
54
+ }
29
55
  export function buildRollbackHint(input = {}) {
30
56
  return {
31
57
  schema: 'yam.rollback-hint.v1',
@@ -82,6 +108,10 @@ export function buildUeyeRunReport(input = {}) {
82
108
  schema: 'yam.ueye-run-report.v1',
83
109
  reference_sources: references,
84
110
  implementation_sources: implementations,
111
+ surface_context: buildUeyeSurfaceContext({
112
+ ...input.surface_context,
113
+ truth_status: isTruthStatus(input.surface_context?.truth_status) ? input.surface_context?.truth_status : derivedTruth
114
+ }),
85
115
  comparison_result: comparisonResult,
86
116
  design_quality: String(input.design_quality || 'not-checked'),
87
117
  blocked_reason: blockedReason,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam-flow",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Progressive proof-first agent flow: start fast, deepen deliberately, stay honest by design.",
5
5
  "type": "module",
6
6
  "author": "0kim0bos",
@@ -26,12 +26,12 @@ Ueye is one route. It should not split into separate lite/deep skills. Use these
26
26
  ```bash
27
27
  yam ueye capture --url http://localhost:3000 --out .yam/screens/home.png
28
28
  yam ueye compare --reference ./reference.png --actual .yam/screens/home.png --json
29
- yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality needs-polish --json
29
+ yam ueye report --reference ./reference.png --actual .yam/screens/home.png --design-quality needs-polish --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --preserved-state --json
30
30
  ```
31
31
 
32
32
  - `capture` uses a project-local capture backend when present, then reports `blocked` instead of installing dependencies or pretending a screenshot exists.
33
33
  - `compare` is local-only and dependency-free. It records hashes, dimensions, comparison result, and visual provenance. Different screenshots stay `partial`; exact file matches can be `verified`.
34
- - `report` records the Ueye run as reference sources, implementation sources, comparison result, design quality, blocked reason, next action, and truth status.
34
+ - `report` records the Ueye run as reference sources, implementation sources, comparison result, surface context, design quality, blocked reason, next action, and truth status.
35
35
  - `report` may include continuity fields when a previous review or screenshot is being rechecked.
36
36
  - Without capture or user-provided screenshots, report implementation/source review separately from visual verification and cap the visual claim.
37
37
 
@@ -93,6 +93,11 @@ Record these fields when a reference or screenshot materially affects the claim:
93
93
  - `source_hash`
94
94
  - `reference_id`
95
95
  - `screenshot_id`
96
+ - `provider_context`
97
+ - `provider_badge`
98
+ - `execution_surface`
99
+ - `app_surface`
100
+ - `browser_surface`
96
101
  - `viewport`
97
102
  - `state`
98
103
  - `local_only`
@@ -103,6 +108,28 @@ Record these fields when a reference or screenshot materially affects the claim:
103
108
 
104
109
  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.
105
110
 
111
+ ### Surface Context
112
+
113
+ Purpose: preserve where and how a visual claim was observed without turning Ueye into an always-on capture gate.
114
+
115
+ Record these fields when they affect the visual claim:
116
+
117
+ - `provider_context`
118
+ - `provider_badge`
119
+ - `execution_surface`
120
+ - `app_surface`
121
+ - `browser_surface`
122
+ - `control_mode`
123
+ - `url`
124
+ - `viewport`
125
+ - `screenshot_id`
126
+ - `evidence_id`
127
+ - `preserved_state`
128
+ - `preserved_url`
129
+ - `truth_status`
130
+
131
+ Use `not-recorded` when the field was not observed. Prefer `partial` over `verified` when surface context exists but no actual implementation screenshot was captured or supplied.
132
+
106
133
  ### Reference Read Proof
107
134
 
108
135
  Purpose: separate reading the reference from judging the implementation.
@@ -164,7 +164,23 @@ Record:
164
164
  - next visual action.
165
165
  - truth status.
166
166
 
167
- Use `yam ueye report --previous-report previous.json --review-session-id current-pass --similar "..." --different "..." --missing "..." --json` when you need a compact continuity record.
167
+ Use `yam ueye report --previous-report previous.json --review-session-id current-pass --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --similar "..." --different "..." --missing "..." --json` when you need a compact continuity record.
168
+
169
+ ### Surface Context
170
+
171
+ Use surface context when a Ueye claim depends on preserving the current app/browser state or explaining where visual evidence came from.
172
+
173
+ Fields:
174
+
175
+ - provider context and badge.
176
+ - execution surface.
177
+ - app surface.
178
+ - browser surface.
179
+ - control mode.
180
+ - preserved URL/state.
181
+ - evidence id.
182
+
183
+ Keep this metadata descriptive. It should support honest visual claims, not force a heavy browser workflow for every Ueye run.
168
184
 
169
185
  ## P0-P3 Ledger
170
186
 
@@ -38,6 +38,7 @@ Do not use for:
38
38
  - Keep Ueye as one route: start fast, then opt into `yam ueye capture` and `yam ueye compare` only when a `verified` visual claim needs real screen evidence.
39
39
  - Use `yam ueye report` when reference, implementation screenshot, comparison result, design quality, and next action should be recorded as one compact run report.
40
40
  - For follow-up visual reviews, keep continuity explicit: previous report/source, current report/source, comparison delta, resolved findings, new findings, and next action.
41
+ - Record surface context when it matters: provider context, provider badge, execution surface, app surface, browser surface, control mode, preserved URL/state, and evidence id.
41
42
  - Generated media can guide direction, but it cannot verify the implemented screen without implementation screenshot evidence.
42
43
  - Prefer the in-app browser for interactive visual checks. Do not switch to a profile-dependent browser unless the user explicitly asks or the task requires existing cookies, sessions, extensions, or profile state.
43
44
 
@@ -61,7 +62,7 @@ Do not use for:
61
62
  - Do not silently use a profile-dependent browser as a fallback. If the in-app browser cannot capture or inspect, report the visual cap as `partial` or `blocked`.
62
63
  - Use `yam ueye capture --url URL --out path.png` when a project-local capture backend is available and the claim needs real browser evidence.
63
64
  - Use `yam ueye compare --reference ref.png --actual shot.png` when reference fidelity needs a proof-ready local comparison record.
64
- - Use `yam ueye report --reference ref.png --actual shot.png --design-quality needs-polish --json` to preserve the run-level visual provenance and next action.
65
+ - Use `yam ueye report --reference ref.png --actual shot.png --design-quality needs-polish --provider-context local --execution-surface in-app-browser --browser-surface in-app-browser --json` to preserve the run-level visual provenance, surface context, and next action.
65
66
  - When continuing a prior review, include previous/current report or screenshot identifiers and mark the comparison as improved, regressed, unchanged, or not-verified.
66
67
  9. Compare reference and implementation when reference fidelity was requested:
67
68
  - matched, similar, different, not-applicable, or not-verified.
@@ -91,18 +92,21 @@ Use these artifacts for reference-led implementation or serious visual review. K
91
92
  2. Visual Provenance:
92
93
  - source kind, path, hash, reference id, screenshot id, redaction/locality flags, operator-provided flag, comparison result, and truth status.
93
94
  - use this to prove the route from reference reading to implementation recheck.
94
- 3. Reference Read Proof:
95
+ 3. Surface Context:
96
+ - provider context, provider badge, execution surface, app surface, browser surface, control mode, preserved URL/state, evidence id, and truth status.
97
+ - use this to explain where and how visual evidence was observed without forcing capture for every Ueye run.
98
+ 4. Reference Read Proof:
95
99
  - concrete observations from the reference before implementation.
96
100
  - no implementation claim should depend on an unrecorded reference observation.
97
- 4. Reference vs Implementation Matrix:
101
+ 5. Reference vs Implementation Matrix:
98
102
  - compare the implemented result against the reference by aspect.
99
103
  - record matched, similar, different, not-applicable, or not-verified.
100
- 5. Design Quality Review:
104
+ 6. Design Quality Review:
101
105
  - judge whether the result is good UI/UX/design after reference comparison.
102
106
  - use pass, needs-polish, or fails for each relevant design dimension.
103
- 6. Review Continuity and Comparison Report, when this is a follow-up review:
107
+ 7. Review Continuity and Comparison Report, when this is a follow-up review:
104
108
  - record previous source/report, current source/report, comparison delta, resolved findings, new findings, still-open findings, truth status, and next action.
105
- 7. Media Generation Proof, when generated images are part of the task:
109
+ 8. Media Generation Proof, when generated images are part of the task:
106
110
  - record whether generation was requested, attempted, blocked, or produced an output.
107
111
  - use `yam media proof --requested --attempted --output path.png --wait-loop --json` when a generated image materially affects the design direction.
108
112
  - never use generated media proof as a substitute for implemented screen evidence.
package/src/bin/yam.ts CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  buildRuntimeBackendEvidence,
17
17
  buildUeyeVisualProvenance,
18
18
  buildUeyeRunReport,
19
+ buildUeyeSurfaceContext,
19
20
  buildYamCompletionProof,
20
21
  detectDbSafetyText as detectTrustDbSafetyText,
21
22
  isTruthStatus
@@ -139,7 +140,7 @@ Usage:
139
140
  yam proof write [dir] [--format json|md] [--out file] [--route route] [--truth status] [--command text]
140
141
  yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
141
142
  yam ueye compare --reference ref.png --actual screenshot.png [--json]
142
- yam ueye report [--reference ref.png] [--actual screenshot.png] [--json]
143
+ yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
143
144
  yam media proof [--requested] [--attempted] [--output file] [--json]
144
145
  yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
145
146
  yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
@@ -808,7 +809,7 @@ function toolsDoctorNextActionDetails({ pack, rows, riskNotes, frameworkChecklis
808
809
  if (rows.some((row) => row.name === 'Yam skills' && row.status !== 'ready')) actions.push(nextActionDetail('install-skills', 'warning', 'skill install state is incomplete', 'run `yam install` and restart Codex before relying on installed skills', 'yam install'));
809
810
  if (riskNotes.some((note) => note.level === 'danger')) actions.push(nextActionDetail('deep-safety', 'error', 'destructive database or production write signal detected', 'use $deep before claiming destructive database or production write safety', 'yam safety "<command or SQL>"'));
810
811
  if (frameworkChecklist?.detected && !commands?.build && !commands?.typecheck) actions.push(nextActionDetail('verification-command', 'warning', 'build/typecheck command not detected', 'add or document a small build/typecheck command for implementation verification', 'yam pack .'));
811
- if (frameworkChecklist?.uiLike) 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'));
812
+ if (frameworkChecklist?.uiLike) 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
813
  return uniqueNextActionDetails(actions);
813
814
  }
814
815
 
@@ -1348,7 +1349,7 @@ Opt-in visual evidence helpers. Ueye stays one skill: fast by default, capture/c
1348
1349
  Usage:
1349
1350
  yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
1350
1351
  yam ueye compare --reference ref.png --actual screenshot.png [--json]
1351
- 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]
1352
+ 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] [--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]
1352
1353
 
1353
1354
  Notes:
1354
1355
  capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
@@ -1359,7 +1360,7 @@ Notes:
1359
1360
 
1360
1361
  async function ueyeCapture(args = []) {
1361
1362
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
1362
- const flags = parseSimpleFlags(args, new Set(['url', 'out', 'viewport', 'wait-until', 'timeout', 'full-page', 'json']));
1363
+ 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']));
1363
1364
  const url = String(flags.url || '');
1364
1365
  const out = String(flags.out || '');
1365
1366
  if (!url || !out) {
@@ -1384,6 +1385,24 @@ async function ueyeCapture(args = []) {
1384
1385
  await page.goto(url, { waitUntil, timeout });
1385
1386
  await page.screenshot({ path: target, fullPage: Boolean(flags.full_page) });
1386
1387
  const info = await imageFileInfo(target);
1388
+ const surfaceContext = buildUeyeSurfaceContext({
1389
+ provider_context: String(flags.provider_context || 'local-playwright'),
1390
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'local-playwright'),
1391
+ execution_surface: String(flags.execution_surface || 'browser-capture'),
1392
+ app_surface: String(flags.app_surface || 'local-project'),
1393
+ browser_surface: String(flags.browser_surface || 'playwright'),
1394
+ control_mode: String(flags.control_mode || 'automated-capture'),
1395
+ route: 'ueye',
1396
+ mode: 'capture',
1397
+ url,
1398
+ viewport: `${viewport.width}x${viewport.height}`,
1399
+ screenshot_id: String(flags.screenshot_id || path.basename(target)),
1400
+ evidence_id: String(flags.evidence_id || `ueye-capture-${timestampId()}`),
1401
+ preserved_state: Boolean(flags.preserved_state),
1402
+ preserved_url: String(flags.preserved_url || url),
1403
+ local_only: true,
1404
+ truth_status: 'verified'
1405
+ });
1387
1406
  const result = {
1388
1407
  schema: 'yam.ueye-capture.v1',
1389
1408
  status: 'verified',
@@ -1393,6 +1412,7 @@ async function ueyeCapture(args = []) {
1393
1412
  full_page: Boolean(flags.full_page),
1394
1413
  sha256: info.sha256,
1395
1414
  dimensions: info.dimensions,
1415
+ surface_context: surfaceContext,
1396
1416
  visual_evidence: `browser screenshot captured: ${target} (${info.dimensions}, sha256:${info.sha256})`
1397
1417
  };
1398
1418
  printJsonOrHuman(result, Boolean(flags.json), 'Ueye capture');
@@ -1415,7 +1435,7 @@ async function ueyeCapture(args = []) {
1415
1435
 
1416
1436
  async function ueyeCompare(args = []) {
1417
1437
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
1418
- const flags = parseSimpleFlags(args, new Set(['reference', 'ref', 'actual', 'screenshot', 'json', 'reference-id', 'screenshot-id']));
1438
+ 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']));
1419
1439
  const reference = String(flags.reference || flags.ref || '');
1420
1440
  const actual = String(flags.actual || flags.screenshot || '');
1421
1441
  if (!reference || !actual) {
@@ -1436,12 +1456,35 @@ async function ueyeCompare(args = []) {
1436
1456
  const sameDimensions = referenceInfo.dimensions === actualInfo.dimensions;
1437
1457
  const comparisonResult = exactMatch ? 'matched' : 'different';
1438
1458
  const truthStatus = exactMatch ? 'verified' : 'partial';
1459
+ const surfaceContext = buildUeyeSurfaceContext({
1460
+ provider_context: String(flags.provider_context || 'local-file'),
1461
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'local-file'),
1462
+ execution_surface: String(flags.execution_surface || 'local-file-compare'),
1463
+ app_surface: String(flags.app_surface || 'local-project'),
1464
+ browser_surface: String(flags.browser_surface || 'not-used'),
1465
+ control_mode: String(flags.control_mode || 'local-compare'),
1466
+ route: 'ueye',
1467
+ mode: 'compare',
1468
+ url: String(flags.url || ''),
1469
+ viewport: String(flags.viewport || ''),
1470
+ screenshot_id: String(flags.screenshot_id || path.basename(actualPath)),
1471
+ evidence_id: String(flags.evidence_id || `ueye-compare-${timestampId()}`),
1472
+ preserved_state: Boolean(flags.preserved_state),
1473
+ preserved_url: String(flags.preserved_url || flags.url || ''),
1474
+ local_only: true,
1475
+ truth_status: truthStatus
1476
+ });
1439
1477
  const provenance = buildUeyeVisualProvenance({
1440
1478
  source_kind: 'implementation_screenshot',
1441
1479
  source_path: actualPath,
1442
1480
  source_hash: actualInfo.sha256,
1443
1481
  reference_id: String(flags.reference_id || path.basename(referencePath)),
1444
1482
  screenshot_id: String(flags.screenshot_id || path.basename(actualPath)),
1483
+ provider_context: surfaceContext.provider_context,
1484
+ provider_badge: surfaceContext.provider_badge,
1485
+ execution_surface: surfaceContext.execution_surface,
1486
+ app_surface: surfaceContext.app_surface,
1487
+ browser_surface: surfaceContext.browser_surface,
1445
1488
  local_only: true,
1446
1489
  redacted: false,
1447
1490
  operator_provided: false,
@@ -1456,6 +1499,7 @@ async function ueyeCompare(args = []) {
1456
1499
  same_dimensions: sameDimensions,
1457
1500
  reference: referenceInfo,
1458
1501
  actual: actualInfo,
1502
+ surface_context: surfaceContext,
1459
1503
  visual_evidence: `browser/local screenshot comparison executed: reference=${referencePath}, actual=${actualPath}, result=${comparisonResult}`,
1460
1504
  visual_provenance: provenance,
1461
1505
  proof_hint: `yam proof --route ueye --truth ${truthStatus} --visual "browser/local screenshot comparison executed: ${comparisonResult}" --visual-provenance '${JSON.stringify(provenance)}' --require-visual`
@@ -1476,7 +1520,7 @@ async function ueyeCompare(args = []) {
1476
1520
 
1477
1521
  async function ueyeReport(args = []) {
1478
1522
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
1479
- 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']));
1523
+ 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', 'json']));
1480
1524
  const reference = String(flags.reference || flags.ref || '');
1481
1525
  const actual = String(flags.actual || flags.screenshot || '');
1482
1526
  const referenceSources = [];
@@ -1485,6 +1529,24 @@ async function ueyeReport(args = []) {
1485
1529
  let blockedReason = String(flags.blocked_reason || '');
1486
1530
  const reviewSessionId = String(flags.review_session_id || `ueye-${timestampId()}`);
1487
1531
  const previousReport = await readPreviousUeyeReport(flags.previous_report);
1532
+ const surfaceContext = buildUeyeSurfaceContext({
1533
+ provider_context: String(flags.provider_context || 'not-recorded'),
1534
+ provider_badge: String(flags.provider_badge || flags.provider_context || 'not-recorded'),
1535
+ execution_surface: String(flags.execution_surface || flags.capture_backend || 'not-recorded'),
1536
+ app_surface: String(flags.app_surface || 'not-recorded'),
1537
+ browser_surface: String(flags.browser_surface || flags.capture_backend || 'not-recorded'),
1538
+ control_mode: String(flags.control_mode || 'not-recorded'),
1539
+ route: 'ueye',
1540
+ mode: 'report',
1541
+ url: String(flags.url || flags.preserved_url || ''),
1542
+ viewport: String(flags.viewport || ''),
1543
+ screenshot_id: String(flags.current_screenshot_id || flags.screenshot_id || ''),
1544
+ evidence_id: String(flags.evidence_id || reviewSessionId),
1545
+ preserved_state: Boolean(flags.preserved_state),
1546
+ preserved_url: String(flags.preserved_url || flags.url || ''),
1547
+ local_only: true,
1548
+ truth_status: actual ? 'partial' : 'assumed'
1549
+ });
1488
1550
 
1489
1551
  try {
1490
1552
  if (reference) {
@@ -1494,6 +1556,11 @@ async function ueyeReport(args = []) {
1494
1556
  source_path: info.path,
1495
1557
  source_hash: info.sha256,
1496
1558
  reference_id: String(flags.reference_id || path.basename(info.path)),
1559
+ provider_context: surfaceContext.provider_context,
1560
+ provider_badge: surfaceContext.provider_badge,
1561
+ execution_surface: surfaceContext.execution_surface,
1562
+ app_surface: surfaceContext.app_surface,
1563
+ browser_surface: surfaceContext.browser_surface,
1497
1564
  local_only: true,
1498
1565
  operator_provided: true,
1499
1566
  comparison_result: 'reference-recorded',
@@ -1508,6 +1575,11 @@ async function ueyeReport(args = []) {
1508
1575
  source_hash: info.sha256,
1509
1576
  reference_id: String(flags.reference_id || referenceSources[0]?.reference_id || ''),
1510
1577
  screenshot_id: String(flags.current_screenshot_id || flags.screenshot_id || path.basename(info.path)),
1578
+ provider_context: surfaceContext.provider_context,
1579
+ provider_badge: surfaceContext.provider_badge,
1580
+ execution_surface: surfaceContext.execution_surface,
1581
+ app_surface: surfaceContext.app_surface,
1582
+ browser_surface: surfaceContext.browser_surface,
1511
1583
  local_only: true,
1512
1584
  operator_provided: false,
1513
1585
  comparison_result: 'implementation-recorded',
@@ -1526,6 +1598,11 @@ async function ueyeReport(args = []) {
1526
1598
  const report = buildUeyeRunReport({
1527
1599
  reference_sources: referenceSources,
1528
1600
  implementation_sources: implementationSources,
1601
+ surface_context: {
1602
+ ...surfaceContext,
1603
+ screenshot_id: surfaceContext.screenshot_id || String(flags.current_screenshot_id || flags.screenshot_id || implementationSources[0]?.screenshot_id || ''),
1604
+ truth_status: blockedReason ? 'blocked' : implementationSources.length ? 'partial' : 'assumed'
1605
+ },
1529
1606
  comparison_result: comparisonResult,
1530
1607
  design_quality: String(flags.design_quality || 'not-checked'),
1531
1608
  blocked_reason: blockedReason,
@@ -1536,6 +1613,7 @@ async function ueyeReport(args = []) {
1536
1613
  previousReport,
1537
1614
  referenceSources,
1538
1615
  implementationSources,
1616
+ surfaceContext: report.surface_context,
1539
1617
  comparisonResult,
1540
1618
  designQuality: String(flags.design_quality || 'not-checked'),
1541
1619
  similar: arrayFlag(flags.similar),
@@ -1593,6 +1671,7 @@ function buildUeyeComparisonReport({
1593
1671
  previousReport,
1594
1672
  referenceSources,
1595
1673
  implementationSources,
1674
+ surfaceContext,
1596
1675
  comparisonResult,
1597
1676
  designQuality,
1598
1677
  similar,
@@ -1633,6 +1712,7 @@ function buildUeyeComparisonReport({
1633
1712
  reference_count: referenceSources.length,
1634
1713
  implementation_count: implementationSources.length
1635
1714
  },
1715
+ surface_context: surfaceContext || buildUeyeSurfaceContext(),
1636
1716
  comparison_result: comparisonResult,
1637
1717
  design_quality: designQuality,
1638
1718
  similar,
@@ -56,6 +56,11 @@ export interface UeyeVisualProvenance {
56
56
  source_hash: string;
57
57
  reference_id: string;
58
58
  screenshot_id: string;
59
+ provider_context: string;
60
+ provider_badge: string;
61
+ execution_surface: string;
62
+ app_surface: string;
63
+ browser_surface: string;
59
64
  local_only: boolean;
60
65
  redacted: boolean;
61
66
  operator_provided: boolean;
@@ -63,6 +68,26 @@ export interface UeyeVisualProvenance {
63
68
  truth_status: TruthStatus;
64
69
  }
65
70
 
71
+ export interface UeyeSurfaceContext {
72
+ schema: 'yam.ueye-surface-context.v1';
73
+ provider_context: string;
74
+ provider_badge: string;
75
+ execution_surface: string;
76
+ app_surface: string;
77
+ browser_surface: string;
78
+ control_mode: string;
79
+ route: string;
80
+ mode: string;
81
+ url: string;
82
+ viewport: string;
83
+ screenshot_id: string;
84
+ evidence_id: string;
85
+ preserved_state: boolean;
86
+ preserved_url: string;
87
+ local_only: boolean;
88
+ truth_status: TruthStatus;
89
+ }
90
+
66
91
  export interface RollbackHint {
67
92
  schema: 'yam.rollback-hint.v1';
68
93
  touched_files: string[];
@@ -96,6 +121,7 @@ export interface UeyeRunReport {
96
121
  schema: 'yam.ueye-run-report.v1';
97
122
  reference_sources: UeyeVisualProvenance[];
98
123
  implementation_sources: UeyeVisualProvenance[];
124
+ surface_context: UeyeSurfaceContext;
99
125
  comparison_result: string;
100
126
  design_quality: string;
101
127
  blocked_reason: string;
@@ -201,6 +227,11 @@ export function buildUeyeVisualProvenance(input: Partial<UeyeVisualProvenance> =
201
227
  source_hash: String(input.source_hash || 'unknown'),
202
228
  reference_id: String(input.reference_id || ''),
203
229
  screenshot_id: String(input.screenshot_id || ''),
230
+ provider_context: String(input.provider_context || 'not-recorded'),
231
+ provider_badge: String(input.provider_badge || 'not-recorded'),
232
+ execution_surface: String(input.execution_surface || 'not-recorded'),
233
+ app_surface: String(input.app_surface || 'not-recorded'),
234
+ browser_surface: String(input.browser_surface || 'not-recorded'),
204
235
  local_only: Boolean(input.local_only),
205
236
  redacted: Boolean(input.redacted),
206
237
  operator_provided: Boolean(input.operator_provided),
@@ -209,6 +240,28 @@ export function buildUeyeVisualProvenance(input: Partial<UeyeVisualProvenance> =
209
240
  };
210
241
  }
211
242
 
243
+ export function buildUeyeSurfaceContext(input: Partial<UeyeSurfaceContext> = {}): UeyeSurfaceContext {
244
+ return {
245
+ schema: 'yam.ueye-surface-context.v1',
246
+ provider_context: String(input.provider_context || 'not-recorded'),
247
+ provider_badge: String(input.provider_badge || input.provider_context || 'not-recorded'),
248
+ execution_surface: String(input.execution_surface || 'not-recorded'),
249
+ app_surface: String(input.app_surface || 'not-recorded'),
250
+ browser_surface: String(input.browser_surface || 'not-recorded'),
251
+ control_mode: String(input.control_mode || 'not-recorded'),
252
+ route: String(input.route || 'ueye'),
253
+ mode: String(input.mode || 'report'),
254
+ url: String(input.url || ''),
255
+ viewport: String(input.viewport || ''),
256
+ screenshot_id: String(input.screenshot_id || ''),
257
+ evidence_id: String(input.evidence_id || ''),
258
+ preserved_state: Boolean(input.preserved_state),
259
+ preserved_url: String(input.preserved_url || ''),
260
+ local_only: input.local_only !== undefined ? Boolean(input.local_only) : true,
261
+ truth_status: isTruthStatus(input.truth_status) ? input.truth_status : 'partial'
262
+ };
263
+ }
264
+
212
265
  export function buildRollbackHint(input: Partial<RollbackHint> = {}): RollbackHint {
213
266
  return {
214
267
  schema: 'yam.rollback-hint.v1',
@@ -268,6 +321,10 @@ export function buildUeyeRunReport(input: Partial<UeyeRunReport> = {}): UeyeRunR
268
321
  schema: 'yam.ueye-run-report.v1',
269
322
  reference_sources: references,
270
323
  implementation_sources: implementations,
324
+ surface_context: buildUeyeSurfaceContext({
325
+ ...input.surface_context,
326
+ truth_status: isTruthStatus(input.surface_context?.truth_status) ? input.surface_context?.truth_status : derivedTruth
327
+ }),
271
328
  comparison_result: comparisonResult,
272
329
  design_quality: String(input.design_quality || 'not-checked'),
273
330
  blocked_reason: blockedReason,
@@ -31,6 +31,24 @@
31
31
  | comparison_result | matched / similar / different / not-verified / not-applicable |
32
32
  | truth_status | verified / partial / skipped / blocked / assumed |
33
33
 
34
+ ## Surface Context
35
+
36
+ | Field | Value |
37
+ | --- | --- |
38
+ | provider_context | not-recorded |
39
+ | provider_badge | not-recorded |
40
+ | execution_surface | not-recorded |
41
+ | app_surface | not-recorded |
42
+ | browser_surface | not-recorded |
43
+ | control_mode | not-recorded |
44
+ | url | |
45
+ | viewport | |
46
+ | screenshot_id | |
47
+ | evidence_id | |
48
+ | preserved_state | true / false |
49
+ | preserved_url | |
50
+ | truth_status | verified / partial / skipped / blocked / assumed |
51
+
34
52
  ## Reference Read Proof
35
53
 
36
54
  - Layout:
@@ -123,6 +141,17 @@ Use `pass`, `needs-polish`, or `fails` for each relevant design dimension before
123
141
  "previous_screenshot_id": "",
124
142
  "current_report_id": "",
125
143
  "current_screenshot_id": "",
144
+ "surface_context": {
145
+ "provider_context": "not-recorded",
146
+ "provider_badge": "not-recorded",
147
+ "execution_surface": "not-recorded",
148
+ "app_surface": "not-recorded",
149
+ "browser_surface": "not-recorded",
150
+ "control_mode": "not-recorded",
151
+ "preserved_state": false,
152
+ "preserved_url": "",
153
+ "truth_status": "partial"
154
+ },
126
155
  "viewport": "",
127
156
  "state": "",
128
157
  "comparison_result": "not-verified",
@@ -42,6 +42,24 @@
42
42
  | comparison_result | matched / similar / different / not-verified / not-applicable |
43
43
  | truth_status | verified / partial / skipped / blocked / assumed |
44
44
 
45
+ ## Surface Context
46
+
47
+ | Field | Value |
48
+ | --- | --- |
49
+ | provider_context | not-recorded |
50
+ | provider_badge | not-recorded |
51
+ | execution_surface | not-recorded |
52
+ | app_surface | not-recorded |
53
+ | browser_surface | not-recorded |
54
+ | control_mode | not-recorded |
55
+ | url | |
56
+ | viewport | |
57
+ | screenshot_id | |
58
+ | evidence_id | |
59
+ | preserved_state | true / false |
60
+ | preserved_url | |
61
+ | truth_status | verified / partial / skipped / blocked / assumed |
62
+
45
63
  ## Reference Read Proof
46
64
 
47
65
  - Layout:
@@ -155,6 +173,17 @@ Result values: pass / needs-polish / fails.
155
173
  "previous_screenshot_id": "",
156
174
  "current_report_id": "",
157
175
  "current_screenshot_id": "",
176
+ "surface_context": {
177
+ "provider_context": "not-recorded",
178
+ "provider_badge": "not-recorded",
179
+ "execution_surface": "not-recorded",
180
+ "app_surface": "not-recorded",
181
+ "browser_surface": "not-recorded",
182
+ "control_mode": "not-recorded",
183
+ "preserved_state": false,
184
+ "preserved_url": "",
185
+ "truth_status": "partial"
186
+ },
158
187
  "viewport": "",
159
188
  "state": "",
160
189
  "comparison_result": "not-verified",
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "0.1.0",
3
+ "version": "0.1.5",
4
4
  "principles": [
5
5
  "Direction before execution.",
6
6
  "Start fast.",
@@ -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 visual provenance, 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, surface context, reference-read proof, visual comparison, design quality review, and P0-P3 issue tracking."
26
26
  },
27
27
  {
28
28
  "id": "question",