yam-flow 2.5.1 → 2.7.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.
@@ -0,0 +1,219 @@
1
+ import { createHash } from 'node:crypto';
2
+ const GATE_ID_PATTERN = /^[a-z0-9][a-z0-9._-]{0,95}$/;
3
+ const BOUNDARIES = new Set(['release', 'update', 'mission', 'generic']);
4
+ const CHECK_STATUSES = new Set(['passed', 'failed', 'skipped']);
5
+ export function buildStrictGateResult(input = {}) {
6
+ const gateId = boundedText(input.gate_id || '', 96);
7
+ const boundary = BOUNDARIES.has(input.boundary) ? input.boundary : 'generic';
8
+ const contractErrors = [];
9
+ if (!GATE_ID_PATTERN.test(gateId))
10
+ contractErrors.push('gate_id_invalid');
11
+ if (!BOUNDARIES.has(input.boundary))
12
+ contractErrors.push('boundary_invalid');
13
+ if (Array.isArray(input.checks) && input.checks.length > 128)
14
+ contractErrors.push('check_limit_exceeded');
15
+ if (Array.isArray(input.blockers) && input.blockers.length > 64)
16
+ contractErrors.push('blocker_limit_exceeded');
17
+ if (Array.isArray(input.evidence) && input.evidence.length > 128)
18
+ contractErrors.push('evidence_limit_exceeded');
19
+ const checks = Array.isArray(input.checks) ? input.checks.slice(0, 128).map((check, index) => {
20
+ const id = boundedText(check?.id || '', 96);
21
+ const status = CHECK_STATUSES.has(check?.status) ? check?.status : 'failed';
22
+ if (!GATE_ID_PATTERN.test(id))
23
+ contractErrors.push(`check_id_invalid:${index}`);
24
+ if (!CHECK_STATUSES.has(check?.status))
25
+ contractErrors.push(`check_status_invalid:${id || index}`);
26
+ return {
27
+ id,
28
+ status,
29
+ note: boundedText(check?.note || '', 512),
30
+ required: check?.required !== false
31
+ };
32
+ }) : [];
33
+ if (!checks.length)
34
+ contractErrors.push('checks_missing');
35
+ const checkIds = checks.map((check) => check.id).filter(Boolean);
36
+ if (new Set(checkIds).size !== checkIds.length)
37
+ contractErrors.push('duplicate_check_id');
38
+ const blockers = uniqueBounded(input.blockers, 64, 512);
39
+ const evidence = uniqueBounded(input.evidence, 128, 512);
40
+ if (!evidence.length)
41
+ contractErrors.push('evidence_missing');
42
+ for (const check of checks) {
43
+ if (check.required && check.status !== 'passed')
44
+ blockers.push(`${check.id || 'unnamed_check'}: ${check.note || 'required gate check did not pass'}`);
45
+ }
46
+ for (const error of contractErrors)
47
+ blockers.push(`gate_contract_invalid: ${error}`);
48
+ const finalBlockers = [...new Set(blockers)];
49
+ const contractValid = contractErrors.length === 0;
50
+ const passed = contractValid && finalBlockers.length === 0;
51
+ const generatedAt = new Date().toISOString();
52
+ const nextAction = boundedText(input.next_action || finalBlockers[0] || 'gate passed; retain the evidence digest with the dependent completion claim', 512);
53
+ const uniqueContractErrors = [...new Set(contractErrors)];
54
+ const truthStatus = passed ? 'verified' : 'blocked';
55
+ const canonical = {
56
+ schema: 'yam.gate-result.v1',
57
+ generated_at: generatedAt,
58
+ gate_id: gateId,
59
+ boundary,
60
+ status: passed ? 'passed' : 'failed',
61
+ checks,
62
+ blockers: finalBlockers,
63
+ evidence,
64
+ next_action: nextAction,
65
+ contract_valid: contractValid,
66
+ contract_errors: uniqueContractErrors,
67
+ truth_status: truthStatus
68
+ };
69
+ return {
70
+ schema: 'yam.gate-result.v1',
71
+ generated_at: generatedAt,
72
+ gate_id: gateId,
73
+ boundary,
74
+ status: passed ? 'passed' : 'failed',
75
+ checks,
76
+ blockers: finalBlockers,
77
+ evidence,
78
+ next_action: nextAction,
79
+ contract_valid: contractValid,
80
+ contract_errors: uniqueContractErrors,
81
+ digest: `sha256:${createHash('sha256').update(stableJson(canonical)).digest('hex')}`,
82
+ truth_status: truthStatus
83
+ };
84
+ }
85
+ export function verifyStrictGateResult(value) {
86
+ const errors = [];
87
+ const record = value && typeof value === 'object' ? value : {};
88
+ if (record.schema !== 'yam.gate-result.v1')
89
+ errors.push('schema_invalid');
90
+ if (typeof record.generated_at !== 'string' || !isIsoDate(record.generated_at))
91
+ errors.push('generated_at_invalid');
92
+ if (!GATE_ID_PATTERN.test(String(record.gate_id || '')))
93
+ errors.push('gate_id_invalid');
94
+ if (!BOUNDARIES.has(record.boundary))
95
+ errors.push('boundary_invalid');
96
+ if (!['passed', 'failed'].includes(String(record.status || '')))
97
+ errors.push('status_invalid');
98
+ const checks = Array.isArray(record.checks) ? record.checks : [];
99
+ const blockers = Array.isArray(record.blockers) ? record.blockers : [];
100
+ const evidence = Array.isArray(record.evidence) ? record.evidence : [];
101
+ const contractErrors = Array.isArray(record.contract_errors) ? record.contract_errors : [];
102
+ if (!checks.length)
103
+ errors.push('checks_missing');
104
+ if (checks.length > 128)
105
+ errors.push('check_limit_exceeded');
106
+ if (blockers.length > 64)
107
+ errors.push('blocker_limit_exceeded');
108
+ if (evidence.length > 128)
109
+ errors.push('evidence_limit_exceeded');
110
+ if (!Array.isArray(record.blockers) || blockers.some((item) => typeof item !== 'string'))
111
+ errors.push('blockers_invalid');
112
+ if (!Array.isArray(record.evidence) || !evidence.length || evidence.some((item) => typeof item !== 'string' || !item.trim()))
113
+ errors.push('evidence_missing');
114
+ if (typeof record.contract_valid !== 'boolean')
115
+ errors.push('contract_valid_missing');
116
+ if (!Array.isArray(record.contract_errors) || contractErrors.some((item) => typeof item !== 'string'))
117
+ errors.push('contract_errors_invalid');
118
+ if (record.contract_valid === true && contractErrors.length)
119
+ errors.push('contract_valid_with_errors');
120
+ if (record.contract_valid === false && !contractErrors.length)
121
+ errors.push('contract_invalid_without_errors');
122
+ if (record.status === 'passed' && record.contract_valid !== true)
123
+ errors.push('passed_with_invalid_contract');
124
+ if (typeof record.next_action !== 'string' || !record.next_action.trim())
125
+ errors.push('next_action_missing');
126
+ if (!['verified', 'blocked'].includes(String(record.truth_status || '')))
127
+ errors.push('truth_status_invalid');
128
+ if (record.status === 'passed' && record.truth_status !== 'verified')
129
+ errors.push('passed_truth_mismatch');
130
+ if (record.status === 'failed' && record.truth_status !== 'blocked')
131
+ errors.push('failed_truth_mismatch');
132
+ for (const [index, check] of checks.entries()) {
133
+ if (!check || typeof check !== 'object') {
134
+ errors.push(`check_invalid:${index}`);
135
+ continue;
136
+ }
137
+ const row = check;
138
+ if (!GATE_ID_PATTERN.test(String(row.id || '')))
139
+ errors.push(`check_id_invalid:${index}`);
140
+ if (!CHECK_STATUSES.has(row.status))
141
+ errors.push(`check_status_invalid:${index}`);
142
+ if (typeof row.note !== 'string')
143
+ errors.push(`check_note_invalid:${index}`);
144
+ if (typeof row.required !== 'boolean')
145
+ errors.push(`check_required_invalid:${index}`);
146
+ }
147
+ const checkIds = checks
148
+ .filter((check) => check && typeof check === 'object')
149
+ .map((check) => String(check.id || ''))
150
+ .filter(Boolean);
151
+ if (new Set(checkIds).size !== checkIds.length)
152
+ errors.push('duplicate_check_id');
153
+ if (record.status === 'passed' && Array.isArray(record.blockers) && record.blockers.length > 0)
154
+ errors.push('passed_with_blockers');
155
+ if (record.status === 'passed' && checks.length) {
156
+ const failedRequired = checks.some((check) => {
157
+ if (!check || typeof check !== 'object')
158
+ return true;
159
+ const row = check;
160
+ return row.required !== false && row.status !== 'passed';
161
+ });
162
+ if (failedRequired)
163
+ errors.push('passed_with_failed_required_check');
164
+ }
165
+ if (record.status === 'failed') {
166
+ const hasFailedRequired = checks.some((check) => {
167
+ if (!check || typeof check !== 'object')
168
+ return false;
169
+ const row = check;
170
+ return row.required !== false && row.status !== 'passed';
171
+ });
172
+ if (record.contract_valid === true && blockers.length === 0 && !hasFailedRequired)
173
+ errors.push('failed_without_blocker_or_failed_check');
174
+ }
175
+ const canonical = {
176
+ schema: record.schema,
177
+ generated_at: record.generated_at,
178
+ gate_id: String(record.gate_id || ''),
179
+ boundary: record.boundary,
180
+ status: record.status,
181
+ checks,
182
+ blockers,
183
+ evidence,
184
+ next_action: record.next_action,
185
+ contract_valid: record.contract_valid,
186
+ contract_errors: contractErrors,
187
+ truth_status: record.truth_status
188
+ };
189
+ const expectedDigest = `sha256:${createHash('sha256').update(stableJson(canonical)).digest('hex')}`;
190
+ if (typeof record.digest !== 'string' || record.digest !== expectedDigest)
191
+ errors.push('digest_invalid');
192
+ return {
193
+ schema: 'yam.gate-result-verification.v1',
194
+ valid: errors.length === 0,
195
+ errors: [...new Set(errors)],
196
+ truth_status: errors.length ? 'blocked' : 'verified',
197
+ next_action: errors.length ? `reject the gate result and repair: ${errors[0]}` : 'gate result contract is structurally valid'
198
+ };
199
+ }
200
+ function isIsoDate(value) {
201
+ return !Number.isNaN(Date.parse(value)) && new Date(value).toISOString() === value;
202
+ }
203
+ function uniqueBounded(value, maxItems, maxChars) {
204
+ if (!Array.isArray(value))
205
+ return [];
206
+ return [...new Set(value.map((item) => boundedText(item, maxChars)).filter(Boolean))].slice(0, maxItems);
207
+ }
208
+ function boundedText(value, max) {
209
+ return String(value || '').replace(/[\r\n\u0000-\u001f\u007f]+/g, ' ').trim().slice(0, max);
210
+ }
211
+ function stableJson(value) {
212
+ if (Array.isArray(value))
213
+ return `[${value.map((item) => stableJson(item)).join(',')}]`;
214
+ if (value && typeof value === 'object') {
215
+ const record = value;
216
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${stableJson(record[key])}`).join(',')}}`;
217
+ }
218
+ return JSON.stringify(value);
219
+ }
@@ -0,0 +1,79 @@
1
+ export interface MediaProviderAssetInput {
2
+ id: string;
3
+ role: 'input' | 'output';
4
+ file_path: string;
5
+ provenance: {
6
+ kind: 'operator' | 'provider';
7
+ source_ref: string;
8
+ recorded_at: string;
9
+ };
10
+ }
11
+ export interface MediaProviderReceipt {
12
+ schema: 'yam.media-provider-receipt.v1';
13
+ created_at: string;
14
+ demand_trigger: {
15
+ kind: 'media_generation' | 'media_transformation';
16
+ evidence: string;
17
+ evidence_truth: 'operator_asserted';
18
+ };
19
+ provider: {
20
+ name: string;
21
+ model: string;
22
+ };
23
+ provider_calls: number;
24
+ provider_execution: boolean;
25
+ dry_run: boolean;
26
+ submit: boolean;
27
+ assets: Array<MediaProviderAssetInput & {
28
+ sha256: string;
29
+ bytes: number;
30
+ }>;
31
+ execution_boundary: {
32
+ provider_calls_by_yam: false;
33
+ provider_claim_source: 'operator_asserted';
34
+ optional: true;
35
+ };
36
+ claims: {
37
+ asset_integrity: 'digest_bound';
38
+ provider_execution: 'operator_asserted';
39
+ visual_correctness: 'not_verified';
40
+ implementation_correctness: 'not_verified';
41
+ };
42
+ truth_status: 'partial';
43
+ digest: string;
44
+ }
45
+ export declare function createMediaProviderReceipt(input: {
46
+ root: string;
47
+ receipt_path: string;
48
+ demand_trigger: {
49
+ kind: 'media_generation' | 'media_transformation';
50
+ evidence: string;
51
+ };
52
+ provider: {
53
+ name: string;
54
+ model: string;
55
+ };
56
+ provider_calls: number;
57
+ provider_execution: boolean;
58
+ dry_run: boolean;
59
+ submit: boolean;
60
+ assets: MediaProviderAssetInput[];
61
+ }): Promise<{
62
+ schema: "yam.media-provider-receipt-write.v1";
63
+ receipt_path: string;
64
+ receipt: MediaProviderReceipt;
65
+ truth_status: "partial";
66
+ next_action: string;
67
+ }>;
68
+ export declare function verifyMediaProviderReceipt(input: {
69
+ root: string;
70
+ receipt_path: string;
71
+ }): Promise<{
72
+ schema: "yam.media-provider-receipt-verification.v1";
73
+ receipt_path: string;
74
+ valid: boolean;
75
+ errors: string[];
76
+ receipt_truth_status: "partial";
77
+ truth_status: "verified" | "blocked";
78
+ next_action: string;
79
+ }>;