@rightkit/release 0.6.31 → 0.6.32

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.
@@ -25,9 +25,10 @@
25
25
  //
26
26
  // Content checks run on an export of HEAD (exactly what is pushed), never on uncommitted work in the checkout.
27
27
  //
28
- // rightkit-gate [--base <ref>] [--plan] [--no-cache]
29
- // run the gate (default base origin/main); --plan prints the scans and runs none;
30
- // --no-cache (or RK_GATE_CACHE=0) reruns every scan
28
+ // rightkit-gate [--base <ref>] [--plan]
29
+ // run the gate (default base origin/main); --plan prints the scans and runs none.
30
+ // There is no cache bypass (GC-T14; Adrian: no no-cache option): a result is reused only under a key
31
+ // naming every input it read, so a changed input always reruns.
31
32
  // rightkit-gate --to check|compile|package|qualify|dev|test --route [--platform p] [--commit <rev>]
32
33
  // prints one JSON line (schema rightkit-gate.route/1) naming the route this host takes for the
33
34
  // stage and the refusal it would meet, and runs nothing: no ownership, snapshot, check, run record
@@ -79,7 +80,7 @@ import path from "node:path";
79
80
  import { hostTarget } from "../cross-targets.mjs";
80
81
  import { FOREIGN_PRE_COMMIT, retireHooksIn } from "../hook-retirement.mjs";
81
82
  import { fileURLToPath } from "node:url";
82
- import { BIOME_CONFIG_FILES, ESLINT_CONFIG_FILES, LOCAL_CLASS_B_JOBS, WORKSPACE_RUST_FILE, ancestorConfigChanged, ancestorDirsOf, canonicalJson, defaultSpawn, defaultSpawnAsync, digestScanSet, fileScanCache, findRustfmt, metadataArgs, planScans, runScans, scanToolPolicyFromPipeline, scanDeferrals, scanFailures } from "../deterministic-scans.mjs";
83
+ import { BIOME_CONFIG_FILES, CHECKER_IDENTITY, ESLINT_CONFIG_FILES, LOCAL_CLASS_B_JOBS, WORKSPACE_RUST_FILE, ancestorConfigChanged, ancestorDirsOf, canonicalJson, defaultSpawn, defaultSpawnAsync, digestScanSet, fileScanCache, findRustfmt, metadataArgs, planScans, runScans, scanToolPolicyFromPipeline, scanDeferrals, scanFailures } from "../deterministic-scans.mjs";
83
84
  import { changedLockNames, parseLock, reach } from "../lock-graph.mjs";
84
85
  import { resolveExecutor } from "../gate-executor.mjs";
85
86
  // The gate's own executor: an outer gate's cargo wrapper on PATH is never this gate's cargo (gate-executor.mjs `gateWrappers`).
@@ -111,7 +112,23 @@ export const PARTIAL_EXIT = 3;
111
112
  /** The platforms a door run can package: desktop (signed, sealed, installed QA, publisher upload) and mobile (signed, sealed, store test track). */
112
113
  export const PLATFORMS = Object.freeze(["mac", "win", "ios", "android"]);
113
114
  export const MOBILE_PLATFORMS = Object.freeze(new Set(["ios", "android"]));
114
- const USAGE = `usage: rightkit-gate --to check|compile|package|qualify|release [--platform mac|win|ios|android] [--tier patch|update] [--receipt-in <file>] [--receipt-out <file>] | rightkit-gate --to check|compile|package|qualify|release --unit npm:@rightkit/<name>|cargo:rightkit-<name> [--receipt-out <file>] | rightkit-gate --to tool --unit cargo:rightkit-qa|cargo:rightkit-credentials [--no-broker <exception-id>] [--receipt-out <file>] [--commit <rev>] (builds the unit's release binary from the commit and enrols it: cargo:rightkit-qa as the managed rightkit-qa runner, cargo:rightkit-credentials as the managed OS credential provider updater signing runs; Mac and Windows) | rightkit-gate --to check|compile|package|qualify --units-file <json {"units":[<selector>,...]} written for this run> | rightkit-gate --to check|compile|package|qualify --all-units-exception <id> (RightKit only: its unit is named on every run, or every unit by Adrian's entry in ci/all-units-exceptions.json of the commit) | rightkit-gate --to check|compile|package|qualify [--unit <selector>] --no-broker <exception-id> (Mac and Dell only; Adrian's recorded exception in .rightkit/broker-exceptions.json of the commit) | rightkit-gate --to dev|test [--commit <rev>] [-- <right-release args>] | rightkit-gate --to check|compile|package|qualify|dev|test --route [--platform mac|win|ios|android] [--commit <rev>] | rightkit-gate stage --platform mac|win [--tier patch|update] | rightkit-gate [--base <ref>] [--plan] [--no-cache] | rightkit-gate install-hook | rightkit-gate env-digest | rightkit-gate snapshot [--commit <rev>] [--verify [--env <sha256>]] [--json] | rightkit-gate live-scans --repo <root> | rightkit-gate clippy [<cargo clippy args>] | rightkit-gate env-digest [--snapshot <dir> --commit <sha>] | rightkit-gate check [--repo <dir>] [--commit <rev> | --worktree] [--rule-tier compile|release] [--only <rule,...>] [--list] [--print-baseline] [--baseline <file>] | rightkit-gate check --commit <sha> --full-a --snapshot <dir> | --version`;
115
+ // The gate's code identity for result keys: the checker (deterministic-scans and scan-digest, CHECKER_IDENTITY) and this file, where
116
+ // the keys are built. An unidentifiable checker gets a per-process value, so nothing is reused across runs.
117
+ let gateCodeIdentityMemo = null;
118
+ function gateCodeIdentity() {
119
+ if (gateCodeIdentityMemo === null) {
120
+ const h = createHash("sha256");
121
+ h.update(CHECKER_IDENTITY ?? `unidentified-${process.pid}-${Date.now()}`);
122
+ h.update("\0");
123
+ h.update(fs.readFileSync(fileURLToPath(import.meta.url)));
124
+ // The runtime cargo wrapper and the broker carrier it runs: their code decides what a carried compile did.
125
+ for (const f of ["cargo-target-shim.mjs", "snapshot-broker.mjs", "snapshot-request.mjs", "snapshot-metadata.mjs", "provenance-verify.mjs", "cargo-ledger.mjs"]) { h.update(`\0${f}\0`); h.update(fs.readFileSync(new URL(`../${f}`, import.meta.url))); }
126
+ gateCodeIdentityMemo = h.digest("hex").slice(0, 24);
127
+ }
128
+ return gateCodeIdentityMemo;
129
+ }
130
+
131
+ const USAGE = `usage: rightkit-gate --to check|compile|package|qualify|release [--platform mac|win|ios|android] [--tier patch|update] [--receipt-in <file>] [--receipt-out <file>] | rightkit-gate --to check|compile|package|qualify|release --unit npm:@rightkit/<name>|cargo:rightkit-<name> [--receipt-out <file>] | rightkit-gate --to tool --unit cargo:rightkit-qa|cargo:rightkit-credentials [--no-broker <exception-id>] [--receipt-out <file>] [--commit <rev>] (builds the unit's release binary from the commit and enrols it: cargo:rightkit-qa as the managed rightkit-qa runner, cargo:rightkit-credentials as the managed OS credential provider updater signing runs; Mac and Windows) | rightkit-gate --to check|compile|package|qualify --units-file <json {"units":[<selector>,...]} written for this run> | rightkit-gate --to check|compile|package|qualify --all-units-exception <id> (RightKit only: its unit is named on every run, or every unit by Adrian's entry in ci/all-units-exceptions.json of the commit) | rightkit-gate --to check|compile|package|qualify [--unit <selector>] --no-broker <exception-id> (Mac and Dell only; Adrian's recorded exception in .rightkit/broker-exceptions.json of the commit) | rightkit-gate --to dev|test [--commit <rev>] [-- <right-release args>] | rightkit-gate --to check|compile|package|qualify|dev|test --route [--platform mac|win|ios|android] [--commit <rev>] | rightkit-gate stage --platform mac|win [--tier patch|update] | rightkit-gate [--base <ref>] [--plan] | rightkit-gate install-hook | rightkit-gate env-digest | rightkit-gate snapshot [--commit <rev>] [--verify [--env <sha256>]] [--json] | rightkit-gate live-scans --repo <root> | rightkit-gate clippy [<cargo clippy args>] | rightkit-gate env-digest [--snapshot <dir> --commit <sha>] | rightkit-gate check [--repo <dir>] [--commit <rev> | --worktree] [--rule-tier compile|release] [--only <rule,...>] [--list] [--print-baseline] [--baseline <file>] | rightkit-gate check --commit <sha> --full-a --snapshot <dir> | --version`;
115
132
  /** The host platform (process.platform) that constructs each platform's artifact; a platform missing here (android) builds on any host. */
116
133
  const STAGE_NATIVE_HOST = Object.freeze({ mac: "darwin", ios: "darwin", win: "win32" });
117
134
 
@@ -3039,6 +3056,11 @@ export async function main(argv, io = {}) {
3039
3056
  gateFile: fileURLToPath(import.meta.url), ...io,
3040
3057
  };
3041
3058
  const args = [...argv];
3059
+ // The cache bypass is retired (GC-T14; workspace rule: never write or run a no-cache option). Refused by name so an old caller learns why.
3060
+ if (args.includes("--no-cache") || (io.env ?? process.env).RK_GATE_CACHE === "0") {
3061
+ (io.err ?? console.error)("rightkit-gate: --no-cache and RK_GATE_CACHE=0 are retired: results are reused only under a key naming every input they read, so a changed input always reruns (GC-T14)");
3062
+ return 2;
3063
+ }
3042
3064
  const option = (name) => { const i = args.indexOf(name); return i < 0 ? undefined : args[i + 1]; };
3043
3065
  // `clippy`: the Clippy route (../clippy-route.mjs; docs/GATE-CONTRACT.md §1 "Clippy route & batching"). Dispatched before the global
3044
3066
  // flags below because everything after `clippy` is cargo's own argv, verbatim (`-v`, `-h` and `--version` are cargo's).
@@ -3969,7 +3991,7 @@ export async function main(argv, io = {}) {
3969
3991
  try { envAtStart = classAEnvironmentDigest({ root: liveBase, gatePackage: o.gatePackage, files: live.files, spawn: o.spawn, includeCaches: false }); envStartWithCaches = classAEnvironmentDigest({ root: liveBase, gatePackage: o.gatePackage, files: live.files, spawn: o.spawn }); cachesAtStart = toolCacheManifest({ root: liveBase, files: live.files }); }
3970
3992
  catch (error) { o.err(`${tag} BLOCKED: ${error.message}`); return 1; }
3971
3993
  }
3972
- if (boundFullA && !args.includes("--no-cache") && o.env.RK_GATE_CACHE !== "0") {
3994
+ if (boundFullA) {
3973
3995
  try {
3974
3996
  const common = git("rev-parse", "--git-common-dir");
3975
3997
  if (failed(common)) throw new Error("no git directory");
@@ -3981,12 +4003,19 @@ export async function main(argv, io = {}) {
3981
4003
  const readBlob = (p) => fs.readFileSync(path.join(liveBase, p), "utf8");
3982
4004
  // The verdict also depends on what is not in the tree: the gate's own code and advisory data, the node and pnpm that run
3983
4005
  // the checks, the installed dependencies (pnpm's install state) and the platform. Any change there is a cache miss.
3984
- const environment = classAEnvironmentDigest({ root: liveBase, gatePackage: o.gatePackage, files, spawn: o.spawn });
4006
+ // Tool caches (vitest, tsc build info, ...) are left out: the scans write them themselves, so keying on them made every rerun miss.
4007
+ const environment = classAEnvironmentDigest({ root: liveBase, gatePackage: o.gatePackage, files, spawn: o.spawn, includeCaches: false });
3985
4008
  const digest = buildCacheDigest({ objectId, headTree: digestOf(files), rustUnits: rust.units, npmUnits: npm, cargoDir: ".", rustLocks, crateFeatures, listFiles, readBlob });
3986
- cache = { ...store, digest: (scan) => { const key = digest(scan); return key === null ? null : `${key}:${environment}:${plan.scopeDigest ?? "repository"}:${plan.scanSetDigest}`; } };
4009
+ // A row's key is its own inputs plus the environment; the run's scope and scan set are not inputs of one row, and keying on them
4010
+ // missed every row whenever one file joined or left the plan. build-helpers is never reused: it reads the host's trace list,
4011
+ // which is outside every key (and takes seconds).
4012
+ // A row field that shapes what the scan checks without changing its id or command is part of its key: a tsc row's tscWorkspace
4013
+ // (the sibling packages its check project maps; scopeScans narrows it per scope) changes the verdict on the same tree.
4014
+ const rowShape = (scan) => (scan.tscWorkspace ? `:tscws=${createHash("sha256").update(canonicalJson([...scan.tscWorkspace].sort())).digest("hex")}` : "");
4015
+ cache = { ...store, digest: (scan) => { if (/^build-helpers(:|$)/.test(scan.id)) return null; const key = digest(scan); return key === null ? null : `${key}:${environment}${rowShape(scan)}`; } };
3987
4016
  } catch { cache = null; }
3988
4017
  }
3989
- if (!classAOnly && !args.includes("--no-cache") && o.env.RK_GATE_CACHE !== "0") {
4018
+ if (!classAOnly) {
3990
4019
  try {
3991
4020
  const headSha = snapshotBCommit?.commit ?? text(git("rev-parse", "HEAD")).trim();
3992
4021
  const treeId = text(git("rev-parse", `${headSha}^{tree}`)).trim();
@@ -4009,7 +4038,10 @@ export async function main(argv, io = {}) {
4009
4038
  if (failed(r)) throw new Error(`git show failed: ${why(r)}`);
4010
4039
  return text(r);
4011
4040
  };
4012
- cache = { ...store, digest: buildCacheDigest({ objectId, headTree: treeId, rustUnits: rust.units, npmUnits: npm, cargoDir: ".", rustLocks, crateFeatures, listFiles, readBlob }) };
4041
+ // The gate's identity is part of every class B key: a changed check or key logic never reuses an older verdict.
4042
+ const rowDigest = buildCacheDigest({ objectId, headTree: treeId, rustUnits: rust.units, npmUnits: npm, cargoDir: ".", rustLocks, crateFeatures, listFiles, readBlob });
4043
+ const gateIdentity = gateCodeIdentity();
4044
+ cache = { ...store, digest: (scan) => { const key = rowDigest(scan); return key === null ? null : `${key}:${gateIdentity}`; } };
4013
4045
  }
4014
4046
  } catch { cache = null; }
4015
4047
  }
@@ -160,9 +160,11 @@ const BUILD_ENV_PREFIXES = ["CARGO_", "RUST", "SCCACHE_", "RK_", "RIGHTKIT_", "R
160
160
  // 3,809 entries and hit none across identical runs): the run's ownership handle, the attest nonce, the request-id file, the carried call's
161
161
  // snapshot request and cargo map, the direct receipt path.
162
162
  const BUILD_ENV_EXCLUDED = /^(RK_GATE_|RUSTRIGHT_QUIET_LEASE$|RIGHTKIT_BUILD_OWNERSHIP$|RIGHTKIT_CARRIED_CARGO_MAP$|RUSTRIGHT_ATTEST_NONCE$|RUSTRIGHT_REQUEST_ID_FILE$|RUSTRIGHT_SNAPSHOT_REQUEST$|RK_DIRECT_RECEIPT$)/;
163
- // A per-run temporary directory inside a value (the runtime cargo wrapper lives under a fresh `rk-gate-shim-XXXXXX`) is normalised: the
164
- // wrapper's own identity (`rk-cargo-shim-<identity>`) still keys the result, the random parent does not.
165
- const normalizeRunTemp = (value) => String(value).replace(/rk-gate-shim-[A-Za-z0-9]+/g, "rk-gate-shim-*");
163
+ // Per-run parts of a value are normalised: the runtime cargo wrapper's fresh parent (`rk-gate-shim-XXXXXX`) and its folder name
164
+ // (`rk-cargo-shim-<identity>`), whose identity hashes the run's broker section and so differs on every run (measured 2026-11-03: two identical
165
+ // Class A runs of rightkit-asr shared no key, only CARGO differed). The wrapper's CODE keys the result through the gate identity instead
166
+ // (the gate package files in the Class A environment digest; gateCodeIdentity for class B), so a changed wrapper still misses.
167
+ const normalizeRunTemp = (value) => String(value).replace(/rk-gate-shim-[A-Za-z0-9]+/g, "rk-gate-shim-*").replace(/rk-cargo-shim-[0-9a-f]+/g, "rk-cargo-shim-*");
166
168
  export const buildEnvName = (name) => {
167
169
  const upper = String(name).toUpperCase();
168
170
  if (BUILD_ENV_EXCLUDED.test(upper)) return false;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rightkit/release",
3
- "version": "0.6.31",
3
+ "version": "0.6.32",
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",
@@ -49,7 +49,7 @@
49
49
  "@rightkit/qa": "0.6.2",
50
50
  "@rightkit/qa-chrome": "0.1.12",
51
51
  "@rightkit/react-shell": "0.7.2",
52
- "@rightkit/release": "0.6.31",
52
+ "@rightkit/release": "0.6.32",
53
53
  "@rightkit/settings-ui": "0.7.0",
54
54
  "@rightkit/social-ui": "0.1.0",
55
55
  "@rightkit/system-ui": "0.1.0",
@@ -557,7 +557,7 @@
557
557
  "rightkit-disk-cleanup": "0.1.0",
558
558
  "rightkit-docs": "0.1.3",
559
559
  "rightkit-doctor": "0.1.0",
560
- "rightkit-document-workspace": "0.1.1",
560
+ "rightkit-document-workspace": "0.1.2",
561
561
  "rightkit-embed": "0.6.2",
562
562
  "rightkit-engine-host": "0.1.1",
563
563
  "rightkit-evidence": "0.1.0",
@@ -630,7 +630,7 @@
630
630
  "rightkit-spend": "0.2.0",
631
631
  "rightkit-sync": "0.2.2",
632
632
  "rightkit-system-monitor": "0.1.0",
633
- "rightkit-tauri-shell": "0.7.0",
633
+ "rightkit-tauri-shell": "0.7.1",
634
634
  "rightkit-terminal-ui": "0.1.0",
635
635
  "rightkit-text-polish": "0.1.0",
636
636
  "rightkit-time": "0.1.1",