yam-flow 0.1.5 → 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 +15 -0
- package/COMMANDS.md +2 -0
- package/README.md +6 -3
- package/dist/bin/yam.js +51 -10
- package/dist/lib/trust-kernel.d.ts +30 -1
- package/dist/lib/trust-kernel.js +120 -3
- package/package.json +2 -2
- package/references/trust-kernel.md +3 -0
- package/references/ueye-proof.md +51 -1
- package/references/ueye.md +27 -3
- package/skills/ueye/SKILL.md +20 -3
- package/src/bin/yam.ts +50 -8
- package/src/lib/trust-kernel.ts +152 -4
- package/templates/ueye-comparison.md +42 -0
- package/templates/ueye-review.md +42 -0
package/references/ueye-proof.md
CHANGED
|
@@ -26,7 +26,7 @@ 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
|
|
29
|
+
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
|
|
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.
|
|
@@ -190,6 +190,55 @@ Dimensions:
|
|
|
190
190
|
Report findings as P0-P3. Keep P2/P3 short unless the user requested a full polish pass.
|
|
191
191
|
For each relevant design dimension, use `pass`, `needs-polish`, or `fails`.
|
|
192
192
|
|
|
193
|
+
### Design Completion Gate
|
|
194
|
+
|
|
195
|
+
Purpose: prevent Ueye from declaring visual work complete before the design evidence supports that claim.
|
|
196
|
+
|
|
197
|
+
Use when the output says the UI is done, complete, polished, final, or verified.
|
|
198
|
+
|
|
199
|
+
Minimum fields:
|
|
200
|
+
|
|
201
|
+
- `completion_claim`: draft / needs-polish / done
|
|
202
|
+
- `mode`: fast / strict
|
|
203
|
+
- `ready_to_claim_done`
|
|
204
|
+
- `design_score`, when used
|
|
205
|
+
- `min_design_score`
|
|
206
|
+
- `checks`
|
|
207
|
+
- `p0`
|
|
208
|
+
- `p1`
|
|
209
|
+
- `warnings`
|
|
210
|
+
- `blockers`
|
|
211
|
+
- `next_action`
|
|
212
|
+
- `truth_status`
|
|
213
|
+
|
|
214
|
+
Required for a done claim when relevant:
|
|
215
|
+
|
|
216
|
+
- direction locked
|
|
217
|
+
- reference read proof for reference-led work
|
|
218
|
+
- implementation screenshot or source-screen evidence
|
|
219
|
+
- reference comparison for reference-led work
|
|
220
|
+
- design quality pass
|
|
221
|
+
- no P0 blockers
|
|
222
|
+
- no unresolved P1 issues unless explicitly deferred
|
|
223
|
+
- CTA affordance checked
|
|
224
|
+
- primary states checked
|
|
225
|
+
- mobile/responsive behavior checked
|
|
226
|
+
- contrast/accessibility-relevant visuals checked
|
|
227
|
+
|
|
228
|
+
Example:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
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
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Attach to proof when needed:
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
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"}'
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The design completion gate is a completion judgment. Keep actual source-screen proof in visual evidence, visual provenance, or a Ueye run report. Do not use self-reported gate fields to pretend a missing screenshot was captured.
|
|
241
|
+
|
|
193
242
|
### Review Continuity And Comparison Report
|
|
194
243
|
|
|
195
244
|
Use this when a Ueye task continues a previous visual review, rechecks a fix, or compares two review runs.
|
|
@@ -222,3 +271,4 @@ Do not claim continuity from memory alone. Link it to a previous report, screens
|
|
|
222
271
|
- Reference-only evidence can support direction, not implementation proof.
|
|
223
272
|
- Generated-only evidence can support ideation or annotation, not implemented-screen verification.
|
|
224
273
|
- Missing screenshots, unavailable browser, or text-only review should cap the result at `partial`, `blocked`, or `assumed`.
|
|
274
|
+
- A done claim with a missing or failing design completion gate should cap the result at `partial` or `blocked`.
|
package/references/ueye.md
CHANGED
|
@@ -66,6 +66,7 @@ Use these when the task depends on visual truth, reference matching, or design q
|
|
|
66
66
|
3. Reference vs Implementation Matrix.
|
|
67
67
|
4. Design Quality Review.
|
|
68
68
|
5. Review Continuity and Comparison Report.
|
|
69
|
+
6. Design Completion Gate.
|
|
69
70
|
|
|
70
71
|
Default bound:
|
|
71
72
|
|
|
@@ -146,6 +147,27 @@ Review dimensions:
|
|
|
146
147
|
For each relevant dimension, record `pass`, `needs-polish`, or `fails`.
|
|
147
148
|
Keep actionable findings in P0-P3 order. Prefer fixing P0/P1 and cheap local P2 issues before broad polish.
|
|
148
149
|
|
|
150
|
+
### Design Completion Gate
|
|
151
|
+
|
|
152
|
+
Use before claiming a Ueye implementation or serious review is done. This is the design-side completion check, not a separate route.
|
|
153
|
+
|
|
154
|
+
Record:
|
|
155
|
+
|
|
156
|
+
- `completion_claim`: draft, needs-polish, or done.
|
|
157
|
+
- `mode`: fast or strict.
|
|
158
|
+
- `design_score`, when score-based completion is useful.
|
|
159
|
+
- P0 and P1 open findings.
|
|
160
|
+
- Whether direction, reference read proof, implementation evidence, comparison, design quality, CTA affordance, state coverage, mobile/responsive behavior, and contrast/accessibility visuals were checked.
|
|
161
|
+
- blockers, warnings, next action, and truth status.
|
|
162
|
+
|
|
163
|
+
Gate behavior:
|
|
164
|
+
|
|
165
|
+
- Draft and needs-polish can stay fast.
|
|
166
|
+
- `done` automatically behaves as strict.
|
|
167
|
+
- P0 or blocked reason should return blocked.
|
|
168
|
+
- Missing required checks for a done claim should cap truth at partial.
|
|
169
|
+
- Only claim done when the gate says `ready_to_claim_done: true`.
|
|
170
|
+
|
|
149
171
|
### Review Continuity and Comparison Report
|
|
150
172
|
|
|
151
173
|
Use when a screen has more than one visual pass, a reference needs follow-up, or a previous Ueye report should stay connected to the next one.
|
|
@@ -199,9 +221,10 @@ Keep this metadata descriptive. It should support honest visual claims, not forc
|
|
|
199
221
|
6. Screenshot/browser recheck when feasible.
|
|
200
222
|
7. Reference vs Implementation Matrix when reference fidelity matters.
|
|
201
223
|
8. Design Quality Review.
|
|
202
|
-
9.
|
|
203
|
-
10.
|
|
204
|
-
11.
|
|
224
|
+
9. Design Completion Gate before any done claim.
|
|
225
|
+
10. Review Continuity and Comparison Report when the work spans multiple passes.
|
|
226
|
+
11. P0/P1 closeout.
|
|
227
|
+
12. Truth status.
|
|
205
228
|
|
|
206
229
|
## Truth Caps
|
|
207
230
|
|
|
@@ -210,3 +233,4 @@ Keep this metadata descriptive. It should support honest visual claims, not forc
|
|
|
210
233
|
- Blocked: requested visual claim cannot be inspected.
|
|
211
234
|
- Assumed: implementation follows code patterns but visual result was not observed.
|
|
212
235
|
- Reference-only or generated-only evidence cannot upgrade a result to full visual verification.
|
|
236
|
+
- A done claim without a passing design completion gate cannot upgrade beyond partial or blocked.
|
package/skills/ueye/SKILL.md
CHANGED
|
@@ -41,6 +41,9 @@ Do not use for:
|
|
|
41
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.
|
|
42
42
|
- Generated media can guide direction, but it cannot verify the implemented screen without implementation screenshot evidence.
|
|
43
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.
|
|
44
|
+
- Ueye owns the design completion gate. Do not rely on a separate code-risk route to catch sloppy visual completion.
|
|
45
|
+
- Draft or needs-polish work may stay fast; a `done` claim requires recorded design gate evidence.
|
|
46
|
+
- Do not claim a Ueye result is done when implementation evidence, design quality, P0/P1 status, state coverage, mobile/responsive behavior, contrast/accessibility visuals, CTA affordance, or reference-read/comparison evidence is missing where relevant.
|
|
44
47
|
|
|
45
48
|
## Workflow
|
|
46
49
|
|
|
@@ -78,8 +81,17 @@ Do not use for:
|
|
|
78
81
|
- accessibility
|
|
79
82
|
- brand or mood fit
|
|
80
83
|
- mark each relevant dimension as pass, needs-polish, or fails.
|
|
81
|
-
11.
|
|
82
|
-
|
|
84
|
+
11. Run the Design Completion Gate before any "done" claim:
|
|
85
|
+
- direction locked
|
|
86
|
+
- reference read proof exists when reference-led
|
|
87
|
+
- implementation screenshot or source-screen evidence exists
|
|
88
|
+
- reference comparison is recorded when reference-led
|
|
89
|
+
- design quality is pass
|
|
90
|
+
- P0 is empty
|
|
91
|
+
- P1 is fixed or explicitly deferred
|
|
92
|
+
- CTA, states, mobile/responsive behavior, and contrast/accessibility visuals were checked
|
|
93
|
+
12. Produce a P0-P3 visual issue ledger and fix path.
|
|
94
|
+
13. Recheck changed/high-risk screens after fixes when feasible.
|
|
83
95
|
|
|
84
96
|
## Required Ueye Artifacts
|
|
85
97
|
|
|
@@ -106,7 +118,11 @@ Use these artifacts for reference-led implementation or serious visual review. K
|
|
|
106
118
|
- use pass, needs-polish, or fails for each relevant design dimension.
|
|
107
119
|
7. Review Continuity and Comparison Report, when this is a follow-up review:
|
|
108
120
|
- record previous source/report, current source/report, comparison delta, resolved findings, new findings, still-open findings, truth status, and next action.
|
|
109
|
-
8.
|
|
121
|
+
8. Design Completion Gate, when implementation or review is claimed done:
|
|
122
|
+
- record completion claim, design score when used, P0/P1 status, gate checks, blockers, warnings, next action, and truth status.
|
|
123
|
+
- use `yam ueye report --completion-claim done --design-quality pass --direction-locked --states-checked --mobile-checked --contrast-checked --cta-checked --json` when a compact proof-ready gate is needed.
|
|
124
|
+
- if the gate is partial or blocked, report the Ueye result as partial or blocked instead of complete.
|
|
125
|
+
9. Media Generation Proof, when generated images are part of the task:
|
|
110
126
|
- record whether generation was requested, attempted, blocked, or produced an output.
|
|
111
127
|
- use `yam media proof --requested --attempted --output path.png --wait-loop --json` when a generated image materially affects the design direction.
|
|
112
128
|
- never use generated media proof as a substitute for implemented screen evidence.
|
|
@@ -120,6 +136,7 @@ Use these artifacts for reference-led implementation or serious visual review. K
|
|
|
120
136
|
- Inspect 1-3 primary images by default; expand only for P0/P1 risk, responsive breakage, or user-requested deep visual QA.
|
|
121
137
|
- Keep source-screen inventory to the 5 most important rows by default.
|
|
122
138
|
- Screenshot unavailable: cap the result at `partial` or `blocked`.
|
|
139
|
+
- Completion gate missing for a `done` claim: cap the result at `partial` or `blocked`.
|
|
123
140
|
- Text-only review: cap the result at `partial`.
|
|
124
141
|
- Mock or invented screenshots: cap the result at `partial` and mark the source.
|
|
125
142
|
- Browser unavailable but source files reviewed: report implementation verification separately from visual verification.
|
package/src/bin/yam.ts
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildMissionPatchEnvelope,
|
|
15
15
|
buildRollbackHint,
|
|
16
16
|
buildRuntimeBackendEvidence,
|
|
17
|
+
buildUeyeDesignCompletionGate,
|
|
17
18
|
buildUeyeVisualProvenance,
|
|
18
19
|
buildUeyeRunReport,
|
|
19
20
|
buildUeyeSurfaceContext,
|
|
@@ -1349,12 +1350,12 @@ Opt-in visual evidence helpers. Ueye stays one skill: fast by default, capture/c
|
|
|
1349
1350
|
Usage:
|
|
1350
1351
|
yam ueye capture --url URL --out screenshot.png [--viewport 1440x900] [--full-page] [--json]
|
|
1351
1352
|
yam ueye compare --reference ref.png --actual screenshot.png [--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]
|
|
1353
|
+
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]
|
|
1353
1354
|
|
|
1354
1355
|
Notes:
|
|
1355
1356
|
capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
|
|
1356
1357
|
compare uses local files only and reports sha256, dimensions, comparison_result, and proof-ready visual provenance.
|
|
1357
|
-
report produces a proof-ready Ueye visual run report and continuity/comparison record without requiring a new capture.
|
|
1358
|
+
report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
|
|
1358
1359
|
`);
|
|
1359
1360
|
}
|
|
1360
1361
|
|
|
@@ -1520,7 +1521,7 @@ async function ueyeCompare(args = []) {
|
|
|
1520
1521
|
|
|
1521
1522
|
async function ueyeReport(args = []) {
|
|
1522
1523
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
|
|
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']));
|
|
1524
|
+
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']));
|
|
1524
1525
|
const reference = String(flags.reference || flags.ref || '');
|
|
1525
1526
|
const actual = String(flags.actual || flags.screenshot || '');
|
|
1526
1527
|
const referenceSources = [];
|
|
@@ -1586,7 +1587,9 @@ async function ueyeReport(args = []) {
|
|
|
1586
1587
|
truth_status: 'partial'
|
|
1587
1588
|
}));
|
|
1588
1589
|
}
|
|
1589
|
-
if (
|
|
1590
|
+
if (flags.comparison_result) {
|
|
1591
|
+
comparisonResult = normalizeUeyeComparisonResult(flags.comparison_result);
|
|
1592
|
+
} else if (reference && actual && referenceSources[0]?.source_hash === implementationSources[0]?.source_hash) {
|
|
1590
1593
|
comparisonResult = 'matched';
|
|
1591
1594
|
} else if (reference && actual) {
|
|
1592
1595
|
comparisonResult = 'different';
|
|
@@ -1605,6 +1608,22 @@ async function ueyeReport(args = []) {
|
|
|
1605
1608
|
},
|
|
1606
1609
|
comparison_result: comparisonResult,
|
|
1607
1610
|
design_quality: String(flags.design_quality || 'not-checked'),
|
|
1611
|
+
design_completion_gate: {
|
|
1612
|
+
mode: flags.strict || flags.gate_mode === 'strict' ? 'strict' : 'fast',
|
|
1613
|
+
completion_claim: flags.completion_claim || flags.completion_status || 'draft',
|
|
1614
|
+
design_score: numberOrNull(flags.design_score),
|
|
1615
|
+
min_design_score: numberOrNull(flags.min_design_score) ?? 8,
|
|
1616
|
+
p0: [...arrayFlag(flags.p0), ...arrayFlag(flags.open_p0)],
|
|
1617
|
+
p1: [...arrayFlag(flags.p1), ...arrayFlag(flags.open_p1)],
|
|
1618
|
+
states_checked: Boolean(flags.states_checked),
|
|
1619
|
+
mobile_checked: Boolean(flags.mobile_checked),
|
|
1620
|
+
responsive_checked: Boolean(flags.responsive_checked),
|
|
1621
|
+
contrast_checked: Boolean(flags.contrast_checked),
|
|
1622
|
+
accessibility_checked: Boolean(flags.accessibility_checked),
|
|
1623
|
+
cta_checked: Boolean(flags.cta_checked),
|
|
1624
|
+
direction_locked: Boolean(flags.direction_locked),
|
|
1625
|
+
reference_read: Boolean(flags.reference_read)
|
|
1626
|
+
},
|
|
1608
1627
|
blocked_reason: blockedReason,
|
|
1609
1628
|
next_action: String(flags.next_visual_action || flags.next_action || '')
|
|
1610
1629
|
});
|
|
@@ -1614,6 +1633,7 @@ async function ueyeReport(args = []) {
|
|
|
1614
1633
|
referenceSources,
|
|
1615
1634
|
implementationSources,
|
|
1616
1635
|
surfaceContext: report.surface_context,
|
|
1636
|
+
designCompletionGate: report.design_completion_gate,
|
|
1617
1637
|
comparisonResult,
|
|
1618
1638
|
designQuality: String(flags.design_quality || 'not-checked'),
|
|
1619
1639
|
similar: arrayFlag(flags.similar),
|
|
@@ -1672,6 +1692,7 @@ function buildUeyeComparisonReport({
|
|
|
1672
1692
|
referenceSources,
|
|
1673
1693
|
implementationSources,
|
|
1674
1694
|
surfaceContext,
|
|
1695
|
+
designCompletionGate,
|
|
1675
1696
|
comparisonResult,
|
|
1676
1697
|
designQuality,
|
|
1677
1698
|
similar,
|
|
@@ -1713,6 +1734,7 @@ function buildUeyeComparisonReport({
|
|
|
1713
1734
|
implementation_count: implementationSources.length
|
|
1714
1735
|
},
|
|
1715
1736
|
surface_context: surfaceContext || buildUeyeSurfaceContext(),
|
|
1737
|
+
design_completion_gate: designCompletionGate || null,
|
|
1716
1738
|
comparison_result: comparisonResult,
|
|
1717
1739
|
design_quality: designQuality,
|
|
1718
1740
|
similar,
|
|
@@ -1732,6 +1754,12 @@ function buildUeyeComparisonReport({
|
|
|
1732
1754
|
};
|
|
1733
1755
|
}
|
|
1734
1756
|
|
|
1757
|
+
function normalizeUeyeComparisonResult(value = '') {
|
|
1758
|
+
const text = String(value || '').toLowerCase();
|
|
1759
|
+
if (['matched', 'similar', 'different', 'not-verified', 'not-applicable'].includes(text)) return text;
|
|
1760
|
+
return 'not-verified';
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1735
1763
|
async function media(args = []) {
|
|
1736
1764
|
const subcommand = args[0] || 'help';
|
|
1737
1765
|
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') return mediaUsage();
|
|
@@ -2015,7 +2043,7 @@ function parseSimpleFlags(args = [], allowed = new Set<string>()) {
|
|
|
2015
2043
|
const key = rawKey.slice(2);
|
|
2016
2044
|
if (allowed.size && !allowed.has(key)) continue;
|
|
2017
2045
|
const normalizedKey = key.replace(/-/g, '_');
|
|
2018
|
-
if (['json', 'full-page', 'requested', 'attempted', 'available', 'wait-loop', 'cleanup-checked'].includes(key)) {
|
|
2046
|
+
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)) {
|
|
2019
2047
|
flags[normalizedKey] = true;
|
|
2020
2048
|
continue;
|
|
2021
2049
|
}
|
|
@@ -2165,6 +2193,7 @@ async function proof(args = []) {
|
|
|
2165
2193
|
printProofList('Mission patch envelope', summary.missionEnvelope || []);
|
|
2166
2194
|
printProofList('Rollback hint', summary.rollbackHint || []);
|
|
2167
2195
|
printProofList('Media proof', summary.mediaProof || []);
|
|
2196
|
+
printProofList('Design completion', summary.designCompletion || []);
|
|
2168
2197
|
printProofList('Changed surfaces', summary.changed);
|
|
2169
2198
|
printProofList('Skipped', summary.skipped);
|
|
2170
2199
|
printProofList('Blocked', summary.blocked);
|
|
@@ -2260,6 +2289,10 @@ function buildProofSummary(parsed, artifact) {
|
|
|
2260
2289
|
...structuredEvidenceList(artifact.mediaProof),
|
|
2261
2290
|
...structuredEvidenceList(parsed.flags.media_proof)
|
|
2262
2291
|
],
|
|
2292
|
+
designCompletion: [
|
|
2293
|
+
...structuredEvidenceList(artifact.designCompletion),
|
|
2294
|
+
...structuredEvidenceList(parsed.flags.design_completion)
|
|
2295
|
+
],
|
|
2263
2296
|
cleanup: parsed.flags.cleanup || artifact.cleanup || '',
|
|
2264
2297
|
changed: [...artifact.changed, ...arrayFlag(parsed.flags.changed)],
|
|
2265
2298
|
skipped: [...artifact.skipped, ...arrayFlag(parsed.flags.skipped)],
|
|
@@ -2325,6 +2358,9 @@ function renderProofMarkdown(summary) {
|
|
|
2325
2358
|
'## Media proof',
|
|
2326
2359
|
...renderProofMarkdownList(summary.mediaProof || []),
|
|
2327
2360
|
'',
|
|
2361
|
+
'## Design completion',
|
|
2362
|
+
...renderProofMarkdownList(summary.designCompletion || []),
|
|
2363
|
+
'',
|
|
2328
2364
|
'## Changed surfaces',
|
|
2329
2365
|
...renderProofMarkdownList(summary.changed),
|
|
2330
2366
|
'',
|
|
@@ -2357,7 +2393,7 @@ function renderProofMarkdownList(values) {
|
|
|
2357
2393
|
function parseProofArgs(args = []) {
|
|
2358
2394
|
const flags: AnyRecord = {};
|
|
2359
2395
|
const positionals: string[] = [];
|
|
2360
|
-
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']);
|
|
2396
|
+
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']);
|
|
2361
2397
|
for (let index = 0; index < args.length; index += 1) {
|
|
2362
2398
|
const arg = args[index];
|
|
2363
2399
|
if (arg.startsWith('--')) {
|
|
@@ -2371,7 +2407,7 @@ function parseProofArgs(args = []) {
|
|
|
2371
2407
|
}
|
|
2372
2408
|
const value = inlineValue ?? args[index + 1] ?? '';
|
|
2373
2409
|
if (inlineValue === undefined) index += 1;
|
|
2374
|
-
if (['command', 'evidence', 'visual', 'runtime', 'runtime-backend-evidence', 'visual-provenance', 'mission-envelope', 'rollback-hint', 'media-proof', 'changed', 'skipped', 'blocked', 'assumed', 'assumption', 'unverified'].includes(key)) {
|
|
2410
|
+
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)) {
|
|
2375
2411
|
flags[normalizedKey] = [...arrayFlag(flags[normalizedKey]), value];
|
|
2376
2412
|
} else {
|
|
2377
2413
|
flags[normalizedKey] = value;
|
|
@@ -2414,6 +2450,7 @@ function emptyProofArtifact() {
|
|
|
2414
2450
|
missionEnvelope: [],
|
|
2415
2451
|
rollbackHint: [],
|
|
2416
2452
|
mediaProof: [],
|
|
2453
|
+
designCompletion: [],
|
|
2417
2454
|
cleanup: '',
|
|
2418
2455
|
changed: [],
|
|
2419
2456
|
skipped: [],
|
|
@@ -2441,6 +2478,7 @@ async function parseProofJson(file, empty) {
|
|
|
2441
2478
|
missionEnvelope: structuredEvidenceList(data.missionEnvelope || data.mission_envelope),
|
|
2442
2479
|
rollbackHint: structuredEvidenceList(data.rollbackHint || data.rollback_hint),
|
|
2443
2480
|
mediaProof: structuredEvidenceList(data.mediaProof || data.media_proof),
|
|
2481
|
+
designCompletion: structuredEvidenceList(data.designCompletion || data.design_completion),
|
|
2444
2482
|
cleanup: String(data.cleanup || data.cleanupStatus || ''),
|
|
2445
2483
|
changed: arrayFlag(data.changed || data.files),
|
|
2446
2484
|
skipped: arrayFlag(data.skipped),
|
|
@@ -2471,6 +2509,7 @@ async function parseProofMarkdown(file, empty) {
|
|
|
2471
2509
|
missionEnvelope: readProofList(text, 'Mission patch envelope'),
|
|
2472
2510
|
rollbackHint: readProofList(text, 'Rollback hint'),
|
|
2473
2511
|
mediaProof: readProofList(text, 'Media proof'),
|
|
2512
|
+
designCompletion: readProofList(text, 'Design completion'),
|
|
2474
2513
|
cleanup: readProofField(text, 'Cleanup') || readProofField(text, 'Cleanup status'),
|
|
2475
2514
|
changed: firstProofList(text, ['Changed surfaces', 'Files']),
|
|
2476
2515
|
skipped: readProofList(text, 'Skipped'),
|
|
@@ -2556,6 +2595,9 @@ function formatStructuredEvidence(value) {
|
|
|
2556
2595
|
if (data.schema === 'yam.media-generation-proof.v1' || data.generation_requested || data.output_hash) {
|
|
2557
2596
|
return JSON.stringify(buildMediaGenerationProof(data));
|
|
2558
2597
|
}
|
|
2598
|
+
if (data.schema === 'yam.ueye-design-completion-gate.v1' || data.completion_claim || data.ready_to_claim_done !== undefined) {
|
|
2599
|
+
return JSON.stringify(buildUeyeDesignCompletionGate(data));
|
|
2600
|
+
}
|
|
2559
2601
|
return JSON.stringify(data);
|
|
2560
2602
|
} catch {
|
|
2561
2603
|
return text;
|
|
@@ -2564,7 +2606,7 @@ function formatStructuredEvidence(value) {
|
|
|
2564
2606
|
|
|
2565
2607
|
function hasProofEvidence(parsed) {
|
|
2566
2608
|
const flags = parsed.flags || {};
|
|
2567
|
-
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);
|
|
2609
|
+
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);
|
|
2568
2610
|
}
|
|
2569
2611
|
|
|
2570
2612
|
function printProofList(label, values) {
|
package/src/lib/trust-kernel.ts
CHANGED
|
@@ -88,6 +88,30 @@ export interface UeyeSurfaceContext {
|
|
|
88
88
|
truth_status: TruthStatus;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
|
+
export interface UeyeDesignGateCheck {
|
|
92
|
+
id: string;
|
|
93
|
+
label: string;
|
|
94
|
+
status: 'pass' | 'fail' | 'warning' | 'not_checked';
|
|
95
|
+
required: boolean;
|
|
96
|
+
next_action: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface UeyeDesignCompletionGate {
|
|
100
|
+
schema: 'yam.ueye-design-completion-gate.v1';
|
|
101
|
+
mode: 'fast' | 'strict';
|
|
102
|
+
completion_claim: 'draft' | 'needs-polish' | 'done';
|
|
103
|
+
ready_to_claim_done: boolean;
|
|
104
|
+
design_score: number | null;
|
|
105
|
+
min_design_score: number;
|
|
106
|
+
checks: UeyeDesignGateCheck[];
|
|
107
|
+
p0: string[];
|
|
108
|
+
p1: string[];
|
|
109
|
+
warnings: string[];
|
|
110
|
+
blockers: string[];
|
|
111
|
+
next_action: string;
|
|
112
|
+
truth_status: TruthStatus;
|
|
113
|
+
}
|
|
114
|
+
|
|
91
115
|
export interface RollbackHint {
|
|
92
116
|
schema: 'yam.rollback-hint.v1';
|
|
93
117
|
touched_files: string[];
|
|
@@ -122,6 +146,7 @@ export interface UeyeRunReport {
|
|
|
122
146
|
reference_sources: UeyeVisualProvenance[];
|
|
123
147
|
implementation_sources: UeyeVisualProvenance[];
|
|
124
148
|
surface_context: UeyeSurfaceContext;
|
|
149
|
+
design_completion_gate: UeyeDesignCompletionGate;
|
|
125
150
|
comparison_result: string;
|
|
126
151
|
design_quality: string;
|
|
127
152
|
blocked_reason: string;
|
|
@@ -129,6 +154,10 @@ export interface UeyeRunReport {
|
|
|
129
154
|
truth_status: TruthStatus;
|
|
130
155
|
}
|
|
131
156
|
|
|
157
|
+
export interface UeyeRunReportInput extends Partial<Omit<UeyeRunReport, 'design_completion_gate'>> {
|
|
158
|
+
design_completion_gate?: Partial<UeyeDesignCompletionGate> & Record<string, unknown>;
|
|
159
|
+
}
|
|
160
|
+
|
|
132
161
|
export interface MediaGenerationProof {
|
|
133
162
|
schema: 'yam.media-generation-proof.v1';
|
|
134
163
|
tool_name: string;
|
|
@@ -171,6 +200,7 @@ export interface ProofSummary {
|
|
|
171
200
|
rollbackHint?: unknown;
|
|
172
201
|
runtimeBackendEvidence?: unknown;
|
|
173
202
|
mediaProof?: unknown;
|
|
203
|
+
designCompletion?: unknown;
|
|
174
204
|
}
|
|
175
205
|
|
|
176
206
|
export interface ProofOptions {
|
|
@@ -206,6 +236,7 @@ export interface YamCompletionProof {
|
|
|
206
236
|
rollbackHint: string[];
|
|
207
237
|
runtimeBackendEvidence: string[];
|
|
208
238
|
mediaProof: string[];
|
|
239
|
+
designCompletion: string[];
|
|
209
240
|
cleanup: unknown;
|
|
210
241
|
changed: string[];
|
|
211
242
|
skipped: string[];
|
|
@@ -262,6 +293,94 @@ export function buildUeyeSurfaceContext(input: Partial<UeyeSurfaceContext> = {})
|
|
|
262
293
|
};
|
|
263
294
|
}
|
|
264
295
|
|
|
296
|
+
export function buildUeyeDesignCompletionGate(input: Partial<UeyeDesignCompletionGate> & Record<string, unknown> = {}): UeyeDesignCompletionGate {
|
|
297
|
+
const completionClaim = normalizeUeyeCompletionClaim(input.completion_claim);
|
|
298
|
+
const mode = String(input.mode || '').toLowerCase() === 'strict' || completionClaim === 'done' ? 'strict' : 'fast';
|
|
299
|
+
const designScore = numberOrNull(input.design_score);
|
|
300
|
+
const minDesignScore = numberOrNull(input.min_design_score) ?? 8;
|
|
301
|
+
const p0 = asList(input.p0);
|
|
302
|
+
const p1 = asList(input.p1);
|
|
303
|
+
const hasReference = Boolean(input.has_reference);
|
|
304
|
+
const hasImplementation = Boolean(input.has_implementation_screenshot);
|
|
305
|
+
const comparisonResult = String(input.comparison_result || 'not-verified');
|
|
306
|
+
const designQuality = String(input.design_quality || 'not-checked');
|
|
307
|
+
const blockedReason = String(input.blocked_reason || '');
|
|
308
|
+
const strictOrDone = mode === 'strict' || completionClaim === 'done';
|
|
309
|
+
const checks: UeyeDesignGateCheck[] = [
|
|
310
|
+
designGateCheck('direction_lock', 'Design direction and target screen were locked before judging completion', Boolean(input.direction_locked) ? 'pass' : 'not_checked', strictOrDone, 'record the target screen and design direction before claiming done'),
|
|
311
|
+
designGateCheck('reference_read', 'Reference read proof exists when reference is used', !hasReference ? 'not_checked' : Boolean(input.reference_read) ? 'pass' : 'fail', hasReference && strictOrDone, 'record concrete reference observations before claiming reference-led work is done'),
|
|
312
|
+
designGateCheck('implementation_screenshot', 'Real implementation screenshot or screen evidence exists', hasImplementation ? 'pass' : 'fail', strictOrDone, 'capture or provide implementation screenshot before claiming done'),
|
|
313
|
+
designGateCheck('reference_comparison', 'Reference comparison is recorded when reference is used', !hasReference ? 'not_checked' : ['matched', 'similar', 'not-applicable'].includes(comparisonResult) ? 'pass' : 'fail', hasReference, 'compare against the reference or mark the difference as intentional before claiming done'),
|
|
314
|
+
designGateCheck('design_quality', 'Design quality result is pass', designQuality === 'pass' ? 'pass' : designQuality === 'needs-polish' ? 'warning' : 'fail', strictOrDone, 'resolve design quality issues or keep the result as needs-polish/partial'),
|
|
315
|
+
designGateCheck('p0_clear', 'No P0 visual blockers remain', p0.length ? 'fail' : 'pass', true, 'fix P0 blockers before any done claim'),
|
|
316
|
+
designGateCheck('p1_clear', 'No P1 major visual issues remain', p1.length ? 'fail' : 'pass', strictOrDone, 'fix or explicitly defer P1 issues before claiming done'),
|
|
317
|
+
designGateCheck('cta_affordance', 'Primary CTA and interaction affordances were checked', Boolean(input.cta_checked) ? 'pass' : 'not_checked', strictOrDone, 'check primary CTA clarity and interaction affordances before claiming done'),
|
|
318
|
+
designGateCheck('state_coverage', 'Primary UI states were checked', Boolean(input.states_checked) ? 'pass' : 'not_checked', strictOrDone, 'check default/loading/error/empty/disabled states or keep completion partial'),
|
|
319
|
+
designGateCheck('mobile_coverage', 'Mobile/responsive behavior was checked', Boolean(input.mobile_checked || input.responsive_checked) ? 'pass' : 'not_checked', strictOrDone, 'check mobile/responsive behavior before claiming done'),
|
|
320
|
+
designGateCheck('accessibility_visuals', 'Contrast/accessibility-relevant visuals were checked', Boolean(input.contrast_checked || input.accessibility_checked) ? 'pass' : 'not_checked', strictOrDone, 'check contrast/accessibility-relevant visuals before claiming done')
|
|
321
|
+
];
|
|
322
|
+
if (designScore !== null || strictOrDone) {
|
|
323
|
+
checks.push(designGateCheck('design_score', `Design score is at least ${minDesignScore}`, designScore !== null && designScore >= minDesignScore ? 'pass' : designScore === null ? 'not_checked' : 'fail', false, `record --design-score ${minDesignScore} or higher when using score-based completion`));
|
|
324
|
+
}
|
|
325
|
+
const blockers = [
|
|
326
|
+
...p0.map((item) => `P0: ${item}`),
|
|
327
|
+
...(blockedReason ? [blockedReason] : []),
|
|
328
|
+
...checks
|
|
329
|
+
.filter((check) => check.required && check.status !== 'pass')
|
|
330
|
+
.map((check) => `${check.id}: ${check.next_action}`)
|
|
331
|
+
];
|
|
332
|
+
const warnings = [
|
|
333
|
+
...p1.map((item) => `P1: ${item}`),
|
|
334
|
+
...checks
|
|
335
|
+
.filter((check) => !check.required && ['fail', 'warning', 'not_checked'].includes(check.status))
|
|
336
|
+
.map((check) => `${check.id}: ${check.next_action}`)
|
|
337
|
+
];
|
|
338
|
+
const ready = completionClaim === 'done' && blockers.length === 0;
|
|
339
|
+
const truth: TruthStatus = blockers.some((item) => item.startsWith('P0:')) || blockedReason ? 'blocked'
|
|
340
|
+
: ready ? 'verified'
|
|
341
|
+
: strictOrDone ? 'partial'
|
|
342
|
+
: hasImplementation ? 'partial'
|
|
343
|
+
: 'assumed';
|
|
344
|
+
return {
|
|
345
|
+
schema: 'yam.ueye-design-completion-gate.v1',
|
|
346
|
+
mode,
|
|
347
|
+
completion_claim: completionClaim,
|
|
348
|
+
ready_to_claim_done: ready,
|
|
349
|
+
design_score: designScore,
|
|
350
|
+
min_design_score: minDesignScore,
|
|
351
|
+
checks,
|
|
352
|
+
p0,
|
|
353
|
+
p1,
|
|
354
|
+
warnings,
|
|
355
|
+
blockers,
|
|
356
|
+
next_action: blockers[0] || (ready ? 'done claim is supported by the recorded design gate' : warnings[0] || 'keep the Ueye result as draft or needs-polish until a done claim is needed'),
|
|
357
|
+
truth_status: truth
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function normalizeUeyeCompletionClaim(value: unknown = ''): UeyeDesignCompletionGate['completion_claim'] {
|
|
362
|
+
const text = String(value || '').toLowerCase();
|
|
363
|
+
if (text === 'done' || text === 'complete' || text === 'completed') return 'done';
|
|
364
|
+
if (text === 'needs-polish' || text === 'needs_polish' || text === 'polish') return 'needs-polish';
|
|
365
|
+
return 'draft';
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function designGateCheck(id: string, label: string, status: UeyeDesignGateCheck['status'], required: boolean, nextAction: string): UeyeDesignGateCheck {
|
|
369
|
+
return {
|
|
370
|
+
id,
|
|
371
|
+
label,
|
|
372
|
+
status,
|
|
373
|
+
required,
|
|
374
|
+
next_action: nextAction
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function numberOrNull(value: unknown = null): number | null {
|
|
379
|
+
if (value === undefined || value === null || value === '') return null;
|
|
380
|
+
const number = Number(value);
|
|
381
|
+
return Number.isFinite(number) ? number : null;
|
|
382
|
+
}
|
|
383
|
+
|
|
265
384
|
export function buildRollbackHint(input: Partial<RollbackHint> = {}): RollbackHint {
|
|
266
385
|
return {
|
|
267
386
|
schema: 'yam.rollback-hint.v1',
|
|
@@ -302,7 +421,7 @@ export function buildRuntimeBackendEvidence(input: Partial<RuntimeBackendEvidenc
|
|
|
302
421
|
};
|
|
303
422
|
}
|
|
304
423
|
|
|
305
|
-
export function buildUeyeRunReport(input:
|
|
424
|
+
export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunReport {
|
|
306
425
|
const references = Array.isArray(input.reference_sources)
|
|
307
426
|
? input.reference_sources.map((item) => buildUeyeVisualProvenance(item))
|
|
308
427
|
: [];
|
|
@@ -317,19 +436,30 @@ export function buildUeyeRunReport(input: Partial<UeyeRunReport> = {}): UeyeRunR
|
|
|
317
436
|
: comparisonResult === 'matched' && hasImplementation ? 'verified'
|
|
318
437
|
: hasImplementation ? 'partial'
|
|
319
438
|
: 'partial';
|
|
439
|
+
const gate = buildUeyeDesignCompletionGate({
|
|
440
|
+
...input.design_completion_gate,
|
|
441
|
+
has_reference: references.length > 0,
|
|
442
|
+
has_implementation_screenshot: hasImplementation,
|
|
443
|
+
comparison_result: comparisonResult,
|
|
444
|
+
design_quality: String(input.design_quality || 'not-checked'),
|
|
445
|
+
blocked_reason: blockedReason
|
|
446
|
+
});
|
|
447
|
+
const shouldApplyDesignGate = gate.mode === 'strict' || gate.completion_claim === 'done' || gate.blockers.length > 0;
|
|
448
|
+
const finalTruth = shouldApplyDesignGate ? weakestTruth(derivedTruth, gate.truth_status) : derivedTruth;
|
|
320
449
|
return {
|
|
321
450
|
schema: 'yam.ueye-run-report.v1',
|
|
322
451
|
reference_sources: references,
|
|
323
452
|
implementation_sources: implementations,
|
|
324
453
|
surface_context: buildUeyeSurfaceContext({
|
|
325
454
|
...input.surface_context,
|
|
326
|
-
truth_status: isTruthStatus(input.surface_context?.truth_status) ? input.surface_context?.truth_status :
|
|
455
|
+
truth_status: isTruthStatus(input.surface_context?.truth_status) ? input.surface_context?.truth_status : finalTruth
|
|
327
456
|
}),
|
|
457
|
+
design_completion_gate: gate,
|
|
328
458
|
comparison_result: comparisonResult,
|
|
329
459
|
design_quality: String(input.design_quality || 'not-checked'),
|
|
330
460
|
blocked_reason: blockedReason,
|
|
331
|
-
next_action: String(input.next_action || (
|
|
332
|
-
truth_status: isTruthStatus(input.truth_status) ? input.truth_status :
|
|
461
|
+
next_action: String(input.next_action || gate.blockers[0] || (finalTruth === 'verified' ? 'no action required' : 'capture or provide implementation screenshot before claiming verified visual status')),
|
|
462
|
+
truth_status: isTruthStatus(input.truth_status) ? input.truth_status : finalTruth
|
|
333
463
|
};
|
|
334
464
|
}
|
|
335
465
|
|
|
@@ -444,6 +574,9 @@ export function classifyEvidenceTruth(summary: ProofSummary = {}, options: Proof
|
|
|
444
574
|
for (const media of asList(summary.mediaProof)) {
|
|
445
575
|
rows.push(evidenceRow('evidence', media, readStructuredTruth(media, 'partial')));
|
|
446
576
|
}
|
|
577
|
+
for (const designCompletion of asList(summary.designCompletion)) {
|
|
578
|
+
rows.push(evidenceRow('visual', designCompletion, readStructuredTruth(designCompletion, 'partial')));
|
|
579
|
+
}
|
|
447
580
|
for (const runtime of asList(summary.runtime)) {
|
|
448
581
|
rows.push(evidenceRow('runtime', runtime, classifyRuntimeEvidence(runtime, options)));
|
|
449
582
|
}
|
|
@@ -473,6 +606,14 @@ export function applyProofTruthCaps(summary: ProofSummary = {}, options: ProofOp
|
|
|
473
606
|
cappedTruth = weakestTruth(cappedTruth, 'partial');
|
|
474
607
|
unverified.push('real_visual_evidence_missing');
|
|
475
608
|
}
|
|
609
|
+
const designCompletionTruth = designCompletionCap(summary.designCompletion);
|
|
610
|
+
if (designCompletionTruth) {
|
|
611
|
+
cappedTruth = weakestTruth(cappedTruth, designCompletionTruth);
|
|
612
|
+
if (designCompletionTruth === 'blocked') blockers.push('ueye_design_completion_blocked');
|
|
613
|
+
if (!['verified', 'proven'].includes(designCompletionTruth)) {
|
|
614
|
+
unverified.push('ueye_design_completion_not_verified');
|
|
615
|
+
}
|
|
616
|
+
}
|
|
476
617
|
if (requested !== cappedTruth) {
|
|
477
618
|
caps.push(`requested ${requested} capped to ${cappedTruth} by evidence`);
|
|
478
619
|
}
|
|
@@ -532,6 +673,7 @@ export function buildYamCompletionProof(summary: ProofSummary = {}, options: Pro
|
|
|
532
673
|
rollbackHint: asList(summary.rollbackHint),
|
|
533
674
|
runtimeBackendEvidence: asList(summary.runtimeBackendEvidence),
|
|
534
675
|
mediaProof: asList(summary.mediaProof),
|
|
676
|
+
designCompletion: asList(summary.designCompletion),
|
|
535
677
|
cleanup: summary.cleanup || '',
|
|
536
678
|
changed: asList(summary.changed),
|
|
537
679
|
skipped: asList(summary.skipped),
|
|
@@ -601,6 +743,12 @@ function readStructuredTruth(value: unknown = '', fallback: TruthStatus = 'parti
|
|
|
601
743
|
}
|
|
602
744
|
}
|
|
603
745
|
|
|
746
|
+
function designCompletionCap(value: unknown = ''): TruthStatus | null {
|
|
747
|
+
const items = asList(value);
|
|
748
|
+
if (!items.length) return null;
|
|
749
|
+
return items.reduce<TruthStatus>((weakest, item) => weakestTruth(weakest, readStructuredTruth(item, 'partial')), 'proven');
|
|
750
|
+
}
|
|
751
|
+
|
|
604
752
|
function strongestSupportedTruth(rows: EvidenceRow[] = []): TruthStatus {
|
|
605
753
|
if (rows.some((row) => row.truth === 'blocked')) return 'blocked';
|
|
606
754
|
if (rows.some((row) => row.truth === 'real_required_missing')) return 'real_required_missing';
|