owlrunkit 0.18.4 → 0.19.1
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 +49 -0
- package/README.md +109 -4
- package/docs/architecture/OWLCODA_RUNKIT_0190_CONSOLIDATED_DAILY_USE_PRODUCT_DECISION_20260812.md +100 -0
- package/docs/architecture/OWLCODA_RUNKIT_0191_PROFILE_RECONCILE_PATCH_PRODUCT_DECISION_20260813.md +30 -0
- package/docs/architecture/OWLCODA_RUN_KIT_CONTRACT_V0_2.md +57 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +28 -4
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/integrations/codex/skills/owlcoda-runkit/references/contract-v0.2.md +57 -0
- package/package.json +1 -1
- package/packages/attest/README.md +9 -5
- package/packages/attest/consumer-surface-v2.json +263 -0
- package/packages/attest/distribution-manifest-v1.json +14 -1
- package/packages/attest/schemas/quick-verification-receipt-v3.schema.json +244 -0
- package/packages/attest/schemas/workspace-snapshot-v2.schema.json +114 -0
- package/packages/attest/src/dependency-environment.mjs +128 -0
- package/packages/attest/src/index.mjs +1 -0
- package/packages/attest/src/quick-receipt-contract.mjs +202 -9
- package/packages/attest/src/quick.mjs +45 -4
- package/packages/attest/src/workspace-snapshot.mjs +133 -6
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/core-contract.mjs +21 -1
- package/scripts/runkit-contract/efficiency-metrics.mjs +559 -0
- package/scripts/runkit-contract/evidence-store.mjs +130 -0
- package/scripts/runkit-contract/onboarding-bootstrap.mjs +685 -0
- package/scripts/runkit-contract/onboarding-doctor.mjs +2 -1
- package/scripts/runkit-contract/outcome-presentation.mjs +171 -0
- package/scripts/runkit-contract/profile-onboarding.mjs +74 -35
- package/scripts/runkit-contract/project-cli-resolver.mjs +26 -5
- package/scripts/runkit-contract/quick-evidence-reuse.mjs +590 -0
- package/scripts/runkit-contract/quick-isolation.mjs +314 -0
- package/scripts/runkit-contract/quick-metrics.mjs +51 -3
- package/scripts/runkit-contract/quick-receipt.mjs +31 -1
- package/scripts/runkit-contract/quick-verify.mjs +216 -25
- package/scripts/runkit-contract/quick-workspace-snapshot.mjs +1 -0
- package/scripts/runkit-contract/runkit-bootstrap.mjs +1 -0
- package/scripts/runkit-contract/runkit-cli.mjs +330 -18
- package/scripts/runkit-contract/runtime-transition.mjs +478 -0
- package/scripts/runkit-contract/schemas/efficiency-event-v1.schema.json +56 -0
- package/scripts/runkit-contract/schemas/efficiency-metrics-v1.schema.json +220 -0
- package/scripts/runkit-contract/schemas/external-runtime-report-v1.schema.json +125 -0
- package/scripts/runkit-contract/schemas/onboarding-bootstrap-plan-v1.schema.json +168 -0
- package/scripts/runkit-contract/schemas/onboarding-bootstrap-receipt-v1.schema.json +130 -0
- package/scripts/runkit-contract/schemas/outcome-summary-v1.schema.json +83 -0
- package/scripts/runkit-contract/schemas/quick-evidence-reuse-plan-v1.schema.json +59 -0
- package/scripts/runkit-contract/schemas/quick-evidence-reuse-receipt-v1.schema.json +79 -0
- package/scripts/runkit-contract/schemas/runtime-transition-receipt-v1.schema.json +185 -0
- package/scripts/runkit-contract/schemas/team-project-event-v4.schema.json +142 -0
- package/scripts/runkit-contract/team-project.mjs +704 -16
- package/scripts/runkit-contract/verification-envelope.mjs +15 -18
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const RUNTIME_LEVEL_BY_DECISION = Object.freeze({
|
|
2
|
+
external_transition_passed: "reported_passed",
|
|
3
|
+
external_transition_failed_recovered: "reported_failed_recovered",
|
|
4
|
+
external_transition_failed_unrecovered: "reported_failed_unrecovered",
|
|
5
|
+
external_transition_indeterminate: "reported_indeterminate",
|
|
6
|
+
external_transition_invalidated: "reported_indeterminate",
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
function verificationLevel(result) {
|
|
10
|
+
const status = result.quickVerificationStatus ?? result.status;
|
|
11
|
+
if (status === "quick_verification_passed") return "passed";
|
|
12
|
+
if (status === "quick_verification_failed") return "failed";
|
|
13
|
+
if (status === "quick_verification_source_mutated") return "invalidated";
|
|
14
|
+
return "not_proven";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function workspaceLevel(result) {
|
|
18
|
+
if (result.mutationDecision === "source_unchanged") return "unchanged";
|
|
19
|
+
if (typeof result.mutationDecision === "string") return "changed";
|
|
20
|
+
return "not_proven";
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function candidateLevel(result) {
|
|
24
|
+
if (result.attestationDecision === "NO_GO") return "not_proven";
|
|
25
|
+
const verification = verificationLevel(result);
|
|
26
|
+
if (verification === "invalidated" || workspaceLevel(result) === "changed") {
|
|
27
|
+
return "invalidated";
|
|
28
|
+
}
|
|
29
|
+
return typeof result.receiptPath === "string" && workspaceLevel(result) === "unchanged"
|
|
30
|
+
? "bound"
|
|
31
|
+
: "not_proven";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function runtimeLevel(result) {
|
|
35
|
+
const transition = result.runtimeTransition;
|
|
36
|
+
if (!transition || transition.status === "runtime_transition_invalid") return "not_proven";
|
|
37
|
+
return RUNTIME_LEVEL_BY_DECISION[transition.transitionDecision] ?? "reported_indeterminate";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resultDecision(result) {
|
|
41
|
+
if (candidateLevel(result) === "invalidated") return "invalidated";
|
|
42
|
+
if (result.attestationDecision === "NO_GO"
|
|
43
|
+
|| [
|
|
44
|
+
"quick_verification_control_invalid",
|
|
45
|
+
"quick_verification_input_invalid",
|
|
46
|
+
"quick_verification_runtime_truth_invalid",
|
|
47
|
+
].includes(result.status)) {
|
|
48
|
+
return "indeterminate";
|
|
49
|
+
}
|
|
50
|
+
const verification = verificationLevel(result);
|
|
51
|
+
if (verification === "passed") return "pass";
|
|
52
|
+
if (verification === "failed") return "fail";
|
|
53
|
+
return "indeterminate";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function headline(result, acceptanceLevels) {
|
|
57
|
+
const decision = resultDecision(result);
|
|
58
|
+
if (decision === "invalidated") {
|
|
59
|
+
return "The verification candidate was invalidated by a workspace or execution-environment change.";
|
|
60
|
+
}
|
|
61
|
+
if (decision === "fail") {
|
|
62
|
+
return "The exact verification command failed for the bound source candidate.";
|
|
63
|
+
}
|
|
64
|
+
if (decision === "indeterminate") {
|
|
65
|
+
return "RunKit did not establish a reusable verification result.";
|
|
66
|
+
}
|
|
67
|
+
if (acceptanceLevels.deploymentRuntime === "reported_failed_recovered") {
|
|
68
|
+
return "Verification passed, and the exact command reported a failed external transition with successful rollback.";
|
|
69
|
+
}
|
|
70
|
+
if (acceptanceLevels.deploymentRuntime === "reported_failed_unrecovered") {
|
|
71
|
+
return "Verification passed, but the exact command reported an unrecovered external transition failure.";
|
|
72
|
+
}
|
|
73
|
+
if (acceptanceLevels.deploymentRuntime === "reported_passed") {
|
|
74
|
+
return "Verification passed, and the exact command reported a successful external transition.";
|
|
75
|
+
}
|
|
76
|
+
return "The exact verification command passed for the bound source candidate.";
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function projectQuickOutcomeV1(result) {
|
|
80
|
+
const acceptanceLevels = {
|
|
81
|
+
candidateIdentity: candidateLevel(result),
|
|
82
|
+
workspaceAttestation: workspaceLevel(result),
|
|
83
|
+
verificationCommand: verificationLevel(result),
|
|
84
|
+
productExperience: "not_proven",
|
|
85
|
+
deploymentRuntime: runtimeLevel(result),
|
|
86
|
+
productionBusiness: "not_proven",
|
|
87
|
+
};
|
|
88
|
+
const proven = [];
|
|
89
|
+
if (acceptanceLevels.candidateIdentity === "bound") {
|
|
90
|
+
proven.push("Quick evidence binds the exact command to the captured source candidate.");
|
|
91
|
+
}
|
|
92
|
+
if (acceptanceLevels.workspaceAttestation === "unchanged") {
|
|
93
|
+
proven.push("The bound workspace state remained unchanged during the command.");
|
|
94
|
+
}
|
|
95
|
+
if (acceptanceLevels.verificationCommand === "passed") {
|
|
96
|
+
proven.push("The exact verification command exited successfully.");
|
|
97
|
+
} else if (acceptanceLevels.verificationCommand === "failed") {
|
|
98
|
+
proven.push("The exact verification command failure was preserved.");
|
|
99
|
+
}
|
|
100
|
+
if (result.attestationDecision === "GO") {
|
|
101
|
+
proven.push("The Quick receipt attestation decision is GO.");
|
|
102
|
+
}
|
|
103
|
+
if (acceptanceLevels.deploymentRuntime !== "not_proven") {
|
|
104
|
+
proven.push("The exact command supplied a bounded external-runtime transition report.");
|
|
105
|
+
}
|
|
106
|
+
const notProven = [
|
|
107
|
+
...(result.attestationDecision === "NO_GO"
|
|
108
|
+
? ["Quick attestation did not establish a reusable candidate binding."]
|
|
109
|
+
: []),
|
|
110
|
+
"Product or UX acceptance was not performed by Quick Verification.",
|
|
111
|
+
...(acceptanceLevels.deploymentRuntime === "not_proven"
|
|
112
|
+
? ["Deployment or runtime activation was not proven."]
|
|
113
|
+
: ["RunKit did not independently observe the external runtime beyond the exact command report."]),
|
|
114
|
+
"Production or business effectiveness was not proven.",
|
|
115
|
+
"No Git, release, deployment, production, or business authority was granted.",
|
|
116
|
+
];
|
|
117
|
+
return {
|
|
118
|
+
schemaVersion: "OwlCodaRunKitOutcomeSummaryV1",
|
|
119
|
+
result: resultDecision(result),
|
|
120
|
+
headline: headline(result, acceptanceLevels),
|
|
121
|
+
proofLevel: typeof result.receiptPath === "string"
|
|
122
|
+
? "captured_verification"
|
|
123
|
+
: "no_reusable_evidence",
|
|
124
|
+
acceptanceLevels,
|
|
125
|
+
proven,
|
|
126
|
+
notProven,
|
|
127
|
+
nextAction: typeof result.nextAllowedAction === "string"
|
|
128
|
+
? result.nextAllowedAction
|
|
129
|
+
: "inspect_result",
|
|
130
|
+
authorizationGranted: false,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function display(value) {
|
|
135
|
+
return value.replaceAll("_", " ");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function humanNextAction(nextAction) {
|
|
139
|
+
if (nextAction.includes(" quick-attest ")) return "Attest this Quick result.";
|
|
140
|
+
const friendly = {
|
|
141
|
+
consume_attestation: "Use the attested verification evidence at the next named gate.",
|
|
142
|
+
inspect_attestation_issues: "Inspect the attestation issues before relying on this result.",
|
|
143
|
+
inspect_output_and_rerun: "Inspect the failed command output, repair the cause, and rerun.",
|
|
144
|
+
restore_source_or_run_a_new_quick_verification: "Restore or freeze the intended candidate, then run a new verification.",
|
|
145
|
+
repair_runtime_report_and_run_a_new_quick_verification: "Repair the runtime report and run a new verification.",
|
|
146
|
+
};
|
|
147
|
+
return friendly[nextAction] ?? nextAction.replaceAll("_", " ");
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function formatQuickOutcomeHumanV1(outcome) {
|
|
151
|
+
const label = {
|
|
152
|
+
pass: "PASS",
|
|
153
|
+
fail: "FAIL",
|
|
154
|
+
invalidated: "INVALIDATED",
|
|
155
|
+
indeterminate: "INDETERMINATE",
|
|
156
|
+
}[outcome.result];
|
|
157
|
+
const levels = outcome.acceptanceLevels;
|
|
158
|
+
return [
|
|
159
|
+
`Result: ${label} — ${outcome.headline}`,
|
|
160
|
+
`Proved: ${outcome.proven.length > 0 ? outcome.proven.join(" ") : "No reusable verification claim."}`,
|
|
161
|
+
"Acceptance: "
|
|
162
|
+
+ `Candidate: ${display(levels.candidateIdentity)}; `
|
|
163
|
+
+ `workspace: ${display(levels.workspaceAttestation)}; `
|
|
164
|
+
+ `verification: ${display(levels.verificationCommand)}; `
|
|
165
|
+
+ "Product/UX: not proven; "
|
|
166
|
+
+ `Deployment/runtime: ${display(levels.deploymentRuntime)}; `
|
|
167
|
+
+ "Production/business: not proven.",
|
|
168
|
+
`Next: ${humanNextAction(outcome.nextAction)}`,
|
|
169
|
+
"Authority: no Git, release, deployment, production, or business authority granted.",
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
@@ -1440,9 +1440,47 @@ export function applyDetectedProfilesV3(input = {}) {
|
|
|
1440
1440
|
});
|
|
1441
1441
|
}
|
|
1442
1442
|
|
|
1443
|
+
function mergeDetectedProfileDefinition(current, detected) {
|
|
1444
|
+
current.commands ??= [];
|
|
1445
|
+
const addedPaths = detected.paths
|
|
1446
|
+
.filter((value) => !current.paths.includes(value))
|
|
1447
|
+
.sort();
|
|
1448
|
+
current.paths = [...new Set([...current.paths, ...detected.paths])].sort();
|
|
1449
|
+
const commands = new Map(current.commands.map((entry) => [entry.id, entry]));
|
|
1450
|
+
const addedCommandIds = [];
|
|
1451
|
+
const updatedCommandIds = [];
|
|
1452
|
+
for (const detectedCommand of detected.commands) {
|
|
1453
|
+
const currentCommand = commands.get(detectedCommand.id);
|
|
1454
|
+
if (!currentCommand) {
|
|
1455
|
+
current.commands.push(structuredClone(detectedCommand));
|
|
1456
|
+
commands.set(detectedCommand.id, detectedCommand);
|
|
1457
|
+
addedCommandIds.push(detectedCommand.id);
|
|
1458
|
+
} else if (canonicalJson(currentCommand) !== canonicalJson(detectedCommand)) {
|
|
1459
|
+
const index = current.commands.findIndex((entry) => entry.id === detectedCommand.id);
|
|
1460
|
+
current.commands[index] = structuredClone(detectedCommand);
|
|
1461
|
+
commands.set(detectedCommand.id, detectedCommand);
|
|
1462
|
+
updatedCommandIds.push(detectedCommand.id);
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
current.commands.sort((left, right) => left.id.localeCompare(right.id));
|
|
1466
|
+
if (
|
|
1467
|
+
addedPaths.length === 0
|
|
1468
|
+
&& addedCommandIds.length === 0
|
|
1469
|
+
&& updatedCommandIds.length === 0
|
|
1470
|
+
) return null;
|
|
1471
|
+
return {
|
|
1472
|
+
profileId: current.id,
|
|
1473
|
+
addedPaths,
|
|
1474
|
+
addedCommandIds: addedCommandIds.sort(),
|
|
1475
|
+
updatedCommandIds: updatedCommandIds.sort(),
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1443
1479
|
function mergeDetectedProfiles(currentProfiles, detectedProfiles) {
|
|
1444
1480
|
const profiles = currentProfiles.profiles.map((profile) => structuredClone(profile));
|
|
1445
1481
|
const byId = new Map(profiles.map((profile) => [profile.id, profile]));
|
|
1482
|
+
const commandOwners = new Map(profiles.flatMap((profile) =>
|
|
1483
|
+
(profile.commands ?? []).map((entry) => [entry.id, profile.id])));
|
|
1446
1484
|
const addedProfileIds = [];
|
|
1447
1485
|
const updatedProfiles = [];
|
|
1448
1486
|
for (const detected of [...detectedProfiles.profiles]
|
|
@@ -1456,45 +1494,26 @@ function mergeDetectedProfiles(currentProfiles, detectedProfiles) {
|
|
|
1456
1494
|
const primaryCoverageComplete = impact.uncoveredPaths.length === 0
|
|
1457
1495
|
&& !impact.warnings.some((warning) => warning.startsWith("supporting_only_match:"));
|
|
1458
1496
|
if (primaryCoverageComplete) continue;
|
|
1497
|
+
const existingCommandOwnerIds = [...new Set(detected.commands
|
|
1498
|
+
.map((command) => commandOwners.get(command.id))
|
|
1499
|
+
.filter(Boolean))];
|
|
1500
|
+
if (existingCommandOwnerIds.length === 1) {
|
|
1501
|
+
const owner = byId.get(existingCommandOwnerIds[0]);
|
|
1502
|
+
const update = mergeDetectedProfileDefinition(owner, detected);
|
|
1503
|
+
if (update !== null) updatedProfiles.push(update);
|
|
1504
|
+
for (const command of owner.commands) commandOwners.set(command.id, owner.id);
|
|
1505
|
+
continue;
|
|
1506
|
+
}
|
|
1459
1507
|
const added = { ...structuredClone(detected), primary: false };
|
|
1460
1508
|
profiles.push(added);
|
|
1461
1509
|
byId.set(added.id, added);
|
|
1510
|
+
for (const command of added.commands) commandOwners.set(command.id, added.id);
|
|
1462
1511
|
addedProfileIds.push(added.id);
|
|
1463
1512
|
continue;
|
|
1464
1513
|
}
|
|
1465
|
-
const
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
current.paths = [...new Set([...current.paths, ...detected.paths])].sort();
|
|
1469
|
-
const commands = new Map(current.commands.map((entry) => [entry.id, entry]));
|
|
1470
|
-
const addedCommandIds = [];
|
|
1471
|
-
const updatedCommandIds = [];
|
|
1472
|
-
for (const detectedCommand of detected.commands) {
|
|
1473
|
-
const currentCommand = commands.get(detectedCommand.id);
|
|
1474
|
-
if (!currentCommand) {
|
|
1475
|
-
current.commands.push(structuredClone(detectedCommand));
|
|
1476
|
-
commands.set(detectedCommand.id, detectedCommand);
|
|
1477
|
-
addedCommandIds.push(detectedCommand.id);
|
|
1478
|
-
} else if (canonicalJson(currentCommand) !== canonicalJson(detectedCommand)) {
|
|
1479
|
-
const index = current.commands.findIndex((entry) => entry.id === detectedCommand.id);
|
|
1480
|
-
current.commands[index] = structuredClone(detectedCommand);
|
|
1481
|
-
commands.set(detectedCommand.id, detectedCommand);
|
|
1482
|
-
updatedCommandIds.push(detectedCommand.id);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
current.commands.sort((left, right) => left.id.localeCompare(right.id));
|
|
1486
|
-
if (
|
|
1487
|
-
addedPaths.length > 0
|
|
1488
|
-
|| addedCommandIds.length > 0
|
|
1489
|
-
|| updatedCommandIds.length > 0
|
|
1490
|
-
) {
|
|
1491
|
-
updatedProfiles.push({
|
|
1492
|
-
profileId: current.id,
|
|
1493
|
-
addedPaths,
|
|
1494
|
-
addedCommandIds: addedCommandIds.sort(),
|
|
1495
|
-
updatedCommandIds: updatedCommandIds.sort(),
|
|
1496
|
-
});
|
|
1497
|
-
}
|
|
1514
|
+
const update = mergeDetectedProfileDefinition(current, detected);
|
|
1515
|
+
if (update !== null) updatedProfiles.push(update);
|
|
1516
|
+
for (const command of current.commands) commandOwners.set(command.id, current.id);
|
|
1498
1517
|
}
|
|
1499
1518
|
return {
|
|
1500
1519
|
proposedProfiles: {
|
|
@@ -1590,6 +1609,16 @@ function validateReconcileReceipt(receipt, reconcileSha256) {
|
|
|
1590
1609
|
return receiptPath;
|
|
1591
1610
|
}
|
|
1592
1611
|
|
|
1612
|
+
function isInitializationProfileScaffold(value) {
|
|
1613
|
+
return value !== null
|
|
1614
|
+
&& typeof value === "object"
|
|
1615
|
+
&& !Array.isArray(value)
|
|
1616
|
+
&& value.schemaVersion === "OwlCodaRunKitProfilesV1"
|
|
1617
|
+
&& Array.isArray(value.profiles)
|
|
1618
|
+
&& value.profiles.length === 0
|
|
1619
|
+
&& Object.keys(value).sort().join(",") === "profiles,schemaVersion";
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1593
1622
|
function recoverProfileReconcileTransaction(root) {
|
|
1594
1623
|
const runtimeRoot = path.join(root, ".owlcoda", "runkit");
|
|
1595
1624
|
if (!existsSync(runtimeRoot)) return null;
|
|
@@ -1710,6 +1739,7 @@ export function reconcileProfilesV2({
|
|
|
1710
1739
|
let currentProfilesSha256;
|
|
1711
1740
|
let currentProfiles;
|
|
1712
1741
|
let currentProfilesBytes;
|
|
1742
|
+
let replacesInitializationScaffold = false;
|
|
1713
1743
|
try {
|
|
1714
1744
|
currentProfilesSha256 = profilesFileHash(root);
|
|
1715
1745
|
if (currentProfilesSha256 === null) {
|
|
@@ -1723,8 +1753,9 @@ export function reconcileProfilesV2({
|
|
|
1723
1753
|
root,
|
|
1724
1754
|
path.join(".owlcoda", "runkit", "profiles.json"),
|
|
1725
1755
|
);
|
|
1756
|
+
replacesInitializationScaffold = isInitializationProfileScaffold(currentProfiles);
|
|
1726
1757
|
const documentIssues = validateProfileDocument(currentProfiles);
|
|
1727
|
-
if (documentIssues.length > 0) {
|
|
1758
|
+
if (documentIssues.length > 0 && !replacesInitializationScaffold) {
|
|
1728
1759
|
return blockedReconcile(null, documentIssues, currentProfilesSha256, !apply);
|
|
1729
1760
|
}
|
|
1730
1761
|
} catch {
|
|
@@ -1749,7 +1780,15 @@ export function reconcileProfilesV2({
|
|
|
1749
1780
|
return blockedReconcile(detection, detectionIssues, currentProfilesSha256, !apply);
|
|
1750
1781
|
}
|
|
1751
1782
|
|
|
1752
|
-
const merged =
|
|
1783
|
+
const merged = replacesInitializationScaffold
|
|
1784
|
+
? {
|
|
1785
|
+
proposedProfiles: structuredClone(detection.proposedProfiles),
|
|
1786
|
+
addedProfileIds: detection.proposedProfiles.profiles
|
|
1787
|
+
.map(profile => profile.id)
|
|
1788
|
+
.sort(),
|
|
1789
|
+
updatedProfiles: [],
|
|
1790
|
+
}
|
|
1791
|
+
: mergeDetectedProfiles(currentProfiles, detection.proposedProfiles);
|
|
1753
1792
|
const proposedIssues = validateProfileDocumentWithDetectedSurfaces(
|
|
1754
1793
|
root,
|
|
1755
1794
|
merged.proposedProfiles,
|
|
@@ -18,6 +18,12 @@ 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.19.0", new Set([
|
|
22
|
+
"sha256:803fb7de76a6ccffd1ec5e0164eeff15a580209fa6dfbef9e561345ab616b403",
|
|
23
|
+
])],
|
|
24
|
+
["0.18.4", new Set([
|
|
25
|
+
"sha256:372d4c247e4b40b0fd2f3fe0f5bf928548095bfb7e2e92e9ac66790e2717f77d",
|
|
26
|
+
])],
|
|
21
27
|
["0.18.3", new Set([
|
|
22
28
|
"sha256:cc7a923649592084150828f24f0d90f280af92514bafdbc4999f4015b3175d5c",
|
|
23
29
|
])],
|
|
@@ -608,20 +614,32 @@ function verifyInstalledCoreBinding(root, packageRoot, version) {
|
|
|
608
614
|
) {
|
|
609
615
|
throw new Error("project_cli_core_config_invalid");
|
|
610
616
|
}
|
|
617
|
+
const installedCore = verifyInstalledCoreClosure(packageRoot, version);
|
|
618
|
+
const actualCoreManifestSha256 = installedCore.coreManifestSha256;
|
|
619
|
+
if (actualCoreManifestSha256 !== core.coreManifestSha256) {
|
|
620
|
+
throw new Error("project_cli_installed_core_drift");
|
|
621
|
+
}
|
|
622
|
+
return {
|
|
623
|
+
schemaVersion: "OwlCodaRunKitInstalledCoreBindingV1",
|
|
624
|
+
coreVersion: version,
|
|
625
|
+
coreManifestSha256: actualCoreManifestSha256,
|
|
626
|
+
trustedIdentitySource: installedCore.trustedIdentitySource,
|
|
627
|
+
configPath: ".owlcoda/runkit/config.json",
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function verifyInstalledCoreClosure(packageRoot, version) {
|
|
611
632
|
const actualCoreManifestSha256 = installedCoreManifestSha256(packageRoot);
|
|
612
633
|
const trusted = trustedCoreManifestHashes(version);
|
|
613
634
|
if (!trusted.hashes.has(actualCoreManifestSha256)) {
|
|
614
635
|
throw new Error("project_cli_untrusted_core_identity");
|
|
615
636
|
}
|
|
616
|
-
if (actualCoreManifestSha256 !== core.coreManifestSha256) {
|
|
617
|
-
throw new Error("project_cli_installed_core_drift");
|
|
618
|
-
}
|
|
619
637
|
return {
|
|
620
638
|
schemaVersion: "OwlCodaRunKitInstalledCoreBindingV1",
|
|
621
639
|
coreVersion: version,
|
|
622
640
|
coreManifestSha256: actualCoreManifestSha256,
|
|
623
641
|
trustedIdentitySource: trusted.source,
|
|
624
|
-
configPath:
|
|
642
|
+
configPath: null,
|
|
625
643
|
};
|
|
626
644
|
}
|
|
627
645
|
|
|
@@ -629,6 +647,7 @@ export function resolveProjectCli({
|
|
|
629
647
|
workspaceRoot,
|
|
630
648
|
expectedVersion = null,
|
|
631
649
|
requireEntrypoint = true,
|
|
650
|
+
requireProjectConfig = requireEntrypoint,
|
|
632
651
|
} = {}) {
|
|
633
652
|
let root;
|
|
634
653
|
try {
|
|
@@ -692,7 +711,9 @@ export function resolveProjectCli({
|
|
|
692
711
|
"project_cli_symlink_rejected",
|
|
693
712
|
);
|
|
694
713
|
const installedCoreBinding = requireEntrypoint
|
|
695
|
-
?
|
|
714
|
+
? requireProjectConfig
|
|
715
|
+
? verifyInstalledCoreBinding(root, installed.packageRoot, version)
|
|
716
|
+
: verifyInstalledCoreClosure(installed.packageRoot, version)
|
|
696
717
|
: null;
|
|
697
718
|
return {
|
|
698
719
|
schemaVersion: "OwlCodaRunKitProjectCliBindingV1",
|