yam-flow 2.5.0 → 2.6.0
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 +51 -0
- package/COMMANDS.md +27 -1
- package/README.md +24 -2
- package/ROADMAP.md +42 -7
- package/dist/bin/yam.js +406 -23
- package/dist/lib/benchmark-promotion.d.ts +53 -0
- package/dist/lib/benchmark-promotion.js +204 -0
- package/dist/lib/bounded-input.d.ts +12 -0
- package/dist/lib/bounded-input.js +48 -0
- package/dist/lib/design-production.d.ts +306 -0
- package/dist/lib/design-production.js +1341 -0
- package/dist/lib/external-updates.d.ts +23 -0
- package/dist/lib/external-updates.js +344 -23
- package/dist/lib/gate-result.d.ts +44 -0
- package/dist/lib/gate-result.js +219 -0
- package/dist/lib/skill-installation.js +23 -5
- package/dist/lib/stable-order.d.ts +1 -0
- package/dist/lib/stable-order.js +5 -0
- package/dist/lib/trust-kernel.js +17 -1
- package/dist/lib/ueye-artifacts.js +3 -2
- package/package.json +6 -2
- package/references/ueye.md +14 -0
- package/skills/ueye/SKILL.md +11 -0
- package/src/bin/yam.ts +415 -19
- package/src/lib/benchmark-promotion.ts +249 -0
- package/src/lib/bounded-input.ts +59 -0
- package/src/lib/design-production.ts +1441 -0
- package/src/lib/external-updates.ts +397 -24
- package/src/lib/gate-result.ts +223 -0
- package/src/lib/skill-installation.ts +24 -5
- package/src/lib/stable-order.ts +4 -0
- package/src/lib/trust-kernel.ts +20 -1
- package/src/lib/ueye-artifacts.ts +3 -2
- package/yam.manifest.json +2 -2
package/src/bin/yam.ts
CHANGED
|
@@ -34,6 +34,19 @@ import {
|
|
|
34
34
|
verifyUeyeAssetManifest,
|
|
35
35
|
verifyUeyeRevisionHistory
|
|
36
36
|
} from '../lib/ueye-artifacts.js';
|
|
37
|
+
import {
|
|
38
|
+
addPlanReviewComment,
|
|
39
|
+
closePlanReviewCanvas,
|
|
40
|
+
createDesignRevisionState,
|
|
41
|
+
createPlanReviewCanvas,
|
|
42
|
+
finalizeDesignProductionPhase,
|
|
43
|
+
readCanonicalDesignRevisionState,
|
|
44
|
+
readPlanReviewSession,
|
|
45
|
+
recordDesignRevisionRound,
|
|
46
|
+
verifyFinalGalleryManifest,
|
|
47
|
+
verifyDesignProductionPhaseReceipt,
|
|
48
|
+
writeFinalGalleryManifest
|
|
49
|
+
} from '../lib/design-production.js';
|
|
37
50
|
import {
|
|
38
51
|
inspectSkillInstallation,
|
|
39
52
|
installSkillSetTransactional,
|
|
@@ -45,6 +58,21 @@ import {
|
|
|
45
58
|
checkExternalUpdates
|
|
46
59
|
} from '../lib/external-updates.js';
|
|
47
60
|
import type { ExternalUpdateComponent } from '../lib/external-updates.js';
|
|
61
|
+
import {
|
|
62
|
+
buildBoundedPromotionReport,
|
|
63
|
+
parsePromotionSample
|
|
64
|
+
} from '../lib/benchmark-promotion.js';
|
|
65
|
+
import {
|
|
66
|
+
buildStrictGateResult,
|
|
67
|
+
verifyStrictGateResult
|
|
68
|
+
} from '../lib/gate-result.js';
|
|
69
|
+
import {
|
|
70
|
+
BoundedInputError,
|
|
71
|
+
GENERAL_STDIN_MAX_BYTES,
|
|
72
|
+
HOOK_STDIN_MAX_BYTES,
|
|
73
|
+
readBoundedStdinJson,
|
|
74
|
+
readBoundedStdinText
|
|
75
|
+
} from '../lib/bounded-input.js';
|
|
48
76
|
|
|
49
77
|
type AnyRecord = Record<string, any>;
|
|
50
78
|
|
|
@@ -181,6 +209,7 @@ Usage:
|
|
|
181
209
|
yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
|
|
182
210
|
yam ueye asset <add|verify> [options]
|
|
183
211
|
yam ueye revision <archive|verify> [options]
|
|
212
|
+
yam ueye production <canvas|revision|gallery|finalize|phase> [options]
|
|
184
213
|
yam media proof [--requested] [--attempted] [--output file] [--json]
|
|
185
214
|
yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
|
|
186
215
|
yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
|
|
@@ -190,6 +219,7 @@ Usage:
|
|
|
190
219
|
yam update apply --component yam|scrapling|insane-search [--json]
|
|
191
220
|
yam update apply --all [--json]
|
|
192
221
|
yam benchmark report [--baseline n] [--current n] [--unit ms] [--target lower|higher] [--json]
|
|
222
|
+
yam benchmark report --candidate-digest sha256 --baseline-digest sha256 --sample seed:candidate:baseline --unit score --evidence-source text [--min-samples n] [--min-mean-delta n] [--min-win-rate n] [--json]
|
|
193
223
|
yam release report [--json]
|
|
194
224
|
yam safety [text...]
|
|
195
225
|
yam memory <init|add|list|summary|resolve> [dir] [options]
|
|
@@ -1328,6 +1358,7 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
|
|
|
1328
1358
|
readinessRow('Supabase plugin', await pluginCacheHas('openai-curated/supabase') ? 'ready' : 'unknown', 'plugin cache only; no DB query performed'),
|
|
1329
1359
|
readinessRow('Vercel plugin', await pluginCacheHas('openai-curated/vercel') ? 'ready' : 'unknown', 'plugin cache only; no deployment/API call performed')
|
|
1330
1360
|
];
|
|
1361
|
+
const capabilityMatrix = buildCapabilityMatrix(rows, globalLiteHookHealth, globalStudyNoteHookHealth);
|
|
1331
1362
|
const riskNotes = [
|
|
1332
1363
|
...instructionSurfaces.issues.map((message) => ({ level: 'issue', reason: message })),
|
|
1333
1364
|
...instructionSurfaces.warnings.map((message) => ({ level: 'warning', reason: message })),
|
|
@@ -1354,6 +1385,7 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
|
|
|
1354
1385
|
frameworkChecklist,
|
|
1355
1386
|
contextPressure,
|
|
1356
1387
|
realProbe,
|
|
1388
|
+
capabilityMatrix,
|
|
1357
1389
|
sqlScan,
|
|
1358
1390
|
riskNotes,
|
|
1359
1391
|
nextActions: nextActionDetails.map((action) => action.next_action),
|
|
@@ -1367,6 +1399,43 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
|
|
|
1367
1399
|
};
|
|
1368
1400
|
}
|
|
1369
1401
|
|
|
1402
|
+
function buildCapabilityMatrix(readinessRows = [], liteHook: AnyRecord = {}, studyNoteHook: AnyRecord = {}) {
|
|
1403
|
+
const readiness = new Map(readinessRows.map((row) => [row.name, row.status]));
|
|
1404
|
+
const capabilities = [
|
|
1405
|
+
capabilityRow('skill_installation', 'stable', readiness.get('Yam skills') || 'unknown', 'yam status plus the transactional installation receipt', 'proves managed skill bytes only; it does not prove a route ran'),
|
|
1406
|
+
capabilityRow('external_update_check', 'stable', 'unknown', 'packaged `yam update check` command', 'this inventory does not contact registries or prove Codex/plugin/network availability'),
|
|
1407
|
+
capabilityRow('external_update_apply', 'beta', 'unknown', 'transactional component updater with rollback receipts', 'runtime readiness is checked only during an explicit run; effective yam identity currently supports POSIX symlinks while Windows npm .cmd shims fail closed as unverified'),
|
|
1408
|
+
capabilityRow('mission_receipt_gate', 'stable', 'ready', 'packaged mission receipt and aggregate gate commands', 'validates supplied real-thread evidence; it does not spawn agents'),
|
|
1409
|
+
capabilityRow('mission_real_subagents', 'instruction-only', 'unknown', 'Mission skill delegates through environment-owned subagent tools', 'CLI cannot prove host slot availability or a real thread outcome'),
|
|
1410
|
+
capabilityRow('ueye_asset_revision_integrity', 'stable', 'ready', 'hash-verified Ueye asset and revision manifests', 'integrity does not prove design quality, licensing, or browser state'),
|
|
1411
|
+
capabilityRow('plan_review_canvas_lite', 'beta', 'ready', 'local static review session artifacts', 'no remote hosting, background service, or automatic implementation start'),
|
|
1412
|
+
capabilityRow('bounded_design_revisions', 'beta', 'ready', 'maximum two reviewer-finding-backed revision rounds', 'round state does not replace Ueye artifact hash verification'),
|
|
1413
|
+
capabilityRow('final_gallery_manifest', 'beta', 'ready', 'path-confined hash-verified packaging manifest', 'gallery completeness does not prove visual or license correctness'),
|
|
1414
|
+
capabilityRow('demand_gated_design_production', 'beta', 'ready', 'pre-recorded operator trigger, Canvas, chronological bounded revisions, gallery, and digest-verifiable phase receipt', 'demand evidence is operator-asserted and overall phase truth remains partial'),
|
|
1415
|
+
capabilityRow('yam_lite_hook', 'beta', String(liteHook.state || readiness.get('yam-lite hook') || 'unknown'), 'optional hook health inspection', 'advisory only; disabled unless explicitly enabled'),
|
|
1416
|
+
capabilityRow('study_note_hook', 'beta', String(studyNoteHook.state || readiness.get('yam-study-note hook') || 'unknown'), 'optional prompt and bounded Stop check', 'does not generate the Study Note or run verification')
|
|
1417
|
+
];
|
|
1418
|
+
return {
|
|
1419
|
+
schema: 'yam.capability-matrix.v1',
|
|
1420
|
+
generated_at: new Date().toISOString(),
|
|
1421
|
+
capabilities,
|
|
1422
|
+
maturity_vocabulary: ['stable', 'beta', 'experimental', 'instruction-only'],
|
|
1423
|
+
truth_status: 'partial',
|
|
1424
|
+
note: 'support maturity and current runtime state are separate; this read-only inventory does not execute capabilities'
|
|
1425
|
+
};
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
function capabilityRow(id, maturity, runtimeState, evidence, limit) {
|
|
1429
|
+
return {
|
|
1430
|
+
id,
|
|
1431
|
+
maturity,
|
|
1432
|
+
runtime_state: runtimeState,
|
|
1433
|
+
evidence,
|
|
1434
|
+
limit,
|
|
1435
|
+
truth_status: runtimeState === 'ready' ? 'partial' : 'assumed'
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1370
1439
|
function printToolsDoctorReport(data) {
|
|
1371
1440
|
console.log('yam tools doctor');
|
|
1372
1441
|
console.log(`Project: ${data.project}`);
|
|
@@ -1383,6 +1452,14 @@ function printToolsDoctorReport(data) {
|
|
|
1383
1452
|
}
|
|
1384
1453
|
}
|
|
1385
1454
|
|
|
1455
|
+
if (data.capabilityMatrix?.capabilities?.length) {
|
|
1456
|
+
console.log('');
|
|
1457
|
+
console.log('Capability matrix:');
|
|
1458
|
+
for (const capability of data.capabilityMatrix.capabilities) {
|
|
1459
|
+
console.log(`- ${capability.id}: ${capability.maturity} / ${capability.runtime_state} (${capability.limit})`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1386
1463
|
if (data.instructionSurfaces.found.length || data.projectSurfaces.length) {
|
|
1387
1464
|
console.log('');
|
|
1388
1465
|
console.log('Project surfaces:');
|
|
@@ -2022,7 +2099,18 @@ async function hookRun(args = []) {
|
|
|
2022
2099
|
console.log(JSON.stringify({ continue: true }));
|
|
2023
2100
|
return;
|
|
2024
2101
|
}
|
|
2025
|
-
|
|
2102
|
+
let input: AnyRecord;
|
|
2103
|
+
try {
|
|
2104
|
+
input = await readStdinJson();
|
|
2105
|
+
} catch (error) {
|
|
2106
|
+
if (!(error instanceof BoundedInputError)) throw error;
|
|
2107
|
+
const code = error.code === 'invalid_json' ? 'invalid_hook_json' : error.code;
|
|
2108
|
+
console.log(JSON.stringify({
|
|
2109
|
+
continue: true,
|
|
2110
|
+
systemMessage: `yam-${profile} hook input rejected: ${code}; continuing without using or echoing the payload.`
|
|
2111
|
+
}));
|
|
2112
|
+
return;
|
|
2113
|
+
}
|
|
2026
2114
|
const event = input?.hook_event_name || input?.hookEventName || input?.event || 'UserPromptSubmit';
|
|
2027
2115
|
const cwd = String(input?.cwd || process.cwd());
|
|
2028
2116
|
if (profile === 'study-note' && event === 'Stop') {
|
|
@@ -2044,15 +2132,7 @@ async function hookRun(args = []) {
|
|
|
2044
2132
|
}
|
|
2045
2133
|
|
|
2046
2134
|
async function readStdinJson() {
|
|
2047
|
-
|
|
2048
|
-
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
2049
|
-
const text = Buffer.concat(chunks).toString('utf8').trim();
|
|
2050
|
-
if (!text) return {};
|
|
2051
|
-
try {
|
|
2052
|
-
return JSON.parse(text);
|
|
2053
|
-
} catch {
|
|
2054
|
-
return {};
|
|
2055
|
-
}
|
|
2135
|
+
return readBoundedStdinJson(process.stdin, HOOK_STDIN_MAX_BYTES);
|
|
2056
2136
|
}
|
|
2057
2137
|
|
|
2058
2138
|
function extractPrompt(input: AnyRecord = {}) {
|
|
@@ -2258,8 +2338,35 @@ function runReleaseReport() {
|
|
|
2258
2338
|
const publishBlockerEvidence = publishBlockerEvidenceFromRelease(checks, tarball);
|
|
2259
2339
|
const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
|
|
2260
2340
|
const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
|
|
2261
|
-
const
|
|
2262
|
-
const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness,
|
|
2341
|
+
const preliminaryOk = failed.length === 0 && publishReadiness.status === 'ready';
|
|
2342
|
+
const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, preliminaryOk);
|
|
2343
|
+
const gateResult = buildStrictGateResult({
|
|
2344
|
+
gate_id: 'release-readiness',
|
|
2345
|
+
boundary: 'release',
|
|
2346
|
+
checks: [
|
|
2347
|
+
...checks.map((check) => ({
|
|
2348
|
+
id: `release-${String(check.id || 'unnamed').replace(/[^a-z0-9._-]/gi, '-')}`,
|
|
2349
|
+
status: check.status === 'passed' ? 'passed' as const : 'failed' as const,
|
|
2350
|
+
note: check.note || check.command || check.id,
|
|
2351
|
+
required: true
|
|
2352
|
+
})),
|
|
2353
|
+
{
|
|
2354
|
+
id: 'publish-readiness',
|
|
2355
|
+
status: publishReadiness.status === 'ready' ? 'passed' as const : 'failed' as const,
|
|
2356
|
+
note: publishReadiness.next_action,
|
|
2357
|
+
required: true
|
|
2358
|
+
}
|
|
2359
|
+
],
|
|
2360
|
+
blockers: publishReadiness.blockers?.map((blocker) => `${blocker.kind}: ${blocker.reason}`) || [],
|
|
2361
|
+
evidence: [
|
|
2362
|
+
`${readinessReceipt.schema}:${readinessReceipt.local_version}`,
|
|
2363
|
+
tarball.integrity ? `tarball-integrity:${tarball.integrity}` : 'tarball-integrity:not-observed',
|
|
2364
|
+
provenance.git_commit ? `git-commit:${provenance.git_commit}` : 'git-commit:not-observed'
|
|
2365
|
+
],
|
|
2366
|
+
next_action: publishReadiness.next_action
|
|
2367
|
+
});
|
|
2368
|
+
const gateContract = verifyStrictGateResult(gateResult);
|
|
2369
|
+
const ok = preliminaryOk && gateResult.status === 'passed' && gateContract.valid;
|
|
2263
2370
|
return {
|
|
2264
2371
|
schema: 'yam.release-report.v1',
|
|
2265
2372
|
generated_at: startedAt,
|
|
@@ -2274,6 +2381,8 @@ function runReleaseReport() {
|
|
|
2274
2381
|
freshness,
|
|
2275
2382
|
publish_readiness: publishReadiness,
|
|
2276
2383
|
readiness_receipt: readinessReceipt,
|
|
2384
|
+
gate_result: gateResult,
|
|
2385
|
+
gate_contract: gateContract,
|
|
2277
2386
|
study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
|
|
2278
2387
|
publishBlockerEvidence,
|
|
2279
2388
|
next_actions: nextActions
|
|
@@ -2685,9 +2794,7 @@ function redactSensitiveText(text = '') {
|
|
|
2685
2794
|
|
|
2686
2795
|
async function readStdinTextIfAvailable() {
|
|
2687
2796
|
if (process.stdin.isTTY) return '';
|
|
2688
|
-
|
|
2689
|
-
for await (const chunk of process.stdin) chunks.push(Buffer.from(chunk));
|
|
2690
|
-
return Buffer.concat(chunks).toString('utf8').trim();
|
|
2797
|
+
return (await readBoundedStdinText(process.stdin, GENERAL_STDIN_MAX_BYTES)).trim();
|
|
2691
2798
|
}
|
|
2692
2799
|
|
|
2693
2800
|
async function loop(args = []) {
|
|
@@ -2773,6 +2880,7 @@ async function ueye(args = []) {
|
|
|
2773
2880
|
if (subcommand === 'report') return ueyeReport(args.slice(1));
|
|
2774
2881
|
if (subcommand === 'asset') return ueyeAsset(args.slice(1));
|
|
2775
2882
|
if (subcommand === 'revision') return ueyeRevision(args.slice(1));
|
|
2883
|
+
if (subcommand === 'production') return ueyeProduction(args.slice(1));
|
|
2776
2884
|
console.error(`unknown ueye command: ${subcommand}`);
|
|
2777
2885
|
return ueyeUsage();
|
|
2778
2886
|
}
|
|
@@ -2791,6 +2899,11 @@ Usage:
|
|
|
2791
2899
|
yam ueye asset verify [--manifest file] [--json]
|
|
2792
2900
|
yam ueye revision archive --file image --round n [--artifact-id id] [--root dir] [--manifest file] [--json]
|
|
2793
2901
|
yam ueye revision verify [--root dir] [--manifest file] [--json]
|
|
2902
|
+
yam ueye production canvas <create|comment|close|show> [options]
|
|
2903
|
+
yam ueye production revision <init|record|show> [options]
|
|
2904
|
+
yam ueye production gallery <write|verify> [options]
|
|
2905
|
+
yam ueye production finalize [options]
|
|
2906
|
+
yam ueye production phase verify [options]
|
|
2794
2907
|
|
|
2795
2908
|
Notes:
|
|
2796
2909
|
capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
|
|
@@ -2798,6 +2911,7 @@ Notes:
|
|
|
2798
2911
|
report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
|
|
2799
2912
|
asset records local reference provenance and protection flags; it never downloads remote content.
|
|
2800
2913
|
revision copies the current local artifact into a non-overwriting round archive before the live file is edited.
|
|
2914
|
+
production is demand-gated: it writes local-only review artifacts, permits at most two finding-backed revision rounds, and keeps gallery correctness claims explicitly unverified.
|
|
2801
2915
|
`);
|
|
2802
2916
|
}
|
|
2803
2917
|
|
|
@@ -2872,6 +2986,196 @@ async function ueyeRevision(args = []) {
|
|
|
2872
2986
|
}
|
|
2873
2987
|
}
|
|
2874
2988
|
|
|
2989
|
+
async function ueyeProduction(args = []) {
|
|
2990
|
+
const surface = String(args[0] || 'help');
|
|
2991
|
+
const isFinalize = surface === 'finalize';
|
|
2992
|
+
const action = isFinalize ? 'run' : String(args[1] || 'help');
|
|
2993
|
+
const flagArgs = isFinalize ? args.slice(1) : args.slice(2);
|
|
2994
|
+
if (surface === 'help' || action === 'help' || surface === '--help' || surface === '-h' || flagArgs.includes('--help') || flagArgs.includes('-h')) return ueyeProductionUsage();
|
|
2995
|
+
const flags = parseSimpleFlags(flagArgs, new Set([
|
|
2996
|
+
'root', 'session-id', 'title', 'artifact-spec', 'session-path', 'render-path',
|
|
2997
|
+
'comment-id', 'artifact-id', 'anchor-kind', 'locator', 'finding', 'requested-change', 'verdict',
|
|
2998
|
+
'state-path', 'finding-id', 'source-comment-id', 'finding-summary', 'finding-evidence', 'planned-change', 'outcome', 'revision-ref',
|
|
2999
|
+
'manifest-path', 'completion-state', 'demand-kind', 'demand-evidence', 'canvas-session-path',
|
|
3000
|
+
'revision-state-path', 'gallery-manifest-path', 'receipt-path', 'json'
|
|
3001
|
+
]));
|
|
3002
|
+
const root = path.resolve(expandHome(flags.root || process.cwd()));
|
|
3003
|
+
try {
|
|
3004
|
+
let result: AnyRecord;
|
|
3005
|
+
if (surface === 'canvas' && action === 'create') {
|
|
3006
|
+
requireProductionFlags(flags, ['session_id', 'title', 'demand_kind', 'demand_evidence', 'artifact_spec']);
|
|
3007
|
+
result = await createPlanReviewCanvas({
|
|
3008
|
+
root,
|
|
3009
|
+
session_id: String(flags.session_id),
|
|
3010
|
+
title: String(flags.title),
|
|
3011
|
+
demand_trigger: {
|
|
3012
|
+
kind: String(flags.demand_kind) as 'repeated_plan_review' | 'multi_asset_production',
|
|
3013
|
+
evidence: String(flags.demand_evidence)
|
|
3014
|
+
},
|
|
3015
|
+
artifacts: await readProductionSpecs(flags.artifact_spec, 'canvas artifact spec') as any,
|
|
3016
|
+
...(flags.session_path ? { session_path: String(flags.session_path) } : {}),
|
|
3017
|
+
...(flags.render_path ? { render_path: String(flags.render_path) } : {})
|
|
3018
|
+
});
|
|
3019
|
+
} else if (surface === 'canvas' && action === 'comment') {
|
|
3020
|
+
requireProductionFlags(flags, ['session_path', 'render_path', 'comment_id', 'artifact_id', 'anchor_kind', 'locator', 'finding', 'requested_change']);
|
|
3021
|
+
result = await addPlanReviewComment({
|
|
3022
|
+
root,
|
|
3023
|
+
session_path: String(flags.session_path),
|
|
3024
|
+
render_path: String(flags.render_path),
|
|
3025
|
+
comment: {
|
|
3026
|
+
id: String(flags.comment_id),
|
|
3027
|
+
anchor: {
|
|
3028
|
+
artifact_id: String(flags.artifact_id),
|
|
3029
|
+
kind: String(flags.anchor_kind) as 'line' | 'region' | 'text',
|
|
3030
|
+
locator: String(flags.locator)
|
|
3031
|
+
},
|
|
3032
|
+
finding: String(flags.finding),
|
|
3033
|
+
requested_change: String(flags.requested_change)
|
|
3034
|
+
}
|
|
3035
|
+
});
|
|
3036
|
+
} else if (surface === 'canvas' && action === 'close') {
|
|
3037
|
+
requireProductionFlags(flags, ['session_path', 'render_path', 'verdict']);
|
|
3038
|
+
result = await closePlanReviewCanvas({
|
|
3039
|
+
root,
|
|
3040
|
+
session_path: String(flags.session_path),
|
|
3041
|
+
render_path: String(flags.render_path),
|
|
3042
|
+
verdict: String(flags.verdict) as 'approve' | 'request_changes'
|
|
3043
|
+
});
|
|
3044
|
+
} else if (surface === 'canvas' && action === 'show') {
|
|
3045
|
+
requireProductionFlags(flags, ['session_path']);
|
|
3046
|
+
const sessionPath = await existingProductionPath(root, String(flags.session_path), 'session_path');
|
|
3047
|
+
result = await readPlanReviewSession(sessionPath);
|
|
3048
|
+
} else if (surface === 'revision' && action === 'init') {
|
|
3049
|
+
requireProductionFlags(flags, ['session_id']);
|
|
3050
|
+
result = await createDesignRevisionState({
|
|
3051
|
+
root,
|
|
3052
|
+
session_id: String(flags.session_id),
|
|
3053
|
+
...(flags.state_path ? { state_path: String(flags.state_path) } : {})
|
|
3054
|
+
});
|
|
3055
|
+
} else if (surface === 'revision' && action === 'record') {
|
|
3056
|
+
requireProductionFlags(flags, ['state_path', 'finding_id', 'source_comment_id', 'finding_summary', 'finding_evidence', 'planned_change', 'outcome', 'revision_ref']);
|
|
3057
|
+
result = await recordDesignRevisionRound({
|
|
3058
|
+
root,
|
|
3059
|
+
state_path: String(flags.state_path),
|
|
3060
|
+
reviewer_finding: {
|
|
3061
|
+
id: String(flags.finding_id),
|
|
3062
|
+
source_comment_id: String(flags.source_comment_id),
|
|
3063
|
+
summary: String(flags.finding_summary),
|
|
3064
|
+
evidence: String(flags.finding_evidence)
|
|
3065
|
+
},
|
|
3066
|
+
planned_change: String(flags.planned_change),
|
|
3067
|
+
outcome: String(flags.outcome) as 'accepted' | 'changes_requested',
|
|
3068
|
+
revision_refs: await readProductionSpecs(flags.revision_ref, 'Ueye revision reference') as any
|
|
3069
|
+
});
|
|
3070
|
+
} else if (surface === 'revision' && action === 'show') {
|
|
3071
|
+
requireProductionFlags(flags, ['state_path']);
|
|
3072
|
+
result = await readCanonicalDesignRevisionState({ root, state_path: String(flags.state_path) });
|
|
3073
|
+
} else if (surface === 'gallery' && action === 'write') {
|
|
3074
|
+
requireProductionFlags(flags, ['manifest_path', 'session_id', 'completion_state', 'artifact_spec']);
|
|
3075
|
+
result = await writeFinalGalleryManifest({
|
|
3076
|
+
root,
|
|
3077
|
+
manifest_path: String(flags.manifest_path),
|
|
3078
|
+
session_id: String(flags.session_id),
|
|
3079
|
+
completion_state: String(flags.completion_state) as 'draft' | 'ready_for_inspection' | 'packaged',
|
|
3080
|
+
artifacts: await readProductionSpecs(flags.artifact_spec, 'gallery artifact spec') as any
|
|
3081
|
+
});
|
|
3082
|
+
} else if (surface === 'gallery' && action === 'verify') {
|
|
3083
|
+
requireProductionFlags(flags, ['manifest_path']);
|
|
3084
|
+
result = await verifyFinalGalleryManifest({ root, manifest_path: String(flags.manifest_path) });
|
|
3085
|
+
} else if (surface === 'finalize' && action === 'run') {
|
|
3086
|
+
requireProductionFlags(flags, ['demand_kind', 'demand_evidence', 'canvas_session_path', 'gallery_manifest_path', 'receipt_path']);
|
|
3087
|
+
result = await finalizeDesignProductionPhase({
|
|
3088
|
+
root,
|
|
3089
|
+
demand_trigger: {
|
|
3090
|
+
kind: String(flags.demand_kind) as 'repeated_plan_review' | 'multi_asset_production',
|
|
3091
|
+
evidence: String(flags.demand_evidence)
|
|
3092
|
+
},
|
|
3093
|
+
canvas_session_path: String(flags.canvas_session_path),
|
|
3094
|
+
revision_state_path: flags.revision_state_path ? String(flags.revision_state_path) : null,
|
|
3095
|
+
gallery_manifest_path: String(flags.gallery_manifest_path),
|
|
3096
|
+
receipt_path: String(flags.receipt_path)
|
|
3097
|
+
});
|
|
3098
|
+
} else if (surface === 'phase' && action === 'verify') {
|
|
3099
|
+
requireProductionFlags(flags, ['receipt_path']);
|
|
3100
|
+
result = await verifyDesignProductionPhaseReceipt({ root, receipt_path: String(flags.receipt_path) });
|
|
3101
|
+
} else {
|
|
3102
|
+
throw new Error(`unknown Ueye production command: ${surface} ${action}`);
|
|
3103
|
+
}
|
|
3104
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye design production');
|
|
3105
|
+
if (result.truth_status === 'blocked') process.exitCode = 1;
|
|
3106
|
+
} catch (error) {
|
|
3107
|
+
const reason = summarizeCheckOutput(errorMessage(error));
|
|
3108
|
+
const result = {
|
|
3109
|
+
schema: 'yam.design-production-error.v1',
|
|
3110
|
+
surface,
|
|
3111
|
+
action,
|
|
3112
|
+
status: 'blocked',
|
|
3113
|
+
reason,
|
|
3114
|
+
next_action: /two_round_limit|maximum (?:is )?2|maximum two/i.test(reason)
|
|
3115
|
+
? 'stop this phase at two rounds and keep unresolved gallery work draft; start a new phase only after new explicit demand authorization'
|
|
3116
|
+
: /stopped with status accepted/i.test(reason)
|
|
3117
|
+
? 'stop revising because the reviewer already accepted this phase'
|
|
3118
|
+
: 'repair the local spec, path, integrity evidence, or phase state before retrying',
|
|
3119
|
+
truth_status: 'blocked'
|
|
3120
|
+
};
|
|
3121
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Ueye design production');
|
|
3122
|
+
process.exitCode = 1;
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
|
|
3126
|
+
function ueyeProductionUsage() {
|
|
3127
|
+
console.log(`yam ueye production
|
|
3128
|
+
|
|
3129
|
+
Demand-gated local design production helpers. Spec files are bounded JSON objects; repeat --artifact-spec or --revision-ref for multiple entries.
|
|
3130
|
+
|
|
3131
|
+
Usage:
|
|
3132
|
+
yam ueye production canvas create [--root dir] --session-id id --title text --demand-kind repeated_plan_review|multi_asset_production --demand-evidence text --artifact-spec file [--json]
|
|
3133
|
+
yam ueye production canvas comment [--root dir] --session-path relative --render-path relative --comment-id id --artifact-id id --anchor-kind line|region|text --locator text --finding text --requested-change text [--json]
|
|
3134
|
+
yam ueye production canvas close [--root dir] --session-path relative --render-path relative --verdict approve|request_changes [--json]
|
|
3135
|
+
yam ueye production canvas show [--root dir] --session-path relative [--json]
|
|
3136
|
+
yam ueye production revision init [--root dir] --session-id id [--state-path .yam/ueye/design-production/<session-id>/revision-state.json] [--json]
|
|
3137
|
+
yam ueye production revision record [--root dir] --state-path relative --finding-id id --source-comment-id id --finding-summary text --finding-evidence text --planned-change text --outcome accepted|changes_requested --revision-ref file [--json]
|
|
3138
|
+
yam ueye production revision show [--root dir] --state-path relative [--json]
|
|
3139
|
+
yam ueye production gallery write [--root dir] --manifest-path relative --session-id id --completion-state draft|ready_for_inspection|packaged --artifact-spec file [--json]
|
|
3140
|
+
yam ueye production gallery verify [--root dir] --manifest-path relative [--json]
|
|
3141
|
+
yam ueye production finalize [--root dir] --demand-kind repeated_plan_review|multi_asset_production --demand-evidence text --canvas-session-path relative [--revision-state-path relative] --gallery-manifest-path relative --receipt-path relative [--json]
|
|
3142
|
+
yam ueye production phase verify [--root dir] --receipt-path relative [--json]
|
|
3143
|
+
|
|
3144
|
+
Use this phase only when repeated plan review or large multi-asset production creates real coordination demand. Canvas artifacts stay static and local. Revision init requires the session's closed request_changes Canvas and fixes state to .yam/ueye/design-production/<session-id>/revision-state.json; --state-path is accepted only when it names that exact path. Record/show/finalize reject alternate state files. Gallery verification covers paths, hashes, dimensions, and revision references only. Finalize binds the explicit demand evidence, closed Canvas, terminal revision state when needed, and verified gallery into one immutable receipt.
|
|
3145
|
+
`);
|
|
3146
|
+
}
|
|
3147
|
+
|
|
3148
|
+
function requireProductionFlags(flags: AnyRecord, names: string[]) {
|
|
3149
|
+
const missing = names.filter((name) => flags[name] === undefined || flags[name] === '');
|
|
3150
|
+
if (missing.length) throw new Error(`missing required option(s): ${missing.map((name) => `--${name.replace(/_/g, '-')}`).join(', ')}`);
|
|
3151
|
+
}
|
|
3152
|
+
|
|
3153
|
+
async function readProductionSpecs(value: unknown, label: string) {
|
|
3154
|
+
const files = arrayFlag(value);
|
|
3155
|
+
if (!files.length) throw new Error(`${label} requires at least one JSON spec file`);
|
|
3156
|
+
const specs: AnyRecord[] = [];
|
|
3157
|
+
for (const file of files) specs.push(await readBoundedJsonSpec(path.resolve(expandHome(file)), label));
|
|
3158
|
+
return specs;
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
async function readBoundedJsonSpec(file: string, label: string) {
|
|
3162
|
+
const stat = await fsp.stat(file);
|
|
3163
|
+
if (!stat.isFile()) throw new Error(`${label} must be a regular file: ${file}`);
|
|
3164
|
+
if (stat.size > 256 * 1024) throw new Error(`${label} exceeds the 256 KiB limit: ${file}`);
|
|
3165
|
+
const value = JSON.parse(await fsp.readFile(file, 'utf8'));
|
|
3166
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`${label} must contain one JSON object: ${file}`);
|
|
3167
|
+
return value;
|
|
3168
|
+
}
|
|
3169
|
+
|
|
3170
|
+
async function existingProductionPath(root: string, value: string, label: string) {
|
|
3171
|
+
if (!value || path.isAbsolute(value) || value.includes('\0')) throw new Error(`${label} must be a non-empty project-relative path`);
|
|
3172
|
+
const realRoot = await fsp.realpath(root);
|
|
3173
|
+
const candidate = await fsp.realpath(path.resolve(realRoot, value));
|
|
3174
|
+
const relative = path.relative(realRoot, candidate);
|
|
3175
|
+
if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) throw new Error(`${label} escapes the project root`);
|
|
3176
|
+
return candidate;
|
|
3177
|
+
}
|
|
3178
|
+
|
|
2875
3179
|
async function ueyePreflight(args = []) {
|
|
2876
3180
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
|
|
2877
3181
|
const parsed = parseDirJsonArgs(args);
|
|
@@ -3711,12 +4015,37 @@ async function missionGate(args = []) {
|
|
|
3711
4015
|
receipts
|
|
3712
4016
|
});
|
|
3713
4017
|
const blockers = [...gate.blockers, ...loadErrors.map((error) => `receipt_load_failed: ${error}`)];
|
|
4018
|
+
const gateResult = buildStrictGateResult({
|
|
4019
|
+
gate_id: 'mission-completion',
|
|
4020
|
+
boundary: 'mission',
|
|
4021
|
+
checks: [
|
|
4022
|
+
{
|
|
4023
|
+
id: 'receipt-inventory',
|
|
4024
|
+
status: gate.ready_to_claim_complete && loadErrors.length === 0 ? 'passed' : 'failed',
|
|
4025
|
+
note: blockers[0] || 'all expected Mission receipts are present and eligible',
|
|
4026
|
+
required: true
|
|
4027
|
+
},
|
|
4028
|
+
{
|
|
4029
|
+
id: 'read-only-boundary',
|
|
4030
|
+
status: gate.read_only_violations.length === 0 ? 'passed' : 'failed',
|
|
4031
|
+
note: gate.read_only_violations.length ? `violations: ${gate.read_only_violations.join(', ')}` : 'reviewer and doctor receipts preserve read-only access',
|
|
4032
|
+
required: true
|
|
4033
|
+
}
|
|
4034
|
+
],
|
|
4035
|
+
blockers,
|
|
4036
|
+
evidence: gate.receipts.map((receipt) => `${receipt.thread_id}:${receipt.receipt_id}:${receipt.outcome}`),
|
|
4037
|
+
next_action: blockers[0] || gate.next_action
|
|
4038
|
+
});
|
|
4039
|
+
const gateContract = verifyStrictGateResult(gateResult);
|
|
4040
|
+
const strictReady = gate.ready_to_claim_complete && loadErrors.length === 0 && gateResult.status === 'passed' && gateContract.valid;
|
|
3714
4041
|
const result = {
|
|
3715
4042
|
...gate,
|
|
3716
4043
|
blockers,
|
|
3717
|
-
ready_to_claim_complete:
|
|
4044
|
+
ready_to_claim_complete: strictReady,
|
|
3718
4045
|
next_action: blockers[0] || gate.next_action,
|
|
3719
|
-
truth_status:
|
|
4046
|
+
truth_status: strictReady ? gate.truth_status : 'blocked',
|
|
4047
|
+
gate_result: gateResult,
|
|
4048
|
+
gate_contract: gateContract,
|
|
3720
4049
|
persistence: flags.out ? 'written' : 'not-persisted',
|
|
3721
4050
|
out: flags.out ? path.resolve(expandHome(flags.out)) : ''
|
|
3722
4051
|
};
|
|
@@ -3802,15 +4131,72 @@ function benchmarkUsage() {
|
|
|
3802
4131
|
|
|
3803
4132
|
Usage:
|
|
3804
4133
|
yam benchmark report [--label text] [--baseline n] [--current n] [--unit ms] [--target lower|higher] [--next-action text] [--json]
|
|
4134
|
+
yam benchmark report --candidate-digest sha256 --baseline-digest sha256 --sample seed:candidate:baseline --unit score --evidence-source text [--sample ...] [--min-samples n] [--min-mean-delta n] [--min-win-rate n] [--json]
|
|
3805
4135
|
|
|
3806
4136
|
Notes:
|
|
3807
4137
|
Records a small optimization loop result. It does not run benchmarks by itself.
|
|
4138
|
+
Promotion mode verifies deterministic paired-sample arithmetic but treats supplied scores as operator assertions, not statistical significance.
|
|
3808
4139
|
`);
|
|
3809
4140
|
}
|
|
3810
4141
|
|
|
3811
4142
|
async function benchmarkReport(args = []) {
|
|
3812
4143
|
if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return benchmarkUsage();
|
|
3813
|
-
const flags = parseSimpleFlags(args, new Set(['label', 'baseline', 'current', 'unit', 'target', 'next-action', 'rollback-hint', 'intent', 'json']));
|
|
4144
|
+
const flags = parseSimpleFlags(args, new Set(['label', 'baseline', 'current', 'unit', 'target', 'next-action', 'rollback-hint', 'intent', 'candidate-digest', 'baseline-digest', 'sample', 'min-samples', 'min-mean-delta', 'min-win-rate', 'evidence-source', 'json']));
|
|
4145
|
+
const promotionRequested = Boolean(
|
|
4146
|
+
flags.candidate_digest
|
|
4147
|
+
|| flags.baseline_digest
|
|
4148
|
+
|| flags.sample
|
|
4149
|
+
|| flags.min_samples
|
|
4150
|
+
|| flags.min_mean_delta
|
|
4151
|
+
|| flags.min_win_rate
|
|
4152
|
+
|| flags.evidence_source
|
|
4153
|
+
);
|
|
4154
|
+
if (promotionRequested) {
|
|
4155
|
+
try {
|
|
4156
|
+
const promotion = buildBoundedPromotionReport({
|
|
4157
|
+
label: String(flags.label || 'unnamed'),
|
|
4158
|
+
candidate_digest: String(flags.candidate_digest || ''),
|
|
4159
|
+
baseline_digest: String(flags.baseline_digest || ''),
|
|
4160
|
+
samples: arrayFlag(flags.sample).map((sample) => parsePromotionSample(sample)),
|
|
4161
|
+
min_samples: flags.min_samples === undefined ? undefined : Number(flags.min_samples),
|
|
4162
|
+
min_mean_delta: flags.min_mean_delta === undefined ? undefined : Number(flags.min_mean_delta),
|
|
4163
|
+
min_win_rate: flags.min_win_rate === undefined ? undefined : Number(flags.min_win_rate),
|
|
4164
|
+
target: String(flags.target || 'higher').toLowerCase() as 'lower' | 'higher',
|
|
4165
|
+
unit: String(flags.unit || ''),
|
|
4166
|
+
evidence_source: String(flags.evidence_source || '')
|
|
4167
|
+
});
|
|
4168
|
+
const gateResult = buildStrictGateResult({
|
|
4169
|
+
gate_id: 'benchmark-promotion',
|
|
4170
|
+
boundary: 'generic',
|
|
4171
|
+
checks: [
|
|
4172
|
+
{
|
|
4173
|
+
id: 'promotion-decision',
|
|
4174
|
+
status: promotion.decision === 'keep' ? 'passed' : 'failed',
|
|
4175
|
+
note: promotion.next_action,
|
|
4176
|
+
required: true
|
|
4177
|
+
}
|
|
4178
|
+
],
|
|
4179
|
+
blockers: promotion.blockers,
|
|
4180
|
+
evidence: [promotion.evidence_digest, promotion.evidence_source || 'operator-supplied paired measurements'],
|
|
4181
|
+
next_action: promotion.next_action
|
|
4182
|
+
});
|
|
4183
|
+
const result = { ...promotion, gate_result: gateResult };
|
|
4184
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Bounded promotion report');
|
|
4185
|
+
if (promotion.decision !== 'keep' || gateResult.status !== 'passed') process.exitCode = 1;
|
|
4186
|
+
return;
|
|
4187
|
+
} catch (error) {
|
|
4188
|
+
const result = {
|
|
4189
|
+
schema: 'yam.bounded-promotion-error.v1',
|
|
4190
|
+
decision: 'blocked',
|
|
4191
|
+
blockers: [errorMessage(error)],
|
|
4192
|
+
next_action: 'repair the malformed paired-sample input before making a promotion decision',
|
|
4193
|
+
truth_status: 'blocked'
|
|
4194
|
+
};
|
|
4195
|
+
printJsonOrHuman(result, Boolean(flags.json), 'Bounded promotion report');
|
|
4196
|
+
process.exitCode = 1;
|
|
4197
|
+
return;
|
|
4198
|
+
}
|
|
4199
|
+
}
|
|
3814
4200
|
const baseline = numberOrNull(flags.baseline);
|
|
3815
4201
|
const current = numberOrNull(flags.current);
|
|
3816
4202
|
const target = String(flags.target || 'lower').toLowerCase() === 'higher' ? 'higher' : 'lower';
|
|
@@ -4405,7 +4791,12 @@ function formatStructuredEvidence(value) {
|
|
|
4405
4791
|
return JSON.stringify(buildMissionSubagentReceipt(data));
|
|
4406
4792
|
}
|
|
4407
4793
|
if (data.schema === 'yam.mission-completion-gate.v1' || data.expected_thread_ids || data.ready_to_claim_complete !== undefined) {
|
|
4408
|
-
|
|
4794
|
+
const completion = buildMissionCompletionGate(data);
|
|
4795
|
+
return JSON.stringify({
|
|
4796
|
+
...completion,
|
|
4797
|
+
...(data.gate_result && typeof data.gate_result === 'object' ? { gate_result: data.gate_result } : {}),
|
|
4798
|
+
...(data.gate_contract && typeof data.gate_contract === 'object' ? { gate_contract: data.gate_contract } : {})
|
|
4799
|
+
});
|
|
4409
4800
|
}
|
|
4410
4801
|
if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
|
|
4411
4802
|
return JSON.stringify(buildMissionPatchEnvelope(data));
|
|
@@ -5407,6 +5798,11 @@ function showPath() {
|
|
|
5407
5798
|
function showRequestedHelp(args: string[]) {
|
|
5408
5799
|
if (!args.some((arg) => arg === '--help' || arg === '-h')) return false;
|
|
5409
5800
|
|
|
5801
|
+
if (args[0] === 'ueye' && args[1] === 'production') {
|
|
5802
|
+
ueyeProductionUsage();
|
|
5803
|
+
return true;
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5410
5806
|
const commandUsage: Record<string, () => void> = {
|
|
5411
5807
|
context: contextUsage,
|
|
5412
5808
|
cleanup: cleanupUsage,
|