sneakoscope 7.3.0 → 7.5.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.
Files changed (151) hide show
  1. package/README.md +117 -5
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/cli/codex-app-command.js +1 -1
  5. package/dist/cli/command-manifest-lite.js +2 -1
  6. package/dist/cli/install-helpers-codex-lb-selftest.js +2 -1
  7. package/dist/cli/install-helpers-install-support.js +6 -16
  8. package/dist/cli/install-helpers.js +59 -50
  9. package/dist/cli/router.js +9 -1
  10. package/dist/config/skills-manifest.json +50 -71
  11. package/dist/core/codex-app.js +5 -5
  12. package/dist/core/codex-native/core-skill-manifest.js +9 -6
  13. package/dist/core/commands/image-ux-review-command.js +243 -50
  14. package/dist/core/commands/naruto-command.js +245 -8
  15. package/dist/core/commands/ppt-command.js +2 -2
  16. package/dist/core/commands/telegram-command.js +123 -10
  17. package/dist/core/commands/triwiki-command.js +11 -1
  18. package/dist/core/commands/triwiki-graph-command.js +160 -0
  19. package/dist/core/commands/wiki-command.js +100 -20
  20. package/dist/core/errors/message.js +3 -0
  21. package/dist/core/feature-registry.js +2 -2
  22. package/dist/core/fsx.js +7 -3
  23. package/dist/core/hooks-runtime/codex-commit-hooks-selftest.js +82 -0
  24. package/dist/core/hooks-runtime/context-graph-freshness-preflight.js +113 -0
  25. package/dist/core/hooks-runtime/official-subagent-lifecycle.js +1 -1
  26. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  27. package/dist/core/hooks-runtime.js +28 -86
  28. package/dist/core/image-ux-review/callout-extraction.js +6 -0
  29. package/dist/core/image-ux-review/imagegen-adapter.js +54 -18
  30. package/dist/core/image-ux-review/real-callout-extractor.js +1 -0
  31. package/dist/core/image-ux-review.js +182 -7
  32. package/dist/core/imagegen/codex-lb-imagegen-target.js +22 -7
  33. package/dist/core/imagegen/imagegen-capability.js +11 -4
  34. package/dist/core/imagegen/require-imagegen.js +29 -5
  35. package/dist/core/init/skills.js +22 -10
  36. package/dist/core/install/installed-package-smoke.js +157 -4
  37. package/dist/core/managed-path-safety.js +8 -10
  38. package/dist/core/mcp-config/backup.js +2 -4
  39. package/dist/core/mcp-config/config-reader.js +2 -4
  40. package/dist/core/mcp-config/scope.js +3 -5
  41. package/dist/core/naruto/context-graph-advisor-scope.js +242 -0
  42. package/dist/core/naruto/context-graph-advisor.js +0 -0
  43. package/dist/core/pipeline-internals/runtime-core.js +44 -0
  44. package/dist/core/policy/latest-version-guidance.js +113 -0
  45. package/dist/core/ppt.js +8 -8
  46. package/dist/core/release/gate-manifest.js +32 -1
  47. package/dist/core/release/npm-stage-contract.js +21 -0
  48. package/dist/core/release/package-size-budget.js +2 -2
  49. package/dist/core/release/release-gate-contract.js +5 -0
  50. package/dist/core/release/stage-publish.js +27 -0
  51. package/dist/core/responses-stream.js +65 -19
  52. package/dist/core/routes/design-policy.js +7 -2
  53. package/dist/core/routes/dollar-manifest-lite.js +0 -4
  54. package/dist/core/routes/evidence.js +1 -1
  55. package/dist/core/routes.js +12 -8
  56. package/dist/core/safety/command-contract/contracts.js +2 -0
  57. package/dist/core/safety/command-contract/types.js +1 -1
  58. package/dist/core/search/context-graph-seeds.js +205 -0
  59. package/dist/core/search/context.js +0 -0
  60. package/dist/core/search/provider.js +2 -2
  61. package/dist/core/subagents/naruto-help-contract.js +2 -1
  62. package/dist/core/subagents/naruto-host-credentials.js +161 -0
  63. package/dist/core/subagents/official-subagent-preparation.js +4 -1
  64. package/dist/core/subagents/official-subagent-prompt.js +44 -2
  65. package/dist/core/subagents/official-subagent-runner.js +26 -8
  66. package/dist/core/subagents/triwiki-attention.js +135 -123
  67. package/dist/core/telegram/bot-api.js +59 -4
  68. package/dist/core/telegram/config.js +3 -2
  69. package/dist/core/telegram/hub.js +3 -2
  70. package/dist/core/telegram/owner-lock.js +4 -6
  71. package/dist/core/telegram/setup.js +223 -73
  72. package/dist/core/triwiki/code-pack.js +12 -120
  73. package/dist/core/triwiki/context-graph/compiler/cache-key.js +277 -0
  74. package/dist/core/triwiki/context-graph/compiler/extract.js +104 -0
  75. package/dist/core/triwiki/context-graph/compiler/freshness.js +108 -0
  76. package/dist/core/triwiki/context-graph/compiler/index.js +237 -0
  77. package/dist/core/triwiki/context-graph/compiler/merge.js +0 -0
  78. package/dist/core/triwiki/context-graph/compiler/serialize.js +115 -0
  79. package/dist/core/triwiki/context-graph/contracts.js +236 -0
  80. package/dist/core/triwiki/context-graph/extractors/code/builders.js +150 -0
  81. package/dist/core/triwiki/context-graph/extractors/code/declarations.js +161 -0
  82. package/dist/core/triwiki/context-graph/extractors/code/index.js +265 -0
  83. package/dist/core/triwiki/context-graph/extractors/code/inventory.js +237 -0
  84. package/dist/core/triwiki/context-graph/extractors/code/module-graph.js +252 -0
  85. package/dist/core/triwiki/context-graph/extractors/code/modules.js +79 -0
  86. package/dist/core/triwiki/context-graph/extractors/code/references.js +184 -0
  87. package/dist/core/triwiki/context-graph/extractors/code/selection.js +100 -0
  88. package/dist/core/triwiki/context-graph/extractors/code/ts-config.js +161 -0
  89. package/dist/core/triwiki/context-graph/extractors/code/types.js +18 -0
  90. package/dist/core/triwiki/context-graph/extractors/evidence/claims.js +289 -0
  91. package/dist/core/triwiki/context-graph/extractors/evidence/index.js +54 -0
  92. package/dist/core/triwiki/context-graph/extractors/evidence/proof-index.js +213 -0
  93. package/dist/core/triwiki/context-graph/extractors/evidence/proofs.js +266 -0
  94. package/dist/core/triwiki/context-graph/extractors/evidence/redaction.js +177 -0
  95. package/dist/core/triwiki/context-graph/extractors/evidence/shared.js +0 -0
  96. package/dist/core/triwiki/context-graph/extractors/evidence/sources.js +154 -0
  97. package/dist/core/triwiki/context-graph/extractors/index.js +7 -0
  98. package/dist/core/triwiki/context-graph/extractors/topology/commands.js +207 -0
  99. package/dist/core/triwiki/context-graph/extractors/topology/gate-edges.js +201 -0
  100. package/dist/core/triwiki/context-graph/extractors/topology/gates.js +182 -0
  101. package/dist/core/triwiki/context-graph/extractors/topology/globs.js +124 -0
  102. package/dist/core/triwiki/context-graph/extractors/topology/index.js +92 -0
  103. package/dist/core/triwiki/context-graph/extractors/topology/routes.js +135 -0
  104. package/dist/core/triwiki/context-graph/extractors/topology/shared.js +215 -0
  105. package/dist/core/triwiki/context-graph/graph-index.js +143 -0
  106. package/dist/core/triwiki/context-graph/ids.js +0 -0
  107. package/dist/core/triwiki/context-graph/lint/index.js +0 -0
  108. package/dist/core/triwiki/context-graph/lint/rules.js +232 -0
  109. package/dist/core/triwiki/context-graph/lint/warnings.js +93 -0
  110. package/dist/core/triwiki/context-graph/paths.js +116 -0
  111. package/dist/core/triwiki/context-graph/profiles.js +78 -0
  112. package/dist/core/triwiki/context-graph/projections/anchors.js +91 -0
  113. package/dist/core/triwiki/context-graph/projections/attention.js +58 -0
  114. package/dist/core/triwiki/context-graph/projections/code-pack-workspace.js +62 -0
  115. package/dist/core/triwiki/context-graph/projections/code-pack.js +144 -0
  116. package/dist/core/triwiki/context-graph/projections/index.js +7 -0
  117. package/dist/core/triwiki/context-graph/projections/module-view.js +140 -0
  118. package/dist/core/triwiki/context-graph/projections/node-summary.js +118 -0
  119. package/dist/core/triwiki/context-graph/projections/pack-contract.js +6 -0
  120. package/dist/core/triwiki/context-graph/query/explain.js +0 -0
  121. package/dist/core/triwiki/context-graph/query/index.js +178 -0
  122. package/dist/core/triwiki/context-graph/query/load.js +116 -0
  123. package/dist/core/triwiki/context-graph/query/pack.js +117 -0
  124. package/dist/core/triwiki/context-graph/query/rank.js +142 -0
  125. package/dist/core/triwiki/context-graph/query/ranking-config.js +64 -0
  126. package/dist/core/triwiki/context-graph/query/seeds.js +241 -0
  127. package/dist/core/triwiki/context-graph/query/snapshot-cache.js +72 -0
  128. package/dist/core/triwiki/context-graph/query/traverse.js +154 -0
  129. package/dist/core/triwiki/context-graph/query-types.js +29 -0
  130. package/dist/core/triwiki/context-graph/store/compile-lock.js +21 -0
  131. package/dist/core/triwiki/context-graph/store/event-log.js +48 -0
  132. package/dist/core/triwiki/context-graph/store/fragment-cache.js +85 -0
  133. package/dist/core/triwiki/context-graph/store/graph-status.js +85 -0
  134. package/dist/core/triwiki/context-graph/store/snapshot-store.js +155 -0
  135. package/dist/core/triwiki/triwiki-proof-bank-index-repair.js +115 -0
  136. package/dist/core/triwiki/triwiki-proof-bank-index-store.js +290 -0
  137. package/dist/core/triwiki/triwiki-proof-bank-index.js +126 -0
  138. package/dist/core/triwiki/triwiki-proof-bank.js +7 -1
  139. package/dist/core/verification/context-graph-affected.js +271 -0
  140. package/dist/core/version.js +1 -1
  141. package/dist/scripts/context-graph-check.js +191 -0
  142. package/dist/scripts/helper-dedup-check.js +0 -1
  143. package/dist/scripts/installed-package-smoke-check.js +4 -1
  144. package/dist/scripts/latest-version-guidance-check.js +74 -0
  145. package/dist/scripts/release-metadata-check.js +20 -3
  146. package/dist/scripts/ux-review-imagegen-blackbox-check.js +25 -1
  147. package/dist/scripts/ux-review-run-wires-imagegen-check.js +9 -1
  148. package/package.json +5 -3
  149. package/release-gates.v2.json +174 -0
  150. package/schemas/triwiki/context-graph.schema.json +119 -0
  151. package/dist/core/triwiki/code-index-scanner.js +0 -309
@@ -12,7 +12,7 @@ import { buildIssueLedgerFromGeneratedCallouts } from './image-ux-review/callout
12
12
  import { planImageUxFixTasks } from './image-ux-review/fix-task-planner.js';
13
13
  import { runImageUxFixLoop } from './image-ux-review/fix-loop.js';
14
14
  import { buildRecapturePlan } from './image-ux-review/recapture.js';
15
- import { addVisualAnchor, ingestImage } from './wiki-image/image-voxel-ledger.js';
15
+ import { addImageRelation, addVisualAnchor, ingestImage } from './wiki-image/image-voxel-ledger.js';
16
16
  import { validateFinalHonestModeReport } from './artifact-schemas.js';
17
17
  import { imagegenEvidenceClassBlockers, isFullImagegenOutputSource } from './imagegen/imagegen-evidence.js';
18
18
  export const IMAGE_UX_REVIEW_GATE_ARTIFACT = 'image-ux-review-gate.json';
@@ -69,6 +69,35 @@ export function imageUxReviewGateAllowsReferenceCloseout(gate = {}) {
69
69
  && gate?.verified_level === 'verified_partial'
70
70
  && gate?.full_review_passed !== true;
71
71
  }
72
+ export function imageUxReviewCommandOutcome(gate = {}, proofOk = false, opts = {}) {
73
+ const reviewReportCompleted = gate?.review_report_completed === true;
74
+ const reviewOnlyCompletionAllowed = opts.allowReviewOnlyCompletion === true;
75
+ const reviewOnlyCompleted = reviewOnlyCompletionAllowed && reviewReportCompleted;
76
+ const gateAndProofPassed = gate?.passed === true && proofOk === true;
77
+ const fullReviewPassed = gate?.full_review_passed === true && gateAndProofPassed;
78
+ return {
79
+ ok: gateAndProofPassed || reviewOnlyCompleted,
80
+ status: fullReviewPassed
81
+ ? 'passed'
82
+ : reviewOnlyCompleted
83
+ ? 'review_completed'
84
+ : gateAndProofPassed
85
+ ? gate?.status || 'passed'
86
+ : 'blocked',
87
+ completion_scope: fullReviewPassed
88
+ ? 'full_review_and_optional_fix_verification'
89
+ : reviewOnlyCompleted
90
+ ? 'capture_imagegen_ocr_and_ux_report'
91
+ : null,
92
+ review_report_completed: reviewReportCompleted,
93
+ review_only_completion_allowed: reviewOnlyCompletionAllowed,
94
+ full_review_passed: gate?.full_review_passed === true,
95
+ requires_human_review: gate?.requires_human_review === true,
96
+ unresolved_p0_p1_count: Number(gate?.unresolved_p0_p1_count || 0),
97
+ safety_blockers: Array.isArray(gate?.blockers) ? gate.blockers : [],
98
+ proof_ok: proofOk === true
99
+ };
100
+ }
72
101
  function cleanText(value, fallback = '') {
73
102
  const text = String(value ?? '').replace(/\s+/g, ' ').trim();
74
103
  return text || fallback;
@@ -340,6 +369,17 @@ export function buildImageUxIssueLedger(contract = {}, generatedReviewLedger = b
340
369
  };
341
370
  }
342
371
  export function buildImageUxIterationReport(contract = {}, policy = buildImageUxReviewPolicy(contract), generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract), issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger), fixTaskPlan = planImageUxFixTasks(issueLedger), fixLoop = runImageUxFixLoop(issueLedger, fixTaskPlan), recapturePlan = buildRecapturePlan(fixLoop)) {
372
+ const generatedImage = (generatedReviewLedger.generated_review_images || [])[0] || null;
373
+ const issues = Array.isArray(issueLedger.issues) ? issueLedger.issues : [];
374
+ const basedOnGeneratedImage = issueLedger.extracted_from_generated_callout === true
375
+ && issues.length > 0
376
+ && issues.every((issue) => issue.extracted_from_generated_image === true
377
+ && issue.generated_image_sha256 === generatedImage?.sha256);
378
+ const requiresHumanReview = (fixTaskPlan.tasks || []).some((task) => task.requires_human_review === true);
379
+ const severityCounts = Object.fromEntries(['P0', 'P1', 'P2', 'P3'].map((severity) => [
380
+ severity,
381
+ issues.filter((issue) => issue.severity === severity).length
382
+ ]));
343
383
  const passed = generatedReviewLedger.passed === true
344
384
  && issueLedger.passed === true
345
385
  && fixLoop.passed === true
@@ -351,6 +391,43 @@ export function buildImageUxIterationReport(contract = {}, policy = buildImageUx
351
391
  schema_version: 2,
352
392
  created_at: nowIso(),
353
393
  contract_hash: contract.sealed_hash || null,
394
+ report_type: 'ux_ui_change_report',
395
+ review_report_completed: generatedReviewLedger.passed === true
396
+ && issueLedger.validation?.ok === true
397
+ && basedOnGeneratedImage,
398
+ full_review_passed: passed,
399
+ requires_human_review: requiresHumanReview,
400
+ unresolved_p0_p1_count: Number(issueLedger.blocking_issue_count || 0),
401
+ analysis_basis: {
402
+ primary_evidence: 'generated_annotated_review_image',
403
+ generated_image_path: generatedImage?.path || null,
404
+ generated_image_sha256: generatedImage?.sha256 || null,
405
+ generated_image_id: generatedImage?.id || null,
406
+ source_screen_id: generatedImage?.source_screen_id || 'screen-1',
407
+ issue_extraction_source: issueLedger.extraction_source || IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT,
408
+ based_on_generated_image: basedOnGeneratedImage
409
+ },
410
+ executive_summary: {
411
+ issue_count: issues.length,
412
+ severity_counts: severityCounts,
413
+ highest_priority: issues.find((issue) => ['P0', 'P1'].includes(issue.severity))?.severity
414
+ || issues[0]?.severity
415
+ || null
416
+ },
417
+ recommendations: issues.map((issue) => ({
418
+ issue_id: issue.id,
419
+ callout_id: issue.callout_id,
420
+ severity: issue.severity,
421
+ title: issue.title,
422
+ observed_problem: issue.detail,
423
+ likely_cause: issue.likely_cause,
424
+ recommended_ux_ui_change: issue.fix_action,
425
+ target_surface: issue.target_surface,
426
+ generated_image_bbox: issue.bbox,
427
+ generated_image_sha256: issue.generated_image_sha256,
428
+ confidence: issue.confidence,
429
+ verification_status: issue.fix_verification_status
430
+ })),
354
431
  loop_policy: {
355
432
  max_full_surface_passes: policy.max_full_surface_passes,
356
433
  max_screen_retries: policy.max_screen_retries,
@@ -416,6 +493,15 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
416
493
  const wrongnessChecked = parts.wrongnessChecked === true || generatedReviewLedger.blockers?.length === 0;
417
494
  const honestModeEvidence = parts.honestModeEvidence || null;
418
495
  const honestModeComplete = parts.honestModeComplete === true && (honestModeEvidence?.ok === true || parts.honestModeEvidenceRequired !== true);
496
+ const requiresHumanReview = (fixTaskPlan.tasks || []).some((task) => task.requires_human_review === true);
497
+ const reviewReportCompleted = realSourceScreenshotPresent
498
+ && officialOrUserScreenshotSource
499
+ && generatedReviewLedger.passed === true
500
+ && realGeneratedCount > 0
501
+ && calloutExtractionSchemaValid
502
+ && issueLedger.extracted_from_generated_callout === true
503
+ && iterationReport.review_report_completed === true
504
+ && iterationReport.analysis_basis?.based_on_generated_image === true;
419
505
  const referenceCloseoutPassed = generatedImageUnavailable
420
506
  && realSourceScreenshotPresent
421
507
  && officialOrUserScreenshotSource
@@ -436,6 +522,10 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
436
522
  status: 'blocked',
437
523
  verified_level: 'blocked',
438
524
  full_review_passed: false,
525
+ review_report_completed: reviewReportCompleted,
526
+ completion_scope: reviewReportCompleted ? 'capture_imagegen_ocr_and_ux_report' : null,
527
+ requires_human_review: requiresHumanReview,
528
+ unresolved_p0_p1_count: Number(issueLedger.blocking_issue_count || 0),
439
529
  reference_only: false,
440
530
  real_source_screenshot_present: realSourceScreenshotPresent,
441
531
  computer_use_or_user_screenshot_source: officialOrUserScreenshotSource,
@@ -448,7 +538,8 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
448
538
  fix_loop_executed_or_not_needed: fixLoopExecutedOrNotNeeded,
449
539
  changed_screens_rechecked: changedScreensRechecked,
450
540
  image_voxel_reference_anchor_created: imageVoxelReferenceAnchorCreated,
451
- image_voxel_relations_created: parts.imageVoxelRelationsCreated === true || realGeneratedImages.some((image) => image.image_voxel_relation === 'generated_callout_review_of') === true,
541
+ image_voxel_relations_created: parts.imageVoxelRelationsCreated === true,
542
+ image_voxel_relation_evidence: parts.imageVoxelRelationEvidence || null,
452
543
  wrongness_checked: wrongnessChecked,
453
544
  honest_mode_complete: honestModeComplete,
454
545
  required_artifacts: [
@@ -497,8 +588,18 @@ export function defaultImageUxReviewGate(contract = {}, parts = {}) {
497
588
  return {
498
589
  ...gate,
499
590
  passed: fullPassed || referenceCloseoutPassed,
500
- status: fullPassed ? 'passed' : referenceCloseoutPassed ? 'verified_partial_reference' : 'blocked',
501
- verified_level: fullPassed ? 'verified' : referenceCloseoutPassed ? 'verified_partial' : 'blocked',
591
+ status: fullPassed
592
+ ? 'passed'
593
+ : referenceCloseoutPassed
594
+ ? 'verified_partial_reference'
595
+ : reviewReportCompleted
596
+ ? 'review_completed'
597
+ : 'blocked',
598
+ verified_level: fullPassed
599
+ ? 'verified'
600
+ : referenceCloseoutPassed || reviewReportCompleted
601
+ ? 'verified_partial'
602
+ : 'blocked',
502
603
  full_review_passed: fullPassed,
503
604
  reference_only: referenceCloseoutPassed
504
605
  };
@@ -515,6 +616,7 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
515
616
  const existingImagegenResponse = await readExistingJson(dir, IMAGE_UX_REVIEW_GPT_IMAGE_2_RESPONSE_ARTIFACT);
516
617
  const generatedReviewLedger = buildImageUxGeneratedReviewLedger(contract, inventory, existingGenerated, { root });
517
618
  const sourceReferenceEvidence = await ensureImageUxSourceReferenceEvidence(root, dir, inventory);
619
+ const imageVoxelRelationEvidence = await ensureImageUxGeneratedRelationEvidence(root, dir, inventory, generatedReviewLedger, sourceReferenceEvidence);
518
620
  const issueLedger = buildImageUxIssueLedger(contract, generatedReviewLedger, existingIssues);
519
621
  const fixTaskPlan = planImageUxFixTasks(issueLedger);
520
622
  const fixLoop = runImageUxFixLoop(issueLedger, fixTaskPlan, opts.fixLoop || {});
@@ -540,7 +642,8 @@ export async function writeImageUxReviewRouteArtifacts(dir, contract = {}, opts
540
642
  recapturePlan,
541
643
  iterationReport,
542
644
  imageVoxelReferenceAnchorCreated: sourceReferenceEvidence.ok === true,
543
- imageVoxelRelationsCreated: opts.imageVoxelRelationsCreated === true,
645
+ imageVoxelRelationsCreated: opts.imageVoxelRelationsCreated === true || imageVoxelRelationEvidence.ok === true,
646
+ imageVoxelRelationEvidence,
544
647
  wrongnessChecked: opts.wrongnessChecked === true,
545
648
  honestModeComplete: honestModeEvidence.ok === true,
546
649
  honestModeEvidenceRequired: true,
@@ -599,7 +702,9 @@ export function imageUxReviewProofEvidence(gate = {}, artifacts = {}) {
599
702
  open_p0_p1_count: issueLedger.blocking_issue_count || 0,
600
703
  fixed_p0_p1_count: (issueLedger.issues || []).filter((issue) => ['P0', 'P1'].includes(issue.severity) && issue.status === 'fixed').length,
601
704
  recapture_re_review_status: artifacts.recapture_plan?.changed_screens_rechecked_or_not_applicable ? 'complete_or_not_applicable' : 'blocked',
602
- image_voxel_relation_count: referenceOnly ? 0 : realGeneratedImages.filter((image) => image.image_voxel_relation).length,
705
+ image_voxel_relation_count: referenceOnly
706
+ ? 0
707
+ : gate.image_voxel_relations_created === true ? realGeneratedImages.length : 0,
603
708
  computer_use_evidence_mode: artifacts.inventory?.source_screens?.some((screen) => screen.capture_source === 'codex_native_computer_use_screenshot' || screen.capture_source === 'codex_computer_use_screenshot') ? 'native_computer_use_source_screenshot' : artifacts.inventory?.source_screens?.some((screen) => screen.capture_source === 'codex_chrome_extension_screenshot') ? 'chrome_extension_source_screenshot' : 'user_or_static_screenshot',
604
709
  claims: {
605
710
  ux_review_source_screenshot_verified: artifacts.inventory?.passed === true,
@@ -609,7 +714,7 @@ export function imageUxReviewProofEvidence(gate = {}, artifacts = {}) {
609
714
  ux_review_changed_screens_rechecked: artifacts.recapture_plan?.changed_screens_rechecked_or_not_applicable === true,
610
715
  ux_review_image_voxel_relations_verified: referenceOnly
611
716
  ? gate.source_reference_evidence?.ok === true
612
- : realGeneratedImages.some((image) => image.image_voxel_relation)
717
+ : gate.image_voxel_relations_created === true
613
718
  },
614
719
  full_verification_blockers: gate.full_verification_blockers || gate.blockers || [],
615
720
  blockers: gate.blockers || []
@@ -668,6 +773,72 @@ async function ensureImageUxSourceReferenceEvidence(root, dir, inventory = {}) {
668
773
  issues
669
774
  };
670
775
  }
776
+ async function ensureImageUxGeneratedRelationEvidence(root, dir, inventory = {}, generatedReviewLedger = {}, sourceReferenceEvidence = {}) {
777
+ const missionId = path.basename(String(dir));
778
+ const sourceScreen = (inventory.source_screens || []).find((screen) => screen.status === 'captured' && screen.source);
779
+ const generatedImages = (generatedReviewLedger.generated_review_images || [])
780
+ .filter((image) => image.real_generated === true && image.mock !== true && image.path);
781
+ if (!missionId || !sourceScreen || !generatedImages.length || sourceReferenceEvidence.ok !== true) {
782
+ return {
783
+ schema: 'sks.image-ux-generated-relation-evidence.v1',
784
+ ok: false,
785
+ reason: !missionId
786
+ ? 'mission_id_missing'
787
+ : !sourceScreen
788
+ ? 'captured_source_screenshot_missing'
789
+ : !generatedImages.length
790
+ ? 'real_generated_image_missing'
791
+ : 'source_reference_anchor_missing',
792
+ relations: 0,
793
+ issues: []
794
+ };
795
+ }
796
+ const sourceImageId = `${missionId}-${sourceScreen.id}-source`;
797
+ const anchorIds = Array.isArray(sourceReferenceEvidence.anchor_ids) ? sourceReferenceEvidence.anchor_ids : [];
798
+ const relationIds = [];
799
+ const issues = [];
800
+ for (const image of generatedImages) {
801
+ const generatedImageId = `${missionId}-${image.id}`;
802
+ try {
803
+ const ingested = await ingestImage(root, image.path, {
804
+ missionId,
805
+ source: 'image-ux-review:gpt-image-2-generated-callout',
806
+ id: generatedImageId
807
+ });
808
+ if (!ingested.ok) {
809
+ issues.push(...(ingested.validation?.issues || [`generated_image_ingest_failed:${image.id}`]));
810
+ continue;
811
+ }
812
+ const relation = await addImageRelation(root, {
813
+ missionId,
814
+ route: '$Image-UX-Review',
815
+ type: 'generated_callout_review_of',
816
+ beforeImageId: sourceImageId,
817
+ afterImageId: generatedImageId,
818
+ sourceImageId,
819
+ generatedImageId,
820
+ anchors: anchorIds,
821
+ status: 'verified_partial',
822
+ verification: 'gpt-image-2-generated-callout-bound-to-source'
823
+ });
824
+ if (relation.ok)
825
+ relationIds.push(`${sourceImageId}->${generatedImageId}`);
826
+ else
827
+ issues.push(...(relation.validation?.issues || [`generated_image_relation_failed:${image.id}`]));
828
+ }
829
+ catch (err) {
830
+ issues.push(err instanceof Error ? err.message : String(err));
831
+ }
832
+ }
833
+ return {
834
+ schema: 'sks.image-ux-generated-relation-evidence.v1',
835
+ ok: relationIds.length === generatedImages.length && issues.length === 0,
836
+ relations: relationIds.length,
837
+ relation_ids: relationIds,
838
+ anchor_ids: anchorIds,
839
+ issues
840
+ };
841
+ }
671
842
  async function ensureImageUxHonestModeEvidence(dir, parts = {}, opts = {}) {
672
843
  const missionId = path.basename(String(dir));
673
844
  const existing = await readExistingJson(dir, IMAGE_UX_REVIEW_HONEST_MODE_ARTIFACT);
@@ -744,6 +915,10 @@ export async function buildImageUxCalloutExtractionReport(root, extraction = {},
744
915
  schema: 'sks.image-ux-callout-extraction-report.v1',
745
916
  created_at: nowIso(),
746
917
  provider: extraction.provider || opts.provider || 'unknown',
918
+ analysis_input: 'generated_annotated_review_image',
919
+ generated_image_is_primary_evidence: true,
920
+ source_screenshot_used_for_extraction: false,
921
+ generated_image_id: opts.generatedImageId || ledger.generated_image_id || issues[0]?.generated_review_image_id || null,
747
922
  generated_image_path: generatedPath,
748
923
  generated_image_sha256: generatedSha,
749
924
  source_screenshot_sha256: sourceSha,
@@ -6,13 +6,20 @@ export const CODEX_LB_IMAGEGEN_TARGET_SCHEMA = 'sks.codex-lb-imagegen-target.v1'
6
6
  export async function resolveCodexLbImagegenTarget(opts = {}) {
7
7
  const env = opts.env || process.env;
8
8
  const home = opts.home || env.HOME || os.homedir();
9
+ const codexHome = opts.codexHome || env.CODEX_HOME || path.join(home, '.codex');
9
10
  const configText = typeof opts.configText === 'string'
10
11
  ? opts.configText
11
- : await readText(path.join(home, '.codex', 'config.toml'), '').catch(() => '');
12
+ : await readText(path.join(codexHome, 'config.toml'), '').catch(() => '');
12
13
  const selected = topLevelTomlString(configText, 'model_provider') === 'codex-lb';
13
- const loaded = await loadCodexLbEnv({ home, processEnv: env }).catch(() => null);
14
- const catalogModels = await readCatalogModels(configText, home);
15
- const explicit = String(opts.explicitModel || env.SKS_IMAGEGEN_RESPONSES_MODEL || env.OPENAI_MODEL || '').trim();
14
+ const loaded = await loadCodexLbEnv({
15
+ home,
16
+ processEnv: env,
17
+ envPath: path.join(codexHome, 'sks-codex-lb.env'),
18
+ legacyEnvPath: path.join(codexHome, 'sks.env'),
19
+ metadataPath: path.join(codexHome, 'sks-codex-lb.json')
20
+ }).catch(() => null);
21
+ const catalogModels = await readCatalogModels(configText, home, codexHome);
22
+ const explicit = String(opts.explicitModel || env.SKS_IMAGEGEN_RESPONSES_MODEL || '').trim();
16
23
  const configuredModel = topLevelTomlString(configText, 'model');
17
24
  const { model, model_source } = explicit
18
25
  ? { model: explicit, model_source: 'explicit' }
@@ -46,16 +53,17 @@ function codexLbImagegenBlocker(state) {
46
53
  return 'codex_lb_imagegen_model_unresolved';
47
54
  return null;
48
55
  }
49
- async function readCatalogModels(configText, home) {
56
+ async function readCatalogModels(configText, home, codexHome) {
50
57
  const configured = topLevelTomlString(configText, 'model_catalog_json');
51
58
  const candidates = [
52
- configured ? path.resolve(configured.replace(/^~(?=\/|$)/, home)) : '',
53
- path.join(home, '.codex', 'sks-codex-lb-tool-catalog.json')
59
+ configured ? resolveCatalogPath(configured, home, codexHome) : '',
60
+ path.join(codexHome, 'sks-codex-lb-tool-catalog.json')
54
61
  ].filter(Boolean);
55
62
  for (const file of candidates) {
56
63
  const payload = await readJson(file, null).catch(() => null);
57
64
  const rows = Array.isArray(payload?.models) ? payload.models : Array.isArray(payload?.data) ? payload.data : [];
58
65
  const slugs = rows
66
+ .filter((row) => row?.supported_in_api !== false && !hiddenCatalogRow(row))
59
67
  .map((row) => String(row?.slug || row?.id || row?.model || '').trim())
60
68
  .filter(Boolean);
61
69
  if (slugs.length)
@@ -63,6 +71,13 @@ async function readCatalogModels(configText, home) {
63
71
  }
64
72
  return [];
65
73
  }
74
+ function resolveCatalogPath(configured, home, codexHome) {
75
+ const expanded = configured.replace(/^~(?=\/|$)/, home);
76
+ return path.isAbsolute(expanded) ? expanded : path.resolve(codexHome, expanded);
77
+ }
78
+ function hiddenCatalogRow(row) {
79
+ return ['hide', 'hidden', 'internal', 'none', 'unavailable'].includes(String(row?.visibility || '').trim().toLowerCase());
80
+ }
66
81
  function topLevelTomlString(text, key) {
67
82
  const topLevel = String(text || '').split(/\n\s*\[/)[0] || '';
68
83
  const re = new RegExp(`(^|\\n)\\s*${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*=\\s*"([^"]*)"\\s*(?:#.*)?(?=\\n|$)`);
@@ -1,6 +1,6 @@
1
1
  import os from 'node:os';
2
2
  import path from 'node:path';
3
- import { codexLbEnvPath, loadCodexLbEnv, parseShellEnvValue } from '../codex-lb/codex-lb-env.js';
3
+ import { loadCodexLbEnv, parseShellEnvValue } from '../codex-lb/codex-lb-env.js';
4
4
  import { nowIso, readText, runProcess, which } from '../fsx.js';
5
5
  import { redactSecrets, redactString } from '../secret-redaction.js';
6
6
  import { evaluateImagegenAuthReadiness } from './imagegen-auth-readiness.js';
@@ -97,7 +97,8 @@ export async function detectImagegenCapability(opts = {}) {
97
97
  }
98
98
  async function detectCodexLbImagegenAuth(opts = {}, env = process.env) {
99
99
  const home = opts.home || env.HOME || process.env.HOME || os.homedir();
100
- const configPath = opts.configPath || path.join(home, '.codex', 'config.toml');
100
+ const codexHome = opts.codexHome || env.CODEX_HOME || path.join(home, '.codex');
101
+ const configPath = opts.configPath || path.join(codexHome, 'config.toml');
101
102
  const configText = typeof opts.configText === 'string'
102
103
  ? opts.configText
103
104
  : await readText(configPath, '').catch(() => '');
@@ -107,11 +108,17 @@ async function detectCodexLbImagegenAuth(opts = {}, env = process.env) {
107
108
  const requiresOpenAiAuth = tomlBoolean(block, 'requires_openai_auth');
108
109
  const envKey = tomlString(block, 'env_key');
109
110
  const baseUrl = tomlString(block, 'base_url') || String(env.CODEX_LB_BASE_URL || '').trim();
110
- const envPath = opts.codexLbEnvPath || codexLbEnvPath(home);
111
+ const envPath = opts.codexLbEnvPath || path.join(codexHome, 'sks-codex-lb.env');
111
112
  const injectedEnvText = typeof opts.codexLbEnvText === 'string' ? opts.codexLbEnvText : null;
112
113
  const keyFromInjectedFile = injectedEnvText !== null && envKey ? parseShellEnvValue(injectedEnvText, envKey) : '';
113
114
  const loaded = injectedEnvText === null
114
- ? await loadCodexLbEnv({ home, processEnv: env, envPath }).catch(() => null)
115
+ ? await loadCodexLbEnv({
116
+ home,
117
+ processEnv: env,
118
+ envPath,
119
+ legacyEnvPath: path.join(codexHome, 'sks.env'),
120
+ metadataPath: path.join(codexHome, 'sks-codex-lb.json')
121
+ }).catch(() => null)
115
122
  : null;
116
123
  const apiKeyPresent = Boolean(opts.codexLbApiKey || keyFromInjectedFile || loaded?.secret_api_key);
117
124
  const apiKeySource = opts.codexLbApiKey
@@ -1,15 +1,27 @@
1
1
  import { detectImagegenCapability } from './imagegen-capability.js';
2
2
  import { repairCodexImagegen } from '../doctor/imagegen-repair.js';
3
3
  export async function requireCodexImagegen(root, opts = {}) {
4
- const capability = await detectImagegenCapability({
5
- codexBin: opts.codexBin || undefined,
4
+ const capabilityOpts = {
6
5
  timeoutMs: opts.timeoutMs || 5000
7
- }).catch((err) => ({
6
+ };
7
+ if (opts.codexBin !== undefined && opts.codexBin !== null)
8
+ capabilityOpts.codexBin = opts.codexBin;
9
+ if (opts.home !== undefined)
10
+ capabilityOpts.home = opts.home;
11
+ if (opts.codexHome !== undefined)
12
+ capabilityOpts.codexHome = opts.codexHome;
13
+ if (opts.env !== undefined)
14
+ capabilityOpts.env = opts.env;
15
+ if (opts.configText !== undefined)
16
+ capabilityOpts.configText = opts.configText;
17
+ if (opts.codexLbEnvText !== undefined)
18
+ capabilityOpts.codexLbEnvText = opts.codexLbEnvText;
19
+ const capability = await detectImagegenCapability(capabilityOpts).catch((err) => ({
8
20
  ok: false,
9
21
  core_ready: false,
10
22
  blockers: [err instanceof Error ? err.message : String(err)]
11
23
  }));
12
- const capabilityReadyBeforeRepair = capability.core_ready === true;
24
+ const capabilityReadyBeforeRepair = imagegenPreflightReady(capability);
13
25
  const repair = opts.autoRepair === true && !capabilityReadyBeforeRepair
14
26
  ? await repairCodexImagegen({
15
27
  root,
@@ -25,7 +37,10 @@ export async function requireCodexImagegen(root, opts = {}) {
25
37
  const finalCapability = repair
26
38
  ? repair.after || capability
27
39
  : capability;
28
- const capabilityReady = finalCapability.core_ready === true || repair?.capability_ready === true;
40
+ const capabilityReady = imagegenPreflightReady(finalCapability) || repair?.capability_ready === true;
41
+ const preflightProvider = finalCapability.core_ready === true
42
+ ? 'codex_app_builtin'
43
+ : codexLbImagegenReady(finalCapability) ? 'codex_lb' : null;
29
44
  const currentTaskToolManifestVerified = repair?.current_task_tool_manifest_verified === true;
30
45
  const generatedOutputVerified = finalCapability.real_output_verified_by_capability_check === true
31
46
  || repair?.real_generation_verified === true;
@@ -55,6 +70,7 @@ export async function requireCodexImagegen(root, opts = {}) {
55
70
  preflight_ready: preflightReady,
56
71
  preflight_only: true,
57
72
  preflight_does_not_satisfy_generated_output_proof: true,
73
+ preflight_provider: preflightProvider,
58
74
  capability_ready: capabilityReady,
59
75
  route_ready: routeReady,
60
76
  current_task_tool_manifest_verified: currentTaskToolManifestVerified,
@@ -80,3 +96,11 @@ export async function requireCodexImagegen(root, opts = {}) {
80
96
  blockers
81
97
  };
82
98
  }
99
+ function imagegenPreflightReady(capability) {
100
+ return capability?.core_ready === true || codexLbImagegenReady(capability);
101
+ }
102
+ function codexLbImagegenReady(capability) {
103
+ return capability?.codex_lb?.selected === true
104
+ && capability?.codex_lb?.available === true
105
+ && capability?.codex_lb?.blocker == null;
106
+ }
@@ -12,7 +12,7 @@ import { reconcileRetiredAgentRoleResidue } from '../agents/agent-role-config.js
12
12
  import { ensureConfinedDirectory, inspectConfinedPath, moveConfinedPath, publicPathError, removeConfinedDirectoryIfEmpty, removeManagedPathVerified, uniqueConfinedPath } from '../managed-path-safety.js';
13
13
  import { collectNestedProjectRoots } from '../doctor/current-project-guidance-nested.js';
14
14
  import { coreEngineeringDirectiveReferenceText, engineeringSanityPolicyText } from '../lean-engineering-policy.js';
15
- import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_EVIDENCE_SOURCE, CODEX_IMAGEGEN_REQUIRED_POLICY, CODEX_WEB_VERIFICATION_POLICY, DEFAULT_CODEX_APP_PLUGINS, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, IMAGEGEN_SOCIAL_SOURCE_POLICY, LEGACY_DOLLAR_SKILL_NAMES, OPENAI_CHATGPT_IMAGES_2_DOC_URL, OPENAI_GPT_IMAGE_2_MODEL_DOC_URL, OPENAI_IMAGE_GENERATION_DOC_URL, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_SKILLS, RESERVED_CODEX_PLUGIN_SKILL_NAMES, SOLUTION_SCOUT_SKILL_NAME, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, imageUxReviewPipelinePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, productDesignPluginPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTrackingText, triwikiStagePolicyText } from '../routes.js';
15
+ import { AWESOME_DESIGN_MD_REFERENCE, CODEX_APP_IMAGE_GENERATION_DOC_URL, CODEX_COMPUTER_USE_ONLY_POLICY, CODEX_IMAGEGEN_EVIDENCE_SOURCE, CODEX_IMAGEGEN_REQUIRED_POLICY, CODEX_WEB_VERIFICATION_POLICY, DEFAULT_CODEX_APP_PLUGINS, DESIGN_SYSTEM_SSOT, DOLLAR_COMMANDS, DOLLAR_SKILL_NAMES, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, GETDESIGN_REFERENCE, IMAGEGEN_SOCIAL_SOURCE_POLICY, LEGACY_DOLLAR_SKILL_NAMES, OPENAI_CHATGPT_IMAGES_2_DOC_URL, OPENAI_GPT_IMAGE_2_MODEL_DOC_URL, OPENAI_IMAGE_GENERATION_DOC_URL, PPT_CONDITIONAL_SKILL_ALLOWLIST, PPT_PIPELINE_MCP_ALLOWLIST, PPT_PIPELINE_SKILL_ALLOWLIST, RECOMMENDED_SKILLS, RESERVED_CODEX_PLUGIN_SKILL_NAMES, SOLUTION_SCOUT_SKILL_NAME, chatCaptureIntakeText, context7ConfigToml, getdesignReferencePolicyText, outcomeRubricPolicyText, pptPipelineAllowlistPolicyText, productDesignPluginPolicyText, speedLanePolicyText, stackCurrentDocsPolicyText, triwikiContextTrackingText, triwikiStagePolicyText } from '../routes.js';
16
16
  import { prefixKnownSksDollarReferences, sksPrefixedSkillName } from '../routes/dollar-prefix.js';
17
17
  import { escapeRegExp } from '../text/regex.js';
18
18
  const SKS_SKILL_MANIFEST_FILE = '.sks-generated.json';
@@ -21,6 +21,12 @@ const GENERATED_PRUNE_POLICY = 'remove_previous_sks_generated_paths_absent_from_
21
21
  const REFLECTION_MEMORY_PATH = '.sneakoscope/memory/q2_facts/post-route-reflection.md';
22
22
  const MANAGED_SKILL_MARKER_RE = /BEGIN SKS (?:IMMUTABLE CORE|MANAGED) SKILL/;
23
23
  const FORGE_SKILL_MARKER_RE = /BEGIN SKS FORGE SKILL/;
24
+ const RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIASES = [
25
+ 'sks-ux-review',
26
+ 'sks-visual-review',
27
+ 'sks-ui-ux-review'
28
+ ];
29
+ const RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIAS_SET = new Set(RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIASES);
24
30
  export const REMOVED_SKS_SKILL_NAMES = [
25
31
  'old-workflow',
26
32
  'team-legacy',
@@ -85,7 +91,6 @@ function reflectionInstructionText(commandPrefix = 'sks') {
85
91
  async function installOfficialSkills(root) {
86
92
  const quarantinedUserCollisions = [];
87
93
  const quarantinedManifestCollisions = await prepareReservedSkillManifestsForWrite(root);
88
- const imageUxReviewSkill = (name) => `---\nname: ${name}\ndescription: $Image-UX-Review/$UX-Review imagegen/gpt-image-2 annotated UI/UX review loop.\n---\n\nUse only for $Image-UX-Review, $UX-Review, $visual-review, or $ui-ux-review UI/UX review requests. ${imageUxReviewPipelinePolicyText()} Route start must check Codex App imagegen capability and run the SKS imagegen repair loop once; if $imagegen/gpt-image-2 is still unavailable, stop with codex_imagegen_unavailable instead of doing text-only review or direct API substitution. Core loop: capture or attach source UI screenshots, then invoke Codex App $imagegen with gpt-image-2 to create a new generated annotated review image from each source screenshot, then analyze the generated review image with vision/OCR into image-ux-issue-ledger.json, then apply only requested safe fixes and recheck changed screens. Text-only screenshot critique cannot satisfy full verification; missing generated annotated review images keep full image-ux-review-gate.json verification blocked, but may close verified_partial/reference-only when source screenshots plus hashes, docs evidence, source Image Voxel anchors, and Honest Mode evidence exist. For live web/browser/webapp capture use Codex Chrome Extension first and halt if it is not installed/enabled; use Codex Computer Use only for native Mac/non-web app screens. Required artifacts: image-ux-review-policy.json, image-ux-screen-inventory.json, image-ux-generated-review-ledger.json, image-ux-issue-ledger.json, image-ux-iteration-report.json, image-ux-review-gate.json. Finish with reflection and Honest Mode.\n`;
89
94
  const canonicalMadSksSqlPlanePolicy = madSksSqlPlanePolicyText()
90
95
  .replace(/^---[\s\S]*?---\s*/, '')
91
96
  .trim();
@@ -136,10 +141,6 @@ async function installOfficialSkills(root) {
136
141
  'gx-visual-validate': `---\nname: gx-visual-validate\ndescription: Validate render metadata against vgraph.json and beta.json.\n---\n\nRun sks gx validate and drift; fail stale or incomplete hashes, nodes, edges, invariants, or anchors.\n`,
137
142
  'turbo-context-pack': `---\nname: turbo-context-pack\ndescription: Build ultra-low-token context packet with Q4 bits, Q3 tags, top-K claims, and minimal evidence.\n---\n\nDefault to Q4/Q3 plus TriWiki RGBA anchors and attention.use_first. Add Q2/Q1 only when needed or when attention.hydrate_first says source hydration is required. Keep id, hash, path, and coordinate tuple for hydration.\n`,
138
143
  'performance-evaluator': `---\nname: performance-evaluator\ndescription: Evaluate SKS performance, evidence-backed token usage, serialized-size proxies, accuracy-proxy, context-compression, or workflow improvements.\n---\n\nUse sks eval run/compare before claims. Treat serialized_size_bytes as a deterministic non-token proxy. Claim token savings only when actual baseline and candidate token counts include an evidence source; otherwise report token savings as not measured. Report accuracy_delta/proxy, required_recall, support, and meaningful_improvement without substituting proxy size for token evidence.\n`,
139
- 'image-ux-review': imageUxReviewSkill('image-ux-review'),
140
- 'ux-review': imageUxReviewSkill('ux-review'),
141
- 'visual-review': imageUxReviewSkill('visual-review'),
142
- 'ui-ux-review': imageUxReviewSkill('ui-ux-review'),
143
144
  'imagegen': `---\nname: imagegen\ndescription: Required bridge to Codex App built-in image generation for logos, image assets, raster visuals, and image edits.\n---\n\nUse for generated or edited image assets: logo, product image, illustration, sprite, mockup, texture, cutout, or bitmap. Prefer the official Codex App built-in image generation feature documented at ${CODEX_APP_IMAGE_GENERATION_DOC_URL}: ask naturally or invoke \`$imagegen\`. SKS route code checks capability before image-dependent routes, attempts doctor imagegen repair once, and reports codex_imagegen_unavailable if Codex App $imagegen still is not ready. For newest-model requests, make the prompt explicit: "Use ChatGPT Images 2.0 / GPT Image 2.0 with gpt-image-2." Useful official references are ${OPENAI_CHATGPT_IMAGES_2_DOC_URL}, ${OPENAI_GPT_IMAGE_2_MODEL_DOC_URL}, and ${OPENAI_IMAGE_GENERATION_DOC_URL}. Codex App image generation counts against Codex usage limits. Capability detection is not output proof; full SKS evidence requires a real selected raster output path or generated review image artifact. Direct OpenAI API fallback is non-Codex evidence and does not satisfy SKS route evidence unless a separate non-Codex API task is explicitly requested. ${IMAGEGEN_SOCIAL_SOURCE_POLICY} ${CODEX_IMAGEGEN_REQUIRED_POLICY} Do not substitute placeholder SVG/HTML/CSS for requested raster assets; follow design.md when relevant.\n`,
144
145
  'imagegen-source-scout': `---\nname: imagegen-source-scout\ndescription: Source scout for current GPT Image 2.0/gpt-image-2 prompt guidance, official docs, and X/social workflow signals.\n---\n\nUse when the user asks for the latest imagegen docs, ChatGPT Images 2.0 / GPT Image 2.0 / gpt-image-2 behavior, X/social reactions, prompt examples, or community workflow hints before creating an image prompt or SKS imagegen policy. Source order: official OpenAI announcement (${OPENAI_CHATGPT_IMAGES_2_DOC_URL}), Codex App image generation docs (${CODEX_APP_IMAGE_GENERATION_DOC_URL}), gpt-image-2 model docs (${OPENAI_GPT_IMAGE_2_MODEL_DOC_URL}), OpenAI Image Generation API docs (${OPENAI_IMAGE_GENERATION_DOC_URL}), then public X/social/community search for prompt-quality heuristics only. ${IMAGEGEN_SOCIAL_SOURCE_POLICY} If social or web search is unavailable, record that social coverage is unverified and continue from official docs. Output a compact evidence split: official capability/evidence rules, prompt heuristics, social/workflow signals, and blockers. Do not generate images itself; pair this with the imagegen skill for actual raster output.\n`,
145
146
  'getdesign-reference': `---\nname: getdesign-reference\ndescription: Use getdesign.md official design reference as an input to the design.md SSOT for UI/UX, presentation, and HTML/PDF systems.\n---\n\nUse when creating or improving design.md, UI/UX design systems, deck-like HTML artifacts, presentation PDFs, or brand-inspired visual systems. design.md is the only design decision SSOT; reference ${GETDESIGN_REFERENCE.url}, ${GETDESIGN_REFERENCE.docs_url}, and ${AWESOME_DESIGN_MD_REFERENCE.url} only as source inputs to synthesize or update that SSOT or a route-local style-token artifact. Prefer the official Codex skill if available with \`${GETDESIGN_REFERENCE.codex_skill_install}\`. If the skill CLI is unavailable, use this generated skill plus official docs/API/CLI/SDK references and curated DESIGN.md examples as inputs. Do not claim getdesign MCP is configured unless a current official MCP surface is actually installed.\n`,
@@ -907,7 +908,9 @@ function buildFallbackSkillsManifest() {
907
908
  ...RECOMMENDED_SKILLS.map((name) => canonicalSkillNameFromValue(name)),
908
909
  ...DOLLAR_COMMANDS.map((command) => canonicalSkillNameFromValue(String(command.command || '').replace(/^\$/, ''))),
909
910
  ...buildSksCoreSkillManifest().skills.map((skill) => skill.canonical_name)
910
- ].filter((name) => Boolean(name) && !REMOVED_SKS_SKILL_NAME_SET.has(String(name))));
911
+ ].filter((name) => Boolean(name)
912
+ && !REMOVED_SKS_SKILL_NAME_SET.has(String(name))
913
+ && !RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIAS_SET.has(String(name))));
911
914
  return {
912
915
  schema: PACKAGED_SKILLS_MANIFEST_SCHEMA,
913
916
  package_version: PACKAGE_VERSION,
@@ -1161,7 +1164,7 @@ function isGeneratedSksPluginCollisionSkill(text, name) {
1161
1164
  return /Maximum-speed \$Computer-Use\/\$CU lane|Codex App pipeline activation:|Sneakoscope generated|Dollar-command route generated by SKS/i.test(s);
1162
1165
  }
1163
1166
  function enrichSkillContent(name, content) {
1164
- if (!['sks', 'answer', 'wiki', 'qa-loop', 'ppt', 'image-ux-review', 'ux-review', 'visual-review', 'ui-ux-review', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'reflection', 'prompt-pipeline', 'pipeline-runner', 'context7-docs', 'turbo-context-pack', 'hproof-evidence-bind'].includes(name))
1167
+ if (!['sks', 'answer', 'wiki', 'qa-loop', 'ppt', 'image-ux-review', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'reflection', 'prompt-pipeline', 'pipeline-runner', 'context7-docs', 'turbo-context-pack', 'hproof-evidence-bind'].includes(name))
1165
1168
  return content;
1166
1169
  const text = String(content || '').trimEnd();
1167
1170
  const activation = pipelineActivationText(name);
@@ -1179,7 +1182,7 @@ Context tracking:
1179
1182
  `;
1180
1183
  }
1181
1184
  function pipelineActivationText(name) {
1182
- const stateful = new Set(['sks', 'qa-loop', 'ppt', 'image-ux-review', 'ux-review', 'visual-review', 'ui-ux-review', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'prompt-pipeline', 'pipeline-runner']);
1185
+ const stateful = new Set(['sks', 'qa-loop', 'ppt', 'image-ux-review', 'computer-use-fast', 'cu', 'goal', 'research', 'autoresearch', 'db', 'gx', 'prompt-pipeline', 'pipeline-runner']);
1183
1186
  if (!stateful.has(name))
1184
1187
  return '';
1185
1188
  return `Codex App pipeline activation:
@@ -1229,6 +1232,7 @@ async function removeGeneratedAgentSkillAliases(root, skillNames) {
1229
1232
  const current = new Set(skillNames);
1230
1233
  const obsolete = [
1231
1234
  ...SKS_SKILL_NAMES_TO_CLEAN_UP,
1235
+ ...RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIASES,
1232
1236
  'qaloop',
1233
1237
  'wiki-refresh',
1234
1238
  'wikirefresh',
@@ -1242,7 +1246,7 @@ async function removeGeneratedAgentSkillAliases(root, skillNames) {
1242
1246
  if (current.has(name))
1243
1247
  continue;
1244
1248
  const dir = path.join(root, '.agents', 'skills', name);
1245
- const action = await cleanupGeneratedSkillResidue(root, dir, name, (text) => isGeneratedSksAgentSkill(text, name), 'generated-agent-skill-user-content-collision');
1249
+ const action = await cleanupGeneratedSkillResidue(root, dir, name, (text) => isGeneratedSksAgentSkill(text, name) || isRetiredGeneratedImageUxReviewAlias(text, name), 'generated-agent-skill-user-content-collision');
1246
1250
  if (action === 'removed')
1247
1251
  removed.push(path.relative(root, dir));
1248
1252
  if (action === 'quarantined')
@@ -1282,6 +1286,14 @@ function isGeneratedSksAgentSkill(text, name) {
1282
1286
  return false;
1283
1287
  return /Sneakoscope generated|Fallback Codex App picker alias|Codex App picker alias for|Dollar-command route generated by SKS/i.test(s);
1284
1288
  }
1289
+ function isRetiredGeneratedImageUxReviewAlias(text, name) {
1290
+ if (!RETIRED_PUBLIC_IMAGE_UX_REVIEW_SKILL_ALIAS_SET.has(String(name)))
1291
+ return false;
1292
+ const s = String(text || '');
1293
+ return MANAGED_SKILL_MARKER_RE.test(s)
1294
+ && new RegExp(`^name:\\s*${escapeRegExp(name)}\\s*$`, 'm').test(s)
1295
+ && /imagegen\/gpt-image-2 annotated UI\/UX review loop/i.test(s);
1296
+ }
1285
1297
  function isGeneratedSksLegacySkill(text, name) {
1286
1298
  if (typeof text !== 'string')
1287
1299
  return false;