owlrunkit 0.13.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.
- package/CHANGELOG.md +9 -0
- package/LICENSE +674 -0
- package/NOTICE.md +137 -0
- package/README.md +44 -0
- package/SOURCE.md +10 -0
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_1.md +160 -0
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +459 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +408 -0
- package/integrations/codex/skills/owlcoda-runkit/agents/openai.yaml +4 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/agents-snippet.md +7 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +10 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/coverage-adopt-request.json +26 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/execution-delivery-packet.json +13 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/finalize-request.json +18 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/goal-contract.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/profiles.json +9 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/ready-for-commit-request.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/resource-preflight-request.json +56 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/resume-request.json +10 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/snapshot-request.json +23 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-context.json +15 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verification-receipt.json +11 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/verify-plan-request.json +31 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/visual-smoke-request.json +32 -0
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/worker-lease.json +7 -0
- package/package.json +24 -0
- package/packages/attest/LICENSE +674 -0
- package/packages/attest/README.md +63 -0
- package/packages/attest/cli/owlcoda-attest.mjs +155 -0
- package/packages/attest/consumer-surface-v1.json +255 -0
- package/packages/attest/distribution-manifest-v1.json +104 -0
- package/packages/attest/fixtures/bad/accepted-authority-leak.json +7 -0
- package/packages/attest/fixtures/bad/accepted-failed-receipt.json +7 -0
- package/packages/attest/fixtures/bad/active-context-binding-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/active-source-binding-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/context-fingerprint-mismatch.json +7 -0
- package/packages/attest/fixtures/bad/duplicate-object-key.json +8 -0
- package/packages/attest/fixtures/bad/multiple-active-leaves.json +10 -0
- package/packages/attest/fixtures/bad/orphan-replacement.json +7 -0
- package/packages/attest/fixtures/bad/root-shape-extra-property.json +8 -0
- package/packages/attest/fixtures/bad/source-fingerprint-mismatch.json +7 -0
- package/packages/attest/fixtures/good/accepted-replacement-chain.json +59 -0
- package/packages/attest/fixtures/good/blocked-single-leaf.json +9 -0
- package/packages/attest/fixtures/index.json +90 -0
- package/packages/attest/sbom.spdx.json +33 -0
- package/packages/attest/schemas/attestation-ref-v1.schema.json +44 -0
- package/packages/attest/schemas/attestation-result-v1.schema.json +163 -0
- package/packages/attest/schemas/offline-attestation-bundle-v1.schema.json +30 -0
- package/packages/attest/schemas/public-verification-bundle-v1.schema.json +74 -0
- package/packages/attest/schemas/quick-verification-receipt-v1.schema.json +219 -0
- package/packages/attest/schemas/workspace-snapshot-v1.schema.json +135 -0
- package/packages/attest/src/canonical.mjs +12 -0
- package/packages/attest/src/formal.mjs +762 -0
- package/packages/attest/src/index.mjs +113 -0
- package/packages/attest/src/offline-bundle.mjs +108 -0
- package/packages/attest/src/quick-receipt-contract.mjs +156 -0
- package/packages/attest/src/quick.mjs +274 -0
- package/packages/attest/src/reference-contract.mjs +64 -0
- package/packages/attest/src/reference.mjs +131 -0
- package/packages/attest/src/workspace-snapshot.mjs +171 -0
- package/registry-release-policy-v1.json +34 -0
- package/sbom.spdx.json +39 -0
- package/scripts/runkit-contract/acceptance-evidence.mjs +103 -0
- package/scripts/runkit-contract/core-contract.mjs +392 -0
- package/scripts/runkit-contract/coverage-adoption.mjs +414 -0
- package/scripts/runkit-contract/delivery-create.mjs +252 -0
- package/scripts/runkit-contract/delivery-selection.mjs +31 -0
- package/scripts/runkit-contract/finalize.mjs +790 -0
- package/scripts/runkit-contract/inspect-presentation.mjs +183 -0
- package/scripts/runkit-contract/install-codex-skill.mjs +239 -0
- package/scripts/runkit-contract/lease-lifecycle.mjs +324 -0
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +415 -0
- package/scripts/runkit-contract/offline-store.mjs +248 -0
- package/scripts/runkit-contract/profile-impact.mjs +223 -0
- package/scripts/runkit-contract/provenance-common.mjs +153 -0
- package/scripts/runkit-contract/quick-attest.mjs +4 -0
- package/scripts/runkit-contract/quick-canonical.mjs +41 -0
- package/scripts/runkit-contract/quick-metrics.mjs +86 -0
- package/scripts/runkit-contract/quick-receipt.mjs +139 -0
- package/scripts/runkit-contract/quick-verify.mjs +201 -0
- package/scripts/runkit-contract/quick-workspace-snapshot.mjs +3 -0
- package/scripts/runkit-contract/ready-for-commit.mjs +188 -0
- package/scripts/runkit-contract/receipt-lineage.mjs +332 -0
- package/scripts/runkit-contract/registry-adoption-gate.mjs +117 -0
- package/scripts/runkit-contract/repair-execution.mjs +997 -0
- package/scripts/runkit-contract/resource-preflight.mjs +829 -0
- package/scripts/runkit-contract/resume-execution.mjs +569 -0
- package/scripts/runkit-contract/runkit-cli.mjs +1214 -0
- package/scripts/runkit-contract/schemas/closeout-receipt-v2.schema.json +42 -0
- package/scripts/runkit-contract/schemas/core-artifact-v2.schema.json +23 -0
- package/scripts/runkit-contract/schemas/core-artifact.schema.json +23 -0
- package/scripts/runkit-contract/schemas/coverage-adopt-request-v1.schema.json +66 -0
- package/scripts/runkit-contract/schemas/engine-pin-v2.schema.json +14 -0
- package/scripts/runkit-contract/schemas/engine-pin.schema.json +14 -0
- package/scripts/runkit-contract/schemas/evidence-coverage-index-v1.schema.json +131 -0
- package/scripts/runkit-contract/schemas/finalize-request-v1.schema.json +131 -0
- package/scripts/runkit-contract/schemas/profiles.schema.json +47 -0
- package/scripts/runkit-contract/schemas/project-config-v2.schema.json +13 -0
- package/scripts/runkit-contract/schemas/project-config.schema.json +13 -0
- package/scripts/runkit-contract/schemas/ready-for-commit-v1.schema.json +26 -0
- package/scripts/runkit-contract/schemas/replayable-evidence-v1.schema.json +82 -0
- package/scripts/runkit-contract/schemas/resource-preflight-request-v1.schema.json +178 -0
- package/scripts/runkit-contract/schemas/resource-preflight-v1.schema.json +153 -0
- package/scripts/runkit-contract/schemas/resume-attempt-v1.schema.json +80 -0
- package/scripts/runkit-contract/schemas/resume-request-v1.schema.json +77 -0
- package/scripts/runkit-contract/schemas/snapshot-v1.schema.json +71 -0
- package/scripts/runkit-contract/schemas/verification-context-v1.schema.json +92 -0
- package/scripts/runkit-contract/schemas/verification-plan-v1.schema.json +150 -0
- package/scripts/runkit-contract/schemas/verification-receipt-v2.schema.json +118 -0
- package/scripts/runkit-contract/schemas/verify-plan-request-v1.schema.json +56 -0
- package/scripts/runkit-contract/schemas/visual-smoke-request-v1.schema.json +64 -0
- package/scripts/runkit-contract/schemas/visual-smoke-result-v1.schema.json +68 -0
- package/scripts/runkit-contract/snapshot.mjs +340 -0
- package/scripts/runkit-contract/source-fingerprint.mjs +404 -0
- package/scripts/runkit-contract/templates/coverage-adopt-request.json +26 -0
- package/scripts/runkit-contract/templates/finalize-request.json +18 -0
- package/scripts/runkit-contract/templates/goal-contract.json +11 -0
- package/scripts/runkit-contract/templates/ready-for-commit-request.json +11 -0
- package/scripts/runkit-contract/templates/resource-preflight-request.json +56 -0
- package/scripts/runkit-contract/templates/resume-request.json +10 -0
- package/scripts/runkit-contract/templates/snapshot-request.json +23 -0
- package/scripts/runkit-contract/templates/verify-plan-request.json +31 -0
- package/scripts/runkit-contract/templates/visual-smoke-request.json +32 -0
- package/scripts/runkit-contract/templates/worker-lease.json +7 -0
- package/scripts/runkit-contract/verification-context.mjs +141 -0
- package/scripts/runkit-contract/verification-plan.mjs +608 -0
- package/scripts/runkit-contract/verification-receipt-gate.mjs +566 -0
- package/scripts/runkit-contract/visual-smoke.mjs +410 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawnSync } from 'node:child_process'
|
|
4
|
+
import { createHash } from 'node:crypto'
|
|
5
|
+
import { lstatSync, readFileSync, realpathSync } from 'node:fs'
|
|
6
|
+
import { isAbsolute, relative, resolve, win32 } from 'node:path'
|
|
7
|
+
import { isDirectExecution, isReservedRuntimePath } from './core-contract.mjs'
|
|
8
|
+
|
|
9
|
+
const SHA256_PATTERN = /^[a-f0-9]{64}$/i
|
|
10
|
+
|
|
11
|
+
function isRecord(value) {
|
|
12
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function sha256(value) {
|
|
16
|
+
return createHash('sha256').update(value).digest('hex')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function compareUnicodeCodeUnits(left, right) {
|
|
20
|
+
if (left < right) return -1
|
|
21
|
+
if (left > right) return 1
|
|
22
|
+
return 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function canonicalSourceStream(files) {
|
|
26
|
+
return Object.entries(files)
|
|
27
|
+
.sort(([left], [right]) => compareUnicodeCodeUnits(left, right))
|
|
28
|
+
.map(([path, hash]) => `${path}\tsha256:${hash.toLowerCase()}\n`)
|
|
29
|
+
.join('')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function canonicalSourceFingerprint(files) {
|
|
33
|
+
return sha256(canonicalSourceStream(files))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function malformedResult(issues, extra = {}) {
|
|
37
|
+
return {
|
|
38
|
+
status: 'malformed_packet',
|
|
39
|
+
exitCode: 3,
|
|
40
|
+
issues,
|
|
41
|
+
...extra,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function safePacketPath(path) {
|
|
46
|
+
if (typeof path !== 'string' || !path || isAbsolute(path) || win32.parse(path).root) return false
|
|
47
|
+
if (path.includes('\\') || path.includes('\0')) return false
|
|
48
|
+
const segments = path.split('/')
|
|
49
|
+
return !segments.includes('..') && !segments.includes('.') && segments.every(Boolean) && !isReservedRuntimePath(path)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function withinRoot(root, candidate) {
|
|
53
|
+
const remainder = relative(root, candidate)
|
|
54
|
+
return remainder === '' || (!remainder.startsWith('../') && remainder !== '..' && !isAbsolute(remainder))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function validOwnedRule(value) {
|
|
58
|
+
if (typeof value !== 'string' || !value) return false
|
|
59
|
+
if (!value.endsWith('/**')) return safePacketPath(value)
|
|
60
|
+
const prefix = value.slice(0, -3)
|
|
61
|
+
return safePacketPath(prefix)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function matchesOwnedRule(filePath, rule) {
|
|
65
|
+
if (rule.endsWith('/**')) {
|
|
66
|
+
const prefix = rule.slice(0, -3)
|
|
67
|
+
return filePath === prefix || filePath.startsWith(`${prefix}/`)
|
|
68
|
+
}
|
|
69
|
+
return filePath === rule
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function ownedStatusRecords(workspaceRoot, ownedPaths) {
|
|
73
|
+
const completed = spawnSync('git', [
|
|
74
|
+
'-C', workspaceRoot,
|
|
75
|
+
'status',
|
|
76
|
+
'--porcelain=v1',
|
|
77
|
+
'-z',
|
|
78
|
+
'--untracked-files=all',
|
|
79
|
+
'--',
|
|
80
|
+
'.',
|
|
81
|
+
':(exclude).owlcoda/runkit/**',
|
|
82
|
+
], {
|
|
83
|
+
encoding: 'utf8',
|
|
84
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
85
|
+
})
|
|
86
|
+
if (completed.error) throw completed.error
|
|
87
|
+
if (completed.status !== 0) throw new Error(completed.stderr.trim() || 'Git status failed')
|
|
88
|
+
const entries = completed.stdout.split('\0')
|
|
89
|
+
const records = []
|
|
90
|
+
for (let index = 0; index < entries.length; index += 1) {
|
|
91
|
+
const entry = entries[index]
|
|
92
|
+
if (!entry) continue
|
|
93
|
+
if (entry.length < 4 || entry[2] !== ' ') throw new Error('Git status emitted a malformed entry')
|
|
94
|
+
const status = entry.slice(0, 2)
|
|
95
|
+
const currentPath = entry.slice(3)
|
|
96
|
+
const paths = [currentPath]
|
|
97
|
+
if (status.includes('R') || status.includes('C')) {
|
|
98
|
+
const priorPath = entries[index + 1]
|
|
99
|
+
if (!priorPath) throw new Error('Git status rename entry is incomplete')
|
|
100
|
+
paths.push(priorPath)
|
|
101
|
+
index += 1
|
|
102
|
+
}
|
|
103
|
+
if (!paths.some(filePath => ownedPaths.some(rule => matchesOwnedRule(filePath, rule)))) continue
|
|
104
|
+
records.push({ status, paths })
|
|
105
|
+
}
|
|
106
|
+
return records.sort((left, right) =>
|
|
107
|
+
JSON.stringify(left.paths).localeCompare(JSON.stringify(right.paths)))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function boundLease({ root, packet }) {
|
|
111
|
+
if (typeof packet.discovery?.leasePath !== 'string'
|
|
112
|
+
|| typeof packet.discovery?.fromLease !== 'string') {
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
const leaseCandidate = resolve(root, packet.discovery.leasePath)
|
|
116
|
+
if (!withinRoot(root, leaseCandidate)) {
|
|
117
|
+
return { malformed: 'ownedPathState leasePath escapes workspace' }
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
const stat = lstatSync(leaseCandidate)
|
|
121
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
122
|
+
return { malformed: 'ownedPathState leasePath must be a regular file' }
|
|
123
|
+
}
|
|
124
|
+
const realLease = realpathSync(leaseCandidate)
|
|
125
|
+
if (!withinRoot(root, realLease)) {
|
|
126
|
+
return { malformed: 'ownedPathState leasePath resolves outside workspace' }
|
|
127
|
+
}
|
|
128
|
+
const lease = JSON.parse(readFileSync(realLease, 'utf8'))
|
|
129
|
+
if (lease.workItemId !== packet.discovery.fromLease
|
|
130
|
+
|| !Array.isArray(lease.ownedPaths)
|
|
131
|
+
|| lease.ownedPaths.length === 0
|
|
132
|
+
|| new Set(lease.ownedPaths).size !== lease.ownedPaths.length
|
|
133
|
+
|| lease.ownedPaths.some(rule => !validOwnedRule(rule))) {
|
|
134
|
+
return { malformed: 'ownedPathState lease artifact is invalid' }
|
|
135
|
+
}
|
|
136
|
+
return { lease }
|
|
137
|
+
} catch {
|
|
138
|
+
return { malformed: 'ownedPathState lease artifact is unreadable' }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function validateOwnedPathState({ root, packet, declaredFiles }) {
|
|
143
|
+
const state = packet?.discovery?.ownedPathState
|
|
144
|
+
const leaseBinding = boundLease({ root, packet })
|
|
145
|
+
if (state === undefined) {
|
|
146
|
+
if (leaseBinding === null) return { issues: [] }
|
|
147
|
+
if (leaseBinding.malformed) return { malformed: [leaseBinding.malformed] }
|
|
148
|
+
let current
|
|
149
|
+
try {
|
|
150
|
+
current = ownedStatusRecords(root, leaseBinding.lease.ownedPaths)
|
|
151
|
+
} catch {
|
|
152
|
+
return { malformed: ['legacy DeliveryPacket requires readable Git status'] }
|
|
153
|
+
}
|
|
154
|
+
const declaredPaths = Object.keys(declaredFiles).sort()
|
|
155
|
+
const currentPaths = current.map(record => record.paths[0]).sort()
|
|
156
|
+
return JSON.stringify(currentPaths) === JSON.stringify(declaredPaths)
|
|
157
|
+
? { issues: [] }
|
|
158
|
+
: { issues: ['owned workspace status paths changed since legacy DeliveryPacket creation'] }
|
|
159
|
+
}
|
|
160
|
+
if (!isRecord(state)
|
|
161
|
+
|| state.schemaVersion !== 'OwlCodaRunKitOwnedPathStateV1'
|
|
162
|
+
|| state.statusMode !== 'porcelain-v1-z-untracked-all-runkit-excluded'
|
|
163
|
+
|| !Array.isArray(state.ownedPaths)
|
|
164
|
+
|| state.ownedPaths.length === 0
|
|
165
|
+
|| !Array.isArray(state.records)) {
|
|
166
|
+
return { malformed: ['ownedPathState is malformed'] }
|
|
167
|
+
}
|
|
168
|
+
if (state.ownedPaths.some(rule => !validOwnedRule(rule))
|
|
169
|
+
|| new Set(state.ownedPaths).size !== state.ownedPaths.length) {
|
|
170
|
+
return { malformed: ['ownedPathState ownedPaths are invalid'] }
|
|
171
|
+
}
|
|
172
|
+
for (const record of state.records) {
|
|
173
|
+
if (!isRecord(record)
|
|
174
|
+
|| typeof record.status !== 'string'
|
|
175
|
+
|| record.status.length !== 2
|
|
176
|
+
|| !Array.isArray(record.paths)
|
|
177
|
+
|| record.paths.length === 0
|
|
178
|
+
|| record.paths.some(filePath => !safePacketPath(filePath))
|
|
179
|
+
|| !record.paths.some(filePath =>
|
|
180
|
+
state.ownedPaths.some(rule => matchesOwnedRule(filePath, rule)))) {
|
|
181
|
+
return { malformed: ['ownedPathState records are invalid'] }
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (leaseBinding === null) {
|
|
185
|
+
return { malformed: ['ownedPathState requires leasePath and fromLease'] }
|
|
186
|
+
}
|
|
187
|
+
if (leaseBinding.malformed) return { malformed: [leaseBinding.malformed] }
|
|
188
|
+
if (JSON.stringify(leaseBinding.lease.ownedPaths) !== JSON.stringify(state.ownedPaths)) {
|
|
189
|
+
return { malformed: ['ownedPathState does not match its lease artifact'] }
|
|
190
|
+
}
|
|
191
|
+
const declaredPaths = Object.keys(declaredFiles).sort()
|
|
192
|
+
const recordedCurrentPaths = state.records
|
|
193
|
+
.map(record => record.paths[0])
|
|
194
|
+
.sort()
|
|
195
|
+
if (JSON.stringify(recordedCurrentPaths) !== JSON.stringify(declaredPaths)) {
|
|
196
|
+
return { malformed: ['ownedPathState records do not match declared changed files'] }
|
|
197
|
+
}
|
|
198
|
+
let current
|
|
199
|
+
try {
|
|
200
|
+
current = ownedStatusRecords(root, state.ownedPaths)
|
|
201
|
+
} catch {
|
|
202
|
+
return { malformed: ['ownedPathState requires readable Git status'] }
|
|
203
|
+
}
|
|
204
|
+
return JSON.stringify(current) === JSON.stringify(state.records)
|
|
205
|
+
? { issues: [] }
|
|
206
|
+
: { issues: ['owned workspace status changed since DeliveryPacket creation'] }
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function extractDeclaredFiles(packet) {
|
|
210
|
+
if (!isRecord(packet) || !isRecord(packet.changedFiles)) {
|
|
211
|
+
return { issues: ['changedFiles must be an object'] }
|
|
212
|
+
}
|
|
213
|
+
const shapes = []
|
|
214
|
+
for (const key of ['files', 'wholeFileSha256']) {
|
|
215
|
+
if (!(key in packet.changedFiles)) continue
|
|
216
|
+
const value = packet.changedFiles[key]
|
|
217
|
+
if (!isRecord(value)) return { issues: [`changedFiles.${key} must be an object`] }
|
|
218
|
+
shapes.push([key, value])
|
|
219
|
+
}
|
|
220
|
+
if (shapes.length === 0) {
|
|
221
|
+
return { issues: ['changedFiles.files or changedFiles.wholeFileSha256 is required'] }
|
|
222
|
+
}
|
|
223
|
+
if (shapes.length > 1) {
|
|
224
|
+
return { issues: ['packet must declare exactly one changed file hash shape'] }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const files = {}
|
|
228
|
+
const issues = []
|
|
229
|
+
const [shape, entries] = shapes[0]
|
|
230
|
+
for (const [path, hash] of Object.entries(entries)) {
|
|
231
|
+
if (!safePacketPath(path)) {
|
|
232
|
+
issues.push(isReservedRuntimePath(path) ? `reserved runtime path: ${path}` : `unsafe scoped path: ${path}`)
|
|
233
|
+
}
|
|
234
|
+
if (typeof hash !== 'string' || !SHA256_PATTERN.test(hash)) {
|
|
235
|
+
issues.push(`invalid SHA-256 for ${path} in changedFiles.${shape}`)
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
files[path] = hash.toLowerCase()
|
|
239
|
+
}
|
|
240
|
+
if (Object.keys(files).length === 0) issues.push('at least one scoped path is required')
|
|
241
|
+
return issues.length > 0 ? { issues } : { files }
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function inspectWorkspaceFile(root, path) {
|
|
245
|
+
let current = root
|
|
246
|
+
const segments = path.split('/')
|
|
247
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
248
|
+
current = resolve(current, segments[index])
|
|
249
|
+
let stat
|
|
250
|
+
try {
|
|
251
|
+
stat = lstatSync(current)
|
|
252
|
+
} catch (error) {
|
|
253
|
+
if (error && typeof error === 'object' && error.code === 'ENOENT') return { status: 'missing' }
|
|
254
|
+
return { status: 'unreadable' }
|
|
255
|
+
}
|
|
256
|
+
if (stat.isSymbolicLink()) return { status: 'malformed', issue: `scoped path traverses a symlink: ${path}` }
|
|
257
|
+
const isLeaf = index === segments.length - 1
|
|
258
|
+
if (!isLeaf && !stat.isDirectory()) {
|
|
259
|
+
return { status: 'malformed', issue: `scoped path has a non-directory parent: ${path}` }
|
|
260
|
+
}
|
|
261
|
+
if (isLeaf && !stat.isFile()) {
|
|
262
|
+
return { status: 'malformed', issue: `scoped path is not a regular file: ${path}` }
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return { status: 'regular_file', path: current }
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function extractDeclaredFingerprint(packet) {
|
|
269
|
+
const declarations = []
|
|
270
|
+
for (const key of ['productSourceFingerprint', 'sourceFingerprint']) {
|
|
271
|
+
if (!(key in packet)) continue
|
|
272
|
+
if (!isRecord(packet[key]) || typeof packet[key].sha256 !== 'string') {
|
|
273
|
+
return { issues: [`${key}.sha256 must be a string`] }
|
|
274
|
+
}
|
|
275
|
+
declarations.push([key, packet[key].sha256])
|
|
276
|
+
}
|
|
277
|
+
if (declarations.length === 0) {
|
|
278
|
+
return { issues: ['productSourceFingerprint.sha256 or sourceFingerprint.sha256 is required'] }
|
|
279
|
+
}
|
|
280
|
+
if (declarations.length > 1) {
|
|
281
|
+
return { issues: ['packet must declare exactly one source fingerprint shape'] }
|
|
282
|
+
}
|
|
283
|
+
const [shape, fingerprint] = declarations[0]
|
|
284
|
+
if (!SHA256_PATTERN.test(fingerprint)) {
|
|
285
|
+
return { issues: [`${shape}.sha256 must be a 64-character SHA-256`] }
|
|
286
|
+
}
|
|
287
|
+
return { fingerprint: fingerprint.toLowerCase(), shape }
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
export function verifyDeliveryPacket({ workspaceRoot, packet }) {
|
|
291
|
+
if (typeof workspaceRoot !== 'string' || !workspaceRoot) {
|
|
292
|
+
return malformedResult(['workspaceRoot is required'])
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
let root
|
|
296
|
+
try {
|
|
297
|
+
root = realpathSync(workspaceRoot)
|
|
298
|
+
} catch {
|
|
299
|
+
return malformedResult(['workspaceRoot does not exist'])
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const declaredFiles = extractDeclaredFiles(packet)
|
|
303
|
+
if (declaredFiles.issues) return malformedResult(declaredFiles.issues)
|
|
304
|
+
const declaredFingerprint = extractDeclaredFingerprint(packet)
|
|
305
|
+
if (declaredFingerprint.issues) return malformedResult(declaredFingerprint.issues)
|
|
306
|
+
const ownedPathState = validateOwnedPathState({
|
|
307
|
+
root,
|
|
308
|
+
packet,
|
|
309
|
+
declaredFiles: declaredFiles.files,
|
|
310
|
+
})
|
|
311
|
+
if (ownedPathState.malformed) return malformedResult(ownedPathState.malformed)
|
|
312
|
+
|
|
313
|
+
const issues = [...ownedPathState.issues]
|
|
314
|
+
const actualFiles = {}
|
|
315
|
+
const declaredCanonicalStream = canonicalSourceStream(declaredFiles.files)
|
|
316
|
+
const fingerprintFromDeclaredHashes = sha256(declaredCanonicalStream)
|
|
317
|
+
if (fingerprintFromDeclaredHashes !== declaredFingerprint.fingerprint) {
|
|
318
|
+
issues.push('declared fingerprint does not match the canonical declared file hashes')
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
for (const [path, expectedHash] of Object.entries(declaredFiles.files).sort(([left], [right]) => compareUnicodeCodeUnits(left, right))) {
|
|
322
|
+
const candidate = resolve(root, path)
|
|
323
|
+
if (!withinRoot(root, candidate)) {
|
|
324
|
+
return malformedResult([`scoped path escapes workspace: ${path}`])
|
|
325
|
+
}
|
|
326
|
+
const inspected = inspectWorkspaceFile(root, path)
|
|
327
|
+
if (inspected.status === 'malformed') return malformedResult([inspected.issue])
|
|
328
|
+
if (inspected.status === 'missing') {
|
|
329
|
+
issues.push(`missing scoped file: ${path}`)
|
|
330
|
+
continue
|
|
331
|
+
}
|
|
332
|
+
if (inspected.status === 'unreadable') {
|
|
333
|
+
issues.push(`unreadable scoped file: ${path}`)
|
|
334
|
+
continue
|
|
335
|
+
}
|
|
336
|
+
let realCandidate
|
|
337
|
+
try {
|
|
338
|
+
realCandidate = realpathSync(inspected.path)
|
|
339
|
+
} catch {
|
|
340
|
+
issues.push(`unreadable scoped file: ${path}`)
|
|
341
|
+
continue
|
|
342
|
+
}
|
|
343
|
+
if (!withinRoot(root, realCandidate)) {
|
|
344
|
+
return malformedResult([`scoped path resolves outside workspace: ${path}`])
|
|
345
|
+
}
|
|
346
|
+
try {
|
|
347
|
+
const actualHash = sha256(readFileSync(realCandidate))
|
|
348
|
+
actualFiles[path] = actualHash
|
|
349
|
+
if (actualHash !== expectedHash) issues.push(`whole-file hash mismatch: ${path}`)
|
|
350
|
+
} catch {
|
|
351
|
+
issues.push(`unreadable scoped file: ${path}`)
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
const hasAllActualFiles = Object.keys(actualFiles).length === Object.keys(declaredFiles.files).length
|
|
356
|
+
const actualCanonicalStream = hasAllActualFiles ? canonicalSourceStream(actualFiles) : null
|
|
357
|
+
const recomputedFingerprint = actualCanonicalStream ? sha256(actualCanonicalStream) : null
|
|
358
|
+
if (recomputedFingerprint && recomputedFingerprint !== declaredFingerprint.fingerprint) {
|
|
359
|
+
issues.push('recomputed workspace fingerprint does not match the packet declaration')
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const status = issues.length === 0 ? 'valid' : 'invalidated_by_concurrent_write'
|
|
363
|
+
return {
|
|
364
|
+
status,
|
|
365
|
+
exitCode: status === 'valid' ? 0 : 2,
|
|
366
|
+
fileCount: Object.keys(declaredFiles.files).length,
|
|
367
|
+
fingerprintShape: declaredFingerprint.shape,
|
|
368
|
+
declaredFingerprint: declaredFingerprint.fingerprint,
|
|
369
|
+
fingerprintFromDeclaredHashes,
|
|
370
|
+
recomputedFingerprint,
|
|
371
|
+
canonicalStream: actualCanonicalStream ?? declaredCanonicalStream,
|
|
372
|
+
issues,
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function parseCliArguments(argv) {
|
|
377
|
+
const result = {}
|
|
378
|
+
for (let index = 0; index < argv.length; index += 2) {
|
|
379
|
+
const flag = argv[index]
|
|
380
|
+
const value = argv[index + 1]
|
|
381
|
+
if ((flag !== '--workspace' && flag !== '--packet') || !value) return null
|
|
382
|
+
if (flag === '--workspace') result.workspaceRoot = value
|
|
383
|
+
if (flag === '--packet') result.packetPath = value
|
|
384
|
+
}
|
|
385
|
+
return result.workspaceRoot && result.packetPath ? result : null
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function runCli(argv) {
|
|
389
|
+
const input = parseCliArguments(argv)
|
|
390
|
+
if (!input) return malformedResult(['usage: source-fingerprint.mjs --workspace <root> --packet <packet.json>'])
|
|
391
|
+
let packet
|
|
392
|
+
try {
|
|
393
|
+
packet = JSON.parse(readFileSync(input.packetPath, 'utf8'))
|
|
394
|
+
} catch {
|
|
395
|
+
return malformedResult(['packet must exist and contain valid JSON'])
|
|
396
|
+
}
|
|
397
|
+
return verifyDeliveryPacket({ workspaceRoot: input.workspaceRoot, packet })
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (isDirectExecution(import.meta.url)) {
|
|
401
|
+
const result = runCli(process.argv.slice(2))
|
|
402
|
+
process.stdout.write(`${JSON.stringify(result)}\n`)
|
|
403
|
+
process.exitCode = result.exitCode
|
|
404
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitCoverageAdoptRequestV1",
|
|
3
|
+
"coverageId": "replace-with-coverage-id",
|
|
4
|
+
"sources": [
|
|
5
|
+
{
|
|
6
|
+
"gateInputPath": ".owlcoda/runkit/executions/replace-with-source-run/verification-receipts/replace-with-receipt/verification-gate-input.json",
|
|
7
|
+
"gateInputSha256": "replace-with-lowercase-sha256",
|
|
8
|
+
"commandBindings": [
|
|
9
|
+
{
|
|
10
|
+
"receiptCommandId": "replace-with-receipt-command-id",
|
|
11
|
+
"profileId": "replace-with-profile-id",
|
|
12
|
+
"commandId": "replace-with-profile-command-id"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"dependencyBindings": [
|
|
16
|
+
{
|
|
17
|
+
"dependencyId": "root-lockfile",
|
|
18
|
+
"source": {
|
|
19
|
+
"kind": "lockfile",
|
|
20
|
+
"identity": "package-lock.json"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitFinalizeRequestV1",
|
|
3
|
+
"receiptId": "replace-with-stable-receipt-id",
|
|
4
|
+
"deliveryPacketPath": ".owlcoda/runkit/executions/replace-with-run-id/delivery-packets/replace-with-packet.json",
|
|
5
|
+
"verificationContext": {
|
|
6
|
+
"schemaVersion": "OwlCodaRunKitVerificationContextV1",
|
|
7
|
+
"reusePolicy": "portable",
|
|
8
|
+
"platform": null,
|
|
9
|
+
"toolchains": [],
|
|
10
|
+
"lockfiles": [],
|
|
11
|
+
"fixtures": [],
|
|
12
|
+
"services": [],
|
|
13
|
+
"environment": []
|
|
14
|
+
},
|
|
15
|
+
"snapshotPaths": [
|
|
16
|
+
".owlcoda/runkit/executions/replace-with-run-id/snapshots/replace-with-snapshot.json"
|
|
17
|
+
]
|
|
18
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitReadyForCommitRequestV1",
|
|
3
|
+
"deliveryPacketPath": ".owlcoda/runkit/executions/replace-with-run-id/delivery-packets/replace-with-packet.json",
|
|
4
|
+
"verificationGateInputPath": ".owlcoda/runkit/executions/replace-with-run-id/verification-receipts/replace-with-receipt/verification-gate-input.json",
|
|
5
|
+
"roots": [
|
|
6
|
+
{
|
|
7
|
+
"role": "candidate",
|
|
8
|
+
"snapshotPath": ".owlcoda/runkit/executions/replace-with-run-id/snapshots/replace-with-root-snapshot.json"
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitResourcePreflightRequestV1",
|
|
3
|
+
"preflightId": "resource-preflight-001",
|
|
4
|
+
"verificationPlanPath": ".owlcoda/runkit/executions/replace-run-id/verification-plans/replace-plan-id.json",
|
|
5
|
+
"verificationPlanSha256": "replace-with-lowercase-sha256",
|
|
6
|
+
"policy": {
|
|
7
|
+
"gateMode": "model_required",
|
|
8
|
+
"maxObservationAgeMs": 300000,
|
|
9
|
+
"unknownHandling": {
|
|
10
|
+
"availability": "fail_closed",
|
|
11
|
+
"quota": "fail_closed",
|
|
12
|
+
"resetTime": "fail_open",
|
|
13
|
+
"pricing": "fail_closed"
|
|
14
|
+
},
|
|
15
|
+
"limits": {
|
|
16
|
+
"maxCalls": 100,
|
|
17
|
+
"maxTotalTokens": 1000000,
|
|
18
|
+
"maxElapsedMs": 3600000,
|
|
19
|
+
"maxCostUsd": 10
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"observations": [
|
|
23
|
+
{
|
|
24
|
+
"providerId": "replace-provider",
|
|
25
|
+
"modelId": "replace-model",
|
|
26
|
+
"adapter": {
|
|
27
|
+
"id": "owlcoda-model-resource-adapter",
|
|
28
|
+
"kind": "owlcoda_runtime",
|
|
29
|
+
"evidenceRef": "artifact:sha256:replace-with-source-reference",
|
|
30
|
+
"evidenceSha256": "replace-with-lowercase-sha256",
|
|
31
|
+
"observedAt": "2026-01-01T00:00:00.000Z"
|
|
32
|
+
},
|
|
33
|
+
"availability": {
|
|
34
|
+
"status": "unknown",
|
|
35
|
+
"reason": "adapter_has_no_verified_live_probe"
|
|
36
|
+
},
|
|
37
|
+
"quota": {
|
|
38
|
+
"remainingCalls": { "status": "unknown", "reason": "provider_not_exposed" },
|
|
39
|
+
"remainingTokens": { "status": "unknown", "reason": "provider_not_exposed" },
|
|
40
|
+
"resetAt": { "status": "unknown", "reason": "provider_not_exposed" }
|
|
41
|
+
},
|
|
42
|
+
"pricing": { "status": "unknown", "reason": "project_pricing_not_declared" }
|
|
43
|
+
}
|
|
44
|
+
],
|
|
45
|
+
"workloads": [
|
|
46
|
+
{
|
|
47
|
+
"id": "replace-model-workload",
|
|
48
|
+
"providerId": "replace-provider",
|
|
49
|
+
"modelId": "replace-model",
|
|
50
|
+
"calls": 1,
|
|
51
|
+
"inputTokens": 0,
|
|
52
|
+
"outputTokens": 0,
|
|
53
|
+
"elapsedMs": 0
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitResumeRequestV1",
|
|
3
|
+
"resumeId": "replace-with-resume-id",
|
|
4
|
+
"continuationRunId": "replace-with-new-run-id-or-null-for-active-source",
|
|
5
|
+
"reason": "replace-with-explicit-resume-reason",
|
|
6
|
+
"coverage": {
|
|
7
|
+
"coverageId": "replace-with-coverage-id",
|
|
8
|
+
"sources": []
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitSnapshotRequestV1",
|
|
3
|
+
"snapshotId": "replace-with-stable-snapshot-id",
|
|
4
|
+
"mode": "project",
|
|
5
|
+
"targetRoot": "replace-with-absolute-project-root",
|
|
6
|
+
"cwd": ".",
|
|
7
|
+
"executable": "replace-with-absolute-executable-path",
|
|
8
|
+
"argv": ["replace-with-exact-argument"],
|
|
9
|
+
"launcherVersion": "replace-with-launcher-version",
|
|
10
|
+
"toolVersions": [{ "name": "replace-with-tool-name", "version": "replace-with-tool-version" }],
|
|
11
|
+
"selectedPaths": ["replace-with-product-source-path"],
|
|
12
|
+
"statusMode": "porcelain-v1-z-untracked-all-runkit-excluded",
|
|
13
|
+
"verificationContext": {
|
|
14
|
+
"schemaVersion": "OwlCodaRunKitVerificationContextV1",
|
|
15
|
+
"reusePolicy": "portable",
|
|
16
|
+
"platform": null,
|
|
17
|
+
"toolchains": [],
|
|
18
|
+
"lockfiles": [],
|
|
19
|
+
"fixtures": [],
|
|
20
|
+
"services": [],
|
|
21
|
+
"environment": []
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitVerifyPlanRequestV1",
|
|
3
|
+
"planId": "replace-with-plan-id",
|
|
4
|
+
"deliveryPacketPath": ".owlcoda/runkit/executions/replace-with-run-id/delivery-packets/replace-with-packet.json",
|
|
5
|
+
"statusMode": "porcelain-v1-z-untracked-all-runkit-excluded",
|
|
6
|
+
"dependencies": [
|
|
7
|
+
{
|
|
8
|
+
"id": "root-lockfile",
|
|
9
|
+
"path": "package-lock.json",
|
|
10
|
+
"baselineSha256": "replace-with-lowercase-sha256"
|
|
11
|
+
}
|
|
12
|
+
],
|
|
13
|
+
"verificationContext": {
|
|
14
|
+
"schemaVersion": "OwlCodaRunKitVerificationContextV1",
|
|
15
|
+
"reusePolicy": "portable",
|
|
16
|
+
"platform": null,
|
|
17
|
+
"toolchains": [
|
|
18
|
+
{
|
|
19
|
+
"name": "node",
|
|
20
|
+
"version": "replace-with-exact-version"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"lockfiles": [],
|
|
24
|
+
"fixtures": [],
|
|
25
|
+
"services": [],
|
|
26
|
+
"environment": []
|
|
27
|
+
},
|
|
28
|
+
"coverageIndexPath": ".owlcoda/runkit/executions/replace-with-run-id/coverage-indexes/replace-with-coverage-id.json",
|
|
29
|
+
"coverageIndexSha256": "replace-with-lowercase-sha256",
|
|
30
|
+
"globalGates": []
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "OwlCodaRunKitVisualSmokeRequestV1",
|
|
3
|
+
"smokeId": "replace-with-smoke-id",
|
|
4
|
+
"mode": "project",
|
|
5
|
+
"targetRoot": "/absolute/project/path",
|
|
6
|
+
"cwd": ".",
|
|
7
|
+
"executable": "/absolute/path/to/automation-runner",
|
|
8
|
+
"argv": ["--result", "/absolute/project/path/.owlcoda/runkit/executions/replace-with-run-id/visual-smoke-evidence/replace-with-smoke-id/result.json"],
|
|
9
|
+
"launcherVersion": "replace-with-version",
|
|
10
|
+
"toolVersions": [],
|
|
11
|
+
"selectedPaths": ["replace-with-source-path"],
|
|
12
|
+
"statusMode": "porcelain-v1-z-untracked-all-runkit-excluded",
|
|
13
|
+
"verificationContext": {
|
|
14
|
+
"schemaVersion": "OwlCodaRunKitVerificationContextV1",
|
|
15
|
+
"reusePolicy": "portable",
|
|
16
|
+
"platform": null,
|
|
17
|
+
"toolchains": [],
|
|
18
|
+
"lockfiles": [],
|
|
19
|
+
"fixtures": [],
|
|
20
|
+
"services": [],
|
|
21
|
+
"environment": []
|
|
22
|
+
},
|
|
23
|
+
"resultPath": ".owlcoda/runkit/executions/replace-with-run-id/visual-smoke-evidence/replace-with-smoke-id/result.json",
|
|
24
|
+
"automationManifest": {
|
|
25
|
+
"schemaVersion": "OwlCodaRunKitVisualAutomationManifestV1",
|
|
26
|
+
"surface": "browser",
|
|
27
|
+
"viewport": { "width": 1280, "height": 720 },
|
|
28
|
+
"assertionIds": ["replace-with-assertion-id"],
|
|
29
|
+
"consolePolicy": { "maxErrors": 0, "maxWarnings": 0, "maxExceptions": 0 },
|
|
30
|
+
"externalNavigationPolicy": "deny"
|
|
31
|
+
}
|
|
32
|
+
}
|