orizu 0.5.26 → 0.5.28
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/README.md +19 -0
- package/dist/artifacts-git-runtime.d.ts +34 -0
- package/dist/artifacts-git-runtime.d.ts.map +1 -0
- package/dist/artifacts-git-runtime.js +312 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts +5 -0
- package/dist/cloudflare-artifacts-git-remote.d.ts.map +1 -0
- package/dist/cloudflare-artifacts-git-remote.js +21 -0
- package/dist/dataset-transfer-download.d.ts +2 -0
- package/dist/dataset-transfer-download.d.ts.map +1 -0
- package/dist/dataset-transfer-download.js +66 -0
- package/dist/dataset-transfer-policy.d.ts +2 -0
- package/dist/dataset-transfer-policy.d.ts.map +1 -0
- package/dist/dataset-transfer-policy.js +8 -0
- package/dist/dataset-transfer-upload.d.ts +42 -0
- package/dist/dataset-transfer-upload.d.ts.map +1 -0
- package/dist/dataset-transfer-upload.js +110 -0
- package/dist/git-credential.d.ts.map +1 -1
- package/dist/git-credential.js +1 -1
- package/dist/git-recovery-environment.d.ts +1 -0
- package/dist/git-recovery-environment.d.ts.map +1 -1
- package/dist/git-recovery-environment.js +1 -1
- package/dist/help.d.ts.map +1 -1
- package/dist/help.js +44 -11
- package/dist/hosted-boot.d.ts +75 -2
- package/dist/hosted-boot.d.ts.map +1 -1
- package/dist/hosted-boot.js +585 -102
- package/dist/hosted-bootstrap.d.ts +2 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -1
- package/dist/hosted-bootstrap.js +12 -3
- package/dist/hosted-harness-opencode.d.ts +8 -9
- package/dist/hosted-harness-opencode.d.ts.map +1 -1
- package/dist/hosted-harness-opencode.js +126 -24
- package/dist/hosted-harness.d.ts +3 -0
- package/dist/hosted-harness.d.ts.map +1 -1
- package/dist/hosted-harness.js +4 -1
- package/dist/hosted-harvest.d.ts +30 -9
- package/dist/hosted-harvest.d.ts.map +1 -1
- package/dist/hosted-harvest.js +222 -28
- package/dist/hosted-loop-lifecycle.d.ts +15 -3
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -1
- package/dist/hosted-loop-lifecycle.js +6 -4
- package/dist/hosted-loop.d.ts +47 -7
- package/dist/hosted-loop.d.ts.map +1 -1
- package/dist/hosted-loop.js +310 -64
- package/dist/hosted-question-limits.d.ts +7 -0
- package/dist/hosted-question-limits.d.ts.map +1 -0
- package/dist/hosted-question-limits.js +6 -0
- package/dist/hosted-question.d.ts +27 -0
- package/dist/hosted-question.d.ts.map +1 -0
- package/dist/hosted-question.js +118 -0
- package/dist/hosted-run-event-sink.d.ts +10 -5
- package/dist/hosted-run-event-sink.d.ts.map +1 -1
- package/dist/hosted-run-event-sink.js +12 -8
- package/dist/hosted-runtime-assets.d.ts +16 -7
- package/dist/hosted-runtime-assets.d.ts.map +1 -1
- package/dist/hosted-runtime-assets.js +40 -12
- package/dist/hosted-session-cli.d.ts.map +1 -1
- package/dist/hosted-session-cli.js +10 -19
- package/dist/http.d.ts +1 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +10 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +113 -92
- package/dist/instruction-set-gepa-launch.d.ts +1 -1
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
- package/dist/instruction-set-gepa-launch.js +5 -3
- package/dist/instruction-set-loader.d.ts.map +1 -1
- package/dist/instruction-set-loader.js +63 -2
- package/dist/instruction-set-lock/index.d.ts +51 -0
- package/dist/instruction-set-lock/index.d.ts.map +1 -0
- package/dist/instruction-set-lock/index.js +459 -0
- package/dist/instruction-set-lock/schema.v1.d.ts +136 -0
- package/dist/instruction-set-lock/schema.v1.d.ts.map +1 -0
- package/dist/instruction-set-lock/schema.v1.js +155 -0
- package/dist/instruction-set-provenance.d.ts +26 -0
- package/dist/instruction-set-provenance.d.ts.map +1 -0
- package/dist/instruction-set-provenance.js +68 -0
- package/dist/instruction-set-sync/helpers.d.ts +22 -0
- package/dist/instruction-set-sync/helpers.d.ts.map +1 -0
- package/dist/instruction-set-sync/helpers.js +677 -0
- package/dist/instruction-set-sync/index.d.ts +60 -0
- package/dist/instruction-set-sync/index.d.ts.map +1 -0
- package/dist/instruction-set-sync/index.js +689 -0
- package/dist/instruction-set-update/index.d.ts +42 -0
- package/dist/instruction-set-update/index.d.ts.map +1 -0
- package/dist/instruction-set-update/index.js +445 -0
- package/dist/instruction-set-verify/index.d.ts +27 -0
- package/dist/instruction-set-verify/index.d.ts.map +1 -0
- package/dist/instruction-set-verify/index.js +865 -0
- package/dist/instruction-sets-cli.d.ts +1 -4
- package/dist/instruction-sets-cli.d.ts.map +1 -1
- package/dist/instruction-sets-cli.js +220 -109
- package/dist/model-config-fields.d.ts +35 -0
- package/dist/model-config-fields.d.ts.map +1 -0
- package/dist/model-config-fields.js +28 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -1
- package/dist/optimizations-list-cli.js +7 -4
- package/dist/score-format.d.ts +2 -0
- package/dist/score-format.d.ts.map +1 -0
- package/dist/score-format.js +18 -0
- package/dist/workbench-repo-contract.d.ts +65 -0
- package/dist/workbench-repo-contract.d.ts.map +1 -0
- package/dist/workbench-repo-contract.js +250 -0
- package/dist/workspace-bootstrap-job.d.ts +45 -0
- package/dist/workspace-bootstrap-job.d.ts.map +1 -0
- package/dist/workspace-bootstrap-job.js +431 -0
- package/dist/workspace-bootstrap-seed.d.ts +74 -0
- package/dist/workspace-bootstrap-seed.d.ts.map +1 -0
- package/dist/workspace-bootstrap-seed.js +209 -0
- package/package.json +4 -3
- package/src/artifacts-git-runtime.ts +360 -0
- package/src/cloudflare-artifacts-git-remote.ts +24 -0
- package/src/dataset-transfer-download.ts +61 -0
- package/src/dataset-transfer-policy.ts +8 -0
- package/src/dataset-transfer-upload.ts +145 -0
- package/src/git-credential.ts +1 -2
- package/src/git-recovery-environment.ts +1 -1
- package/src/help.ts +44 -11
- package/src/hosted-boot.ts +644 -109
- package/src/hosted-bootstrap.ts +14 -3
- package/src/hosted-harness-opencode.ts +130 -28
- package/src/hosted-harness.ts +4 -1
- package/src/hosted-harvest.ts +258 -33
- package/src/hosted-loop-lifecycle.ts +19 -5
- package/src/hosted-loop.ts +340 -68
- package/src/hosted-question-limits.ts +6 -0
- package/src/hosted-question.ts +145 -0
- package/src/hosted-run-event-sink.ts +24 -12
- package/src/hosted-runtime-assets.ts +51 -11
- package/src/hosted-session-cli.ts +10 -24
- package/src/http.ts +10 -4
- package/src/index.ts +108 -134
- package/src/instruction-set-gepa-launch.ts +4 -4
- package/src/instruction-set-loader.ts +53 -2
- package/src/instruction-set-lock/index.ts +561 -0
- package/src/instruction-set-lock/schema.v1.json +141 -0
- package/src/instruction-set-lock/schema.v1.ts +155 -0
- package/src/instruction-set-provenance.ts +89 -0
- package/src/instruction-set-sync/helpers.ts +707 -0
- package/src/instruction-set-sync/index.ts +791 -0
- package/src/instruction-set-update/index.ts +488 -0
- package/src/instruction-set-verify/index.ts +1067 -0
- package/src/instruction-sets-cli.ts +226 -114
- package/src/model-config-fields.ts +31 -0
- package/src/optimizations-list-cli.ts +7 -4
- package/src/score-format.ts +16 -0
- package/src/workbench-repo-contract.ts +314 -0
- package/src/workspace-bootstrap-job.ts +611 -0
- package/src/workspace-bootstrap-seed.ts +350 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +46 -2
- package/vendor/skills/orizu/SKILL.md +4 -2
- package/vendor/skills/orizu/flows/promote.md +4 -0
- package/vendor/skills/orizu/references/assess-and-plan.md +1 -1
- package/vendor/skills/orizu/references/authority-map.md +1 -1
- package/vendor/skills/orizu/references/cli-reference.md +151 -35
- package/vendor/skills/orizu/references/instructions-after-prompts.md +3 -3
- package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +5 -6
- package/vendor/skills/orizu/references/migrate-hand-rolled-instruction-layout.md +252 -0
- package/vendor/skills/orizu/references/optimization-reports.md +5 -4
- package/vendor/skills/orizu/references/optimization-with-gepa.md +1 -1
- package/vendor/skills/orizu/references/orizu-in-your-codebase.md +195 -0
- package/vendor/skills/orizu/references/primer.md +4 -2
- package/vendor/skills/orizu/references/prompt-control-plane.md +33 -29
- package/vendor/skills/orizu/references/vocabulary.md +1 -1
|
@@ -0,0 +1,1067 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process'
|
|
2
|
+
import { createHash } from 'node:crypto'
|
|
3
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, statSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { tmpdir } from 'node:os'
|
|
5
|
+
import { join, relative, resolve } from 'node:path'
|
|
6
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
7
|
+
|
|
8
|
+
import { parseLock, type InstructionSetLockV1, type SyncedVersionLock } from '../instruction-set-lock/index.js'
|
|
9
|
+
import { MODEL_CONFIG_FIELDS } from '../model-config-fields.js'
|
|
10
|
+
import { managedHelperPaths, renderGeneratedIndex } from '../instruction-set-sync/helpers.js'
|
|
11
|
+
import { canonicalVersionDigest, profileSlug, renderGeneratedVersionModule } from '../instruction-set-sync/index.js'
|
|
12
|
+
|
|
13
|
+
export type VerifyGroup = 'group1' | 'group2' | 'group3' | 'group4'
|
|
14
|
+
export type VerifyGroupStatus = 'PASS' | 'FAIL' | 'SKIPPED'
|
|
15
|
+
|
|
16
|
+
export interface VerifyFinding {
|
|
17
|
+
group: VerifyGroup
|
|
18
|
+
code: string
|
|
19
|
+
path: string
|
|
20
|
+
expected?: string
|
|
21
|
+
found?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface VerifyReport {
|
|
25
|
+
ok: boolean
|
|
26
|
+
groups: Record<VerifyGroup, VerifyGroupStatus>
|
|
27
|
+
failures: VerifyFinding[]
|
|
28
|
+
warnings: VerifyFinding[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface VersionLocation {
|
|
32
|
+
setSlug: string
|
|
33
|
+
profileSlug: string
|
|
34
|
+
versionSlug: string
|
|
35
|
+
relativePath: string
|
|
36
|
+
absolutePath: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
interface VersionManifest {
|
|
40
|
+
instructionSetId: string
|
|
41
|
+
instructionSetSlug: string
|
|
42
|
+
profileVersionId: string
|
|
43
|
+
modelConfigIdentity: string
|
|
44
|
+
profileSlug: string
|
|
45
|
+
versionNumber: number
|
|
46
|
+
digest: string
|
|
47
|
+
components: Record<string, string>
|
|
48
|
+
settings: Record<string, unknown>
|
|
49
|
+
provenance: {
|
|
50
|
+
instructionSetId: string
|
|
51
|
+
profileVersionId: string
|
|
52
|
+
digest: string
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface GeneratedVersionModule {
|
|
57
|
+
components: Record<string, string>
|
|
58
|
+
manifest: VersionManifest
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface HelperRequest {
|
|
62
|
+
specifier: string
|
|
63
|
+
kind: 'exact' | 'default' | 'profile'
|
|
64
|
+
path: string
|
|
65
|
+
expectedInstructionSetId: string
|
|
66
|
+
expectedProfileVersionId: string
|
|
67
|
+
expectedDigest: string
|
|
68
|
+
expectedModelConfigIdentity: string
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
interface HelperExecutionResult {
|
|
72
|
+
results: HelperCheckResult[]
|
|
73
|
+
unknownRejection: string | null
|
|
74
|
+
malformedRejection: string | null
|
|
75
|
+
modelConfigRejection: string | null
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface HelperCheckResult {
|
|
79
|
+
specifier: string
|
|
80
|
+
kind: 'exact' | 'default' | 'profile'
|
|
81
|
+
loaded?: {
|
|
82
|
+
components?: Record<string, string>
|
|
83
|
+
settings?: Record<string, unknown>
|
|
84
|
+
provenance?: { instructionSetId?: string; profileVersionId?: string; digest?: string }
|
|
85
|
+
}
|
|
86
|
+
modelConfig?: Record<string, unknown>
|
|
87
|
+
loadError?: string
|
|
88
|
+
modelConfigError?: string
|
|
89
|
+
validRejection?: string
|
|
90
|
+
corruptBytesRejection?: string
|
|
91
|
+
wrongDigestRejection?: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
interface StaticImportMap {
|
|
95
|
+
imports: Map<string, string>
|
|
96
|
+
versions: Record<string, Record<string, Record<string, string>>>
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
class StaticObjectParser {
|
|
100
|
+
private index = 0
|
|
101
|
+
|
|
102
|
+
constructor(private readonly source: string) {}
|
|
103
|
+
|
|
104
|
+
parse(): unknown {
|
|
105
|
+
const value = this.parseValue()
|
|
106
|
+
this.skipWhitespace()
|
|
107
|
+
if (this.index !== this.source.length) throw new Error('trailing_tokens')
|
|
108
|
+
return value
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private parseValue(): unknown {
|
|
112
|
+
this.skipWhitespace()
|
|
113
|
+
if (this.source[this.index] === '{') return this.parseObject()
|
|
114
|
+
if (this.source[this.index] === '"') return this.parseString()
|
|
115
|
+
return this.parseIdentifier()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private parseObject(): Record<string, unknown> {
|
|
119
|
+
const result: Record<string, unknown> = {}
|
|
120
|
+
this.expect('{')
|
|
121
|
+
this.skipWhitespace()
|
|
122
|
+
if (this.source[this.index] === '}') {
|
|
123
|
+
this.index += 1
|
|
124
|
+
return result
|
|
125
|
+
}
|
|
126
|
+
while (true) {
|
|
127
|
+
const key = this.parseString()
|
|
128
|
+
this.skipWhitespace()
|
|
129
|
+
this.expect(':')
|
|
130
|
+
result[key] = this.parseValue()
|
|
131
|
+
this.skipWhitespace()
|
|
132
|
+
const token = this.source[this.index]
|
|
133
|
+
if (token === '}') {
|
|
134
|
+
this.index += 1
|
|
135
|
+
return result
|
|
136
|
+
}
|
|
137
|
+
this.expect(',')
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
private parseString(): string {
|
|
142
|
+
const start = this.index
|
|
143
|
+
this.expect('"')
|
|
144
|
+
let isEscaped = false
|
|
145
|
+
while (this.index < this.source.length) {
|
|
146
|
+
const character = this.source[this.index]!
|
|
147
|
+
this.index += 1
|
|
148
|
+
if (character === '"' && !isEscaped) {
|
|
149
|
+
return JSON.parse(this.source.slice(start, this.index)) as string
|
|
150
|
+
}
|
|
151
|
+
isEscaped = character === '\\' && !isEscaped
|
|
152
|
+
if (character !== '\\') isEscaped = false
|
|
153
|
+
}
|
|
154
|
+
throw new Error('unterminated_string')
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
private parseIdentifier(): string {
|
|
158
|
+
const match = /^[A-Za-z_$][A-Za-z0-9_$]*/u.exec(this.source.slice(this.index))
|
|
159
|
+
if (!match) throw new Error('identifier_expected')
|
|
160
|
+
this.index += match[0].length
|
|
161
|
+
return match[0]
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private skipWhitespace(): void {
|
|
165
|
+
while (/\s/u.test(this.source[this.index] ?? '')) this.index += 1
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private expect(token: string): void {
|
|
169
|
+
this.skipWhitespace()
|
|
170
|
+
if (this.source[this.index] !== token) throw new Error(`${token}_expected`)
|
|
171
|
+
this.index += 1
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function staticImportMap(source: string): StaticImportMap {
|
|
176
|
+
const imports = new Map<string, string>()
|
|
177
|
+
const importPattern = /^import \* as ([A-Za-z_$][A-Za-z0-9_$]*) from '([^']+)'$/gmu
|
|
178
|
+
for (const match of source.matchAll(importPattern)) imports.set(match[1]!, match[2]!)
|
|
179
|
+
const versionsMatch = /^export const versions = (.+) as const$/mu.exec(source)
|
|
180
|
+
if (!versionsMatch) throw new Error('versions_export_missing')
|
|
181
|
+
const parsed = new StaticObjectParser(versionsMatch[1]!).parse()
|
|
182
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) throw new Error('versions_export_invalid')
|
|
183
|
+
const versions: Record<string, Record<string, Record<string, string>>> = {}
|
|
184
|
+
for (const [setSlug, profilesValue] of Object.entries(parsed as Record<string, unknown>)) {
|
|
185
|
+
if (!profilesValue || typeof profilesValue !== 'object' || Array.isArray(profilesValue)) throw new Error('profiles_map_invalid')
|
|
186
|
+
const profiles: Record<string, Record<string, string>> = {}
|
|
187
|
+
for (const [profileSlug, versionsValue] of Object.entries(profilesValue as Record<string, unknown>)) {
|
|
188
|
+
if (!versionsValue || typeof versionsValue !== 'object' || Array.isArray(versionsValue)) throw new Error('versions_map_invalid')
|
|
189
|
+
const mappedVersions: Record<string, string> = {}
|
|
190
|
+
for (const [versionSlug, identifier] of Object.entries(versionsValue as Record<string, unknown>)) {
|
|
191
|
+
if (typeof identifier !== 'string') throw new Error('version_import_invalid')
|
|
192
|
+
mappedVersions[versionSlug] = identifier
|
|
193
|
+
}
|
|
194
|
+
profiles[profileSlug] = mappedVersions
|
|
195
|
+
}
|
|
196
|
+
versions[setSlug] = profiles
|
|
197
|
+
}
|
|
198
|
+
return { imports, versions }
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function hash(bytes: string | Buffer): string {
|
|
202
|
+
return `sha256:${createHash('sha256').update(bytes).digest('hex')}`
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function isDirectory(path: string): boolean {
|
|
206
|
+
try { return statSync(path).isDirectory() } catch { return false }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function directoryNames(path: string): string[] {
|
|
210
|
+
if (!isDirectory(path)) return []
|
|
211
|
+
return readdirSync(path).filter(name => isDirectory(join(path, name))).sort()
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function versionLocations(appRoot: string): VersionLocation[] {
|
|
215
|
+
const instructionSetsRoot = join(appRoot, 'instruction-sets')
|
|
216
|
+
return directoryNames(instructionSetsRoot).flatMap(setSlug =>
|
|
217
|
+
directoryNames(join(instructionSetsRoot, setSlug)).flatMap(profileSlug =>
|
|
218
|
+
directoryNames(join(instructionSetsRoot, setSlug, profileSlug)).map(versionSlug => ({
|
|
219
|
+
setSlug,
|
|
220
|
+
profileSlug,
|
|
221
|
+
versionSlug,
|
|
222
|
+
relativePath: `instruction-sets/${setSlug}/${profileSlug}/${versionSlug}`,
|
|
223
|
+
absolutePath: join(instructionSetsRoot, setSlug, profileSlug, versionSlug),
|
|
224
|
+
}))
|
|
225
|
+
)
|
|
226
|
+
)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function manifestValue(value: unknown): VersionManifest | null {
|
|
230
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
231
|
+
const manifest = value as Record<string, unknown>
|
|
232
|
+
if (
|
|
233
|
+
typeof manifest.instructionSetId !== 'string' ||
|
|
234
|
+
typeof manifest.instructionSetSlug !== 'string' ||
|
|
235
|
+
typeof manifest.profileVersionId !== 'string' ||
|
|
236
|
+
typeof manifest.modelConfigIdentity !== 'string' ||
|
|
237
|
+
typeof manifest.profileSlug !== 'string' ||
|
|
238
|
+
typeof manifest.versionNumber !== 'number' ||
|
|
239
|
+
typeof manifest.digest !== 'string' ||
|
|
240
|
+
!manifest.settings || typeof manifest.settings !== 'object' || Array.isArray(manifest.settings) ||
|
|
241
|
+
!manifest.components || typeof manifest.components !== 'object' || Array.isArray(manifest.components) ||
|
|
242
|
+
!Object.values(manifest.components).every(item => typeof item === 'string') ||
|
|
243
|
+
!manifest.provenance || typeof manifest.provenance !== 'object' || Array.isArray(manifest.provenance) ||
|
|
244
|
+
typeof (manifest.provenance as Record<string, unknown>).instructionSetId !== 'string' ||
|
|
245
|
+
typeof (manifest.provenance as Record<string, unknown>).profileVersionId !== 'string' ||
|
|
246
|
+
typeof (manifest.provenance as Record<string, unknown>).digest !== 'string'
|
|
247
|
+
) return null
|
|
248
|
+
return manifest as unknown as VersionManifest
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function readManifest(location: VersionLocation): VersionManifest | null {
|
|
252
|
+
try {
|
|
253
|
+
return manifestValue(JSON.parse(readFileSync(join(location.absolutePath, 'manifest.json'), 'utf8')))
|
|
254
|
+
} catch {
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function errorText(error: unknown): string {
|
|
260
|
+
return error instanceof Error ? error.message : String(error)
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function finding(
|
|
264
|
+
group: VerifyGroup,
|
|
265
|
+
code: string,
|
|
266
|
+
path: string,
|
|
267
|
+
expected?: unknown,
|
|
268
|
+
found?: unknown
|
|
269
|
+
): VerifyFinding {
|
|
270
|
+
return {
|
|
271
|
+
group,
|
|
272
|
+
code,
|
|
273
|
+
path,
|
|
274
|
+
...(expected !== undefined ? { expected: String(expected) } : {}),
|
|
275
|
+
...(found !== undefined ? { found: String(found) } : {}),
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function recordMismatch(
|
|
280
|
+
failures: VerifyFinding[],
|
|
281
|
+
group: VerifyGroup,
|
|
282
|
+
code: string,
|
|
283
|
+
path: string,
|
|
284
|
+
expected: unknown,
|
|
285
|
+
found: unknown
|
|
286
|
+
): void {
|
|
287
|
+
if (expected !== found) failures.push(finding(group, code, path, expected, found))
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function lockedVersions(lock: InstructionSetLockV1, appRoot: string): Array<VersionLocation & {
|
|
291
|
+
instructionSetId: string
|
|
292
|
+
version: SyncedVersionLock
|
|
293
|
+
}> {
|
|
294
|
+
return Object.entries(lock.instructionSets).flatMap(([setSlug, set]) =>
|
|
295
|
+
Object.entries(set.profiles).flatMap(([profileSlug, profile]) =>
|
|
296
|
+
Object.entries(profile.versions).map(([versionSlug, version]) => ({
|
|
297
|
+
setSlug,
|
|
298
|
+
profileSlug,
|
|
299
|
+
versionSlug,
|
|
300
|
+
version,
|
|
301
|
+
instructionSetId: set.instructionSetId,
|
|
302
|
+
relativePath: `instruction-sets/${setSlug}/${profileSlug}/${versionSlug}`,
|
|
303
|
+
absolutePath: join(appRoot, 'instruction-sets', setSlug, profileSlug, versionSlug),
|
|
304
|
+
}))
|
|
305
|
+
)
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const CUSTOMER_MODULE_TIMEOUT_MS = 1_500
|
|
310
|
+
|
|
311
|
+
class CustomerModuleError extends Error {
|
|
312
|
+
constructor(readonly code: 'helper_import_failed' | 'helper_import_timeout', readonly modulePath: string, detail?: string) {
|
|
313
|
+
super(detail ?? code)
|
|
314
|
+
this.name = 'CustomerModuleError'
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
async function runCustomerModule<T>(source: string, resolveDir: string, modulePath: string): Promise<T> {
|
|
319
|
+
let result
|
|
320
|
+
try {
|
|
321
|
+
const { build } = await import('esbuild')
|
|
322
|
+
result = await build({
|
|
323
|
+
stdin: { contents: source, resolveDir, loader: 'ts' },
|
|
324
|
+
bundle: true,
|
|
325
|
+
format: 'esm',
|
|
326
|
+
platform: 'node',
|
|
327
|
+
target: 'node20',
|
|
328
|
+
write: false,
|
|
329
|
+
})
|
|
330
|
+
} catch (error) {
|
|
331
|
+
throw new CustomerModuleError('helper_import_failed', modulePath, errorText(error))
|
|
332
|
+
}
|
|
333
|
+
const compiled = result.outputFiles[0]?.contents
|
|
334
|
+
if (!compiled) throw new CustomerModuleError('helper_import_failed', modulePath, 'instruction_set_module_compile_failed')
|
|
335
|
+
const directory = mkdtempSync(join(tmpdir(), 'orizu-verify-module-'))
|
|
336
|
+
const compiledPath = join(directory, 'module.mjs')
|
|
337
|
+
writeFileSync(compiledPath, compiled)
|
|
338
|
+
try {
|
|
339
|
+
// Scrubbed probe environment: only locale-neutral, non-secret variables reach customer modules.
|
|
340
|
+
const childEnvironment: NodeJS.ProcessEnv = { NODE_ENV: process.env.NODE_ENV ?? 'production', NO_COLOR: process.env.NO_COLOR ?? '1' }
|
|
341
|
+
for (const name of ['PATH', 'HOME', 'TMPDIR', 'TEMP', 'TMP'] as const) {
|
|
342
|
+
if (process.env[name] !== undefined) childEnvironment[name] = process.env[name]
|
|
343
|
+
}
|
|
344
|
+
const child = spawnSync('node', [compiledPath], {
|
|
345
|
+
encoding: 'utf8',
|
|
346
|
+
env: childEnvironment,
|
|
347
|
+
timeout: CUSTOMER_MODULE_TIMEOUT_MS,
|
|
348
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
349
|
+
})
|
|
350
|
+
if (child.error && (child.error as NodeJS.ErrnoException).code === 'ETIMEDOUT') {
|
|
351
|
+
throw new CustomerModuleError('helper_import_timeout', modulePath)
|
|
352
|
+
}
|
|
353
|
+
if (child.error) {
|
|
354
|
+
throw new CustomerModuleError('helper_import_failed', modulePath, errorText(child.error))
|
|
355
|
+
}
|
|
356
|
+
if (child.status !== 0) {
|
|
357
|
+
throw new CustomerModuleError('helper_import_failed', modulePath, child.stderr.trim() || `node_exit_${child.status}`)
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
return JSON.parse(child.stdout) as T
|
|
361
|
+
} catch (error) {
|
|
362
|
+
throw new CustomerModuleError('helper_import_failed', modulePath, `invalid_child_output:${errorText(error)}`)
|
|
363
|
+
}
|
|
364
|
+
} finally {
|
|
365
|
+
rmSync(directory, { recursive: true, force: true })
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
async function importGeneratedVersion(path: string): Promise<GeneratedVersionModule> {
|
|
370
|
+
return runCustomerModule<GeneratedVersionModule>(
|
|
371
|
+
`import * as target from ${JSON.stringify(path)}; process.stdout.write(JSON.stringify({ components: target.components, manifest: target.manifest }));`,
|
|
372
|
+
join(path, '..'),
|
|
373
|
+
path
|
|
374
|
+
)
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function importGeneratedLock(path: string): Promise<unknown> {
|
|
378
|
+
return runCustomerModule<unknown>(
|
|
379
|
+
`import * as target from ${JSON.stringify(path)}; process.stdout.write(JSON.stringify(target.lock));`,
|
|
380
|
+
join(path, '..'),
|
|
381
|
+
path
|
|
382
|
+
)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
async function checkHelperExport(path: string, exportName: string): Promise<boolean> {
|
|
386
|
+
return runCustomerModule<boolean>(
|
|
387
|
+
`import * as target from ${JSON.stringify(path)}; process.stdout.write(JSON.stringify(typeof target[${JSON.stringify(exportName)}] === 'function'));`,
|
|
388
|
+
join(path, '..'),
|
|
389
|
+
path
|
|
390
|
+
)
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function componentRecord(value: unknown): Record<string, string> | null {
|
|
394
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return null
|
|
395
|
+
const record = value as Record<string, unknown>
|
|
396
|
+
return Object.values(record).every(item => typeof item === 'string')
|
|
397
|
+
? record as Record<string, string>
|
|
398
|
+
: null
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function checkGroup1(
|
|
402
|
+
locations: VersionLocation[],
|
|
403
|
+
lock: InstructionSetLockV1,
|
|
404
|
+
manifests: Map<string, VersionManifest>,
|
|
405
|
+
failures: VerifyFinding[]
|
|
406
|
+
): void {
|
|
407
|
+
for (const location of locations) {
|
|
408
|
+
const manifestPath = `${location.relativePath}/manifest.json`
|
|
409
|
+
const manifest = readManifest(location)
|
|
410
|
+
if (!manifest) {
|
|
411
|
+
failures.push(finding('group1', 'version_manifest_invalid', manifestPath))
|
|
412
|
+
continue
|
|
413
|
+
}
|
|
414
|
+
manifests.set(location.relativePath, manifest)
|
|
415
|
+
const locked = lock.instructionSets[location.setSlug]?.profiles[location.profileSlug]?.versions[location.versionSlug]
|
|
416
|
+
if (!locked) continue
|
|
417
|
+
recordMismatch(failures, 'group1', 'manifest_instruction_set_slug_mismatch', manifestPath, location.setSlug, manifest.instructionSetSlug)
|
|
418
|
+
recordMismatch(failures, 'group1', 'manifest_profile_slug_mismatch', manifestPath, location.profileSlug, manifest.profileSlug)
|
|
419
|
+
recordMismatch(failures, 'group1', 'manifest_profile_identity_mismatch', manifestPath, location.profileSlug, profileSlug(manifest.modelConfigIdentity))
|
|
420
|
+
recordMismatch(failures, 'group1', 'manifest_version_slug_mismatch', manifestPath, location.versionSlug, `v${manifest.versionNumber}`)
|
|
421
|
+
recordMismatch(failures, 'group1', 'manifest_instruction_set_id_mismatch', manifestPath, lock.instructionSets[location.setSlug]!.instructionSetId, manifest.instructionSetId)
|
|
422
|
+
recordMismatch(failures, 'group1', 'manifest_profile_version_id_mismatch', manifestPath, locked.profileVersionId, manifest.profileVersionId)
|
|
423
|
+
recordMismatch(failures, 'group1', 'manifest_version_number_mismatch', manifestPath, locked.versionNumber, manifest.versionNumber)
|
|
424
|
+
recordMismatch(failures, 'group1', 'manifest_digest_mismatch', manifestPath, locked.digest, manifest.digest)
|
|
425
|
+
recordMismatch(failures, 'group1', 'manifest_provenance_instruction_set_id_mismatch', manifestPath, lock.instructionSets[location.setSlug]!.instructionSetId, manifest.provenance.instructionSetId)
|
|
426
|
+
recordMismatch(failures, 'group1', 'manifest_provenance_profile_version_id_mismatch', manifestPath, locked.profileVersionId, manifest.provenance.profileVersionId)
|
|
427
|
+
recordMismatch(failures, 'group1', 'manifest_provenance_digest_mismatch', manifestPath, locked.digest, manifest.provenance.digest)
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async function checkGroup2(
|
|
432
|
+
versions: ReturnType<typeof lockedVersions>,
|
|
433
|
+
manifests: Map<string, VersionManifest>,
|
|
434
|
+
failures: VerifyFinding[],
|
|
435
|
+
importGeneratedModule: boolean
|
|
436
|
+
): Promise<void> {
|
|
437
|
+
for (const location of versions) {
|
|
438
|
+
if (!isDirectory(location.absolutePath)) continue
|
|
439
|
+
const manifest = manifests.get(location.relativePath)
|
|
440
|
+
if (!manifest) continue
|
|
441
|
+
const actualHashes: Record<string, string> = Object.create(null)
|
|
442
|
+
const actualBytes: Record<string, string> = Object.create(null)
|
|
443
|
+
const componentsRoot = join(location.absolutePath, 'components')
|
|
444
|
+
if (isDirectory(componentsRoot)) {
|
|
445
|
+
for (const filename of readdirSync(componentsRoot).sort()) {
|
|
446
|
+
const absoluteComponentPath = join(componentsRoot, filename)
|
|
447
|
+
if (!statSync(absoluteComponentPath).isFile() || !filename.endsWith('.prompt.md')) continue
|
|
448
|
+
const name = filename.slice(0, -'.prompt.md'.length)
|
|
449
|
+
if (!Object.hasOwn(location.version.components, name)) {
|
|
450
|
+
failures.push(finding(
|
|
451
|
+
'group2',
|
|
452
|
+
`component_unexpected:${name}`,
|
|
453
|
+
`${location.relativePath}/components/${filename}`,
|
|
454
|
+
'absent',
|
|
455
|
+
hash(readFileSync(absoluteComponentPath))
|
|
456
|
+
))
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
for (const [name, lockedHash] of Object.entries(location.version.components)) {
|
|
461
|
+
const componentPath = `${location.relativePath}/components/${name}.prompt.md`
|
|
462
|
+
const absoluteComponentPath = join(location.absolutePath, 'components', `${name}.prompt.md`)
|
|
463
|
+
if (!existsSync(absoluteComponentPath)) {
|
|
464
|
+
failures.push(finding('group2', 'component_missing', componentPath, lockedHash, 'missing'))
|
|
465
|
+
continue
|
|
466
|
+
}
|
|
467
|
+
let bytes: Buffer
|
|
468
|
+
try {
|
|
469
|
+
if (!statSync(absoluteComponentPath).isFile()) {
|
|
470
|
+
failures.push(finding('group2', 'component_not_regular_file', componentPath, 'regular readable file', 'non-file'))
|
|
471
|
+
continue
|
|
472
|
+
}
|
|
473
|
+
bytes = readFileSync(absoluteComponentPath)
|
|
474
|
+
} catch (error) {
|
|
475
|
+
failures.push(finding('group2', 'component_unreadable', componentPath, 'readable file', errorText(error)))
|
|
476
|
+
continue
|
|
477
|
+
}
|
|
478
|
+
const foundHash = hash(bytes)
|
|
479
|
+
actualHashes[name] = foundHash
|
|
480
|
+
actualBytes[name] = bytes.toString('utf8')
|
|
481
|
+
recordMismatch(failures, 'group2', 'component_hash_mismatch', componentPath, lockedHash, foundHash)
|
|
482
|
+
recordMismatch(failures, 'group2', 'manifest_component_hash_mismatch', componentPath, lockedHash, manifest.components[name] ?? 'missing')
|
|
483
|
+
}
|
|
484
|
+
for (const name of Object.keys(manifest.components)) {
|
|
485
|
+
if (!Object.hasOwn(location.version.components, name)) {
|
|
486
|
+
failures.push(finding('group2', 'manifest_component_not_locked', `${location.relativePath}/manifest.json`, 'absent', name))
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
if (Object.keys(actualHashes).length === Object.keys(location.version.components).length) {
|
|
490
|
+
const foundDigest = canonicalVersionDigest(actualHashes, manifest.settings)
|
|
491
|
+
recordMismatch(failures, 'group2', 'version_digest_mismatch', `${location.relativePath}/manifest.json`, location.version.digest, foundDigest)
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
const generatedPath = `${location.relativePath}/components.generated.ts`
|
|
495
|
+
const absoluteGeneratedPath = join(location.absolutePath, 'components.generated.ts')
|
|
496
|
+
if (!existsSync(absoluteGeneratedPath)) {
|
|
497
|
+
failures.push(finding('group2', 'generated_components_missing', generatedPath))
|
|
498
|
+
continue
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
const generatedSource = readFileSync(absoluteGeneratedPath, 'utf8')
|
|
502
|
+
const expectedSource = renderGeneratedVersionModule(actualBytes, manifest)
|
|
503
|
+
if (generatedSource !== expectedSource) {
|
|
504
|
+
failures.push(finding('group2', 'generated_module_drift', generatedPath))
|
|
505
|
+
continue
|
|
506
|
+
}
|
|
507
|
+
if (!importGeneratedModule) continue
|
|
508
|
+
if (failures.some(failure => failure.group === 'group2' && failure.path.startsWith(location.relativePath))) continue
|
|
509
|
+
const importedModule = await importGeneratedVersion(absoluteGeneratedPath)
|
|
510
|
+
const generated = componentRecord(importedModule.components)
|
|
511
|
+
if (!generated) {
|
|
512
|
+
failures.push(finding('group2', 'generated_components_invalid', generatedPath))
|
|
513
|
+
continue
|
|
514
|
+
}
|
|
515
|
+
const generatedManifest = manifestValue(importedModule.manifest)
|
|
516
|
+
if (!generatedManifest) {
|
|
517
|
+
failures.push(finding('group2', 'generated_manifest_invalid', generatedPath))
|
|
518
|
+
} else {
|
|
519
|
+
const generatedRecord = generatedManifest as unknown as Record<string, unknown>
|
|
520
|
+
const diskRecord = manifest as unknown as Record<string, unknown>
|
|
521
|
+
const fields = [...new Set([...Object.keys(generatedRecord), ...Object.keys(diskRecord)])].sort()
|
|
522
|
+
for (const field of fields) {
|
|
523
|
+
if (!isDeepStrictEqual(generatedRecord[field], diskRecord[field])) {
|
|
524
|
+
failures.push(finding('group2', `generated_manifest_mismatch:${field}`, generatedPath))
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
for (const field of ['instructionSetId', 'profileVersionId', 'digest'] as const) {
|
|
528
|
+
if (generatedManifest.provenance[field] !== manifest.provenance[field]) {
|
|
529
|
+
failures.push(finding('group2', `generated_manifest_mismatch:provenance.${field}`, generatedPath))
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
const names = [...new Set([...Object.keys(actualBytes), ...Object.keys(generated)])].sort()
|
|
534
|
+
for (const name of names) {
|
|
535
|
+
if (actualBytes[name] !== generated[name]) {
|
|
536
|
+
failures.push(finding(
|
|
537
|
+
'group2',
|
|
538
|
+
'generated_component_mismatch',
|
|
539
|
+
generatedPath,
|
|
540
|
+
actualBytes[name] === undefined ? 'missing' : hash(actualBytes[name]),
|
|
541
|
+
generated[name] === undefined ? 'missing' : hash(generated[name])
|
|
542
|
+
))
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
} catch (error) {
|
|
546
|
+
const code = error instanceof CustomerModuleError ? error.code : 'generated_components_import_failed'
|
|
547
|
+
failures.push(finding('group2', code, generatedPath, undefined, errorText(error)))
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export class InstructionSetUpdateGateProfileIdentityError extends Error {
|
|
553
|
+
readonly code: string
|
|
554
|
+
|
|
555
|
+
constructor(
|
|
556
|
+
specifier: string,
|
|
557
|
+
readonly expected: string,
|
|
558
|
+
readonly found: string,
|
|
559
|
+
readonly manifestPath: string
|
|
560
|
+
) {
|
|
561
|
+
const code = `instruction_set_update_gate_profile_identity_mismatch:${specifier}`
|
|
562
|
+
super(`${code}; expected ${expected}, manifest ${manifestPath} records ${found}`)
|
|
563
|
+
this.name = 'InstructionSetUpdateGateProfileIdentityError'
|
|
564
|
+
this.code = code
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Runs group 1 plus the pure group-2 checks; never executes customer code.
|
|
569
|
+
export async function verifyMaterializedVersionAgainstLock(
|
|
570
|
+
appRoot: string,
|
|
571
|
+
lock: InstructionSetLockV1,
|
|
572
|
+
setSlug: string,
|
|
573
|
+
profileSlugValue: string,
|
|
574
|
+
versionSlug: string
|
|
575
|
+
): Promise<VerifyFinding[]> {
|
|
576
|
+
const versions = lockedVersions(lock, appRoot).filter(version =>
|
|
577
|
+
version.setSlug === setSlug
|
|
578
|
+
&& version.profileSlug === profileSlugValue
|
|
579
|
+
&& version.versionSlug === versionSlug
|
|
580
|
+
)
|
|
581
|
+
if (versions.length === 0) throw new Error('instruction_set_update_gate_no_versions')
|
|
582
|
+
const failures: VerifyFinding[] = []
|
|
583
|
+
const manifests = new Map<string, VersionManifest>()
|
|
584
|
+
checkGroup1(versions, lock, manifests, failures)
|
|
585
|
+
const expectedIdentity = lock.instructionSets[setSlug]?.profiles[profileSlugValue]?.modelConfigIdentity
|
|
586
|
+
if (expectedIdentity !== undefined) {
|
|
587
|
+
for (const version of versions) {
|
|
588
|
+
const foundIdentity = manifests.get(version.relativePath)?.modelConfigIdentity
|
|
589
|
+
if (foundIdentity !== undefined && foundIdentity !== expectedIdentity) {
|
|
590
|
+
throw new InstructionSetUpdateGateProfileIdentityError(
|
|
591
|
+
`${setSlug}/${expectedIdentity}@${version.versionSlug}`,
|
|
592
|
+
expectedIdentity,
|
|
593
|
+
foundIdentity,
|
|
594
|
+
`${version.relativePath}/manifest.json`
|
|
595
|
+
)
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
await checkGroup2(versions, manifests, failures, false)
|
|
600
|
+
return failures
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
async function runHelperChecks(
|
|
604
|
+
appRoot: string,
|
|
605
|
+
loadPath: string,
|
|
606
|
+
verifyPath: string,
|
|
607
|
+
modelConfigPath: string,
|
|
608
|
+
lock: InstructionSetLockV1,
|
|
609
|
+
requests: HelperRequest[]
|
|
610
|
+
): Promise<HelperExecutionResult> {
|
|
611
|
+
const source = `
|
|
612
|
+
import { loadInstructions } from ${JSON.stringify(loadPath)}
|
|
613
|
+
import { verifyIntegrity } from ${JSON.stringify(verifyPath)}
|
|
614
|
+
import { loadModelConfig } from ${JSON.stringify(modelConfigPath)}
|
|
615
|
+
const lock = JSON.parse(${JSON.stringify(JSON.stringify(lock))})
|
|
616
|
+
const requests = JSON.parse(${JSON.stringify(JSON.stringify(requests.map(({ specifier, kind }) => ({ specifier, kind }))))})
|
|
617
|
+
function rejection(run) {
|
|
618
|
+
try { run(); return null } catch (error) {
|
|
619
|
+
return typeof error?.code === 'string' && error.code.length > 0 ? error.code : '__unnamed__'
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
const results = []
|
|
623
|
+
for (const request of requests) {
|
|
624
|
+
try {
|
|
625
|
+
const loaded = loadInstructions(request.specifier)
|
|
626
|
+
const input = { ...loaded, digest: loaded?.provenance?.digest }
|
|
627
|
+
const result = { ...request, loaded, validRejection: rejection(() => verifyIntegrity(input, lock)) }
|
|
628
|
+
if (request.kind === 'exact') {
|
|
629
|
+
const modelConfigFailure = rejection(() => { result.modelConfig = loadModelConfig(request.specifier) })
|
|
630
|
+
if (modelConfigFailure) result.modelConfigError = modelConfigFailure
|
|
631
|
+
const firstName = Object.keys(loaded.components)[0]
|
|
632
|
+
const corruptComponents = { ...loaded.components, [firstName]: loaded.components[firstName] + '\\0' }
|
|
633
|
+
result.corruptBytesRejection = rejection(() => verifyIntegrity({ ...input, components: corruptComponents }, lock))
|
|
634
|
+
const wrongDigest = 'sha256:' + '0'.repeat(64)
|
|
635
|
+
result.wrongDigestRejection = rejection(() => verifyIntegrity({
|
|
636
|
+
...input,
|
|
637
|
+
digest: wrongDigest,
|
|
638
|
+
provenance: { ...loaded.provenance, digest: wrongDigest },
|
|
639
|
+
}, lock))
|
|
640
|
+
}
|
|
641
|
+
results.push(result)
|
|
642
|
+
} catch (error) {
|
|
643
|
+
results.push({ ...request, loadError: typeof error?.code === 'string' ? error.code : String(error) })
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const unknownRejection = rejection(() => loadInstructions('orizu-verifier-unknown'))
|
|
647
|
+
const malformedRejection = rejection(() => loadInstructions('bad set/'))
|
|
648
|
+
const modelConfigRejection = rejection(() => loadModelConfig('orizu-verifier-unknown'))
|
|
649
|
+
process.stdout.write(JSON.stringify({ results, unknownRejection, malformedRejection, modelConfigRejection }))
|
|
650
|
+
`
|
|
651
|
+
return runCustomerModule<HelperExecutionResult>(source, appRoot, 'helpers/load.ts')
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function modelConfigSetting(settings: Record<string, unknown>, key: string): { present: boolean; value?: unknown } {
|
|
655
|
+
const dot = key.indexOf('.')
|
|
656
|
+
if (dot < 0) return Object.hasOwn(settings, key) && settings[key] !== null
|
|
657
|
+
? { present: true, value: settings[key] }
|
|
658
|
+
: { present: false }
|
|
659
|
+
const parent = settings[key.slice(0, dot)]
|
|
660
|
+
const child = key.slice(dot + 1)
|
|
661
|
+
return parent !== null && typeof parent === 'object' && !Array.isArray(parent)
|
|
662
|
+
&& Object.hasOwn(parent, child) && (parent as Record<string, unknown>)[child] !== null
|
|
663
|
+
? { present: true, value: (parent as Record<string, unknown>)[child] }
|
|
664
|
+
: { present: false }
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function modelConfigFindingValue(field: string, value: unknown): string {
|
|
668
|
+
if (value === undefined) return `${field}=absent`
|
|
669
|
+
if (typeof value === 'string') return `${field}=${value}`
|
|
670
|
+
return `${field}=${JSON.stringify(value)}`
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
async function checkGroup3(
|
|
674
|
+
appRoot: string,
|
|
675
|
+
lock: InstructionSetLockV1,
|
|
676
|
+
versions: ReturnType<typeof lockedVersions>,
|
|
677
|
+
manifests: Map<string, VersionManifest>,
|
|
678
|
+
failures: VerifyFinding[]
|
|
679
|
+
): Promise<void> {
|
|
680
|
+
if (failures.some(failure => failure.group === 'group2')) return
|
|
681
|
+
const importMapPath = join(appRoot, 'generated', 'index.ts')
|
|
682
|
+
if (existsSync(importMapPath) && readFileSync(importMapPath, 'utf8') !== renderGeneratedIndex(lock)) return
|
|
683
|
+
|
|
684
|
+
const loadPath = join(appRoot, 'helpers', 'load.ts')
|
|
685
|
+
const verifyPath = join(appRoot, 'helpers', 'verify.ts')
|
|
686
|
+
const modelConfigPath = join(appRoot, 'helpers', 'model-config.ts')
|
|
687
|
+
const absent = [loadPath, verifyPath, modelConfigPath].filter(path => !existsSync(path))
|
|
688
|
+
if (absent.length > 0) {
|
|
689
|
+
failures.push(finding('group3', 'helpers_missing', 'helpers', 'load.ts,verify.ts,model-config.ts', absent.map(path => relative(appRoot, path)).join(',')))
|
|
690
|
+
return
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
try {
|
|
694
|
+
const loadValid = await checkHelperExport(loadPath, 'loadInstructions')
|
|
695
|
+
const verifyValid = await checkHelperExport(verifyPath, 'verifyIntegrity')
|
|
696
|
+
const modelConfigValid = await checkHelperExport(modelConfigPath, 'loadModelConfig')
|
|
697
|
+
if (!loadValid || !verifyValid || !modelConfigValid) {
|
|
698
|
+
failures.push(finding('group3', 'helper_exports_invalid', 'helpers'))
|
|
699
|
+
return
|
|
700
|
+
}
|
|
701
|
+
} catch (error) {
|
|
702
|
+
const customerError = error instanceof CustomerModuleError ? error : null
|
|
703
|
+
failures.push(finding(
|
|
704
|
+
'group3',
|
|
705
|
+
customerError?.code ?? 'helper_import_failed',
|
|
706
|
+
customerError ? relative(appRoot, customerError.modulePath) : 'helpers',
|
|
707
|
+
undefined,
|
|
708
|
+
errorText(error)
|
|
709
|
+
))
|
|
710
|
+
return
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
const requests: HelperRequest[] = []
|
|
714
|
+
for (const [setSlug, set] of Object.entries(lock.instructionSets)) {
|
|
715
|
+
for (const [profileSlug, profile] of Object.entries(set.profiles)) {
|
|
716
|
+
if (profile.modelConfigIdentity === undefined) {
|
|
717
|
+
failures.push(finding('group3', 'model_config_identity_unbound', `instruction-sets/${setSlug}/${profileSlug}`))
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
for (const location of versions) {
|
|
722
|
+
if (!isDirectory(location.absolutePath)) continue
|
|
723
|
+
const manifest = manifests.get(location.relativePath)
|
|
724
|
+
if (!manifest) continue
|
|
725
|
+
requests.push({
|
|
726
|
+
specifier: `${location.setSlug}/${manifest.modelConfigIdentity}@v${location.version.versionNumber}`,
|
|
727
|
+
kind: 'exact',
|
|
728
|
+
path: location.relativePath,
|
|
729
|
+
expectedInstructionSetId: location.instructionSetId,
|
|
730
|
+
expectedProfileVersionId: location.version.profileVersionId,
|
|
731
|
+
expectedDigest: location.version.digest,
|
|
732
|
+
expectedModelConfigIdentity: lock.instructionSets[location.setSlug]?.profiles[location.profileSlug]?.modelConfigIdentity ?? manifest.modelConfigIdentity,
|
|
733
|
+
})
|
|
734
|
+
}
|
|
735
|
+
for (const [setSlug, set] of Object.entries(lock.instructionSets)) {
|
|
736
|
+
const defaultProfile = set.profiles[set.default]
|
|
737
|
+
if (defaultProfile?.production) {
|
|
738
|
+
const version = defaultProfile.versions[defaultProfile.production]
|
|
739
|
+
if (version) requests.push({
|
|
740
|
+
specifier: setSlug,
|
|
741
|
+
kind: 'default',
|
|
742
|
+
path: `instruction-sets/${setSlug}/${set.default}/${defaultProfile.production}`,
|
|
743
|
+
expectedInstructionSetId: set.instructionSetId,
|
|
744
|
+
expectedProfileVersionId: version.profileVersionId,
|
|
745
|
+
expectedDigest: version.digest,
|
|
746
|
+
expectedModelConfigIdentity: defaultProfile.modelConfigIdentity
|
|
747
|
+
?? manifests.get(`instruction-sets/${setSlug}/${set.default}/${defaultProfile.production}`)?.modelConfigIdentity
|
|
748
|
+
?? '',
|
|
749
|
+
})
|
|
750
|
+
}
|
|
751
|
+
for (const [profileSlug, profile] of Object.entries(set.profiles)) {
|
|
752
|
+
if (!profile.production) continue
|
|
753
|
+
const version = profile.versions[profile.production]
|
|
754
|
+
const manifest = manifests.get(`instruction-sets/${setSlug}/${profileSlug}/${profile.production}`)
|
|
755
|
+
if (!version || !manifest) continue
|
|
756
|
+
requests.push({
|
|
757
|
+
specifier: `${setSlug}/${manifest.modelConfigIdentity}`,
|
|
758
|
+
kind: 'profile',
|
|
759
|
+
path: `instruction-sets/${setSlug}/${profileSlug}/${profile.production}`,
|
|
760
|
+
expectedInstructionSetId: set.instructionSetId,
|
|
761
|
+
expectedProfileVersionId: version.profileVersionId,
|
|
762
|
+
expectedDigest: version.digest,
|
|
763
|
+
expectedModelConfigIdentity: profile.modelConfigIdentity ?? manifest.modelConfigIdentity,
|
|
764
|
+
})
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
let execution: HelperExecutionResult
|
|
769
|
+
try {
|
|
770
|
+
execution = await runHelperChecks(appRoot, loadPath, verifyPath, modelConfigPath, lock, requests)
|
|
771
|
+
} catch (error) {
|
|
772
|
+
const customerError = error instanceof CustomerModuleError ? error : null
|
|
773
|
+
failures.push(finding('group3', customerError?.code ?? 'helper_import_failed', customerError?.modulePath ?? 'helpers', undefined, errorText(error)))
|
|
774
|
+
return
|
|
775
|
+
}
|
|
776
|
+
const expectedRejection = 'instruction_set_specifier_unknown'
|
|
777
|
+
if (execution.unknownRejection !== expectedRejection || execution.malformedRejection !== expectedRejection) {
|
|
778
|
+
failures.push(finding(
|
|
779
|
+
'group3',
|
|
780
|
+
'helper_rejection_inert',
|
|
781
|
+
'helpers/load.ts',
|
|
782
|
+
`${expectedRejection},${expectedRejection}`,
|
|
783
|
+
`${execution.unknownRejection ?? 'accepted'},${execution.malformedRejection ?? 'accepted'}`
|
|
784
|
+
))
|
|
785
|
+
}
|
|
786
|
+
if (execution.modelConfigRejection !== expectedRejection) {
|
|
787
|
+
failures.push(finding(
|
|
788
|
+
'group3',
|
|
789
|
+
'helper_rejection_inert',
|
|
790
|
+
'helpers/model-config.ts',
|
|
791
|
+
expectedRejection,
|
|
792
|
+
execution.modelConfigRejection ?? 'accepted'
|
|
793
|
+
))
|
|
794
|
+
}
|
|
795
|
+
for (const request of requests) {
|
|
796
|
+
const result = execution.results.find(candidate => candidate.specifier === request.specifier && candidate.kind === request.kind)
|
|
797
|
+
if (!result || result.loadError) {
|
|
798
|
+
failures.push(finding('group3', request.kind === 'exact' ? 'load_failed' : 'pointer_load_failed', request.path, request.specifier, result?.loadError ?? 'missing child result'))
|
|
799
|
+
continue
|
|
800
|
+
}
|
|
801
|
+
if (request.kind === 'exact' && result.modelConfigError) {
|
|
802
|
+
failures.push(finding('group3', 'model_config_load_failed', request.path, request.specifier, result.modelConfigError))
|
|
803
|
+
continue
|
|
804
|
+
}
|
|
805
|
+
const identitySlash = request.expectedModelConfigIdentity.indexOf('/')
|
|
806
|
+
const expectedProvider = identitySlash < 0
|
|
807
|
+
? request.expectedModelConfigIdentity
|
|
808
|
+
: request.expectedModelConfigIdentity.slice(0, identitySlash)
|
|
809
|
+
const expectedModel = typeof result.loaded?.settings?.model === 'string'
|
|
810
|
+
? result.loaded.settings.model
|
|
811
|
+
: identitySlash < 0
|
|
812
|
+
? request.expectedModelConfigIdentity
|
|
813
|
+
: request.expectedModelConfigIdentity.slice(identitySlash + 1)
|
|
814
|
+
const settings = result.loaded?.settings ?? {}
|
|
815
|
+
const hydratedFields = MODEL_CONFIG_FIELDS.map(definition => {
|
|
816
|
+
const found = definition.settingsKeys.map(key => modelConfigSetting(settings, key)).find(item => item.present)
|
|
817
|
+
const expected = definition.field === 'MODEL' && !found ? expectedModel : found?.value
|
|
818
|
+
return [definition.field, expected, result.modelConfig?.[definition.field]] as const
|
|
819
|
+
})
|
|
820
|
+
const modelConfigComparisons = [
|
|
821
|
+
['CONFIG_IDENTITY', request.expectedModelConfigIdentity, result.modelConfig?.CONFIG_IDENTITY],
|
|
822
|
+
['PROVIDER', expectedProvider, result.modelConfig?.PROVIDER],
|
|
823
|
+
...hydratedFields,
|
|
824
|
+
['RAW', settings, result.modelConfig?.RAW],
|
|
825
|
+
] as const
|
|
826
|
+
const mismatchedModelConfigFields = request.kind === 'exact'
|
|
827
|
+
? modelConfigComparisons.filter(([, expected, found]) => !isDeepStrictEqual(found, expected))
|
|
828
|
+
: []
|
|
829
|
+
for (const [field, expected, found] of mismatchedModelConfigFields) {
|
|
830
|
+
failures.push(finding('group3', field === 'CONFIG_IDENTITY' || field === 'PROVIDER' || field === 'MODEL'
|
|
831
|
+
? 'model_config_identity_mismatch' : 'model_config_field_mismatch', request.path,
|
|
832
|
+
modelConfigFindingValue(field, expected), modelConfigFindingValue(field, found)))
|
|
833
|
+
}
|
|
834
|
+
if (mismatchedModelConfigFields.length > 0) continue
|
|
835
|
+
if (
|
|
836
|
+
result.loaded?.provenance?.instructionSetId !== request.expectedInstructionSetId ||
|
|
837
|
+
result.loaded?.provenance?.profileVersionId !== request.expectedProfileVersionId ||
|
|
838
|
+
result.loaded?.provenance?.digest !== request.expectedDigest
|
|
839
|
+
) {
|
|
840
|
+
failures.push(finding('group3', 'helper_provenance_mismatch', request.path))
|
|
841
|
+
continue
|
|
842
|
+
}
|
|
843
|
+
if (result.validRejection) {
|
|
844
|
+
failures.push(finding('group3', 'integrity_failed', request.path, undefined, result.validRejection))
|
|
845
|
+
continue
|
|
846
|
+
}
|
|
847
|
+
if (request.kind === 'exact' && (
|
|
848
|
+
!result.corruptBytesRejection || result.corruptBytesRejection === '__unnamed__' ||
|
|
849
|
+
!result.wrongDigestRejection || result.wrongDigestRejection === '__unnamed__'
|
|
850
|
+
)) {
|
|
851
|
+
failures.push(finding('group3', 'helper_integrity_inert', request.path))
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function compiledShadowFindings(
|
|
857
|
+
appRoot: string,
|
|
858
|
+
versions: ReturnType<typeof lockedVersions>
|
|
859
|
+
): VerifyFinding[] {
|
|
860
|
+
const emittedTypeScriptPaths = [
|
|
861
|
+
'generated/index.ts',
|
|
862
|
+
...managedHelperPaths(),
|
|
863
|
+
...versions.map(version => `${version.relativePath}/components.generated.ts`),
|
|
864
|
+
]
|
|
865
|
+
return emittedTypeScriptPaths.flatMap(emittedPath => {
|
|
866
|
+
const shadowPath = `${emittedPath.slice(0, -'.ts'.length)}.js`
|
|
867
|
+
return existsSync(join(appRoot, shadowPath))
|
|
868
|
+
? [finding('group4', 'generated_index_compiled_shadow', shadowPath)]
|
|
869
|
+
: []
|
|
870
|
+
})
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
async function checkGroup4(
|
|
874
|
+
appRoot: string,
|
|
875
|
+
lock: InstructionSetLockV1,
|
|
876
|
+
locations: VersionLocation[],
|
|
877
|
+
versions: ReturnType<typeof lockedVersions>,
|
|
878
|
+
manifests: Map<string, VersionManifest>,
|
|
879
|
+
failures: VerifyFinding[],
|
|
880
|
+
warnings: VerifyFinding[],
|
|
881
|
+
hasCompiledShadows: boolean
|
|
882
|
+
): Promise<void> {
|
|
883
|
+
for (const [setSlug, set] of Object.entries(lock.instructionSets)) {
|
|
884
|
+
const defaultProfile = set.profiles[set.default]
|
|
885
|
+
if (defaultProfile && !isDirectory(join(appRoot, 'instruction-sets', setSlug, set.default))) {
|
|
886
|
+
failures.push(finding('group4', 'default_pointer_target_missing', `instruction-sets/${setSlug}/${set.default}`))
|
|
887
|
+
}
|
|
888
|
+
for (const [profileSlug, profile] of Object.entries(set.profiles)) {
|
|
889
|
+
if (profile.production && !isDirectory(join(appRoot, 'instruction-sets', setSlug, profileSlug, profile.production))) {
|
|
890
|
+
failures.push(finding('group4', 'pointer_target_missing', `instruction-sets/${setSlug}/${profileSlug}/${profile.production}`))
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
const lockedPaths = new Set(versions.map(version => version.relativePath))
|
|
896
|
+
for (const version of versions) {
|
|
897
|
+
if (!isDirectory(version.absolutePath)) {
|
|
898
|
+
failures.push(finding('group4', 'lock_version_folder_missing', version.relativePath))
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
for (const location of locations) {
|
|
902
|
+
if (!lockedPaths.has(location.relativePath)) failures.push(finding('group4', 'orphan_version_folder', location.relativePath))
|
|
903
|
+
}
|
|
904
|
+
const instructionSetsRoot = join(appRoot, 'instruction-sets')
|
|
905
|
+
for (const setSlug of directoryNames(instructionSetsRoot)) {
|
|
906
|
+
if (!Object.hasOwn(lock.instructionSets, setSlug)) {
|
|
907
|
+
failures.push(finding('group4', 'orphan_instruction_set_folder', `instruction-sets/${setSlug}`))
|
|
908
|
+
continue
|
|
909
|
+
}
|
|
910
|
+
for (const profileSlug of directoryNames(join(instructionSetsRoot, setSlug))) {
|
|
911
|
+
if (!Object.hasOwn(lock.instructionSets[setSlug]!.profiles, profileSlug)) {
|
|
912
|
+
failures.push(finding('group4', 'orphan_profile_folder', `instruction-sets/${setSlug}/${profileSlug}`))
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
const helperFingerprints = lock.helpers ?? {}
|
|
918
|
+
const expectedHelperPaths = managedHelperPaths()
|
|
919
|
+
for (const helperPath of expectedHelperPaths) {
|
|
920
|
+
const hasFingerprint = Object.hasOwn(helperFingerprints, helperPath)
|
|
921
|
+
const absoluteHelperPath = resolve(appRoot, helperPath)
|
|
922
|
+
if (!existsSync(absoluteHelperPath)) {
|
|
923
|
+
failures.push(finding('group4', 'helper_missing', helperPath, hasFingerprint ? helperFingerprints[helperPath] : 'managed Helper', 'missing'))
|
|
924
|
+
} else if (!hasFingerprint) {
|
|
925
|
+
failures.push(finding('group4', 'helper_fingerprint_missing', helperPath))
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
for (const [helperPath, expectedHash] of Object.entries(helperFingerprints)) {
|
|
929
|
+
const absoluteHelperPath = resolve(appRoot, helperPath)
|
|
930
|
+
const confined = relative(appRoot, absoluteHelperPath)
|
|
931
|
+
if (confined.startsWith('..') || resolve(appRoot, confined) !== absoluteHelperPath) {
|
|
932
|
+
failures.push(finding('group4', 'helper_path_unsafe', helperPath))
|
|
933
|
+
continue
|
|
934
|
+
}
|
|
935
|
+
if (!existsSync(absoluteHelperPath)) {
|
|
936
|
+
failures.push(finding('group4', 'helper_missing', helperPath, expectedHash, 'missing'))
|
|
937
|
+
continue
|
|
938
|
+
}
|
|
939
|
+
const foundHash = hash(readFileSync(absoluteHelperPath))
|
|
940
|
+
if (foundHash !== expectedHash) warnings.push(finding('group4', 'helper_modified', helperPath, expectedHash, foundHash))
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
const importMapPath = join(appRoot, 'generated', 'index.ts')
|
|
944
|
+
if (!existsSync(importMapPath)) {
|
|
945
|
+
warnings.push(finding('group4', 'import_map_missing', 'generated/index.ts'))
|
|
946
|
+
return
|
|
947
|
+
}
|
|
948
|
+
const importMapSource = readFileSync(importMapPath, 'utf8')
|
|
949
|
+
const importMapDrifted = importMapSource !== renderGeneratedIndex(lock)
|
|
950
|
+
if (importMapDrifted) {
|
|
951
|
+
failures.push(finding('group4', 'generated_module_drift', 'generated/index.ts'))
|
|
952
|
+
}
|
|
953
|
+
const versionModuleIntegrityFailed = failures.some(failure => failure.group === 'group2')
|
|
954
|
+
if (!importMapDrifted && !versionModuleIntegrityFailed && !hasCompiledShadows) {
|
|
955
|
+
try {
|
|
956
|
+
const generatedLock = await importGeneratedLock(importMapPath)
|
|
957
|
+
if (!isDeepStrictEqual(generatedLock, lock)) {
|
|
958
|
+
failures.push(finding('group4', 'generated_lock_stale', 'generated/index.ts'))
|
|
959
|
+
}
|
|
960
|
+
} catch (error) {
|
|
961
|
+
const code = error instanceof CustomerModuleError ? error.code : 'import_map_mismatch'
|
|
962
|
+
failures.push(finding('group4', code, 'generated/index.ts', 'importable generated Lock', errorText(error)))
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
try {
|
|
966
|
+
const importMap = staticImportMap(importMapSource)
|
|
967
|
+
const mappedKeys = Object.entries(importMap.versions).flatMap(([setSlug, profiles]) =>
|
|
968
|
+
Object.entries(profiles).flatMap(([profileSlug, mappedVersions]) =>
|
|
969
|
+
Object.keys(mappedVersions).map(versionSlug => `${setSlug}/${profileSlug}/${versionSlug}`)
|
|
970
|
+
)
|
|
971
|
+
).sort()
|
|
972
|
+
const expectedKeys = versions.map(version => `${version.setSlug}/${version.profileSlug}/${version.versionSlug}`).sort()
|
|
973
|
+
if (JSON.stringify(mappedKeys) !== JSON.stringify(expectedKeys)) {
|
|
974
|
+
failures.push(finding('group4', 'import_map_mismatch', 'generated/index.ts', expectedKeys.join(','), mappedKeys.join(',')))
|
|
975
|
+
}
|
|
976
|
+
const usedIdentifiers = new Set<string>()
|
|
977
|
+
for (const version of versions) {
|
|
978
|
+
const identifier = importMap.versions[version.setSlug]?.[version.profileSlug]?.[version.versionSlug]
|
|
979
|
+
if (!identifier) continue
|
|
980
|
+
usedIdentifiers.add(identifier)
|
|
981
|
+
const foundDestination = importMap.imports.get(identifier) ?? 'missing'
|
|
982
|
+
const expectedDestination = `../instruction-sets/${version.setSlug}/${version.profileSlug}/${version.versionSlug}/components.generated.js`
|
|
983
|
+
if (foundDestination !== expectedDestination) {
|
|
984
|
+
const manifest = manifests.get(version.relativePath)
|
|
985
|
+
const specifier = manifest
|
|
986
|
+
? `${version.setSlug}/${manifest.modelConfigIdentity}@v${version.version.versionNumber}`
|
|
987
|
+
: `${version.setSlug}/${version.profileSlug}@v${version.version.versionNumber}`
|
|
988
|
+
failures.push(finding(
|
|
989
|
+
'group4',
|
|
990
|
+
`import_map_destination_mismatch:${specifier}`,
|
|
991
|
+
'generated/index.ts',
|
|
992
|
+
expectedDestination,
|
|
993
|
+
foundDestination
|
|
994
|
+
))
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
const importedIdentifiers = [...importMap.imports.keys()].sort()
|
|
998
|
+
const mappedIdentifiers = [...usedIdentifiers].sort()
|
|
999
|
+
if (JSON.stringify(importedIdentifiers) !== JSON.stringify(mappedIdentifiers)) {
|
|
1000
|
+
failures.push(finding('group4', 'import_map_mismatch', 'generated/index.ts', mappedIdentifiers.join(','), importedIdentifiers.join(',')))
|
|
1001
|
+
}
|
|
1002
|
+
} catch (error) {
|
|
1003
|
+
failures.push(finding('group4', 'import_map_mismatch', 'generated/index.ts', 'valid static map', errorText(error)))
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
export async function verifyInstructionSetTree(out: string): Promise<VerifyReport> {
|
|
1008
|
+
const appRoot = resolve(out, 'orizu')
|
|
1009
|
+
const failures: VerifyFinding[] = []
|
|
1010
|
+
const warnings: VerifyFinding[] = []
|
|
1011
|
+
let lock: InstructionSetLockV1
|
|
1012
|
+
try {
|
|
1013
|
+
lock = parseLock(readFileSync(join(appRoot, 'orizu.lock.json'), 'utf8'))
|
|
1014
|
+
} catch (error) {
|
|
1015
|
+
failures.push(finding('group4', 'lock_invalid', 'orizu.lock.json', undefined, errorText(error)))
|
|
1016
|
+
return {
|
|
1017
|
+
ok: false,
|
|
1018
|
+
groups: { group1: 'SKIPPED', group2: 'SKIPPED', group3: 'SKIPPED', group4: 'FAIL' },
|
|
1019
|
+
failures,
|
|
1020
|
+
warnings,
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
const locations = versionLocations(appRoot)
|
|
1025
|
+
const versions = lockedVersions(lock, appRoot)
|
|
1026
|
+
const manifests = new Map<string, VersionManifest>()
|
|
1027
|
+
checkGroup1(locations, lock, manifests, failures)
|
|
1028
|
+
await checkGroup2(versions, manifests, failures, true)
|
|
1029
|
+
const shadowFailures = compiledShadowFindings(appRoot, versions)
|
|
1030
|
+
failures.push(...shadowFailures)
|
|
1031
|
+
if (shadowFailures.length === 0) {
|
|
1032
|
+
await checkGroup3(appRoot, lock, versions, manifests, failures)
|
|
1033
|
+
}
|
|
1034
|
+
await checkGroup4(appRoot, lock, locations, versions, manifests, failures, warnings, shadowFailures.length > 0)
|
|
1035
|
+
const groups = Object.fromEntries(
|
|
1036
|
+
(['group1', 'group2', 'group3', 'group4'] as const).map(group => [
|
|
1037
|
+
group,
|
|
1038
|
+
group === 'group3' && shadowFailures.length > 0
|
|
1039
|
+
? 'SKIPPED'
|
|
1040
|
+
: failures.some(failure => failure.group === group) ? 'FAIL' : 'PASS',
|
|
1041
|
+
])
|
|
1042
|
+
) as Record<VerifyGroup, VerifyGroupStatus>
|
|
1043
|
+
return { ok: failures.length === 0, groups, failures, warnings }
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
export function printVerifyReport(report: VerifyReport, json: boolean, print: (line: string) => void): void {
|
|
1047
|
+
if (json) {
|
|
1048
|
+
print(JSON.stringify(report))
|
|
1049
|
+
return
|
|
1050
|
+
}
|
|
1051
|
+
for (const group of ['group1', 'group2', 'group3', 'group4'] as const) {
|
|
1052
|
+
const count = report.failures.filter(failure => failure.group === group).length
|
|
1053
|
+
print(`${report.groups[group]} ${group}${count === 0 ? '' : ` (${count})`}`)
|
|
1054
|
+
}
|
|
1055
|
+
for (const failure of report.failures) {
|
|
1056
|
+
const detail = failure.expected !== undefined || failure.found !== undefined
|
|
1057
|
+
? ` expected ${failure.expected ?? 'n/a'} found ${failure.found ?? 'n/a'}`
|
|
1058
|
+
: ''
|
|
1059
|
+
print(`FAIL ${failure.group} ${failure.path} ${failure.code}${detail}`)
|
|
1060
|
+
}
|
|
1061
|
+
for (const warning of report.warnings) {
|
|
1062
|
+
const detail = warning.expected !== undefined || warning.found !== undefined
|
|
1063
|
+
? ` expected ${warning.expected ?? 'n/a'} found ${warning.found ?? 'n/a'}`
|
|
1064
|
+
: ''
|
|
1065
|
+
print(`WARN ${warning.group} ${warning.path} ${warning.code}${detail}`)
|
|
1066
|
+
}
|
|
1067
|
+
}
|