yam-flow 2.5.1 → 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/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,14 @@ 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';
48
69
  import {
49
70
  BoundedInputError,
50
71
  GENERAL_STDIN_MAX_BYTES,
@@ -188,6 +209,7 @@ Usage:
188
209
  yam ueye report [--reference ref.png] [--actual screenshot.png] [--provider-context local] [--execution-surface in-app-browser] [--json]
189
210
  yam ueye asset <add|verify> [options]
190
211
  yam ueye revision <archive|verify> [options]
212
+ yam ueye production <canvas|revision|gallery|finalize|phase> [options]
191
213
  yam media proof [--requested] [--attempted] [--output file] [--json]
192
214
  yam runtime evidence [--backend terminal|in-app-browser|playwright|tmux|zellij] [--claim observed|started|stopped|cleanup-verified] [--json]
193
215
  yam mission queue [--agent-id id] [--scope text] [--changed file] [--verification-hint text] [--json]
@@ -197,6 +219,7 @@ Usage:
197
219
  yam update apply --component yam|scrapling|insane-search [--json]
198
220
  yam update apply --all [--json]
199
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]
200
223
  yam release report [--json]
201
224
  yam safety [text...]
202
225
  yam memory <init|add|list|summary|resolve> [dir] [options]
@@ -1335,6 +1358,7 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
1335
1358
  readinessRow('Supabase plugin', await pluginCacheHas('openai-curated/supabase') ? 'ready' : 'unknown', 'plugin cache only; no DB query performed'),
1336
1359
  readinessRow('Vercel plugin', await pluginCacheHas('openai-curated/vercel') ? 'ready' : 'unknown', 'plugin cache only; no deployment/API call performed')
1337
1360
  ];
1361
+ const capabilityMatrix = buildCapabilityMatrix(rows, globalLiteHookHealth, globalStudyNoteHookHealth);
1338
1362
  const riskNotes = [
1339
1363
  ...instructionSurfaces.issues.map((message) => ({ level: 'issue', reason: message })),
1340
1364
  ...instructionSurfaces.warnings.map((message) => ({ level: 'warning', reason: message })),
@@ -1361,6 +1385,7 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
1361
1385
  frameworkChecklist,
1362
1386
  contextPressure,
1363
1387
  realProbe,
1388
+ capabilityMatrix,
1364
1389
  sqlScan,
1365
1390
  riskNotes,
1366
1391
  nextActions: nextActionDetails.map((action) => action.next_action),
@@ -1374,6 +1399,43 @@ async function buildToolsDoctorReport(targetDir = process.cwd()) {
1374
1399
  };
1375
1400
  }
1376
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
+
1377
1439
  function printToolsDoctorReport(data) {
1378
1440
  console.log('yam tools doctor');
1379
1441
  console.log(`Project: ${data.project}`);
@@ -1390,6 +1452,14 @@ function printToolsDoctorReport(data) {
1390
1452
  }
1391
1453
  }
1392
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
+
1393
1463
  if (data.instructionSurfaces.found.length || data.projectSurfaces.length) {
1394
1464
  console.log('');
1395
1465
  console.log('Project surfaces:');
@@ -2268,8 +2338,35 @@ function runReleaseReport() {
2268
2338
  const publishBlockerEvidence = publishBlockerEvidenceFromRelease(checks, tarball);
2269
2339
  const publishReadiness = releasePublishReadiness(checks, provenance, tarball, publishBlockerEvidence);
2270
2340
  const nextActions = releaseNextActions(checks, provenance, tarball, publishBlockerEvidence, publishReadiness);
2271
- const ok = failed.length === 0 && publishReadiness.status === 'ready';
2272
- const readinessReceipt = releaseReadinessReceipt(provenance, freshness, tarball, publishReadiness, ok);
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;
2273
2370
  return {
2274
2371
  schema: 'yam.release-report.v1',
2275
2372
  generated_at: startedAt,
@@ -2284,6 +2381,8 @@ function runReleaseReport() {
2284
2381
  freshness,
2285
2382
  publish_readiness: publishReadiness,
2286
2383
  readiness_receipt: readinessReceipt,
2384
+ gate_result: gateResult,
2385
+ gate_contract: gateContract,
2287
2386
  study_note: releaseStudyNote(publishReadiness, publishBlockerEvidence),
2288
2387
  publishBlockerEvidence,
2289
2388
  next_actions: nextActions
@@ -2781,6 +2880,7 @@ async function ueye(args = []) {
2781
2880
  if (subcommand === 'report') return ueyeReport(args.slice(1));
2782
2881
  if (subcommand === 'asset') return ueyeAsset(args.slice(1));
2783
2882
  if (subcommand === 'revision') return ueyeRevision(args.slice(1));
2883
+ if (subcommand === 'production') return ueyeProduction(args.slice(1));
2784
2884
  console.error(`unknown ueye command: ${subcommand}`);
2785
2885
  return ueyeUsage();
2786
2886
  }
@@ -2799,6 +2899,11 @@ Usage:
2799
2899
  yam ueye asset verify [--manifest file] [--json]
2800
2900
  yam ueye revision archive --file image --round n [--artifact-id id] [--root dir] [--manifest file] [--json]
2801
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]
2802
2907
 
2803
2908
  Notes:
2804
2909
  capture uses a locally available Playwright install when present. It does not download browsers or install dependencies.
@@ -2806,6 +2911,7 @@ Notes:
2806
2911
  report produces a proof-ready Ueye visual run report, design completion gate, and continuity/comparison record without requiring a new capture.
2807
2912
  asset records local reference provenance and protection flags; it never downloads remote content.
2808
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.
2809
2915
  `);
2810
2916
  }
2811
2917
 
@@ -2880,6 +2986,196 @@ async function ueyeRevision(args = []) {
2880
2986
  }
2881
2987
  }
2882
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
+
2883
3179
  async function ueyePreflight(args = []) {
2884
3180
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return ueyeUsage();
2885
3181
  const parsed = parseDirJsonArgs(args);
@@ -3719,12 +4015,37 @@ async function missionGate(args = []) {
3719
4015
  receipts
3720
4016
  });
3721
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;
3722
4041
  const result = {
3723
4042
  ...gate,
3724
4043
  blockers,
3725
- ready_to_claim_complete: gate.ready_to_claim_complete && loadErrors.length === 0,
4044
+ ready_to_claim_complete: strictReady,
3726
4045
  next_action: blockers[0] || gate.next_action,
3727
- truth_status: blockers.length ? 'blocked' : gate.truth_status,
4046
+ truth_status: strictReady ? gate.truth_status : 'blocked',
4047
+ gate_result: gateResult,
4048
+ gate_contract: gateContract,
3728
4049
  persistence: flags.out ? 'written' : 'not-persisted',
3729
4050
  out: flags.out ? path.resolve(expandHome(flags.out)) : ''
3730
4051
  };
@@ -3810,15 +4131,72 @@ function benchmarkUsage() {
3810
4131
 
3811
4132
  Usage:
3812
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]
3813
4135
 
3814
4136
  Notes:
3815
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.
3816
4139
  `);
3817
4140
  }
3818
4141
 
3819
4142
  async function benchmarkReport(args = []) {
3820
4143
  if (args[0] === 'help' || args[0] === '--help' || args[0] === '-h') return benchmarkUsage();
3821
- 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
+ }
3822
4200
  const baseline = numberOrNull(flags.baseline);
3823
4201
  const current = numberOrNull(flags.current);
3824
4202
  const target = String(flags.target || 'lower').toLowerCase() === 'higher' ? 'higher' : 'lower';
@@ -4413,7 +4791,12 @@ function formatStructuredEvidence(value) {
4413
4791
  return JSON.stringify(buildMissionSubagentReceipt(data));
4414
4792
  }
4415
4793
  if (data.schema === 'yam.mission-completion-gate.v1' || data.expected_thread_ids || data.ready_to_claim_complete !== undefined) {
4416
- return JSON.stringify(buildMissionCompletionGate(data));
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
+ });
4417
4800
  }
4418
4801
  if (data.schema === 'yam.mission-patch-envelope.v1' || data.agent_id || data.assigned_scope) {
4419
4802
  return JSON.stringify(buildMissionPatchEnvelope(data));
@@ -5415,6 +5798,11 @@ function showPath() {
5415
5798
  function showRequestedHelp(args: string[]) {
5416
5799
  if (!args.some((arg) => arg === '--help' || arg === '-h')) return false;
5417
5800
 
5801
+ if (args[0] === 'ueye' && args[1] === 'production') {
5802
+ ueyeProductionUsage();
5803
+ return true;
5804
+ }
5805
+
5418
5806
  const commandUsage: Record<string, () => void> = {
5419
5807
  context: contextUsage,
5420
5808
  cleanup: cleanupUsage,