owlrunkit 0.13.0 → 0.14.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 +14 -0
- package/README.md +22 -4
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +79 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +101 -2
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +1 -1
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +5 -0
- package/packages/attest/src/quick.mjs +5 -0
- package/registry-release-policy-v1.json +1 -1
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +6 -1
- package/scripts/runkit-contract/onboarding-doctor.mjs +607 -0
- package/scripts/runkit-contract/profile-onboarding.mjs +332 -0
- package/scripts/runkit-contract/registry-adoption-gate.mjs +10 -7
- package/scripts/runkit-contract/registry-adoption.mjs +197 -0
- package/scripts/runkit-contract/resume-execution.mjs +8 -2
- package/scripts/runkit-contract/runkit-cli.mjs +174 -6
- package/scripts/runkit-contract/schemas/adoption-evidence-v1.schema.json +63 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
} from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
readWorkspaceJsonBounded,
|
|
8
|
+
resolveBoundedWorkspaceRoot,
|
|
9
|
+
} from "./onboarding-doctor.mjs";
|
|
10
|
+
import { resolveProfileImpactDetailed } from "./profile-impact.mjs";
|
|
11
|
+
import {
|
|
12
|
+
safeIdentifier,
|
|
13
|
+
safeRelativePath,
|
|
14
|
+
} from "./provenance-common.mjs";
|
|
15
|
+
|
|
16
|
+
function command(id, script) {
|
|
17
|
+
return {
|
|
18
|
+
id,
|
|
19
|
+
cwd: ".",
|
|
20
|
+
executable: "npm",
|
|
21
|
+
argv: ["run", script],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function detectedScripts(manifest) {
|
|
26
|
+
return Object.keys(manifest.scripts ?? {}).sort();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function profileCandidates(root, manifest) {
|
|
30
|
+
const scripts = new Set(detectedScripts(manifest));
|
|
31
|
+
const candidates = [];
|
|
32
|
+
if (
|
|
33
|
+
existsSync(path.join(root, "prisma"))
|
|
34
|
+
|| [...scripts].some((script) => /^(db:|prisma)/.test(script))
|
|
35
|
+
) {
|
|
36
|
+
const commands = [...scripts]
|
|
37
|
+
.filter((script) => /^(db:|prisma)/.test(script))
|
|
38
|
+
.map((script) => command(`npm-${script.replaceAll(":", "-")}`, script));
|
|
39
|
+
candidates.push({
|
|
40
|
+
id: "database",
|
|
41
|
+
paths: ["prisma/**"],
|
|
42
|
+
role: "primary",
|
|
43
|
+
primary: false,
|
|
44
|
+
requiresProfileIds: [],
|
|
45
|
+
commands,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
if (
|
|
49
|
+
existsSync(path.join(root, "package.json"))
|
|
50
|
+
&& [...scripts].some((script) => ["build", "lint", "test", "typecheck"].includes(script))
|
|
51
|
+
) {
|
|
52
|
+
const commands = ["build", "lint", "test", "typecheck"]
|
|
53
|
+
.filter((script) => scripts.has(script))
|
|
54
|
+
.map((script) => command(`npm-${script}`, script));
|
|
55
|
+
candidates.push({
|
|
56
|
+
id: "node-quality",
|
|
57
|
+
paths: ["src/**", "tests/**"],
|
|
58
|
+
role: "primary",
|
|
59
|
+
primary: false,
|
|
60
|
+
requiresProfileIds: [],
|
|
61
|
+
commands,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (
|
|
65
|
+
existsSync(path.join(root, "playwright.config.ts"))
|
|
66
|
+
|| existsSync(path.join(root, "playwright.config.js"))
|
|
67
|
+
|| scripts.has("e2e")
|
|
68
|
+
) {
|
|
69
|
+
const commands = scripts.has("e2e") ? [command("npm-e2e", "e2e")] : [];
|
|
70
|
+
candidates.push({
|
|
71
|
+
id: "web-e2e",
|
|
72
|
+
paths: ["playwright.config.ts", "tests/e2e/**"],
|
|
73
|
+
role: "primary",
|
|
74
|
+
primary: false,
|
|
75
|
+
requiresProfileIds: [],
|
|
76
|
+
commands,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return candidates.sort((left, right) => left.id.localeCompare(right.id));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function detectProfiles({ workspaceRoot } = {}) {
|
|
83
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
84
|
+
const manifest = readWorkspaceJsonBounded(root, "package.json");
|
|
85
|
+
const candidates = profileCandidates(root, manifest);
|
|
86
|
+
const primaryCandidates = candidates.filter((profile) => profile.role === "primary");
|
|
87
|
+
return {
|
|
88
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV1",
|
|
89
|
+
status: "profiles_detected",
|
|
90
|
+
dryRun: true,
|
|
91
|
+
writesPerformed: 0,
|
|
92
|
+
candidates,
|
|
93
|
+
primaryProfileId: primaryCandidates.length === 1 ? primaryCandidates[0].id : null,
|
|
94
|
+
warnings: primaryCandidates.length > 1
|
|
95
|
+
? [`ambiguous_primary_profile:${primaryCandidates.map((profile) => profile.id).join(",")}`]
|
|
96
|
+
: [],
|
|
97
|
+
detectedScripts: detectedScripts(manifest),
|
|
98
|
+
authorizationGranted: false,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function exactKeys(value, allowed) {
|
|
103
|
+
return value
|
|
104
|
+
&& typeof value === "object"
|
|
105
|
+
&& !Array.isArray(value)
|
|
106
|
+
&& Object.keys(value).every((key) => allowed.includes(key));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function representativePath(rule) {
|
|
110
|
+
return rule.endsWith("/**")
|
|
111
|
+
? `${rule.slice(0, -3)}/__owlrunkit_profile_surface__`
|
|
112
|
+
: rule;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function validateProfileDocument(config) {
|
|
116
|
+
const issues = [];
|
|
117
|
+
if (
|
|
118
|
+
!exactKeys(config, ["schemaVersion", "profiles"])
|
|
119
|
+
|| config.schemaVersion !== "OwlCodaRunKitProfilesV1"
|
|
120
|
+
|| !Array.isArray(config.profiles)
|
|
121
|
+
|| config.profiles.length === 0
|
|
122
|
+
) {
|
|
123
|
+
return ["profiles_document_invalid"];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const ids = new Set();
|
|
127
|
+
const commandIds = new Set();
|
|
128
|
+
for (const profile of config.profiles) {
|
|
129
|
+
if (!exactKeys(profile, [
|
|
130
|
+
"commands",
|
|
131
|
+
"id",
|
|
132
|
+
"paths",
|
|
133
|
+
"primary",
|
|
134
|
+
"requiresProfileIds",
|
|
135
|
+
"role",
|
|
136
|
+
])) {
|
|
137
|
+
issues.push("profile_shape_invalid");
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (typeof profile.id !== "string" || profile.id.length === 0) {
|
|
141
|
+
issues.push("profile_id_invalid");
|
|
142
|
+
} else if (ids.has(profile.id)) {
|
|
143
|
+
issues.push(`duplicate_profile_id:${profile.id}`);
|
|
144
|
+
} else {
|
|
145
|
+
ids.add(profile.id);
|
|
146
|
+
}
|
|
147
|
+
if (!Array.isArray(profile.paths) || profile.paths.length === 0) {
|
|
148
|
+
issues.push(`profile_paths_missing:${profile.id ?? "unknown"}`);
|
|
149
|
+
}
|
|
150
|
+
if (profile.role !== undefined && !["primary", "supporting"].includes(profile.role)) {
|
|
151
|
+
issues.push(`profile_role_invalid:${profile.id ?? "unknown"}`);
|
|
152
|
+
}
|
|
153
|
+
if (profile.primary !== undefined && typeof profile.primary !== "boolean") {
|
|
154
|
+
issues.push(`profile_primary_invalid:${profile.id ?? "unknown"}`);
|
|
155
|
+
}
|
|
156
|
+
if (
|
|
157
|
+
profile.requiresProfileIds !== undefined
|
|
158
|
+
&& (
|
|
159
|
+
!Array.isArray(profile.requiresProfileIds)
|
|
160
|
+
|| profile.requiresProfileIds.some((id) => typeof id !== "string" || id.length === 0)
|
|
161
|
+
|| new Set(profile.requiresProfileIds).size !== profile.requiresProfileIds.length
|
|
162
|
+
)
|
|
163
|
+
) {
|
|
164
|
+
issues.push(`profile_requirements_invalid:${profile.id ?? "unknown"}`);
|
|
165
|
+
}
|
|
166
|
+
if (profile.commands !== undefined) {
|
|
167
|
+
if (!Array.isArray(profile.commands)) {
|
|
168
|
+
issues.push(`profile_commands_invalid:${profile.id ?? "unknown"}`);
|
|
169
|
+
} else {
|
|
170
|
+
for (const entry of profile.commands) {
|
|
171
|
+
if (
|
|
172
|
+
!exactKeys(entry, ["argv", "cwd", "executable", "id"])
|
|
173
|
+
|| typeof entry.id !== "string"
|
|
174
|
+
|| entry.id.length === 0
|
|
175
|
+
|| typeof entry.cwd !== "string"
|
|
176
|
+
|| entry.cwd.length === 0
|
|
177
|
+
|| typeof entry.executable !== "string"
|
|
178
|
+
|| entry.executable.length === 0
|
|
179
|
+
|| !Array.isArray(entry.argv)
|
|
180
|
+
|| entry.argv.some((argument) => typeof argument !== "string" || argument.length === 0)
|
|
181
|
+
) {
|
|
182
|
+
issues.push(`profile_command_invalid:${profile.id ?? "unknown"}`);
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
safeIdentifier(entry.id, "profile command id");
|
|
187
|
+
} catch {
|
|
188
|
+
issues.push(`profile_command_id_invalid:${profile.id}:${entry.id}`);
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
safeRelativePath(entry.cwd, "profile command cwd", { allowDot: true });
|
|
192
|
+
} catch {
|
|
193
|
+
issues.push(`profile_command_cwd_invalid:${profile.id}:${entry.id}`);
|
|
194
|
+
}
|
|
195
|
+
if (commandIds.has(entry.id)) {
|
|
196
|
+
issues.push(`duplicate_profile_command_id:${entry.id}`);
|
|
197
|
+
} else {
|
|
198
|
+
commandIds.add(entry.id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (issues.length > 0) return [...new Set(issues)].sort();
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const changedPaths = config.profiles.flatMap((profile) =>
|
|
208
|
+
profile.paths.map(representativePath));
|
|
209
|
+
resolveProfileImpactDetailed({ changedPaths, profiles: config.profiles });
|
|
210
|
+
} catch (error) {
|
|
211
|
+
issues.push(error instanceof Error ? error.message : String(error));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
for (const profile of config.profiles) {
|
|
215
|
+
for (const requiredId of profile.requiresProfileIds ?? []) {
|
|
216
|
+
if (!ids.has(requiredId)) {
|
|
217
|
+
issues.push(`required_profile_missing:${profile.id}:${requiredId}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
const actionable = config.profiles.filter((profile) =>
|
|
222
|
+
(profile.role ?? "primary") === "primary"
|
|
223
|
+
&& Array.isArray(profile.commands)
|
|
224
|
+
&& profile.commands.length > 0);
|
|
225
|
+
if (actionable.length === 0) issues.push("actionable_primary_profile_missing");
|
|
226
|
+
const explicitPrimaryIds = actionable
|
|
227
|
+
.filter((profile) => profile.primary === true)
|
|
228
|
+
.map((profile) => profile.id)
|
|
229
|
+
.sort();
|
|
230
|
+
if (explicitPrimaryIds.length > 1) {
|
|
231
|
+
issues.push(`ambiguous_primary_profile:${explicitPrimaryIds.join(",")}`);
|
|
232
|
+
} else if (explicitPrimaryIds.length === 0 && actionable.length > 1) {
|
|
233
|
+
issues.push(`ambiguous_primary_profile:${actionable.map((profile) => profile.id).sort().join(",")}`);
|
|
234
|
+
}
|
|
235
|
+
return [...new Set(issues)].sort();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function validateProfiles({ workspaceRoot } = {}) {
|
|
239
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
240
|
+
const issues = [];
|
|
241
|
+
let profileIds = [];
|
|
242
|
+
try {
|
|
243
|
+
const config = readWorkspaceJsonBounded(
|
|
244
|
+
root,
|
|
245
|
+
path.join(".owlcoda", "runkit", "profiles.json"),
|
|
246
|
+
);
|
|
247
|
+
profileIds = Array.isArray(config.profiles)
|
|
248
|
+
? config.profiles
|
|
249
|
+
.map((profile) => profile?.id)
|
|
250
|
+
.filter((id) => typeof id === "string")
|
|
251
|
+
.sort()
|
|
252
|
+
: [];
|
|
253
|
+
issues.push(...validateProfileDocument(config));
|
|
254
|
+
if (issues.length === 0) {
|
|
255
|
+
const detected = detectProfiles({ workspaceRoot: root });
|
|
256
|
+
const detectedPaths = detected.candidates.flatMap((profile) =>
|
|
257
|
+
profile.paths.map(representativePath));
|
|
258
|
+
if (detectedPaths.length > 0) {
|
|
259
|
+
const impact = resolveProfileImpactDetailed({
|
|
260
|
+
changedPaths: detectedPaths,
|
|
261
|
+
profiles: config.profiles,
|
|
262
|
+
});
|
|
263
|
+
issues.push(...impact.uncoveredPaths.map((value) =>
|
|
264
|
+
`uncovered_detected_surface:${value}`));
|
|
265
|
+
issues.push(...impact.warnings
|
|
266
|
+
.filter((warning) => warning.startsWith("supporting_only_match:"))
|
|
267
|
+
.map((warning) => `detected_surface_${warning}`));
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
} catch (error) {
|
|
271
|
+
issues.push(error instanceof Error ? error.message : String(error));
|
|
272
|
+
}
|
|
273
|
+
const valid = issues.length === 0;
|
|
274
|
+
return {
|
|
275
|
+
schemaVersion: "OwlCodaRunKitProfilesValidationV1",
|
|
276
|
+
status: valid ? "valid" : "invalid",
|
|
277
|
+
valid,
|
|
278
|
+
exitCode: valid ? 0 : 2,
|
|
279
|
+
profileIds,
|
|
280
|
+
issues: [...new Set(issues)].sort(),
|
|
281
|
+
authorizationGranted: false,
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export function resolveProfilesImpact({ workspaceRoot, changedPaths } = {}) {
|
|
286
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
287
|
+
const config = readWorkspaceJsonBounded(
|
|
288
|
+
root,
|
|
289
|
+
path.join(".owlcoda", "runkit", "profiles.json"),
|
|
290
|
+
);
|
|
291
|
+
const documentIssues = validateProfileDocument(config);
|
|
292
|
+
if (documentIssues.length > 0) {
|
|
293
|
+
return {
|
|
294
|
+
schemaVersion: "OwlCodaRunKitProfileImpactV1",
|
|
295
|
+
status: "profile_impact_blocked",
|
|
296
|
+
valid: false,
|
|
297
|
+
exitCode: 2,
|
|
298
|
+
changedPaths: [...new Set(changedPaths)].sort(),
|
|
299
|
+
decision: "invalid_profiles",
|
|
300
|
+
primaryProfileId: null,
|
|
301
|
+
directProfileIds: [],
|
|
302
|
+
transitiveProfileIds: [],
|
|
303
|
+
supportingProfileIds: [],
|
|
304
|
+
selectedProfileIds: [],
|
|
305
|
+
uncoveredPaths: [],
|
|
306
|
+
warnings: [],
|
|
307
|
+
issues: documentIssues,
|
|
308
|
+
authorizationGranted: false,
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
const impact = resolveProfileImpactDetailed({
|
|
312
|
+
changedPaths,
|
|
313
|
+
profiles: config.profiles,
|
|
314
|
+
});
|
|
315
|
+
const issues = [
|
|
316
|
+
...impact.uncoveredPaths.map((value) => `uncovered_changed_path:${value}`),
|
|
317
|
+
...impact.warnings.filter((warning) =>
|
|
318
|
+
warning.startsWith("supporting_only_match:")
|
|
319
|
+
|| warning.startsWith("ambiguous_primary_profile:")),
|
|
320
|
+
];
|
|
321
|
+
const valid = impact.decision === "targeted_profiles" && issues.length === 0;
|
|
322
|
+
return {
|
|
323
|
+
schemaVersion: "OwlCodaRunKitProfileImpactV1",
|
|
324
|
+
status: valid ? "profile_impact_resolved" : "profile_impact_blocked",
|
|
325
|
+
valid,
|
|
326
|
+
exitCode: valid ? 0 : 2,
|
|
327
|
+
changedPaths: [...new Set(changedPaths)].sort(),
|
|
328
|
+
...impact,
|
|
329
|
+
issues: [...new Set(issues)].sort(),
|
|
330
|
+
authorizationGranted: false,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
@@ -32,12 +32,12 @@ function decision(policy, issueCodes) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
function
|
|
36
|
-
const tarballName =
|
|
37
|
-
return `${OFFICIAL_NPM_REGISTRY}/${
|
|
35
|
+
export function expectedRegistryTarballUrl(packageName, version) {
|
|
36
|
+
const tarballName = packageName.split("/").at(-1);
|
|
37
|
+
return `${OFFICIAL_NPM_REGISTRY}/${packageName}/-/${tarballName}-${version}.tgz`;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
function
|
|
40
|
+
export function isCanonicalSha512Integrity(value) {
|
|
41
41
|
if (typeof value !== "string") return false;
|
|
42
42
|
const match = /^sha512-([A-Za-z0-9+/]+={0,2})$/.exec(value);
|
|
43
43
|
if (!match) return false;
|
|
@@ -49,9 +49,12 @@ function validReleaseEvidence(policy, release) {
|
|
|
49
49
|
if (
|
|
50
50
|
policy.registry !== OFFICIAL_NPM_REGISTRY
|
|
51
51
|
|| Object.keys(release).sort().join("\n") !== RELEASE_EVIDENCE_FIELDS.join("\n")
|
|
52
|
-
|| !/^[0-9a-f]{40}
|
|
53
|
-
|| !
|
|
54
|
-
|| release.tarballUrl !==
|
|
52
|
+
|| !/^[0-9a-f]{40}$/i.test(release.shasum)
|
|
53
|
+
|| !isCanonicalSha512Integrity(release.integrity)
|
|
54
|
+
|| release.tarballUrl !== expectedRegistryTarballUrl(
|
|
55
|
+
policy.packageName,
|
|
56
|
+
policy.candidateVersion,
|
|
57
|
+
)
|
|
55
58
|
) {
|
|
56
59
|
return false;
|
|
57
60
|
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
lstatSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
OFFICIAL_NPM_REGISTRY,
|
|
11
|
+
evaluateRegistryAdoption,
|
|
12
|
+
} from "./registry-adoption-gate.mjs";
|
|
13
|
+
import {
|
|
14
|
+
readLocalInstallBinding,
|
|
15
|
+
readRegistryExact,
|
|
16
|
+
resolveBoundedWorkspaceRoot,
|
|
17
|
+
} from "./onboarding-doctor.mjs";
|
|
18
|
+
import { writeJsonExclusiveAtomically } from "./provenance-common.mjs";
|
|
19
|
+
|
|
20
|
+
const PACKAGE_NAME = "owlrunkit";
|
|
21
|
+
const CANDIDATE_VERSION = "0.14.0";
|
|
22
|
+
const EXACT_SPEC = `${PACKAGE_NAME}@${CANDIDATE_VERSION}`;
|
|
23
|
+
|
|
24
|
+
const POLICY = Object.freeze({
|
|
25
|
+
schemaVersion: "OwlCodaRunKitRegistryReleasePolicyV1",
|
|
26
|
+
status: "registry_verified",
|
|
27
|
+
packageName: PACKAGE_NAME,
|
|
28
|
+
candidateVersion: CANDIDATE_VERSION,
|
|
29
|
+
registry: OFFICIAL_NPM_REGISTRY,
|
|
30
|
+
registryRequiredBeforeAdoption: true,
|
|
31
|
+
exactVersionRequired: true,
|
|
32
|
+
requiredRegistryBindings: [
|
|
33
|
+
"packageName",
|
|
34
|
+
"version",
|
|
35
|
+
"shasum",
|
|
36
|
+
"integrity",
|
|
37
|
+
"tarballUrl",
|
|
38
|
+
],
|
|
39
|
+
forbiddenAdoptionSources: [
|
|
40
|
+
"directory",
|
|
41
|
+
"file",
|
|
42
|
+
"git",
|
|
43
|
+
"local_tarball",
|
|
44
|
+
"symlink",
|
|
45
|
+
"workspace",
|
|
46
|
+
],
|
|
47
|
+
currentPublishedRelease: null,
|
|
48
|
+
registryRelease: null,
|
|
49
|
+
registryEvidenceMustBeExternal: true,
|
|
50
|
+
registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
|
|
51
|
+
embeddedAttestComponentVersion: "0.2.0",
|
|
52
|
+
standaloneAttestPublicationRequired: false,
|
|
53
|
+
signatureRequired: false,
|
|
54
|
+
githubActionRequired: false,
|
|
55
|
+
foreignProjectWriteAuthorized: false,
|
|
56
|
+
authorizationGranted: false,
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
function ineligible(issueCodes) {
|
|
60
|
+
return {
|
|
61
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
|
|
62
|
+
status: "ineligible",
|
|
63
|
+
decision: "INELIGIBLE",
|
|
64
|
+
issueCodes,
|
|
65
|
+
packageName: PACKAGE_NAME,
|
|
66
|
+
version: CANDIDATE_VERSION,
|
|
67
|
+
authorizationGranted: false,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function assertRealDirectory(directory, label) {
|
|
72
|
+
const stat = lstatSync(directory);
|
|
73
|
+
if (stat.isSymbolicLink() || !stat.isDirectory() || realpathSync(directory) !== path.resolve(directory)) {
|
|
74
|
+
throw new Error(`${label} must be a real directory without symlink ancestors.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function adoptionRoot(workspaceRoot) {
|
|
79
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
80
|
+
const owlcodaRoot = path.join(root, ".owlcoda");
|
|
81
|
+
const runtimeRoot = path.join(owlcodaRoot, "runkit");
|
|
82
|
+
assertRealDirectory(owlcodaRoot, ".owlcoda");
|
|
83
|
+
assertRealDirectory(runtimeRoot, "RunKit runtime");
|
|
84
|
+
const target = path.join(runtimeRoot, "adoption");
|
|
85
|
+
if (!existsSync(target)) mkdirSync(target);
|
|
86
|
+
assertRealDirectory(target, "RunKit adoption directory");
|
|
87
|
+
return target;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function runRegistryAdoption({
|
|
91
|
+
workspaceRoot,
|
|
92
|
+
exactSpec,
|
|
93
|
+
registryClient,
|
|
94
|
+
timeoutMs = 1_500,
|
|
95
|
+
} = {}) {
|
|
96
|
+
if (exactSpec !== EXACT_SPEC) return ineligible(["exact_registry_spec_required"]);
|
|
97
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
98
|
+
const local = readLocalInstallBinding({ workspaceRoot: root });
|
|
99
|
+
if (local.status !== "bound") {
|
|
100
|
+
return ineligible(local.issueCodes ?? ["registry_install_binding_mismatch"]);
|
|
101
|
+
}
|
|
102
|
+
const observedRelease = registryClient
|
|
103
|
+
? await registryClient.readExact({
|
|
104
|
+
registry: OFFICIAL_NPM_REGISTRY,
|
|
105
|
+
packageName: PACKAGE_NAME,
|
|
106
|
+
version: CANDIDATE_VERSION,
|
|
107
|
+
})
|
|
108
|
+
: await readRegistryExact({
|
|
109
|
+
registryUrl: OFFICIAL_NPM_REGISTRY,
|
|
110
|
+
packageName: PACKAGE_NAME,
|
|
111
|
+
version: CANDIDATE_VERSION,
|
|
112
|
+
timeoutMs,
|
|
113
|
+
});
|
|
114
|
+
const release = observedRelease.status === "registry_verified"
|
|
115
|
+
? {
|
|
116
|
+
schemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
|
|
117
|
+
...observedRelease,
|
|
118
|
+
}
|
|
119
|
+
: observedRelease;
|
|
120
|
+
if (release.status !== "registry_verified") {
|
|
121
|
+
return ineligible(release.issueCodes ?? ["registry_release_not_verified"]);
|
|
122
|
+
}
|
|
123
|
+
const install = {
|
|
124
|
+
kind: "registry",
|
|
125
|
+
requestedSpec: EXACT_SPEC,
|
|
126
|
+
packageName: local.packageName,
|
|
127
|
+
version: local.version,
|
|
128
|
+
shasum: release.shasum,
|
|
129
|
+
integrity: local.integrity,
|
|
130
|
+
resolved: local.resolved,
|
|
131
|
+
};
|
|
132
|
+
const decision = evaluateRegistryAdoption({
|
|
133
|
+
policy: POLICY,
|
|
134
|
+
releaseEvidence: release,
|
|
135
|
+
install,
|
|
136
|
+
});
|
|
137
|
+
if (decision.decision !== "ELIGIBLE") return ineligible(decision.issueCodes);
|
|
138
|
+
|
|
139
|
+
const relativeEvidencePath = `.owlcoda/runkit/adoption/${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`;
|
|
140
|
+
const evidencePath = path.join(adoptionRoot(root), `${PACKAGE_NAME}-${CANDIDATE_VERSION}.json`);
|
|
141
|
+
const evidence = {
|
|
142
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionEvidenceV1",
|
|
143
|
+
status: "adopted",
|
|
144
|
+
decision: "ELIGIBLE",
|
|
145
|
+
packageName: PACKAGE_NAME,
|
|
146
|
+
version: CANDIDATE_VERSION,
|
|
147
|
+
exactSpec: EXACT_SPEC,
|
|
148
|
+
registryRelease: {
|
|
149
|
+
registry: release.registry,
|
|
150
|
+
packageName: release.packageName,
|
|
151
|
+
version: release.version,
|
|
152
|
+
shasum: release.shasum,
|
|
153
|
+
integrity: release.integrity,
|
|
154
|
+
tarballUrl: release.tarballUrl,
|
|
155
|
+
},
|
|
156
|
+
installedBinding: {
|
|
157
|
+
requestedSpec: local.requestedSpec,
|
|
158
|
+
packageName: local.packageName,
|
|
159
|
+
version: local.version,
|
|
160
|
+
resolved: local.resolved,
|
|
161
|
+
integrity: local.integrity,
|
|
162
|
+
},
|
|
163
|
+
authority: {
|
|
164
|
+
git: false,
|
|
165
|
+
release: false,
|
|
166
|
+
foreignProjectWrite: false,
|
|
167
|
+
},
|
|
168
|
+
authorizationGranted: false,
|
|
169
|
+
};
|
|
170
|
+
writeJsonExclusiveAtomically(evidencePath, evidence);
|
|
171
|
+
return {
|
|
172
|
+
schemaVersion: "OwlCodaRunKitRegistryAdoptionResultV1",
|
|
173
|
+
status: "adopted",
|
|
174
|
+
decision: "ELIGIBLE",
|
|
175
|
+
evidencePath: relativeEvidencePath,
|
|
176
|
+
issueCodes: [],
|
|
177
|
+
packageName: PACKAGE_NAME,
|
|
178
|
+
version: CANDIDATE_VERSION,
|
|
179
|
+
authorizationGranted: false,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function formatAdoptionHuman(result) {
|
|
184
|
+
if (result.status === "adopted") {
|
|
185
|
+
return [
|
|
186
|
+
`Adopted ${result.packageName}@${result.version}`,
|
|
187
|
+
`Evidence: ${result.evidencePath}`,
|
|
188
|
+
"Git and release authority remain false.",
|
|
189
|
+
"",
|
|
190
|
+
].join("\n");
|
|
191
|
+
}
|
|
192
|
+
return [
|
|
193
|
+
`Adoption blocked: ${result.issueCodes.join(", ")}`,
|
|
194
|
+
"No adoption evidence was written.",
|
|
195
|
+
"",
|
|
196
|
+
].join("\n");
|
|
197
|
+
}
|
|
@@ -94,6 +94,11 @@ function assertRegularArtifact(filePath, label) {
|
|
|
94
94
|
if (!stat.isFile()) throw new Error(`${label} must be a regular file.`);
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function readRegularJsonArtifact(filePath, label) {
|
|
98
|
+
assertRegularArtifact(filePath, label);
|
|
99
|
+
return readJson(filePath);
|
|
100
|
+
}
|
|
101
|
+
|
|
97
102
|
function acquireResumeLock(workspaceRoot) {
|
|
98
103
|
const runtimeRoot = path.join(workspaceRoot, RUNTIME_ROOT);
|
|
99
104
|
assertSafeDirectory(workspaceRoot, runtimeRoot, "RunKit runtime root");
|
|
@@ -299,7 +304,7 @@ function validatedParentCloseout(workspaceRoot, sourceRoot, sourceRunId) {
|
|
|
299
304
|
throw new Error("Parent closeout payload is invalid.");
|
|
300
305
|
}
|
|
301
306
|
if (payload.decision === "accepted") validateAcceptedParentEvidence(sourceRoot, sourceRunId, payload.verification);
|
|
302
|
-
const pin =
|
|
307
|
+
const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Parent engine pin");
|
|
303
308
|
const pinGate = validateExecutionPin({ expected: pin, actual: closeout.artifact.core });
|
|
304
309
|
if (pinGate.status !== "valid") throw new Error(`Parent closeout does not match its engine pin: ${pinGate.issues.join("; ")}`);
|
|
305
310
|
return {
|
|
@@ -392,7 +397,7 @@ function appendSameExecution({ workspaceRoot, sourceRunId, sourceRoot, request,
|
|
|
392
397
|
throw new Error("Unclosed execution resume requires continuationRunId=null.");
|
|
393
398
|
}
|
|
394
399
|
assertSoleActiveRun(workspaceRoot, sourceRunId);
|
|
395
|
-
const pin =
|
|
400
|
+
const pin = readRegularJsonArtifact(path.join(sourceRoot, "engine-pin.json"), "Resume engine pin");
|
|
396
401
|
const pinGate = validateExecutionPin({ expected: pin, actual: currentCoreIdentity() });
|
|
397
402
|
if (pinGate.status !== "valid") throw new Error(`Resume engine pin is stale: ${pinGate.issues.join("; ")}`);
|
|
398
403
|
assertNoActiveWriterLease(sourceRoot);
|
|
@@ -507,6 +512,7 @@ function createContinuation({ workspaceRoot, sourceRunId, sourceRoot, request, g
|
|
|
507
512
|
runId: targetRunId,
|
|
508
513
|
state: "planned",
|
|
509
514
|
enginePin,
|
|
515
|
+
goalContractSha256: sha256(goal.bytes),
|
|
510
516
|
continuation: {
|
|
511
517
|
parentRunId: sourceRunId,
|
|
512
518
|
resumeId: request.resumeId,
|