owlrunkit 0.16.0 → 0.17.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 +40 -3
- package/README.md +61 -12
- package/docs/architecture/OWLCODA_RUNKIT_017_AGENT_NATIVE_PRODUCT_DEFINITION.md +154 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +74 -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-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +12 -2
- package/scripts/runkit-contract/fleet-discovery.mjs +768 -26
- package/scripts/runkit-contract/formal-workflow.mjs +201 -0
- package/scripts/runkit-contract/lifecycle-orchestration.mjs +7 -2
- package/scripts/runkit-contract/project-cli-resolver.mjs +3 -0
- package/scripts/runkit-contract/project-control-parser.mjs +62 -3
- package/scripts/runkit-contract/runkit-cli.mjs +162 -13
- package/scripts/runkit-contract/schemas/core-successor-plan-v1.schema.json +14 -2
- package/scripts/runkit-contract/schemas/fleet-discovery-v1.schema.json +33 -2
- package/scripts/runkit-contract/schemas/team-project-definition-v1.schema.json +108 -0
- package/scripts/runkit-contract/schemas/team-project-event-v1.schema.json +99 -0
- package/scripts/runkit-contract/schemas/team-project-status-v1.schema.json +50 -0
- package/scripts/runkit-contract/schemas/verification-envelope-v1.schema.json +102 -0
- package/scripts/runkit-contract/sync-core016-release-identity.mjs +9 -4
- package/scripts/runkit-contract/sync-release-identity.mjs +224 -0
- package/scripts/runkit-contract/team-project.mjs +1006 -0
- package/scripts/runkit-contract/templates/team-project-definition.json +30 -0
- package/scripts/runkit-contract/templates/team-project-event.json +9 -0
- package/scripts/runkit-contract/templates/verification-envelope.json +28 -0
- package/scripts/runkit-contract/verification-envelope-check.mjs +45 -0
- package/scripts/runkit-contract/verification-envelope.mjs +846 -0
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
realpathSync,
|
|
7
7
|
} from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
acceptedCloseoutVerificationIssues,
|
|
@@ -33,6 +34,16 @@ import {
|
|
|
33
34
|
} from "./source-candidate.mjs";
|
|
34
35
|
import { validateReceiptLineage } from "./receipt-lineage.mjs";
|
|
35
36
|
import { verificationContextFingerprint } from "./verification-context.mjs";
|
|
37
|
+
import {
|
|
38
|
+
runVerificationEnvelopeV1,
|
|
39
|
+
validateVerificationEnvelopeV1,
|
|
40
|
+
verifyVerificationEnvelopeReceiptV1,
|
|
41
|
+
} from "./verification-envelope.mjs";
|
|
42
|
+
|
|
43
|
+
const VERIFICATION_ENVELOPE_CHECKER = fileURLToPath(new URL(
|
|
44
|
+
"./verification-envelope-check.mjs",
|
|
45
|
+
import.meta.url,
|
|
46
|
+
));
|
|
36
47
|
|
|
37
48
|
const FORBIDDEN_EXTERNAL_TOOLS = new Set([
|
|
38
49
|
"curl",
|
|
@@ -623,6 +634,184 @@ export function recordFormalCheckV1(options) {
|
|
|
623
634
|
);
|
|
624
635
|
}
|
|
625
636
|
|
|
637
|
+
async function recordFormalEnvelopeCheckWithinControlTransaction({
|
|
638
|
+
workspaceRoot,
|
|
639
|
+
runId,
|
|
640
|
+
workItemId,
|
|
641
|
+
checkId,
|
|
642
|
+
envelope,
|
|
643
|
+
hooks = {},
|
|
644
|
+
}) {
|
|
645
|
+
safeIdentifier(checkId, "checkId");
|
|
646
|
+
const root = realpathSync(workspaceRoot);
|
|
647
|
+
const { executionRoot, pinGate } = loadActiveExecution(root, runId);
|
|
648
|
+
if (pinGate.status !== "valid") return { ...pinGate, authorizationGranted: false };
|
|
649
|
+
const validatedEnvelope = validateVerificationEnvelopeV1({
|
|
650
|
+
workspaceRoot: root,
|
|
651
|
+
envelope,
|
|
652
|
+
});
|
|
653
|
+
const receiptPath = path.join(checkRoot(executionRoot), `${checkId}.json`);
|
|
654
|
+
const candidateResult = resumeOrFreezeCandidate({
|
|
655
|
+
workspaceRoot: root,
|
|
656
|
+
executionRoot,
|
|
657
|
+
runId,
|
|
658
|
+
workItemId,
|
|
659
|
+
checkId,
|
|
660
|
+
});
|
|
661
|
+
const candidate = candidateResult.candidate;
|
|
662
|
+
if (candidate.status !== "source_candidate_frozen") return candidate;
|
|
663
|
+
hooks.afterCandidatePersist?.();
|
|
664
|
+
const candidateDocument = readJson(path.join(root, candidate.candidatePath));
|
|
665
|
+
const envelopePath = path.join(
|
|
666
|
+
executionRoot,
|
|
667
|
+
"formal-envelopes",
|
|
668
|
+
`${checkId}.json`,
|
|
669
|
+
);
|
|
670
|
+
const envelopeResult = writeOrResumeExactJson(
|
|
671
|
+
envelopePath,
|
|
672
|
+
validatedEnvelope.envelope,
|
|
673
|
+
"Formal Verification Envelope",
|
|
674
|
+
);
|
|
675
|
+
const envelopeArtifactRoot = path.join(
|
|
676
|
+
executionRoot,
|
|
677
|
+
"verification-envelopes",
|
|
678
|
+
checkId,
|
|
679
|
+
);
|
|
680
|
+
const envelopeReceipt = await runVerificationEnvelopeV1({
|
|
681
|
+
workspaceRoot: root,
|
|
682
|
+
envelope: envelopeResult.value,
|
|
683
|
+
artifactRoot: envelopeArtifactRoot,
|
|
684
|
+
});
|
|
685
|
+
hooks.afterEnvelopePersist?.();
|
|
686
|
+
const commandArgv = [
|
|
687
|
+
realpathSync(process.execPath),
|
|
688
|
+
VERIFICATION_ENVELOPE_CHECKER,
|
|
689
|
+
"--workspace",
|
|
690
|
+
root,
|
|
691
|
+
"--receipt",
|
|
692
|
+
envelopeReceipt.receiptPath,
|
|
693
|
+
];
|
|
694
|
+
const verificationCommandPolicy = {
|
|
695
|
+
schemaVersion: "OwlCodaRunKitFormalVerificationCommandPolicyV1",
|
|
696
|
+
mode: "verification_envelope_v1",
|
|
697
|
+
riskClassification: "enforced_verification_envelope",
|
|
698
|
+
profileCommandIds: [],
|
|
699
|
+
executable: commandArgv[0],
|
|
700
|
+
argv: commandArgv.slice(1),
|
|
701
|
+
envelopeSha256: validatedEnvelope.envelopeSha256,
|
|
702
|
+
envelopeReceiptSha256: envelopeReceipt.receiptSha256,
|
|
703
|
+
authorizationGranted: false,
|
|
704
|
+
};
|
|
705
|
+
const context = derivedVerificationContext(root);
|
|
706
|
+
const request = {
|
|
707
|
+
schemaVersion: "OwlCodaRunKitSnapshotRequestV1",
|
|
708
|
+
snapshotId: `${checkId}-snapshot`,
|
|
709
|
+
mode: "project",
|
|
710
|
+
targetRoot: root,
|
|
711
|
+
cwd: ".",
|
|
712
|
+
executable: verificationCommandPolicy.executable,
|
|
713
|
+
argv: verificationCommandPolicy.argv,
|
|
714
|
+
launcherVersion: "owlrunkit-formal-envelope-v1",
|
|
715
|
+
toolVersions: structuredClone(context.toolchains),
|
|
716
|
+
selectedPaths: candidateDocument.sourceManifest.entries
|
|
717
|
+
.filter((entry) => entry.operation !== "deleted")
|
|
718
|
+
.map((entry) => entry.path)
|
|
719
|
+
.sort(),
|
|
720
|
+
statusMode: "porcelain-v1-z-untracked-all-runkit-excluded",
|
|
721
|
+
verificationContext: context,
|
|
722
|
+
};
|
|
723
|
+
const requestPath = path.join(
|
|
724
|
+
executionRoot,
|
|
725
|
+
"formal-check-requests",
|
|
726
|
+
`${checkId}.json`,
|
|
727
|
+
);
|
|
728
|
+
const requestResult = writeOrResumeExactJson(
|
|
729
|
+
requestPath,
|
|
730
|
+
request,
|
|
731
|
+
"Formal check request",
|
|
732
|
+
);
|
|
733
|
+
hooks.afterRequestPersist?.();
|
|
734
|
+
const snapshotResult = resumeSnapshot({
|
|
735
|
+
workspaceRoot: root,
|
|
736
|
+
executionRoot,
|
|
737
|
+
runId,
|
|
738
|
+
request: requestResult.value,
|
|
739
|
+
candidateDocument,
|
|
740
|
+
});
|
|
741
|
+
const snapshot = snapshotResult.snapshot;
|
|
742
|
+
hooks.afterSnapshotPersist?.();
|
|
743
|
+
const savedSnapshot = readJson(path.join(root, snapshot.snapshotPath));
|
|
744
|
+
const existingReceipt = existsSync(receiptPath)
|
|
745
|
+
? readRegularJson(receiptPath, "Formal check receipt")
|
|
746
|
+
: null;
|
|
747
|
+
const sequence = existingReceipt?.sequence
|
|
748
|
+
?? readChecks(executionRoot).length + 1;
|
|
749
|
+
const status = snapshot.status === "snapshot_passed"
|
|
750
|
+
&& envelopeReceipt.formalEligible
|
|
751
|
+
? "formal_check_passed"
|
|
752
|
+
: "formal_check_failed";
|
|
753
|
+
const receipt = {
|
|
754
|
+
schemaVersion: "OwlCodaRunKitFormalCheckV1",
|
|
755
|
+
runId,
|
|
756
|
+
checkId,
|
|
757
|
+
sequence,
|
|
758
|
+
status,
|
|
759
|
+
candidatePath: candidate.candidatePath,
|
|
760
|
+
candidateSha256: candidate.candidateSha256,
|
|
761
|
+
payloadSha256: candidate.payloadSha256,
|
|
762
|
+
sourceFingerprint: candidate.sourceFingerprint,
|
|
763
|
+
verificationContext: context,
|
|
764
|
+
verificationContextFingerprint: verificationContextFingerprint(context),
|
|
765
|
+
snapshotRequestPath: relativeToWorkspace(root, requestPath),
|
|
766
|
+
snapshotPath: snapshot.snapshotPath,
|
|
767
|
+
commandExitCode: savedSnapshot.command.exitCode,
|
|
768
|
+
verificationCommandPolicy: {
|
|
769
|
+
schemaVersion: verificationCommandPolicy.schemaVersion,
|
|
770
|
+
mode: verificationCommandPolicy.mode,
|
|
771
|
+
riskClassification: verificationCommandPolicy.riskClassification,
|
|
772
|
+
profileCommandIds: verificationCommandPolicy.profileCommandIds,
|
|
773
|
+
envelopeSha256: verificationCommandPolicy.envelopeSha256,
|
|
774
|
+
envelopeReceiptSha256: verificationCommandPolicy.envelopeReceiptSha256,
|
|
775
|
+
authorizationGranted: false,
|
|
776
|
+
},
|
|
777
|
+
envelopePath: relativeToWorkspace(root, envelopePath),
|
|
778
|
+
envelopeSha256: validatedEnvelope.envelopeSha256,
|
|
779
|
+
envelopeReceiptPath: relativeToWorkspace(root, envelopeReceipt.receiptPath),
|
|
780
|
+
envelopeReceiptSha256: envelopeReceipt.receiptSha256,
|
|
781
|
+
envelopeFormalEligible: envelopeReceipt.formalEligible,
|
|
782
|
+
finalized: false,
|
|
783
|
+
authorizationGranted: false,
|
|
784
|
+
};
|
|
785
|
+
const receiptResult = writeOrResumeExactJson(
|
|
786
|
+
receiptPath,
|
|
787
|
+
receipt,
|
|
788
|
+
"Formal check receipt",
|
|
789
|
+
);
|
|
790
|
+
hooks.afterReceiptPersist?.();
|
|
791
|
+
return {
|
|
792
|
+
...receiptResult.value,
|
|
793
|
+
exitCode: status === "formal_check_passed" ? 0 : 2,
|
|
794
|
+
checkReceiptPath: relativeToWorkspace(root, receiptPath),
|
|
795
|
+
resumed: candidateResult.resumed
|
|
796
|
+
|| envelopeResult.resumed
|
|
797
|
+
|| envelopeReceipt.resumed
|
|
798
|
+
|| requestResult.resumed
|
|
799
|
+
|| snapshotResult.resumed
|
|
800
|
+
|| receiptResult.resumed,
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
export function recordFormalEnvelopeCheckV1(options) {
|
|
805
|
+
const workspaceRoot = realpathSync(options.workspaceRoot);
|
|
806
|
+
return withControlTransaction(
|
|
807
|
+
workspaceRoot,
|
|
808
|
+
() => recordFormalEnvelopeCheckWithinControlTransaction({
|
|
809
|
+
...options,
|
|
810
|
+
workspaceRoot,
|
|
811
|
+
}),
|
|
812
|
+
);
|
|
813
|
+
}
|
|
814
|
+
|
|
626
815
|
function finalizeFormalChecksWithinControlTransaction({
|
|
627
816
|
workspaceRoot,
|
|
628
817
|
runId,
|
|
@@ -651,6 +840,18 @@ function finalizeFormalChecksWithinControlTransaction({
|
|
|
651
840
|
if (latest.status !== "formal_check_passed" || selected.length === 0) {
|
|
652
841
|
throw new Error("Formal finish requires the latest source to have a passed check.");
|
|
653
842
|
}
|
|
843
|
+
for (const check of selected) {
|
|
844
|
+
if (!check.envelopeReceiptPath) continue;
|
|
845
|
+
const verifiedEnvelope = verifyVerificationEnvelopeReceiptV1({
|
|
846
|
+
workspaceRoot: root,
|
|
847
|
+
receiptPath: path.join(root, check.envelopeReceiptPath),
|
|
848
|
+
});
|
|
849
|
+
if (!verifiedEnvelope.formalEligible
|
|
850
|
+
|| verifiedEnvelope.receiptSha256 !== check.envelopeReceiptSha256
|
|
851
|
+
|| verifiedEnvelope.envelopeSha256 !== check.envelopeSha256) {
|
|
852
|
+
throw new Error(`Formal finish requires a valid Formal-eligible envelope receipt: ${check.checkId}.`);
|
|
853
|
+
}
|
|
854
|
+
}
|
|
654
855
|
const request = {
|
|
655
856
|
schemaVersion: "OwlCodaRunKitFinalizeRequestV1",
|
|
656
857
|
receiptId: `${finalizeId}-receipt`,
|
|
@@ -486,6 +486,7 @@ export function activeAcceptedGate({ workspaceRoot, runId }) {
|
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
488
|
const distinctMatchingPackets = collapseByteIdenticalDeliveryCandidates(matchingPackets);
|
|
489
|
+
const declaredSourceArtifact = validated.active.receipt.sourceArtifact;
|
|
489
490
|
let matchingCandidate = null;
|
|
490
491
|
if (distinctMatchingPackets.length === 0) {
|
|
491
492
|
const candidateRoot = path.join(executionRoot, "source-candidates");
|
|
@@ -524,7 +525,12 @@ export function activeAcceptedGate({ workspaceRoot, runId }) {
|
|
|
524
525
|
candidates.map(({ candidate }) => candidate.sourceManifest.sha256),
|
|
525
526
|
);
|
|
526
527
|
if (manifestFingerprints.size === 1) {
|
|
527
|
-
|
|
528
|
+
matchingCandidate = candidates.find(({ candidatePath }) => (
|
|
529
|
+
declaredSourceArtifact?.kind === "source_candidate_v2"
|
|
530
|
+
&& declaredSourceArtifact.path
|
|
531
|
+
=== relativeToWorkspace(workspaceRoot, candidatePath)
|
|
532
|
+
&& declaredSourceArtifact.sha256 === sha256(readFileSync(candidatePath))
|
|
533
|
+
)) ?? candidates[0];
|
|
528
534
|
}
|
|
529
535
|
}
|
|
530
536
|
if (
|
|
@@ -537,7 +543,6 @@ export function activeAcceptedGate({ workspaceRoot, runId }) {
|
|
|
537
543
|
throw new Error("Accepted finish requires one fresh source binding matching the active verification gate; source drift remains unresolved.");
|
|
538
544
|
}
|
|
539
545
|
const [matchingPacket] = distinctMatchingPackets;
|
|
540
|
-
const declaredSourceArtifact = validated.active.receipt.sourceArtifact;
|
|
541
546
|
if (declaredSourceArtifact !== undefined) {
|
|
542
547
|
const selectedPath = matchingCandidate === null
|
|
543
548
|
? matchingPacket.packetPath
|
|
@@ -18,6 +18,9 @@ const OFFICIAL_CLI_ENTRYPOINT =
|
|
|
18
18
|
const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
|
|
19
19
|
const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
|
|
20
20
|
const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
|
|
21
|
+
["0.16.0", new Set([
|
|
22
|
+
"sha256:320fb1d97b4459d1a14b0b67807dcf2bc6b03970492cb9fb2c6245b17912c81e",
|
|
23
|
+
])],
|
|
21
24
|
["0.15.1", new Set([
|
|
22
25
|
"sha256:e8ca57522a8e473da356ceb3768bb650267894638d450d5e49463ab6f51c752b",
|
|
23
26
|
])],
|
|
@@ -140,7 +140,40 @@ function allowedKeys(value, expected) {
|
|
|
140
140
|
return Object.keys(value).every(key => expected.includes(key));
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
function
|
|
143
|
+
function validateHistoricalLeaseOwnedPaths(paths, contract) {
|
|
144
|
+
try {
|
|
145
|
+
return contract.validateLeaseOwnedPaths(paths);
|
|
146
|
+
} catch {
|
|
147
|
+
if (!Array.isArray(paths) || paths.length === 0) throw new Error("invalid");
|
|
148
|
+
const normalized = [...new Set(paths.map(value => {
|
|
149
|
+
if (
|
|
150
|
+
typeof value !== "string"
|
|
151
|
+
|| value.length === 0
|
|
152
|
+
|| path.isAbsolute(value)
|
|
153
|
+
|| path.win32.isAbsolute(value)
|
|
154
|
+
|| value.includes("\\")
|
|
155
|
+
) throw new Error("invalid");
|
|
156
|
+
const segments = value.split("/");
|
|
157
|
+
if (segments.some(segment => (
|
|
158
|
+
segment.length === 0 || segment === "." || segment === ".."
|
|
159
|
+
))) throw new Error("invalid");
|
|
160
|
+
if (
|
|
161
|
+
value.startsWith(`${contract.runtimeRoot}/`)
|
|
162
|
+
&& value !== `${contract.runtimeRoot}/profiles.json`
|
|
163
|
+
) throw new Error("invalid");
|
|
164
|
+
return value;
|
|
165
|
+
}))].sort(compareCodeUnits);
|
|
166
|
+
return normalized;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function inspectLeases(
|
|
171
|
+
root,
|
|
172
|
+
executionRoot,
|
|
173
|
+
runId,
|
|
174
|
+
contract,
|
|
175
|
+
{ historicalAccepted = false, expectedReleasedLeaseIds = null } = {},
|
|
176
|
+
) {
|
|
144
177
|
const leasesRoot = path.join(executionRoot, "leases");
|
|
145
178
|
if (!hasFilesystemEntry(leasesRoot)) {
|
|
146
179
|
return {
|
|
@@ -200,7 +233,9 @@ function inspectLeases(root, executionRoot, runId, contract) {
|
|
|
200
233
|
issues.push(`Lease attempt is invalid: ${runId}/${entry.name}`);
|
|
201
234
|
}
|
|
202
235
|
try {
|
|
203
|
-
const ownedPaths =
|
|
236
|
+
const ownedPaths = historicalAccepted
|
|
237
|
+
? validateHistoricalLeaseOwnedPaths(lease.ownedPaths, contract)
|
|
238
|
+
: contract.validateLeaseOwnedPaths(lease.ownedPaths);
|
|
204
239
|
if (ownedPaths.some(ownedPath => (
|
|
205
240
|
ownedPath.includes("*")
|
|
206
241
|
&& (!ownedPath.endsWith("/**") || ownedPath.slice(0, -3).includes("*"))
|
|
@@ -232,6 +267,14 @@ function inspectLeases(root, executionRoot, runId, contract) {
|
|
|
232
267
|
.map(lease => lease.workItemId)
|
|
233
268
|
.filter(workItemId => typeof workItemId === "string")
|
|
234
269
|
.sort(compareCodeUnits);
|
|
270
|
+
if (
|
|
271
|
+
historicalAccepted
|
|
272
|
+
&& Array.isArray(expectedReleasedLeaseIds)
|
|
273
|
+
&& JSON.stringify(releasedWorkItemIds)
|
|
274
|
+
!== JSON.stringify([...expectedReleasedLeaseIds].sort(compareCodeUnits))
|
|
275
|
+
) {
|
|
276
|
+
issues.push(`Historical accepted released lease binding mismatch: ${runId}`);
|
|
277
|
+
}
|
|
235
278
|
return {
|
|
236
279
|
status: issues.length > 0
|
|
237
280
|
? "invalid"
|
|
@@ -337,6 +380,11 @@ function inspectCloseout(root, executionRoot, runId, pin, contract) {
|
|
|
337
380
|
}
|
|
338
381
|
: {}),
|
|
339
382
|
core: issues.length === 0 ? closeout.artifact.core : null,
|
|
383
|
+
releasedLeaseIds: issues.length === 0
|
|
384
|
+
&& payload.decision === "accepted"
|
|
385
|
+
&& Array.isArray(payload.verification?.releasedLeaseIds)
|
|
386
|
+
? [...payload.verification.releasedLeaseIds]
|
|
387
|
+
: null,
|
|
340
388
|
issues,
|
|
341
389
|
};
|
|
342
390
|
}
|
|
@@ -746,7 +794,18 @@ export function parseProjectControlState({
|
|
|
746
794
|
&& contract.validateExecutionPin({ expected: pin, actual: currentCore }).status !== "valid";
|
|
747
795
|
const preserveHistoricalLeases = lifecycle === "closed"
|
|
748
796
|
&& new Set(["blocked", "rejected"]).has(closeout.decision);
|
|
749
|
-
const inspectedLease = inspectLeases(
|
|
797
|
+
const inspectedLease = inspectLeases(
|
|
798
|
+
root,
|
|
799
|
+
executionRoot,
|
|
800
|
+
runId,
|
|
801
|
+
contract,
|
|
802
|
+
{
|
|
803
|
+
historicalAccepted: historical
|
|
804
|
+
&& lifecycle === "closed"
|
|
805
|
+
&& closeout.decision === "accepted",
|
|
806
|
+
expectedReleasedLeaseIds: closeout.releasedLeaseIds,
|
|
807
|
+
},
|
|
808
|
+
);
|
|
750
809
|
const lease = preserveHistoricalLeases && inspectedLease.status !== "invalid"
|
|
751
810
|
? {
|
|
752
811
|
...inspectedLease,
|
|
@@ -65,6 +65,7 @@ import {
|
|
|
65
65
|
import {
|
|
66
66
|
finalizeFormalChecksV1,
|
|
67
67
|
recordFormalCheckV1,
|
|
68
|
+
recordFormalEnvelopeCheckV1,
|
|
68
69
|
resumeCompletedFormalFinishV1,
|
|
69
70
|
validateLegacyVerificationSideEffectsV1,
|
|
70
71
|
} from "./formal-workflow.mjs";
|
|
@@ -93,7 +94,10 @@ import {
|
|
|
93
94
|
} from "./profile-onboarding.mjs";
|
|
94
95
|
import {
|
|
95
96
|
discoverFleet,
|
|
97
|
+
inspectFleetRegistry,
|
|
96
98
|
registerFleetCoverageRoot,
|
|
99
|
+
replaceFleetRegistry,
|
|
100
|
+
rollbackFleetRegistry,
|
|
97
101
|
} from "./fleet-discovery.mjs";
|
|
98
102
|
import { routeRunKitAssuranceV1 } from "./assurance-router.mjs";
|
|
99
103
|
import { deriveHumanStatusFromInspectV1 } from "./human-status.mjs";
|
|
@@ -132,6 +136,13 @@ import {
|
|
|
132
136
|
runRegistryAdoption,
|
|
133
137
|
} from "./registry-adoption.mjs";
|
|
134
138
|
import { OFFICIAL_NPM_REGISTRY } from "./registry-adoption-gate.mjs";
|
|
139
|
+
import {
|
|
140
|
+
appendTeamProjectEventV1,
|
|
141
|
+
buildTeamProjectTakeoverV1,
|
|
142
|
+
formatTeamProjectStatusHumanV1,
|
|
143
|
+
initializeTeamProjectV1,
|
|
144
|
+
readTeamProjectStatusV1,
|
|
145
|
+
} from "./team-project.mjs";
|
|
135
146
|
|
|
136
147
|
const TOP_LEVEL_HELP = `OwlRunKit ${CORE_VERSION}
|
|
137
148
|
|
|
@@ -162,20 +173,21 @@ Two-stage deployment
|
|
|
162
173
|
owlrunkit deployment execute --workspace . [--resume] --prepare <receipt> --owner-decision <decision-v1.json> --owner-authority <signed-v2.json> --manifest <remote.json>
|
|
163
174
|
|
|
164
175
|
Fleet and status
|
|
165
|
-
owlrunkit fleet register-root
|
|
166
|
-
owlrunkit fleet
|
|
167
|
-
owlrunkit
|
|
168
|
-
|
|
176
|
+
owlrunkit fleet <register-root|inspect-registry> [options]
|
|
177
|
+
owlrunkit fleet replace-registry --request <file> --receipt <file> [--dry-run]
|
|
178
|
+
owlrunkit fleet rollback-registry --receipt <file> --rollback-receipt <file>
|
|
179
|
+
owlrunkit fleet discover; owlrunkit status --workspace .
|
|
180
|
+
|
|
181
|
+
Agent team projects
|
|
182
|
+
owlrunkit project <init|assign|checkpoint|handoff|decision|capture|status|takeover> [options]
|
|
183
|
+
Durable Agent work, dependencies, decisions, gates, progress, and handoffs.
|
|
169
184
|
Core successor
|
|
170
185
|
owlrunkit core-successor <plan|apply|resume> --workspace . [...]
|
|
171
186
|
|
|
172
187
|
Other onboarding commands:
|
|
173
|
-
owlrunkit profiles validate --workspace .
|
|
174
|
-
owlrunkit profiles impact --workspace . --changed <path>
|
|
188
|
+
owlrunkit profiles validate --workspace .; owlrunkit profiles impact --workspace . --changed <path>
|
|
175
189
|
owlrunkit inspect --workspace . --json
|
|
176
|
-
|
|
177
|
-
Quick receipts cannot become Formal acceptance.
|
|
178
|
-
Formal accepted does not grant Git or release authority.
|
|
190
|
+
Formal accepted does not grant Git or release authority; Quick cannot become Formal.
|
|
179
191
|
`;
|
|
180
192
|
|
|
181
193
|
const COMMAND_HELP = Object.freeze({
|
|
@@ -193,12 +205,33 @@ Required:
|
|
|
193
205
|
`,
|
|
194
206
|
fleet: `Usage:
|
|
195
207
|
owlrunkit fleet register-root --fleet-root <directory>
|
|
208
|
+
owlrunkit fleet inspect-registry [--registry <file>]
|
|
209
|
+
owlrunkit fleet replace-registry --request <file> --receipt <file> [--registry <file>] [--dry-run]
|
|
210
|
+
owlrunkit fleet rollback-registry --receipt <file> --rollback-receipt <file> [--registry <file>]
|
|
196
211
|
owlrunkit fleet discover [--fleet-root <directory>] [--fleet-manifest <file>]
|
|
197
212
|
`,
|
|
198
213
|
"fleet register-root": `Usage:
|
|
199
214
|
owlrunkit fleet register-root --fleet-root <directory>
|
|
200
215
|
|
|
201
216
|
Registers one durable fleet coverage root for later no-argument discovery.
|
|
217
|
+
`,
|
|
218
|
+
"fleet inspect-registry": `Usage:
|
|
219
|
+
owlrunkit fleet inspect-registry [--registry <file>]
|
|
220
|
+
|
|
221
|
+
Reads the exact registry bytes, hashes, roots, and membership without mutation.
|
|
222
|
+
`,
|
|
223
|
+
"fleet replace-registry": `Usage:
|
|
224
|
+
owlrunkit fleet replace-registry --request <file> --receipt <file>
|
|
225
|
+
[--registry <file>] [--dry-run]
|
|
226
|
+
|
|
227
|
+
Atomically replaces a registry from an exact preimage and evidence-bound fleet
|
|
228
|
+
membership. Coverage reduction requires explicit exclusion evidence.
|
|
229
|
+
`,
|
|
230
|
+
"fleet rollback-registry": `Usage:
|
|
231
|
+
owlrunkit fleet rollback-registry --receipt <replacement-receipt.json>
|
|
232
|
+
--rollback-receipt <file> [--registry <file>]
|
|
233
|
+
|
|
234
|
+
Restores the exact preimage recorded by a replacement receipt.
|
|
202
235
|
`,
|
|
203
236
|
"fleet discover": `Usage:
|
|
204
237
|
owlrunkit fleet discover [--fleet-root <directory>] [--fleet-manifest <file>]
|
|
@@ -230,7 +263,12 @@ Creates the execution and exact writer lease.
|
|
|
230
263
|
`,
|
|
231
264
|
"formal check": `Usage:
|
|
232
265
|
owlrunkit formal check --workspace <directory> --run-id <id>
|
|
233
|
-
--work-item <id> --check-id <id> --
|
|
266
|
+
--work-item <id> --check-id <id> --envelope <verification-envelope.json>
|
|
267
|
+
owlrunkit formal check --workspace <directory> --run-id <id>
|
|
268
|
+
--work-item <id> --check-id <id> --cwd <directory> -- <built-in-command> [args...]
|
|
269
|
+
|
|
270
|
+
Verification Envelopes admit real project commands only when file, network,
|
|
271
|
+
process, environment, output, timeout, and cleanup constraints are enforced.
|
|
234
272
|
`,
|
|
235
273
|
"formal finish": `Usage:
|
|
236
274
|
owlrunkit formal finish --workspace <directory> --run-id <id>
|
|
@@ -299,6 +337,24 @@ profile, preflight, lineage, and result. --request is an advanced legacy path.
|
|
|
299
337
|
|
|
300
338
|
Shows completed milestones, current state, remaining gates, and the next allowed
|
|
301
339
|
action without granting any authority.
|
|
340
|
+
`,
|
|
341
|
+
project: `Usage:
|
|
342
|
+
owlrunkit project init --workspace <directory> --definition <project.json>
|
|
343
|
+
owlrunkit project <assign|checkpoint|handoff|decision|capture> --workspace <directory> --request <event.json>
|
|
344
|
+
owlrunkit project status --workspace <directory> [--json]
|
|
345
|
+
owlrunkit project takeover --workspace <directory> --agent <id> [--json]
|
|
346
|
+
|
|
347
|
+
Project status is derived from durable work items, Agent assignments,
|
|
348
|
+
dependencies, decisions, checkpoints, integration gates, and evidence refs.
|
|
349
|
+
No self-reported percentage is accepted.
|
|
350
|
+
`,
|
|
351
|
+
"project status": `Usage:
|
|
352
|
+
owlrunkit project status --workspace <directory> [--json]
|
|
353
|
+
`,
|
|
354
|
+
"project takeover": `Usage:
|
|
355
|
+
owlrunkit project takeover --workspace <directory> --agent <id> [--json]
|
|
356
|
+
|
|
357
|
+
Builds a read-only blank-session recovery packet for one Agent.
|
|
302
358
|
`,
|
|
303
359
|
});
|
|
304
360
|
|
|
@@ -1564,6 +1620,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1564
1620
|
"formal",
|
|
1565
1621
|
"lease",
|
|
1566
1622
|
"profiles",
|
|
1623
|
+
"project",
|
|
1567
1624
|
]).has(command);
|
|
1568
1625
|
const [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
|
|
1569
1626
|
const helpRequested = optionValues.includes("--help")
|
|
@@ -1598,10 +1655,14 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1598
1655
|
? ["json", "verbose", "history"]
|
|
1599
1656
|
: command === "init"
|
|
1600
1657
|
? ["json"]
|
|
1658
|
+
: command === "project" && new Set(["status", "takeover"]).has(action)
|
|
1659
|
+
? ["json"]
|
|
1601
1660
|
: command === "doctor" || command === "adopt" || command === "status"
|
|
1602
1661
|
? ["json"]
|
|
1603
1662
|
: command === "profiles"
|
|
1604
1663
|
? ["json", "dry-run", "apply"]
|
|
1664
|
+
: command === "fleet" && action === "replace-registry"
|
|
1665
|
+
? ["dry-run"]
|
|
1605
1666
|
: command === "candidate"
|
|
1606
1667
|
? ["delivery-v1"]
|
|
1607
1668
|
: command === "core-successor" && action === "resume"
|
|
@@ -1651,19 +1712,50 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1651
1712
|
if (action === "register-root") {
|
|
1652
1713
|
const registered = await registerFleetCoverageRoot({
|
|
1653
1714
|
fleetRoot: requireOption(options, "fleet-root"),
|
|
1715
|
+
fleetRegistryPath: options.registry,
|
|
1654
1716
|
});
|
|
1655
1717
|
return {
|
|
1656
1718
|
...registered,
|
|
1657
1719
|
exitCode: 0,
|
|
1658
1720
|
};
|
|
1659
1721
|
}
|
|
1722
|
+
if (action === "inspect-registry") {
|
|
1723
|
+
return {
|
|
1724
|
+
...await inspectFleetRegistry({
|
|
1725
|
+
fleetRegistryPath: options.registry,
|
|
1726
|
+
}),
|
|
1727
|
+
exitCode: 0,
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
if (action === "replace-registry") {
|
|
1731
|
+
return {
|
|
1732
|
+
...await replaceFleetRegistry({
|
|
1733
|
+
replacementRequestPath: requireOption(options, "request"),
|
|
1734
|
+
receiptPath: options.receipt,
|
|
1735
|
+
dryRun: options["dry-run"] === true,
|
|
1736
|
+
fleetRegistryPath: options.registry,
|
|
1737
|
+
}),
|
|
1738
|
+
exitCode: 0,
|
|
1739
|
+
};
|
|
1740
|
+
}
|
|
1741
|
+
if (action === "rollback-registry") {
|
|
1742
|
+
return {
|
|
1743
|
+
...await rollbackFleetRegistry({
|
|
1744
|
+
replacementReceiptPath: requireOption(options, "receipt"),
|
|
1745
|
+
rollbackReceiptPath: requireOption(options, "rollback-receipt"),
|
|
1746
|
+
fleetRegistryPath: options.registry,
|
|
1747
|
+
}),
|
|
1748
|
+
exitCode: 0,
|
|
1749
|
+
};
|
|
1750
|
+
}
|
|
1660
1751
|
if (action !== "discover") {
|
|
1661
|
-
throw new Error("Usage: owlrunkit fleet <register-root|
|
|
1752
|
+
throw new Error("Usage: owlrunkit fleet <register-root|inspect-registry|replace-registry|rollback-registry|discover>");
|
|
1662
1753
|
}
|
|
1663
1754
|
const discovered = await discoverFleet({
|
|
1664
1755
|
fleetRoots: options["fleet-root"] ?? null,
|
|
1665
1756
|
fleetManifestPath: options["fleet-manifest"] ?? null,
|
|
1666
1757
|
workspaceRoots: options["workspace-root"] ?? null,
|
|
1758
|
+
fleetRegistryPath: options.registry ?? null,
|
|
1667
1759
|
});
|
|
1668
1760
|
return {
|
|
1669
1761
|
...discovered,
|
|
@@ -2172,10 +2264,30 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
2172
2264
|
});
|
|
2173
2265
|
}
|
|
2174
2266
|
if (action === "check") {
|
|
2267
|
+
const workItemId = options["work-item"] ?? options["from-lease"] ?? "delivery";
|
|
2268
|
+
if (
|
|
2269
|
+
options["work-item"] !== undefined
|
|
2270
|
+
&& options["from-lease"] !== undefined
|
|
2271
|
+
&& options["work-item"] !== options["from-lease"]
|
|
2272
|
+
) {
|
|
2273
|
+
throw new Error("formal check work-item and legacy from-lease must match.");
|
|
2274
|
+
}
|
|
2275
|
+
if (options.envelope !== undefined) {
|
|
2276
|
+
if (commandArgv.length > 0) {
|
|
2277
|
+
throw new Error("formal check accepts either --envelope or an exact built-in command, not both.");
|
|
2278
|
+
}
|
|
2279
|
+
return await recordFormalEnvelopeCheckV1({
|
|
2280
|
+
workspaceRoot: workspace(options),
|
|
2281
|
+
runId: safeRunId(requireOption(options, "run-id")),
|
|
2282
|
+
workItemId,
|
|
2283
|
+
checkId: requireOption(options, "check-id"),
|
|
2284
|
+
envelope: readJson(realpathSync(requireOption(options, "envelope"))),
|
|
2285
|
+
});
|
|
2286
|
+
}
|
|
2175
2287
|
return recordFormalCheckV1({
|
|
2176
2288
|
workspaceRoot: workspace(options),
|
|
2177
2289
|
runId: safeRunId(requireOption(options, "run-id")),
|
|
2178
|
-
workItemId
|
|
2290
|
+
workItemId,
|
|
2179
2291
|
checkId: requireOption(options, "check-id"),
|
|
2180
2292
|
cwd: options.cwd ?? ".",
|
|
2181
2293
|
commandArgv,
|
|
@@ -2292,6 +2404,43 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
2292
2404
|
].join("\n"),
|
|
2293
2405
|
);
|
|
2294
2406
|
}
|
|
2407
|
+
if (command === "project") {
|
|
2408
|
+
const root = requireOption(options, "workspace");
|
|
2409
|
+
if (action === "init") {
|
|
2410
|
+
return initializeTeamProjectV1({
|
|
2411
|
+
workspaceRoot: root,
|
|
2412
|
+
definition: readJson(realpathSync(requireOption(options, "definition"))),
|
|
2413
|
+
});
|
|
2414
|
+
}
|
|
2415
|
+
if (new Set(["assign", "checkpoint", "handoff", "decision", "capture"]).has(action)) {
|
|
2416
|
+
return appendTeamProjectEventV1({
|
|
2417
|
+
workspaceRoot: root,
|
|
2418
|
+
event: readJson(realpathSync(requireOption(options, "request"))),
|
|
2419
|
+
});
|
|
2420
|
+
}
|
|
2421
|
+
if (action === "status") {
|
|
2422
|
+
const status = readTeamProjectStatusV1({ workspaceRoot: root });
|
|
2423
|
+
return options.json === true
|
|
2424
|
+
? status
|
|
2425
|
+
: withHumanOutput(status, formatTeamProjectStatusHumanV1(status));
|
|
2426
|
+
}
|
|
2427
|
+
if (action === "takeover") {
|
|
2428
|
+
const takeover = buildTeamProjectTakeoverV1({
|
|
2429
|
+
workspaceRoot: root,
|
|
2430
|
+
agentId: requireOption(options, "agent"),
|
|
2431
|
+
});
|
|
2432
|
+
return options.json === true
|
|
2433
|
+
? takeover
|
|
2434
|
+
: withHumanOutput(takeover, [
|
|
2435
|
+
`${takeover.projectId}: ${takeover.objective}`,
|
|
2436
|
+
`Agent: ${takeover.agentId}`,
|
|
2437
|
+
`Responsibility: ${takeover.currentResponsibility?.workItemId ?? "none"}`,
|
|
2438
|
+
`Next: ${takeover.nextAction ?? "none"}`,
|
|
2439
|
+
"",
|
|
2440
|
+
].join("\n"));
|
|
2441
|
+
}
|
|
2442
|
+
throw new Error("Usage: owlrunkit project <init|assign|checkpoint|handoff|decision|capture|status|takeover> [options]");
|
|
2443
|
+
}
|
|
2295
2444
|
if (command === "profiles") {
|
|
2296
2445
|
const root = requireOption(options, "workspace");
|
|
2297
2446
|
if (action === "detect") {
|
|
@@ -2455,7 +2604,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
2455
2604
|
return withControlTransaction(root, () => closeout(options));
|
|
2456
2605
|
}
|
|
2457
2606
|
if (command === "finish") return finishExecution(options);
|
|
2458
|
-
throw new Error("Usage: owlrunkit <init|doctor|adopt|assurance|fleet|profiles|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
|
|
2607
|
+
throw new Error("Usage: owlrunkit <init|doctor|adopt|assurance|fleet|profiles|project|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
|
|
2459
2608
|
} catch (error) {
|
|
2460
2609
|
return {
|
|
2461
2610
|
status: "invalid_input",
|