owlrunkit 0.18.3 → 0.18.4
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 +42 -0
- package/README.md +125 -52
- package/docs/architecture/OWLCODA_RUNKIT_0184_EXTERNAL_DELIVERY_INTAKE_PRODUCT_DECISION_20260812.md +209 -0
- package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +8 -7
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +109 -77
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/distribution-manifest-v1.json +5 -0
- package/packages/attest/src/quick-receipt-contract.mjs +3 -3
- package/packages/attest/src/quick.mjs +5 -0
- package/packages/attest/src/workspace-snapshot.mjs +10 -1
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/assurance-router.mjs +25 -6
- package/scripts/runkit-contract/core-contract.mjs +9 -1
- package/scripts/runkit-contract/onboarding-doctor.mjs +1 -1
- package/scripts/runkit-contract/profile-onboarding.mjs +263 -15
- package/scripts/runkit-contract/project-cli-resolver.mjs +6 -0
- package/scripts/runkit-contract/project-external-delivery.mjs +707 -0
- package/scripts/runkit-contract/quick-verify.mjs +1 -1
- package/scripts/runkit-contract/runkit-bootstrap.mjs +6 -1
- package/scripts/runkit-contract/runkit-cli.mjs +278 -140
- package/scripts/runkit-contract/schemas/assurance-route-v2.schema.json +26 -0
- package/scripts/runkit-contract/schemas/profile-detection-v3.schema.json +170 -0
- package/scripts/runkit-contract/schemas/team-project-event-v3.schema.json +146 -0
- package/scripts/runkit-contract/schemas/team-project-status-v2.schema.json +85 -0
- package/scripts/runkit-contract/schemas/usage-mode-recommendation-v2.schema.json +136 -0
- package/scripts/runkit-contract/team-project.mjs +477 -15
- package/scripts/runkit-contract/usage-mode.mjs +28 -7
|
@@ -56,9 +56,9 @@ function incompleteReasons(input) {
|
|
|
56
56
|
return [...new Set(reasons)].sort();
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
function result({ lane, riskMode, reasonCodes, nextCommand }) {
|
|
59
|
+
function result({ schemaVersion, lane, riskMode, reasonCodes, nextCommand }) {
|
|
60
60
|
return {
|
|
61
|
-
schemaVersion
|
|
61
|
+
schemaVersion,
|
|
62
62
|
status: "routed",
|
|
63
63
|
lane,
|
|
64
64
|
riskMode,
|
|
@@ -68,14 +68,15 @@ function result({ lane, riskMode, reasonCodes, nextCommand }) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
function routeRunKitAssurance(input, { schemaVersion, commandPrefix }) {
|
|
72
72
|
const incomplete = incompleteReasons(input);
|
|
73
73
|
if (incomplete.length > 0) {
|
|
74
74
|
return result({
|
|
75
|
+
schemaVersion,
|
|
75
76
|
lane: "formal",
|
|
76
77
|
riskMode: "full",
|
|
77
78
|
reasonCodes: ["assurance_input_incomplete", ...incomplete],
|
|
78
|
-
nextCommand:
|
|
79
|
+
nextCommand: `${commandPrefix} formal start`,
|
|
79
80
|
});
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -95,15 +96,17 @@ export function routeRunKitAssuranceV1(input) {
|
|
|
95
96
|
];
|
|
96
97
|
if (formalReasons.length > 0) {
|
|
97
98
|
return result({
|
|
99
|
+
schemaVersion,
|
|
98
100
|
lane: "formal",
|
|
99
101
|
riskMode: "full",
|
|
100
102
|
reasonCodes: [...new Set(formalReasons)].sort(),
|
|
101
|
-
nextCommand:
|
|
103
|
+
nextCommand: `${commandPrefix} formal start`,
|
|
102
104
|
});
|
|
103
105
|
}
|
|
104
106
|
|
|
105
107
|
if (!input.needsDurableEvidence) {
|
|
106
108
|
return result({
|
|
109
|
+
schemaVersion,
|
|
107
110
|
lane: "none",
|
|
108
111
|
riskMode: "lightweight",
|
|
109
112
|
reasonCodes: ["durable_evidence_not_required"],
|
|
@@ -113,6 +116,7 @@ export function routeRunKitAssuranceV1(input) {
|
|
|
113
116
|
|
|
114
117
|
if (input.exactCommandCount !== 1) {
|
|
115
118
|
return result({
|
|
119
|
+
schemaVersion,
|
|
116
120
|
lane: "none",
|
|
117
121
|
riskMode: "lightweight",
|
|
118
122
|
reasonCodes: ["quick_exact_command_required"],
|
|
@@ -121,9 +125,24 @@ export function routeRunKitAssuranceV1(input) {
|
|
|
121
125
|
}
|
|
122
126
|
|
|
123
127
|
return result({
|
|
128
|
+
schemaVersion,
|
|
124
129
|
lane: "quick",
|
|
125
130
|
riskMode: "lightweight",
|
|
126
131
|
reasonCodes: ["single_bounded_verification"],
|
|
127
|
-
nextCommand:
|
|
132
|
+
nextCommand: `${commandPrefix} quick-verify`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function routeRunKitAssuranceV1(input) {
|
|
137
|
+
return routeRunKitAssurance(input, {
|
|
138
|
+
schemaVersion: "OwlCodaRunKitAssuranceRouteV1",
|
|
139
|
+
commandPrefix: "owlrunkit",
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function routeRunKitAssuranceV2(input) {
|
|
144
|
+
return routeRunKitAssurance(input, {
|
|
145
|
+
schemaVersion: "OwlCodaRunKitAssuranceRouteV2",
|
|
146
|
+
commandPrefix: "npx --no-install owlrunkit",
|
|
128
147
|
});
|
|
129
148
|
}
|
|
@@ -14,7 +14,7 @@ import { fileURLToPath } from "node:url";
|
|
|
14
14
|
import { parseProjectControlState } from "./project-control-parser.mjs";
|
|
15
15
|
|
|
16
16
|
export const CONTRACT_VERSION = "0.2";
|
|
17
|
-
export const CORE_VERSION = "0.18.
|
|
17
|
+
export const CORE_VERSION = "0.18.4";
|
|
18
18
|
export const RUNTIME_ROOT = ".owlcoda/runkit";
|
|
19
19
|
|
|
20
20
|
const CORE_FILES = [
|
|
@@ -43,6 +43,7 @@ const CORE_FILES = [
|
|
|
43
43
|
"project-cli-resolver.mjs",
|
|
44
44
|
"project-control-parser.mjs",
|
|
45
45
|
"project-control-state.mjs",
|
|
46
|
+
"project-external-delivery.mjs",
|
|
46
47
|
"provenance-common.mjs",
|
|
47
48
|
"quick-attest.mjs",
|
|
48
49
|
"quick-canonical.mjs",
|
|
@@ -76,6 +77,7 @@ const CORE_FILES = [
|
|
|
76
77
|
"verification-plan.mjs",
|
|
77
78
|
"visual-smoke.mjs",
|
|
78
79
|
"schemas/assurance-route-v1.schema.json",
|
|
80
|
+
"schemas/assurance-route-v2.schema.json",
|
|
79
81
|
"schemas/closeout-receipt-v2.schema.json",
|
|
80
82
|
"schemas/adoption-evidence-v1.schema.json",
|
|
81
83
|
"schemas/coverage-adopt-request-v1.schema.json",
|
|
@@ -102,6 +104,7 @@ const CORE_FILES = [
|
|
|
102
104
|
"schemas/owner-deployment-decision-v1.schema.json",
|
|
103
105
|
"schemas/profile-apply-receipt-v1.schema.json",
|
|
104
106
|
"schemas/profile-detection-v2.schema.json",
|
|
107
|
+
"schemas/profile-detection-v3.schema.json",
|
|
105
108
|
"schemas/profile-reconcile-plan-v1.schema.json",
|
|
106
109
|
"schemas/profile-reconcile-receipt-v1.schema.json",
|
|
107
110
|
"schemas/project-config.schema.json",
|
|
@@ -127,9 +130,12 @@ const CORE_FILES = [
|
|
|
127
130
|
"schemas/team-project-definition-v1.schema.json",
|
|
128
131
|
"schemas/team-project-event-v1.schema.json",
|
|
129
132
|
"schemas/team-project-event-v2.schema.json",
|
|
133
|
+
"schemas/team-project-event-v3.schema.json",
|
|
130
134
|
"schemas/team-project-successor-receipt-v1.schema.json",
|
|
131
135
|
"schemas/team-project-status-v1.schema.json",
|
|
136
|
+
"schemas/team-project-status-v2.schema.json",
|
|
132
137
|
"schemas/usage-mode-recommendation-v1.schema.json",
|
|
138
|
+
"schemas/usage-mode-recommendation-v2.schema.json",
|
|
133
139
|
"schemas/verification-envelope-v1.schema.json",
|
|
134
140
|
"schemas/verification-finalize-attempt-v1.schema.json",
|
|
135
141
|
"schemas/verification-preflight-v1.schema.json",
|
|
@@ -161,10 +167,12 @@ const CORE_FILES = [
|
|
|
161
167
|
];
|
|
162
168
|
|
|
163
169
|
const CORE_DEPENDENCY_FILES = [
|
|
170
|
+
"packages/attest/schemas/workspace-snapshot-v1.schema.json",
|
|
164
171
|
"packages/attest/src/formal.mjs",
|
|
165
172
|
"packages/attest/src/offline-bundle.mjs",
|
|
166
173
|
"packages/attest/src/quick-receipt-contract.mjs",
|
|
167
174
|
"packages/attest/src/reference-contract.mjs",
|
|
175
|
+
"packages/attest/src/workspace-snapshot.mjs",
|
|
168
176
|
];
|
|
169
177
|
|
|
170
178
|
function sha256(value) {
|
|
@@ -818,7 +818,7 @@ export function formatDoctorHuman(report) {
|
|
|
818
818
|
: []),
|
|
819
819
|
...(otherWarnings.length > 0 ? [`Warnings: ${otherWarnings.join(", ")}`] : []),
|
|
820
820
|
`Next: ${report.nextAllowedAction === "adopt_current_release"
|
|
821
|
-
? `owlrunkit adopt --workspace . --exact ${PACKAGE_NAME}@${EXPECTED_VERSION}`
|
|
821
|
+
? `npx --no-install owlrunkit adopt --workspace . --exact ${PACKAGE_NAME}@${EXPECTED_VERSION}`
|
|
822
822
|
: report.nextAllowedAction}`,
|
|
823
823
|
...(report.maintenanceNextAction
|
|
824
824
|
? [`Maintenance: ${report.maintenanceNextAction}`]
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
1
2
|
import { createHash } from "node:crypto";
|
|
2
3
|
import {
|
|
3
4
|
existsSync,
|
|
@@ -765,7 +766,7 @@ function bindCommandsToProjectTool(profiles, toolBinding) {
|
|
|
765
766
|
}));
|
|
766
767
|
}
|
|
767
768
|
|
|
768
|
-
function proposedProfileDocument(candidates, inputManifest) {
|
|
769
|
+
function proposedProfileDocument(candidates, inputManifest, detectedSurfacePaths = []) {
|
|
769
770
|
const actionable = candidates.filter(profile => profile.commands.length > 0);
|
|
770
771
|
const selectedPrimary = actionable.find(profile => profile.id === "node-quality")
|
|
771
772
|
?? actionable[0]
|
|
@@ -779,13 +780,162 @@ function proposedProfileDocument(candidates, inputManifest) {
|
|
|
779
780
|
...profile,
|
|
780
781
|
paths: [...new Set([
|
|
781
782
|
...profile.paths,
|
|
782
|
-
...(profile.id === selectedPrimary?.id
|
|
783
|
+
...(profile.id === selectedPrimary?.id
|
|
784
|
+
? [...rootSurfacePaths, ...detectedSurfacePaths]
|
|
785
|
+
: []),
|
|
783
786
|
])].sort(),
|
|
784
787
|
primary: profile.id === selectedPrimary?.id,
|
|
785
788
|
})),
|
|
786
789
|
};
|
|
787
790
|
}
|
|
788
791
|
|
|
792
|
+
function visibleGitChangedPaths(root) {
|
|
793
|
+
try {
|
|
794
|
+
const gitRoot = realpathSync(execFileSync(
|
|
795
|
+
"git",
|
|
796
|
+
["rev-parse", "--show-toplevel"],
|
|
797
|
+
{
|
|
798
|
+
cwd: root,
|
|
799
|
+
encoding: "utf8",
|
|
800
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
801
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
802
|
+
},
|
|
803
|
+
).trim());
|
|
804
|
+
const workspacePrefix = path.relative(gitRoot, root).split(path.sep).join("/");
|
|
805
|
+
if (workspacePrefix.startsWith("../") || workspacePrefix === "..") {
|
|
806
|
+
return { paths: [], issueCodes: ["profile_git_root_unusable"] };
|
|
807
|
+
}
|
|
808
|
+
const normalizeGitPath = (filePath) => {
|
|
809
|
+
const normalized = filePath.split(path.sep).join("/");
|
|
810
|
+
if (!workspacePrefix) return normalized;
|
|
811
|
+
const prefix = `${workspacePrefix}/`;
|
|
812
|
+
return normalized.startsWith(prefix) ? normalized.slice(prefix.length) : null;
|
|
813
|
+
};
|
|
814
|
+
const raw = execFileSync(
|
|
815
|
+
"git",
|
|
816
|
+
["status", "--porcelain=v1", "-z", "--untracked-files=all", "--", "."],
|
|
817
|
+
{
|
|
818
|
+
cwd: root,
|
|
819
|
+
encoding: "utf8",
|
|
820
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
821
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
822
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
823
|
+
},
|
|
824
|
+
);
|
|
825
|
+
const fields = raw.split("\0");
|
|
826
|
+
const paths = [];
|
|
827
|
+
for (let index = 0; index < fields.length; index += 1) {
|
|
828
|
+
const field = fields[index];
|
|
829
|
+
if (!field) continue;
|
|
830
|
+
const status = field.slice(0, 2);
|
|
831
|
+
const currentPath = normalizeGitPath(field.slice(3));
|
|
832
|
+
let priorPath = null;
|
|
833
|
+
if (status.includes("R") || status.includes("C")) {
|
|
834
|
+
const prior = fields[index + 1];
|
|
835
|
+
if (!prior) return { paths: [], issueCodes: ["profile_git_status_unusable"] };
|
|
836
|
+
priorPath = normalizeGitPath(prior);
|
|
837
|
+
index += 1;
|
|
838
|
+
}
|
|
839
|
+
if (currentPath === null || (status.includes("R") && priorPath === null)) {
|
|
840
|
+
return { paths: [], issueCodes: ["profile_git_path_outside_workspace"] };
|
|
841
|
+
}
|
|
842
|
+
const changedPaths = status.includes("R") ? [currentPath, priorPath] : [currentPath];
|
|
843
|
+
for (const filePath of changedPaths) {
|
|
844
|
+
if (!filePath
|
|
845
|
+
|| filePath === ".owlcoda/runkit"
|
|
846
|
+
|| filePath.startsWith(".owlcoda/runkit/")) continue;
|
|
847
|
+
try {
|
|
848
|
+
safeRelativePath(filePath, "profile changed path");
|
|
849
|
+
paths.push(filePath);
|
|
850
|
+
} catch {
|
|
851
|
+
return { paths: [], issueCodes: ["profile_changed_path_unusable"] };
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
return { paths: [...new Set(paths)].sort(), issueCodes: [] };
|
|
856
|
+
} catch {
|
|
857
|
+
return { paths: [], issueCodes: ["profile_git_status_unavailable"] };
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
function sourceCandidatePath(filePath) {
|
|
862
|
+
const segments = filePath.split("/");
|
|
863
|
+
const nonSourceSegments = new Set([
|
|
864
|
+
".cache",
|
|
865
|
+
".mypy_cache",
|
|
866
|
+
".pytest_cache",
|
|
867
|
+
".ruff_cache",
|
|
868
|
+
".turbo",
|
|
869
|
+
".vite",
|
|
870
|
+
".vitest",
|
|
871
|
+
"__pycache__",
|
|
872
|
+
"node_modules",
|
|
873
|
+
]);
|
|
874
|
+
if (segments.some(segment => nonSourceSegments.has(segment))) return false;
|
|
875
|
+
if ([".github", ".owlcoda", "docs"].includes(segments[0])) return false;
|
|
876
|
+
return !/\.(?:md|mdx|rst|txt)$/iu.test(filePath);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function shellQuote(value) {
|
|
880
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function profileCoveragePlan({ root, proposedProfiles, currentProfilesSha256 }) {
|
|
884
|
+
const changed = visibleGitChangedPaths(root);
|
|
885
|
+
const impact = resolveProfileImpactDetailed({
|
|
886
|
+
changedPaths: changed.paths,
|
|
887
|
+
profiles: proposedProfiles.profiles,
|
|
888
|
+
});
|
|
889
|
+
const uncoveredChangedPaths = [...impact.uncoveredPaths];
|
|
890
|
+
const uncoveredSourcePaths = uncoveredChangedPaths.filter(sourceCandidatePath);
|
|
891
|
+
const selectedPrimary = proposedProfiles.profiles.find(profile => profile.primary) ?? null;
|
|
892
|
+
const primaryBlockers = [];
|
|
893
|
+
if (selectedPrimary === null) primaryBlockers.push("actionable_primary_profile_missing");
|
|
894
|
+
if (uncoveredSourcePaths.length > 0) {
|
|
895
|
+
primaryBlockers.push(
|
|
896
|
+
`candidate_paths_do_not_cover_changed_source:${uncoveredSourcePaths.join(",")}`,
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
const minimalSuggestedProfile = selectedPrimary && uncoveredSourcePaths.length > 0
|
|
900
|
+
? {
|
|
901
|
+
...structuredClone(selectedPrimary),
|
|
902
|
+
paths: [...new Set([
|
|
903
|
+
...selectedPrimary.paths,
|
|
904
|
+
...uncoveredSourcePaths,
|
|
905
|
+
])].sort(),
|
|
906
|
+
primary: true,
|
|
907
|
+
}
|
|
908
|
+
: null;
|
|
909
|
+
const repairedProfiles = minimalSuggestedProfile
|
|
910
|
+
? {
|
|
911
|
+
...structuredClone(proposedProfiles),
|
|
912
|
+
profiles: proposedProfiles.profiles.map(profile => (
|
|
913
|
+
profile.id === minimalSuggestedProfile.id
|
|
914
|
+
? structuredClone(minimalSuggestedProfile)
|
|
915
|
+
: structuredClone(profile)
|
|
916
|
+
)),
|
|
917
|
+
}
|
|
918
|
+
: proposedProfiles;
|
|
919
|
+
const command = currentProfilesSha256 === null ? "detect" : "reconcile";
|
|
920
|
+
const workspace = shellQuote(root);
|
|
921
|
+
return {
|
|
922
|
+
status: primaryBlockers.length > 0 ? "profiles_insufficient" : "profiles_detected",
|
|
923
|
+
proposedProfiles: repairedProfiles,
|
|
924
|
+
changedPaths: changed.paths,
|
|
925
|
+
uncoveredChangedPaths,
|
|
926
|
+
uncoveredSourcePaths,
|
|
927
|
+
primaryBlockers: [...new Set(primaryBlockers)].sort(),
|
|
928
|
+
minimalSuggestedProfile,
|
|
929
|
+
repairCommands: {
|
|
930
|
+
preview: `npx --no-install owlrunkit profiles ${command} --workspace ${workspace} --dry-run`,
|
|
931
|
+
apply: minimalSuggestedProfile || primaryBlockers.length === 0
|
|
932
|
+
? `npx --no-install owlrunkit profiles ${command} --workspace ${workspace} --apply`
|
|
933
|
+
: null,
|
|
934
|
+
},
|
|
935
|
+
issueCodes: changed.issueCodes,
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
|
|
789
939
|
function profilesFileHash(root) {
|
|
790
940
|
const ref = path.join(root, ".owlcoda", "runkit", "profiles.json");
|
|
791
941
|
if (!existsSync(ref)) return null;
|
|
@@ -944,10 +1094,10 @@ function recoverProfileApplyTransaction({
|
|
|
944
1094
|
return receipt;
|
|
945
1095
|
}
|
|
946
1096
|
|
|
947
|
-
|
|
1097
|
+
function detectProfilesVersion({
|
|
948
1098
|
workspaceRoot,
|
|
949
1099
|
projectToolResolver,
|
|
950
|
-
} = {}) {
|
|
1100
|
+
} = {}, { schemaVersion, includeCoverage }) {
|
|
951
1101
|
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
952
1102
|
const manifest = readWorkspaceJsonBounded(root, "package.json");
|
|
953
1103
|
const discovered = discoverWorkspaces(root, manifest);
|
|
@@ -960,6 +1110,23 @@ export function detectProfilesV2({
|
|
|
960
1110
|
toolBinding.packageManager,
|
|
961
1111
|
true,
|
|
962
1112
|
);
|
|
1113
|
+
const detectedSurfacePaths = includeCoverage
|
|
1114
|
+
? (() => {
|
|
1115
|
+
const candidateSurfacePaths = new Set(
|
|
1116
|
+
rawCandidates.flatMap(profile => profile.paths),
|
|
1117
|
+
);
|
|
1118
|
+
return profileCandidates(
|
|
1119
|
+
root,
|
|
1120
|
+
manifest,
|
|
1121
|
+
toolBinding.executable,
|
|
1122
|
+
discovered.workspaces,
|
|
1123
|
+
toolBinding.packageManager,
|
|
1124
|
+
true,
|
|
1125
|
+
true,
|
|
1126
|
+
).flatMap(profile => profile.paths)
|
|
1127
|
+
.filter(profilePath => !candidateSurfacePaths.has(profilePath));
|
|
1128
|
+
})()
|
|
1129
|
+
: [];
|
|
963
1130
|
const candidates = bindCommandsToProjectTool(rawCandidates, toolBinding);
|
|
964
1131
|
let inputManifest = [];
|
|
965
1132
|
const issueCodes = [
|
|
@@ -971,21 +1138,44 @@ export function detectProfilesV2({
|
|
|
971
1138
|
} catch (error) {
|
|
972
1139
|
issueCodes.push(error instanceof Error ? error.message : String(error));
|
|
973
1140
|
}
|
|
974
|
-
const
|
|
1141
|
+
const initialProfiles = proposedProfileDocument(
|
|
1142
|
+
candidates,
|
|
1143
|
+
inputManifest,
|
|
1144
|
+
detectedSurfacePaths,
|
|
1145
|
+
);
|
|
1146
|
+
const currentProfilesSha256 = profilesFileHash(root);
|
|
1147
|
+
const coverage = includeCoverage
|
|
1148
|
+
? profileCoveragePlan({
|
|
1149
|
+
root,
|
|
1150
|
+
proposedProfiles: initialProfiles,
|
|
1151
|
+
currentProfilesSha256,
|
|
1152
|
+
})
|
|
1153
|
+
: {
|
|
1154
|
+
status: "profiles_detected",
|
|
1155
|
+
proposedProfiles: initialProfiles,
|
|
1156
|
+
changedPaths: [],
|
|
1157
|
+
uncoveredChangedPaths: [],
|
|
1158
|
+
uncoveredSourcePaths: [],
|
|
1159
|
+
primaryBlockers: [],
|
|
1160
|
+
minimalSuggestedProfile: null,
|
|
1161
|
+
repairCommands: null,
|
|
1162
|
+
issueCodes: [],
|
|
1163
|
+
};
|
|
1164
|
+
const proposedProfiles = coverage.proposedProfiles;
|
|
1165
|
+
issueCodes.push(...coverage.issueCodes);
|
|
975
1166
|
issueCodes.push(...validateProfileDocument(proposedProfiles));
|
|
976
1167
|
const qualityIssues = [...new Set(issueCodes)].sort();
|
|
977
1168
|
const confidence = qualityIssues.length === 0 && toolBinding.confidence === "high"
|
|
978
1169
|
? "high"
|
|
979
1170
|
: "review_required";
|
|
980
|
-
const currentProfilesSha256 = profilesFileHash(root);
|
|
981
1171
|
const applyStatus = currentProfilesSha256 !== null
|
|
982
1172
|
? "profiles_already_exists"
|
|
983
1173
|
: confidence === "high"
|
|
984
1174
|
? "ready_to_apply"
|
|
985
1175
|
: "review_required";
|
|
986
1176
|
const body = {
|
|
987
|
-
schemaVersion
|
|
988
|
-
status:
|
|
1177
|
+
schemaVersion,
|
|
1178
|
+
status: coverage.status,
|
|
989
1179
|
dryRun: true,
|
|
990
1180
|
writesPerformed: 0,
|
|
991
1181
|
confidence,
|
|
@@ -1009,6 +1199,16 @@ export function detectProfilesV2({
|
|
|
1009
1199
|
currentProfilesSha256,
|
|
1010
1200
|
detectedScripts: detectedScripts(manifest),
|
|
1011
1201
|
detectedWorkspaces: discovered.workspaces,
|
|
1202
|
+
...(includeCoverage
|
|
1203
|
+
? {
|
|
1204
|
+
changedPaths: coverage.changedPaths,
|
|
1205
|
+
uncoveredChangedPaths: coverage.uncoveredChangedPaths,
|
|
1206
|
+
uncoveredSourcePaths: coverage.uncoveredSourcePaths,
|
|
1207
|
+
primaryBlockers: coverage.primaryBlockers,
|
|
1208
|
+
minimalSuggestedProfile: coverage.minimalSuggestedProfile,
|
|
1209
|
+
repairCommands: coverage.repairCommands,
|
|
1210
|
+
}
|
|
1211
|
+
: {}),
|
|
1012
1212
|
authorizationGranted: false,
|
|
1013
1213
|
};
|
|
1014
1214
|
return {
|
|
@@ -1017,6 +1217,20 @@ export function detectProfilesV2({
|
|
|
1017
1217
|
};
|
|
1018
1218
|
}
|
|
1019
1219
|
|
|
1220
|
+
export function detectProfilesV2(input = {}) {
|
|
1221
|
+
return detectProfilesVersion(input, {
|
|
1222
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV2",
|
|
1223
|
+
includeCoverage: false,
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
export function detectProfilesV3(input = {}) {
|
|
1228
|
+
return detectProfilesVersion(input, {
|
|
1229
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV3",
|
|
1230
|
+
includeCoverage: true,
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1020
1234
|
function blockedApply(detection, issueCodes, currentProfilesSha256 = null) {
|
|
1021
1235
|
return {
|
|
1022
1236
|
schemaVersion: "OwlCodaRunKitProfilesApplyReceiptV1",
|
|
@@ -1032,13 +1246,19 @@ function blockedApply(detection, issueCodes, currentProfilesSha256 = null) {
|
|
|
1032
1246
|
function verifyDetectionInputs(
|
|
1033
1247
|
root,
|
|
1034
1248
|
detection,
|
|
1035
|
-
{
|
|
1249
|
+
{
|
|
1250
|
+
allowedApplyStatuses = ["ready_to_apply"],
|
|
1251
|
+
schemaVersion = "OwlCodaRunKitProfileDetectionV2",
|
|
1252
|
+
checkChangedPaths = false,
|
|
1253
|
+
} = {},
|
|
1036
1254
|
) {
|
|
1037
1255
|
const issues = [];
|
|
1038
1256
|
const { detectionSha256, ...body } = detection;
|
|
1039
1257
|
if (
|
|
1040
|
-
detection.schemaVersion !==
|
|
1041
|
-
||
|
|
1258
|
+
detection.schemaVersion !== schemaVersion
|
|
1259
|
+
|| !(schemaVersion === "OwlCodaRunKitProfileDetectionV3"
|
|
1260
|
+
? ["profiles_detected", "profiles_insufficient"].includes(detection.status)
|
|
1261
|
+
: detection.status === "profiles_detected")
|
|
1042
1262
|
|| !/^[a-f0-9]{64}$/.test(detectionSha256 ?? "")
|
|
1043
1263
|
|| sha256(canonicalJson(body)) !== detectionSha256
|
|
1044
1264
|
) {
|
|
@@ -1073,13 +1293,22 @@ function verifyDetectionInputs(
|
|
|
1073
1293
|
}
|
|
1074
1294
|
}
|
|
1075
1295
|
}
|
|
1296
|
+
if (checkChangedPaths) {
|
|
1297
|
+
const currentChangedPaths = visibleGitChangedPaths(root);
|
|
1298
|
+
issues.push(...currentChangedPaths.issueCodes);
|
|
1299
|
+
if (!Array.isArray(detection.changedPaths)) {
|
|
1300
|
+
issues.push("profile_detection_invalid");
|
|
1301
|
+
} else if (canonicalJson(currentChangedPaths.paths) !== canonicalJson(detection.changedPaths)) {
|
|
1302
|
+
issues.push("profile_detection_changed_paths_drift");
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1076
1305
|
return [...new Set(issues)].sort();
|
|
1077
1306
|
}
|
|
1078
1307
|
|
|
1079
|
-
|
|
1308
|
+
function applyDetectedProfilesVersion({
|
|
1080
1309
|
workspaceRoot,
|
|
1081
1310
|
detection,
|
|
1082
|
-
} = {}) {
|
|
1311
|
+
} = {}, { schemaVersion, checkChangedPaths }) {
|
|
1083
1312
|
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
1084
1313
|
let currentProfilesSha256;
|
|
1085
1314
|
try {
|
|
@@ -1087,7 +1316,10 @@ export function applyDetectedProfilesV2({
|
|
|
1087
1316
|
} catch {
|
|
1088
1317
|
return blockedApply(detection, ["profile_apply_directory_untrusted"]);
|
|
1089
1318
|
}
|
|
1090
|
-
const issues = verifyDetectionInputs(root, detection ?? {}
|
|
1319
|
+
const issues = verifyDetectionInputs(root, detection ?? {}, {
|
|
1320
|
+
schemaVersion,
|
|
1321
|
+
checkChangedPaths,
|
|
1322
|
+
});
|
|
1091
1323
|
if (issues.length > 0) return blockedApply(detection, issues);
|
|
1092
1324
|
|
|
1093
1325
|
const receiptPath = path.join(
|
|
@@ -1194,6 +1426,20 @@ export function applyDetectedProfilesV2({
|
|
|
1194
1426
|
}
|
|
1195
1427
|
}
|
|
1196
1428
|
|
|
1429
|
+
export function applyDetectedProfilesV2(input = {}) {
|
|
1430
|
+
return applyDetectedProfilesVersion(input, {
|
|
1431
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV2",
|
|
1432
|
+
checkChangedPaths: false,
|
|
1433
|
+
});
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
export function applyDetectedProfilesV3(input = {}) {
|
|
1437
|
+
return applyDetectedProfilesVersion(input, {
|
|
1438
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV3",
|
|
1439
|
+
checkChangedPaths: true,
|
|
1440
|
+
});
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1197
1443
|
function mergeDetectedProfiles(currentProfiles, detectedProfiles) {
|
|
1198
1444
|
const profiles = currentProfiles.profiles.map((profile) => structuredClone(profile));
|
|
1199
1445
|
const byId = new Map(profiles.map((profile) => [profile.id, profile]));
|
|
@@ -1490,9 +1736,11 @@ export function reconcileProfilesV2({
|
|
|
1490
1736
|
);
|
|
1491
1737
|
}
|
|
1492
1738
|
|
|
1493
|
-
const detection =
|
|
1739
|
+
const detection = detectProfilesV3({ workspaceRoot: root, projectToolResolver });
|
|
1494
1740
|
const detectionIssues = verifyDetectionInputs(root, detection, {
|
|
1495
1741
|
allowedApplyStatuses: ["profiles_already_exists"],
|
|
1742
|
+
schemaVersion: "OwlCodaRunKitProfileDetectionV3",
|
|
1743
|
+
checkChangedPaths: true,
|
|
1496
1744
|
});
|
|
1497
1745
|
if (detection.currentProfilesSha256 !== currentProfilesSha256) {
|
|
1498
1746
|
detectionIssues.push("profile_reconcile_detection_preimage_mismatch");
|
|
@@ -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.18.3", new Set([
|
|
22
|
+
"sha256:cc7a923649592084150828f24f0d90f280af92514bafdbc4999f4015b3175d5c",
|
|
23
|
+
])],
|
|
24
|
+
["0.18.2", new Set([
|
|
25
|
+
"sha256:abe17ff794b4019d8d476f09eb5d4105bae1070b1df0aab53e6c94c7ec8b65a6",
|
|
26
|
+
])],
|
|
21
27
|
["0.18.1", new Set([
|
|
22
28
|
"sha256:349b4157d3d089462b172de1619ccd169b5c1669b9322e2580ac6ec4f8616b07",
|
|
23
29
|
])],
|