@rightkit/release 0.2.151 → 0.2.154

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 (70) hide show
  1. package/LICENSE-APACHE +201 -0
  2. package/LICENSE-MIT +21 -0
  3. package/README.md +117 -8
  4. package/build-release.mjs +7 -2
  5. package/cli/right-release.mjs +18 -5
  6. package/committed-source.mjs +19 -1
  7. package/crate-graph.mjs +121 -0
  8. package/deps.mjs +0 -0
  9. package/development-signing.mjs +8 -0
  10. package/development.mjs +46 -13
  11. package/devtools-policy.json +5 -0
  12. package/devtools-policy.mjs +119 -0
  13. package/direct-bootstrap.mjs +9 -3
  14. package/dmg-build.mjs +19 -5
  15. package/dmg-layout.mjs +2 -2
  16. package/dmg-spec.mjs +97 -0
  17. package/execution-policy.mjs +17 -1
  18. package/github-release.mjs +25 -7
  19. package/github-upload-gate.mjs +84 -0
  20. package/hardeningscan.mjs +3 -0
  21. package/inventory-check.mjs +4 -4
  22. package/lsclean.sh +0 -0
  23. package/mac-bundle-signing.mjs +1 -1
  24. package/mac-sign-core.mjs +97 -0
  25. package/mac-signing-matrix.mjs +186 -0
  26. package/msix-appinstaller.mjs +4 -1
  27. package/msix-build.mjs +3 -6
  28. package/msix-config.mjs +5 -1
  29. package/msix-repair-guard.mjs +24 -0
  30. package/msix-repair-guard.ps1 +356 -0
  31. package/msix-signer.mjs +211 -0
  32. package/ownership-catalog-config.json +6 -7
  33. package/ownership-command.mjs +69 -8
  34. package/ownership-evidence.mjs +1 -1
  35. package/ownership-key-authority.mjs +1 -1
  36. package/ownership-key-command.mjs +15 -3
  37. package/ownership-key-elevation.mjs +11 -2
  38. package/ownership-mandatory-set.json +420 -0
  39. package/ownership-mandatory-set.mjs +273 -0
  40. package/ownership.mjs +2 -2
  41. package/package.json +25 -11
  42. package/packaged-app-identity.mjs +88 -0
  43. package/preflight.mjs +25 -3
  44. package/prune-r2.mjs +28 -3
  45. package/publication-gate.mjs +49 -10
  46. package/publish-cargo.mjs +21 -1
  47. package/publish-swift.mjs +78 -141
  48. package/publish-update.mjs +24 -2
  49. package/qa-build-features.mjs +216 -0
  50. package/qa-control-scan.mjs +435 -0
  51. package/registry-parity.mjs +18 -1
  52. package/release-invocation.mjs +2 -1
  53. package/release-state.mjs +9 -0
  54. package/release-switch-registry.json +33 -0
  55. package/release-switch-scan.mjs +452 -0
  56. package/release.mjs +320 -26
  57. package/renamed-away.mjs +29 -0
  58. package/rightkit-ownership.json +682 -709
  59. package/rightkit-sync.mjs +8 -4
  60. package/rightkit-version-policy.mjs +18 -0
  61. package/rightkit-versions.json +211 -127
  62. package/runtime-budget-classes.json +4 -0
  63. package/runtime-budgets.mjs +870 -0
  64. package/runtime-bundle-scan.mjs +61 -0
  65. package/sign-macos.mjs +28 -30
  66. package/sign-updater.mjs +5 -2
  67. package/swift-mac-dmg.mjs +7 -2
  68. package/swift-package-command.mjs +1 -1
  69. package/upload-large.mjs +0 -0
  70. package/upload-release.mjs +10 -37
@@ -18,6 +18,7 @@ import { spawnSync } from "./background-process.mjs";
18
18
  import { main as stagedMain } from "./ownership-staged.mjs";
19
19
  import { loadTrustedExternal } from "./ownership-approval.mjs";
20
20
  import { trustComparison } from "./ownership-policy-bundle.mjs";
21
+ import { isBlockingFinding } from "./ownership-verdict.mjs";
21
22
 
22
23
  const packageRoot = path.dirname(fileURLToPath(import.meta.url));
23
24
 
@@ -37,7 +38,7 @@ export function trustCompare(args, { print, trustLoader = loadTrustedExternal }
37
38
  return 0;
38
39
  }
39
40
 
40
- export async function main(args, { evidenceFactory = createOwnershipEvidence, gitRunner = spawnSync, print = (value) => console.log(value), trustLoader = loadTrustedExternal } = {}) {
41
+ export async function main(args, { evidenceFactory = createOwnershipEvidence, gitRunner = spawnSync, print = (value) => console.log(value), stderr = (line) => console.error(line), trustLoader = loadTrustedExternal } = {}) {
41
42
  if (args.includes("--help")) {
42
43
  print("right-release ownership [--app <dir>] [--json]\nright-release ownership --staged [--app <dir>] [--json] (pre-commit gate: scans the index, not the working tree)\nright-release ownership --write-manifest [--rightkit <dir>] [--approve-new-capabilities]\n--approve-new-capabilities permits new capability ids during local generation only. Existing owner conflicts are always refused. Protected installation authorization remains separate.");
43
44
  return 0;
@@ -58,20 +59,27 @@ export async function main(args, { evidenceFactory = createOwnershipEvidence, gi
58
59
  }
59
60
  const app = path.resolve(flag(args, "--app") ?? process.cwd());
60
61
  const top = gitRunner("git", ["-C", app, "rev-parse", "--show-toplevel"], { encoding: "utf8", windowsHide: true });
61
- if (top.status !== 0) return failClosed(args, "repository", "could not resolve --app to a Git repository");
62
+ if (top.status !== 0) return refuse(args, { type: "repository", message: "could not resolve --app to a Git repository" }, { print, stderr });
62
63
  const repoRoot = path.resolve(String(top.stdout).trim());
63
64
  let checked;
64
65
  try { checked = evidenceFactory(repoRoot, app); }
65
- catch (error) { return failClosed(args, "protected-evidence", error.message, print); }
66
+ catch (error) {
67
+ // A refusal from the protected checkout carries its check name and offending paths.
68
+ const blocked = error?.blocked ? [blockedCheck(error.blocked.check, error.message, error.blocked.paths)] : undefined;
69
+ return refuse(args, { type: "protected-evidence", message: error.message, blocked }, { print, stderr });
70
+ }
66
71
  const result = checked.result;
67
72
  if (args.includes("--audit-json")) return printAuditJson(result, print);
73
+ // A refusal names every blocking check on stderr and in the JSON `blocked` field. Advise-tier findings never refuse.
74
+ const blocked = checked.enrollmentPending || checked.ok ? undefined : blockingChecks(checked);
75
+ if (blocked) reportBlocked(blocked, stderr);
68
76
  if (args.includes("--json")) {
69
77
  const error = checked.enrollmentPending ? undefined : !checked.floor.ok
70
78
  ? { type: "ownership-floor", message: checked.floor.problems.join("; ") }
71
79
  : result.coverage.length
72
80
  ? { type: "ownership-coverage", message: `${result.coverage.length} ownership coverage gap(s)` }
73
81
  : undefined;
74
- print(JSON.stringify({ ok: checked.ok, ...(checked.enrollmentPending ? { enrollmentPending: true, warning: checked.warning } : {}), ...(error ? { error } : {}), rows: result.rows, violations: result.violations, covered: result.covered, problems: [...result.problems, ...checked.floor.problems], coverage: result.coverage, metadata: result.metadata }, null, 2));
82
+ print(JSON.stringify({ ok: checked.ok, ...(checked.enrollmentPending ? { enrollmentPending: true, warning: checked.warning } : {}), ...(error ? { error } : {}), ...(blocked ? { blocked } : {}), rows: result.rows, violations: result.violations, covered: result.covered, problems: [...result.problems, ...checked.floor.problems], coverage: result.coverage, metadata: result.metadata }, null, 2));
75
83
  } else {
76
84
  print(`RightKit ownership: ${path.basename(app)}`);
77
85
  print(formatTable(result.rows));
@@ -79,7 +87,7 @@ export async function main(args, { evidenceFactory = createOwnershipEvidence, gi
79
87
  print(` OWN COPY ${v.fingerprint} (${v.package}) ${v.file}${v.line ? `:${v.line}` : ""} ${v.text}`);
80
88
  }
81
89
  for (const p of result.problems) print(` ${checked.enrollmentPending ? "DIAGNOSTIC" : "EXCEPTION"} ${p}`);
82
- print(checked.enrollmentPending ? `[right-release] ${checked.warning}` : checked.ok ? "[right-release] ownership passed" : `[right-release] ownership FAILED: ${[...result.problems, ...checked.floor.problems].join("; ") || "move these to RightKit or add a reviewed, dated exception"}`);
90
+ print(verdictLine(checked, result, blocked));
83
91
  }
84
92
  // CI lanes may pass `--advisory-when-pending`: before approval keys are enrolled the scan asserts
85
93
  // nothing, so findings print as diagnostics and the step exits 0. Release lanes never pass it.
@@ -115,9 +123,62 @@ function printAuditJson(result, print) {
115
123
  }
116
124
  }
117
125
 
118
- function failClosed(args, type, message, print = (value) => console.log(value)) {
119
- if (args.includes("--audit-json")) { console.error(`[right-release] ownership audit failed: ${message}`); return 1; }
120
- if (args.includes("--json")) print(JSON.stringify({ ok: false, error: { type, message }, rows: [], violations: [], covered: [], problems: [message], coverage: [], metadata: null }, null, 2));
126
+ // Each refusal names its blocking check and the first offending paths. Stderr and the JSON `blocked` field carry the same list.
127
+ const BLOCKED_PATH_LIMIT = 10;
128
+
129
+ function blockedCheck(check, message, paths = []) {
130
+ return { check, message, count: paths.length, paths: paths.slice(0, BLOCKED_PATH_LIMIT) };
131
+ }
132
+
133
+ // The checks that refuse a scan that did not pass. Advise-tier findings are never listed here.
134
+ function blockingChecks(checked) {
135
+ const result = checked.result;
136
+ const checks = [];
137
+ const blocking = (result.violations ?? []).filter(isBlockingFinding);
138
+ if (blocking.length) checks.push(blockedCheck("ownership-violation", `${blocking.length} block-tier finding(s) duplicate RightKit owners`, blocking.map((entry) => entry.file).filter(Boolean)));
139
+ const floorProblems = checked.floor?.ok === false ? checked.floor.problems ?? [] : [];
140
+ if (floorProblems.length) checks.push(blockedCheck("ownership-floor", floorProblems.join("; ")));
141
+ const problems = (result.problems ?? []).filter((problem) => !floorProblems.includes(problem));
142
+ if (problems.length) checks.push(blockedCheck("ownership-problem", problems.join("; ")));
143
+ const gaps = result.coverage ?? [];
144
+ if (gaps.length) checks.push(blockedCheck("ownership-coverage", `${gaps.length} coverage gap(s): ${[...new Set(gaps.map((gap) => gap.reason))].join(", ")}`, gaps.map((gap) => gap.path)));
145
+ if (!checks.length) checks.push(blockedCheck("ownership-unknown", "scan reported not ok without a listed cause"));
146
+ return checks;
147
+ }
148
+
149
+ function reportBlocked(checks, stderr) {
150
+ for (const check of checks) {
151
+ stderr(`[right-release] refused by check ${check.check}: ${check.message}`);
152
+ for (const file of check.paths) stderr(` - ${file}`);
153
+ if (check.count > check.paths.length) stderr(` ... and ${check.count - check.paths.length} more`);
154
+ }
155
+ }
156
+
157
+ function tierCounts(violations) {
158
+ const counts = { block: 0, advise: 0 };
159
+ for (const violation of violations) counts[isBlockingFinding(violation) ? "block" : "advise"] += 1;
160
+ return counts;
161
+ }
162
+
163
+ // The human verdict line. Advise-only results pass and say so; a refusal names its checks.
164
+ function verdictLine(checked, result, blocked) {
165
+ if (checked.enrollmentPending) return `[right-release] ${checked.warning}`;
166
+ if (checked.ok) {
167
+ const counts = tierCounts(result.violations ?? []);
168
+ return counts.advise
169
+ ? `[right-release] ownership ADVISE: block ${counts.block}, advise ${counts.advise} (reported, not refused)`
170
+ : "[right-release] ownership passed";
171
+ }
172
+ const hint = blocked.some((entry) => entry.check === "ownership-violation") ? "; move these to RightKit or add a reviewed, dated exception" : "";
173
+ return `[right-release] ownership FAILED: ${blocked.map((entry) => `${entry.check}: ${entry.message}`).join("; ")}${hint}`;
174
+ }
175
+
176
+ // Refusal before any verdict: unresolved repository, protected evidence, or a dirty protected checkout.
177
+ function refuse(args, { type, message, blocked }, { print = (value) => console.log(value), stderr = (line) => console.error(line) } = {}) {
178
+ const checks = blocked ?? [blockedCheck(type, message)];
179
+ reportBlocked(checks, stderr);
180
+ if (args.includes("--audit-json")) { stderr(`[right-release] ownership audit failed: ${message}`); return 1; }
181
+ if (args.includes("--json")) print(JSON.stringify({ ok: false, error: { type, message }, blocked: checks, rows: [], violations: [], covered: [], problems: [message], coverage: [], metadata: null }, null, 2));
121
182
  else print(`[right-release] ownership FAILED: ${message}`);
122
183
  return 1;
123
184
  }
@@ -90,7 +90,7 @@ function createEvidence(repoRoot, {
90
90
  : { ok: false, problems: ["protected ownership floor is unavailable until trust registry enrollment"] };
91
91
  if (bundleProblem) { floor.ok = false; floor.problems = [bundleProblem, ...floor.problems.filter((problem) => !problem.startsWith("ownership policy bundle"))]; }
92
92
  // Real paths on both sides: Git's root is resolved (macOS /private/var), callers may pass /var or /tmp.
93
- const real = (p) => { try { return realpathSync(p); } catch { return path.resolve(p); } };
93
+ const real = (p) => { try { return realpathSync.native(p); } catch { return path.resolve(p); } };
94
94
  const requestedRoot = path.relative(real(root), real(scanRoot)).replaceAll("\\", "/") || ".";
95
95
  if (requestedRoot.startsWith("../") || requestedRoot === ".." || path.isAbsolute(requestedRoot)) throw new Error("ownership evidence scan root must be inside repository");
96
96
  const evidenceDiagnostics = [];
@@ -37,7 +37,7 @@ function validateAuthority(authority) {
37
37
  if (typeof authority.checkoutPath !== "string" || !path.isAbsolute(authority.checkoutPath) || authority.checkoutPath.includes("\0")) throw new Error("protected key authority checkoutPath must be absolute");
38
38
  const repositoryIdentity = canonicalRepositoryIdentity(authority.repositoryIdentity);
39
39
  if (repositoryIdentity !== authority.repositoryIdentity) throw new Error("protected key authority identity must be canonical");
40
- const checkoutPath = realpathSync(authority.checkoutPath);
40
+ const checkoutPath = realpathSync.native(authority.checkoutPath);
41
41
  if (!statSync(checkoutPath).isDirectory()) throw new Error("protected key authority checkoutPath must be a directory");
42
42
  return { repositoryIdentity, checkoutPath };
43
43
  }
@@ -41,7 +41,8 @@ function readLog(root) {
41
41
  }
42
42
  function ensureCleanMetadata(root, run, rels) {
43
43
  for (const rel of rels) assertSafeMetadataParents(root, rel);
44
- const result = run("git", ["-C", root, "status", "--porcelain=v1", "--untracked-files=all"]);
44
+ // Limited to the metadata paths: an unscoped status of a large checkout overflows the child's output buffer.
45
+ const result = run("git", ["-C", root, "status", "--porcelain=v1", "--untracked-files=all", "--", ...rels]);
45
46
  if (result.status !== 0) throw new Error("could not inspect repository metadata status");
46
47
  const status = String(result.stdout ?? "");
47
48
  for (const rel of rels) {
@@ -49,7 +50,7 @@ function ensureCleanMetadata(root, run, rels) {
49
50
  }
50
51
  }
51
52
  function metadataIsDirty(root, run) {
52
- const r = run("git", ["-C", root, "status", "--porcelain=v1", "--untracked-files=all"]);
53
+ const r = run("git", ["-C", root, "status", "--porcelain=v1", "--untracked-files=all", "--", LOG, REQUEST_DIR]);
53
54
  if (r.status !== 0) throw new Error("could not inspect repository metadata status");
54
55
  return String(r.stdout ?? "").split(/\r?\n/).some((line) => {
55
56
  const file = line.slice(3).replaceAll('"', "");
@@ -355,7 +356,7 @@ export async function main(args, deps = {}) {
355
356
  try {
356
357
  if (!platform) throw new Error(`ownership key management unsupported platform: ${process.platform}`);
357
358
  // Delegate BEFORE taking the command lock: the desktop-session child needs that same lock.
358
- if (args[0] === "trust-sync" && needsDesktopDelegation({ hostPlatform: deps.hostPlatform, env: deps.env, run, elevatedCheck: deps.elevatedCheck })) return delegateTrustSync(deps, run, print);
359
+ if (args[0] === "trust-sync" && !args.includes("--check") && needsDesktopDelegation({ hostPlatform: deps.hostPlatform, env: deps.env, run, elevatedCheck: deps.elevatedCheck })) return delegateTrustSync(deps, run, print);
359
360
  const helper = deps.helper ?? helperFor(run);
360
361
  const lockPath = deps.lockPath ?? path.join(os.tmpdir(), "rightkit-key-command.lock");
361
362
  let lockFd;
@@ -524,6 +525,17 @@ async function trustSync(_args, d) {
524
525
  if (!trust?.trust?.rootFingerprint) throw new Error("protected root trust is not enrolled on this host; run ownership enroll");
525
526
  if (trust.trust.rootFingerprint !== pin) throw new Error("committed key log root does not match this host's protected trust");
526
527
  const baseline = d.baseline ?? parse(readFileSync(path.join(here, "ownership-default-policy.json"), "utf8"), "ownership default policy");
528
+ if (_args.includes("--check")) {
529
+ // Read-only: say whether a sync would ask for approval, and for what. Asks nothing, elevates nothing, writes no trust.
530
+ const request = makeTrustRequest(log, pin, baseline, d.authority);
531
+ try {
532
+ const summary = (d.summarize ?? summarizeRequestFile)({ file: request.file, digest: request.digest, existing: trust.trust, ...(d.summaryInputs ?? {}) });
533
+ if (!summary.changed) { d.print("trust already current: no approval needed"); return 0; }
534
+ d.print(summary.lines.join("\n"));
535
+ d.print("approval needed: trust-sync on this host would ask for the change above");
536
+ return 3;
537
+ } finally { await import("node:fs").then(({ unlinkSync }) => unlinkSync(request.file)).catch(() => {}); }
538
+ }
527
539
  const installed = await installCheckpoint(log, pin, baseline, d);
528
540
  if (installed === false) { d.print("trust already current"); return 0; }
529
541
  postVerify(d.root, log, d);
@@ -169,7 +169,7 @@ export function capSummary(lines, { maxLines = 16, maxChars = 1600 } = {}) {
169
169
 
170
170
  export function buildMacConfirmScript(text) {
171
171
  // The text field takes keyboard focus, so stray typing never presses a button; approval needs "yes" typed.
172
- return `display dialog ${appleScriptText(`${text}\n\nType yes below, then click Approve.`)} default answer "" with title ${appleScriptText(CONFIRM_TITLE)} buttons {"Cancel","Approve\u2026"} default button "Approve\u2026" cancel button "Cancel" with icon caution`;
172
+ return `display dialog ${appleScriptText(`${text}\n\nType the word yes below (not your password), then click Approve.`)} default answer "" with title ${appleScriptText(CONFIRM_TITLE)} buttons {"Cancel","Approve\u2026"} default button "Approve\u2026" cancel button "Cancel" with icon caution`;
173
173
  }
174
174
 
175
175
  export const WINDOWS_APPROVE_PREFIX = "RK_CONFIRM_APPROVED:";
@@ -189,7 +189,16 @@ export function buildWindowsConfirmScript(text, nonce = "") {
189
189
 
190
190
  function readLineSync() {
191
191
  const buf = Buffer.alloc(1); let line = "";
192
- for (;;) { let n = 0; try { n = readSync(0, buf, 0, 1, null); } catch { return line; } if (n === 0 || buf[0] === 10) return line; line += String.fromCharCode(buf[0]); }
192
+ for (;;) {
193
+ let n = 0;
194
+ try { n = readSync(0, buf, 0, 1, null); } catch (error) {
195
+ // A terminal whose stdin is non-blocking reports "no input yet" as EAGAIN: wait for the human, do not treat it as an answer.
196
+ if (error?.code === "EAGAIN") { Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50); continue; }
197
+ return line;
198
+ }
199
+ if (n === 0 || buf[0] === 10) return line;
200
+ line += String.fromCharCode(buf[0]);
201
+ }
193
202
  }
194
203
 
195
204
  /**
@@ -0,0 +1,420 @@
1
+ {
2
+ "schema": 1,
3
+ "source": {
4
+ "table": "private-audits/TABLE1-MANDATORY.md:56-78 (M1-M23 requirement catalog)",
5
+ "versions": "private-audits/inventory/REQUIRED-VERSIONS.md (source versions at rightkit HEAD a15e62fd, registry readback 2026-10-13)",
6
+ "note": "min = required version (source version at P1.7 HEAD). registryMax = highest registry version at readback; where registryMax < min the required version is not yet published, so every consumer fails until it ships."
7
+ },
8
+ "defaultProfile": "desktop",
9
+ "defaultApps": [
10
+ "heardright",
11
+ "scraperight",
12
+ "coderight",
13
+ "cutright",
14
+ "genright",
15
+ "postright",
16
+ "viewright",
17
+ "mailright",
18
+ "pulse",
19
+ "ember"
20
+ ],
21
+ "appDeclarationFile": "rightkit-mandatory.json",
22
+ "profiles": {
23
+ "desktop": {
24
+ "description": "Default: every desktop app owes every row unless the app declares N/A with a reason."
25
+ },
26
+ "apple": {
27
+ "description": "Table 1-Apple is an unratified draft. Profile is supported by the data model; no Apple rows are shipped.",
28
+ "ratified": false
29
+ }
30
+ },
31
+ "rows": [
32
+ {
33
+ "id": "M1",
34
+ "name": "Window host",
35
+ "profile": "desktop",
36
+ "owners": [
37
+ {
38
+ "name": "rightkit-tauri-shell",
39
+ "ecosystem": "cargo",
40
+ "min": "0.3.0"
41
+ },
42
+ {
43
+ "name": "rightkit-device",
44
+ "ecosystem": "cargo",
45
+ "min": "0.1.3"
46
+ },
47
+ {
48
+ "name": "@rightkit/tauri",
49
+ "ecosystem": "npm",
50
+ "min": "0.2.0",
51
+ "registryMax": "0.2.0"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "id": "M2",
57
+ "name": "App frame",
58
+ "profile": "desktop",
59
+ "owners": [
60
+ {
61
+ "name": "@rightkit/react-shell",
62
+ "ecosystem": "npm",
63
+ "min": "0.6.4"
64
+ },
65
+ {
66
+ "name": "@rightkit/platform-ui",
67
+ "ecosystem": "npm",
68
+ "min": "0.2.8",
69
+ "registryMax": "0.2.7"
70
+ },
71
+ {
72
+ "name": "@rightkit/theme",
73
+ "ecosystem": "npm",
74
+ "min": "0.2.3",
75
+ "registryMax": "0.2.3"
76
+ }
77
+ ]
78
+ },
79
+ {
80
+ "id": "M3",
81
+ "name": "Engine supervision",
82
+ "profile": "desktop",
83
+ "owners": [
84
+ {
85
+ "name": "rightkit-job-queue",
86
+ "ecosystem": "cargo",
87
+ "min": "0.3.0"
88
+ },
89
+ {
90
+ "name": "rightkit-child-process",
91
+ "ecosystem": "cargo",
92
+ "min": "0.4.0"
93
+ }
94
+ ]
95
+ },
96
+ {
97
+ "id": "M4",
98
+ "name": "Window/CLI protocol",
99
+ "profile": "desktop",
100
+ "owners": [
101
+ {
102
+ "name": "rightkit-job-queue",
103
+ "ecosystem": "cargo",
104
+ "min": "0.3.0"
105
+ },
106
+ {
107
+ "name": "rightkit-sidecar",
108
+ "ecosystem": "cargo",
109
+ "min": "0.2.0"
110
+ }
111
+ ]
112
+ },
113
+ {
114
+ "id": "M5",
115
+ "name": "Other child processes",
116
+ "profile": "desktop",
117
+ "owners": [
118
+ {
119
+ "name": "rightkit-child-process",
120
+ "ecosystem": "cargo",
121
+ "min": "0.4.0"
122
+ }
123
+ ]
124
+ },
125
+ {
126
+ "id": "M6",
127
+ "name": "Data location",
128
+ "profile": "desktop",
129
+ "owners": [
130
+ {
131
+ "name": "rightkit-daemon",
132
+ "ecosystem": "cargo",
133
+ "min": "0.2.0"
134
+ }
135
+ ]
136
+ },
137
+ {
138
+ "id": "M7",
139
+ "name": "Local services",
140
+ "profile": "desktop",
141
+ "owners": [
142
+ {
143
+ "name": "rightkit-daemon",
144
+ "ecosystem": "cargo",
145
+ "min": "0.2.0"
146
+ }
147
+ ]
148
+ },
149
+ {
150
+ "id": "M8",
151
+ "name": "Safe file writes",
152
+ "profile": "desktop",
153
+ "owners": [
154
+ {
155
+ "name": "rightkit-fs",
156
+ "ecosystem": "cargo",
157
+ "min": "0.4.0",
158
+ "registryMax": "0.3.3"
159
+ }
160
+ ]
161
+ },
162
+ {
163
+ "id": "M9",
164
+ "name": "Database",
165
+ "profile": "desktop",
166
+ "owners": [
167
+ {
168
+ "name": "rightkit-db",
169
+ "ecosystem": "cargo",
170
+ "min": "0.4.0",
171
+ "registryMax": "0.3.2"
172
+ }
173
+ ]
174
+ },
175
+ {
176
+ "id": "M10",
177
+ "name": "Settings",
178
+ "profile": "desktop",
179
+ "owners": [
180
+ {
181
+ "name": "rightkit-app-settings",
182
+ "ecosystem": "cargo",
183
+ "min": "0.2.0"
184
+ },
185
+ {
186
+ "name": "@rightkit/settings-ui",
187
+ "ecosystem": "npm",
188
+ "min": "0.4.4",
189
+ "registryMax": "0.4.3"
190
+ }
191
+ ]
192
+ },
193
+ {
194
+ "id": "M11",
195
+ "name": "Secrets",
196
+ "profile": "desktop",
197
+ "owners": [
198
+ {
199
+ "name": "rightkit-credentials",
200
+ "ecosystem": "cargo",
201
+ "min": "0.2.0"
202
+ }
203
+ ]
204
+ },
205
+ {
206
+ "id": "M12",
207
+ "name": "Licence/device",
208
+ "profile": "desktop",
209
+ "owners": [
210
+ {
211
+ "name": "rightkit-license",
212
+ "ecosystem": "cargo",
213
+ "min": "0.2.0",
214
+ "registryMax": "0.1.3"
215
+ },
216
+ {
217
+ "name": "rightkit-device",
218
+ "ecosystem": "cargo",
219
+ "min": "0.1.3"
220
+ },
221
+ {
222
+ "name": "@rightkit/license",
223
+ "ecosystem": "npm",
224
+ "min": "0.2.0",
225
+ "registryMax": "0.1.8"
226
+ }
227
+ ]
228
+ },
229
+ {
230
+ "id": "M13",
231
+ "name": "Pro entitlement",
232
+ "profile": "desktop",
233
+ "owners": [
234
+ {
235
+ "name": "rightkit-license",
236
+ "ecosystem": "cargo",
237
+ "min": "0.2.0",
238
+ "registryMax": "0.1.3"
239
+ }
240
+ ]
241
+ },
242
+ {
243
+ "id": "M14",
244
+ "name": "Legal/onboarding",
245
+ "profile": "desktop",
246
+ "owners": [
247
+ {
248
+ "name": "@rightkit/legal",
249
+ "ecosystem": "npm",
250
+ "min": "0.3.1",
251
+ "registryMax": "0.3.1"
252
+ },
253
+ {
254
+ "name": "@rightkit/legal-ui",
255
+ "ecosystem": "npm",
256
+ "min": "0.2.2",
257
+ "registryMax": "0.2.2"
258
+ },
259
+ {
260
+ "name": "@rightkit/onboarding",
261
+ "ecosystem": "npm",
262
+ "min": "0.3.3",
263
+ "registryMax": "0.3.2"
264
+ }
265
+ ]
266
+ },
267
+ {
268
+ "id": "M15",
269
+ "name": "Logging",
270
+ "profile": "desktop",
271
+ "owners": [
272
+ {
273
+ "name": "rightkit-logs",
274
+ "ecosystem": "cargo",
275
+ "min": "0.2.2",
276
+ "registryMax": "0.2.1"
277
+ },
278
+ {
279
+ "name": "@rightkit/logs",
280
+ "ecosystem": "npm",
281
+ "min": "0.1.4",
282
+ "registryMax": "0.1.4"
283
+ }
284
+ ]
285
+ },
286
+ {
287
+ "id": "M16",
288
+ "name": "Crash reporting",
289
+ "profile": "desktop",
290
+ "owners": [
291
+ {
292
+ "name": "rightkit-crash",
293
+ "ecosystem": "cargo",
294
+ "min": "0.2.2",
295
+ "registryMax": "0.2.1"
296
+ }
297
+ ]
298
+ },
299
+ {
300
+ "id": "M17",
301
+ "name": "HTTP",
302
+ "profile": "desktop",
303
+ "owners": [
304
+ {
305
+ "name": "rightkit-http",
306
+ "ecosystem": "cargo",
307
+ "min": "0.3.0",
308
+ "registryMax": "0.2.5"
309
+ }
310
+ ]
311
+ },
312
+ {
313
+ "id": "M18",
314
+ "name": "Updates",
315
+ "profile": "desktop",
316
+ "owners": [
317
+ {
318
+ "name": "@rightkit/app-update",
319
+ "ecosystem": "npm",
320
+ "min": "0.2.5"
321
+ }
322
+ ]
323
+ },
324
+ {
325
+ "id": "M19",
326
+ "name": "Release tooling",
327
+ "profile": "desktop",
328
+ "owners": [
329
+ {
330
+ "name": "@rightkit/release",
331
+ "ecosystem": "npm",
332
+ "min": "0.2.153",
333
+ "registryMax": "0.2.151",
334
+ "allowDev": true
335
+ }
336
+ ]
337
+ },
338
+ {
339
+ "id": "M20",
340
+ "name": "QA",
341
+ "profile": "desktop",
342
+ "owners": [
343
+ {
344
+ "name": "rightkit-qa",
345
+ "ecosystem": "cargo",
346
+ "min": "0.2.16",
347
+ "registryMax": "0.2.14",
348
+ "allowDev": true
349
+ },
350
+ {
351
+ "name": "rightkit-qa-driver",
352
+ "ecosystem": "cargo",
353
+ "min": "0.3.0",
354
+ "allowDev": true
355
+ },
356
+ {
357
+ "name": "@rightkit/qa",
358
+ "ecosystem": "npm",
359
+ "min": "0.4.8",
360
+ "registryMax": "0.4.7",
361
+ "allowDev": true
362
+ },
363
+ {
364
+ "name": "@rightkit/qa-chrome",
365
+ "ecosystem": "npm",
366
+ "min": "0.1.6",
367
+ "registryMax": "0.1.6",
368
+ "allowDev": true
369
+ }
370
+ ]
371
+ },
372
+ {
373
+ "id": "M22",
374
+ "name": "Agent surface",
375
+ "profile": "desktop",
376
+ "owners": [
377
+ {
378
+ "name": "rightkit-mcp",
379
+ "ecosystem": "cargo",
380
+ "min": "0.6.0",
381
+ "registryMax": "0.5.3"
382
+ }
383
+ ]
384
+ }
385
+ ],
386
+ "pendingRows": [
387
+ {
388
+ "id": "M21",
389
+ "name": "Test mode",
390
+ "profile": "desktop",
391
+ "owners": [],
392
+ "ownerPending": "No selected owner (D4 unratified); never fails."
393
+ },
394
+ {
395
+ "id": "M23",
396
+ "name": "Release-time switches",
397
+ "profile": "desktop",
398
+ "owners": [],
399
+ "ownerPending": "No selected owner (D4 unratified); never fails."
400
+ }
401
+ ],
402
+ "amendments": {
403
+ "v2.1": {
404
+ "description": "TABLE1-AMENDMENT-v2.1 (draft): @rightkit/tauri-shell joins M1. Opt-in via --amendment v2.1.",
405
+ "rows": [
406
+ {
407
+ "id": "M1",
408
+ "profile": "desktop",
409
+ "owners": [
410
+ {
411
+ "name": "@rightkit/tauri-shell",
412
+ "ecosystem": "npm",
413
+ "min": "0.1.1"
414
+ }
415
+ ]
416
+ }
417
+ ]
418
+ }
419
+ }
420
+ }