@rightkit/release 0.6.31 → 0.6.33

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,36 @@ 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
+ /**
132
+ * The real cargo a broker host's runtime wrapper runs non-compiling calls with (GATE-CONTRACT GC-A11: metadata, fmt --check, deny,
133
+ * audit, package --list never go to the broker, which refuses them since rustright a2972f0): the broker's registered toolchain first
134
+ * (managed-cargo.mjs directCargoPath), else PATH without the RustRight shims; null when none exists.
135
+ */
136
+ function directCargoBinOf(o) {
137
+ const pk = o.platform === "win32" ? (Object.keys(o.env).find((k) => k.toUpperCase() === "PATH") ?? "Path") : "PATH";
138
+ const d = directCargoPath({ env: { ...o.env, PATH: o.env[pk] }, exists: o.exists, platform: o.platform, gateWrappers: true });
139
+ const names = o.platform === "win32" ? ["cargo.exe"] : ["cargo"];
140
+ for (const dir of String(d.path ?? "").split(o.platform === "win32" ? ";" : ":").filter(Boolean)) for (const n of names) { const c = path.join(dir, n); if (o.exists(c)) return c; }
141
+ return null;
142
+ }
143
+
144
+ 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
145
  /** The host platform (process.platform) that constructs each platform's artifact; a platform missing here (android) builds on any host. */
116
146
  const STAGE_NATIVE_HOST = Object.freeze({ mac: "darwin", ios: "darwin", win: "win32" });
117
147
 
@@ -2316,6 +2346,10 @@ async function bindUnitCargo({ o, git, root, ds, executor, which, scopeDirs, ext
2316
2346
  // Installing the carrier map sends nothing: the ownership records each carried request's sub-handle just before it is sent (snapshot-broker.mjs).
2317
2347
  const { brokerSection } = await import("../snapshot-broker.mjs");
2318
2348
  broker = { checkoutDirs: Object.fromEntries(workspaces.map((w) => [w, path.join(checkoutReal, ...(w === "." ? [] : w.split("/")))])), section: (o.brokerSection ?? brokerSection)(sb) };
2349
+ // The unit stages' non-compiling cargo (the `cargo fmt --check` step, metadata) runs directly with the broker's real cargo, as class A
2350
+ // does (GC-A11). Without it the wrapper sent `cargo fmt -p <crate> --check` to the broker, which refuses non-compiling requests: every
2351
+ // unit compile on the Mac and Dell stopped there (rightkit-document-workspace on the Dell, 2026-11-03, exit 125).
2352
+ realCargo = directCargoBinOf(o);
2319
2353
  } else {
2320
2354
  realCargo = realCargoOf(which("cargo"));
2321
2355
  if (!realCargo) return { refusal: "no cargo on PATH to wrap for the unit stages" };
@@ -3039,6 +3073,11 @@ export async function main(argv, io = {}) {
3039
3073
  gateFile: fileURLToPath(import.meta.url), ...io,
3040
3074
  };
3041
3075
  const args = [...argv];
3076
+ // 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.
3077
+ if (args.includes("--no-cache") || (io.env ?? process.env).RK_GATE_CACHE === "0") {
3078
+ (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)");
3079
+ return 2;
3080
+ }
3042
3081
  const option = (name) => { const i = args.indexOf(name); return i < 0 ? undefined : args[i + 1]; };
3043
3082
  // `clippy`: the Clippy route (../clippy-route.mjs; docs/GATE-CONTRACT.md §1 "Clippy route & batching"). Dispatched before the global
3044
3083
  // flags below because everything after `clippy` is cargo's own argv, verbatim (`-v`, `-h` and `--version` are cargo's).
@@ -3675,7 +3714,7 @@ export async function main(argv, io = {}) {
3675
3714
  // broker host never reaches here; a direct Windows host keeps the fail-closed static walk). Removed when the gate ends (cleanup).
3676
3715
  if (o.platform !== "win32" && rust.workspaces.length) {
3677
3716
  // GC-A11: the wrapper runs non-compiling calls directly, so a broker-mode wrapper also needs the real toolchain cargo (shims off PATH).
3678
- const directCargoBin = () => { const pk = o.platform === "win32" ? (Object.keys(o.env).find((k) => k.toUpperCase() === "PATH") ?? "Path") : "PATH"; const d = directCargoPath({ env: { ...o.env, PATH: o.env[pk] }, exists: o.exists, platform: o.platform, gateWrappers: true }); const names = o.platform === "win32" ? ["cargo.exe"] : ["cargo"]; for (const dir of String(d.path ?? "").split(o.platform === "win32" ? ";" : ":").filter(Boolean)) for (const n of names) { const c = path.join(dir, n); if (o.exists(c)) return c; } return null; };
3717
+ const directCargoBin = () => directCargoBinOf(o);
3679
3718
  const realCargo = wrapBroker ? (directCargoBin() ?? undefined) : realCargoOf(which("cargo"));
3680
3719
  if (!realCargo && !wrapBroker) { o.err(`${tag} BLOCKED: no cargo on PATH to wrap for the snapshot compile; nothing compiled`); return 1; }
3681
3720
  try {
@@ -3969,7 +4008,7 @@ export async function main(argv, io = {}) {
3969
4008
  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
4009
  catch (error) { o.err(`${tag} BLOCKED: ${error.message}`); return 1; }
3971
4010
  }
3972
- if (boundFullA && !args.includes("--no-cache") && o.env.RK_GATE_CACHE !== "0") {
4011
+ if (boundFullA) {
3973
4012
  try {
3974
4013
  const common = git("rev-parse", "--git-common-dir");
3975
4014
  if (failed(common)) throw new Error("no git directory");
@@ -3981,12 +4020,19 @@ export async function main(argv, io = {}) {
3981
4020
  const readBlob = (p) => fs.readFileSync(path.join(liveBase, p), "utf8");
3982
4021
  // 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
4022
  // 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 });
4023
+ // Tool caches (vitest, tsc build info, ...) are left out: the scans write them themselves, so keying on them made every rerun miss.
4024
+ const environment = classAEnvironmentDigest({ root: liveBase, gatePackage: o.gatePackage, files, spawn: o.spawn, includeCaches: false });
3985
4025
  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}`; } };
4026
+ // 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
4027
+ // missed every row whenever one file joined or left the plan. build-helpers is never reused: it reads the host's trace list,
4028
+ // which is outside every key (and takes seconds).
4029
+ // 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
4030
+ // (the sibling packages its check project maps; scopeScans narrows it per scope) changes the verdict on the same tree.
4031
+ const rowShape = (scan) => (scan.tscWorkspace ? `:tscws=${createHash("sha256").update(canonicalJson([...scan.tscWorkspace].sort())).digest("hex")}` : "");
4032
+ 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
4033
  } catch { cache = null; }
3988
4034
  }
3989
- if (!classAOnly && !args.includes("--no-cache") && o.env.RK_GATE_CACHE !== "0") {
4035
+ if (!classAOnly) {
3990
4036
  try {
3991
4037
  const headSha = snapshotBCommit?.commit ?? text(git("rev-parse", "HEAD")).trim();
3992
4038
  const treeId = text(git("rev-parse", `${headSha}^{tree}`)).trim();
@@ -4009,7 +4055,10 @@ export async function main(argv, io = {}) {
4009
4055
  if (failed(r)) throw new Error(`git show failed: ${why(r)}`);
4010
4056
  return text(r);
4011
4057
  };
4012
- cache = { ...store, digest: buildCacheDigest({ objectId, headTree: treeId, rustUnits: rust.units, npmUnits: npm, cargoDir: ".", rustLocks, crateFeatures, listFiles, readBlob }) };
4058
+ // The gate's identity is part of every class B key: a changed check or key logic never reuses an older verdict.
4059
+ const rowDigest = buildCacheDigest({ objectId, headTree: treeId, rustUnits: rust.units, npmUnits: npm, cargoDir: ".", rustLocks, crateFeatures, listFiles, readBlob });
4060
+ const gateIdentity = gateCodeIdentity();
4061
+ cache = { ...store, digest: (scan) => { const key = rowDigest(scan); return key === null ? null : `${key}:${gateIdentity}`; } };
4013
4062
  }
4014
4063
  } catch { cache = null; }
4015
4064
  }
@@ -175,7 +175,8 @@ export function decideCargoInvocation({ args, cwd, env, map, locate, realpath =
175
175
  *
176
176
  * Broker mode (`broker: { checkoutDirs, section }`, the Mac): `checkoutDirs` is the gate's { [cargoDir]: absolute path of that
177
177
  * workspace in the primary checkout } (the snapshot-to-checkout map, the only source of a request's primaryCwd) and `section` the
178
- * public part of the gate's snapshot broker (brokerSection). `realCargo` then only runs `version` and `help`; it may be null.
178
+ * public part of the gate's snapshot broker (brokerSection). `realCargo` then runs `version`, `help` and the non-compiling calls
179
+ * (GC-A11: metadata, fmt --check, deny, audit, package --list), which the broker refuses; null leaves those to fail at the broker.
179
180
  *
180
181
  * Windows, broker mode only (rightkit#1350, rustright#215): a child started without a shell finds only an .exe, so `bin/cargo.exe` is a
181
182
  * byte copy of the live RustRight client (`broker.section.client`) and `bin/gate-trampoline.json` beside it pins node, this module and the
@@ -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.33",
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.33",
53
53
  "@rightkit/settings-ui": "0.7.0",
54
54
  "@rightkit/social-ui": "0.1.0",
55
55
  "@rightkit/system-ui": "0.1.0",
@@ -527,7 +527,7 @@
527
527
  "rightkit-code-audit-provider-sdk": "0.1.0",
528
528
  "rightkit-color": "0.1.0",
529
529
  "rightkit-compose": "0.2.2",
530
- "rightkit-compositor": "0.1.0",
530
+ "rightkit-compositor": "0.2.0",
531
531
  "rightkit-content-plan": "0.1.1",
532
532
  "rightkit-content-proof": "0.1.0",
533
533
  "rightkit-context": "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.3",
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",