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.
Files changed (27) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +61 -17
  3. package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +166 -0
  4. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +41 -16
  5. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  6. package/package.json +1 -1
  7. package/packages/attest/distribution-manifest-v1.json +11 -1
  8. package/packages/attest/src/quick.mjs +11 -1
  9. package/registry-release-policy-v1.json +5 -5
  10. package/sbom.spdx.json +5 -5
  11. package/scripts/runkit-contract/assurance-router.mjs +31 -21
  12. package/scripts/runkit-contract/core-contract.mjs +5 -1
  13. package/scripts/runkit-contract/inspect-presentation.mjs +38 -5
  14. package/scripts/runkit-contract/install-codex-skill.mjs +60 -1
  15. package/scripts/runkit-contract/onboarding-doctor.mjs +65 -19
  16. package/scripts/runkit-contract/profile-onboarding.mjs +601 -22
  17. package/scripts/runkit-contract/project-cli-resolver.mjs +6 -0
  18. package/scripts/runkit-contract/quick-attest.mjs +1 -0
  19. package/scripts/runkit-contract/quick-metrics.mjs +43 -5
  20. package/scripts/runkit-contract/quick-receipt.mjs +43 -5
  21. package/scripts/runkit-contract/quick-verify.mjs +20 -4
  22. package/scripts/runkit-contract/runkit-cli.mjs +278 -24
  23. package/scripts/runkit-contract/schemas/profile-reconcile-plan-v1.schema.json +107 -0
  24. package/scripts/runkit-contract/schemas/profile-reconcile-receipt-v1.schema.json +60 -0
  25. package/scripts/runkit-contract/schemas/usage-mode-recommendation-v1.schema.json +136 -0
  26. package/scripts/runkit-contract/templates/assurance-request.json +6 -0
  27. 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
+ }