owlrunkit 0.22.0 → 0.23.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 +51 -0
- package/README.md +53 -9
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +55 -16
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +10 -0
- package/packages/attest/src/quick.mjs +10 -0
- package/registry-release-policy-v2.json +5 -5
- package/sbom.spdx.json +4 -4
- package/scripts/runkit-contract/core-contract.mjs +5 -1
- package/scripts/runkit-contract/fleet-skill-plan.mjs +14 -66
- package/scripts/runkit-contract/formal-workflow.mjs +1 -1
- package/scripts/runkit-contract/install-codex-skill.mjs +2097 -196
- package/scripts/runkit-contract/project-cli-resolver.mjs +8 -0
- package/scripts/runkit-contract/project-completion-continuity.mjs +545 -0
- package/scripts/runkit-contract/registry-adoption.mjs +4 -4
- package/scripts/runkit-contract/runkit-cli.mjs +41 -5
- package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +16 -3
- package/scripts/runkit-contract/schemas/project-completion-continuity-v1.schema.json +144 -0
- package/scripts/runkit-contract/schemas/successor-scaffold-v1.schema.json +184 -0
- package/scripts/runkit-contract/schemas/team-project-status-v5.schema.json +169 -0
|
@@ -22,10 +22,18 @@ const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set([
|
|
|
22
22
|
"0.21.0",
|
|
23
23
|
"0.21.1",
|
|
24
24
|
"0.22.0",
|
|
25
|
+
"0.22.1",
|
|
26
|
+
"0.23.0",
|
|
25
27
|
]);
|
|
26
28
|
const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
|
|
27
29
|
const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
|
|
28
30
|
const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
|
|
31
|
+
["0.22.1", new Set([
|
|
32
|
+
"sha256:41447517f78b74a070802bd67c8b066a833eedde72e2606e3127fa1258c289a4",
|
|
33
|
+
])],
|
|
34
|
+
["0.22.0", new Set([
|
|
35
|
+
"sha256:e79d2123d88ada0e2e31f62fabee83b1d661ca02e93a383691a502f721ae1e7e",
|
|
36
|
+
])],
|
|
29
37
|
["0.21.0", new Set([
|
|
30
38
|
"sha256:f85b9bf8c32705d5f5dfebdb653e83ac8d1ac0468214c17f230cbd2de04246fd",
|
|
31
39
|
])],
|
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import {
|
|
3
|
+
lstatSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
realpathSync,
|
|
6
|
+
} from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
quickSnapshotFingerprintValid,
|
|
11
|
+
validWorkspaceSnapshotShape,
|
|
12
|
+
} from "../../packages/attest/src/quick-receipt-contract.mjs";
|
|
13
|
+
import { captureWorkspaceSnapshot } from "./quick-workspace-snapshot.mjs";
|
|
14
|
+
import { readTeamProjectStatusV4 } from "./team-project.mjs";
|
|
15
|
+
|
|
16
|
+
const AUTHORITY_BOUNDARY = Object.freeze({
|
|
17
|
+
agentDispatch: false,
|
|
18
|
+
git: false,
|
|
19
|
+
release: false,
|
|
20
|
+
deployment: false,
|
|
21
|
+
production: false,
|
|
22
|
+
business: false,
|
|
23
|
+
automation: false,
|
|
24
|
+
money: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const MAX_CHANGED_PATHS = 256;
|
|
28
|
+
|
|
29
|
+
function canonical(value) {
|
|
30
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
31
|
+
if (value && typeof value === "object") {
|
|
32
|
+
return `{${Object.keys(value).sort().map(key => (
|
|
33
|
+
`${JSON.stringify(key)}:${canonical(value[key])}`
|
|
34
|
+
)).join(",")}}`;
|
|
35
|
+
}
|
|
36
|
+
const encoded = JSON.stringify(value);
|
|
37
|
+
if (encoded === undefined) throw new Error("Completion continuity value is not canonical JSON.");
|
|
38
|
+
return encoded;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function compareCodeUnits(left, right) {
|
|
42
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function uniqueSorted(values) {
|
|
46
|
+
return [...new Set(values)].sort(compareCodeUnits);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function canonicalGitRoot(workspaceRoot) {
|
|
50
|
+
const requested = path.resolve(workspaceRoot);
|
|
51
|
+
const stat = lstatSync(requested);
|
|
52
|
+
const resolved = realpathSync(requested);
|
|
53
|
+
if (stat.isSymbolicLink() || !stat.isDirectory() || requested !== resolved) {
|
|
54
|
+
throw new Error("Successor source workspace must be a real directory without symlink traversal.");
|
|
55
|
+
}
|
|
56
|
+
const gitRoot = realpathSync(execFileSync(
|
|
57
|
+
"git",
|
|
58
|
+
["-C", resolved, "rev-parse", "--show-toplevel"],
|
|
59
|
+
{
|
|
60
|
+
encoding: "utf8",
|
|
61
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
62
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
63
|
+
},
|
|
64
|
+
).trim());
|
|
65
|
+
if (gitRoot !== resolved) {
|
|
66
|
+
throw new Error(`Successor source workspace must be the exact Git worktree root: ${gitRoot}.`);
|
|
67
|
+
}
|
|
68
|
+
return resolved;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function stableWorkspaceSnapshot(workspaceRoot) {
|
|
72
|
+
const before = captureWorkspaceSnapshot(workspaceRoot);
|
|
73
|
+
const after = captureWorkspaceSnapshot(workspaceRoot);
|
|
74
|
+
if (canonical(before) !== canonical(after)) {
|
|
75
|
+
throw new Error("Successor source workspace changed while its read-only snapshot was captured.");
|
|
76
|
+
}
|
|
77
|
+
return after;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function readBaselineEvent(controllerRoot, delivery) {
|
|
81
|
+
const runtimeRoot = path.join(controllerRoot, ".owlcoda", "runkit");
|
|
82
|
+
const requested = path.resolve(runtimeRoot, delivery.truthRef);
|
|
83
|
+
const relative = path.relative(runtimeRoot, requested);
|
|
84
|
+
if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
|
|
85
|
+
throw new Error("Completion continuity source delivery truth ref escapes the RunKit root.");
|
|
86
|
+
}
|
|
87
|
+
const stat = lstatSync(requested);
|
|
88
|
+
if (stat.isSymbolicLink() || !stat.isFile() || realpathSync(requested) !== requested) {
|
|
89
|
+
throw new Error("Completion continuity source delivery must be a regular file without symlinks.");
|
|
90
|
+
}
|
|
91
|
+
const event = JSON.parse(readFileSync(requested, "utf8"));
|
|
92
|
+
if (event.type !== "source_delivery_imported"
|
|
93
|
+
|| event.deliveryId !== delivery.deliveryId
|
|
94
|
+
|| event.workspaceSnapshotFingerprint !== delivery.workspaceSnapshotFingerprint
|
|
95
|
+
|| !validWorkspaceSnapshotShape(event.targetSnapshot)
|
|
96
|
+
|| !quickSnapshotFingerprintValid(event.targetSnapshot)
|
|
97
|
+
|| event.targetSnapshot.sourceFingerprint !== delivery.workspaceSnapshotFingerprint) {
|
|
98
|
+
throw new Error("Completion continuity source delivery baseline is invalid.");
|
|
99
|
+
}
|
|
100
|
+
return event;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function changedOverlayPaths(before, after) {
|
|
104
|
+
const beforeRows = new Map(before.dirtyOverlay.map(row => [row.path, canonical(row)]));
|
|
105
|
+
const afterRows = new Map(after.dirtyOverlay.map(row => [row.path, canonical(row)]));
|
|
106
|
+
return uniqueSorted([...beforeRows.keys(), ...afterRows.keys()].filter(filePath => (
|
|
107
|
+
beforeRows.get(filePath) !== afterRows.get(filePath)
|
|
108
|
+
)));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function committedPaths(root, beforeHead, afterHead) {
|
|
112
|
+
if (beforeHead === afterHead) return { paths: [], complete: true };
|
|
113
|
+
if (beforeHead === null || afterHead === null) return { paths: [], complete: false };
|
|
114
|
+
try {
|
|
115
|
+
const raw = execFileSync(
|
|
116
|
+
"git",
|
|
117
|
+
[
|
|
118
|
+
"-C", root, "diff", "--name-status", "-z", "--find-renames",
|
|
119
|
+
beforeHead, afterHead, "--",
|
|
120
|
+
],
|
|
121
|
+
{
|
|
122
|
+
encoding: "utf8",
|
|
123
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
124
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
125
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
const fields = raw.split("\0");
|
|
129
|
+
if (fields.at(-1) === "") fields.pop();
|
|
130
|
+
const paths = [];
|
|
131
|
+
for (let index = 0; index < fields.length;) {
|
|
132
|
+
const status = fields[index];
|
|
133
|
+
index += 1;
|
|
134
|
+
if (!/^[A-Z][0-9]*$/u.test(status ?? "") || !fields[index]) {
|
|
135
|
+
throw new Error("Committed source delta is not valid Git name-status output.");
|
|
136
|
+
}
|
|
137
|
+
paths.push(fields[index]);
|
|
138
|
+
index += 1;
|
|
139
|
+
if (status.startsWith("R") || status.startsWith("C")) {
|
|
140
|
+
if (!fields[index]) {
|
|
141
|
+
throw new Error("Committed rename or copy is missing its destination path.");
|
|
142
|
+
}
|
|
143
|
+
paths.push(fields[index]);
|
|
144
|
+
index += 1;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
paths: uniqueSorted(paths),
|
|
149
|
+
complete: true,
|
|
150
|
+
};
|
|
151
|
+
} catch {
|
|
152
|
+
return { paths: [], complete: false };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function dependencyPaths(before, after) {
|
|
157
|
+
const beforeRows = new Map(before.dependencyLockfiles.map(row => [row.path, row.sha256]));
|
|
158
|
+
const afterRows = new Map(after.dependencyLockfiles.map(row => [row.path, row.sha256]));
|
|
159
|
+
return uniqueSorted([...beforeRows.keys(), ...afterRows.keys()].filter(filePath => (
|
|
160
|
+
beforeRows.get(filePath) !== afterRows.get(filePath)
|
|
161
|
+
)));
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function sourceDelta(root, baseline, current) {
|
|
165
|
+
const committed = committedPaths(root, baseline.headCommit, current.headCommit);
|
|
166
|
+
const overlayPaths = changedOverlayPaths(baseline, current);
|
|
167
|
+
const dependencyEnvironmentPaths = dependencyPaths(baseline, current);
|
|
168
|
+
const allPaths = uniqueSorted([
|
|
169
|
+
...committed.paths,
|
|
170
|
+
...overlayPaths,
|
|
171
|
+
...dependencyEnvironmentPaths,
|
|
172
|
+
]);
|
|
173
|
+
return {
|
|
174
|
+
baselineHead: baseline.headCommit,
|
|
175
|
+
currentHead: current.headCommit,
|
|
176
|
+
headChanged: baseline.headCommit !== current.headCommit,
|
|
177
|
+
trackedTreeChanged: baseline.trackedTreeIdentity !== current.trackedTreeIdentity,
|
|
178
|
+
submodulesChanged: canonical(baseline.submodules) !== canonical(current.submodules),
|
|
179
|
+
committedPaths: committed.paths.slice(0, MAX_CHANGED_PATHS),
|
|
180
|
+
overlayPaths: overlayPaths.slice(0, MAX_CHANGED_PATHS),
|
|
181
|
+
dependencyEnvironmentPaths: dependencyEnvironmentPaths.slice(0, MAX_CHANGED_PATHS),
|
|
182
|
+
changedPaths: allPaths.slice(0, MAX_CHANGED_PATHS),
|
|
183
|
+
changedPathCount: allPaths.length,
|
|
184
|
+
changedPathsTruncated: allPaths.length > MAX_CHANGED_PATHS,
|
|
185
|
+
pathEvidenceComplete: committed.complete,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function baselineDelivery(status, targetRoot) {
|
|
190
|
+
return [...status.sourceDeliveries]
|
|
191
|
+
.filter(delivery => path.resolve(delivery.targetWorkspaceRef) === targetRoot)
|
|
192
|
+
.sort((left, right) => (
|
|
193
|
+
compareCodeUnits(left.occurredAt, right.occurredAt)
|
|
194
|
+
|| compareCodeUnits(left.deliveryId, right.deliveryId)
|
|
195
|
+
))
|
|
196
|
+
.at(-1) ?? null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function continuityProjection({ controllerRoot, status, targetWorkspaceRoot = null }) {
|
|
200
|
+
if (status.overall !== "completed") {
|
|
201
|
+
return {
|
|
202
|
+
schemaVersion: "OwlCodaRunKitProjectCompletionContinuityV1",
|
|
203
|
+
status: "not_applicable",
|
|
204
|
+
projectId: status.projectId,
|
|
205
|
+
projectTruthHash: status.projectTruthHash,
|
|
206
|
+
disposition: "project_work_ledger_incomplete",
|
|
207
|
+
targetWorkspaceRef: null,
|
|
208
|
+
baseline: null,
|
|
209
|
+
current: null,
|
|
210
|
+
delta: null,
|
|
211
|
+
nextAction: null,
|
|
212
|
+
readOnly: true,
|
|
213
|
+
writesPerformed: false,
|
|
214
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
215
|
+
authorizationGranted: false,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const requestedTarget = targetWorkspaceRoot === null
|
|
220
|
+
? status.sourceDeliveries.at(-1)?.targetWorkspaceRef ?? null
|
|
221
|
+
: targetWorkspaceRoot;
|
|
222
|
+
if (requestedTarget === null) {
|
|
223
|
+
return {
|
|
224
|
+
schemaVersion: "OwlCodaRunKitProjectCompletionContinuityV1",
|
|
225
|
+
status: "baseline_missing",
|
|
226
|
+
projectId: status.projectId,
|
|
227
|
+
projectTruthHash: status.projectTruthHash,
|
|
228
|
+
disposition: "completed_project_source_baseline_missing",
|
|
229
|
+
targetWorkspaceRef: null,
|
|
230
|
+
baseline: null,
|
|
231
|
+
current: null,
|
|
232
|
+
delta: null,
|
|
233
|
+
nextAction: "Review the current workspace and bind an explicit source baseline before deciding whether the project is terminal.",
|
|
234
|
+
readOnly: true,
|
|
235
|
+
writesPerformed: false,
|
|
236
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
237
|
+
authorizationGranted: false,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let targetRoot;
|
|
242
|
+
try {
|
|
243
|
+
targetRoot = canonicalGitRoot(requestedTarget);
|
|
244
|
+
} catch {
|
|
245
|
+
return {
|
|
246
|
+
schemaVersion: "OwlCodaRunKitProjectCompletionContinuityV1",
|
|
247
|
+
status: "source_unverifiable",
|
|
248
|
+
projectId: status.projectId,
|
|
249
|
+
projectTruthHash: status.projectTruthHash,
|
|
250
|
+
disposition: "completed_project_source_unverifiable",
|
|
251
|
+
targetWorkspaceRef: path.resolve(requestedTarget),
|
|
252
|
+
baseline: null,
|
|
253
|
+
current: null,
|
|
254
|
+
delta: null,
|
|
255
|
+
nextAction: "Restore or explicitly replace the modeled source workspace before deciding whether the project is terminal.",
|
|
256
|
+
readOnly: true,
|
|
257
|
+
writesPerformed: false,
|
|
258
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
259
|
+
authorizationGranted: false,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
const delivery = baselineDelivery(status, targetRoot);
|
|
264
|
+
if (delivery === null) {
|
|
265
|
+
const current = stableWorkspaceSnapshot(targetRoot);
|
|
266
|
+
return {
|
|
267
|
+
schemaVersion: "OwlCodaRunKitProjectCompletionContinuityV1",
|
|
268
|
+
status: "baseline_missing",
|
|
269
|
+
projectId: status.projectId,
|
|
270
|
+
projectTruthHash: status.projectTruthHash,
|
|
271
|
+
disposition: "completed_project_source_baseline_missing",
|
|
272
|
+
targetWorkspaceRef: targetRoot,
|
|
273
|
+
baseline: null,
|
|
274
|
+
current: {
|
|
275
|
+
headCommit: current.headCommit,
|
|
276
|
+
workspaceSnapshotFingerprint: current.sourceFingerprint,
|
|
277
|
+
},
|
|
278
|
+
delta: null,
|
|
279
|
+
nextAction: "Review the current workspace and bind an explicit source baseline before deciding whether the project is terminal.",
|
|
280
|
+
readOnly: true,
|
|
281
|
+
writesPerformed: false,
|
|
282
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
283
|
+
authorizationGranted: false,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const event = readBaselineEvent(controllerRoot, delivery);
|
|
288
|
+
const current = stableWorkspaceSnapshot(targetRoot);
|
|
289
|
+
const refreshed = readTeamProjectStatusV4({ workspaceRoot: controllerRoot });
|
|
290
|
+
if (refreshed.projectTruthHash !== status.projectTruthHash) {
|
|
291
|
+
throw new Error("Project truth changed while completion continuity was being inspected.");
|
|
292
|
+
}
|
|
293
|
+
const drifted = current.sourceFingerprint !== event.targetSnapshot.sourceFingerprint;
|
|
294
|
+
return {
|
|
295
|
+
schemaVersion: "OwlCodaRunKitProjectCompletionContinuityV1",
|
|
296
|
+
status: drifted ? "workspace_drift" : "terminal",
|
|
297
|
+
projectId: status.projectId,
|
|
298
|
+
projectTruthHash: status.projectTruthHash,
|
|
299
|
+
disposition: drifted
|
|
300
|
+
? "completed_project_with_unmodeled_workspace_drift"
|
|
301
|
+
: "completed_project_terminal",
|
|
302
|
+
targetWorkspaceRef: targetRoot,
|
|
303
|
+
baseline: {
|
|
304
|
+
kind: "imported_source_delivery",
|
|
305
|
+
deliveryId: delivery.deliveryId,
|
|
306
|
+
occurredAt: delivery.occurredAt,
|
|
307
|
+
targetHead: delivery.targetHead,
|
|
308
|
+
workspaceSnapshotFingerprint: delivery.workspaceSnapshotFingerprint,
|
|
309
|
+
truthRef: delivery.truthRef,
|
|
310
|
+
},
|
|
311
|
+
current: {
|
|
312
|
+
headCommit: current.headCommit,
|
|
313
|
+
workspaceSnapshotFingerprint: current.sourceFingerprint,
|
|
314
|
+
},
|
|
315
|
+
delta: drifted ? sourceDelta(targetRoot, event.targetSnapshot, current) : null,
|
|
316
|
+
nextAction: drifted
|
|
317
|
+
? "Review the unmodeled workspace drift and author a successor only after confirming its objective and scope."
|
|
318
|
+
: null,
|
|
319
|
+
readOnly: true,
|
|
320
|
+
writesPerformed: false,
|
|
321
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
322
|
+
authorizationGranted: false,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function hasExplicitDeliveryTruth(status) {
|
|
327
|
+
return status.externalGates.length > 0
|
|
328
|
+
|| status.deliveryLifecycle.stages.some(stage => stage.truthRef !== null);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function continuityDominantGap(continuity) {
|
|
332
|
+
if (continuity.disposition === "completed_project_with_unmodeled_workspace_drift") {
|
|
333
|
+
return {
|
|
334
|
+
kind: "unmodeled_workspace_drift",
|
|
335
|
+
id: "workspace-drift",
|
|
336
|
+
title: "Unmodeled workspace drift",
|
|
337
|
+
workItemId: null,
|
|
338
|
+
agentId: null,
|
|
339
|
+
reason: "The completed Project Driver ledger is older than the bound source workspace.",
|
|
340
|
+
truthRefs: [continuity.baseline.truthRef],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
if (continuity.disposition === "completed_project_source_baseline_missing") {
|
|
344
|
+
return {
|
|
345
|
+
kind: "source_baseline",
|
|
346
|
+
id: "last-modeled-source",
|
|
347
|
+
title: "Source baseline missing",
|
|
348
|
+
workItemId: null,
|
|
349
|
+
agentId: null,
|
|
350
|
+
reason: "The completed Project Driver ledger has no explicit source snapshot baseline.",
|
|
351
|
+
truthRefs: ["project/definition.json"],
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
if (continuity.disposition === "completed_project_source_unverifiable") {
|
|
355
|
+
return {
|
|
356
|
+
kind: "source_baseline",
|
|
357
|
+
id: "last-modeled-source",
|
|
358
|
+
title: "Source baseline unverifiable",
|
|
359
|
+
workItemId: null,
|
|
360
|
+
agentId: null,
|
|
361
|
+
reason: "The modeled source workspace cannot be verified as an exact Git worktree root.",
|
|
362
|
+
truthRefs: ["project/definition.json"],
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
return {
|
|
366
|
+
kind: "none",
|
|
367
|
+
id: null,
|
|
368
|
+
title: null,
|
|
369
|
+
workItemId: null,
|
|
370
|
+
agentId: null,
|
|
371
|
+
reason: null,
|
|
372
|
+
truthRefs: continuity.baseline === null
|
|
373
|
+
? ["project/definition.json"]
|
|
374
|
+
: [continuity.baseline.truthRef],
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export function readTeamProjectStatusV5({ workspaceRoot }) {
|
|
379
|
+
const controllerRoot = realpathSync(path.resolve(workspaceRoot));
|
|
380
|
+
const status = readTeamProjectStatusV4({ workspaceRoot: controllerRoot });
|
|
381
|
+
const completionContinuity = continuityProjection({ controllerRoot, status });
|
|
382
|
+
const explicitDeliveryTruth = hasExplicitDeliveryTruth(status);
|
|
383
|
+
const shouldProjectContinuity = status.overall === "completed" && (
|
|
384
|
+
completionContinuity.status === "workspace_drift"
|
|
385
|
+
|| !explicitDeliveryTruth
|
|
386
|
+
);
|
|
387
|
+
if (!shouldProjectContinuity) {
|
|
388
|
+
return {
|
|
389
|
+
...status,
|
|
390
|
+
schemaVersion: "OwlCodaRunKitTeamProjectStatusV5",
|
|
391
|
+
completionContinuity,
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
const dominantGap = continuityDominantGap(completionContinuity);
|
|
395
|
+
const nextAction = completionContinuity.nextAction;
|
|
396
|
+
return {
|
|
397
|
+
...status,
|
|
398
|
+
schemaVersion: "OwlCodaRunKitTeamProjectStatusV5",
|
|
399
|
+
completionContinuity,
|
|
400
|
+
headline: dominantGap.kind === "none"
|
|
401
|
+
? "Completed project is terminal against its bound source baseline; authorizationGranted=false."
|
|
402
|
+
: `Dominant gap: ${dominantGap.kind}; blocker: ${dominantGap.reason} next: ${nextAction}`,
|
|
403
|
+
dominantGap,
|
|
404
|
+
nextAction,
|
|
405
|
+
nextActorId: null,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function pathPrefix(rule) {
|
|
410
|
+
return rule.endsWith("/**") ? rule.slice(0, -3) : null;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function pathCovered(rule, candidate) {
|
|
414
|
+
if (rule === candidate) return true;
|
|
415
|
+
const prefix = pathPrefix(rule);
|
|
416
|
+
return prefix !== null && (candidate === prefix || candidate.startsWith(`${prefix}/`));
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function scaffoldLanes(status, changedPaths) {
|
|
420
|
+
const matches = new Map(status.workItems.map(item => [item.workItemId, []]));
|
|
421
|
+
const unmatched = [];
|
|
422
|
+
const conflicts = [];
|
|
423
|
+
for (const changedPath of changedPaths) {
|
|
424
|
+
const owners = status.workItems.filter(item => (
|
|
425
|
+
item.ownedPaths.some(rule => pathCovered(rule, changedPath))
|
|
426
|
+
));
|
|
427
|
+
if (owners.length === 0) unmatched.push(changedPath);
|
|
428
|
+
for (const owner of owners) matches.get(owner.workItemId).push(changedPath);
|
|
429
|
+
if (owners.length > 1) {
|
|
430
|
+
conflicts.push({
|
|
431
|
+
path: changedPath,
|
|
432
|
+
historicalWorkItemIds: owners.map(item => item.workItemId).sort(compareCodeUnits),
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
const possibleLanes = status.workItems
|
|
437
|
+
.filter(item => matches.get(item.workItemId).length > 0)
|
|
438
|
+
.map(item => ({
|
|
439
|
+
laneId: `historical-scope:${item.workItemId}`,
|
|
440
|
+
basis: "historical_owned_paths_only",
|
|
441
|
+
historicalWorkItemId: item.workItemId,
|
|
442
|
+
suggestedOwnedPaths: uniqueSorted(matches.get(item.workItemId)),
|
|
443
|
+
requiresHumanConfirmation: true,
|
|
444
|
+
}));
|
|
445
|
+
if (unmatched.length > 0) {
|
|
446
|
+
possibleLanes.push({
|
|
447
|
+
laneId: "unassigned-paths",
|
|
448
|
+
basis: "unmatched_paths_only",
|
|
449
|
+
historicalWorkItemId: null,
|
|
450
|
+
suggestedOwnedPaths: uniqueSorted(unmatched),
|
|
451
|
+
requiresHumanConfirmation: true,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
return { possibleLanes, pathConflicts: conflicts };
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function buildSuccessorScaffoldV1({ workspaceRoot, fromWorkspaceRoot }) {
|
|
458
|
+
const controllerRoot = realpathSync(path.resolve(workspaceRoot));
|
|
459
|
+
const status = readTeamProjectStatusV4({ workspaceRoot: controllerRoot });
|
|
460
|
+
if (status.overall !== "completed") {
|
|
461
|
+
throw new Error("Successor scaffold requires a completed Project Driver ledger.");
|
|
462
|
+
}
|
|
463
|
+
const targetRoot = canonicalGitRoot(fromWorkspaceRoot);
|
|
464
|
+
const completionContinuity = continuityProjection({
|
|
465
|
+
controllerRoot,
|
|
466
|
+
status,
|
|
467
|
+
targetWorkspaceRoot: targetRoot,
|
|
468
|
+
});
|
|
469
|
+
const changedPaths = completionContinuity.delta?.changedPaths ?? [];
|
|
470
|
+
const { possibleLanes, pathConflicts } = scaffoldLanes(status, changedPaths);
|
|
471
|
+
const drifted = completionContinuity.status === "workspace_drift";
|
|
472
|
+
const baselineMissing = completionContinuity.status === "baseline_missing";
|
|
473
|
+
return {
|
|
474
|
+
schemaVersion: "OwlCodaRunKitSuccessorScaffoldV1",
|
|
475
|
+
status: drifted
|
|
476
|
+
? "successor_review_required"
|
|
477
|
+
: baselineMissing
|
|
478
|
+
? "successor_baseline_missing"
|
|
479
|
+
: completionContinuity.status === "terminal"
|
|
480
|
+
? "successor_not_required"
|
|
481
|
+
: "successor_source_unverifiable",
|
|
482
|
+
exitCode: 0,
|
|
483
|
+
project: {
|
|
484
|
+
projectId: status.projectId,
|
|
485
|
+
projectTruthHash: status.projectTruthHash,
|
|
486
|
+
overall: status.overall,
|
|
487
|
+
},
|
|
488
|
+
completionContinuity,
|
|
489
|
+
unmodeledCandidates: drifted
|
|
490
|
+
? [{
|
|
491
|
+
candidateId: "workspace-drift",
|
|
492
|
+
kind: "workspace_source_delta",
|
|
493
|
+
targetWorkspaceRef: targetRoot,
|
|
494
|
+
baselineFingerprint: completionContinuity.baseline.workspaceSnapshotFingerprint,
|
|
495
|
+
currentFingerprint: completionContinuity.current.workspaceSnapshotFingerprint,
|
|
496
|
+
changedPaths,
|
|
497
|
+
changedPathCount: completionContinuity.delta.changedPathCount,
|
|
498
|
+
changedPathsTruncated: completionContinuity.delta.changedPathsTruncated,
|
|
499
|
+
pathEvidenceComplete: completionContinuity.delta.pathEvidenceComplete,
|
|
500
|
+
}]
|
|
501
|
+
: [],
|
|
502
|
+
pathConflicts,
|
|
503
|
+
possibleLanes,
|
|
504
|
+
draft: {
|
|
505
|
+
projectId: null,
|
|
506
|
+
objective: null,
|
|
507
|
+
workItems: possibleLanes.map(lane => ({
|
|
508
|
+
draftLaneId: lane.laneId,
|
|
509
|
+
title: null,
|
|
510
|
+
ownedPaths: [...lane.suggestedOwnedPaths],
|
|
511
|
+
ownerAgentId: null,
|
|
512
|
+
})),
|
|
513
|
+
requiresHumanConfirmation: [
|
|
514
|
+
"project_id",
|
|
515
|
+
"objective",
|
|
516
|
+
"work_item_boundaries",
|
|
517
|
+
"owners",
|
|
518
|
+
"acceptance",
|
|
519
|
+
],
|
|
520
|
+
},
|
|
521
|
+
nextAction: completionContinuity.nextAction,
|
|
522
|
+
persisted: false,
|
|
523
|
+
readOnly: true,
|
|
524
|
+
writesPerformed: false,
|
|
525
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
526
|
+
authorizationGranted: false,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
export function formatSuccessorScaffoldHumanV1(scaffold) {
|
|
531
|
+
const continuity = scaffold.completionContinuity;
|
|
532
|
+
const lines = [
|
|
533
|
+
`Successor review: ${scaffold.status}`,
|
|
534
|
+
`Project: ${scaffold.project.projectId} (${scaffold.project.overall})`,
|
|
535
|
+
`Continuity: ${continuity.disposition}`,
|
|
536
|
+
`Source: ${continuity.targetWorkspaceRef ?? "not bound"}`,
|
|
537
|
+
`Unmodeled paths: ${scaffold.unmodeledCandidates[0]?.changedPathCount ?? 0}`,
|
|
538
|
+
`Possible lanes: ${scaffold.possibleLanes.length}; conflicts: ${scaffold.pathConflicts.length}`,
|
|
539
|
+
`Next: ${scaffold.nextAction ?? "stop; no successor is currently indicated"}`,
|
|
540
|
+
"Read-only: yes; project/event writes: 0; Agent dispatch: false.",
|
|
541
|
+
"Authorization: Git/release/deployment/production/business remain false.",
|
|
542
|
+
"",
|
|
543
|
+
];
|
|
544
|
+
return lines.join("\n");
|
|
545
|
+
}
|
|
@@ -47,10 +47,10 @@ const POLICY = Object.freeze({
|
|
|
47
47
|
],
|
|
48
48
|
rollbackRelease: {
|
|
49
49
|
packageName: PACKAGE_NAME,
|
|
50
|
-
version: "0.
|
|
51
|
-
shasum: "
|
|
52
|
-
integrity: "sha512-
|
|
53
|
-
tarballUrl: "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.
|
|
50
|
+
version: "0.22.1",
|
|
51
|
+
shasum: "675b417134825e876e6e2b7c2e8d2aea4045e59f",
|
|
52
|
+
integrity: "sha512-Vp+o6u00Di18fuPFcbEzdBpRd79e8P4yZq77RF2wuJG5ITjUGf/pAzOb5sOOVzj8P/MXcnhKcnRQKT70kUyZuA==",
|
|
53
|
+
tarballUrl: "https://registry.npmjs.org/owlrunkit/-/owlrunkit-0.22.1.tgz",
|
|
54
54
|
},
|
|
55
55
|
registryEvidenceMustBeExternal: true,
|
|
56
56
|
registryEvidenceSchemaVersion: "OwlCodaRunKitRegistryReleaseEvidenceV1",
|