yam-flow 0.1.7 → 0.1.8

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.
@@ -17,6 +17,8 @@ export type EvidenceKind = 'command' | 'evidence' | 'visual' | 'runtime' | 'skip
17
17
  export type RuntimeSubsystem = 'real_runtime' | 'tmux_physical' | 'process_cleanup' | 'browser_visual' | 'db_safety';
18
18
  export type RuntimeBackend = 'none' | 'in_app_browser' | 'playwright' | 'terminal' | 'tmux' | 'zellij' | 'unknown';
19
19
  export type RuntimeClaim = 'not_started' | 'started' | 'observed' | 'stopped' | 'cleanup_verified' | 'blocked' | 'unknown';
20
+ export type LoopStageStatus = 'passed' | 'failed' | 'blocked' | 'skipped' | 'partial' | 'pending' | 'recorded';
21
+ export type ToolIntent = 'read_only' | 'write' | 'destructive' | 'runtime' | 'visual' | 'publish';
20
22
 
21
23
  export interface SafetyHit {
22
24
  level: SafetyLevel;
@@ -149,6 +151,48 @@ export interface RuntimeBackendEvidence {
149
151
  truth_status: TruthStatus;
150
152
  }
151
153
 
154
+ export interface StudyNoteSection {
155
+ code: string;
156
+ role: string;
157
+ symptom?: string;
158
+ summary?: string;
159
+ truth_status: TruthStatus;
160
+ }
161
+
162
+ export interface StudyNote {
163
+ schema: 'yam.study-note.v1';
164
+ problem: StudyNoteSection;
165
+ change: StudyNoteSection;
166
+ why_it_matters: string;
167
+ learning_note: string;
168
+ limits: string[];
169
+ truth_status: TruthStatus;
170
+ }
171
+
172
+ export interface LoopStage {
173
+ id: string;
174
+ status: LoopStageStatus;
175
+ note: string;
176
+ truth_status: TruthStatus;
177
+ }
178
+
179
+ export interface LoopReport {
180
+ schema: 'yam.loop-report.v1';
181
+ generated_at: string;
182
+ route: string;
183
+ intent: string;
184
+ loop_kind: string;
185
+ stages: LoopStage[];
186
+ evidence: string[];
187
+ blockers: string[];
188
+ truth_status: TruthStatus;
189
+ intent_label: ToolIntent;
190
+ tool_intent: ToolIntent;
191
+ next_action: string;
192
+ remaining_tasks: string[];
193
+ study_note: StudyNote;
194
+ }
195
+
152
196
  export interface UeyeRunReport {
153
197
  schema: 'yam.ueye-run-report.v1';
154
198
  reference_sources: UeyeVisualProvenance[];
@@ -452,6 +496,72 @@ export function buildRuntimeBackendEvidence(input: Partial<RuntimeBackendEvidenc
452
496
  };
453
497
  }
454
498
 
499
+ export function buildStudyNote(input: Partial<StudyNote> & Record<string, unknown> = {}): StudyNote {
500
+ const problem = buildStudyNoteSection(input.problem, {
501
+ code: input.issue_code,
502
+ role: input.issue_role,
503
+ symptom: input.issue_symptom
504
+ }, 'problem');
505
+ const change = buildStudyNoteSection(input.change, {
506
+ code: input.changed_code,
507
+ role: input.changed_role,
508
+ summary: input.change_summary
509
+ }, 'change');
510
+ const whyItMatters = shortText(input.why_it_matters || input.why_important);
511
+ const learningNote = shortText(input.learning_note);
512
+ const limits = [
513
+ ...asList(input.limits),
514
+ ...missingStudyNoteLimits(problem, change, whyItMatters, learningNote)
515
+ ];
516
+ const hasProblem = Boolean(problem.code || problem.role || problem.symptom);
517
+ const hasChange = Boolean(change.code || change.role || change.summary);
518
+ const truth: TruthStatus = isTruthStatus(input.truth_status) ? input.truth_status
519
+ : hasProblem || hasChange || whyItMatters || learningNote ? 'partial'
520
+ : 'assumed';
521
+ return {
522
+ schema: 'yam.study-note.v1',
523
+ problem,
524
+ change,
525
+ why_it_matters: whyItMatters,
526
+ learning_note: learningNote,
527
+ limits: [...new Set(limits)],
528
+ truth_status: truth
529
+ };
530
+ }
531
+
532
+ export function buildLoopReport(input: Partial<LoopReport> & Record<string, unknown> = {}): LoopReport {
533
+ const stages = Array.isArray(input.stages)
534
+ ? input.stages.map((stage) => buildLoopStage(stage))
535
+ : [];
536
+ const evidence = asList(input.evidence);
537
+ const blockers = asList(input.blockers || input.blocked);
538
+ const hasBlockedStage = stages.some((stage) => stage.status === 'blocked' || stage.status === 'failed');
539
+ const requestedTruth = isTruthStatus(input.truth_status) ? input.truth_status : '';
540
+ const derivedTruth: TruthStatus = blockers.length || hasBlockedStage ? 'blocked'
541
+ : evidence.length && stages.some((stage) => stage.status === 'passed') ? 'verified'
542
+ : evidence.length || stages.length ? 'partial'
543
+ : 'assumed';
544
+ const truth = requestedTruth ? weakestTruth(requestedTruth, derivedTruth) : derivedTruth;
545
+ const studyNote = buildStudyNote(input.study_note && typeof input.study_note === 'object' ? input.study_note as unknown as Record<string, unknown> : input);
546
+ const intentLabel = normalizeToolIntentLabel(input.intent_label || input.tool_intent);
547
+ return {
548
+ schema: 'yam.loop-report.v1',
549
+ generated_at: String(input.generated_at || new Date().toISOString()),
550
+ route: String(input.route || ''),
551
+ intent: shortText(input.intent),
552
+ loop_kind: String(input.loop_kind || 'harness'),
553
+ stages,
554
+ evidence,
555
+ blockers,
556
+ truth_status: truth,
557
+ intent_label: intentLabel,
558
+ tool_intent: intentLabel,
559
+ next_action: shortText(input.next_action || (blockers[0] ? 'resolve the blocker before claiming this loop complete' : 'record the next smallest useful action')),
560
+ remaining_tasks: asList(input.remaining_tasks || input.remaining_task),
561
+ study_note: studyNote
562
+ };
563
+ }
564
+
455
565
  export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunReport {
456
566
  const references = Array.isArray(input.reference_sources)
457
567
  ? input.reference_sources.map((item) => buildUeyeVisualProvenance(item))
@@ -494,6 +604,84 @@ export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunRepor
494
604
  };
495
605
  }
496
606
 
607
+ function buildStudyNoteSection(section: unknown, fallback: Record<string, unknown>, kind: 'problem' | 'change'): StudyNoteSection {
608
+ const source = section && typeof section === 'object' ? section as Record<string, unknown> : fallback;
609
+ const code = shortText(source.code);
610
+ const role = shortText(source.role);
611
+ const symptom = kind === 'problem' ? shortText(source.symptom) : undefined;
612
+ const summary = kind === 'change' ? shortText(source.summary) : undefined;
613
+ return {
614
+ code,
615
+ role,
616
+ ...(symptom !== undefined ? { symptom } : {}),
617
+ ...(summary !== undefined ? { summary } : {}),
618
+ truth_status: code || role || symptom || summary ? 'partial' : 'assumed'
619
+ };
620
+ }
621
+
622
+ function missingStudyNoteLimits(problem: StudyNoteSection, change: StudyNoteSection, whyItMatters = '', learningNote = ''): string[] {
623
+ const limits: string[] = [];
624
+ if (!problem.code) limits.push('issue_code not provided');
625
+ if (!problem.role) limits.push('issue_role not provided');
626
+ if (!problem.symptom) limits.push('issue_symptom not provided');
627
+ if (!change.code) limits.push('changed_code not provided');
628
+ if (!change.role) limits.push('changed_role not provided');
629
+ if (!change.summary) limits.push('change_summary not provided');
630
+ if (!whyItMatters) limits.push('why_it_matters not provided');
631
+ if (!learningNote) limits.push('learning_note not provided');
632
+ return limits;
633
+ }
634
+
635
+ function buildLoopStage(value: unknown = ''): LoopStage {
636
+ if (value && typeof value === 'object') {
637
+ const item = value as Partial<LoopStage>;
638
+ return loopStage(String(item.id || ''), item.status, String(item.note || ''));
639
+ }
640
+ const [id = '', rawStatus = '', ...noteParts] = String(value || '').split(':');
641
+ return loopStage(id, rawStatus, noteParts.join(':'));
642
+ }
643
+
644
+ function loopStage(id = '', rawStatus: unknown = '', note = ''): LoopStage {
645
+ const status = normalizeLoopStageStatus(rawStatus);
646
+ return {
647
+ id: shortText(id || 'stage'),
648
+ status,
649
+ note: shortText(note),
650
+ truth_status: stageTruth(status)
651
+ };
652
+ }
653
+
654
+ function normalizeLoopStageStatus(value: unknown = ''): LoopStageStatus {
655
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
656
+ if (text === 'pass' || text === 'passed' || text === 'ok') return 'passed';
657
+ if (text === 'fail' || text === 'failed' || text === 'error') return 'failed';
658
+ if (text === 'block' || text === 'blocked') return 'blocked';
659
+ if (text === 'skip' || text === 'skipped') return 'skipped';
660
+ if (text === 'partial') return 'partial';
661
+ if (text === 'pending') return 'pending';
662
+ return 'recorded';
663
+ }
664
+
665
+ function stageTruth(status: LoopStageStatus): TruthStatus {
666
+ if (status === 'passed') return 'verified';
667
+ if (status === 'failed' || status === 'blocked') return 'blocked';
668
+ if (status === 'skipped') return 'skipped';
669
+ if (status === 'pending') return 'assumed';
670
+ return 'partial';
671
+ }
672
+
673
+ function normalizeToolIntentLabel(value: unknown = ''): ToolIntent {
674
+ const text = String(value || '').toLowerCase().replace(/[-\s]/g, '_');
675
+ if (['read_only', 'write', 'destructive', 'runtime', 'visual', 'publish'].includes(text)) return text as ToolIntent;
676
+ return 'read_only';
677
+ }
678
+
679
+ function shortText(value: unknown = '', limit = 240): string {
680
+ const text = String(value || '').trim().replace(/\s+/g, ' ');
681
+ if (text.length <= limit) return text;
682
+ return `${text.slice(0, limit - 3)}...`;
683
+ }
684
+
497
685
  export function buildMediaGenerationProof(input: Partial<MediaGenerationProof> = {}): MediaGenerationProof {
498
686
  const requested = Boolean(input.generation_requested);
499
687
  const attempted = Boolean(input.generation_attempted);
package/yam.manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yam",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "principles": [
5
5
  "Direction before execution.",
6
6
  "Start fast.",
@@ -10,7 +10,8 @@
10
10
  "Heavy proof when risk or user intent calls for it.",
11
11
  "Token-aware by default.",
12
12
  "Context-reuse first.",
13
- "End with remaining tasks and fix-first items."
13
+ "End with remaining tasks and fix-first items.",
14
+ "Explain what failed, what changed, and why it matters in short non-specialist language when useful."
14
15
  ],
15
16
  "defaultHooks": false,
16
17
  "routes": [