@rightkit/release 0.2.122 → 0.2.124

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/build-release.mjs CHANGED
@@ -241,7 +241,9 @@ try {
241
241
  }
242
242
  const ownershipEvidence = preflight.find((check) => check.name === "ownership")?.evidence;
243
243
  if (!ownershipEvidence) fail("ownership evidence was not produced by release preflight");
244
- if (ownershipEvidence.commit !== commit) fail("ownership scan source differs from release commit");
244
+ if (ownershipEvidence.commit !== commit) {
245
+ fail(`ownership scan commit ${ownershipEvidence.commit ?? "(missing)"} differs from release commit ${commit}; regenerate with right-release ownership at release commit ${commit}`);
246
+ }
245
247
  for (const check of preflight) {
246
248
  if (check.status !== "ok") console.error(`[right-release] preflight ${check.name}: ${check.detail}`);
247
249
  }
@@ -41,7 +41,7 @@ function createEvidence(repoRoot, {
41
41
  let enrollmentPending = trustState.enrolled !== true;
42
42
  if (committed && enrollmentPending && !allowPendingAdvisory && !trustState.trust?.rootFingerprint) throw new Error("protected ownership evidence requires both distinct, unrevoked approval keys to be enrolled");
43
43
  // A valid protected registry with incomplete key enrollment can still drive
44
- // a useful local backlog scan. It cannot produce a source binding or verify
44
+ // a useful local backlog scan. It cannot assert a protected source binding or verify
45
45
  // approval signatures, so this result is deliberately advisory.
46
46
  const useCommittedSource = committed && (!enrollmentPending || Boolean(trustState.trust?.rootFingerprint));
47
47
  if (useCommittedSource && requireCleanCheckout) assertCommittedCheckoutClean(root);
@@ -68,6 +68,7 @@ function createEvidence(repoRoot, {
68
68
  }
69
69
  if (useCommittedSource && requireCleanCheckout) assertCommittedCheckoutClean(root, { expectedCommit: source.commit, expectedTree: source.tree });
70
70
  const commit = useCommittedSource ? source.commit : git(root, ["rev-parse", "HEAD"], gitRunner);
71
+ if (!commit) throw new Error("ownership evidence: scan source did not produce a HEAD commit");
71
72
  const tree = useCommittedSource ? source.tree : git(root, ["rev-parse", `${commit}^{tree}`], gitRunner);
72
73
  const scannerVersion = SCANNER_VERSION;
73
74
  const catalogVersion = manifest.version ?? String(manifest.schema ?? "1");
@@ -154,7 +155,8 @@ function createEvidence(repoRoot, {
154
155
  schema: 1,
155
156
  repositoryIdentity: repositoryIdentity ? canonicalRepositoryIdentity(result.metadata?.repositoryIdentity) : null,
156
157
  source: enrollmentPending ? "enrollment-pending-advisory" : useCommittedSource ? "committed-git-tree" : "mutable-checkout-test-seam",
157
- ...(enrollmentPending ? {} : { commit, tree }),
158
+ commit,
159
+ ...(enrollmentPending ? {} : { tree }),
158
160
  catalogDigest,
159
161
  catalogVersion,
160
162
  scannerVersion,
@@ -37,6 +37,7 @@ function statusFor(row, report) {
37
37
  if (report.enrollmentPending && (row.status === "blocked" || row.status === "blocked-by-findings")) return "enrollment-pending";
38
38
  if (row.status === "blocked" || row.status === "blocked-by-findings") return "blocked";
39
39
  if (row.status === "excepted") return "excepted";
40
+ if (row.status === "qa-only") return "qa-only";
40
41
  if (row.status === "verified-integration") return "verified-integration";
41
42
  if (row.status === "declared-dependency" || row.uses === true) return "declared-dependency";
42
43
  if (report.coverage?.length) return "unscanned";
@@ -59,7 +60,7 @@ function exceptionRequests(root, app, report) {
59
60
 
60
61
  function asRecord(app, root, scope, report, manifest, versions, scannedAt) {
61
62
  const meta = report.metadata ?? {};
62
- const findings = (report.findings ?? []).map((f) => ({ fingerprint: f.fingerprint, capability: f.capability, file: f.file, line: f.line ?? 0, occurrenceHash: f.occurrenceHash ?? sha256(canonicalJson(f)) }));
63
+ const findings = (report.findings ?? []).map((f) => ({ fingerprint: f.fingerprint, capability: f.capability, file: f.file, line: f.line ?? 0, occurrenceHash: f.occurrenceHash ?? sha256(canonicalJson(f)), ...(f.qaOnly ? { qaOnly: true } : {}) }));
63
64
  const files = [...new Set(findings.map((f) => f.file))].sort();
64
65
  const contentDigestSource = meta.contentDigests ? "scanner-inputs" : "finding-occurrences-only";
65
66
  const contentDigests = meta.contentDigests ?? findings.map((f) => ({ path: f.file, digest: f.occurrenceHash, kind: "finding-occurrence" }));
package/ownership.mjs CHANGED
@@ -15,7 +15,7 @@ export const EXCEPTIONS_FILE = path.join(".rightkit", "ownership-exceptions.json
15
15
  const CODE_EXT = new Set([".rs", ".ts", ".tsx", ".js", ".mjs", ".cjs", ".jsx", ".swift", ".m", ".mm", ".c", ".cc", ".cpp", ".h", ".py", ".go", ".kt", ".java", ".cs"]);
16
16
  // Listed installed/build inputs retain explicit coverage diagnostics.
17
17
  const OPAQUE_DIRS = new Set(["target", ".build", "DerivedData", "Pods"]);
18
- export const SCANNER_VERSION = "2.2.0";
18
+ export const SCANNER_VERSION = "2.3.0";
19
19
  export const catalogDigest = (manifest) => sha256(manifest);
20
20
  const byteDigest = (bytes) => createHash("sha256").update(bytes).digest("hex");
21
21
  const inputDigest = (file) => file.digest ?? byteDigest(file.bytes);
@@ -249,7 +249,8 @@ function dependenciesFromInventory(appRoot, inventory) {
249
249
  const alias = spec.match(/^(?:npm:)(@[^/]+\/[^@]+|[^@]+)(?:@|$)/)?.[1];
250
250
  const localPath = spec.match(/^(?:file:|link:)(.+)$/)?.[1];
251
251
  const isGit = /^(git\+|https?:.*\.git)/i.test(spec);
252
- addDependency(deps, "npm", alias ?? name, rel, { alias: alias ? name : undefined, local: !!localPath, source: localPath ? spec.split(":")[0] : isGit ? "git" : spec.startsWith("workspace:") ? "workspace" : undefined });
252
+ const devOnly = field === "devDependencies" && !["dependencies", "optionalDependencies", "peerDependencies"].some((section) => Object.hasOwn(pkg[section] ?? {}, name));
253
+ addDependency(deps, "npm", alias ?? name, rel, { alias: alias ? name : undefined, local: !!localPath, source: localPath ? spec.split(":")[0] : isGit ? "git" : spec.startsWith("workspace:") ? "workspace" : undefined, ...(devOnly ? { devOnly: true } : {}) });
253
254
  if (localPath) {
254
255
  resolveLocalPath(appRoot, rel, localPath, coverage, rel, inventory, "local-npm-dependency", name);
255
256
  } else if (isGit) coverage.push({ path: rel, status: "unscanned", reason: `git-npm-dependency-source:${name}` });
@@ -359,13 +360,29 @@ function trustedProvenance(appRoot, inventory, trust, repositoryIdentity, covera
359
360
  return { excluded, classifications };
360
361
  }
361
362
 
363
+ function npmImportUsage(inventory) {
364
+ const usage = new Map(), qaSegments = new Set(["test", "tests", "__tests__", "e2e", "qa", "fixtures"]);
365
+ for (const { rel, bytes } of inventory.files) {
366
+ if (!CODE_EXT.has(path.posix.extname(rel).toLowerCase()) || !bytes) continue;
367
+ const qaPath = rel.split("/").some((segment) => qaSegments.has(segment)) || /(?:\.(?:test|spec)\.|^qa-.*\.)/.test(path.posix.basename(rel));
368
+ for (const match of bytes.toString("utf8").matchAll(/\bimport\b\s*(?:[\w$*{},\s]+?\bfrom\s*)?["']([^"']+)["']|\b(?:import|require)\s*\(\s*["']([^"']+)["']/g)) {
369
+ const specifier = match[1] ?? match[2];
370
+ const name = specifier.split("/").slice(0, specifier.startsWith("@") ? 2 : 1).join("/");
371
+ usage.set(name, (usage.get(name) ?? true) && qaPath);
372
+ }
373
+ }
374
+ return usage;
375
+ }
376
+
362
377
  function scanInternal(appRoot, manifest, { inventory = fileInventory(appRoot), trust, repositoryIdentity, repositoryRoot = appRoot, pathPrefix = "" } = {}) {
363
378
  const findings = [], coverage = [...inventory.coverage], deps = dependenciesFromInventory(appRoot, inventory), contentDigests = [...(deps.contentDigests ?? [])];
364
379
  coverage.push(...(deps.coverage ?? []));
365
380
  const provenance = trustedProvenance(appRoot, inventory, trust, repositoryIdentity, coverage, repositoryRoot, pathPrefix);
381
+ const npmUsage = npmImportUsage(inventory);
366
382
  for (const c of manifest.capabilities) for (const f of c.fingerprints.filter((x) => x.type === "dependency")) for (const d of deps) {
367
383
  if (d.ecosystem === f.ecosystem && (d.name === f.name || d.alias === f.name)) {
368
- findings.push({ fingerprint: f.id, capability: c.capability, package: c.package, file: d.file, line: 0, text: `${d.ecosystem} dependency ${d.alias ? `${d.alias} aliases ` : ""}${d.name}`, local: d.local, provenance: d.source });
384
+ const qaOnly = c.capability !== "desktop-qa" && d.ecosystem === "npm" && d.devOnly && npmUsage.get(d.alias ?? d.name) === true;
385
+ findings.push({ fingerprint: f.id, capability: c.capability, package: c.package, file: d.file, line: 0, text: `${d.ecosystem} dependency ${d.alias ? `${d.alias} aliases ` : ""}${d.name}`, local: d.local, provenance: d.source, ...(qaOnly ? { qaOnly: true } : {}) });
369
386
  }
370
387
  }
371
388
  const matchers = sourceMatchers(manifest);
@@ -469,16 +486,18 @@ export function evaluate(findings, exceptions, options = {}) {
469
486
  }
470
487
  const pending = enrollmentPending(options.trust, options.trustEnrollment);
471
488
  if (pending) warnings.push("approval enrollment pending; detected findings remain open backlog requests until distinct Windows & macOS keys are enrolled");
472
- for (const f of findings) if (!covered.some((x) => x.occurrenceHash === occurrenceHash(f))) violations.push({ ...f, occurrenceHash: occurrenceHash(f) });
473
- return { violations, covered, problems, warnings, enrollmentPending: pending };
489
+ const qaOnly = findings.filter((f) => f.qaOnly === true && f.capability !== "desktop-qa");
490
+ for (const f of findings) if (!qaOnly.includes(f) && !covered.some((x) => x.occurrenceHash === occurrenceHash(f))) violations.push({ ...f, occurrenceHash: occurrenceHash(f) });
491
+ return { violations, covered, qaOnly, problems, warnings, enrollmentPending: pending };
474
492
  }
475
493
 
476
494
  export function capabilityTable(manifest, deps, result) {
477
495
  return [...new Map(manifest.capabilities.map((c) => [c.capability, c.package])).entries()].map(([capability, owner]) => {
478
496
  const declared = deps.some((d) => d.name === owner || d.name === owner.replace(/^rightkit-/, "@rightkit/"));
479
497
  const own = result.violations.filter((x) => x.capability === capability), covered = result.covered.filter((x) => x.capability === capability);
480
- const status = own.length ? "blocked" : covered.length ? "excepted" : declared ? "declared-dependency" : result.coverage?.length ? "unscanned" : "not-detected";
481
- return { capability, owner, uses: declared, status, count: own.length + covered.length };
498
+ const qaOnly = (result.qaOnly ?? []).filter((x) => x.capability === capability && !covered.some((item) => item.occurrenceHash === x.occurrenceHash));
499
+ const status = own.length ? "blocked" : covered.length ? "excepted" : qaOnly.length ? "qa-only" : declared ? "declared-dependency" : result.coverage?.length ? "unscanned" : "not-detected";
500
+ return { capability, owner, uses: declared, status, count: own.length + covered.length + qaOnly.length };
482
501
  });
483
502
  }
484
503
  export function formatTable(rows) { return ["capability owner status", ...rows.map((x) => `${x.capability} ${x.owner} ${x.status}`)].join("\n"); }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.2.122",
3
+ "version": "0.2.124",
4
4
  "description": "Portable release CLI/SDK with fail-closed public GitHub Releases and private R2 + RightApps distribution.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "type": "module",
@@ -27,9 +27,9 @@
27
27
  "@rightkit/onboarding": "0.2.3",
28
28
  "@rightkit/pdf-ui": "0.1.2",
29
29
  "@rightkit/platform-ui": "0.1.2",
30
- "@rightkit/qa": "0.2.4",
30
+ "@rightkit/qa": "0.3.0",
31
31
  "@rightkit/qa-chrome": "0.1.2",
32
- "@rightkit/release": "0.2.122",
32
+ "@rightkit/release": "0.2.124",
33
33
  "@rightkit/settings-ui": "0.2.7",
34
34
  "@rightkit/shell": "0.1.0",
35
35
  "@rightkit/tauri": "0.1.1",
@@ -144,14 +144,17 @@
144
144
  "0.2.118",
145
145
  "0.2.119",
146
146
  "0.2.120",
147
- "0.2.121"
147
+ "0.2.121",
148
+ "0.2.122",
149
+ "0.2.123"
148
150
  ],
149
151
  "@rightkit/qa": [
150
152
  "0.1.0",
151
153
  "0.2.0",
152
154
  "0.2.1",
153
155
  "0.2.2",
154
- "0.2.3"
156
+ "0.2.3",
157
+ "0.2.4"
155
158
  ],
156
159
  "@rightkit/app-shell": [
157
160
  "0.1.0",
@@ -243,6 +246,9 @@
243
246
  "rightkit-playback": "0.1.0",
244
247
  "rightkit-process": "0.2.1",
245
248
  "rightkit-qa": "0.2.7",
249
+ "rightkit-qts": "0.1.1",
250
+ "rightkit-qts-ggml": "0.1.0",
251
+ "rightkit-qts-ggml-sys": "0.1.0",
246
252
  "rightkit-sandbox": "0.1.0",
247
253
  "rightkit-search": "0.1.0",
248
254
  "rightkit-secrets": "0.1.0",
@@ -290,6 +296,9 @@
290
296
  "rightkit-playback": "0.1.0",
291
297
  "rightkit-process": "0.2.1",
292
298
  "rightkit-qa": "0.2.7",
299
+ "rightkit-qts": "0.1.1",
300
+ "rightkit-qts-ggml": "0.1.0",
301
+ "rightkit-qts-ggml-sys": "0.1.0",
293
302
  "rightkit-sandbox": "0.1.0",
294
303
  "rightkit-search": "0.1.0",
295
304
  "rightkit-secrets": "0.1.0",
@@ -315,9 +324,9 @@
315
324
  "@rightkit/onboarding": "0.2.3",
316
325
  "@rightkit/pdf-ui": "0.1.2",
317
326
  "@rightkit/platform-ui": "0.1.2",
318
- "@rightkit/qa": "0.2.4",
327
+ "@rightkit/qa": "0.3.0",
319
328
  "@rightkit/qa-chrome": "0.1.2",
320
- "@rightkit/release": "0.2.121",
329
+ "@rightkit/release": "0.2.123",
321
330
  "@rightkit/settings-ui": "0.2.7",
322
331
  "@rightkit/shell": "0.1.0",
323
332
  "@rightkit/tauri": "0.1.1",