owlrunkit 0.20.0 → 0.22.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 +88 -0
- package/README.md +102 -10
- package/docs/architecture/OWLCODA_RUNKIT_0201_RELEASE_TRUTH_AND_PROFILE_NORMALIZATION_PATCH_DECISION_20260813.md +95 -0
- package/docs/architecture/OWLCODA_RUNKIT_021_TEAM_DELIVERY_PHASE2_PRODUCT_DECISION_20260813.md +249 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +117 -12
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/README.md +7 -1
- package/packages/attest/consumer-surface-v3.json +275 -0
- package/packages/attest/distribution-manifest-v1.json +24 -0
- package/packages/attest/schemas/canonical-candidate-identity-v1.schema.json +112 -0
- package/packages/attest/schemas/foreign-workspace-filesystem-identity-v1.schema.json +32 -0
- package/packages/attest/schemas/quick-verification-receipt-v4.schema.json +226 -0
- package/packages/attest/src/candidate-identity.mjs +454 -0
- package/packages/attest/src/index.mjs +11 -0
- package/packages/attest/src/quick-receipt-contract.mjs +126 -5
- package/packages/attest/src/quick.mjs +53 -8
- package/{registry-release-policy-v1.json → registry-release-policy-v2.json} +8 -9
- package/sbom.spdx.json +4 -4
- package/scripts/runkit-contract/core-contract.mjs +19 -1
- package/scripts/runkit-contract/fleet-skill-plan.mjs +223 -0
- package/scripts/runkit-contract/formal-workflow.mjs +3 -1
- package/scripts/runkit-contract/hook-recovery.mjs +496 -0
- package/scripts/runkit-contract/inspect-presentation.mjs +187 -0
- package/scripts/runkit-contract/install-codex-skill.mjs +1 -0
- package/scripts/runkit-contract/onboarding-bootstrap.mjs +15 -0
- package/scripts/runkit-contract/onboarding-doctor.mjs +378 -9
- package/scripts/runkit-contract/profile-onboarding.mjs +6 -0
- package/scripts/runkit-contract/project-cli-resolver.mjs +17 -1
- package/scripts/runkit-contract/quick-metrics.mjs +9 -5
- package/scripts/runkit-contract/quick-verify.mjs +108 -24
- package/scripts/runkit-contract/registry-adoption-gate.mjs +12 -3
- package/scripts/runkit-contract/registry-adoption.mjs +10 -5
- package/scripts/runkit-contract/runkit-bootstrap.mjs +1 -0
- package/scripts/runkit-contract/runkit-cli.mjs +332 -30
- package/scripts/runkit-contract/schemas/delivery-lifecycle-v1.schema.json +91 -0
- package/scripts/runkit-contract/schemas/external-gate-v1.schema.json +68 -0
- package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +140 -0
- package/scripts/runkit-contract/schemas/hook-recovery-v1.schema.json +592 -0
- package/scripts/runkit-contract/schemas/inspect-compact-v1.schema.json +93 -0
- package/scripts/runkit-contract/schemas/inspect-compact-v2.schema.json +122 -0
- package/scripts/runkit-contract/schemas/team-delivery-status-v2.schema.json +219 -0
- package/scripts/runkit-contract/schemas/team-delivery-status-v3.schema.json +113 -0
- package/scripts/runkit-contract/schemas/team-project-event-v6.schema.json +82 -0
- package/scripts/runkit-contract/schemas/team-project-event-v7.schema.json +80 -0
- package/scripts/runkit-contract/schemas/team-project-status-v4.schema.json +208 -0
- package/scripts/runkit-contract/schemas/team-recommendation-v2.schema.json +120 -0
- package/scripts/runkit-contract/sync-release-identity.mjs +87 -0
- package/scripts/runkit-contract/team-delivery.mjs +203 -3
- package/scripts/runkit-contract/team-project.mjs +1032 -11
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import {
|
|
3
|
+
lstatSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
RUNTIME_ROOT,
|
|
11
|
+
currentCoreIdentity,
|
|
12
|
+
validateExecutionPin,
|
|
13
|
+
validateProjectConfigV2,
|
|
14
|
+
} from "./core-contract.mjs";
|
|
15
|
+
import { inspectProjectControlState } from "./project-control-state.mjs";
|
|
16
|
+
import { readTeamProjectHookSourceV1 } from "./team-project.mjs";
|
|
17
|
+
|
|
18
|
+
const MAX_OUTPUT_BYTES = 2_000;
|
|
19
|
+
const MAX_CONTROL_BYTES = 1_048_576;
|
|
20
|
+
const MAX_PROJECT_ID_BYTES = 96;
|
|
21
|
+
const MAX_ACTOR_ID_BYTES = 96;
|
|
22
|
+
const MAX_GAP_ID_BYTES = 96;
|
|
23
|
+
const MAX_OBJECTIVE_PREVIEW_BYTES = 180;
|
|
24
|
+
const MAX_HEADLINE_BYTES = 120;
|
|
25
|
+
const MAX_GAP_REASON_BYTES = 120;
|
|
26
|
+
const MAX_NEXT_ACTION_BYTES = 120;
|
|
27
|
+
const MAX_WARNING_CODES = 4;
|
|
28
|
+
const MAX_WARNING_CODE_BYTES = 64;
|
|
29
|
+
|
|
30
|
+
const AUTHORITY_BOUNDARY = Object.freeze({
|
|
31
|
+
agentDispatch: false,
|
|
32
|
+
git: false,
|
|
33
|
+
release: false,
|
|
34
|
+
deployment: false,
|
|
35
|
+
production: false,
|
|
36
|
+
business: false,
|
|
37
|
+
automation: false,
|
|
38
|
+
money: false,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
function sha256Ref(value) {
|
|
42
|
+
return "sha256:" + createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function compareCodeUnits(left, right) {
|
|
46
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function pathEntryExists(filePath) {
|
|
50
|
+
try {
|
|
51
|
+
lstatSync(filePath);
|
|
52
|
+
return true;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (error?.code === "ENOENT") return false;
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function withinRoot(root, candidate) {
|
|
60
|
+
const relative = path.relative(root, candidate);
|
|
61
|
+
return relative === ""
|
|
62
|
+
|| (
|
|
63
|
+
relative !== ".."
|
|
64
|
+
&& !relative.startsWith(".." + path.sep)
|
|
65
|
+
&& !path.isAbsolute(relative)
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function canonicalWorkspaceRoot(workspaceRoot) {
|
|
70
|
+
if (typeof workspaceRoot !== "string" || workspaceRoot.length === 0) {
|
|
71
|
+
throw new Error("workspace_required");
|
|
72
|
+
}
|
|
73
|
+
const requested = path.resolve(workspaceRoot);
|
|
74
|
+
const stat = lstatSync(requested);
|
|
75
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
76
|
+
throw new Error("workspace_redirected");
|
|
77
|
+
}
|
|
78
|
+
const real = realpathSync(requested);
|
|
79
|
+
return real;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function readRegularJsonBounded(root, filePath, label) {
|
|
83
|
+
const stat = lstatSync(filePath);
|
|
84
|
+
if (
|
|
85
|
+
stat.isSymbolicLink()
|
|
86
|
+
|| !stat.isFile()
|
|
87
|
+
|| stat.size > MAX_CONTROL_BYTES
|
|
88
|
+
) {
|
|
89
|
+
throw new Error(label + "_invalid");
|
|
90
|
+
}
|
|
91
|
+
const real = realpathSync(filePath);
|
|
92
|
+
if (real !== path.resolve(filePath) || !withinRoot(root, real)) {
|
|
93
|
+
throw new Error(label + "_redirected");
|
|
94
|
+
}
|
|
95
|
+
return JSON.parse(readFileSync(real, "utf8"));
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function unsafeCodePoint(codePoint) {
|
|
99
|
+
return codePoint <= 0x1f
|
|
100
|
+
|| (codePoint >= 0x7f && codePoint <= 0x9f)
|
|
101
|
+
|| (codePoint >= 0xd800 && codePoint <= 0xdfff)
|
|
102
|
+
|| codePoint === 0x2028
|
|
103
|
+
|| codePoint === 0x2029;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function escapedCodePoint(character) {
|
|
107
|
+
const codePoint = character.codePointAt(0);
|
|
108
|
+
if (!unsafeCodePoint(codePoint)) return character;
|
|
109
|
+
if (codePoint <= 0xffff) {
|
|
110
|
+
return "\\u" + codePoint.toString(16).padStart(4, "0");
|
|
111
|
+
}
|
|
112
|
+
return "\\u{" + codePoint.toString(16) + "}";
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function boundedUntrustedText(value, maxBytes) {
|
|
116
|
+
if (value === null || value === undefined) {
|
|
117
|
+
return { value: null, truncated: false };
|
|
118
|
+
}
|
|
119
|
+
let rendered = "";
|
|
120
|
+
let renderedBytes = 0;
|
|
121
|
+
let truncated = false;
|
|
122
|
+
for (const character of String(value)) {
|
|
123
|
+
const token = escapedCodePoint(character);
|
|
124
|
+
const tokenBytes = Buffer.byteLength(token);
|
|
125
|
+
if (renderedBytes + tokenBytes + 3 > maxBytes) {
|
|
126
|
+
truncated = true;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
rendered += token;
|
|
130
|
+
renderedBytes += tokenBytes;
|
|
131
|
+
}
|
|
132
|
+
if (truncated) rendered += "…";
|
|
133
|
+
return { value: rendered, truncated };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function exactBoundedIdentity(value, maxBytes) {
|
|
137
|
+
if (value === null || value === undefined) return null;
|
|
138
|
+
const text = String(value);
|
|
139
|
+
return Buffer.byteLength(text) <= maxBytes ? text : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function safeWarningCode(value) {
|
|
143
|
+
if (
|
|
144
|
+
typeof value !== "string"
|
|
145
|
+
|| !/^[a-z0-9][a-z0-9._:-]*$/u.test(value)
|
|
146
|
+
|| Buffer.byteLength(value) > MAX_WARNING_CODE_BYTES
|
|
147
|
+
) {
|
|
148
|
+
return "unclassified_warning";
|
|
149
|
+
}
|
|
150
|
+
return value;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function warningSummary(entries) {
|
|
154
|
+
const groups = new Map();
|
|
155
|
+
let count = 0;
|
|
156
|
+
let blockingCount = 0;
|
|
157
|
+
for (const entry of entries) {
|
|
158
|
+
const increment = Number.isSafeInteger(entry.count) && entry.count > 0
|
|
159
|
+
? entry.count
|
|
160
|
+
: 1;
|
|
161
|
+
count += increment;
|
|
162
|
+
if (entry.blocking === true) blockingCount += increment;
|
|
163
|
+
const code = safeWarningCode(entry.code);
|
|
164
|
+
const current = groups.get(code) ?? { code, count: 0, blocking: false };
|
|
165
|
+
current.count += increment;
|
|
166
|
+
current.blocking ||= entry.blocking === true;
|
|
167
|
+
groups.set(code, current);
|
|
168
|
+
}
|
|
169
|
+
const ordered = [...groups.values()].sort((left, right) => (
|
|
170
|
+
compareCodeUnits(left.code, right.code)
|
|
171
|
+
));
|
|
172
|
+
return {
|
|
173
|
+
count,
|
|
174
|
+
blockingCount,
|
|
175
|
+
codes: ordered.slice(0, MAX_WARNING_CODES).map(group => group.code),
|
|
176
|
+
truncated: ordered.length > MAX_WARNING_CODES,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function executionProjection(control) {
|
|
181
|
+
return {
|
|
182
|
+
state: control?.recovery?.state ?? "invalid_control_truth",
|
|
183
|
+
openCount: control?.activeRunIds?.length ?? 0,
|
|
184
|
+
activeLeaseCount: control?.activeLeaseIds?.length ?? 0,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function noProjectGap() {
|
|
189
|
+
return {
|
|
190
|
+
kind: "no_project_driver",
|
|
191
|
+
id: null,
|
|
192
|
+
reason: "No Project Driver is initialized.",
|
|
193
|
+
truncated: false,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function invalidGap() {
|
|
198
|
+
return {
|
|
199
|
+
kind: "invalid_control_truth",
|
|
200
|
+
id: null,
|
|
201
|
+
reason: "RunKit control truth must be repaired before recovery.",
|
|
202
|
+
truncated: false,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function projectGap(status) {
|
|
207
|
+
const kind = safeWarningCode(status.dominantGap?.kind ?? "none");
|
|
208
|
+
const id = exactBoundedIdentity(status.dominantGap?.id, MAX_GAP_ID_BYTES);
|
|
209
|
+
const reason = boundedUntrustedText(
|
|
210
|
+
status.dominantGap?.reason,
|
|
211
|
+
MAX_GAP_REASON_BYTES,
|
|
212
|
+
);
|
|
213
|
+
return {
|
|
214
|
+
kind,
|
|
215
|
+
id,
|
|
216
|
+
reason: reason.value,
|
|
217
|
+
truncated: reason.truncated
|
|
218
|
+
|| (status.dominantGap?.id !== null
|
|
219
|
+
&& status.dominantGap?.id !== undefined
|
|
220
|
+
&& id === null),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function baseProjection({
|
|
225
|
+
status,
|
|
226
|
+
exitCode,
|
|
227
|
+
execution,
|
|
228
|
+
warnings,
|
|
229
|
+
project = null,
|
|
230
|
+
nextAction = null,
|
|
231
|
+
}) {
|
|
232
|
+
const objective = project
|
|
233
|
+
? boundedUntrustedText(project.status.objective, MAX_OBJECTIVE_PREVIEW_BYTES)
|
|
234
|
+
: { value: null, truncated: false };
|
|
235
|
+
const headline = project
|
|
236
|
+
? boundedUntrustedText(project.status.headline, MAX_HEADLINE_BYTES)
|
|
237
|
+
: { value: null, truncated: false };
|
|
238
|
+
const next = boundedUntrustedText(nextAction, MAX_NEXT_ACTION_BYTES);
|
|
239
|
+
const projectedGap = project ? projectGap(project.status) : noProjectGap();
|
|
240
|
+
return {
|
|
241
|
+
schemaVersion: "OwlCodaRunKitHookRecoveryV1",
|
|
242
|
+
status,
|
|
243
|
+
exitCode,
|
|
244
|
+
projectId: project
|
|
245
|
+
? exactBoundedIdentity(project.status.projectId, MAX_PROJECT_ID_BYTES)
|
|
246
|
+
: null,
|
|
247
|
+
projectTruthHash: project?.status.projectTruthHash ?? null,
|
|
248
|
+
objectiveDigest: project ? sha256Ref(project.status.objective) : null,
|
|
249
|
+
objectivePreview: objective.value,
|
|
250
|
+
objectiveTruncated: objective.truncated,
|
|
251
|
+
projectTextTrust: "untrusted_data",
|
|
252
|
+
overall: project?.status.overall ?? null,
|
|
253
|
+
progress: {
|
|
254
|
+
completedWorkItems: project?.status.counts?.completed ?? null,
|
|
255
|
+
totalWorkItems: project?.status.counts?.total ?? null,
|
|
256
|
+
},
|
|
257
|
+
headline: headline.value,
|
|
258
|
+
dominantGap: status === "invalid_control_truth"
|
|
259
|
+
? invalidGap()
|
|
260
|
+
: projectedGap,
|
|
261
|
+
nextActorId: project
|
|
262
|
+
? exactBoundedIdentity(project.status.nextActorId, MAX_ACTOR_ID_BYTES)
|
|
263
|
+
: null,
|
|
264
|
+
nextAction: next.value,
|
|
265
|
+
deliveryDisposition: project?.status.deliveryDisposition ?? null,
|
|
266
|
+
execution,
|
|
267
|
+
warnings,
|
|
268
|
+
latestProjectFactAt: project?.latestProjectFactAt ?? null,
|
|
269
|
+
textTruncated: objective.truncated
|
|
270
|
+
|| headline.truncated
|
|
271
|
+
|| projectedGap.truncated
|
|
272
|
+
|| next.truncated,
|
|
273
|
+
readOnly: true,
|
|
274
|
+
writesPerformed: false,
|
|
275
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
276
|
+
authorizationGranted: false,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function outputBytes(value) {
|
|
281
|
+
return Buffer.byteLength(JSON.stringify(value) + "\n");
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function enforceOutputBound(value) {
|
|
285
|
+
if (outputBytes(value) <= MAX_OUTPUT_BYTES) return value;
|
|
286
|
+
const fallback = {
|
|
287
|
+
...value,
|
|
288
|
+
status: "invalid_control_truth",
|
|
289
|
+
exitCode: 2,
|
|
290
|
+
projectId: null,
|
|
291
|
+
objectivePreview: null,
|
|
292
|
+
objectiveTruncated: true,
|
|
293
|
+
headline: "Hook recovery projection exceeded its byte bound.",
|
|
294
|
+
dominantGap: invalidGap(),
|
|
295
|
+
nextActorId: null,
|
|
296
|
+
nextAction: "inspect_project_truth_manually",
|
|
297
|
+
warnings: warningSummary([{
|
|
298
|
+
code: "projection_byte_limit_exceeded",
|
|
299
|
+
blocking: true,
|
|
300
|
+
}]),
|
|
301
|
+
textTruncated: true,
|
|
302
|
+
};
|
|
303
|
+
if (outputBytes(fallback) > MAX_OUTPUT_BYTES) {
|
|
304
|
+
throw new Error("hook_recovery_byte_bound_invariant_failed");
|
|
305
|
+
}
|
|
306
|
+
return fallback;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function invalidProjection({
|
|
310
|
+
execution = executionProjection(null),
|
|
311
|
+
warningCodes,
|
|
312
|
+
nextAction,
|
|
313
|
+
project = null,
|
|
314
|
+
}) {
|
|
315
|
+
return enforceOutputBound(baseProjection({
|
|
316
|
+
status: "invalid_control_truth",
|
|
317
|
+
exitCode: 2,
|
|
318
|
+
execution,
|
|
319
|
+
warnings: warningSummary(warningCodes.map(code => ({
|
|
320
|
+
code,
|
|
321
|
+
blocking: true,
|
|
322
|
+
}))),
|
|
323
|
+
project,
|
|
324
|
+
nextAction,
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function inspectCoreBinding(root) {
|
|
329
|
+
try {
|
|
330
|
+
const configPath = path.join(root, RUNTIME_ROOT, "config.json");
|
|
331
|
+
if (!pathEntryExists(configPath)) {
|
|
332
|
+
return { valid: false, code: "core_config_missing" };
|
|
333
|
+
}
|
|
334
|
+
const config = readRegularJsonBounded(root, configPath, "core_config");
|
|
335
|
+
const configGate = validateProjectConfigV2(config);
|
|
336
|
+
if (!configGate.valid) {
|
|
337
|
+
return { valid: false, code: "core_config_invalid" };
|
|
338
|
+
}
|
|
339
|
+
const pinGate = validateExecutionPin({
|
|
340
|
+
expected: config.core,
|
|
341
|
+
actual: currentCoreIdentity(),
|
|
342
|
+
});
|
|
343
|
+
return pinGate.status === "valid"
|
|
344
|
+
? { valid: true }
|
|
345
|
+
: { valid: false, code: "core_version_mismatch" };
|
|
346
|
+
} catch {
|
|
347
|
+
return { valid: false, code: "core_config_invalid" };
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function readProjectSource(root) {
|
|
352
|
+
try {
|
|
353
|
+
const projectRoot = path.join(root, RUNTIME_ROOT, "project");
|
|
354
|
+
if (!pathEntryExists(projectRoot)) {
|
|
355
|
+
return { state: "absent", project: null };
|
|
356
|
+
}
|
|
357
|
+
const projectStat = lstatSync(projectRoot);
|
|
358
|
+
const projectReal = realpathSync(projectRoot);
|
|
359
|
+
if (
|
|
360
|
+
projectStat.isSymbolicLink()
|
|
361
|
+
|| !projectStat.isDirectory()
|
|
362
|
+
|| projectReal !== path.resolve(projectRoot)
|
|
363
|
+
|| !withinRoot(root, projectReal)
|
|
364
|
+
) {
|
|
365
|
+
return { state: "invalid", project: null };
|
|
366
|
+
}
|
|
367
|
+
const definitionPath = path.join(projectRoot, "definition.json");
|
|
368
|
+
if (!pathEntryExists(definitionPath)) {
|
|
369
|
+
return { state: "invalid", project: null };
|
|
370
|
+
}
|
|
371
|
+
return {
|
|
372
|
+
state: "valid",
|
|
373
|
+
project: readTeamProjectHookSourceV1({ workspaceRoot: root }),
|
|
374
|
+
};
|
|
375
|
+
} catch {
|
|
376
|
+
return { state: "invalid", project: null };
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
export function buildHookRecoveryV1({ workspaceRoot } = {}) {
|
|
381
|
+
let root;
|
|
382
|
+
try {
|
|
383
|
+
root = canonicalWorkspaceRoot(workspaceRoot);
|
|
384
|
+
} catch {
|
|
385
|
+
return invalidProjection({
|
|
386
|
+
warningCodes: ["workspace_invalid"],
|
|
387
|
+
nextAction: "repair_workspace_binding",
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
const core = inspectCoreBinding(root);
|
|
392
|
+
let control;
|
|
393
|
+
try {
|
|
394
|
+
control = inspectProjectControlState({
|
|
395
|
+
workspaceRoot: root,
|
|
396
|
+
currentCore: currentCoreIdentity(),
|
|
397
|
+
});
|
|
398
|
+
} catch {
|
|
399
|
+
control = null;
|
|
400
|
+
}
|
|
401
|
+
const execution = executionProjection(control);
|
|
402
|
+
const projectSource = readProjectSource(root);
|
|
403
|
+
const project = projectSource.project;
|
|
404
|
+
|
|
405
|
+
if (!core.valid) {
|
|
406
|
+
return invalidProjection({
|
|
407
|
+
execution,
|
|
408
|
+
warningCodes: [core.code],
|
|
409
|
+
nextAction: "repair_core_binding",
|
|
410
|
+
project,
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
if (!control || control.status === "invalid") {
|
|
414
|
+
return invalidProjection({
|
|
415
|
+
execution,
|
|
416
|
+
warningCodes: ["control_truth_invalid"],
|
|
417
|
+
nextAction: "repair_execution_artifacts",
|
|
418
|
+
project,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
if (control.activeRunIds.length > 1) {
|
|
422
|
+
return invalidProjection({
|
|
423
|
+
execution,
|
|
424
|
+
warningCodes: ["multiple_active_executions"],
|
|
425
|
+
nextAction: "select_active_execution",
|
|
426
|
+
project,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
if (control.closedHistory?.blocking === true) {
|
|
430
|
+
return invalidProjection({
|
|
431
|
+
execution,
|
|
432
|
+
warningCodes: ["ambiguous_closed_history"],
|
|
433
|
+
nextAction: "repair_execution_lineage",
|
|
434
|
+
project,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
if (projectSource.state === "invalid") {
|
|
438
|
+
return invalidProjection({
|
|
439
|
+
execution,
|
|
440
|
+
warningCodes: ["project_truth_invalid"],
|
|
441
|
+
nextAction: "repair_project_truth",
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (projectSource.state === "absent") {
|
|
445
|
+
return enforceOutputBound(baseProjection({
|
|
446
|
+
status: "no_project_driver",
|
|
447
|
+
exitCode: 0,
|
|
448
|
+
execution,
|
|
449
|
+
warnings: warningSummary([]),
|
|
450
|
+
nextAction: control.activeRunIds.length === 1
|
|
451
|
+
? "inspect_active_execution"
|
|
452
|
+
: null,
|
|
453
|
+
}));
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const identityIssues = [];
|
|
457
|
+
if (exactBoundedIdentity(project.status.projectId, MAX_PROJECT_ID_BYTES) === null) {
|
|
458
|
+
identityIssues.push("project_id_exceeds_hook_bound");
|
|
459
|
+
}
|
|
460
|
+
if (
|
|
461
|
+
project.status.nextActorId !== null
|
|
462
|
+
&& exactBoundedIdentity(project.status.nextActorId, MAX_ACTOR_ID_BYTES) === null
|
|
463
|
+
) {
|
|
464
|
+
identityIssues.push("next_actor_id_exceeds_hook_bound");
|
|
465
|
+
}
|
|
466
|
+
if (
|
|
467
|
+
project.status.dominantGap?.id !== null
|
|
468
|
+
&& exactBoundedIdentity(project.status.dominantGap.id, MAX_GAP_ID_BYTES) === null
|
|
469
|
+
) {
|
|
470
|
+
identityIssues.push("dominant_gap_id_exceeds_hook_bound");
|
|
471
|
+
}
|
|
472
|
+
if (identityIssues.length > 0) {
|
|
473
|
+
return invalidProjection({
|
|
474
|
+
execution,
|
|
475
|
+
warningCodes: identityIssues,
|
|
476
|
+
nextAction: "inspect_project_truth_manually",
|
|
477
|
+
project,
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const warningEntries = (project.status.warnings ?? []).map(warning => ({
|
|
482
|
+
code: warning.code,
|
|
483
|
+
count: 1,
|
|
484
|
+
blocking: warning.blocking === true,
|
|
485
|
+
}));
|
|
486
|
+
return enforceOutputBound(baseProjection({
|
|
487
|
+
status: "hook_recovery_ready",
|
|
488
|
+
exitCode: 0,
|
|
489
|
+
execution,
|
|
490
|
+
warnings: warningSummary(warningEntries),
|
|
491
|
+
project,
|
|
492
|
+
nextAction: project.status.nextAction,
|
|
493
|
+
}));
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
export const HOOK_RECOVERY_MAX_OUTPUT_BYTES = MAX_OUTPUT_BYTES;
|
|
@@ -176,6 +176,193 @@ export function buildInspectSummary(
|
|
|
176
176
|
};
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
function compactWarningSummary(projectDriver) {
|
|
180
|
+
const byCode = new Map();
|
|
181
|
+
for (const warning of projectDriver?.warnings ?? []) {
|
|
182
|
+
const current = byCode.get(warning.code) ?? {
|
|
183
|
+
code: warning.code,
|
|
184
|
+
count: 0,
|
|
185
|
+
blocking: warning.blocking === true,
|
|
186
|
+
};
|
|
187
|
+
current.count += 1;
|
|
188
|
+
current.blocking ||= warning.blocking === true;
|
|
189
|
+
byCode.set(warning.code, current);
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
count: [...byCode.values()].reduce((total, group) => total + group.count, 0),
|
|
193
|
+
groups: [...byCode.values()].sort((left, right) => compareCodeUnits(left.code, right.code)),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function warningBucket(groups) {
|
|
198
|
+
const ordered = [...groups].sort((left, right) => compareCodeUnits(left.code, right.code));
|
|
199
|
+
return {
|
|
200
|
+
count: ordered.reduce((total, group) => total + group.count, 0),
|
|
201
|
+
groups: ordered,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function projectWarningBuckets(projectDriver) {
|
|
206
|
+
const actionable = new Map();
|
|
207
|
+
const advisories = new Map();
|
|
208
|
+
for (const warning of projectDriver?.warnings ?? []) {
|
|
209
|
+
const advisory = warning.code === "no_bound_verification_receipt";
|
|
210
|
+
const target = advisory ? advisories : actionable;
|
|
211
|
+
const current = target.get(warning.code) ?? {
|
|
212
|
+
code: warning.code,
|
|
213
|
+
count: 0,
|
|
214
|
+
blocking: advisory ? false : warning.blocking === true,
|
|
215
|
+
recommendedAction: advisory
|
|
216
|
+
? "no_retroactive_action_required"
|
|
217
|
+
: warning.recommendedAction ?? "review_project_warning",
|
|
218
|
+
};
|
|
219
|
+
current.count += 1;
|
|
220
|
+
current.blocking ||= warning.blocking === true;
|
|
221
|
+
target.set(warning.code, current);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
projectWarnings: warningBucket(actionable.values()),
|
|
225
|
+
advisorySummary: warningBucket(advisories.values()),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function maintenanceWarningBuckets(localMaintenance) {
|
|
230
|
+
const maintenance = [];
|
|
231
|
+
const launcherCount = localMaintenance?.profileLaunchers?.launcherWarnings?.length ?? 0;
|
|
232
|
+
if (launcherCount > 0) {
|
|
233
|
+
maintenance.push({
|
|
234
|
+
code: "profile_launcher_warnings",
|
|
235
|
+
count: launcherCount,
|
|
236
|
+
blocking: false,
|
|
237
|
+
recommendedAction: "review_profile_launchers",
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
for (const affected of localMaintenance?.runtimeIsolation?.affectedScripts ?? []) {
|
|
241
|
+
maintenance.push({
|
|
242
|
+
code: `runkit_runtime_collection_risk:${affected.tool}:${affected.script}`,
|
|
243
|
+
count: 1,
|
|
244
|
+
blocking: false,
|
|
245
|
+
recommendedAction: affected.tool === "eslint"
|
|
246
|
+
&& affected.ignoreFile.startsWith("eslint.config.")
|
|
247
|
+
? "add_runkit_to_eslint_flat_config_global_ignores"
|
|
248
|
+
: `exclude_runkit_runtime_in_${affected.ignoreFile}`,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
const skill = (localMaintenance?.skill?.warningCodes ?? []).map(code => ({
|
|
252
|
+
code,
|
|
253
|
+
count: 1,
|
|
254
|
+
blocking: false,
|
|
255
|
+
recommendedAction: code === "shared_skill_version_drift"
|
|
256
|
+
? "reload_package_bound_skill"
|
|
257
|
+
: "use_package_bound_skill",
|
|
258
|
+
}));
|
|
259
|
+
return {
|
|
260
|
+
maintenanceWarnings: warningBucket(maintenance),
|
|
261
|
+
skillDriftWarnings: warningBucket(skill),
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export function buildInspectCompactV1(inspected) {
|
|
266
|
+
const projectDriver = inspected.projectDriver ?? null;
|
|
267
|
+
const summary = inspected.summary;
|
|
268
|
+
return {
|
|
269
|
+
schemaVersion: "OwlCodaRunKitInspectCompactV1",
|
|
270
|
+
status: "inspected_compact",
|
|
271
|
+
exitCode: inspected.exitCode,
|
|
272
|
+
projectTruthHash: projectDriver?.projectTruthHash ?? null,
|
|
273
|
+
progress: {
|
|
274
|
+
overall: projectDriver?.overall ?? null,
|
|
275
|
+
completedWorkItems: projectDriver?.counts?.completed ?? null,
|
|
276
|
+
totalWorkItems: projectDriver?.counts?.total ?? null,
|
|
277
|
+
},
|
|
278
|
+
dominantGap: {
|
|
279
|
+
code: summary.dominantGap.code,
|
|
280
|
+
reason: summary.dominantGap.reasons[0] ?? null,
|
|
281
|
+
},
|
|
282
|
+
ownerAgentId: projectDriver?.nextActorId
|
|
283
|
+
?? projectDriver?.dominantGap?.agentId
|
|
284
|
+
?? null,
|
|
285
|
+
nextAction: summary.recommendedNextAction ?? summary.nextAllowedAction,
|
|
286
|
+
warningSummary: compactWarningSummary(projectDriver),
|
|
287
|
+
execution: {
|
|
288
|
+
state: summary.currentExecution.state,
|
|
289
|
+
openCount: summary.currentExecution.openCount,
|
|
290
|
+
activeLeaseCount: summary.leases.activeCount,
|
|
291
|
+
},
|
|
292
|
+
authorityBoundary: {
|
|
293
|
+
git: false,
|
|
294
|
+
release: false,
|
|
295
|
+
deployment: false,
|
|
296
|
+
production: false,
|
|
297
|
+
business: false,
|
|
298
|
+
},
|
|
299
|
+
authorizationGranted: false,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
export function buildInspectCompactV2(inspected) {
|
|
304
|
+
const projectDriver = inspected.projectDriver ?? null;
|
|
305
|
+
const summary = inspected.summary;
|
|
306
|
+
const { projectWarnings, advisorySummary } = projectWarningBuckets(projectDriver);
|
|
307
|
+
const { maintenanceWarnings, skillDriftWarnings } = maintenanceWarningBuckets(
|
|
308
|
+
inspected.localMaintenance ?? null,
|
|
309
|
+
);
|
|
310
|
+
const warningSummary = warningBucket([
|
|
311
|
+
...projectWarnings.groups,
|
|
312
|
+
...maintenanceWarnings.groups,
|
|
313
|
+
...skillDriftWarnings.groups,
|
|
314
|
+
]);
|
|
315
|
+
const lifecycleNextAction = summary.lifecycleNextAction ?? summary.nextAllowedAction;
|
|
316
|
+
const projectNextAction = projectDriver?.nextAction ?? null;
|
|
317
|
+
const maintenanceNextAction = skillDriftWarnings.groups.some(
|
|
318
|
+
group => group.code === "shared_skill_version_drift",
|
|
319
|
+
)
|
|
320
|
+
? "reload_package_bound_skill"
|
|
321
|
+
: maintenanceWarnings.groups.length > 0
|
|
322
|
+
? maintenanceWarnings.groups[0].recommendedAction
|
|
323
|
+
: null;
|
|
324
|
+
return {
|
|
325
|
+
schemaVersion: "OwlCodaRunKitInspectCompactV2",
|
|
326
|
+
status: "inspected_compact",
|
|
327
|
+
exitCode: inspected.exitCode,
|
|
328
|
+
projectTruthHash: projectDriver?.projectTruthHash ?? null,
|
|
329
|
+
progress: {
|
|
330
|
+
overall: projectDriver?.overall ?? null,
|
|
331
|
+
completedWorkItems: projectDriver?.counts?.completed ?? null,
|
|
332
|
+
totalWorkItems: projectDriver?.counts?.total ?? null,
|
|
333
|
+
},
|
|
334
|
+
dominantGap: {
|
|
335
|
+
code: summary.dominantGap.code,
|
|
336
|
+
reason: summary.dominantGap.reasons[0] ?? null,
|
|
337
|
+
},
|
|
338
|
+
ownerAgentId: projectDriver?.nextActorId
|
|
339
|
+
?? projectDriver?.dominantGap?.agentId
|
|
340
|
+
?? null,
|
|
341
|
+
nextAction: projectNextAction ?? lifecycleNextAction ?? maintenanceNextAction,
|
|
342
|
+
lifecycleNextAction,
|
|
343
|
+
projectNextAction,
|
|
344
|
+
maintenanceNextAction,
|
|
345
|
+
projectWarnings,
|
|
346
|
+
maintenanceWarnings,
|
|
347
|
+
skillDriftWarnings,
|
|
348
|
+
advisorySummary,
|
|
349
|
+
warningSummary,
|
|
350
|
+
execution: {
|
|
351
|
+
state: summary.currentExecution.state,
|
|
352
|
+
openCount: summary.currentExecution.openCount,
|
|
353
|
+
activeLeaseCount: summary.leases.activeCount,
|
|
354
|
+
},
|
|
355
|
+
authorityBoundary: {
|
|
356
|
+
git: false,
|
|
357
|
+
release: false,
|
|
358
|
+
deployment: false,
|
|
359
|
+
production: false,
|
|
360
|
+
business: false,
|
|
361
|
+
},
|
|
362
|
+
authorizationGranted: false,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
179
366
|
function value(value) {
|
|
180
367
|
if (value === null || value === undefined || value === "") return "none";
|
|
181
368
|
return String(value);
|
|
@@ -786,6 +786,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
786
786
|
workspaceRoots: options.workspace ?? null,
|
|
787
787
|
fleetManifestPath: options["fleet-manifest"] ?? null,
|
|
788
788
|
fleetRoots: options["fleet-root"] ?? null,
|
|
789
|
+
fleetRegistryPath: options.registry ?? null,
|
|
789
790
|
});
|
|
790
791
|
return { exitCode: 0, ...output };
|
|
791
792
|
}
|