owlrunkit 0.20.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +88 -0
- package/README.md +102 -10
- package/docs/architecture/OWLCODA_RUNKIT_0201_RELEASE_TRUTH_AND_PROFILE_NORMALIZATION_PATCH_DECISION_20260813.md +95 -0
- package/docs/architecture/OWLCODA_RUNKIT_021_TEAM_DELIVERY_PHASE2_PRODUCT_DECISION_20260813.md +249 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +117 -12
- package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
- package/package.json +1 -1
- package/packages/attest/README.md +7 -1
- package/packages/attest/consumer-surface-v3.json +275 -0
- package/packages/attest/distribution-manifest-v1.json +24 -0
- package/packages/attest/schemas/canonical-candidate-identity-v1.schema.json +112 -0
- package/packages/attest/schemas/foreign-workspace-filesystem-identity-v1.schema.json +32 -0
- package/packages/attest/schemas/quick-verification-receipt-v4.schema.json +226 -0
- package/packages/attest/src/candidate-identity.mjs +454 -0
- package/packages/attest/src/index.mjs +11 -0
- package/packages/attest/src/quick-receipt-contract.mjs +126 -5
- package/packages/attest/src/quick.mjs +53 -8
- package/{registry-release-policy-v1.json → registry-release-policy-v2.json} +8 -9
- package/sbom.spdx.json +4 -4
- package/scripts/runkit-contract/core-contract.mjs +19 -1
- package/scripts/runkit-contract/fleet-skill-plan.mjs +223 -0
- package/scripts/runkit-contract/formal-workflow.mjs +3 -1
- package/scripts/runkit-contract/hook-recovery.mjs +496 -0
- package/scripts/runkit-contract/inspect-presentation.mjs +187 -0
- package/scripts/runkit-contract/install-codex-skill.mjs +1 -0
- package/scripts/runkit-contract/onboarding-bootstrap.mjs +15 -0
- package/scripts/runkit-contract/onboarding-doctor.mjs +378 -9
- package/scripts/runkit-contract/profile-onboarding.mjs +6 -0
- package/scripts/runkit-contract/project-cli-resolver.mjs +17 -1
- package/scripts/runkit-contract/quick-metrics.mjs +9 -5
- package/scripts/runkit-contract/quick-verify.mjs +108 -24
- package/scripts/runkit-contract/registry-adoption-gate.mjs +12 -3
- package/scripts/runkit-contract/registry-adoption.mjs +10 -5
- package/scripts/runkit-contract/runkit-bootstrap.mjs +1 -0
- package/scripts/runkit-contract/runkit-cli.mjs +332 -30
- package/scripts/runkit-contract/schemas/delivery-lifecycle-v1.schema.json +91 -0
- package/scripts/runkit-contract/schemas/external-gate-v1.schema.json +68 -0
- package/scripts/runkit-contract/schemas/fleet-skill-upgrade-plan-v1.schema.json +140 -0
- package/scripts/runkit-contract/schemas/hook-recovery-v1.schema.json +592 -0
- package/scripts/runkit-contract/schemas/inspect-compact-v1.schema.json +93 -0
- package/scripts/runkit-contract/schemas/inspect-compact-v2.schema.json +122 -0
- package/scripts/runkit-contract/schemas/team-delivery-status-v2.schema.json +219 -0
- package/scripts/runkit-contract/schemas/team-delivery-status-v3.schema.json +113 -0
- package/scripts/runkit-contract/schemas/team-project-event-v6.schema.json +82 -0
- package/scripts/runkit-contract/schemas/team-project-event-v7.schema.json +80 -0
- package/scripts/runkit-contract/schemas/team-project-status-v4.schema.json +208 -0
- package/scripts/runkit-contract/schemas/team-recommendation-v2.schema.json +120 -0
- package/scripts/runkit-contract/sync-release-identity.mjs +87 -0
- package/scripts/runkit-contract/team-delivery.mjs +203 -3
- package/scripts/runkit-contract/team-project.mjs +1032 -11
|
@@ -46,6 +46,16 @@ const TRANSACTION_PATHS = Object.freeze([
|
|
|
46
46
|
"adoption",
|
|
47
47
|
]);
|
|
48
48
|
|
|
49
|
+
function shellQuote(value) {
|
|
50
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function requestedSuccessorSpec(value) {
|
|
54
|
+
return /^owlrunkit@[0-9]+\.[0-9]+\.[0-9]+$/u.test(value ?? "")
|
|
55
|
+
? value
|
|
56
|
+
: null;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
function pathEntryExists(filePath) {
|
|
50
60
|
try {
|
|
51
61
|
lstatSync(filePath);
|
|
@@ -301,6 +311,7 @@ export async function planOnboardingBootstrapV1({
|
|
|
301
311
|
} = {}) {
|
|
302
312
|
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
303
313
|
const issueCodes = [];
|
|
314
|
+
const requestedSuccessor = requestedSuccessorSpec(exactSpec);
|
|
304
315
|
if (exactSpec !== EXACT_SPEC) issueCodes.push("bootstrap_exact_spec_required");
|
|
305
316
|
const local = readLocalInstallBinding({ workspaceRoot: root });
|
|
306
317
|
if (local.status !== "bound") {
|
|
@@ -385,6 +396,8 @@ export async function planOnboardingBootstrapV1({
|
|
|
385
396
|
issueCodes: [...new Set(issueCodes)].sort(),
|
|
386
397
|
nextCommand: issueCodes.length === 0
|
|
387
398
|
? `npx --no-install owlrunkit bootstrap --workspace ${JSON.stringify(root)} --exact ${EXACT_SPEC} --apply`
|
|
399
|
+
: issueCodes.includes("bootstrap_exact_spec_required") && requestedSuccessor !== null
|
|
400
|
+
? `cd ${shellQuote(root)} && npm install --save-exact ${requestedSuccessor} && npx --no-install owlrunkit bootstrap --workspace ${shellQuote(root)} --exact ${requestedSuccessor} --dry-run`
|
|
388
401
|
: "repair bootstrap preflight issues and rerun --dry-run",
|
|
389
402
|
authorizationGranted: false,
|
|
390
403
|
};
|
|
@@ -573,6 +586,7 @@ export async function applyOnboardingBootstrapV1({
|
|
|
573
586
|
"doctor",
|
|
574
587
|
() => runDoctor({
|
|
575
588
|
workspaceRoot: root,
|
|
589
|
+
discoverSharedSkill: true,
|
|
576
590
|
registryUrl: OFFICIAL_NPM_REGISTRY,
|
|
577
591
|
registryClient,
|
|
578
592
|
}),
|
|
@@ -677,6 +691,7 @@ export function formatOnboardingBootstrapHumanV1(result) {
|
|
|
677
691
|
const issues = result.issueCodes?.join(", ") || "unknown issue";
|
|
678
692
|
return [
|
|
679
693
|
`Bootstrap blocked: ${issues}`,
|
|
694
|
+
...(result.nextCommand ? [`Repair: ${result.nextCommand}`] : []),
|
|
680
695
|
`Rollback: ${result.rollbackOutcome ?? "not_required"}.`,
|
|
681
696
|
...(result.receiptPath ? [`Receipt: ${result.receiptPath}`] : []),
|
|
682
697
|
"No Git, release, deployment, production, or business authority was granted.",
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
closeSync,
|
|
3
|
+
existsSync,
|
|
3
4
|
fstatSync,
|
|
4
5
|
lstatSync,
|
|
5
6
|
openSync,
|
|
6
7
|
readSync,
|
|
7
8
|
realpathSync,
|
|
8
9
|
} from "node:fs";
|
|
10
|
+
import { homedir } from "node:os";
|
|
9
11
|
import path from "node:path";
|
|
12
|
+
import { userInfo } from "node:os";
|
|
10
13
|
|
|
11
14
|
import {
|
|
12
15
|
CORE_VERSION,
|
|
@@ -20,12 +23,19 @@ import {
|
|
|
20
23
|
} from "./registry-adoption-gate.mjs";
|
|
21
24
|
import { resolveProjectCli } from "./project-cli-resolver.mjs";
|
|
22
25
|
import { inspectProjectControlState } from "./project-control-state.mjs";
|
|
26
|
+
import {
|
|
27
|
+
inspectFleetForSkillChange,
|
|
28
|
+
inspectInstalledSkillCompatibility,
|
|
29
|
+
} from "./install-codex-skill.mjs";
|
|
23
30
|
|
|
24
31
|
const PACKAGE_NAME = "owlrunkit";
|
|
25
32
|
const EXPECTED_VERSION = CORE_VERSION;
|
|
26
33
|
const MAX_JSON_BYTES = 1_048_576;
|
|
27
34
|
const MAX_JSON_DEPTH = 64;
|
|
28
35
|
const MAX_JSON_NODES = 50_000;
|
|
36
|
+
const PACKAGE_SKILL_RELATIVE = path.join(
|
|
37
|
+
"integrations", "codex", "skills", "owlcoda-runkit",
|
|
38
|
+
);
|
|
29
39
|
|
|
30
40
|
class StrictJsonParser {
|
|
31
41
|
constructor(text, { maxDepth = MAX_JSON_DEPTH, maxNodes = MAX_JSON_NODES } = {}) {
|
|
@@ -286,6 +296,81 @@ export function readWorkspaceJsonBounded(root, relativePath, options) {
|
|
|
286
296
|
return text === null ? null : parseJsonStrict(text, options);
|
|
287
297
|
}
|
|
288
298
|
|
|
299
|
+
function parsedSkillRoot(requestedRoot) {
|
|
300
|
+
if (typeof requestedRoot !== "string" || requestedRoot.length === 0) return null;
|
|
301
|
+
try {
|
|
302
|
+
const root = realpathSync(requestedRoot);
|
|
303
|
+
const skillText = readWorkspaceFileBounded(root, "SKILL.md");
|
|
304
|
+
const config = readWorkspaceJsonBounded(
|
|
305
|
+
root,
|
|
306
|
+
path.join("assets", "templates", "config.json"),
|
|
307
|
+
);
|
|
308
|
+
const version = skillText.match(/standalone `owlrunkit@(\d+\.\d+\.\d+)`/u)?.[1] ?? null;
|
|
309
|
+
const configCoreVersion = config?.core?.coreVersion ?? null;
|
|
310
|
+
return {
|
|
311
|
+
root,
|
|
312
|
+
skillPath: path.join(root, "SKILL.md"),
|
|
313
|
+
version,
|
|
314
|
+
configCoreVersion,
|
|
315
|
+
status: version !== null && configCoreVersion === version ? "readable" : "invalid",
|
|
316
|
+
};
|
|
317
|
+
} catch {
|
|
318
|
+
return {
|
|
319
|
+
root: path.resolve(requestedRoot),
|
|
320
|
+
skillPath: path.resolve(requestedRoot, "SKILL.md"),
|
|
321
|
+
version: null,
|
|
322
|
+
configCoreVersion: null,
|
|
323
|
+
status: "invalid",
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function discoveredSharedSkillRoot() {
|
|
329
|
+
const requested = process.env.OWLCODA_RUNKIT_PROCESS_SKILL_ROOT;
|
|
330
|
+
if (typeof requested === "string" && requested.length > 0) return requested;
|
|
331
|
+
const candidates = [
|
|
332
|
+
path.join(homedir(), ".agents", "skills", "owlcoda-runkit"),
|
|
333
|
+
path.join(homedir(), ".codex", "skills", "owlcoda-runkit"),
|
|
334
|
+
];
|
|
335
|
+
return candidates.find(candidate => existsSync(candidate)) ?? null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function inspectDoctorSkillStateV1({ packageRoot, sharedSkillRoot } = {}) {
|
|
339
|
+
const packageSkill = typeof packageRoot === "string"
|
|
340
|
+
? parsedSkillRoot(path.join(packageRoot, PACKAGE_SKILL_RELATIVE))
|
|
341
|
+
: null;
|
|
342
|
+
const selectedSharedRoot = sharedSkillRoot ?? discoveredSharedSkillRoot();
|
|
343
|
+
const sharedSkill = selectedSharedRoot === null ? null : parsedSkillRoot(selectedSharedRoot);
|
|
344
|
+
const warningCodes = [];
|
|
345
|
+
if (sharedSkill === null) warningCodes.push("shared_skill_not_found");
|
|
346
|
+
else if (sharedSkill.status !== "readable") warningCodes.push("shared_skill_invalid");
|
|
347
|
+
else if (packageSkill?.status === "readable" && sharedSkill.version !== packageSkill.version) {
|
|
348
|
+
warningCodes.push("shared_skill_version_drift");
|
|
349
|
+
}
|
|
350
|
+
const packageIssueCodes = [];
|
|
351
|
+
if (packageSkill?.status !== "readable") warningCodes.push("package_skill_unavailable");
|
|
352
|
+
const skillDrift = warningCodes.includes("shared_skill_version_drift");
|
|
353
|
+
return {
|
|
354
|
+
schemaVersion: "OwlCodaRunKitDoctorSkillStateV1",
|
|
355
|
+
packageVersion: packageSkill?.version ?? null,
|
|
356
|
+
configCoreVersion: packageSkill?.configCoreVersion ?? null,
|
|
357
|
+
packageSkillVersion: packageSkill?.version ?? null,
|
|
358
|
+
packageSkillPath: packageSkill?.skillPath ?? null,
|
|
359
|
+
processSkillVersion: sharedSkill?.version ?? null,
|
|
360
|
+
processSkillPath: sharedSkill?.skillPath ?? null,
|
|
361
|
+
loadedSkillVersion: "unknown",
|
|
362
|
+
skillDrift,
|
|
363
|
+
blocking: false,
|
|
364
|
+
issueCodes: packageIssueCodes,
|
|
365
|
+
warningCodes,
|
|
366
|
+
recommendedAction: skillDrift
|
|
367
|
+
? `Start a new Agent session and load the package-bound Skill at ${packageSkill.skillPath}.`
|
|
368
|
+
: warningCodes.length > 0
|
|
369
|
+
? `Use the package-bound Skill at ${packageSkill?.skillPath ?? "the installed package"}.`
|
|
370
|
+
: "none",
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
|
|
289
374
|
async function readResponseJsonBounded(response) {
|
|
290
375
|
if (!response.body) throw new Error("registry response body is missing");
|
|
291
376
|
const reader = response.body.getReader();
|
|
@@ -478,6 +563,75 @@ function ignoreFileCoversRuntime(root, ignoreFile) {
|
|
|
478
563
|
return ignored;
|
|
479
564
|
}
|
|
480
565
|
|
|
566
|
+
const ESLINT_FLAT_CONFIG_FILES = Object.freeze([
|
|
567
|
+
"eslint.config.js",
|
|
568
|
+
"eslint.config.mjs",
|
|
569
|
+
"eslint.config.cjs",
|
|
570
|
+
"eslint.config.ts",
|
|
571
|
+
"eslint.config.mts",
|
|
572
|
+
"eslint.config.cts",
|
|
573
|
+
]);
|
|
574
|
+
|
|
575
|
+
function eslintIgnoreListCoversRuntime(listText) {
|
|
576
|
+
let ignored = false;
|
|
577
|
+
for (const match of listText.matchAll(/(["'])([^"']*)\1/gu)) {
|
|
578
|
+
const negated = match[2].startsWith("!");
|
|
579
|
+
const pattern = (negated ? match[2].slice(1) : match[2])
|
|
580
|
+
.replace(/^\.\//u, "")
|
|
581
|
+
.replace(/^\*\*\//u, "");
|
|
582
|
+
if (new Set([
|
|
583
|
+
".owlcoda/",
|
|
584
|
+
".owlcoda/**",
|
|
585
|
+
".owlcoda/runkit/",
|
|
586
|
+
".owlcoda/runkit/**",
|
|
587
|
+
]).has(pattern)) {
|
|
588
|
+
ignored = !negated;
|
|
589
|
+
} else if (negated && pattern.startsWith(".owlcoda/runkit/")) {
|
|
590
|
+
ignored = false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return ignored;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
function eslintFlatConfigCoversRuntime(text) {
|
|
597
|
+
const importsGlobalHelper = /(?:import\s*\{[^}]*\bglobalIgnores\b[^}]*\}\s*from|(?:const|let|var)\s*\{[^}]*\bglobalIgnores\b[^}]*\}\s*=\s*require\s*\()\s*["'](?:eslint\/config|@eslint\/config-helpers)["']/su.test(text);
|
|
598
|
+
if (importsGlobalHelper) {
|
|
599
|
+
for (const match of text.matchAll(/\bglobalIgnores\s*\(\s*\[([^\]]*)\]/gsu)) {
|
|
600
|
+
if (eslintIgnoreListCoversRuntime(match[1])) return true;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
for (const match of text.matchAll(/\{([^{}]*)\}/gsu)) {
|
|
604
|
+
const body = match[1];
|
|
605
|
+
const withoutOptionalName = body.replace(
|
|
606
|
+
/(?:^|,)\s*name\s*:\s*(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')\s*(?=,|$)/su,
|
|
607
|
+
"",
|
|
608
|
+
);
|
|
609
|
+
const ignores = /^\s*,?\s*ignores\s*:\s*\[([^\]]*)\]\s*,?\s*$/su.exec(
|
|
610
|
+
withoutOptionalName,
|
|
611
|
+
);
|
|
612
|
+
if (ignores === null) continue;
|
|
613
|
+
if (eslintIgnoreListCoversRuntime(ignores[1])) return true;
|
|
614
|
+
}
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
function eslintFlatConfig(root) {
|
|
619
|
+
for (const relative of ESLINT_FLAT_CONFIG_FILES) {
|
|
620
|
+
if (!existsSync(path.join(root, relative))) continue;
|
|
621
|
+
let text;
|
|
622
|
+
try {
|
|
623
|
+
text = readWorkspaceFileBounded(root, relative);
|
|
624
|
+
} catch {
|
|
625
|
+
return { relative, coversRuntime: false };
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
relative,
|
|
629
|
+
coversRuntime: eslintFlatConfigCoversRuntime(text),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
}
|
|
634
|
+
|
|
481
635
|
const BROAD_COLLECTION_TOOLS = Object.freeze([
|
|
482
636
|
{
|
|
483
637
|
tool: "biome",
|
|
@@ -544,14 +698,18 @@ function runtimeIsolationCheck(root) {
|
|
|
544
698
|
if (typeof value !== "string" || !hasBroadDotTarget(value)) continue;
|
|
545
699
|
for (const descriptor of BROAD_COLLECTION_TOOLS) {
|
|
546
700
|
if (!descriptor.command.test(value)) continue;
|
|
701
|
+
const flatConfig = descriptor.tool === "eslint" ? eslintFlatConfig(root) : null;
|
|
702
|
+
if (flatConfig?.coversRuntime) continue;
|
|
547
703
|
if (
|
|
704
|
+
flatConfig === null
|
|
705
|
+
&&
|
|
548
706
|
descriptor.ignoreFileSupported
|
|
549
707
|
&& ignoreFileCoversRuntime(root, descriptor.ignoreFile)
|
|
550
708
|
) {
|
|
551
709
|
continue;
|
|
552
710
|
}
|
|
553
711
|
affectedScripts.push({
|
|
554
|
-
ignoreFile: descriptor.ignoreFile,
|
|
712
|
+
ignoreFile: flatConfig?.relative ?? descriptor.ignoreFile,
|
|
555
713
|
script,
|
|
556
714
|
tool: descriptor.tool,
|
|
557
715
|
});
|
|
@@ -568,6 +726,21 @@ export function inspectRuntimeIsolationV1({ workspaceRoot } = {}) {
|
|
|
568
726
|
return runtimeIsolationCheck(resolveBoundedWorkspaceRoot(workspaceRoot));
|
|
569
727
|
}
|
|
570
728
|
|
|
729
|
+
export function inspectLocalMaintenanceV1({
|
|
730
|
+
workspaceRoot,
|
|
731
|
+
packageRoot,
|
|
732
|
+
sharedSkillRoot,
|
|
733
|
+
} = {}) {
|
|
734
|
+
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
735
|
+
return {
|
|
736
|
+
schemaVersion: "OwlCodaRunKitLocalMaintenanceV1",
|
|
737
|
+
profileLaunchers: readProfileLauncherReadiness({ workspaceRoot: root }),
|
|
738
|
+
runtimeIsolation: runtimeIsolationCheck(root),
|
|
739
|
+
skill: inspectDoctorSkillStateV1({ packageRoot, sharedSkillRoot }),
|
|
740
|
+
authorizationGranted: false,
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
571
744
|
function configCheck(root) {
|
|
572
745
|
try {
|
|
573
746
|
const config = readWorkspaceJsonBounded(
|
|
@@ -577,7 +750,13 @@ function configCheck(root) {
|
|
|
577
750
|
);
|
|
578
751
|
if (config === null) return { status: "missing" };
|
|
579
752
|
const gate = validateProjectConfigV2(config);
|
|
580
|
-
return gate.valid
|
|
753
|
+
return gate.valid
|
|
754
|
+
? {
|
|
755
|
+
status: "ok",
|
|
756
|
+
coreVersion: config.core.coreVersion,
|
|
757
|
+
coreManifestSha256: config.core.coreManifestSha256,
|
|
758
|
+
}
|
|
759
|
+
: { status: "invalid", issues: gate.issues };
|
|
581
760
|
} catch {
|
|
582
761
|
return { status: "invalid" };
|
|
583
762
|
}
|
|
@@ -689,11 +868,17 @@ export function readAdoptionReadiness({ workspaceRoot } = {}) {
|
|
|
689
868
|
return adoptionCheck(root);
|
|
690
869
|
}
|
|
691
870
|
|
|
692
|
-
|
|
871
|
+
async function buildDoctorReport({
|
|
693
872
|
workspaceRoot,
|
|
873
|
+
sharedSkillRoot,
|
|
874
|
+
discoverSharedSkill = false,
|
|
694
875
|
registryUrl,
|
|
695
876
|
timeoutMs = 1_500,
|
|
696
877
|
registryClient,
|
|
878
|
+
includeSharedSkill = false,
|
|
879
|
+
skillRoot = path.join(userInfo().homedir, ".agents", "skills", "owlcoda-runkit"),
|
|
880
|
+
skillInspector = inspectInstalledSkillCompatibility,
|
|
881
|
+
skillFleetInspector = inspectFleetForSkillChange,
|
|
697
882
|
} = {}) {
|
|
698
883
|
const root = resolveBoundedWorkspaceRoot(workspaceRoot);
|
|
699
884
|
const core = currentCoreIdentity();
|
|
@@ -715,6 +900,76 @@ export async function runDoctor({
|
|
|
715
900
|
const profiles = await doctorProfilesCheck(root);
|
|
716
901
|
const adoption = adoptionCheck(root, installed);
|
|
717
902
|
const runtimeIsolation = runtimeIsolationCheck(root);
|
|
903
|
+
const skill = includeSharedSkill && (sharedSkillRoot !== undefined || discoverSharedSkill)
|
|
904
|
+
? {
|
|
905
|
+
...inspectDoctorSkillStateV1({
|
|
906
|
+
packageRoot: installed.status === "bound" ? installed.packageRoot : undefined,
|
|
907
|
+
sharedSkillRoot,
|
|
908
|
+
}),
|
|
909
|
+
packageVersion: installed.status === "bound" ? installed.version : null,
|
|
910
|
+
configCoreVersion: config.status === "ok" ? config.coreVersion : null,
|
|
911
|
+
}
|
|
912
|
+
: includeSharedSkill
|
|
913
|
+
? {
|
|
914
|
+
schemaVersion: "OwlCodaRunKitDoctorSkillStateV1",
|
|
915
|
+
packageVersion: installed.status === "bound" ? installed.version : null,
|
|
916
|
+
configCoreVersion: core.coreVersion,
|
|
917
|
+
packageSkillVersion: null,
|
|
918
|
+
packageSkillPath: null,
|
|
919
|
+
processSkillVersion: null,
|
|
920
|
+
processSkillPath: null,
|
|
921
|
+
loadedSkillVersion: "unknown",
|
|
922
|
+
skillDrift: false,
|
|
923
|
+
blocking: false,
|
|
924
|
+
issueCodes: [],
|
|
925
|
+
warningCodes: [],
|
|
926
|
+
recommendedAction: "none",
|
|
927
|
+
}
|
|
928
|
+
: null;
|
|
929
|
+
let sharedSkill = null;
|
|
930
|
+
if (includeSharedSkill) {
|
|
931
|
+
let inspectedSkill;
|
|
932
|
+
try {
|
|
933
|
+
inspectedSkill = await skillInspector({ targetRoot: skillRoot });
|
|
934
|
+
} catch (error) {
|
|
935
|
+
inspectedSkill = {
|
|
936
|
+
status: "invalid",
|
|
937
|
+
installedSkillVersion: null,
|
|
938
|
+
issues: [error instanceof Error ? error.message : String(error)],
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
let activationBlockedReason = new Set(["unmanaged", "drifted"]).has(
|
|
942
|
+
inspectedSkill.status,
|
|
943
|
+
)
|
|
944
|
+
? `shared_skill_integrity_invalid:${inspectedSkill.status}`
|
|
945
|
+
: null;
|
|
946
|
+
if (inspectedSkill.status !== "valid" && activationBlockedReason === null) {
|
|
947
|
+
try {
|
|
948
|
+
await skillFleetInspector();
|
|
949
|
+
} catch (error) {
|
|
950
|
+
activationBlockedReason = error instanceof Error ? error.message : String(error);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
const sharedSkillDrift = inspectedSkill.status === "version_mismatch";
|
|
954
|
+
sharedSkill = {
|
|
955
|
+
status: inspectedSkill.status === "valid"
|
|
956
|
+
? "valid"
|
|
957
|
+
: sharedSkillDrift
|
|
958
|
+
? "expected_nonblocking_drift"
|
|
959
|
+
: "unavailable_nonblocking",
|
|
960
|
+
blocking: false,
|
|
961
|
+
projectCliVersion: installed.version ?? null,
|
|
962
|
+
projectCoreVersion: core.coreVersion,
|
|
963
|
+
sharedSkillVersion: inspectedSkill.installedSkillVersion ?? null,
|
|
964
|
+
issueCodes: [...new Set(inspectedSkill.issues ?? [])].sort(),
|
|
965
|
+
activationBlockedReason,
|
|
966
|
+
recommendedAction: inspectedSkill.status === "valid"
|
|
967
|
+
? null
|
|
968
|
+
: activationBlockedReason?.startsWith("skill_update_blocked_active_execution:")
|
|
969
|
+
? "wait_for_active_execution_or_lease_then_refresh_shared_skill"
|
|
970
|
+
: "run_fleet_safe_shared_skill_refresh",
|
|
971
|
+
};
|
|
972
|
+
}
|
|
718
973
|
const checks = {
|
|
719
974
|
node: {
|
|
720
975
|
status: nodeOk ? "ok" : "unsupported",
|
|
@@ -744,6 +999,8 @@ export async function runDoctor({
|
|
|
744
999
|
detected: readProjectScripts(root),
|
|
745
1000
|
},
|
|
746
1001
|
runtimeIsolation,
|
|
1002
|
+
...(skill === null ? {} : { skill }),
|
|
1003
|
+
...(sharedSkill === null ? {} : { sharedSkill }),
|
|
747
1004
|
lifecycle: {
|
|
748
1005
|
status: lifecycle.status,
|
|
749
1006
|
activeRunIds: lifecycle.activeRunIds,
|
|
@@ -762,6 +1019,7 @@ export async function runDoctor({
|
|
|
762
1019
|
if (installed.status !== "bound") {
|
|
763
1020
|
issueCodes.push(...(installed.issueCodes ?? ["registry_install_binding_mismatch"]));
|
|
764
1021
|
}
|
|
1022
|
+
issueCodes.push(...(skill?.issueCodes ?? []));
|
|
765
1023
|
if (
|
|
766
1024
|
registry.status === "registry_verified"
|
|
767
1025
|
&& (
|
|
@@ -779,11 +1037,14 @@ export async function runDoctor({
|
|
|
779
1037
|
...runtimeIsolation.affectedScripts
|
|
780
1038
|
.map(({ script, tool }) => `runkit_runtime_collection_risk:${tool}:${script}`),
|
|
781
1039
|
...(profileLauncherWarningCount > 0 ? ["profile_launcher_warnings"] : []),
|
|
1040
|
+
...(skill?.warningCodes ?? []),
|
|
782
1041
|
].sort();
|
|
783
1042
|
const lifecycleNextAction = lifecycle.recovery.nextAllowedAction;
|
|
784
|
-
const maintenanceNextAction =
|
|
785
|
-
? "
|
|
786
|
-
:
|
|
1043
|
+
const maintenanceNextAction = skill?.skillDrift
|
|
1044
|
+
? "reload_package_bound_skill"
|
|
1045
|
+
: profileLauncherWarningCount > 0
|
|
1046
|
+
? "review_profile_launchers"
|
|
1047
|
+
: null;
|
|
787
1048
|
const nextAllowedAction = uniqueIssues.length === 0
|
|
788
1049
|
? lifecycleNextAction !== "plan_new_execution"
|
|
789
1050
|
? lifecycleNextAction
|
|
@@ -802,7 +1063,9 @@ export async function runDoctor({
|
|
|
802
1063
|
? "repair_execution_artifacts"
|
|
803
1064
|
: "repair_registry_binding";
|
|
804
1065
|
return {
|
|
805
|
-
schemaVersion:
|
|
1066
|
+
schemaVersion: includeSharedSkill
|
|
1067
|
+
? "OwlCodaRunKitDoctorReportV2"
|
|
1068
|
+
: "OwlCodaRunKitDoctorReportV1",
|
|
806
1069
|
status: uniqueIssues.length === 0 ? "ready" : "issues_found",
|
|
807
1070
|
checks,
|
|
808
1071
|
issueCodes: uniqueIssues,
|
|
@@ -810,7 +1073,9 @@ export async function runDoctor({
|
|
|
810
1073
|
profileLauncherWarnings: {
|
|
811
1074
|
count: profileLauncherWarningCount,
|
|
812
1075
|
blocking: false,
|
|
813
|
-
recommendedAction:
|
|
1076
|
+
recommendedAction: profileLauncherWarningCount > 0
|
|
1077
|
+
? "review_profile_launchers"
|
|
1078
|
+
: null,
|
|
814
1079
|
},
|
|
815
1080
|
lifecycleNextAction,
|
|
816
1081
|
maintenanceNextAction,
|
|
@@ -819,6 +1084,85 @@ export async function runDoctor({
|
|
|
819
1084
|
};
|
|
820
1085
|
}
|
|
821
1086
|
|
|
1087
|
+
export async function runDoctor(options = {}) {
|
|
1088
|
+
return buildDoctorReport({ ...options, includeSharedSkill: false });
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
export async function runDoctorV2(options = {}) {
|
|
1092
|
+
return buildDoctorReport({ ...options, includeSharedSkill: true });
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
function shellQuote(value) {
|
|
1096
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
function doctorRuntimeIsolationGuidance(runtimeIsolation) {
|
|
1100
|
+
return runtimeIsolation.affectedScripts.map((affected) => ({
|
|
1101
|
+
configurationFile: affected.ignoreFile,
|
|
1102
|
+
configurationStyle: affected.tool === "eslint"
|
|
1103
|
+
&& affected.ignoreFile.startsWith("eslint.config.")
|
|
1104
|
+
? "flat_config_global_ignores"
|
|
1105
|
+
: "ignore_file",
|
|
1106
|
+
recommendedPattern: affected.tool === "eslint"
|
|
1107
|
+
&& affected.ignoreFile.startsWith("eslint.config.")
|
|
1108
|
+
? ".owlcoda/runkit/**"
|
|
1109
|
+
: ".owlcoda/runkit/",
|
|
1110
|
+
script: affected.script,
|
|
1111
|
+
tool: affected.tool,
|
|
1112
|
+
}));
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
export async function runDoctorV3(options = {}) {
|
|
1116
|
+
const report = await buildDoctorReport({ ...options, includeSharedSkill: true });
|
|
1117
|
+
const skillRoot = options.skillRoot
|
|
1118
|
+
?? path.join(userInfo().homedir, ".agents", "skills", "owlcoda-runkit");
|
|
1119
|
+
const archiveRoot = path.join(
|
|
1120
|
+
userInfo().homedir,
|
|
1121
|
+
".agents",
|
|
1122
|
+
"skill-archives",
|
|
1123
|
+
"owlcoda-runkit",
|
|
1124
|
+
);
|
|
1125
|
+
const sharedSkill = report.checks.sharedSkill;
|
|
1126
|
+
const activation = sharedSkill.status === "valid"
|
|
1127
|
+
? {
|
|
1128
|
+
status: "not_evaluated",
|
|
1129
|
+
reason: "shared_skill_already_current",
|
|
1130
|
+
}
|
|
1131
|
+
: sharedSkill.activationBlockedReason
|
|
1132
|
+
? {
|
|
1133
|
+
status: "blocked",
|
|
1134
|
+
reason: sharedSkill.activationBlockedReason,
|
|
1135
|
+
}
|
|
1136
|
+
: {
|
|
1137
|
+
status: "eligible",
|
|
1138
|
+
reason: "fleet_check_passed",
|
|
1139
|
+
};
|
|
1140
|
+
return {
|
|
1141
|
+
...report,
|
|
1142
|
+
schemaVersion: "OwlCodaRunKitDoctorReportV3",
|
|
1143
|
+
checks: {
|
|
1144
|
+
...report.checks,
|
|
1145
|
+
runtimeIsolation: {
|
|
1146
|
+
...report.checks.runtimeIsolation,
|
|
1147
|
+
guidance: doctorRuntimeIsolationGuidance(report.checks.runtimeIsolation),
|
|
1148
|
+
},
|
|
1149
|
+
sharedSkill: {
|
|
1150
|
+
...sharedSkill,
|
|
1151
|
+
activation: {
|
|
1152
|
+
...activation,
|
|
1153
|
+
checkCommand: `npx --no-install owlrunkit fleet skill-plan --skill-root ${shellQuote(skillRoot)} --archive-root ${shellQuote(archiveRoot)}`,
|
|
1154
|
+
},
|
|
1155
|
+
},
|
|
1156
|
+
},
|
|
1157
|
+
profileLauncherWarnings: {
|
|
1158
|
+
...report.profileLauncherWarnings,
|
|
1159
|
+
diagnosticCommand: report.profileLauncherWarnings.count > 0
|
|
1160
|
+
? "npx --no-install owlrunkit profiles reconcile --workspace . --dry-run"
|
|
1161
|
+
: null,
|
|
1162
|
+
},
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
|
|
822
1166
|
export function formatDoctorHuman(report) {
|
|
823
1167
|
const otherWarnings = report.warningCodes.filter(
|
|
824
1168
|
(code) => code !== "profile_launcher_warnings",
|
|
@@ -828,13 +1172,38 @@ export function formatDoctorHuman(report) {
|
|
|
828
1172
|
`Node: ${report.checks.node.status} (${report.checks.node.version})`,
|
|
829
1173
|
`Core: ${report.checks.core.status} (${report.checks.core.version})`,
|
|
830
1174
|
`Installed package: ${report.checks.installedPackage.status}`,
|
|
1175
|
+
...(report.checks.skill
|
|
1176
|
+
? [
|
|
1177
|
+
`Skill: package ${report.checks.skill.packageSkillVersion ?? "unknown"}; process ${report.checks.skill.processSkillVersion ?? "unknown"}${report.checks.skill.skillDrift ? " (drift, non-blocking)" : ""}; loaded ${report.checks.skill.loadedSkillVersion}.`,
|
|
1178
|
+
`Package-bound Skill: ${report.checks.skill.packageSkillPath ?? "unavailable"}`,
|
|
1179
|
+
`Process-wide Skill: ${report.checks.skill.processSkillPath ?? "unavailable"}`,
|
|
1180
|
+
]
|
|
1181
|
+
: []),
|
|
831
1182
|
`Registry: ${report.checks.registry.status}`,
|
|
832
1183
|
`Profiles: ${report.checks.profiles.status}`,
|
|
833
1184
|
`Adoption: ${report.checks.adoption.status}`,
|
|
1185
|
+
...(report.checks.sharedSkill
|
|
1186
|
+
? [`Shared Skill: ${report.checks.sharedSkill.status} (${report.checks.sharedSkill.sharedSkillVersion ?? "missing"}; project Core ${report.checks.sharedSkill.projectCoreVersion})`]
|
|
1187
|
+
: []),
|
|
1188
|
+
...(report.checks.sharedSkill?.activationBlockedReason
|
|
1189
|
+
&& !report.checks.sharedSkill?.activation
|
|
1190
|
+
? [`Shared Skill activation: ${report.checks.sharedSkill.activationBlockedReason}`]
|
|
1191
|
+
: []),
|
|
1192
|
+
...(report.checks.sharedSkill?.activation
|
|
1193
|
+
? [
|
|
1194
|
+
`Shared Skill activation: ${report.checks.sharedSkill.activation.status} (${report.checks.sharedSkill.activation.reason})`,
|
|
1195
|
+
`Fleet-safe check: ${report.checks.sharedSkill.activation.checkCommand}`,
|
|
1196
|
+
]
|
|
1197
|
+
: []),
|
|
834
1198
|
`Lifecycle: ${report.checks.lifecycle.status}`,
|
|
835
1199
|
...(report.issueCodes.length > 0 ? [`Issues: ${report.issueCodes.join(", ")}`] : []),
|
|
836
1200
|
...(report.profileLauncherWarnings.count > 0
|
|
837
|
-
? [
|
|
1201
|
+
? [
|
|
1202
|
+
`Profile launcher warnings: ${report.profileLauncherWarnings.count} (non-blocking)`,
|
|
1203
|
+
...(report.profileLauncherWarnings.diagnosticCommand
|
|
1204
|
+
? [`Profile launcher diagnostic: ${report.profileLauncherWarnings.diagnosticCommand}`]
|
|
1205
|
+
: []),
|
|
1206
|
+
]
|
|
838
1207
|
: []),
|
|
839
1208
|
...(otherWarnings.length > 0 ? [`Warnings: ${otherWarnings.join(", ")}`] : []),
|
|
840
1209
|
`Next: ${report.nextAllowedAction === "adopt_current_release"
|
|
@@ -1826,6 +1826,12 @@ export function reconcileProfilesV2({
|
|
|
1826
1826
|
authorizationGranted: false,
|
|
1827
1827
|
};
|
|
1828
1828
|
if (!apply || proposedProfilesSha256 === currentProfilesSha256) return plan;
|
|
1829
|
+
if (normalizationOnly) {
|
|
1830
|
+
return {
|
|
1831
|
+
...plan,
|
|
1832
|
+
status: "profiles_already_current",
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1829
1835
|
|
|
1830
1836
|
let runtimeRoot;
|
|
1831
1837
|
let receiptRoot;
|
|
@@ -15,10 +15,26 @@ const MAX_CORE_TOTAL_BYTES = 67_108_864;
|
|
|
15
15
|
const PACKAGE_NAME = "owlrunkit";
|
|
16
16
|
const OFFICIAL_CLI_ENTRYPOINT =
|
|
17
17
|
"scripts/runkit-contract/runkit-bootstrap.mjs";
|
|
18
|
-
const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set([
|
|
18
|
+
const SELF_NORMALIZED_QUICK_CORE_VERSIONS = new Set([
|
|
19
|
+
"0.19.2",
|
|
20
|
+
"0.20.0",
|
|
21
|
+
"0.20.1",
|
|
22
|
+
"0.21.0",
|
|
23
|
+
"0.21.1",
|
|
24
|
+
"0.22.0",
|
|
25
|
+
]);
|
|
19
26
|
const EXACT_VERSION = /^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/;
|
|
20
27
|
const SHA256_REF = /^sha256:[a-f0-9]{64}$/;
|
|
21
28
|
const TRUSTED_PRIOR_CORE_IDENTITIES = new Map([
|
|
29
|
+
["0.21.0", new Set([
|
|
30
|
+
"sha256:f85b9bf8c32705d5f5dfebdb653e83ac8d1ac0468214c17f230cbd2de04246fd",
|
|
31
|
+
])],
|
|
32
|
+
["0.20.1", new Set([
|
|
33
|
+
"sha256:8af2b9792731efa37f596255a7677dcec6e2fd3cc079dfdb0e6ff29e8017e784",
|
|
34
|
+
])],
|
|
35
|
+
["0.20.0", new Set([
|
|
36
|
+
"sha256:30098aa9344c17c1ed0b5ed45ef1c590b3214b824402f584029c1ecb38ca7a70",
|
|
37
|
+
])],
|
|
22
38
|
["0.19.2", new Set([
|
|
23
39
|
"sha256:c46ae9d8ed0572e5636ab5725f922f8db3ee5bf0f966b200bce7b2287c9267c8",
|
|
24
40
|
])],
|
|
@@ -22,12 +22,13 @@ const INVALID_ATTESTATION_ISSUES = new Set([
|
|
|
22
22
|
"attestation_material_missing",
|
|
23
23
|
"receipt_material_hash_mismatch",
|
|
24
24
|
]);
|
|
25
|
-
const
|
|
25
|
+
const SUCCESSOR_MUTATION_ISSUES = new Set([
|
|
26
26
|
"source_mutated_during_verification",
|
|
27
27
|
"bound_ignored_path_mutated_during_verification",
|
|
28
28
|
"dependency_environment_mutated_during_verification",
|
|
29
29
|
"consumer_source_mutated_during_verification",
|
|
30
30
|
"quick_isolation_cleanup_failed",
|
|
31
|
+
"foreign_workspace_mutated_during_verification",
|
|
31
32
|
]);
|
|
32
33
|
|
|
33
34
|
function readHistoricalReceipt(receiptPath) {
|
|
@@ -38,11 +39,14 @@ function readHistoricalReceipt(receiptPath) {
|
|
|
38
39
|
&& quickSnapshotFingerprintValid(receipt.workspaceAfter);
|
|
39
40
|
const sourceChanged = receipt.workspaceBefore.sourceFingerprint
|
|
40
41
|
!== receipt.workspaceAfter.sourceFingerprint;
|
|
41
|
-
const
|
|
42
|
+
const successorReceipt = [
|
|
43
|
+
"OwlCodaQuickVerificationReceiptV3",
|
|
44
|
+
"OwlCodaQuickVerificationReceiptV4",
|
|
45
|
+
].includes(receipt.schemaVersion);
|
|
42
46
|
const mutationBindingValid = receipt.mutationDecision === "source_unchanged"
|
|
43
|
-
? !sourceChanged && !receipt.issueCodes.some(issue =>
|
|
44
|
-
:
|
|
45
|
-
? receipt.issueCodes.some(issue =>
|
|
47
|
+
? !sourceChanged && !receipt.issueCodes.some(issue => SUCCESSOR_MUTATION_ISSUES.has(issue))
|
|
48
|
+
: successorReceipt
|
|
49
|
+
? receipt.issueCodes.some(issue => SUCCESSOR_MUTATION_ISSUES.has(issue))
|
|
46
50
|
: sourceChanged && receipt.issueCodes.includes("source_mutated_during_verification");
|
|
47
51
|
if (!snapshotsValid || !mutationBindingValid) {
|
|
48
52
|
throw new Error("quick_receipt_source_binding_invalid");
|