yam-flow 2.1.0 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +23 -0
- package/COMMANDS.md +9 -4
- package/README.md +10 -6
- package/dist/bin/yam.js +209 -29
- package/dist/lib/trust-kernel.d.ts +31 -1
- package/dist/lib/trust-kernel.js +128 -6
- package/package.json +2 -1
- package/references/final-report.md +5 -3
- package/references/scout-wide-scan.md +150 -0
- package/references/scout.md +9 -0
- package/references/study-note.md +22 -6
- package/references/ueye-proof.md +42 -1
- package/references/ueye.md +29 -3
- package/skills/deep/SKILL.md +3 -2
- package/skills/mission/SKILL.md +3 -2
- package/skills/quick/SKILL.md +3 -2
- package/skills/scout/SKILL.md +18 -5
- package/skills/ueye/SKILL.md +3 -2
- package/src/bin/yam.ts +215 -30
- package/src/lib/trust-kernel.ts +155 -7
- package/templates/yam.project.md +2 -1
- package/yam.manifest.json +5 -4
package/src/lib/trust-kernel.ts
CHANGED
|
@@ -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
|
|
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
|
|
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;
|
package/templates/yam.project.md
CHANGED
|
@@ -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,
|
|
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
|
|
3
|
+
"version": "2.2.1",
|
|
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,
|
|
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
|
],
|
|
@@ -35,8 +36,8 @@
|
|
|
35
36
|
},
|
|
36
37
|
{
|
|
37
38
|
"id": "scout",
|
|
38
|
-
"stage": "v0.
|
|
39
|
-
"purpose": "
|
|
39
|
+
"stage": "v0.2",
|
|
40
|
+
"purpose": "Investigation, wide search, evidence-led source mapping, blocked-source fallback, opposition pass, and recommendation without code changes by default."
|
|
40
41
|
},
|
|
41
42
|
{
|
|
42
43
|
"id": "deep",
|