yam-flow 0.1.6 → 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;
@@ -137,10 +139,60 @@ export interface RuntimeBackendEvidence {
137
139
  evidence_id: string;
138
140
  command: string;
139
141
  cleanup_checked: boolean;
142
+ started_at: string;
143
+ stopped_at: string;
144
+ exit_code: number | null;
145
+ pid: number | null;
146
+ port: number | null;
147
+ cleanup_method: string;
148
+ cleanup_observed: boolean;
149
+ left_running_intentionally: boolean;
140
150
  note: string;
141
151
  truth_status: TruthStatus;
142
152
  }
143
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
+
144
196
  export interface UeyeRunReport {
145
197
  schema: 'yam.ueye-run-report.v1';
146
198
  reference_sources: UeyeVisualProvenance[];
@@ -409,6 +461,13 @@ export function buildMissionPatchEnvelope(input: Partial<MissionPatchEnvelope> =
409
461
  export function buildRuntimeBackendEvidence(input: Partial<RuntimeBackendEvidence> = {}): RuntimeBackendEvidence {
410
462
  const backend = normalizeRuntimeBackend(input.backend);
411
463
  const claim = normalizeRuntimeClaim(input.claim);
464
+ const cleanupObserved = Boolean(input.cleanup_observed);
465
+ const leftRunningIntentionally = Boolean(input.left_running_intentionally);
466
+ const exitCode = numberOrNull(input.exit_code);
467
+ const pid = numberOrNull(input.pid);
468
+ const port = numberOrNull(input.port);
469
+ const stoppedAt = String(input.stopped_at || '');
470
+ const cleanupMethod = String(input.cleanup_method || '');
412
471
  return {
413
472
  schema: 'yam.runtime-backend-evidence.v1',
414
473
  backend,
@@ -416,8 +475,90 @@ export function buildRuntimeBackendEvidence(input: Partial<RuntimeBackendEvidenc
416
475
  evidence_id: String(input.evidence_id || ''),
417
476
  command: String(input.command || ''),
418
477
  cleanup_checked: Boolean(input.cleanup_checked),
478
+ started_at: String(input.started_at || ''),
479
+ stopped_at: stoppedAt,
480
+ exit_code: exitCode,
481
+ pid,
482
+ port,
483
+ cleanup_method: cleanupMethod,
484
+ cleanup_observed: cleanupObserved,
485
+ left_running_intentionally: leftRunningIntentionally,
419
486
  note: String(input.note || ''),
420
- truth_status: isTruthStatus(input.truth_status) ? input.truth_status : runtimeBackendTruth(backend, claim, Boolean(input.cleanup_checked))
487
+ truth_status: isTruthStatus(input.truth_status) ? input.truth_status : runtimeBackendTruth(backend, claim, {
488
+ cleanupChecked: Boolean(input.cleanup_checked),
489
+ cleanupObserved,
490
+ leftRunningIntentionally,
491
+ stoppedAt,
492
+ exitCode,
493
+ pid,
494
+ cleanupMethod
495
+ })
496
+ };
497
+ }
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
421
562
  };
422
563
  }
423
564
 
@@ -463,6 +604,84 @@ export function buildUeyeRunReport(input: UeyeRunReportInput = {}): UeyeRunRepor
463
604
  };
464
605
  }
465
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
+
466
685
  export function buildMediaGenerationProof(input: Partial<MediaGenerationProof> = {}): MediaGenerationProof {
467
686
  const requested = Boolean(input.generation_requested);
468
687
  const attempted = Boolean(input.generation_attempted);
@@ -834,11 +1053,24 @@ function normalizeRuntimeClaim(value: unknown = ''): RuntimeClaim {
834
1053
  return 'unknown';
835
1054
  }
836
1055
 
837
- function runtimeBackendTruth(backend: RuntimeBackend, claim: RuntimeClaim, cleanupChecked = false): TruthStatus {
1056
+ function runtimeBackendTruth(backend: RuntimeBackend, claim: RuntimeClaim, details: {
1057
+ cleanupChecked?: boolean;
1058
+ cleanupObserved?: boolean;
1059
+ leftRunningIntentionally?: boolean;
1060
+ stoppedAt?: string;
1061
+ exitCode?: number | null;
1062
+ pid?: number | null;
1063
+ cleanupMethod?: string;
1064
+ } = {}): TruthStatus {
838
1065
  if (claim === 'blocked') return 'blocked';
839
1066
  if (backend === 'none' || claim === 'not_started') return 'skipped';
840
1067
  if (backend === 'unknown' || claim === 'unknown') return 'partial';
841
- if (claim === 'cleanup_verified' || cleanupChecked) return 'proven';
1068
+ const hasClosureEvidence = Boolean(details.stoppedAt || details.exitCode !== null || details.cleanupMethod);
1069
+ if (claim === 'cleanup_verified' || details.cleanupChecked) {
1070
+ if (details.cleanupObserved && hasClosureEvidence) return 'proven';
1071
+ if (details.leftRunningIntentionally && (details.pid !== null || details.cleanupMethod)) return 'partial';
1072
+ return 'real_required_missing';
1073
+ }
842
1074
  if (claim === 'observed' || claim === 'stopped' || claim === 'started') return 'verified';
843
1075
  return 'partial';
844
1076
  }
@@ -46,6 +46,20 @@
46
46
  - Risky changes:
47
47
  - Public/release changes: scan for external tool names, comparison wording, and attribution wording; remove those hits unless the user explicitly approves keeping them.
48
48
 
49
+ ## Security Policy
50
+
51
+ - Sensitive surfaces:
52
+ - Secrets/env handling:
53
+ - Auth/payment/DB policy:
54
+ - Deployment/release policy:
55
+ - Package/dependency policy:
56
+
57
+ ## Report Policy
58
+
59
+ - Explain changed code in plain language:
60
+ - Always include remaining tasks or useful improvements:
61
+ - Preserve source boundaries for research/proof notes:
62
+
49
63
  ## Known Risks
50
64
 
51
65
  - Auth/payment/DB/security:
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": [