owlrunkit 0.18.0 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +13 -1
- package/docs/architecture/OWLCODA_RUNKIT_USAGE_MODE_SELECTION_PRODUCT_DECISION_20260811.md +142 -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 +6 -1
- package/packages/attest/src/quick.mjs +6 -1
- package/registry-release-policy-v1.json +5 -5
- package/sbom.spdx.json +5 -5
- package/scripts/runkit-contract/assurance-router.mjs +28 -20
- 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 +19 -1
- package/scripts/runkit-contract/profile-onboarding.mjs +597 -22
- package/scripts/runkit-contract/project-cli-resolver.mjs +3 -0
- package/scripts/runkit-contract/quick-attest.mjs +1 -0
- package/scripts/runkit-contract/quick-metrics.mjs +43 -5
- package/scripts/runkit-contract/quick-verify.mjs +15 -4
- package/scripts/runkit-contract/runkit-cli.mjs +196 -8
- package/scripts/runkit-contract/schemas/profile-reconcile-plan-v1.schema.json +105 -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 +229 -0
|
@@ -6,9 +6,40 @@ import {
|
|
|
6
6
|
} from "node:fs";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
decodeUtf8Strict,
|
|
11
|
+
parseJsonStrict,
|
|
12
|
+
readFileBytesBounded,
|
|
13
|
+
} from "../../packages/attest/src/formal.mjs";
|
|
14
|
+
import { quickSnapshotFingerprintValid } from "../../packages/attest/src/quick-receipt-contract.mjs";
|
|
15
|
+
import {
|
|
16
|
+
attestQuickReceipt,
|
|
17
|
+
validQuickReceiptShape,
|
|
18
|
+
} from "./quick-attest.mjs";
|
|
10
19
|
import { quickReceiptRoot } from "./quick-receipt.mjs";
|
|
11
20
|
|
|
21
|
+
const INVALID_ATTESTATION_ISSUES = new Set([
|
|
22
|
+
"attestation_material_missing",
|
|
23
|
+
"receipt_material_hash_mismatch",
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
function readHistoricalReceipt(receiptPath) {
|
|
27
|
+
const { bytes } = readFileBytesBounded(receiptPath);
|
|
28
|
+
const receipt = parseJsonStrict(decodeUtf8Strict(bytes));
|
|
29
|
+
if (!validQuickReceiptShape(receipt)) throw new Error("quick_receipt_schema_invalid");
|
|
30
|
+
const snapshotsValid = quickSnapshotFingerprintValid(receipt.workspaceBefore)
|
|
31
|
+
&& quickSnapshotFingerprintValid(receipt.workspaceAfter);
|
|
32
|
+
const sourceChanged = receipt.workspaceBefore.sourceFingerprint
|
|
33
|
+
!== receipt.workspaceAfter.sourceFingerprint;
|
|
34
|
+
const mutationBindingValid = receipt.mutationDecision === "source_unchanged"
|
|
35
|
+
? !sourceChanged && !receipt.issueCodes.includes("source_mutated_during_verification")
|
|
36
|
+
: sourceChanged && receipt.issueCodes.includes("source_mutated_during_verification");
|
|
37
|
+
if (!snapshotsValid || !mutationBindingValid) {
|
|
38
|
+
throw new Error("quick_receipt_source_binding_invalid");
|
|
39
|
+
}
|
|
40
|
+
return receipt;
|
|
41
|
+
}
|
|
42
|
+
|
|
12
43
|
function trustedReceiptRoot(workspaceRoot) {
|
|
13
44
|
const workspace = realpathSync(workspaceRoot);
|
|
14
45
|
const root = quickReceiptRoot(workspace);
|
|
@@ -65,10 +96,17 @@ export function readLocalQuickMetrics(workspaceRoot) {
|
|
|
65
96
|
};
|
|
66
97
|
for (const receiptPath of receiptPaths) {
|
|
67
98
|
try {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
99
|
+
const receipt = readHistoricalReceipt(receiptPath);
|
|
100
|
+
const attestation = attestQuickReceipt({ receiptPath, workspaceRoot });
|
|
101
|
+
if (attestation.issueCodes.some((issue) => INVALID_ATTESTATION_ISSUES.has(issue))) {
|
|
102
|
+
totals.invalid += 1;
|
|
103
|
+
} else if (receipt.mutationDecision === "invalidated_by_command_source_mutation") {
|
|
104
|
+
totals.sourceMutated += 1;
|
|
105
|
+
} else if (receipt.exitResult.exitCode === 0 && receipt.exitResult.signal === null) {
|
|
106
|
+
totals.passed += 1;
|
|
107
|
+
} else {
|
|
108
|
+
totals.failed += 1;
|
|
109
|
+
}
|
|
72
110
|
} catch {
|
|
73
111
|
totals.invalid += 1;
|
|
74
112
|
}
|
|
@@ -9,6 +9,14 @@ import {
|
|
|
9
9
|
} from "./quick-receipt.mjs";
|
|
10
10
|
import { captureWorkspaceSnapshot } from "./quick-workspace-snapshot.mjs";
|
|
11
11
|
|
|
12
|
+
function shellQuote(value) {
|
|
13
|
+
return `'${String(value).replaceAll("'", `'"'"'`)}'`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function quickAttestCommand(workspaceRoot, receiptPath) {
|
|
17
|
+
return `owlrunkit quick-attest --workspace ${shellQuote(workspaceRoot)} --receipt ${shellQuote(receiptPath)}`;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
function runExactCommand({ executable, argv, cwd, stdoutFd, stderrFd }) {
|
|
13
21
|
return new Promise((resolve) => {
|
|
14
22
|
let settled = false;
|
|
@@ -153,6 +161,7 @@ export async function runQuickVerification({ workspaceRoot, commandArgv }) {
|
|
|
153
161
|
}
|
|
154
162
|
|
|
155
163
|
if (sourceMutated) {
|
|
164
|
+
const attestCommand = quickAttestCommand(root, persisted.receiptPath);
|
|
156
165
|
return {
|
|
157
166
|
status: "quick_verification_source_mutated",
|
|
158
167
|
exitCode: 2,
|
|
@@ -162,13 +171,14 @@ export async function runQuickVerification({ workspaceRoot, commandArgv }) {
|
|
|
162
171
|
commandExitCode: execution.exitCode,
|
|
163
172
|
mutationDecision: "invalidated_by_command_source_mutation",
|
|
164
173
|
issueCodes,
|
|
165
|
-
attestCommand
|
|
174
|
+
attestCommand,
|
|
166
175
|
issues: [],
|
|
167
176
|
nextAllowedAction: "restore_source_or_run_a_new_quick_verification",
|
|
168
177
|
authorizationGranted: false,
|
|
169
178
|
};
|
|
170
179
|
}
|
|
171
180
|
if (execution.launchError || execution.exitCode !== 0) {
|
|
181
|
+
const attestCommand = quickAttestCommand(root, persisted.receiptPath);
|
|
172
182
|
return {
|
|
173
183
|
status: "quick_verification_failed",
|
|
174
184
|
exitCode: 1,
|
|
@@ -178,12 +188,13 @@ export async function runQuickVerification({ workspaceRoot, commandArgv }) {
|
|
|
178
188
|
commandExitCode: execution.exitCode,
|
|
179
189
|
mutationDecision: "source_unchanged",
|
|
180
190
|
issueCodes,
|
|
181
|
-
attestCommand
|
|
191
|
+
attestCommand,
|
|
182
192
|
issues: execution.launchError ? [execution.launchError] : [],
|
|
183
193
|
nextAllowedAction: "inspect_output_and_rerun",
|
|
184
194
|
authorizationGranted: false,
|
|
185
195
|
};
|
|
186
196
|
}
|
|
197
|
+
const attestCommand = quickAttestCommand(root, persisted.receiptPath);
|
|
187
198
|
return {
|
|
188
199
|
status: "quick_verification_passed",
|
|
189
200
|
exitCode: 0,
|
|
@@ -193,9 +204,9 @@ export async function runQuickVerification({ workspaceRoot, commandArgv }) {
|
|
|
193
204
|
commandExitCode: 0,
|
|
194
205
|
mutationDecision: "source_unchanged",
|
|
195
206
|
issueCodes,
|
|
196
|
-
attestCommand
|
|
207
|
+
attestCommand,
|
|
197
208
|
issues: [],
|
|
198
|
-
nextAllowedAction:
|
|
209
|
+
nextAllowedAction: attestCommand,
|
|
199
210
|
authorizationGranted: false,
|
|
200
211
|
};
|
|
201
212
|
}
|
|
@@ -89,6 +89,7 @@ import {
|
|
|
89
89
|
applyDetectedProfilesV2,
|
|
90
90
|
detectProfiles,
|
|
91
91
|
detectProfilesV2,
|
|
92
|
+
reconcileProfilesV2,
|
|
92
93
|
resolveProfilesImpact,
|
|
93
94
|
validateProfiles,
|
|
94
95
|
} from "./profile-onboarding.mjs";
|
|
@@ -100,6 +101,10 @@ import {
|
|
|
100
101
|
rollbackFleetRegistry,
|
|
101
102
|
} from "./fleet-discovery.mjs";
|
|
102
103
|
import { routeRunKitAssuranceV1 } from "./assurance-router.mjs";
|
|
104
|
+
import {
|
|
105
|
+
formatUsageModeHumanV1,
|
|
106
|
+
recommendUsageModeV1,
|
|
107
|
+
} from "./usage-mode.mjs";
|
|
103
108
|
import { deriveHumanStatusFromInspectV1 } from "./human-status.mjs";
|
|
104
109
|
import {
|
|
105
110
|
closeDeploymentExecuteChildRun,
|
|
@@ -163,20 +168,20 @@ Usage:
|
|
|
163
168
|
owlrunkit <command> [options]
|
|
164
169
|
|
|
165
170
|
Start here:
|
|
171
|
+
owlrunkit mode recommend --workspace .
|
|
166
172
|
owlrunkit doctor --workspace .
|
|
167
173
|
owlrunkit profiles detect --workspace . --dry-run
|
|
168
174
|
owlrunkit profiles detect --workspace . --apply
|
|
169
175
|
owlrunkit assurance route --workspace . --request <risk-facts.json>
|
|
170
176
|
owlrunkit adopt --workspace . --exact owlrunkit@${CORE_VERSION}
|
|
171
|
-
|
|
172
177
|
Quick Verification
|
|
173
178
|
owlrunkit quick-verify --workspace . -- <command> [args...]
|
|
174
179
|
Low-risk, source-bound reusable evidence without writer leases or acceptance.
|
|
175
180
|
|
|
176
181
|
Formal Delivery
|
|
177
182
|
owlrunkit formal <start|check|finish> --workspace . --run-id <id> [...]
|
|
178
|
-
Use for multi-writer,
|
|
179
|
-
|
|
183
|
+
Use for multi-writer, permissions, security, funds, irreversible change,
|
|
184
|
+
uncertain rollback, schema/data migration, or explicit Formal acceptance.
|
|
180
185
|
|
|
181
186
|
Frozen dirty source
|
|
182
187
|
owlrunkit candidate <freeze|verify|materialize> --workspace . [...]
|
|
@@ -204,6 +209,13 @@ Formal accepted does not grant Git or release authority; Quick cannot become For
|
|
|
204
209
|
`;
|
|
205
210
|
|
|
206
211
|
const COMMAND_HELP = Object.freeze({
|
|
212
|
+
adopt: `Usage:
|
|
213
|
+
owlrunkit adopt --workspace <directory> --exact owlrunkit@${CORE_VERSION}
|
|
214
|
+
[--registry <url>] [--timeout-ms <milliseconds>] [--json]
|
|
215
|
+
|
|
216
|
+
Verifies the exact registry release against the project-local install and writes
|
|
217
|
+
project-owned adoption evidence. It grants no Git or release authority.
|
|
218
|
+
`,
|
|
207
219
|
assurance: `Usage:
|
|
208
220
|
owlrunkit assurance route --workspace <directory> --request <risk-facts.json>
|
|
209
221
|
|
|
@@ -215,6 +227,38 @@ repository, release, deployment, or destructive authority.
|
|
|
215
227
|
|
|
216
228
|
Required:
|
|
217
229
|
--request <file> Structured assurance risk facts.
|
|
230
|
+
`,
|
|
231
|
+
mode: `Usage:
|
|
232
|
+
owlrunkit mode recommend --workspace <directory> [risk facts]
|
|
233
|
+
owlrunkit mode status --workspace <directory> [--json]
|
|
234
|
+
|
|
235
|
+
Selects continuity and assurance independently. Recommendation and status are
|
|
236
|
+
read-only and grant no Git, release, deployment, production, or business authority.
|
|
237
|
+
`,
|
|
238
|
+
"mode recommend": `Usage:
|
|
239
|
+
owlrunkit mode recommend --workspace <directory>
|
|
240
|
+
[--requested-mode auto|off|light|managed|formal]
|
|
241
|
+
[--needs-evidence] [--exact-command-count <count>] [--writer-count <count>]
|
|
242
|
+
[--cross-session] [--long-running] [--interruption-recovery]
|
|
243
|
+
[--may-mutate-project] [--external-side-effects]
|
|
244
|
+
[--irreversible] [--rollback-uncertain]
|
|
245
|
+
[--permission-sensitive] [--security-sensitive] [--funds-at-risk]
|
|
246
|
+
[--schema-or-data-migration] [--formal-acceptance]
|
|
247
|
+
[--risk-category <category>] [--json]
|
|
248
|
+
|
|
249
|
+
Returns one user mode plus the independent continuity and assurance selections.
|
|
250
|
+
Persisted mode preferences are not part of this release.
|
|
251
|
+
`,
|
|
252
|
+
"mode status": `Usage:
|
|
253
|
+
owlrunkit mode status --workspace <directory> [--json]
|
|
254
|
+
|
|
255
|
+
Reads the current mode preference source without creating project state.
|
|
256
|
+
`,
|
|
257
|
+
"quick-attest": `Usage:
|
|
258
|
+
owlrunkit quick-attest --workspace <directory> --receipt <receipt.json> [--json]
|
|
259
|
+
|
|
260
|
+
Attests one Quick Receipt against its bound materials and current workspace.
|
|
261
|
+
It never grants repository or business authority.
|
|
218
262
|
`,
|
|
219
263
|
fleet: `Usage:
|
|
220
264
|
owlrunkit fleet register-root --fleet-root <directory>
|
|
@@ -254,6 +298,7 @@ With no source option, discovers the complete fleet inside registered roots.
|
|
|
254
298
|
`,
|
|
255
299
|
profiles: `Usage:
|
|
256
300
|
owlrunkit profiles detect --workspace <directory> [--dry-run | --apply]
|
|
301
|
+
owlrunkit profiles reconcile --workspace <directory> (--dry-run | --apply)
|
|
257
302
|
owlrunkit profiles validate --workspace <directory>
|
|
258
303
|
owlrunkit profiles impact --workspace <directory> --changed <path>
|
|
259
304
|
`,
|
|
@@ -262,6 +307,12 @@ With no source option, discovers the complete fleet inside registered roots.
|
|
|
262
307
|
|
|
263
308
|
--dry-run reports deterministic candidates. --apply atomically adopts only a
|
|
264
309
|
high-confidence unambiguous profile.
|
|
310
|
+
`,
|
|
311
|
+
"profiles reconcile": `Usage:
|
|
312
|
+
owlrunkit profiles reconcile --workspace <directory> (--dry-run | --apply)
|
|
313
|
+
|
|
314
|
+
--dry-run previews deterministic additions and command updates without writing.
|
|
315
|
+
--apply atomically updates profiles.json and writes a hash-bound migration receipt.
|
|
265
316
|
`,
|
|
266
317
|
formal: `Usage:
|
|
267
318
|
owlrunkit formal start --workspace <directory> --run-id <id> [...]
|
|
@@ -1550,8 +1601,13 @@ function inspect(options) {
|
|
|
1550
1601
|
throw new Error(`Execution was not found: ${requestedRunId}`);
|
|
1551
1602
|
}
|
|
1552
1603
|
const onboarding = onboardingProjection(root, recovery);
|
|
1604
|
+
const projectDefinitionPath = runtimePath(root, "project", "definition.json");
|
|
1605
|
+
const projectDriver = pathEntryExists(projectDefinitionPath)
|
|
1606
|
+
? readTeamProjectStatusV1({ workspaceRoot: root })
|
|
1607
|
+
: null;
|
|
1553
1608
|
const summary = buildInspectSummary(result, {
|
|
1554
1609
|
maintenanceNextAction: onboarding.maintenanceNextAction ?? null,
|
|
1610
|
+
projectDriver,
|
|
1555
1611
|
});
|
|
1556
1612
|
const view = options.history
|
|
1557
1613
|
? { mode: "history" }
|
|
@@ -1563,7 +1619,13 @@ function inspect(options) {
|
|
|
1563
1619
|
execution: executions.find(execution => execution.runId === requestedRunId),
|
|
1564
1620
|
}
|
|
1565
1621
|
: { mode: "summary" };
|
|
1566
|
-
return {
|
|
1622
|
+
return {
|
|
1623
|
+
...result,
|
|
1624
|
+
summary,
|
|
1625
|
+
view,
|
|
1626
|
+
onboarding,
|
|
1627
|
+
...(projectDriver === null ? {} : { projectDriver }),
|
|
1628
|
+
};
|
|
1567
1629
|
}
|
|
1568
1630
|
|
|
1569
1631
|
function closeout(options) {
|
|
@@ -1784,6 +1846,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1784
1846
|
"fleet",
|
|
1785
1847
|
"formal",
|
|
1786
1848
|
"lease",
|
|
1849
|
+
"mode",
|
|
1787
1850
|
"profiles",
|
|
1788
1851
|
"project",
|
|
1789
1852
|
]).has(command);
|
|
@@ -1806,7 +1869,9 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1806
1869
|
}
|
|
1807
1870
|
}
|
|
1808
1871
|
const options = parseOptions(nested ? nestedOptions : optionValues, {
|
|
1809
|
-
multi: command === "
|
|
1872
|
+
multi: command === "mode"
|
|
1873
|
+
? ["risk-category"]
|
|
1874
|
+
: command === "profiles" && action === "impact"
|
|
1810
1875
|
? ["changed"]
|
|
1811
1876
|
: (command === "fleet" && action === "discover")
|
|
1812
1877
|
|| (command === "core-successor" && action === "plan")
|
|
@@ -1826,7 +1891,27 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1826
1891
|
: command === "project" && action === "decision"
|
|
1827
1892
|
? ["blocking-work-item", "option", "evidence"]
|
|
1828
1893
|
: [],
|
|
1829
|
-
boolean: command === "
|
|
1894
|
+
boolean: command === "mode" && action === "recommend"
|
|
1895
|
+
? [
|
|
1896
|
+
"json",
|
|
1897
|
+
"needs-evidence",
|
|
1898
|
+
"may-mutate-project",
|
|
1899
|
+
"external-side-effects",
|
|
1900
|
+
"cross-session",
|
|
1901
|
+
"long-running",
|
|
1902
|
+
"interruption-recovery",
|
|
1903
|
+
"dirty-parallel-workspace",
|
|
1904
|
+
"irreversible",
|
|
1905
|
+
"rollback-uncertain",
|
|
1906
|
+
"permission-sensitive",
|
|
1907
|
+
"security-sensitive",
|
|
1908
|
+
"funds-at-risk",
|
|
1909
|
+
"schema-or-data-migration",
|
|
1910
|
+
"formal-acceptance",
|
|
1911
|
+
]
|
|
1912
|
+
: command === "mode" && action === "status"
|
|
1913
|
+
? ["json"]
|
|
1914
|
+
: command === "inspect"
|
|
1830
1915
|
? ["json", "verbose", "history"]
|
|
1831
1916
|
: command === "init"
|
|
1832
1917
|
? ["json"]
|
|
@@ -1887,6 +1972,97 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
1887
1972
|
const requestPath = realpathSync(requireOption(options, "request"));
|
|
1888
1973
|
return routeRunKitAssuranceV1(readJson(requestPath));
|
|
1889
1974
|
}
|
|
1975
|
+
if (command === "mode") {
|
|
1976
|
+
const root = workspace(options);
|
|
1977
|
+
if (action === "recommend") {
|
|
1978
|
+
assertOnlyOptions(options, [
|
|
1979
|
+
"workspace",
|
|
1980
|
+
"requested-mode",
|
|
1981
|
+
"needs-evidence",
|
|
1982
|
+
"exact-command-count",
|
|
1983
|
+
"writer-count",
|
|
1984
|
+
"may-mutate-project",
|
|
1985
|
+
"external-side-effects",
|
|
1986
|
+
"cross-session",
|
|
1987
|
+
"long-running",
|
|
1988
|
+
"interruption-recovery",
|
|
1989
|
+
"dirty-parallel-workspace",
|
|
1990
|
+
"irreversible",
|
|
1991
|
+
"rollback-uncertain",
|
|
1992
|
+
"permission-sensitive",
|
|
1993
|
+
"security-sensitive",
|
|
1994
|
+
"funds-at-risk",
|
|
1995
|
+
"schema-or-data-migration",
|
|
1996
|
+
"formal-acceptance",
|
|
1997
|
+
"risk-category",
|
|
1998
|
+
"json",
|
|
1999
|
+
]);
|
|
2000
|
+
const recommendation = recommendUsageModeV1({
|
|
2001
|
+
workspaceRoot: root,
|
|
2002
|
+
requestedMode: options["requested-mode"] ?? "auto",
|
|
2003
|
+
needsDurableEvidence: options["needs-evidence"] === true,
|
|
2004
|
+
exactCommandCount: parseCanonicalNonnegativeSafeInteger(
|
|
2005
|
+
options["exact-command-count"] ?? "0",
|
|
2006
|
+
"exact-command-count",
|
|
2007
|
+
),
|
|
2008
|
+
writerCount: parseCanonicalNonnegativeSafeInteger(
|
|
2009
|
+
options["writer-count"] ?? "1",
|
|
2010
|
+
"writer-count",
|
|
2011
|
+
),
|
|
2012
|
+
mayMutateProject: options["may-mutate-project"] === true,
|
|
2013
|
+
externalSideEffects: options["external-side-effects"] === true,
|
|
2014
|
+
crossSession: options["cross-session"] === true,
|
|
2015
|
+
longRunning: options["long-running"] === true,
|
|
2016
|
+
interruptionRecovery: options["interruption-recovery"] === true,
|
|
2017
|
+
dirtyParallelWorkspace: options["dirty-parallel-workspace"] === true,
|
|
2018
|
+
irreversibleChange: options.irreversible === true,
|
|
2019
|
+
rollbackUncertain: options["rollback-uncertain"] === true,
|
|
2020
|
+
permissionSensitive: options["permission-sensitive"] === true,
|
|
2021
|
+
securitySensitive: options["security-sensitive"] === true,
|
|
2022
|
+
fundsAtRisk: options["funds-at-risk"] === true,
|
|
2023
|
+
schemaOrDataMigration: options["schema-or-data-migration"] === true,
|
|
2024
|
+
needsFormalAcceptance: options["formal-acceptance"] === true,
|
|
2025
|
+
riskCategories: options["risk-category"] ?? [],
|
|
2026
|
+
});
|
|
2027
|
+
return options.json === true
|
|
2028
|
+
? recommendation
|
|
2029
|
+
: withHumanOutput(recommendation, formatUsageModeHumanV1(recommendation));
|
|
2030
|
+
}
|
|
2031
|
+
if (action === "status") {
|
|
2032
|
+
assertOnlyOptions(options, ["workspace", "json"]);
|
|
2033
|
+
const recommendation = recommendUsageModeV1({
|
|
2034
|
+
workspaceRoot: root,
|
|
2035
|
+
requestedMode: "auto",
|
|
2036
|
+
needsDurableEvidence: false,
|
|
2037
|
+
exactCommandCount: 0,
|
|
2038
|
+
writerCount: 1,
|
|
2039
|
+
mayMutateProject: false,
|
|
2040
|
+
externalSideEffects: false,
|
|
2041
|
+
crossSession: false,
|
|
2042
|
+
longRunning: false,
|
|
2043
|
+
interruptionRecovery: false,
|
|
2044
|
+
dirtyParallelWorkspace: false,
|
|
2045
|
+
irreversibleChange: false,
|
|
2046
|
+
rollbackUncertain: false,
|
|
2047
|
+
permissionSensitive: false,
|
|
2048
|
+
securitySensitive: false,
|
|
2049
|
+
fundsAtRisk: false,
|
|
2050
|
+
schemaOrDataMigration: false,
|
|
2051
|
+
needsFormalAcceptance: false,
|
|
2052
|
+
riskCategories: [],
|
|
2053
|
+
});
|
|
2054
|
+
const status = {
|
|
2055
|
+
...recommendation,
|
|
2056
|
+
status: "mode_status",
|
|
2057
|
+
preferenceSource: "default_auto",
|
|
2058
|
+
persistedPreference: null,
|
|
2059
|
+
};
|
|
2060
|
+
return options.json === true
|
|
2061
|
+
? status
|
|
2062
|
+
: withHumanOutput(status, formatUsageModeHumanV1(status));
|
|
2063
|
+
}
|
|
2064
|
+
throw new Error("Usage: owlrunkit mode <recommend|status> --workspace <directory>");
|
|
2065
|
+
}
|
|
1890
2066
|
if (command === "fleet") {
|
|
1891
2067
|
if (action === "register-root") {
|
|
1892
2068
|
const registered = await registerFleetCoverageRoot({
|
|
@@ -3002,13 +3178,25 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
3002
3178
|
? applyDetectedProfilesV2({ workspaceRoot: root, detection })
|
|
3003
3179
|
: detection;
|
|
3004
3180
|
}
|
|
3181
|
+
if (action === "reconcile") {
|
|
3182
|
+
if (options.apply === true && options["dry-run"] === true) {
|
|
3183
|
+
throw new Error("profiles reconcile accepts either --apply or --dry-run, not both.");
|
|
3184
|
+
}
|
|
3185
|
+
if (options.apply !== true && options["dry-run"] !== true) {
|
|
3186
|
+
throw new Error("profiles reconcile requires --dry-run or --apply.");
|
|
3187
|
+
}
|
|
3188
|
+
return reconcileProfilesV2({
|
|
3189
|
+
workspaceRoot: root,
|
|
3190
|
+
apply: options.apply === true,
|
|
3191
|
+
});
|
|
3192
|
+
}
|
|
3005
3193
|
if (action === "validate") return validateProfiles({ workspaceRoot: root });
|
|
3006
3194
|
if (action === "impact") {
|
|
3007
3195
|
const changedPaths = options.changed ?? [];
|
|
3008
3196
|
if (changedPaths.length === 0) throw new Error("profiles impact requires --changed.");
|
|
3009
3197
|
return resolveProfilesImpact({ workspaceRoot: root, changedPaths });
|
|
3010
3198
|
}
|
|
3011
|
-
throw new Error("Usage: owlrunkit profiles <detect|validate|impact> [options]");
|
|
3199
|
+
throw new Error("Usage: owlrunkit profiles <detect|reconcile|validate|impact> [options]");
|
|
3012
3200
|
}
|
|
3013
3201
|
if (command === "lease") {
|
|
3014
3202
|
const root = workspace(options);
|
|
@@ -3151,7 +3339,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
|
|
|
3151
3339
|
return withControlTransaction(root, () => closeout(options));
|
|
3152
3340
|
}
|
|
3153
3341
|
if (command === "finish") return finishExecution(options);
|
|
3154
|
-
throw new Error("Usage: owlrunkit <init|doctor|adopt|assurance|fleet|profiles|project|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
|
|
3342
|
+
throw new Error("Usage: owlrunkit <init|doctor|adopt|mode|assurance|fleet|profiles|project|status|formal|candidate|deployment|core-successor|start|verify|repair|offline-export|offline-import|quick-verify|quick-attest|quick-metrics|finish|plan|inspect|lease|delivery|coverage-adopt|resume|verify-plan|resource-preflight|snapshot|visual-smoke|finalize|ready-for-commit|closeout> [options]");
|
|
3155
3343
|
} catch (error) {
|
|
3156
3344
|
return {
|
|
3157
3345
|
status: "invalid_input",
|
|
@@ -0,0 +1,105 @@
|
|
|
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
|
+
"addedProfileIds": { "$ref": "#/$defs/strings" },
|
|
20
|
+
"updatedProfiles": { "type": "array", "items": { "$ref": "#/$defs/update" } },
|
|
21
|
+
"proposedProfiles": { "$ref": "#/$defs/profiles" },
|
|
22
|
+
"issueCodes": { "$ref": "#/$defs/strings" },
|
|
23
|
+
"authorizationGranted": { "const": false }
|
|
24
|
+
},
|
|
25
|
+
"$defs": {
|
|
26
|
+
"hash": { "type": "string", "pattern": "^[a-f0-9]{64}$" },
|
|
27
|
+
"strings": { "type": "array", "uniqueItems": true, "items": { "type": "string", "minLength": 1 } },
|
|
28
|
+
"update": {
|
|
29
|
+
"type": "object",
|
|
30
|
+
"additionalProperties": false,
|
|
31
|
+
"required": ["profileId", "addedPaths", "addedCommandIds", "updatedCommandIds"],
|
|
32
|
+
"properties": {
|
|
33
|
+
"profileId": { "type": "string", "minLength": 1 },
|
|
34
|
+
"addedPaths": { "$ref": "#/$defs/strings" },
|
|
35
|
+
"addedCommandIds": { "$ref": "#/$defs/strings" },
|
|
36
|
+
"updatedCommandIds": { "$ref": "#/$defs/strings" }
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"command": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"additionalProperties": false,
|
|
42
|
+
"required": ["id", "cwd", "executable", "argv"],
|
|
43
|
+
"properties": {
|
|
44
|
+
"id": { "type": "string", "minLength": 1 },
|
|
45
|
+
"cwd": { "type": "string", "minLength": 1 },
|
|
46
|
+
"executable": { "oneOf": [{ "type": "null" }, { "type": "string", "minLength": 1 }] },
|
|
47
|
+
"argv": { "type": "array", "minItems": 1, "items": { "type": "string" } }
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"profile": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"additionalProperties": false,
|
|
53
|
+
"required": ["id", "paths", "role", "primary", "requiresProfileIds", "commands"],
|
|
54
|
+
"properties": {
|
|
55
|
+
"id": { "type": "string", "minLength": 1 },
|
|
56
|
+
"paths": { "$ref": "#/$defs/strings" },
|
|
57
|
+
"role": { "enum": ["primary", "supporting"] },
|
|
58
|
+
"primary": { "type": "boolean" },
|
|
59
|
+
"requiresProfileIds": { "$ref": "#/$defs/strings" },
|
|
60
|
+
"commands": { "type": "array", "items": { "$ref": "#/$defs/command" } }
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"profiles": {
|
|
64
|
+
"type": "object",
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"required": ["schemaVersion", "profiles"],
|
|
67
|
+
"properties": {
|
|
68
|
+
"schemaVersion": { "const": "OwlCodaRunKitProfilesV1" },
|
|
69
|
+
"profiles": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/profile" } }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"examples": [{
|
|
74
|
+
"schemaVersion": "OwlCodaRunKitProfilesReconcilePlanV1",
|
|
75
|
+
"status": "profiles_reconcile_ready",
|
|
76
|
+
"valid": true,
|
|
77
|
+
"exitCode": 0,
|
|
78
|
+
"dryRun": true,
|
|
79
|
+
"writesPerformed": 0,
|
|
80
|
+
"reconcileSha256": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
81
|
+
"detectionSha256": "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
82
|
+
"currentProfilesSha256": "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
|
|
83
|
+
"proposedProfilesSha256": "dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
|
84
|
+
"addedProfileIds": [],
|
|
85
|
+
"updatedProfiles": [{
|
|
86
|
+
"profileId": "node-quality",
|
|
87
|
+
"addedPaths": ["package.json"],
|
|
88
|
+
"addedCommandIds": ["npm-test"],
|
|
89
|
+
"updatedCommandIds": []
|
|
90
|
+
}],
|
|
91
|
+
"proposedProfiles": {
|
|
92
|
+
"schemaVersion": "OwlCodaRunKitProfilesV1",
|
|
93
|
+
"profiles": [{
|
|
94
|
+
"id": "node-quality",
|
|
95
|
+
"paths": ["package.json"],
|
|
96
|
+
"role": "primary",
|
|
97
|
+
"primary": true,
|
|
98
|
+
"requiresProfileIds": [],
|
|
99
|
+
"commands": [{ "id": "npm-test", "cwd": ".", "executable": "/usr/bin/node", "argv": ["/usr/lib/npm-cli.js", "run", "test"] }]
|
|
100
|
+
}]
|
|
101
|
+
},
|
|
102
|
+
"issueCodes": [],
|
|
103
|
+
"authorizationGranted": false
|
|
104
|
+
}]
|
|
105
|
+
}
|
|
@@ -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
|
+
}
|