yam-flow 2.1.0 → 2.2.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.
@@ -20,6 +20,7 @@ export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' |
20
20
  export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
21
21
  export type LoopEvidenceLevel = 'none' | 'fixture' | 'smoke' | 'local' | 'real';
22
22
  export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
23
+ export type ReadinessState = 'usable' | 'degraded' | 'blocked' | 'unknown';
23
24
 
24
25
  export interface SafetyHit {
25
26
  level: SafetyLevel;
@@ -189,6 +190,13 @@ export interface LoopReport {
189
190
  evidence_level: LoopEvidenceLevel;
190
191
  evidence_stamp: string;
191
192
  source_digest: string;
193
+ touched_files: string[];
194
+ read_files: string[];
195
+ verified_files: string[];
196
+ skipped_checks: string[];
197
+ stop_condition: string;
198
+ resume_hint: string;
199
+ readiness_state: ReadinessState;
192
200
  covered_requirements: string[];
193
201
  uncovered_requirements: string[];
194
202
  blockers: string[];
@@ -220,6 +228,7 @@ export interface UeyeRunReport {
220
228
  implementation_sources: UeyeVisualProvenance[];
221
229
  surface_context: UeyeSurfaceContext;
222
230
  design_completion_gate: UeyeDesignCompletionGate;
231
+ deep_visual_review: UeyeDeepVisualReview;
223
232
  comparison_result: string;
224
233
  design_quality: string;
225
234
  blocked_reason: string;
@@ -227,8 +236,29 @@ export interface UeyeRunReport {
227
236
  truth_status: TruthStatus;
228
237
  }
229
238
 
230
- export interface UeyeRunReportInput extends Partial<Omit<UeyeRunReport, 'design_completion_gate'>> {
239
+ export interface UeyeRunReportInput extends Partial<Omit<UeyeRunReport, 'design_completion_gate' | 'deep_visual_review'>> {
231
240
  design_completion_gate?: Partial<UeyeDesignCompletionGate> & Record<string, unknown>;
241
+ deep_visual_review?: Partial<UeyeDeepVisualReview> & Record<string, unknown>;
242
+ }
243
+
244
+ export interface UeyeDeepVisualReview {
245
+ schema: 'yam.ueye-deep-visual-review.v1';
246
+ acceptance_criteria: string[];
247
+ touched_files: string[];
248
+ read_files: string[];
249
+ verified_files: string[];
250
+ skipped_checks: string[];
251
+ residual_risks: string[];
252
+ stop_condition: string;
253
+ resume_hint: string;
254
+ deep_visual_checks: string[];
255
+ design_system_evidence: string[];
256
+ state_matrix: Record<string, string>;
257
+ implementation_evidence: string[];
258
+ blockers: string[];
259
+ warnings: string[];
260
+ next_action: string;
261
+ truth_status: TruthStatus;
232
262
  }
233
263
 
234
264
  export interface MediaGenerationProof {
@@ -555,24 +585,32 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
555
585
  ? input.stages.map((stage) => buildLoopStage(stage))
556
586
  : [];
557
587
  const evidence = asList(input.evidence);
588
+ const touchedFiles = asList(input.touched_files || input.touched_file);
589
+ const readFiles = asList(input.read_files || input.read_file);
590
+ const verifiedFiles = asList(input.verified_files || input.verified_file);
591
+ const skippedChecks = asList(input.skipped_checks || input.skipped_check);
592
+ const stopCondition = shortText(input.stop_condition);
593
+ const resumeHint = shortText(input.resume_hint);
594
+ const readinessState = normalizeReadinessState(input.readiness_state);
558
595
  const coveredRequirements = asList(input.covered_requirements || input.covered_requirement);
559
596
  const uncoveredRequirements = asList(input.uncovered_requirements || input.uncovered_requirement);
560
597
  const blockers = asList(input.blockers || input.blocked);
561
598
  const blockedBy = asList(input.blocked_by);
562
- const blockedKind = shortText(input.blocked_kind || (uncoveredRequirements.length ? 'requirement_uncovered' : blockers.length || blockedBy.length ? 'evidence_missing' : ''));
599
+ const blockedKind = shortText(input.blocked_kind || (readinessState === 'blocked' ? 'readiness_blocked' : uncoveredRequirements.length ? 'requirement_uncovered' : blockers.length || blockedBy.length ? 'evidence_missing' : ''));
563
600
  const failureCause = shortText(input.failure_cause);
564
601
  const recoveryHint = shortText(input.recovery_hint);
565
602
  const avoidanceNote = shortText(input.avoidance_note);
566
603
  const evidenceStamp = shortText(input.evidence_stamp || input.source_digest, 320);
567
604
  const sourceDigest = shortText(input.source_digest || input.evidence_stamp, 320);
568
605
  const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
569
- const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length);
606
+ const hasBlockerSignal = Boolean(blockers.length || blockedBy.length || blockedKind || uncoveredRequirements.length || readinessState === 'blocked');
570
607
  const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
571
608
  const derivedTruth: TruthStatus = hasBlockerSignal || hasBlockedStage ? 'blocked'
572
609
  : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
573
610
  : evidence.length || stages.length ? 'partial'
574
611
  : 'assumed';
575
- const truth = requestedTruth ? weakestTruth(requestedTruth, derivedTruth) : derivedTruth;
612
+ const readinessCap = readinessState === 'blocked' ? 'blocked' : readinessState === 'degraded' ? 'partial' : '';
613
+ const truth = requestedTruth ? weakestTruth(requestedTruth, derivedTruth, readinessCap) : weakestTruth(derivedTruth, readinessCap);
576
614
  const studyNote = buildStudyNote(input.study_note && typeof input.study_note === 'object' ? input.study_note as unknown as Record<string, unknown> : input);
577
615
  const intentLabel = normalizeToolIntentLabel(input.intent_label || input.tool_intent);
578
616
  return {
@@ -589,6 +627,13 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
589
627
  evidence_level: normalizeLoopEvidenceLevel(input.evidence_level),
590
628
  evidence_stamp: evidenceStamp,
591
629
  source_digest: sourceDigest,
630
+ touched_files: touchedFiles,
631
+ read_files: readFiles,
632
+ verified_files: verifiedFiles,
633
+ skipped_checks: skippedChecks,
634
+ stop_condition: stopCondition,
635
+ resume_hint: resumeHint,
636
+ readiness_state: readinessState,
592
637
  covered_requirements: coveredRequirements,
593
638
  uncovered_requirements: uncoveredRequirements,
594
639
  blockers,
@@ -597,7 +642,7 @@ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unkn
597
642
  truth_status: truth,
598
643
  intent_label: intentLabel,
599
644
  tool_intent: intentLabel,
600
- next_action: shortText(input.next_action || (uncoveredRequirements.length ? `cover requirement before claiming complete: ${uncoveredRequirements[0]}` : hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
645
+ next_action: shortText(input.next_action || (readinessState === 'blocked' ? 'restore readiness before claiming this loop complete' : uncoveredRequirements.length ? `cover requirement before claiming complete: ${uncoveredRequirements[0]}` : hasBlockerSignal ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
601
646
  safe_retry: shortText(input.safe_retry || (hasBlockerSignal ? 'retry only after the blocker is resolved and new evidence is recorded' : 'not required')),
602
647
  recovery_hint: recoveryHint,
603
648
  fix_first_items: asList(input.fix_first_items || input.fix_first_item),
@@ -625,6 +670,7 @@ export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunRepor
625
670
  const blockedReason = String(input.blocked_reason || '');
626
671
  const comparisonResult = String(input.comparison_result || 'not-verified');
627
672
  const hasImplementation = implementations.length > 0;
673
+ const deepVisualReview = buildUeyeDeepVisualReview(input.deep_visual_review);
628
674
  const derivedTruth: TruthStatus = blockedReason ? 'blocked'
629
675
  : !references.length && !hasImplementation ? 'assumed'
630
676
  : comparisonResult === 'matched' && hasImplementation ? 'verified'
@@ -639,7 +685,8 @@ export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunRepor
639
685
  blocked_reason: blockedReason
640
686
  });
641
687
  const shouldApplyDesignGate = gate.mode === 'strict' || gate.completion_claim === 'done' || gate.blockers.length > 0;
642
- const finalTruth = shouldApplyDesignGate ? weakestTruth(derivedTruth, gate.truth_status) : derivedTruth;
688
+ const deepTruthCap = deepVisualReview.truth_status === 'assumed' ? '' : deepVisualReview.truth_status;
689
+ const finalTruth = shouldApplyDesignGate ? weakestTruth(derivedTruth, gate.truth_status, deepTruthCap) : weakestTruth(derivedTruth, deepTruthCap);
643
690
  return {
644
691
  schema: 'yam.ueye-run-report.v1',
645
692
  reference_sources: references,
@@ -649,14 +696,78 @@ export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunRepor
649
696
  truth_status: isTruthStatus(input.surface_context?.truth_status) ? input.surface_context?.truth_status : finalTruth
650
697
  }),
651
698
  design_completion_gate: gate,
699
+ deep_visual_review: deepVisualReview,
652
700
  comparison_result: comparisonResult,
653
701
  design_quality: String(input.design_quality || 'not-checked'),
654
702
  blocked_reason: blockedReason,
655
- next_action: String(input.next_action || gate.blockers[0] || (finalTruth === 'verified' ? 'no action required' : 'capture or provide implementation screenshot before claiming verified visual status')),
703
+ next_action: String(input.next_action || gate.blockers[0] || deepVisualReview.blockers[0] || (finalTruth === 'verified' ? 'no action required' : 'capture or provide implementation screenshot before claiming verified visual status')),
656
704
  truth_status: isTruthStatus(input.truth_status) ? input.truth_status : finalTruth
657
705
  };
658
706
  }
659
707
 
708
+ export function buildUeyeDeepVisualReview(input: Partial<UeyeDeepVisualReview> & Record<string, unknown> = {}): UeyeDeepVisualReview {
709
+ const acceptanceCriteria = asList(input.acceptance_criteria || input.acceptance_criterion);
710
+ const touchedFiles = asList(input.touched_files || input.touched_file);
711
+ const readFiles = asList(input.read_files || input.read_file);
712
+ const verifiedFiles = asList(input.verified_files || input.verified_file);
713
+ const skippedChecks = asList(input.skipped_checks || input.skipped_check);
714
+ const residualRisks = asList(input.residual_risks || input.residual_risk);
715
+ const deepVisualChecks = asList(input.deep_visual_checks || input.deep_visual_check);
716
+ const designSystemEvidence = asList(input.design_system_evidence || input.design_system);
717
+ const implementationEvidence = asList(input.implementation_evidence || input.implementation_evidence_item);
718
+ const stateMatrix = buildStateMatrix(input.state_matrix, input);
719
+ const stopCondition = shortText(input.stop_condition);
720
+ const resumeHint = shortText(input.resume_hint);
721
+ const blockers = [
722
+ ...asList(input.blockers || input.blocked),
723
+ ...(Object.values(stateMatrix).some((status) => status === 'fail' || status === 'blocked') ? ['state_matrix: fix failed or blocked UI states before claiming done'] : [])
724
+ ];
725
+ const warnings = [
726
+ ...asList(input.warnings || input.warning),
727
+ ...skippedChecks.map((check) => `skipped: ${check}`),
728
+ ...residualRisks.map((risk) => `risk: ${risk}`),
729
+ ...(acceptanceCriteria.length && !implementationEvidence.length ? ['implementation_evidence: record how the UI was checked against acceptance criteria'] : [])
730
+ ];
731
+ const hasAnySignal = Boolean(
732
+ acceptanceCriteria.length ||
733
+ touchedFiles.length ||
734
+ readFiles.length ||
735
+ verifiedFiles.length ||
736
+ skippedChecks.length ||
737
+ residualRisks.length ||
738
+ stopCondition ||
739
+ resumeHint ||
740
+ deepVisualChecks.length ||
741
+ designSystemEvidence.length ||
742
+ implementationEvidence.length ||
743
+ Object.keys(stateMatrix).length
744
+ );
745
+ const truth: TruthStatus = blockers.length ? 'blocked'
746
+ : hasAnySignal && skippedChecks.length ? 'partial'
747
+ : hasAnySignal && implementationEvidence.length ? 'verified'
748
+ : hasAnySignal ? 'partial'
749
+ : 'assumed';
750
+ return {
751
+ schema: 'yam.ueye-deep-visual-review.v1',
752
+ acceptance_criteria: acceptanceCriteria,
753
+ touched_files: touchedFiles,
754
+ read_files: readFiles,
755
+ verified_files: verifiedFiles,
756
+ skipped_checks: skippedChecks,
757
+ residual_risks: residualRisks,
758
+ stop_condition: stopCondition,
759
+ resume_hint: resumeHint,
760
+ deep_visual_checks: deepVisualChecks,
761
+ design_system_evidence: designSystemEvidence,
762
+ state_matrix: stateMatrix,
763
+ implementation_evidence: implementationEvidence,
764
+ blockers,
765
+ warnings,
766
+ next_action: shortText(input.next_action || blockers[0] || warnings[0] || (hasAnySignal ? 'continue with the recorded Ueye verification boundary' : 'record acceptance criteria and visual evidence when Ueye needs deep verification')),
767
+ truth_status: isTruthStatus(input.truth_status) ? input.truth_status : truth
768
+ };
769
+ }
770
+
660
771
  function buildStudyNoteSection(section: unknown, fallback: Record<string, unknown>, kind: 'problem' | 'change'): StudyNoteSection {
661
772
  const source = section && typeof section === 'object' ? section as Record<string, unknown> : fallback;
662
773
  const code = shortText(source.code);
@@ -741,6 +852,43 @@ function normalizeLoopEvidenceLevel(value: unknown = ''): LoopEvidenceLevel {
741
852
  return 'none';
742
853
  }
743
854
 
855
+ function normalizeReadinessState(value: unknown = ''): ReadinessState {
856
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
857
+ if (['usable', 'degraded', 'blocked', 'unknown'].includes(text)) return text as ReadinessState;
858
+ return 'unknown';
859
+ }
860
+
861
+ function buildStateMatrix(rawStateMatrix: unknown = null, input: Record<string, unknown> = {}): Record<string, string> {
862
+ const result: Record<string, string> = {};
863
+ if (rawStateMatrix && typeof rawStateMatrix === 'object' && !Array.isArray(rawStateMatrix)) {
864
+ for (const [key, value] of Object.entries(rawStateMatrix as Record<string, unknown>)) {
865
+ const name = shortText(key, 80);
866
+ if (name) result[name] = normalizeStateCheckStatus(value);
867
+ }
868
+ }
869
+ for (const item of asList(input.state_check)) {
870
+ const [name = '', rawStatus = 'checked'] = item.split(':');
871
+ const key = shortText(name, 80);
872
+ if (key) result[key] = normalizeStateCheckStatus(rawStatus);
873
+ }
874
+ for (const key of ['default', 'loading', 'error', 'empty', 'disabled', 'hover', 'focus', 'mobile']) {
875
+ const value = input[`${key}_state`];
876
+ if (value !== undefined) result[key] = normalizeStateCheckStatus(value);
877
+ }
878
+ return result;
879
+ }
880
+
881
+ function normalizeStateCheckStatus(value: unknown = ''): string {
882
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
883
+ if (['pass', 'checked', 'verified', 'ok'].includes(text)) return 'pass';
884
+ if (['fail', 'failed', 'broken'].includes(text)) return 'fail';
885
+ if (['blocked', 'missing'].includes(text)) return 'blocked';
886
+ if (['skipped', 'skip'].includes(text)) return 'skipped';
887
+ if (['partial', 'needs_polish', 'warning'].includes(text)) return 'partial';
888
+ if (['not_checked', 'unknown'].includes(text)) return 'not_checked';
889
+ return shortText(value, 80) || 'checked';
890
+ }
891
+
744
892
  function shortText(value: unknown = '', limit = 240): string {
745
893
  const text = String(value || '').trim().replace(/\s+/g, ' ');
746
894
  if (text.length <= limit) return text;
@@ -59,7 +59,8 @@
59
59
  ## Report Policy
60
60
 
61
61
  - Study Note requirement for changed code/config/release metadata/docs/artifacts:
62
- - Explain touched code or artifact role, changed behavior, one syntax/structure insight, verification, and uncertainty:
62
+ - Explain touched code or artifact role, execution point, before/after behavior, one syntax/structure insight, verification, and uncertainty:
63
+ - Architecture hygiene checks for `page.tsx`, `global.css`, and DB `jsonb` overuse:
63
64
  - Always include remaining tasks or useful improvements:
64
65
  - Preserve source boundaries for research/proof notes:
65
66
 
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "principles": [
5
5
  "Direction before execution.",
6
6
  "Start fast.",
@@ -12,7 +12,8 @@
12
12
  "Context-reuse first.",
13
13
  "End with remaining tasks and fix-first items.",
14
14
  "When code, config, release metadata, documentation, or project artifacts changed, include a Study Note even if no yam skill was explicitly invoked.",
15
- "Explain what changed, what role it has, what was wrong or missing when relevant, what behavior is expected, and one useful syntax or structure insight in short non-specialist language.",
15
+ "Explain what changed, what role it has, where it runs or is read, what was wrong or missing when relevant, before/after behavior, expected behavior, and one useful syntax or structure insight in short non-specialist language.",
16
+ "When relevant, include architecture hygiene: avoid dumping unrelated logic into page.tsx, one-off component CSS into global.css, or structured product data into broad DB jsonb blobs when better project boundaries exist.",
16
17
  "Use handoff notes for fix-first items, remaining tasks, recommended next direction, requirement coverage, blocker kind, failure cause, recovery hint, safe retry, evidence level, and owner scope when useful.",
17
18
  "Use bounded heavy verification for serious claims, with explicit stop conditions instead of endless proof loops."
18
19
  ],