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.
- package/AGENTS.md +7 -4
- package/CHANGELOG.md +57 -0
- package/COMMANDS.md +15 -3
- package/DECISIONS.md +28 -5
- package/README.md +33 -4
- package/ROADMAP.md +2 -2
- package/dist/bin/yam.js +827 -19
- package/dist/lib/trust-kernel.d.ts +50 -0
- package/dist/lib/trust-kernel.js +187 -4
- package/package.json +1 -1
- package/references/cleanup-scan.md +33 -0
- package/references/final-report.md +6 -1
- package/references/risk-escalation.md +4 -0
- package/references/runtime-orchestration.md +7 -0
- package/references/scout.md +9 -3
- package/references/tool-trust-layer.md +14 -2
- package/references/truth-matrix.md +4 -0
- package/skills/scout/SKILL.md +19 -9
- package/src/bin/yam.ts +835 -19
- package/src/lib/trust-kernel.ts +235 -3
- package/templates/yam.project.md +14 -0
- package/yam.manifest.json +3 -2
package/src/lib/trust-kernel.ts
CHANGED
|
@@ -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,
|
|
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,
|
|
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
|
-
|
|
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
|
}
|
package/templates/yam.project.md
CHANGED
|
@@ -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.
|
|
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": [
|