owlrunkit 0.18.0 → 0.18.2
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 +41 -0
- package/README.md +61 -17
- package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +166 -0
- package/integrations/codex/skills/owlcoda-runkit/SKILL.md +41 -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 +11 -1
- package/packages/attest/src/quick.mjs +11 -1
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/assurance-router.mjs +31 -21
- package/scripts/runkit-contract/core-contract.mjs +5 -1
- package/scripts/runkit-contract/inspect-presentation.mjs +38 -5
- package/scripts/runkit-contract/install-codex-skill.mjs +60 -1
- package/scripts/runkit-contract/onboarding-doctor.mjs +65 -19
- package/scripts/runkit-contract/profile-onboarding.mjs +601 -22
- package/scripts/runkit-contract/project-cli-resolver.mjs +6 -0
- package/scripts/runkit-contract/quick-attest.mjs +1 -0
- package/scripts/runkit-contract/quick-metrics.mjs +43 -5
- package/scripts/runkit-contract/quick-receipt.mjs +43 -5
- package/scripts/runkit-contract/quick-verify.mjs +20 -4
- package/scripts/runkit-contract/runkit-cli.mjs +278 -24
- package/scripts/runkit-contract/schemas/profile-reconcile-plan-v1.schema.json +107 -0
- package/scripts/runkit-contract/schemas/profile-reconcile-receipt-v1.schema.json +60 -0
- package/scripts/runkit-contract/schemas/usage-mode-recommendation-v1.schema.json +136 -0
- package/scripts/runkit-contract/templates/assurance-request.json +6 -0
- package/scripts/runkit-contract/usage-mode.mjs +227 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/profile-reconcile-plan-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Profiles Reconcile Plan V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "status", "valid", "exitCode", "dryRun", "writesPerformed", "detectionSha256", "currentProfilesSha256", "issueCodes", "authorizationGranted"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": { "const": "OwlCodaRunKitProfilesReconcilePlanV1" },
|
|
10
|
+
"status": { "enum": ["profiles_reconcile_ready", "profiles_already_current", "profiles_reconcile_blocked"] },
|
|
11
|
+
"valid": { "type": "boolean" },
|
|
12
|
+
"exitCode": { "enum": [0, 2] },
|
|
13
|
+
"dryRun": { "type": "boolean" },
|
|
14
|
+
"writesPerformed": { "const": 0 },
|
|
15
|
+
"reconcileSha256": { "$ref": "#/$defs/hash" },
|
|
16
|
+
"detectionSha256": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/hash" }] },
|
|
17
|
+
"currentProfilesSha256": { "oneOf": [{ "type": "null" }, { "$ref": "#/$defs/hash" }] },
|
|
18
|
+
"proposedProfilesSha256": { "$ref": "#/$defs/hash" },
|
|
19
|
+
"normalizationOnly": { "type": "boolean" },
|
|
20
|
+
"addedProfileIds": { "$ref": "#/$defs/strings" },
|
|
21
|
+
"updatedProfiles": { "type": "array", "items": { "$ref": "#/$defs/update" } },
|
|
22
|
+
"proposedProfiles": { "$ref": "#/$defs/profiles" },
|
|
23
|
+
"issueCodes": { "$ref": "#/$defs/strings" },
|
|
24
|
+
"authorizationGranted": { "const": false }
|
|
25
|
+
},
|
|
26
|
+
"$defs": {
|
|
27
|
+
"hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
28
|
+
"strings": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
29
|
+
"update": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"required": ["profileId", "addedPaths", "addedCommandIds", "updatedCommandIds"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"profileId": { "type": "string", "minLength": 1 },
|
|
35
|
+
"addedPaths": { "$ref": "#/$defs/strings" },
|
|
36
|
+
"addedCommandIds": { "$ref": "#/$defs/strings" },
|
|
37
|
+
"updatedCommandIds": { "$ref": "#/$defs/strings" }
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"command": {
|
|
41
|
+
"type": "object",
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"required": ["id", "cwd", "executable", "argv"],
|
|
44
|
+
"properties": {
|
|
45
|
+
"id": { "type": "string", "minLength": 1 },
|
|
46
|
+
"cwd": { "type": "string", "minLength": 1 },
|
|
47
|
+
"executable": { "oneOf": [{ "type": "null" }, { "type": "string", "minLength": 1 }] },
|
|
48
|
+
"argv": { "type": "array", "minItems": 1, "items": { "type": "string" } }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"profile": {
|
|
52
|
+
"type": "object",
|
|
53
|
+
"additionalProperties": false,
|
|
54
|
+
"required": ["id", "paths", "role", "primary", "requiresProfileIds", "commands"],
|
|
55
|
+
"properties": {
|
|
56
|
+
"id": { "type": "string", "minLength": 1 },
|
|
57
|
+
"paths": { "$ref": "#/$defs/strings" },
|
|
58
|
+
"role": { "enum": ["primary", "supporting"] },
|
|
59
|
+
"primary": { "type": "boolean" },
|
|
60
|
+
"requiresProfileIds": { "$ref": "#/$defs/strings" },
|
|
61
|
+
"commands": { "type": "array", "items": { "$ref": "#/$defs/command" } }
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"profiles": {
|
|
65
|
+
"type": "object",
|
|
66
|
+
"additionalProperties": false,
|
|
67
|
+
"required": ["schemaVersion", "profiles"],
|
|
68
|
+
"properties": {
|
|
69
|
+
"schemaVersion": { "const": "OwlCodaRunKitProfilesV1" },
|
|
70
|
+
"profiles": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/profile" } }
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"examples": [{
|
|
75
|
+
"schemaVersion": "OwlCodaRunKitProfilesReconcilePlanV1",
|
|
76
|
+
"status": "profiles_reconcile_ready",
|
|
77
|
+
"valid": true,
|
|
78
|
+
"exitCode": 0,
|
|
79
|
+
"dryRun": true,
|
|
80
|
+
"writesPerformed": 0,
|
|
81
|
+
"reconcileSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
82
|
+
"detectionSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
83
|
+
"currentProfilesSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
|
84
|
+
"proposedProfilesSha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
85
|
+
"normalizationOnly": false,
|
|
86
|
+
"addedProfileIds": [],
|
|
87
|
+
"updatedProfiles": [{
|
|
88
|
+
"profileId": "node-quality",
|
|
89
|
+
"addedPaths": ["package.json"],
|
|
90
|
+
"addedCommandIds": ["npm-test"],
|
|
91
|
+
"updatedCommandIds": []
|
|
92
|
+
}],
|
|
93
|
+
"proposedProfiles": {
|
|
94
|
+
"schemaVersion": "OwlCodaRunKitProfilesV1",
|
|
95
|
+
"profiles": [{
|
|
96
|
+
"id": "node-quality",
|
|
97
|
+
"paths": ["package.json"],
|
|
98
|
+
"role": "primary",
|
|
99
|
+
"primary": true,
|
|
100
|
+
"requiresProfileIds": [],
|
|
101
|
+
"commands": [{ "id": "npm-test", "cwd": ".", "executable": "/usr/bin/node", "argv": ["/usr/lib/npm-cli.js", "run", "test"] }]
|
|
102
|
+
}]
|
|
103
|
+
},
|
|
104
|
+
"issueCodes": [],
|
|
105
|
+
"authorizationGranted": false
|
|
106
|
+
}]
|
|
107
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/profile-reconcile-receipt-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Profiles Reconcile Receipt V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": ["schemaVersion", "status", "exitCode", "writesPerformed", "dryRun", "reconcileSha256", "detectionSha256", "beforeProfilesSha256", "appliedProfilesSha256", "addedProfileIds", "updatedProfiles", "receiptPath", "issueCodes", "authorizationGranted"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": { "const": "OwlCodaRunKitProfilesReconcileReceiptV1" },
|
|
10
|
+
"status": { "const": "profiles_reconciled" },
|
|
11
|
+
"exitCode": { "const": 0 },
|
|
12
|
+
"writesPerformed": { "const": 2 },
|
|
13
|
+
"dryRun": { "const": false },
|
|
14
|
+
"reconcileSha256": { "$ref": "#/$defs/hash" },
|
|
15
|
+
"detectionSha256": { "$ref": "#/$defs/hash" },
|
|
16
|
+
"beforeProfilesSha256": { "$ref": "#/$defs/hash" },
|
|
17
|
+
"appliedProfilesSha256": { "$ref": "#/$defs/hash" },
|
|
18
|
+
"addedProfileIds": { "$ref": "#/$defs/strings" },
|
|
19
|
+
"updatedProfiles": { "type": "array", "items": { "$ref": "#/$defs/update" } },
|
|
20
|
+
"receiptPath": { "type": "string", "pattern": "^\\.owlcoda/runkit/profile-reconcile-receipts/[a-f0-9]{64}\\.json$" },
|
|
21
|
+
"issueCodes": { "$ref": "#/$defs/strings" },
|
|
22
|
+
"authorizationGranted": { "const": false }
|
|
23
|
+
},
|
|
24
|
+
"$defs": {
|
|
25
|
+
"hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
26
|
+
"strings": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
27
|
+
"update": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"additionalProperties": false,
|
|
30
|
+
"required": ["profileId", "addedPaths", "addedCommandIds", "updatedCommandIds"],
|
|
31
|
+
"properties": {
|
|
32
|
+
"profileId": { "type": "string", "minLength": 1 },
|
|
33
|
+
"addedPaths": { "$ref": "#/$defs/strings" },
|
|
34
|
+
"addedCommandIds": { "$ref": "#/$defs/strings" },
|
|
35
|
+
"updatedCommandIds": { "$ref": "#/$defs/strings" }
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"examples": [{
|
|
40
|
+
"schemaVersion": "OwlCodaRunKitProfilesReconcileReceiptV1",
|
|
41
|
+
"status": "profiles_reconciled",
|
|
42
|
+
"exitCode": 0,
|
|
43
|
+
"writesPerformed": 2,
|
|
44
|
+
"dryRun": false,
|
|
45
|
+
"reconcileSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
46
|
+
"detectionSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
47
|
+
"beforeProfilesSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
|
48
|
+
"appliedProfilesSha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
49
|
+
"addedProfileIds": [],
|
|
50
|
+
"updatedProfiles": [{
|
|
51
|
+
"profileId": "node-quality",
|
|
52
|
+
"addedPaths": ["package.json"],
|
|
53
|
+
"addedCommandIds": ["npm-test"],
|
|
54
|
+
"updatedCommandIds": []
|
|
55
|
+
}],
|
|
56
|
+
"receiptPath": ".owlcoda/runkit/profile-reconcile-receipts/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.json",
|
|
57
|
+
"issueCodes": [],
|
|
58
|
+
"authorizationGranted": false
|
|
59
|
+
}]
|
|
60
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://owlcoda.dev/schemas/runkit/usage-mode-recommendation-v1.json",
|
|
4
|
+
"title": "OwlCoda RunKit Usage Mode Recommendation V1",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schemaVersion",
|
|
9
|
+
"status",
|
|
10
|
+
"requestedMode",
|
|
11
|
+
"recommendedMode",
|
|
12
|
+
"continuity",
|
|
13
|
+
"assurance",
|
|
14
|
+
"triggerReasons",
|
|
15
|
+
"rejectedLighterModes",
|
|
16
|
+
"estimatedOverhead",
|
|
17
|
+
"nextCommand",
|
|
18
|
+
"downgrade",
|
|
19
|
+
"authorityBoundary",
|
|
20
|
+
"authorizationGranted"
|
|
21
|
+
],
|
|
22
|
+
"properties": {
|
|
23
|
+
"schemaVersion": { "const": "OwlCodaRunKitUsageModeRecommendationV1" },
|
|
24
|
+
"status": { "enum": ["mode_recommended", "mode_status"] },
|
|
25
|
+
"requestedMode": { "enum": ["auto", "off", "light", "managed", "formal"] },
|
|
26
|
+
"recommendedMode": { "enum": ["off", "light", "managed", "formal"] },
|
|
27
|
+
"continuity": { "enum": ["none", "project_driver"] },
|
|
28
|
+
"assurance": { "enum": ["none", "quick", "formal"] },
|
|
29
|
+
"triggerReasons": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 1,
|
|
32
|
+
"uniqueItems": true,
|
|
33
|
+
"items": { "type": "string", "minLength": 1 }
|
|
34
|
+
},
|
|
35
|
+
"rejectedLighterModes": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["mode", "reasonCodes"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"mode": { "enum": ["off", "light", "managed"] },
|
|
43
|
+
"reasonCodes": {
|
|
44
|
+
"type": "array",
|
|
45
|
+
"minItems": 1,
|
|
46
|
+
"uniqueItems": true,
|
|
47
|
+
"items": { "type": "string", "minLength": 1 }
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"estimatedOverhead": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"required": ["additionalSteps", "evidenceArtifacts"],
|
|
56
|
+
"properties": {
|
|
57
|
+
"additionalSteps": { "type": "integer", "minimum": 0 },
|
|
58
|
+
"evidenceArtifacts": { "type": "integer", "minimum": 0 }
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"nextCommand": {
|
|
62
|
+
"oneOf": [
|
|
63
|
+
{ "type": "null" },
|
|
64
|
+
{ "type": "string", "minLength": 1 }
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"downgrade": {
|
|
68
|
+
"type": "object",
|
|
69
|
+
"additionalProperties": false,
|
|
70
|
+
"required": ["allowed", "reasonCodes"],
|
|
71
|
+
"properties": {
|
|
72
|
+
"allowed": { "type": "boolean" },
|
|
73
|
+
"reasonCodes": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"uniqueItems": true,
|
|
76
|
+
"items": { "type": "string", "minLength": 1 }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"authorityBoundary": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"additionalProperties": false,
|
|
83
|
+
"required": ["authorizationGranted", "git", "release", "deployment", "production", "business"],
|
|
84
|
+
"properties": {
|
|
85
|
+
"authorizationGranted": { "const": false },
|
|
86
|
+
"git": { "const": false },
|
|
87
|
+
"release": { "const": false },
|
|
88
|
+
"deployment": { "const": false },
|
|
89
|
+
"production": { "const": false },
|
|
90
|
+
"business": { "const": false }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"authorizationGranted": { "const": false },
|
|
94
|
+
"preferenceSource": { "const": "default_auto" },
|
|
95
|
+
"persistedPreference": { "type": "null" }
|
|
96
|
+
},
|
|
97
|
+
"examples": [
|
|
98
|
+
{
|
|
99
|
+
"schemaVersion": "OwlCodaRunKitUsageModeRecommendationV1",
|
|
100
|
+
"status": "mode_recommended",
|
|
101
|
+
"requestedMode": "auto",
|
|
102
|
+
"recommendedMode": "managed",
|
|
103
|
+
"continuity": "project_driver",
|
|
104
|
+
"assurance": "quick",
|
|
105
|
+
"triggerReasons": ["cross_session_continuity", "single_bounded_verification"],
|
|
106
|
+
"rejectedLighterModes": [
|
|
107
|
+
{
|
|
108
|
+
"mode": "off",
|
|
109
|
+
"reasonCodes": ["cross_session_continuity", "single_bounded_verification"]
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"mode": "light",
|
|
113
|
+
"reasonCodes": ["cross_session_continuity", "single_bounded_verification"]
|
|
114
|
+
}
|
|
115
|
+
],
|
|
116
|
+
"estimatedOverhead": {
|
|
117
|
+
"additionalSteps": 3,
|
|
118
|
+
"evidenceArtifacts": 2
|
|
119
|
+
},
|
|
120
|
+
"nextCommand": "owlrunkit project status --workspace '.'",
|
|
121
|
+
"downgrade": {
|
|
122
|
+
"allowed": true,
|
|
123
|
+
"reasonCodes": []
|
|
124
|
+
},
|
|
125
|
+
"authorityBoundary": {
|
|
126
|
+
"authorizationGranted": false,
|
|
127
|
+
"git": false,
|
|
128
|
+
"release": false,
|
|
129
|
+
"deployment": false,
|
|
130
|
+
"production": false,
|
|
131
|
+
"business": false
|
|
132
|
+
},
|
|
133
|
+
"authorizationGranted": false
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
@@ -6,6 +6,12 @@
|
|
|
6
6
|
"longRunning": false,
|
|
7
7
|
"interruptionRecovery": false,
|
|
8
8
|
"dirtyParallelWorkspace": false,
|
|
9
|
+
"irreversibleChange": false,
|
|
10
|
+
"rollbackUncertain": false,
|
|
11
|
+
"permissionSensitive": false,
|
|
12
|
+
"securitySensitive": false,
|
|
13
|
+
"fundsAtRisk": false,
|
|
14
|
+
"schemaOrDataMigration": false,
|
|
9
15
|
"exactCommandCount": 1,
|
|
10
16
|
"writerCount": 0,
|
|
11
17
|
"riskCategories": [],
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { RUNKIT_RISK_CATEGORIES } from "./assurance-router.mjs";
|
|
2
|
+
|
|
3
|
+
const USER_MODES = new Set(["auto", "off", "light", "managed", "formal"]);
|
|
4
|
+
|
|
5
|
+
const BOOLEAN_FACTS = [
|
|
6
|
+
"needsDurableEvidence",
|
|
7
|
+
"mayMutateProject",
|
|
8
|
+
"externalSideEffects",
|
|
9
|
+
"crossSession",
|
|
10
|
+
"longRunning",
|
|
11
|
+
"interruptionRecovery",
|
|
12
|
+
"dirtyParallelWorkspace",
|
|
13
|
+
"irreversibleChange",
|
|
14
|
+
"rollbackUncertain",
|
|
15
|
+
"permissionSensitive",
|
|
16
|
+
"securitySensitive",
|
|
17
|
+
"fundsAtRisk",
|
|
18
|
+
"schemaOrDataMigration",
|
|
19
|
+
"needsFormalAcceptance",
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const MATERIAL_RISK_CATEGORIES = new Map([
|
|
23
|
+
["destructive", "risk_destructive"],
|
|
24
|
+
["foreign_write", "risk_foreign_write"],
|
|
25
|
+
["funds", "funds_at_risk"],
|
|
26
|
+
["migration", "schema_or_data_migration"],
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
const KNOWN_RISK_CATEGORIES = new Set(RUNKIT_RISK_CATEGORIES);
|
|
30
|
+
|
|
31
|
+
const AUTHORITY_BOUNDARY = Object.freeze({
|
|
32
|
+
authorizationGranted: false,
|
|
33
|
+
git: false,
|
|
34
|
+
release: false,
|
|
35
|
+
deployment: false,
|
|
36
|
+
production: false,
|
|
37
|
+
business: false,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
function uniqueSorted(values) {
|
|
41
|
+
return [...new Set(values)].sort();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function requireFacts(input) {
|
|
45
|
+
if (!USER_MODES.has(input?.requestedMode)) {
|
|
46
|
+
throw new Error("requestedMode must be auto, off, light, managed, or formal");
|
|
47
|
+
}
|
|
48
|
+
for (const field of BOOLEAN_FACTS) {
|
|
49
|
+
if (typeof input[field] !== "boolean") throw new Error(`${field} must be boolean`);
|
|
50
|
+
}
|
|
51
|
+
for (const field of ["exactCommandCount", "writerCount"]) {
|
|
52
|
+
if (!Number.isSafeInteger(input[field]) || input[field] < 0) {
|
|
53
|
+
throw new Error(`${field} must be a non-negative safe integer`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (!Array.isArray(input.riskCategories)
|
|
57
|
+
|| !input.riskCategories.every((value) => typeof value === "string")) {
|
|
58
|
+
throw new Error("riskCategories must be an array of strings");
|
|
59
|
+
}
|
|
60
|
+
const unknownCategories = uniqueSorted(
|
|
61
|
+
input.riskCategories.filter((value) => !KNOWN_RISK_CATEGORIES.has(value)),
|
|
62
|
+
);
|
|
63
|
+
if (unknownCategories.length > 0) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
`riskCategories contains an unknown category: ${unknownCategories.join(", ")}. `
|
|
66
|
+
+ `Allowed: ${RUNKIT_RISK_CATEGORIES.join(", ")}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function formalReasonCodes(input) {
|
|
72
|
+
return uniqueSorted([
|
|
73
|
+
...(input.writerCount > 1 ? ["multiple_writers"] : []),
|
|
74
|
+
...(input.permissionSensitive ? ["permission_sensitive"] : []),
|
|
75
|
+
...(input.securitySensitive ? ["security_sensitive"] : []),
|
|
76
|
+
...(input.fundsAtRisk ? ["funds_at_risk"] : []),
|
|
77
|
+
...(input.irreversibleChange ? ["irreversible_change"] : []),
|
|
78
|
+
...(input.rollbackUncertain ? ["rollback_uncertain"] : []),
|
|
79
|
+
...(input.schemaOrDataMigration ? ["schema_or_data_migration"] : []),
|
|
80
|
+
...(input.needsFormalAcceptance ? ["formal_acceptance_required"] : []),
|
|
81
|
+
...input.riskCategories
|
|
82
|
+
.filter((category) => MATERIAL_RISK_CATEGORIES.has(category))
|
|
83
|
+
.map((category) => MATERIAL_RISK_CATEGORIES.get(category)),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function continuityReasonCodes(input) {
|
|
88
|
+
return uniqueSorted([
|
|
89
|
+
...(input.crossSession ? ["cross_session_continuity"] : []),
|
|
90
|
+
...(input.longRunning ? ["long_running_continuity"] : []),
|
|
91
|
+
...(input.interruptionRecovery ? ["interruption_recovery_continuity"] : []),
|
|
92
|
+
...(input.dirtyParallelWorkspace ? ["dirty_workspace_continuity"] : []),
|
|
93
|
+
...(input.writerCount > 1 ? ["multi_writer_continuity"] : []),
|
|
94
|
+
]);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function estimateOverhead(continuity, assurance) {
|
|
98
|
+
const continuitySteps = continuity === "project_driver" ? 2 : 0;
|
|
99
|
+
const assuranceSteps = assurance === "quick" ? 1 : assurance === "formal" ? 3 : 0;
|
|
100
|
+
const continuityArtifacts = continuity === "project_driver" ? 1 : 0;
|
|
101
|
+
const assuranceArtifacts = assurance === "quick" ? 1 : assurance === "formal" ? 4 : 0;
|
|
102
|
+
return {
|
|
103
|
+
additionalSteps: continuitySteps + assuranceSteps,
|
|
104
|
+
evidenceArtifacts: continuityArtifacts + assuranceArtifacts,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function shellQuote(value) {
|
|
109
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function nextCommand({ workspaceRoot, continuity, assurance }) {
|
|
113
|
+
const workspace = shellQuote(workspaceRoot ?? ".");
|
|
114
|
+
if (assurance === "formal") {
|
|
115
|
+
return `owlrunkit formal start --workspace ${workspace}`;
|
|
116
|
+
}
|
|
117
|
+
if (continuity === "project_driver") {
|
|
118
|
+
return `owlrunkit project status --workspace ${workspace}`;
|
|
119
|
+
}
|
|
120
|
+
if (assurance === "quick") {
|
|
121
|
+
return `owlrunkit quick-verify --workspace ${workspace} -- <command> [args...]`;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function rejectedModes(recommendedMode, reasonCodes) {
|
|
127
|
+
const lighter = {
|
|
128
|
+
off: [],
|
|
129
|
+
light: ["off"],
|
|
130
|
+
managed: ["off", "light"],
|
|
131
|
+
formal: ["off", "light", "managed"],
|
|
132
|
+
}[recommendedMode];
|
|
133
|
+
return lighter.map((mode) => ({ mode, reasonCodes }));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function recommendUsageModeV1(input) {
|
|
137
|
+
requireFacts(input);
|
|
138
|
+
const forcedFormalReasons = formalReasonCodes(input);
|
|
139
|
+
const continuityReasons = continuityReasonCodes(input);
|
|
140
|
+
const requestedMode = input.requestedMode;
|
|
141
|
+
|
|
142
|
+
let continuity = "none";
|
|
143
|
+
let assurance = "none";
|
|
144
|
+
const triggerReasons = [];
|
|
145
|
+
|
|
146
|
+
if (requestedMode === "managed" || (requestedMode === "auto" && continuityReasons.length > 0)) {
|
|
147
|
+
continuity = "project_driver";
|
|
148
|
+
triggerReasons.push(...(continuityReasons.length > 0 ? continuityReasons : ["managed_mode_requested"]));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (forcedFormalReasons.length > 0) {
|
|
152
|
+
assurance = "formal";
|
|
153
|
+
if (continuityReasons.length > 0) continuity = "project_driver";
|
|
154
|
+
triggerReasons.push(...forcedFormalReasons);
|
|
155
|
+
} else if (requestedMode === "formal") {
|
|
156
|
+
assurance = "formal";
|
|
157
|
+
triggerReasons.push("formal_mode_requested");
|
|
158
|
+
} else if (requestedMode === "light") {
|
|
159
|
+
assurance = "quick";
|
|
160
|
+
triggerReasons.push("light_mode_requested");
|
|
161
|
+
} else if (requestedMode !== "off" && input.needsDurableEvidence) {
|
|
162
|
+
if (input.exactCommandCount === 1) {
|
|
163
|
+
assurance = "quick";
|
|
164
|
+
triggerReasons.push("single_bounded_verification");
|
|
165
|
+
} else {
|
|
166
|
+
triggerReasons.push("quick_exact_command_required");
|
|
167
|
+
}
|
|
168
|
+
} else if (requestedMode === "off") {
|
|
169
|
+
triggerReasons.push("off_mode_requested");
|
|
170
|
+
} else {
|
|
171
|
+
triggerReasons.push("durable_evidence_not_required");
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const recommendedMode = assurance === "formal"
|
|
175
|
+
? "formal"
|
|
176
|
+
: continuity === "project_driver"
|
|
177
|
+
? "managed"
|
|
178
|
+
: assurance === "quick"
|
|
179
|
+
? "light"
|
|
180
|
+
: "off";
|
|
181
|
+
const blockingReasons = forcedFormalReasons.length > 0
|
|
182
|
+
? forcedFormalReasons
|
|
183
|
+
: uniqueSorted(triggerReasons);
|
|
184
|
+
const downgradeAllowed = forcedFormalReasons.length === 0;
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
schemaVersion: "OwlCodaRunKitUsageModeRecommendationV1",
|
|
188
|
+
status: "mode_recommended",
|
|
189
|
+
requestedMode,
|
|
190
|
+
recommendedMode,
|
|
191
|
+
continuity,
|
|
192
|
+
assurance,
|
|
193
|
+
triggerReasons: uniqueSorted(triggerReasons),
|
|
194
|
+
rejectedLighterModes: rejectedModes(recommendedMode, blockingReasons),
|
|
195
|
+
estimatedOverhead: estimateOverhead(continuity, assurance),
|
|
196
|
+
nextCommand: nextCommand({ workspaceRoot: input.workspaceRoot, continuity, assurance }),
|
|
197
|
+
downgrade: {
|
|
198
|
+
allowed: downgradeAllowed,
|
|
199
|
+
reasonCodes: downgradeAllowed ? [] : forcedFormalReasons,
|
|
200
|
+
},
|
|
201
|
+
authorityBoundary: { ...AUTHORITY_BOUNDARY },
|
|
202
|
+
authorizationGranted: false,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function formatUsageModeHumanV1(result) {
|
|
207
|
+
const triggers = result.triggerReasons.length > 0
|
|
208
|
+
? result.triggerReasons.join(", ")
|
|
209
|
+
: "none";
|
|
210
|
+
const rejected = result.rejectedLighterModes.length > 0
|
|
211
|
+
? result.rejectedLighterModes
|
|
212
|
+
.map((entry) => `${entry.mode} (${entry.reasonCodes.join(", ")})`)
|
|
213
|
+
.join("; ")
|
|
214
|
+
: "none";
|
|
215
|
+
return [
|
|
216
|
+
`Recommended mode: ${result.recommendedMode}`,
|
|
217
|
+
`Continuity: ${result.continuity}`,
|
|
218
|
+
`Assurance: ${result.assurance}`,
|
|
219
|
+
`Why: ${triggers}`,
|
|
220
|
+
`Rejected lighter modes: ${rejected}`,
|
|
221
|
+
`Estimated overhead: ${result.estimatedOverhead.additionalSteps} steps, ${result.estimatedOverhead.evidenceArtifacts} evidence artifacts`,
|
|
222
|
+
`Next command: ${result.nextCommand ?? "none"}`,
|
|
223
|
+
`User downgrade allowed: ${result.downgrade.allowed ? "yes" : "no"}`,
|
|
224
|
+
"Authority: no Git, release, deployment, production, or business authorization granted.",
|
|
225
|
+
"",
|
|
226
|
+
].join("\n");
|
|
227
|
+
}
|