@codacy/verity-cli 0.35.3 → 0.35.5

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/bin/verity.js CHANGED
@@ -14732,7 +14732,9 @@ var SECURITY_PATTERNS = [
14732
14732
  /(^|\/)pom\.xml$/,
14733
14733
  /(^|\/)gradle\.lockfile$/
14734
14734
  ];
14735
- var PROD_SERVICE_URL = "https://ofcamwrjwrkazqvdchko.supabase.co/functions/v1";
14735
+ var PROD_SERVICE_ORIGIN = "https://server.verity.md";
14736
+ var LEGACY_PROD_SERVICE_ORIGIN = "https://ofcamwrjwrkazqvdchko.supabase.co";
14737
+ var PROD_SERVICE_URL = `${PROD_SERVICE_ORIGIN}/functions/v1`;
14736
14738
  var DEFAULT_SERVICE_URL = "".length > 0 ? "" : PROD_SERVICE_URL;
14737
14739
  var GITHUB_CLIENT_ID = "Iv23li88HxAi3ZrbYzWh";
14738
14740
  var GITHUB_DEVICE_CODE_URL = "https://github.com/login/device/code";
@@ -14823,6 +14825,36 @@ function rotateIfNeeded(file) {
14823
14825
  var import_promises = require("node:fs/promises");
14824
14826
  var import_node_path3 = require("node:path");
14825
14827
  var import_node_child_process2 = require("node:child_process");
14828
+
14829
+ // src/lib/service-origin.ts
14830
+ var LEGACY = new URL(LEGACY_PROD_SERVICE_ORIGIN);
14831
+ function canonicalServiceUrl(raw) {
14832
+ const trimmed = raw.trim();
14833
+ let u;
14834
+ try {
14835
+ u = new URL(trimmed);
14836
+ } catch {
14837
+ return raw;
14838
+ }
14839
+ if (u.protocol !== LEGACY.protocol || u.host.toLowerCase() !== LEGACY.host || u.username !== "" || u.password !== "") {
14840
+ return raw;
14841
+ }
14842
+ return trimmed.replace(/^https:\/\/[^/?#]+/i, PROD_SERVICE_ORIGIN);
14843
+ }
14844
+ function normalizeServiceUrl(raw) {
14845
+ const trimmed = canonicalServiceUrl(raw).trim().replace(/\/+$/, "");
14846
+ try {
14847
+ const u = new URL(trimmed);
14848
+ return `${u.protocol}//${u.host.toLowerCase()}${u.pathname.replace(/\/+$/, "")}`;
14849
+ } catch {
14850
+ return trimmed.toLowerCase();
14851
+ }
14852
+ }
14853
+ function sameServiceUrl(a, b) {
14854
+ return normalizeServiceUrl(a) === normalizeServiceUrl(b);
14855
+ }
14856
+
14857
+ // src/lib/credentials.ts
14826
14858
  function globalCredentialsPath() {
14827
14859
  return `${process.env.HOME}/.verity/credentials`;
14828
14860
  }
@@ -14843,7 +14875,8 @@ function parseCredentialLine(line2) {
14843
14875
  const rest = idx === -1 ? line2 : line2.slice(idx);
14844
14876
  const tokenMatch = rest.match(TOKEN_RE);
14845
14877
  if (!tokenMatch) return null;
14846
- const serviceUrl = rest.match(/service_url:\s*(https?:\/\/\S+)/)?.[1];
14878
+ const serviceUrlRaw = rest.match(/service_url:\s*(https?:\/\/\S+)/)?.[1];
14879
+ const serviceUrl = serviceUrlRaw != null ? canonicalServiceUrl(serviceUrlRaw) : void 0;
14847
14880
  const userIdRaw = rest.match(/user_id:\s*(\d+)/)?.[1];
14848
14881
  const email = rest.match(/email:\s*(\S+)/)?.[1];
14849
14882
  return {
@@ -14865,7 +14898,7 @@ function encodeRemoteKey(remote) {
14865
14898
  function serializeCredential(remote, rec) {
14866
14899
  const key = encodeRemoteKey(remote);
14867
14900
  const out2 = [key ? `${key} token: ${rec.token}` : `token: ${rec.token}`];
14868
- if (rec.serviceUrl) out2.push(`service_url: ${rec.serviceUrl}`);
14901
+ if (rec.serviceUrl) out2.push(`service_url: ${canonicalServiceUrl(rec.serviceUrl)}`);
14869
14902
  if (rec.userId != null) out2.push(`user_id: ${rec.userId}`);
14870
14903
  if (rec.email) out2.push(`email: ${rec.email}`);
14871
14904
  return out2.join(" ");
@@ -14938,7 +14971,7 @@ async function removeSupersededUserCredentials(loginServiceUrl, loginUserId) {
14938
14971
  let removed = 0;
14939
14972
  for (const line2 of content.split("\n")) {
14940
14973
  const parsed = parseCredentialLine(line2);
14941
- const superseded = parsed !== null && parsed.remote !== "" && parsed.rec.userId === loginUserId && (!parsed.rec.serviceUrl || parsed.rec.serviceUrl === loginServiceUrl);
14974
+ const superseded = parsed !== null && parsed.remote !== "" && parsed.rec.userId === loginUserId && (!parsed.rec.serviceUrl || sameServiceUrl(parsed.rec.serviceUrl, loginServiceUrl));
14942
14975
  if (superseded) {
14943
14976
  removed++;
14944
14977
  continue;
@@ -14990,9 +15023,10 @@ function parseLocalCredentialFile(content) {
14990
15023
  const tokenMatch = content.match(/token:\s*((?:gate_|verity_)[a-f0-9]+)/);
14991
15024
  if (!tokenMatch) return null;
14992
15025
  const userIdMatch = content.match(/^user_id:\s*(\d+)/m);
15026
+ const serviceUrl = content.match(/service_url:\s*(https?:\/\/\S+)/)?.[1];
14993
15027
  return {
14994
15028
  token: tokenMatch[1],
14995
- serviceUrl: content.match(/service_url:\s*(https?:\/\/\S+)/)?.[1],
15029
+ serviceUrl: serviceUrl != null ? canonicalServiceUrl(serviceUrl) : void 0,
14996
15030
  userId: userIdMatch ? Number(userIdMatch[1]) : void 0,
14997
15031
  email: content.match(/^email:\s*(\S+)/m)?.[1]
14998
15032
  };
@@ -15306,15 +15340,7 @@ async function checkTokenPin(token, targetUrl) {
15306
15340
  if (!stored.serviceUrl) return { attach: false, reason: "unpinnable" };
15307
15341
  return normalizeUrl(stored.serviceUrl) === normalizeUrl(targetUrl) ? { attach: true } : { attach: false, reason: "mismatch", mintedFor: stored.serviceUrl };
15308
15342
  }
15309
- function normalizeUrl(raw) {
15310
- const trimmed = raw.trim().replace(/\/+$/, "");
15311
- try {
15312
- const u = new URL(trimmed);
15313
- return `${u.protocol}//${u.host.toLowerCase()}${u.pathname.replace(/\/+$/, "")}`;
15314
- } catch {
15315
- return trimmed.toLowerCase();
15316
- }
15317
- }
15343
+ var normalizeUrl = normalizeServiceUrl;
15318
15344
  function pinRefusalMessage(verdict, targetUrl) {
15319
15345
  if (verdict.reason === "unpinnable") {
15320
15346
  return `Refusing to send your Verity login to ${targetUrl}: this machine's credential does not record which service issued it, so it cannot be verified. Run "verity login" to re-issue it.`;
@@ -15325,7 +15351,7 @@ function pinRefusalMessage(verdict, targetUrl) {
15325
15351
  // src/lib/cli-version.ts
15326
15352
  function cliVersion() {
15327
15353
  try {
15328
- return true ? "0.35.3" : "dev";
15354
+ return true ? "0.35.5" : "dev";
15329
15355
  } catch {
15330
15356
  return "dev";
15331
15357
  }
@@ -15530,7 +15556,7 @@ async function serviceUrlFromVerityMd() {
15530
15556
  const line2 = content.split("\n").find((l) => /\*{0,2}(?:url|service)\*{0,2}\s*:/i.test(l));
15531
15557
  if (line2) {
15532
15558
  const urlMatch = line2.match(/https:\/\/[^\s]+/);
15533
- if (urlMatch) return urlMatch[0];
15559
+ if (urlMatch) return canonicalServiceUrl(urlMatch[0]);
15534
15560
  }
15535
15561
  } catch {
15536
15562
  }
@@ -18048,6 +18074,124 @@ function describeRemote(found) {
18048
18074
  return `version ${found.standard.version}, pushed${who} on ${when}${also}`;
18049
18075
  }
18050
18076
 
18077
+ // src/lib/json-file.ts
18078
+ var import_promises3 = require("node:fs/promises");
18079
+ var import_node_path12 = require("node:path");
18080
+ function jsonSemanticEqual(a, b) {
18081
+ if (a === b) return true;
18082
+ if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
18083
+ return a === b;
18084
+ }
18085
+ const aIsArr = Array.isArray(a);
18086
+ const bIsArr = Array.isArray(b);
18087
+ if (aIsArr || bIsArr) {
18088
+ if (!aIsArr || !bIsArr || a.length !== b.length) return false;
18089
+ for (let i2 = 0; i2 < a.length; i2++) {
18090
+ if (!jsonSemanticEqual(a[i2], b[i2])) return false;
18091
+ }
18092
+ return true;
18093
+ }
18094
+ const ao = a;
18095
+ const bo = b;
18096
+ const aKeys = Object.keys(ao).filter((k) => ao[k] !== void 0);
18097
+ const bKeys = Object.keys(bo).filter((k) => bo[k] !== void 0);
18098
+ if (aKeys.length !== bKeys.length) return false;
18099
+ for (const k of aKeys) {
18100
+ if (bo[k] === void 0) return false;
18101
+ if (!jsonSemanticEqual(ao[k], bo[k])) return false;
18102
+ }
18103
+ return true;
18104
+ }
18105
+ function detectJsonIndent(raw) {
18106
+ const m = raw.match(/\n([ \t]+)\S/);
18107
+ return m ? m[1] : 2;
18108
+ }
18109
+ async function writeJsonFilePreservingStyle(file, value) {
18110
+ let currentRaw = null;
18111
+ try {
18112
+ currentRaw = await (0, import_promises3.readFile)(file, "utf-8");
18113
+ } catch {
18114
+ currentRaw = null;
18115
+ }
18116
+ if (currentRaw !== null) {
18117
+ try {
18118
+ if (jsonSemanticEqual(JSON.parse(currentRaw), value)) return false;
18119
+ } catch {
18120
+ }
18121
+ }
18122
+ const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
18123
+ const next = JSON.stringify(value, null, indent) + "\n";
18124
+ if (next === currentRaw) return false;
18125
+ await (0, import_promises3.mkdir)((0, import_node_path12.dirname)(file), { recursive: true });
18126
+ await (0, import_promises3.writeFile)(file, next);
18127
+ return true;
18128
+ }
18129
+
18130
+ // src/lib/setup-state.ts
18131
+ var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
18132
+ var MOMENTS = ["stop", "pre-commit", "pre-push"];
18133
+ var INTENSITIES = ["lightweight", "balanced", "thorough"];
18134
+ var TELEMETRY = ["enabled", "declined", "deferred"];
18135
+ var VERSION = /^[0-9A-Za-z.+-]{1,32}$/;
18136
+ var stamp = (v) => typeof v === "string" && v.length > 0 && v.length <= 64;
18137
+ function setupStateFrom(raw) {
18138
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
18139
+ const r = raw;
18140
+ if (r.version !== 1) return null;
18141
+ const out2 = { version: 1 };
18142
+ if (INTENSITIES.includes(r.intensity)) out2.intensity = r.intensity;
18143
+ if (Array.isArray(r.moments) && r.moments.every((m) => MOMENTS.includes(m))) {
18144
+ out2.moments = [...new Set(r.moments)];
18145
+ }
18146
+ if (TELEMETRY.includes(r.telemetry)) out2.telemetry = r.telemetry;
18147
+ if (Number.isSafeInteger(r.declinedStandardVersion) && r.declinedStandardVersion >= 1) {
18148
+ out2.declinedStandardVersion = r.declinedStandardVersion;
18149
+ }
18150
+ if (Number.isSafeInteger(r.standardVersion) && r.standardVersion >= 1) {
18151
+ out2.standardVersion = r.standardVersion;
18152
+ }
18153
+ const init2 = r.init;
18154
+ if (init2 && typeof init2 === "object" && stamp(init2.completed_at) && typeof init2.cli_version === "string" && VERSION.test(init2.cli_version)) {
18155
+ out2.init = { completed_at: init2.completed_at, cli_version: init2.cli_version };
18156
+ }
18157
+ const plugin = r.pluginSetup;
18158
+ if (plugin && typeof plugin === "object" && typeof plugin.version === "string" && VERSION.test(plugin.version) && stamp(plugin.completed_at)) {
18159
+ out2.pluginSetup = { version: plugin.version, completed_at: plugin.completed_at };
18160
+ }
18161
+ if (r.wiring === "project") out2.wiring = "project";
18162
+ return out2;
18163
+ }
18164
+ var MAX_SETUP_BYTES = 64 * 1024;
18165
+ function readSetupText() {
18166
+ const read2 = readRegularFileInRoot(repoRoot(), SETUP_STATE_FILE, MAX_SETUP_BYTES);
18167
+ return read2.ok ? read2.content : null;
18168
+ }
18169
+ async function readSetupState() {
18170
+ const raw = readSetupText();
18171
+ if (raw === null) return null;
18172
+ try {
18173
+ return setupStateFrom(JSON.parse(raw));
18174
+ } catch {
18175
+ return null;
18176
+ }
18177
+ }
18178
+ async function writeSetupState(patch) {
18179
+ const current = await readSetupState() ?? { version: 1 };
18180
+ const next = { ...current, ...patch, version: 1 };
18181
+ const raw = readSetupText();
18182
+ if (raw !== null) {
18183
+ try {
18184
+ if (jsonSemanticEqual(JSON.parse(raw), next)) return next;
18185
+ } catch {
18186
+ }
18187
+ }
18188
+ const text3 = JSON.stringify(next, null, raw !== null ? detectJsonIndent(raw) : 2) + "\n";
18189
+ if (!replaceInRoot(projectPath(VERITY_DIR), "setup.json", text3)) {
18190
+ throw new Error(`Could not write ${SETUP_STATE_FILE}: it is a link, or ${VERITY_DIR} is not a folder of this repository`);
18191
+ }
18192
+ return next;
18193
+ }
18194
+
18051
18195
  // src/lib/catalogue-conversion.ts
18052
18196
  var PACK_ID2 = /^[a-z][a-z0-9-]{0,39}$/;
18053
18197
  var MAX_PACKS2 = 12;
@@ -18078,6 +18222,12 @@ function count(v) {
18078
18222
  }
18079
18223
 
18080
18224
  // src/lib/push-setup.ts
18225
+ function latestFromConflict(result) {
18226
+ const err2 = result.errorBody?.error;
18227
+ const raw = err2 && typeof err2 === "object" ? err2.latest_version : void 0;
18228
+ const n2 = typeof raw === "string" && /^[1-9][0-9]*$/.test(raw) ? Number(raw) : typeof raw === "number" ? raw : NaN;
18229
+ return Number.isSafeInteger(n2) && n2 >= 1 ? n2 : null;
18230
+ }
18081
18231
  async function pushStandard(globals, opts = {}) {
18082
18232
  const lines = [];
18083
18233
  const token = await resolveToken(globals.token);
@@ -18113,11 +18263,15 @@ async function pushStandard(globals, opts = {}) {
18113
18263
  if (result.ok) {
18114
18264
  standardVersion = result.data.version;
18115
18265
  lines.push(`Standard pushed to Verity (v${result.data.version}) \u2713`);
18266
+ if (Number.isSafeInteger(standardVersion) && standardVersion >= 1) {
18267
+ await writeSetupState({ standardVersion }).catch(() => {
18268
+ });
18269
+ }
18116
18270
  const converted = conversionLine(result.data);
18117
18271
  if (converted) lines.push(converted);
18118
18272
  } else if (opts.expectedLatest !== void 0 && result.http_status === 409 && result.error.startsWith("STANDARD_EXISTS")) {
18119
18273
  lines.push("The service has a Standard now that this run did not see, so nothing was pushed over it.");
18120
- return { lines, standardVersion: null, conflict: true };
18274
+ return { lines, standardVersion: null, conflict: true, latest: latestFromConflict(result) };
18121
18275
  } else {
18122
18276
  lines.push(`Could not push the Standard: ${terminalText(result.error, 300)} \u2014 run "verity standard push" later.`);
18123
18277
  }
@@ -18285,7 +18439,7 @@ async function finishSplitLogin(opts) {
18285
18439
  if (read2.status === "invalid") fail2("invalid", `the pending sign-in is unusable (${read2.reason}) \u2014 run "verity login --start" again`, true);
18286
18440
  if (read2.status === "expired") fail2("expired", 'the code expired before it was approved \u2014 run "verity login --start" again', true);
18287
18441
  const pending = read2.record;
18288
- if (pending.service_url !== opts.serviceUrl) {
18442
+ if (!sameServiceUrl(pending.service_url, opts.serviceUrl)) {
18289
18443
  fail2(
18290
18444
  "service_changed",
18291
18445
  `the sign-in was started for ${pending.service_url}, but this machine now resolves ${opts.serviceUrl} \u2014 run "verity login --start" again`,
@@ -18646,61 +18800,6 @@ function registerLogoutCommand(program2) {
18646
18800
  // src/lib/hooks.ts
18647
18801
  var import_promises4 = require("node:fs/promises");
18648
18802
  var import_node_path13 = require("node:path");
18649
-
18650
- // src/lib/json-file.ts
18651
- var import_promises3 = require("node:fs/promises");
18652
- var import_node_path12 = require("node:path");
18653
- function jsonSemanticEqual(a, b) {
18654
- if (a === b) return true;
18655
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) {
18656
- return a === b;
18657
- }
18658
- const aIsArr = Array.isArray(a);
18659
- const bIsArr = Array.isArray(b);
18660
- if (aIsArr || bIsArr) {
18661
- if (!aIsArr || !bIsArr || a.length !== b.length) return false;
18662
- for (let i2 = 0; i2 < a.length; i2++) {
18663
- if (!jsonSemanticEqual(a[i2], b[i2])) return false;
18664
- }
18665
- return true;
18666
- }
18667
- const ao = a;
18668
- const bo = b;
18669
- const aKeys = Object.keys(ao).filter((k) => ao[k] !== void 0);
18670
- const bKeys = Object.keys(bo).filter((k) => bo[k] !== void 0);
18671
- if (aKeys.length !== bKeys.length) return false;
18672
- for (const k of aKeys) {
18673
- if (bo[k] === void 0) return false;
18674
- if (!jsonSemanticEqual(ao[k], bo[k])) return false;
18675
- }
18676
- return true;
18677
- }
18678
- function detectJsonIndent(raw) {
18679
- const m = raw.match(/\n([ \t]+)\S/);
18680
- return m ? m[1] : 2;
18681
- }
18682
- async function writeJsonFilePreservingStyle(file, value) {
18683
- let currentRaw = null;
18684
- try {
18685
- currentRaw = await (0, import_promises3.readFile)(file, "utf-8");
18686
- } catch {
18687
- currentRaw = null;
18688
- }
18689
- if (currentRaw !== null) {
18690
- try {
18691
- if (jsonSemanticEqual(JSON.parse(currentRaw), value)) return false;
18692
- } catch {
18693
- }
18694
- }
18695
- const indent = currentRaw !== null ? detectJsonIndent(currentRaw) : 2;
18696
- const next = JSON.stringify(value, null, indent) + "\n";
18697
- if (next === currentRaw) return false;
18698
- await (0, import_promises3.mkdir)((0, import_node_path12.dirname)(file), { recursive: true });
18699
- await (0, import_promises3.writeFile)(file, next);
18700
- return true;
18701
- }
18702
-
18703
- // src/lib/hooks.ts
18704
18803
  var VERITY_STOP_HOOK = {
18705
18804
  type: "command",
18706
18805
  command: "verity analyze",
@@ -19270,26 +19369,43 @@ function readProjectConfig() {
19270
19369
  cfg = { ...rest, turn_review_refused: refused2 };
19271
19370
  }
19272
19371
  }
19372
+ if (cfg.formal === "off" && cfg.path) {
19373
+ const refused2 = repoControlledRefusal(cfg.path);
19374
+ if (refused2 === "unverified") {
19375
+ cfg = { ...cfg, formal_off_unverified: true };
19376
+ } else if (refused2) {
19377
+ const { formal: _dropped, ...rest } = cfg;
19378
+ cfg = { ...rest, formal_off_refused: refused2 };
19379
+ }
19380
+ }
19273
19381
  return cfg;
19274
19382
  }
19275
- function turnReviewRefusalMessage(reason) {
19383
+ function formalOffUnverifiedNote() {
19384
+ return 'off-unverified: .verity/config.json says "off" and git could not confirm that the repository does not control it, so the "off" is kept and the formal check does not run';
19385
+ }
19386
+ function formalOffRefusalMessage(reason) {
19276
19387
  switch (reason) {
19277
19388
  case "tracked":
19278
- return '.verity/config.json is tracked in git, so its turn_review "off" is ignored (a repository cannot switch the review off) \u2014 untrack it with: git rm --cached .verity/config.json';
19389
+ return '.verity/config.json is tracked in git, so its formal "off" is ignored (a repository cannot switch formal verification off). To make your "off" count, untrack the file: git rm --cached .verity/config.json';
19279
19390
  case "symlink":
19280
- return '.verity/config.json is reached through a symlink, so its turn_review "off" is ignored';
19281
- case "unverified":
19282
- return 'could not confirm with git that .verity/config.json is untracked, so its turn_review "off" is ignored';
19391
+ return '.verity/config.json is reached through a symlink, so its formal "off" is ignored';
19283
19392
  }
19284
19393
  }
19285
- function formalRefusalMessage(reason) {
19394
+ function formalSetting(cfg, env = process.env) {
19395
+ const fromEnv = env.VERITY_GIT_MOMENT_FORMAL;
19396
+ if (fromEnv === "off" || fromEnv === "push") return { state: fromEnv, source: "env" };
19397
+ if (cfg.formal === "off") return { state: "off", source: "config" };
19398
+ if (cfg.formal === "push") return { state: "push", source: "config" };
19399
+ return { state: "push", source: "default" };
19400
+ }
19401
+ function turnReviewRefusalMessage(reason) {
19286
19402
  switch (reason) {
19287
19403
  case "tracked":
19288
- return ".verity/config.json is tracked in git, so its formal setting is ignored (a repository cannot switch formal verification on) \u2014 untrack it with: git rm --cached .verity/config.json";
19404
+ return '.verity/config.json is tracked in git, so its turn_review "off" is ignored (a repository cannot switch the review off) \u2014 untrack it with: git rm --cached .verity/config.json';
19289
19405
  case "symlink":
19290
- return ".verity/config.json is reached through a symlink, so its formal setting is ignored";
19406
+ return '.verity/config.json is reached through a symlink, so its turn_review "off" is ignored';
19291
19407
  case "unverified":
19292
- return "could not confirm with git that .verity/config.json is untracked, so its formal setting is ignored";
19408
+ return 'could not confirm with git that .verity/config.json is untracked, so its turn_review "off" is ignored';
19293
19409
  }
19294
19410
  }
19295
19411
  function writeProjectConfig(patch) {
@@ -21094,13 +21210,15 @@ var import_node_child_process9 = require("node:child_process");
21094
21210
  var import_node_fs24 = require("node:fs");
21095
21211
  var VERITY_GITIGNORE_MARKER = "# Verity \u2014 machine-local state.";
21096
21212
  var SETTINGS_LOCAL_IGNORE_ENTRY = ".claude/settings.local.json";
21213
+ var BLOCK_COMMENT = [
21214
+ "# Verity \u2014 machine-local state. Everything in .verity/ is ignored. The Standard",
21215
+ "# is held by the Verity service (verity standard pull writes it here), and the",
21216
+ "# knowledge graph under .verity/memory/ is rebuilt from it. Committing either",
21217
+ "# puts generated files in every diff and pull request."
21218
+ ];
21097
21219
  var VERITY_GITIGNORE_BLOCK = [
21098
- "# Verity \u2014 machine-local state. Everything in .verity/ is ignored except the",
21099
- "# shared standard, which is meant to be committed. The knowledge graph under",
21100
- "# .verity/memory/ is machine-local too: the service rebuilds it, and committing",
21101
- "# it puts generated notes in every diff and pull request.",
21220
+ ...BLOCK_COMMENT,
21102
21221
  ".verity/*",
21103
- "!.verity/standard.yaml",
21104
21222
  SETTINGS_LOCAL_IGNORE_ENTRY,
21105
21223
  ""
21106
21224
  ].join("\n");
@@ -21125,9 +21243,11 @@ var SUPERSEDED_MEMORY_LINES = /* @__PURE__ */ new Set([
21125
21243
  ".verity/memory/log.md",
21126
21244
  SUPERSEDED_COMMENT
21127
21245
  ]);
21128
- function isIgnored3(path) {
21246
+ var SUPERSEDED_STANDARD_LINE = "!.verity/standard.yaml";
21247
+ var OLD_COMMENT_LINES = /* @__PURE__ */ new Set([SUPERSEDED_COMMENT, ...MEMORY_COMMENT]);
21248
+ function isIgnored3(path, opts = {}) {
21129
21249
  try {
21130
- (0, import_node_child_process9.execSync)(`git check-ignore -q -- "${path}"`, { stdio: "pipe" });
21250
+ (0, import_node_child_process9.execSync)(`git check-ignore -q ${opts.noIndex ? "--no-index " : ""}-- "${path}"`, { stdio: "pipe" });
21131
21251
  return true;
21132
21252
  } catch (err2) {
21133
21253
  return err2.status === 1 ? false : null;
@@ -21135,10 +21255,9 @@ function isIgnored3(path) {
21135
21255
  }
21136
21256
  function semanticsHold() {
21137
21257
  const snapshot = isIgnored3(".verity/.snapshot/__probe__");
21138
- const standard = isIgnored3(".verity/standard.yaml");
21139
21258
  const futureState = isIgnored3(".verity/.__probe-future-state__");
21140
- if (snapshot === null || standard === null || futureState === null) return null;
21141
- return snapshot === true && futureState === true && standard === false;
21259
+ if (snapshot === null || futureState === null) return null;
21260
+ return snapshot === true && futureState === true;
21142
21261
  }
21143
21262
  function memoryPosture() {
21144
21263
  const ignored = isIgnored3(".verity/memory/domain/__probe__.md");
@@ -21155,6 +21274,22 @@ function committedMemoryFiles() {
21155
21274
  return [];
21156
21275
  }
21157
21276
  }
21277
+ var STANDARD_PATH = ".verity/standard.yaml";
21278
+ function standardPosture() {
21279
+ const ignored = isIgnored3(STANDARD_PATH, { noIndex: true });
21280
+ if (ignored === null) return "unknown";
21281
+ return ignored ? "ignored" : "committable";
21282
+ }
21283
+ function committedStandardFile() {
21284
+ try {
21285
+ return (0, import_node_child_process9.execSync)(`git ls-files -z -- ${STANDARD_PATH}`, {
21286
+ encoding: "utf-8",
21287
+ stdio: ["pipe", "pipe", "pipe"]
21288
+ }).split("\0").filter(Boolean).length > 0;
21289
+ } catch {
21290
+ return false;
21291
+ }
21292
+ }
21158
21293
  var OPT_OUT_COMMENT_LINES = /* @__PURE__ */ new Set([
21159
21294
  MEMORY_OPT_OUT_MARKER,
21160
21295
  '# Remove these three lines (or run "verity memory untrack") to stop tracking it.'
@@ -21172,6 +21307,19 @@ function fenceMemoryLines(lines) {
21172
21307
  }
21173
21308
  return out2;
21174
21309
  }
21310
+ function fenceStandardLines(lines) {
21311
+ const out2 = [];
21312
+ for (const line2 of lines) {
21313
+ const trimmed = line2.trim();
21314
+ if (trimmed.startsWith(VERITY_GITIGNORE_MARKER) && trimmed !== BLOCK_COMMENT[0]) {
21315
+ out2.push(...BLOCK_COMMENT);
21316
+ continue;
21317
+ }
21318
+ if (trimmed === SUPERSEDED_STANDARD_LINE || OLD_COMMENT_LINES.has(trimmed)) continue;
21319
+ out2.push(line2);
21320
+ }
21321
+ return out2;
21322
+ }
21175
21323
  function ensureVerityGitignore() {
21176
21324
  let content = "";
21177
21325
  try {
@@ -21183,9 +21331,11 @@ function ensureVerityGitignore() {
21183
21331
  const lines = content.split("\n");
21184
21332
  const needsRepair = lines.some((l) => BREAKING_ENTRIES.has(l.trim()));
21185
21333
  const hasSupersededMemory = hasMarker && !optedOut && lines.some((l) => SUPERSEDED_MEMORY_LINES.has(l.trim()));
21334
+ const hasSupersededStandard = hasMarker && lines.some((l) => l.trim() === SUPERSEDED_STANDARD_LINE);
21186
21335
  const memoryIsCommitted = hasSupersededMemory && committedMemoryFiles().length > 0;
21336
+ const standardIsCommitted = hasSupersededStandard && committedStandardFile();
21187
21337
  const verified = (result) => semanticsHold() === false ? "conflict" : result;
21188
- if (hasMarker && !needsRepair && !hasSupersededMemory) return verified("covered");
21338
+ if (hasMarker && !needsRepair && !hasSupersededMemory && !hasSupersededStandard) return verified("covered");
21189
21339
  if (!hasMarker && !needsRepair) {
21190
21340
  if (semanticsHold() === true) return "covered";
21191
21341
  }
@@ -21194,6 +21344,9 @@ function ensureVerityGitignore() {
21194
21344
  if (needsRepair) {
21195
21345
  next = next.map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l);
21196
21346
  }
21347
+ if (hasSupersededStandard && !standardIsCommitted) {
21348
+ next = fenceStandardLines(next);
21349
+ }
21197
21350
  if (hasSupersededMemory && !memoryIsCommitted) {
21198
21351
  next = fenceMemoryLines(next);
21199
21352
  }
@@ -21204,7 +21357,7 @@ function ensureVerityGitignore() {
21204
21357
  }
21205
21358
  if (text3 !== content) (0, import_node_fs24.writeFileSync)(".gitignore", text3);
21206
21359
  return verified(
21207
- memoryIsCommitted ? "memory-tracked" : hasSupersededMemory ? "memory-fenced" : needsRepair ? "repaired" : "added"
21360
+ memoryIsCommitted ? "memory-tracked" : standardIsCommitted ? "standard-tracked" : hasSupersededMemory ? "memory-fenced" : hasSupersededStandard ? "standard-fenced" : needsRepair ? "repaired" : "added"
21208
21361
  );
21209
21362
  } catch {
21210
21363
  return "failed";
@@ -21219,7 +21372,7 @@ function untrackMemory() {
21219
21372
  return "failed";
21220
21373
  }
21221
21374
  }
21222
- function writeFencedBlock() {
21375
+ function writeFencedBlock(fence) {
21223
21376
  let content = "";
21224
21377
  try {
21225
21378
  content = (0, import_node_fs24.readFileSync)(".gitignore", "utf-8");
@@ -21227,7 +21380,8 @@ function writeFencedBlock() {
21227
21380
  }
21228
21381
  const hasMarker = content.includes(VERITY_GITIGNORE_MARKER);
21229
21382
  let lines = content.split("\n").map((l) => BREAKING_ENTRIES.has(l.trim()) ? ".verity/*" : l);
21230
- lines = fenceMemoryLines(lines);
21383
+ if (fence.standard) lines = fenceStandardLines(lines);
21384
+ if (fence.memory) lines = fenceMemoryLines(lines);
21231
21385
  let text3 = lines.join("\n");
21232
21386
  if (!hasMarker) {
21233
21387
  const sep7 = text3 === "" ? "" : text3.endsWith("\n") ? "\n" : "\n\n";
@@ -21254,7 +21408,7 @@ function fenceMemory() {
21254
21408
  } catch {
21255
21409
  }
21256
21410
  };
21257
- if (!writeFencedBlock()) return { untracked: 0, ok: false };
21411
+ if (!writeFencedBlock({ memory: true })) return { untracked: 0, ok: false };
21258
21412
  if (memoryPosture() !== "ignored") {
21259
21413
  restore();
21260
21414
  return { untracked: 0, ok: false };
@@ -21266,6 +21420,34 @@ function fenceMemory() {
21266
21420
  }
21267
21421
  return { untracked: tracked, ok: true };
21268
21422
  }
21423
+ function untrackStandard() {
21424
+ let original = null;
21425
+ try {
21426
+ original = (0, import_node_fs24.readFileSync)(".gitignore", "utf-8");
21427
+ } catch {
21428
+ original = null;
21429
+ }
21430
+ const restore = () => {
21431
+ try {
21432
+ if (original === null) (0, import_node_fs24.rmSync)(".gitignore", { force: true });
21433
+ else (0, import_node_fs24.writeFileSync)(".gitignore", original);
21434
+ } catch {
21435
+ }
21436
+ };
21437
+ if (!writeFencedBlock({ standard: true })) return { ok: false, why: "gitignore" };
21438
+ if (standardPosture() !== "ignored") {
21439
+ restore();
21440
+ return { ok: false, why: "still-committable" };
21441
+ }
21442
+ if (!committedStandardFile()) return { ok: true, untracked: false };
21443
+ try {
21444
+ (0, import_node_child_process9.execSync)(`git rm --cached --quiet -- ${STANDARD_PATH}`, { stdio: "pipe" });
21445
+ return { ok: true, untracked: true };
21446
+ } catch {
21447
+ restore();
21448
+ return { ok: false, why: "index" };
21449
+ }
21450
+ }
21269
21451
  function memoryOptOut() {
21270
21452
  try {
21271
21453
  return (0, import_node_fs24.readFileSync)(".gitignore", "utf-8").includes(MEMORY_OPT_OUT_MARKER);
@@ -21295,18 +21477,23 @@ function committedVerityState() {
21295
21477
  } catch {
21296
21478
  return [];
21297
21479
  }
21298
- return out2.split("\0").filter(Boolean).filter((p) => p !== ".verity/standard.yaml" && !p.startsWith(".verity/memory/"));
21480
+ return out2.split("\0").filter(Boolean).filter((p) => p !== STANDARD_PATH && !p.startsWith(".verity/memory/"));
21299
21481
  }
21300
21482
  function untrackVerityState() {
21301
21483
  const tracked = committedVerityState();
21302
21484
  if (tracked.length === 0) return "none";
21485
+ const standardWasTracked = committedStandardFile();
21303
21486
  try {
21304
21487
  (0, import_node_child_process9.execSync)("git rm -r --cached --quiet -- .verity", { stdio: "pipe" });
21305
- const keeps = [".verity/standard.yaml"];
21306
- if (memoryPosture() === "committable") keeps.push(".verity/memory");
21307
- for (const keep of keeps) {
21488
+ if (standardWasTracked) {
21489
+ try {
21490
+ (0, import_node_child_process9.execSync)(`git add -f -- ${STANDARD_PATH}`, { stdio: "pipe" });
21491
+ } catch {
21492
+ }
21493
+ }
21494
+ if (memoryPosture() === "committable") {
21308
21495
  try {
21309
- (0, import_node_child_process9.execSync)(`git add -- "${keep}"`, { stdio: "pipe" });
21496
+ (0, import_node_child_process9.execSync)("git add -- .verity/memory", { stdio: "pipe" });
21310
21497
  } catch {
21311
21498
  }
21312
21499
  }
@@ -21827,8 +22014,13 @@ var CLAUDE_MD_PROSE = [
21827
22014
  " `verity standard generate`. It works only for a project with no Standard on the",
21828
22015
  " service. It writes `.verity/repo-profile.json` and sends nothing without consent.",
21829
22016
  " Show the user that file. Run the command again with `--yes` only after they agree.",
21830
- "- The rules themselves: edit `.verity/standard.yaml`, then run `verity standard push`."
22017
+ "- The rules themselves: run `verity standard pull` first (the file here can be behind",
22018
+ " the team's), edit `.verity/standard.yaml`, then run `verity standard push`."
21831
22019
  ].join("\n");
22020
+ var CLAUDE_MD_PROSE_PRE_PULL = CLAUDE_MD_PROSE.replace(
22021
+ /- The rules themselves: run `verity standard pull` first \(the file here can be behind\n the team's\), edit `\.verity\/standard\.yaml`, then run `verity standard push`\.$/,
22022
+ "- The rules themselves: edit `.verity/standard.yaml`, then run `verity standard push`."
22023
+ );
21832
22024
  var CLAUDE_MD_PROSE_PRE_VERBOSE = [
21833
22025
  "## Project Memory",
21834
22026
  "",
@@ -22466,6 +22658,7 @@ function stripKnownProse(interior) {
22466
22658
  const trimmed = interior.replace(/^\n+/, "");
22467
22659
  for (const prose of [
22468
22660
  CLAUDE_MD_PROSE,
22661
+ CLAUDE_MD_PROSE_PRE_PULL,
22469
22662
  CLAUDE_MD_PROSE_PRE_VERBOSE,
22470
22663
  CLAUDE_MD_PROSE_PRE_SETTINGS,
22471
22664
  CLAUDE_MD_PROSE_PRE_STDIN,
@@ -25531,6 +25724,8 @@ async function generateStandard(opts) {
25531
25724
  } catch (err2) {
25532
25725
  return { status: "failed", reason: `the Standard is on the service (v${version}) but could not be written here: ${terminalText(err2.message, 200)}` };
25533
25726
  }
25727
+ await writeSetupState({ standardVersion: version }).catch(() => {
25728
+ });
25534
25729
  return {
25535
25730
  status: "created",
25536
25731
  version,
@@ -25711,68 +25906,6 @@ async function runGenerate(globals, opts) {
25711
25906
  var import_node_fs37 = require("node:fs");
25712
25907
  var import_yaml6 = __toESM2(require_dist());
25713
25908
 
25714
- // src/lib/setup-state.ts
25715
- var SETUP_STATE_FILE = `${VERITY_DIR}/setup.json`;
25716
- var MOMENTS = ["stop", "pre-commit", "pre-push"];
25717
- var INTENSITIES = ["lightweight", "balanced", "thorough"];
25718
- var TELEMETRY = ["enabled", "declined", "deferred"];
25719
- var VERSION = /^[0-9A-Za-z.+-]{1,32}$/;
25720
- var stamp = (v) => typeof v === "string" && v.length > 0 && v.length <= 64;
25721
- function setupStateFrom(raw) {
25722
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) return null;
25723
- const r = raw;
25724
- if (r.version !== 1) return null;
25725
- const out2 = { version: 1 };
25726
- if (INTENSITIES.includes(r.intensity)) out2.intensity = r.intensity;
25727
- if (Array.isArray(r.moments) && r.moments.every((m) => MOMENTS.includes(m))) {
25728
- out2.moments = [...new Set(r.moments)];
25729
- }
25730
- if (TELEMETRY.includes(r.telemetry)) out2.telemetry = r.telemetry;
25731
- if (Number.isSafeInteger(r.declinedStandardVersion) && r.declinedStandardVersion >= 1) {
25732
- out2.declinedStandardVersion = r.declinedStandardVersion;
25733
- }
25734
- const init2 = r.init;
25735
- if (init2 && typeof init2 === "object" && stamp(init2.completed_at) && typeof init2.cli_version === "string" && VERSION.test(init2.cli_version)) {
25736
- out2.init = { completed_at: init2.completed_at, cli_version: init2.cli_version };
25737
- }
25738
- const plugin = r.pluginSetup;
25739
- if (plugin && typeof plugin === "object" && typeof plugin.version === "string" && VERSION.test(plugin.version) && stamp(plugin.completed_at)) {
25740
- out2.pluginSetup = { version: plugin.version, completed_at: plugin.completed_at };
25741
- }
25742
- if (r.wiring === "project") out2.wiring = "project";
25743
- return out2;
25744
- }
25745
- var MAX_SETUP_BYTES = 64 * 1024;
25746
- function readSetupText() {
25747
- const read2 = readRegularFileInRoot(repoRoot(), SETUP_STATE_FILE, MAX_SETUP_BYTES);
25748
- return read2.ok ? read2.content : null;
25749
- }
25750
- async function readSetupState() {
25751
- const raw = readSetupText();
25752
- if (raw === null) return null;
25753
- try {
25754
- return setupStateFrom(JSON.parse(raw));
25755
- } catch {
25756
- return null;
25757
- }
25758
- }
25759
- async function writeSetupState(patch) {
25760
- const current = await readSetupState() ?? { version: 1 };
25761
- const next = { ...current, ...patch, version: 1 };
25762
- const raw = readSetupText();
25763
- if (raw !== null) {
25764
- try {
25765
- if (jsonSemanticEqual(JSON.parse(raw), next)) return next;
25766
- } catch {
25767
- }
25768
- }
25769
- const text3 = JSON.stringify(next, null, raw !== null ? detectJsonIndent(raw) : 2) + "\n";
25770
- if (!replaceInRoot(projectPath(VERITY_DIR), "setup.json", text3)) {
25771
- throw new Error(`Could not write ${SETUP_STATE_FILE}: it is a link, or ${VERITY_DIR} is not a folder of this repository`);
25772
- }
25773
- return next;
25774
- }
25775
-
25776
25909
  // src/commands/init/rules.ts
25777
25910
  async function projectLabel(token, serviceUrl, verbose) {
25778
25911
  try {
@@ -25917,6 +26050,8 @@ async function pullStandard(globals, opts = {}) {
25917
26050
  } catch {
25918
26051
  }
25919
26052
  if (local && typeof local === "object" && !Array.isArray(local) && sameStandard(local, remote.standard.content)) {
26053
+ await writeSetupState({ standardVersion: version, declinedStandardVersion: void 0 }).catch(() => {
26054
+ });
25920
26055
  return { status: "same", version };
25921
26056
  }
25922
26057
  try {
@@ -25927,7 +26062,7 @@ async function pullStandard(globals, opts = {}) {
25927
26062
  }
25928
26063
  try {
25929
26064
  const outcome = await adoptRemoteSetup(remote, { serviceUrl: url.data, projectId, expectStandard: readBytes });
25930
- await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
26065
+ await writeSetupState({ standardVersion: version, declinedStandardVersion: void 0 }).catch(() => {
25931
26066
  });
25932
26067
  return { status: "pulled", version, backup, notes: outcome.notes };
25933
26068
  } catch (err2) {
@@ -26146,8 +26281,13 @@ function registerStandardCommands(program2) {
26146
26281
  for (const path of result.written) printInfo(` ${terminalText(path, 200)} \u2713`);
26147
26282
  for (const note of result.notes) printWarn(` ${terminalText(note, 300)}`);
26148
26283
  if (opts.push) {
26149
- const pushed = await pushStandard(globals);
26284
+ const token = await resolveToken(globals.token);
26285
+ const expected = token.ok && urlResult.ok ? await expectedLatestForPush(globals, token.data.token, urlResult.data) : void 0;
26286
+ const pushed = await pushStandard(globals, expected !== void 0 ? { expectedLatest: expected } : {});
26150
26287
  for (const line2 of pushed.lines) printInfo(` ${terminalText(line2, 600)}`);
26288
+ if (pushed.conflict) {
26289
+ printWarn(` The team's Standard is v${pushed.latest ?? "?"} now. Run verity standard pull to use it here, or verity standard push --force to replace it with this file.`);
26290
+ }
26151
26291
  if (pushed.standardVersion !== null) {
26152
26292
  await correctVerityMdVersion({
26153
26293
  standard: result.standard,
@@ -26161,7 +26301,7 @@ function registerStandardCommands(program2) {
26161
26301
  standard.command("generate").description("Have Verity write project-specific rules from a profile of this repository (asks before sending it)").option("--yes", `Send the profile without asking (you have read ${PROFILE_FILE})`).option("--status", "Print whether the service writes project rules for this project: on, off, unsupported or unknown. Sends nothing.").action((opts) => runGenerate(program2.opts(), opts));
26162
26302
  standard.command("pull").description("Write the team's Standard on the service into .verity/standard.yaml (keeps a copy of yours)").action(() => runPull(program2.opts()));
26163
26303
  standard.command("catalogue [pack]").description("What each review pack holds: every pack, or the rules of one (core, api, data, \u2026)").option("--json", "Print the catalogue as JSON").action((pack, opts) => runCatalogue(program2.opts(), pack, opts));
26164
- standard.command("push").description("Upload the project Standard to the service").option("--file <path>", "Path to standard YAML file", STANDARD_FILE).option("--created-by <name>", "Who created this version", "claude-code").action(async (opts) => {
26304
+ standard.command("push").description("Upload the project Standard to the service, as the next version after the one this machine last saw").option("--file <path>", "Path to standard YAML file", STANDARD_FILE).option("--created-by <name>", "Who created this version", "claude-code").option("--force", "Upload even when the team's Standard moved since this machine last pulled or pushed it").action(async (opts) => {
26165
26305
  const globals = program2.opts();
26166
26306
  const tokenResult = await resolveToken(globals.token);
26167
26307
  if (!tokenResult.ok) {
@@ -26184,19 +26324,33 @@ function registerStandardCommands(program2) {
26184
26324
  process.exit(1);
26185
26325
  }
26186
26326
  if (upload.warning) printWarn(terminalText(upload.warning, 600));
26327
+ const expected = opts.force ? void 0 : await expectedLatestForPush(globals, tokenResult.data.token, urlResult.data);
26187
26328
  const result = await apiRequest({
26188
26329
  method: "POST",
26189
26330
  path: "/standards",
26190
26331
  serviceUrl: urlResult.data,
26191
26332
  token: tokenResult.data.token,
26192
- body: { content: upload.content, created_by: upload.createdBy },
26333
+ body: {
26334
+ content: upload.content,
26335
+ created_by: upload.createdBy,
26336
+ ...expected !== void 0 ? { expected_latest: expected } : {}
26337
+ },
26193
26338
  verbose: globals.verbose
26194
26339
  });
26195
26340
  if (!result.ok) {
26341
+ if (expected !== void 0 && result.http_status === 409 && result.error.startsWith("STANDARD_EXISTS")) {
26342
+ const latest = latestFromConflict(result);
26343
+ printError(latest !== null ? `The team's Standard is v${latest} now${expected !== null ? `, and this machine last saw v${expected}` : ", and this machine saw none"}. Nothing was pushed. Run "verity standard pull" first (it keeps a copy of your file), put your changes into the pulled file, then push again. To replace v${latest} with this file as it is: verity standard push --force` : `The service has no Standard for this project, but this machine last saw v${expected}. Nothing was pushed. To share this file as v1: verity standard push --force`);
26344
+ process.exit(1);
26345
+ }
26196
26346
  printError(terminalText(result.error, 300));
26197
26347
  process.exit(1);
26198
26348
  }
26199
26349
  printInfo(`Standard uploaded (version ${versionText(result.data.version)})`);
26350
+ if (Number.isSafeInteger(result.data.version) && result.data.version >= 1) {
26351
+ await writeSetupState({ standardVersion: result.data.version }).catch(() => {
26352
+ });
26353
+ }
26200
26354
  const converted = conversionLine(result.data);
26201
26355
  if (converted) {
26202
26356
  printInfo(converted);
@@ -26204,6 +26358,7 @@ function registerStandardCommands(program2) {
26204
26358
  }
26205
26359
  printJson(result.data);
26206
26360
  });
26361
+ standard.command("untrack").description(`Stop tracking ${STANDARD_FILE} in git: Verity keeps it on each machine, and reviews read the service`).action(() => runUntrack());
26207
26362
  standard.command("get").description("Get the current Standard from the service").option("--version <n>", "Specific version number").option("--resolved", "Show it as a review reads it: the catalogue packs in `extends` expanded into patterns").action(async (opts) => {
26208
26363
  const globals = program2.opts();
26209
26364
  const tokenResult = await resolveToken(globals.token);
@@ -26278,6 +26433,32 @@ function registerStandardCommands(program2) {
26278
26433
  function versionText(value) {
26279
26434
  return typeof value === "number" && Number.isSafeInteger(value) ? String(value) : "?";
26280
26435
  }
26436
+ async function expectedLatestForPush(globals, token, serviceUrl) {
26437
+ const state = await readSetupState();
26438
+ if (state?.standardVersion !== void 0) return state.standardVersion;
26439
+ const remote = await fetchRemoteSetup({ token, serviceUrl, verbose: globals.verbose });
26440
+ if (remote.status === "found") {
26441
+ printInfo(`This machine had no record of the team's Standard. It is v${remote.standard.version} on the service, and this push replaces it.`);
26442
+ return remote.standard.version;
26443
+ }
26444
+ if (remote.status === "none") return null;
26445
+ printWarn(`Could not read the team's Standard from the service (${terminalText(remote.reason, 200)}), so this push is not conditional on it.`);
26446
+ return void 0;
26447
+ }
26448
+ function runUntrack() {
26449
+ const wasTracked = committedStandardFile();
26450
+ const result = untrackStandard();
26451
+ if (!result.ok) {
26452
+ printError(result.why === "gitignore" ? "Could not write .gitignore. Nothing changed." : result.why === "still-committable" ? `Git still lets ${STANDARD_FILE} be committed after the .gitignore change, so nothing was untracked and .gitignore is as it was. A nested .gitignore re-includes it: check with git check-ignore -v --no-index ${STANDARD_FILE}` : `Could not remove ${STANDARD_FILE} from the git index. .gitignore is as it was.`);
26453
+ process.exit(1);
26454
+ }
26455
+ if (!wasTracked) {
26456
+ printInfo(`${STANDARD_FILE} is not tracked in git. Git ignores it from here on; nothing else changed.`);
26457
+ return;
26458
+ }
26459
+ printInfo(`Untracked ${STANDARD_FILE} (staged). The file stays on disk here. Commit to finish.`);
26460
+ printInfo("Teammates lose the file on their next pull: `verity standard pull` (or `verity init`) writes the team's Standard back for them. Reviews read the Standard on the service, so none is affected.");
26461
+ }
26281
26462
  var UPLOAD_MAX_BYTES = 512 * 1024;
26282
26463
  function readUploadFile(file) {
26283
26464
  const root = realOrSelf(repoRoot());
@@ -26439,11 +26620,15 @@ function registerConfigCommands(program2) {
26439
26620
  }
26440
26621
  printInfo(pluginMode ? "It applies from the next turn." : "It applies from the next Claude Code session (Claude Code reads hooks when a session starts).");
26441
26622
  });
26442
- config.command("formal [state]").description('Get or set formal verification at push: "push" or "off" (needs Lean \u2014 verity lean status)').action((state) => {
26623
+ config.command("formal [state]").description('Get or set formal verification at push: "push" (the default) or "off" (needs Lean \u2014 verity lean status)').action((state) => {
26443
26624
  if (state === void 0) {
26444
26625
  const cfg = readProjectConfig();
26445
- process.stdout.write((cfg.formal === "push" ? "push" : "off") + "\n");
26446
- if (cfg.formal_refused) printWarn(formalRefusalMessage(cfg.formal_refused));
26626
+ const setting = formalSetting(cfg);
26627
+ process.stdout.write(setting.state + "\n");
26628
+ if (cfg.formal_off_refused && setting.source === "default") printWarn(formalOffRefusalMessage(cfg.formal_off_refused));
26629
+ else if (setting.source === "default") printInfo("On by default. To turn it off for this checkout, run: verity config formal off");
26630
+ if (setting.source === "env") printInfo("Set by VERITY_GIT_MOMENT_FORMAL in this environment");
26631
+ if (cfg.formal_off_unverified && setting.source === "config") printWarn(formalOffUnverifiedNote());
26447
26632
  return;
26448
26633
  }
26449
26634
  if (state !== "push" && state !== "off") {
@@ -26451,13 +26636,18 @@ function registerConfigCommands(program2) {
26451
26636
  process.exit(1);
26452
26637
  }
26453
26638
  writeProjectConfig({ formal: state === "push" ? "push" : "off" });
26454
- const refused2 = state === "push" ? readProjectConfig().formal_refused : void 0;
26455
- if (refused2) {
26456
- printWarn(`Formal verification at push: still off \u2014 ${formalRefusalMessage(refused2)}`);
26639
+ const after = readProjectConfig();
26640
+ const offRefused = state === "off" && formalSetting(after).state === "push" ? after.formal_off_refused : void 0;
26641
+ if (offRefused) {
26642
+ printWarn(`Formal verification at push: still on \u2014 ${formalOffRefusalMessage(offRefused)}`);
26457
26643
  return;
26458
26644
  }
26459
- printInfo(state === "push" ? "Formal verification at push: on for this checkout (Lean must be installed \u2014 verity lean status)" : "Formal verification at push: off");
26645
+ printInfo(state === "push" ? "Formal verification at push: on for this checkout (Lean must be installed \u2014 verity lean status)" : "Formal verification at push: off for this checkout");
26460
26646
  if (state === "push") printInfo("The phase runs with your Verity login (a push without one is skipped and says so: not-logged-in)");
26647
+ const env = process.env.VERITY_GIT_MOMENT_FORMAL;
26648
+ if ((env === "off" || env === "push") && env !== state) {
26649
+ printWarn(`VERITY_GIT_MOMENT_FORMAL=${env} in this environment overrides the file while it is set`);
26650
+ }
26461
26651
  });
26462
26652
  config.command("verbose [state]").description('Get or set the line Verity prints when a turn has nothing new to review: "on" or "off" (the default)').action((state) => {
26463
26653
  if (state === void 0) {
@@ -29616,11 +29806,11 @@ async function readStopHookStdin() {
29616
29806
  return new Promise((resolve10) => {
29617
29807
  const chunks = [];
29618
29808
  let size = 0;
29619
- let settled = false;
29809
+ let settled2 = false;
29620
29810
  let timer;
29621
29811
  const settle = (result) => {
29622
- if (settled) return;
29623
- settled = true;
29812
+ if (settled2) return;
29813
+ settled2 = true;
29624
29814
  if (timer) clearTimeout(timer);
29625
29815
  process.stdin.off("data", onData);
29626
29816
  process.stdin.off("end", onEnd);
@@ -34352,15 +34542,23 @@ function renderNodeMarkdown(candidate, nodeId, createdAt) {
34352
34542
  ].join("\n");
34353
34543
  return fm;
34354
34544
  }
34355
- async function runSeed(opts) {
34356
- if (!(0, import_node_fs53.existsSync)(STANDARD_FILE)) {
34357
- return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
34545
+ async function standardForSeed(opts) {
34546
+ if ((0, import_node_fs53.existsSync)(STANDARD_FILE)) {
34547
+ const raw = readRepoFile(STANDARD_FILE);
34548
+ if (raw === null) return null;
34549
+ try {
34550
+ const parsed = (0, import_yaml9.parse)(raw);
34551
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
34552
+ } catch {
34553
+ return null;
34554
+ }
34358
34555
  }
34359
- let standardDoc;
34360
- try {
34361
- const raw = await (0, import_promises11.readFile)(STANDARD_FILE, "utf-8");
34362
- standardDoc = (0, import_yaml9.parse)(raw);
34363
- } catch {
34556
+ const remote = await fetchRemoteSetup({ serviceUrl: opts.serviceUrl, token: opts.token, verbose: opts.verbose });
34557
+ return remote.status === "found" ? remote.standard.content : null;
34558
+ }
34559
+ async function runSeed(opts) {
34560
+ const standardDoc = await standardForSeed(opts);
34561
+ if (standardDoc === null) {
34364
34562
  return { created: 0, existing: 0, failed: 0, skipped: "no_standard", candidates: [] };
34365
34563
  }
34366
34564
  const knowledgeSpec = standardDoc.knowledge_spec ?? {};
@@ -34487,9 +34685,8 @@ async function memoryManifest(run4) {
34487
34685
  try {
34488
34686
  await ensureMemoryDir();
34489
34687
  const seedMarker = (0, import_node_path43.join)(VERITY_DIR, ".seeded");
34490
- const hasStandard = (0, import_node_fs54.existsSync)(STANDARD_FILE);
34491
34688
  const alreadyTried = (0, import_node_fs54.existsSync)(seedMarker);
34492
- if (hasStandard && !alreadyTried) {
34689
+ if (!alreadyTried) {
34493
34690
  const preManifest = await buildManifest();
34494
34691
  if (preManifest.nodes.length === 0) {
34495
34692
  try {
@@ -36958,7 +37155,7 @@ function runAsync(tc, cwd, args2, timeoutMs) {
36958
37155
  let stdout = "";
36959
37156
  let stderr = "";
36960
37157
  let timedOut = false;
36961
- let settled = false;
37158
+ let settled2 = false;
36962
37159
  const kill = () => {
36963
37160
  try {
36964
37161
  if (posix && child.pid) process.kill(-child.pid, "SIGKILL");
@@ -36977,8 +37174,8 @@ function runAsync(tc, cwd, args2, timeoutMs) {
36977
37174
  if (stderr.length < MAX) stderr += d.toString("utf-8");
36978
37175
  });
36979
37176
  const finish = (ok, message) => {
36980
- if (settled) return;
36981
- settled = true;
37177
+ if (settled2) return;
37178
+ settled2 = true;
36982
37179
  clearTimeout(timer);
36983
37180
  if (ok) resolve10({ ok: true, stdout, stderr: "", durationMs: Date.now() - started, timedOut: false });
36984
37181
  else resolve10({ ok: false, stdout, stderr: stderr || message || "", durationMs: Date.now() - started, timedOut });
@@ -37487,16 +37684,138 @@ function resumeInterruptedInstall(opts) {
37487
37684
  return shouldStartBackgroundInstall({ formal: opts.formal, env, free: opts.free }).start && startBackgroundInstall({ cli: opts.cli, env, run: opts.run });
37488
37685
  }
37489
37686
 
37687
+ // src/lib/prove/formal-default.ts
37688
+ var import_node_fs64 = require("node:fs");
37689
+ var import_node_path52 = require("node:path");
37690
+ var NOTICE_MARKER = ".formal-default-notice";
37691
+ var LEAN_MARKER = ".formal-lean-checked";
37692
+ function markerPath3(name2) {
37693
+ return (0, import_node_path52.join)(verityHome(), name2);
37694
+ }
37695
+ function writeMarker(name2, now) {
37696
+ try {
37697
+ (0, import_node_fs64.mkdirSync)(verityHome(), { recursive: true, mode: 448 });
37698
+ (0, import_node_fs64.writeFileSync)(markerPath3(name2), now.toISOString() + "\n");
37699
+ } catch {
37700
+ }
37701
+ }
37702
+ function formalDefaultNoticeDue() {
37703
+ return !(0, import_node_fs64.existsSync)(markerPath3(NOTICE_MARKER));
37704
+ }
37705
+ function recordFormalDefaultNotice(now = /* @__PURE__ */ new Date()) {
37706
+ writeMarker(NOTICE_MARKER, now);
37707
+ }
37708
+ var BLOCKED_LINES = {
37709
+ "opted-out": () => "VERITY_NO_LEAN=1 is set, so Verity does not download Lean 4. Without Lean 4, a push skips the formal check.",
37710
+ ci: () => "This is a CI environment, so Verity does not download Lean 4 (VERITY_LEAN=1 allows it). Without Lean 4, a push skips the formal check.",
37711
+ disk: (detail2) => `Lean 4 needs more disk space${detail2 ? ` (${detail2})` : ""}. Until Lean 4 is installed, a push skips the formal check. Free some space, then run: verity lean install`,
37712
+ "no-release": () => "Lean 4 has no release for this machine, so a push skips the formal check."
37713
+ };
37714
+ function leanLine(lean) {
37715
+ switch (lean.kind) {
37716
+ case "started":
37717
+ return "Lean 4 (about 800 MB) is downloading in the background. To see the progress, run: verity lean status";
37718
+ case "downloading":
37719
+ return "Lean 4 is downloading in the background. To see the progress, run: verity lean status";
37720
+ case "failed":
37721
+ return "The Lean 4 download could not start. Until Lean 4 is installed, a push skips the formal check. To install it, run: verity lean install";
37722
+ case "previous-failed":
37723
+ return "The last Lean 4 download failed. Until Lean 4 is installed, a push skips the formal check. To try again, run: verity lean install";
37724
+ case "blocked":
37725
+ return BLOCKED_LINES[lean.why](lean.detail);
37726
+ case "installed":
37727
+ case "unknown":
37728
+ return null;
37729
+ }
37730
+ }
37731
+ function formalDefaultUserMessage(lean, offRefused) {
37732
+ const aboutLean = leanLine(lean);
37733
+ return [
37734
+ ...offRefused ? [`Verity: ${formalOffRefusalMessage(offRefused)}`] : [],
37735
+ "Verity: formal verification at push is now on by default.",
37736
+ "At a push, Verity can check a small Lean model of the riskiest changed function and replay a counterexample on the real code. For the replay, the source of that function goes to the Verity service.",
37737
+ "Only a reproduced bug can block a push.",
37738
+ ...aboutLean ? [aboutLean] : [],
37739
+ "To turn it off for this project, run: verity config formal off"
37740
+ ].join("\n");
37741
+ }
37742
+ var AGENT_LINE = "Formal verification at push is on by default in this project. At a push, Verity can model one changed function in Lean, so a push can take longer. A block from it means that the counterexample was reproduced on the real code. If the user wants it off, the command is `verity config formal off`.";
37743
+ function settled(lean) {
37744
+ return lean.kind === "installed" || lean.kind === "blocked" && lean.why === "no-release";
37745
+ }
37746
+ var LEAN_RECHECK_MS = 7 * 24 * 60 * 60 * 1e3;
37747
+ function settledRecently(now) {
37748
+ try {
37749
+ const at = Date.parse((0, import_node_fs64.readFileSync)(markerPath3(LEAN_MARKER), "utf-8").trim());
37750
+ return Number.isFinite(at) && now.getTime() - at >= 0 && now.getTime() - at < LEAN_RECHECK_MS;
37751
+ } catch {
37752
+ return false;
37753
+ }
37754
+ }
37755
+ function leanAtStart(opts, now, probeAgain) {
37756
+ const { env } = opts;
37757
+ if (env.VERITY_NO_LEAN === "1") return { kind: "blocked", why: "opted-out" };
37758
+ if (env.CI && env.VERITY_LEAN !== "1") return { kind: "blocked", why: "ci" };
37759
+ const state = readInstallState(env);
37760
+ if (state?.state === "failed") return { kind: "previous-failed" };
37761
+ if (state?.state === "done") return { kind: "installed" };
37762
+ if (state?.state === "running") return installRunning(env) ? { kind: "downloading" } : { kind: "unknown" };
37763
+ if (!probeAgain && settledRecently(now)) return { kind: "unknown" };
37764
+ const lean = probeAndStart(opts);
37765
+ if (settled(lean)) writeMarker(LEAN_MARKER, now);
37766
+ return lean;
37767
+ }
37768
+ function probeAndStart(opts) {
37769
+ const { env } = opts;
37770
+ const d = shouldStartBackgroundInstall({ formal: "push", env, free: opts.free });
37771
+ if (d.start) {
37772
+ const started = startBackgroundInstall({ cli: opts.cli, env, ...opts.run ? { run: opts.run } : {} });
37773
+ return started ? { kind: "started" } : { kind: "failed" };
37774
+ }
37775
+ switch (d.why) {
37776
+ case "installed":
37777
+ return { kind: "installed" };
37778
+ case "running":
37779
+ return { kind: "downloading" };
37780
+ case "opted-out":
37781
+ case "ci":
37782
+ case "no-release":
37783
+ return { kind: "blocked", why: d.why };
37784
+ case "disk":
37785
+ return { kind: "blocked", why: "disk", ...d.detail ? { detail: d.detail } : {} };
37786
+ case "formal-off":
37787
+ return { kind: "unknown" };
37788
+ }
37789
+ }
37790
+ function formalAtSessionStart(opts) {
37791
+ const env = opts.env ?? process.env;
37792
+ const now = opts.now ?? /* @__PURE__ */ new Date();
37793
+ const none = { userMsg: null, agentLine: null, leanStarted: false };
37794
+ const setting = formalSetting(opts.cfg, env);
37795
+ if (setting.state !== "push") return none;
37796
+ const noticeDue = setting.source === "default" && formalDefaultNoticeDue();
37797
+ let lean;
37798
+ try {
37799
+ lean = leanAtStart({ cli: opts.cli, env, ...opts.free !== void 0 ? { free: opts.free } : {}, ...opts.run ? { run: opts.run } : {} }, now, noticeDue);
37800
+ } catch (err2) {
37801
+ lean = { kind: "failed" };
37802
+ logEvent("formal_default_lean_failed", { error: String(err2?.message ?? err2).slice(0, 200) });
37803
+ }
37804
+ const leanStarted = lean.kind === "started";
37805
+ if (!noticeDue) return { ...none, leanStarted };
37806
+ return { userMsg: formalDefaultUserMessage(lean, opts.cfg.formal_off_refused), agentLine: AGENT_LINE, leanStarted };
37807
+ }
37808
+
37490
37809
  // src/commands/baseline.ts
37491
- var import_node_fs66 = require("node:fs");
37492
- var import_node_path54 = require("node:path");
37810
+ var import_node_fs67 = require("node:fs");
37811
+ var import_node_path55 = require("node:path");
37493
37812
 
37494
37813
  // src/lib/skew-notice.ts
37495
- var import_node_fs64 = require("node:fs");
37496
- var import_node_path52 = require("node:path");
37814
+ var import_node_fs65 = require("node:fs");
37815
+ var import_node_path53 = require("node:path");
37497
37816
  var MARKER2 = ".skew-notice.json";
37498
- function markerPath3() {
37499
- return (0, import_node_path52.join)(verityHome(), MARKER2);
37817
+ function markerPath4() {
37818
+ return (0, import_node_path53.join)(verityHome(), MARKER2);
37500
37819
  }
37501
37820
  function today(now) {
37502
37821
  const y = now.getFullYear();
@@ -37506,7 +37825,7 @@ function today(now) {
37506
37825
  }
37507
37826
  function skewNoticeDue(skew, now = /* @__PURE__ */ new Date()) {
37508
37827
  try {
37509
- const seen = JSON.parse((0, import_node_fs64.readFileSync)(markerPath3(), "utf-8"));
37828
+ const seen = JSON.parse((0, import_node_fs65.readFileSync)(markerPath4(), "utf-8"));
37510
37829
  return !(seen.global === skew.global && seen.hooks === skew.hooks && seen.day === today(now));
37511
37830
  } catch {
37512
37831
  return true;
@@ -37514,15 +37833,15 @@ function skewNoticeDue(skew, now = /* @__PURE__ */ new Date()) {
37514
37833
  }
37515
37834
  function recordSkewNotice(skew, now = /* @__PURE__ */ new Date()) {
37516
37835
  try {
37517
- (0, import_node_fs64.mkdirSync)(verityHome(), { recursive: true, mode: 448 });
37518
- (0, import_node_fs64.writeFileSync)(markerPath3(), JSON.stringify({ global: skew.global, hooks: skew.hooks, day: today(now) }) + "\n");
37836
+ (0, import_node_fs65.mkdirSync)(verityHome(), { recursive: true, mode: 448 });
37837
+ (0, import_node_fs65.writeFileSync)(markerPath4(), JSON.stringify({ global: skew.global, hooks: skew.hooks, day: today(now) }) + "\n");
37519
37838
  } catch {
37520
37839
  }
37521
37840
  }
37522
37841
 
37523
37842
  // src/lib/project-skills.ts
37524
- var import_node_fs65 = require("node:fs");
37525
- var import_node_path53 = require("node:path");
37843
+ var import_node_fs66 = require("node:fs");
37844
+ var import_node_path54 = require("node:path");
37526
37845
  var PROJECT_SKILL_NAMES = [
37527
37846
  "verity-setup",
37528
37847
  "verity-analyze",
@@ -37548,14 +37867,14 @@ var LEGACY_SKILL_NAMES = [
37548
37867
  var ALL = [...PROJECT_SKILL_NAMES, ...LEGACY_SKILL_NAMES];
37549
37868
  function staleProjectSkills() {
37550
37869
  const root = projectPath(".claude/skills");
37551
- if (!(0, import_node_fs65.existsSync)(root)) return [];
37552
- return ALL.filter((name2) => (0, import_node_fs65.existsSync)((0, import_node_path53.join)(root, name2)));
37870
+ if (!(0, import_node_fs66.existsSync)(root)) return [];
37871
+ return ALL.filter((name2) => (0, import_node_fs66.existsSync)((0, import_node_path54.join)(root, name2)));
37553
37872
  }
37554
37873
  function removeProjectSkills() {
37555
37874
  const root = projectPath(".claude/skills");
37556
37875
  const removed = [];
37557
37876
  for (const name2 of staleProjectSkills()) {
37558
- (0, import_node_fs65.rmSync)((0, import_node_path53.join)(root, name2), { recursive: true, force: true });
37877
+ (0, import_node_fs66.rmSync)((0, import_node_path54.join)(root, name2), { recursive: true, force: true });
37559
37878
  removed.push(name2);
37560
37879
  }
37561
37880
  return removed;
@@ -37590,7 +37909,7 @@ function registerBaselineCommands(program2) {
37590
37909
  const loginWarning = "Until they have, do not send them to run `verity login` in a terminal: that runs the global one, which can sign this machine in to a different server.";
37591
37910
  const skewNote = !skew ? null : skewLoud ? `Before anything else, including /verity:init's questions, tell the user their global \`verity\` is ${skew.global}, the hooks run ${skew.hooks}, and \`${skew.remedy}\` fixes it. ${loginWarning}` : `The user's global \`verity\` is ${skew.global} and the hooks run ${skew.hooks}; \`${skew.remedy}\` fixes it. They were already told today, so do not repeat it unprompted \u2014 mention it only where it matters, such as before suggesting a \`verity\` command for their terminal. ${loginWarning}`;
37592
37911
  if (!verityConfigured()) {
37593
- if (isMachineStore((0, import_node_path54.join)(repoRoot(), VERITY_DIR))) process.exit(0);
37912
+ if (isMachineStore((0, import_node_path55.join)(repoRoot(), VERITY_DIR))) process.exit(0);
37594
37913
  const how = isPluginInvocation() ? "Tell the user to run /verity:init \u2014 they type it; you cannot invoke it." : "Tell the user to run `verity init` in their terminal.";
37595
37914
  const lines = [
37596
37915
  `Verity is installed but this project is not set up yet, so the quality gate will not review anything here. ${how}`
@@ -37624,17 +37943,20 @@ function registerBaselineCommands(program2) {
37624
37943
  let memoryMsg = null;
37625
37944
  let memoryAgentLine = null;
37626
37945
  const memoryNotice = projectPath(`${VERITY_DIR}/.memory-fence-notice`);
37627
- if (realStart && !(0, import_node_fs66.existsSync)(memoryNotice)) {
37946
+ if (realStart && !(0, import_node_fs67.existsSync)(memoryNotice)) {
37628
37947
  const trackedGraph = memoryOptOut() ? 0 : committedMemoryFiles().length;
37629
37948
  if (trackedGraph > 0) {
37630
37949
  memoryMsg = `Verity: this project commits its knowledge base (${trackedGraph} files under .verity/memory/), so Verity's generated notes show up in every diff and pull request. Run \`verity memory untrack\` to keep them on disk but out of git, or \`verity memory track\` to keep committing them on purpose.`;
37631
37950
  memoryAgentLine = `This project has ${trackedGraph} knowledge-graph files tracked in git under .verity/memory/. As of Verity 0.32.6 the graph is machine-local by default \u2014 it is rebuilt from the service, and committing it puts generated notes in every pull request. If the user wants that stopped, run \`verity memory untrack\` for them: it keeps every file on disk and stages their removal from the index, so they only need to commit \u2014 and their teammates' working copies will vanish on the next pull and re-sync from the service, which is expected. If they would rather keep committing it, \`verity memory track\` records that and nothing will offer again.`;
37632
37951
  try {
37633
- (0, import_node_fs66.writeFileSync)(memoryNotice, (/* @__PURE__ */ new Date()).toISOString() + "\n");
37952
+ (0, import_node_fs67.writeFileSync)(memoryNotice, (/* @__PURE__ */ new Date()).toISOString() + "\n");
37634
37953
  } catch {
37635
37954
  }
37636
37955
  }
37637
37956
  }
37957
+ const formalStart = realStart ? formalAtSessionStart({ cli: process.argv[1], cfg: readProjectConfig() }) : null;
37958
+ const formalMsg = formalStart?.userMsg ?? null;
37959
+ const formalAgentLine = formalStart?.agentLine ?? null;
37638
37960
  if (isPluginInvocation() && realStart) {
37639
37961
  const installed2 = activePluginVersion();
37640
37962
  const state = await readSetupState().catch(() => null);
@@ -37658,10 +37980,11 @@ function registerBaselineCommands(program2) {
37658
37980
  }
37659
37981
  }
37660
37982
  if (memoryAgentLine) notices.push(memoryAgentLine);
37983
+ if (formalAgentLine) notices.push(formalAgentLine);
37661
37984
  if (notices.length > 0 || skewMsg) {
37662
37985
  process.stdout.write(
37663
37986
  JSON.stringify({
37664
- ...userMsg || skewMsg || memoryMsg ? { systemMessage: [skewMsg, userMsg, memoryMsg].filter(Boolean).join("\n") } : {},
37987
+ ...userMsg || skewMsg || memoryMsg || formalMsg ? { systemMessage: [skewMsg, userMsg, memoryMsg, formalMsg].filter(Boolean).join("\n") } : {},
37665
37988
  hookSpecificOutput: {
37666
37989
  hookEventName: "SessionStart",
37667
37990
  additionalContext: notices.join("\n")
@@ -37669,17 +37992,20 @@ function registerBaselineCommands(program2) {
37669
37992
  }) + "\n"
37670
37993
  );
37671
37994
  if (skew && skewMsg) recordSkewNotice(skew);
37995
+ if (formalMsg) recordFormalDefaultNotice();
37672
37996
  }
37673
- } else if (memoryMsg) {
37997
+ } else if (memoryMsg || formalMsg) {
37998
+ const agentLines = [memoryAgentLine, formalAgentLine].filter(Boolean).join("\n");
37674
37999
  process.stdout.write(
37675
38000
  JSON.stringify({
37676
- systemMessage: memoryMsg,
38001
+ systemMessage: [memoryMsg, formalMsg].filter(Boolean).join("\n"),
37677
38002
  hookSpecificOutput: {
37678
38003
  hookEventName: "SessionStart",
37679
- ...memoryAgentLine ? { additionalContext: memoryAgentLine } : {}
38004
+ ...agentLines ? { additionalContext: agentLines } : {}
37680
38005
  }
37681
38006
  }) + "\n"
37682
38007
  );
38008
+ if (formalMsg) recordFormalDefaultNotice();
37683
38009
  }
37684
38010
  if (deferredToPlugin("baseline capture", session ?? null)) {
37685
38011
  process.exit(0);
@@ -37687,7 +38013,7 @@ function registerBaselineCommands(program2) {
37687
38013
  const authForScope = await resolveToken(program2.opts().token);
37688
38014
  const scopeToken = authForScope.ok ? authForScope.data.token : void 0;
37689
38015
  if (authForScope.ok && pullsAtSessionStart(authForScope.data, source2)) spawnBackgroundPull();
37690
- if (source2 !== "compact") resumeInterruptedInstall({ cli: process.argv[1], formal: readProjectConfig().formal });
38016
+ if (source2 !== "compact") resumeInterruptedInstall({ cli: process.argv[1], formal: formalSetting(readProjectConfig()).state });
37691
38017
  const scopeSession = session;
37692
38018
  const result = captureBaseline({ sessionId: sessionScopeKey(scopeToken, scopeSession), source: source2 });
37693
38019
  logEvent("baseline_capture", {
@@ -37733,31 +38059,31 @@ function hookSource(value) {
37733
38059
  }
37734
38060
 
37735
38061
  // src/commands/review.ts
37736
- var import_node_fs67 = require("node:fs");
37737
- var import_node_path55 = require("node:path");
38062
+ var import_node_fs68 = require("node:fs");
38063
+ var import_node_path56 = require("node:path");
37738
38064
  function containReviewPaths(paths, base) {
37739
38065
  const kept = [];
37740
38066
  const refused2 = [];
37741
38067
  for (const raw of paths) {
37742
- const candidate = (0, import_node_path55.isAbsolute)(raw) ? relativeForm(base, raw) : raw;
38068
+ const candidate = (0, import_node_path56.isAbsolute)(raw) ? relativeForm(base, raw) : raw;
37743
38069
  if (candidate === "" || resolveInside(base, candidate) === null) refused2.push(raw);
37744
38070
  else kept.push(candidate);
37745
38071
  }
37746
38072
  return { kept, refused: refused2 };
37747
38073
  }
37748
38074
  function relativeForm(base, abs) {
37749
- const lexical = (0, import_node_path55.relative)(base, abs);
37750
- if (!(lexical === ".." || lexical.startsWith(`..${import_node_path55.sep}`) || (0, import_node_path55.isAbsolute)(lexical))) return lexical;
38075
+ const lexical = (0, import_node_path56.relative)(base, abs);
38076
+ if (!(lexical === ".." || lexical.startsWith(`..${import_node_path56.sep}`) || (0, import_node_path56.isAbsolute)(lexical))) return lexical;
37751
38077
  try {
37752
- let dir = (0, import_node_path55.dirname)(abs);
37753
- let rest = (0, import_node_path55.basename)(abs);
37754
- while (!(0, import_node_fs67.existsSync)(dir)) {
37755
- const up = (0, import_node_path55.dirname)(dir);
38078
+ let dir = (0, import_node_path56.dirname)(abs);
38079
+ let rest = (0, import_node_path56.basename)(abs);
38080
+ while (!(0, import_node_fs68.existsSync)(dir)) {
38081
+ const up = (0, import_node_path56.dirname)(dir);
37756
38082
  if (up === dir) return lexical;
37757
- rest = (0, import_node_path55.join)((0, import_node_path55.basename)(dir), rest);
38083
+ rest = (0, import_node_path56.join)((0, import_node_path56.basename)(dir), rest);
37758
38084
  dir = up;
37759
38085
  }
37760
- return (0, import_node_path55.relative)((0, import_node_fs67.realpathSync)(base), (0, import_node_path55.join)((0, import_node_fs67.realpathSync)(dir), rest));
38086
+ return (0, import_node_path56.relative)((0, import_node_fs68.realpathSync)(base), (0, import_node_path56.join)((0, import_node_fs68.realpathSync)(dir), rest));
37761
38087
  } catch {
37762
38088
  return lexical;
37763
38089
  }
@@ -37796,7 +38122,7 @@ async function runReview(opts, globals) {
37796
38122
  const changedFiles = changed.kept;
37797
38123
  const analyzable = filterAnalyzable(allFiles);
37798
38124
  const securityFiles = filterSecurity(allFiles);
37799
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs67.existsSync)(f) || resolveFile(f) !== null);
38125
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).filter((f) => (0, import_node_fs68.existsSync)(f) || resolveFile(f) !== null);
37800
38126
  const staticResults = runCodacyAnalysisIfAvailable(scannable);
37801
38127
  const codeDelta = collectCodeDelta(allFiles);
37802
38128
  const tokenResult = await resolveToken(globals.token);
@@ -37867,12 +38193,12 @@ async function runReview(opts, globals) {
37867
38193
  }
37868
38194
 
37869
38195
  // src/commands/guard.ts
37870
- var import_node_fs69 = require("node:fs");
37871
- var import_node_path58 = require("node:path");
38196
+ var import_node_fs70 = require("node:fs");
38197
+ var import_node_path59 = require("node:path");
37872
38198
 
37873
38199
  // src/lib/vendored.ts
37874
- var import_node_fs68 = require("node:fs");
37875
- var import_node_path56 = require("node:path");
38200
+ var import_node_fs69 = require("node:fs");
38201
+ var import_node_path57 = require("node:path");
37876
38202
  var INSTALLED_SKILL = /^\.claude\/skills\/(verity-[a-z0-9-]+)\/(.+)$/;
37877
38203
  function dataDirOrNull() {
37878
38204
  try {
@@ -37887,7 +38213,7 @@ function shippedSkillFile(path, dataDir = dataDirOrNull()) {
37887
38213
  const [, skill, rel] = m;
37888
38214
  if (rel.split("/").some((seg) => seg === ".." || seg === ".")) return null;
37889
38215
  try {
37890
- return (0, import_node_fs68.readFileSync)((0, import_node_path56.join)(dataDir, "skills", skill, rel), "utf8");
38216
+ return (0, import_node_fs69.readFileSync)((0, import_node_path57.join)(dataDir, "skills", skill, rel), "utf8");
37891
38217
  } catch {
37892
38218
  return null;
37893
38219
  }
@@ -37926,10 +38252,10 @@ function summarizeFindings(response) {
37926
38252
  return out2;
37927
38253
  });
37928
38254
  }
37929
- function finishOneShot(settled) {
37930
- if (!settled) return { status: "pending" };
37931
- if (!settled.ok) return { status: "failed" };
37932
- const r = settled.response;
38255
+ function finishOneShot(settled2) {
38256
+ if (!settled2) return { status: "pending" };
38257
+ if (!settled2.ok) return { status: "failed" };
38258
+ const r = settled2.response;
37933
38259
  return {
37934
38260
  status: "arrived",
37935
38261
  ...typeof r.agent_attestation === "string" ? { attestation: r.agent_attestation } : {},
@@ -37940,13 +38266,13 @@ function finishOneShot(settled) {
37940
38266
  async function runAgentReview(options) {
37941
38267
  const now = options.now ?? Date.now;
37942
38268
  const remaining = () => options.deadlineMs - (now() - options.startedAt);
37943
- let settled = null;
38269
+ let settled2 = null;
37944
38270
  const oneShotDone = options.oneShot.then(
37945
38271
  (outcome) => {
37946
- settled = outcome;
38272
+ settled2 = outcome;
37947
38273
  },
37948
38274
  () => {
37949
- settled = { ok: false };
38275
+ settled2 = { ok: false };
37950
38276
  }
37951
38277
  );
37952
38278
  if (remaining() < MIN_AGENT_START_MS) return { kind: "skipped", reason: "not enough time left after prep" };
@@ -37968,7 +38294,7 @@ async function runAgentReview(options) {
37968
38294
  if (turn >= MAX_TURNS || remaining() < MIN_TURN_MS) break;
37969
38295
  const results = await options.executeTools(calls);
37970
38296
  let oneShot;
37971
- const current = settled;
38297
+ const current = settled2;
37972
38298
  if (current && sentOneShot === "pending") {
37973
38299
  oneShot = current.ok ? { status: "arrived", findings: summarizeFindings(current.response) } : { status: "failed" };
37974
38300
  sentOneShot = oneShot.status;
@@ -37983,13 +38309,13 @@ async function runAgentReview(options) {
37983
38309
  calls = next.data.calls ?? [];
37984
38310
  done = next.data.done;
37985
38311
  }
37986
- if (!settled) {
38312
+ if (!settled2) {
37987
38313
  const wait = Math.max(0, remaining() - FINISH_TIMEOUT_MS);
37988
38314
  await Promise.race([oneShotDone, new Promise((resolve10) => setTimeout(resolve10, wait))]);
37989
38315
  }
37990
38316
  const finished = await options.http.post(
37991
38317
  "/agent-review/finish",
37992
- { state, session_id: options.sessionId, one_shot: finishOneShot(settled) },
38318
+ { state, session_id: options.sessionId, one_shot: finishOneShot(settled2) },
37993
38319
  FINISH_TIMEOUT_MS
37994
38320
  );
37995
38321
  if (!finished.ok) {
@@ -38016,7 +38342,7 @@ function effortProfile(root, env = process.env) {
38016
38342
  }
38017
38343
 
38018
38344
  // src/lib/prove/auto.ts
38019
- var import_node_path57 = require("node:path");
38345
+ var import_node_path58 = require("node:path");
38020
38346
  var import_node_crypto21 = require("node:crypto");
38021
38347
 
38022
38348
  // src/lib/prove/grounding.ts
@@ -40244,7 +40570,7 @@ async function runFormalPhase(input) {
40244
40570
  modelName = gen.name;
40245
40571
  const rel = `${AUTO_MODELS_DIR}/${gen.name}.lean`;
40246
40572
  writeFileInRootNoFollow(root, rel, gen.source);
40247
- const abs = resolveModelFile(root, rel) ?? (0, import_node_path57.join)(root, rel);
40573
+ const abs = resolveModelFile(root, rel) ?? (0, import_node_path58.join)(root, rel);
40248
40574
  progress.model = { name: gen.name, file: rel, rounds: round };
40249
40575
  progress.step = "check";
40250
40576
  report = await deps.check(root, abs);
@@ -40393,7 +40719,7 @@ async function runFormalPhase(input) {
40393
40719
  return done({
40394
40720
  status: "checked",
40395
40721
  target,
40396
- model: { name: modelName, file: (0, import_node_path57.relative)(root, (0, import_node_path57.join)(root, modelFile)), rounds },
40722
+ model: { name: modelName, file: (0, import_node_path58.relative)(root, (0, import_node_path58.join)(root, modelFile)), rounds },
40397
40723
  properties: report.properties,
40398
40724
  findings
40399
40725
  });
@@ -40470,7 +40796,7 @@ async function generateAndCheck(root, req, opts) {
40470
40796
  }
40471
40797
  const rel = `${dir}/${gen.name}.lean`;
40472
40798
  writeFileInRootNoFollow(root, rel, gen.source);
40473
- const abs = resolveModelFile(root, rel) ?? (0, import_node_path57.join)(root, rel);
40799
+ const abs = resolveModelFile(root, rel) ?? (0, import_node_path58.join)(root, rel);
40474
40800
  const report = await opts.deps.check(root, abs);
40475
40801
  const abstracts = report.header?.abstracts ?? [];
40476
40802
  const stray = abstracts.find((a) => !headerFiles.has(a.file)) ?? (abstracts[0] && abstracts[0].file !== req.file ? abstracts[0] : void 0);
@@ -40879,7 +41205,7 @@ var WRITERS_BUDGET_MS = 1500;
40879
41205
  var EXCERPT_MAX_LINES2 = 30;
40880
41206
  var VENDORED_READ_MAX_BYTES = 256 * 1024;
40881
41207
  var GUARD_BLOCK_CAP = 2;
40882
- var GUARD_ITER_FILE = (0, import_node_path58.join)(VERITY_DIR, ".guard-iteration");
41208
+ var GUARD_ITER_FILE = (0, import_node_path59.join)(VERITY_DIR, ".guard-iteration");
40883
41209
  function readPreToolUseStdin() {
40884
41210
  const empty = { command: "", cwd: null, sessionId: null };
40885
41211
  return new Promise((resolve10) => {
@@ -40887,11 +41213,11 @@ function readPreToolUseStdin() {
40887
41213
  if (process.stdin.isTTY) return resolve10(empty);
40888
41214
  const chunks = [];
40889
41215
  let timer;
40890
- let settled = false;
41216
+ let settled2 = false;
40891
41217
  const onData = (c) => chunks.push(c);
40892
41218
  const settle = (value) => {
40893
- if (settled) return;
40894
- settled = true;
41219
+ if (settled2) return;
41220
+ settled2 = true;
40895
41221
  if (timer) clearTimeout(timer);
40896
41222
  process.stdin.removeListener("data", onData);
40897
41223
  process.stdin.removeListener("end", onEnd);
@@ -40924,7 +41250,7 @@ function readPreToolUseStdin() {
40924
41250
  }
40925
41251
  function readIterMap() {
40926
41252
  try {
40927
- const raw = JSON.parse((0, import_node_fs69.readFileSync)(GUARD_ITER_FILE, "utf-8"));
41253
+ const raw = JSON.parse((0, import_node_fs70.readFileSync)(GUARD_ITER_FILE, "utf-8"));
40928
41254
  if (raw && typeof raw === "object") {
40929
41255
  if (typeof raw.moment === "string" && typeof raw.count === "number") {
40930
41256
  return { [raw.moment]: raw.count };
@@ -40944,10 +41270,10 @@ function readIter(moment) {
40944
41270
  }
40945
41271
  function writeIter(moment, count2) {
40946
41272
  try {
40947
- (0, import_node_fs69.mkdirSync)(VERITY_DIR, { recursive: true });
41273
+ (0, import_node_fs70.mkdirSync)(VERITY_DIR, { recursive: true });
40948
41274
  const map = readIterMap();
40949
41275
  map[moment] = count2;
40950
- (0, import_node_fs69.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
41276
+ (0, import_node_fs70.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
40951
41277
  } catch {
40952
41278
  }
40953
41279
  }
@@ -40957,10 +41283,10 @@ function resetIter(moment) {
40957
41283
  if (!(moment in map)) return;
40958
41284
  delete map[moment];
40959
41285
  if (Object.keys(map).length === 0) {
40960
- if ((0, import_node_fs69.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs69.unlinkSync)(GUARD_ITER_FILE);
41286
+ if ((0, import_node_fs70.existsSync)(GUARD_ITER_FILE)) (0, import_node_fs70.unlinkSync)(GUARD_ITER_FILE);
40961
41287
  } else {
40962
- (0, import_node_fs69.mkdirSync)(VERITY_DIR, { recursive: true });
40963
- (0, import_node_fs69.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
41288
+ (0, import_node_fs70.mkdirSync)(VERITY_DIR, { recursive: true });
41289
+ (0, import_node_fs70.writeFileSync)(GUARD_ITER_FILE, JSON.stringify(map));
40964
41290
  }
40965
41291
  } catch {
40966
41292
  }
@@ -41037,7 +41363,7 @@ function ofThisChange(response, sentFiles) {
41037
41363
  function buildGuardRequest(moment, files, codeDelta, iter, sessionId, statedIntent, coverageTelemetry) {
41038
41364
  const analyzable = filterAnalyzable(files);
41039
41365
  const securityFiles = filterSecurity(files);
41040
- const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs69.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
41366
+ const scannable = Array.from(/* @__PURE__ */ new Set([...analyzable, ...securityFiles])).map((f) => (0, import_node_fs70.existsSync)(f) ? f : resolveFile(f)).filter((f) => f !== null);
41041
41367
  const staticResults = runCodacyAnalysisIfAvailable(scannable);
41042
41368
  const trigger = moment === "pre-commit" ? "hook:pre-commit" : "hook:pre-push";
41043
41369
  const requestBody2 = {
@@ -41210,11 +41536,14 @@ function startAgentReview(input) {
41210
41536
  };
41211
41537
  return run4().catch((err2) => ({ kind: "failed", reason: String(err2?.message ?? err2) }));
41212
41538
  }
41539
+ function formalOffUnverifiedLine(moment, config) {
41540
+ if (moment !== "pre-push" || !config.formal_off_unverified) return "";
41541
+ const setting = formalSetting(config);
41542
+ return setting.state === "off" && setting.source === "config" ? `formal: skipped (${formalOffUnverifiedNote()})` : "";
41543
+ }
41213
41544
  function startFormalPhase(input) {
41214
41545
  if (input.moment !== "pre-push") return null;
41215
- const env = process.env.VERITY_GIT_MOMENT_FORMAL;
41216
- if (env === "off") return null;
41217
- if (env !== "push" && readProjectConfig().formal !== "push") return null;
41546
+ if (formalSetting(input.config).state !== "push") return null;
41218
41547
  const profile = effortProfile(input.root);
41219
41548
  const deadlineAt = input.hookStartedAt + profile.pushDeadlineMs - 1e4;
41220
41549
  const ig = loadVerityIgnore();
@@ -41420,7 +41749,8 @@ async function runGuard(opts, globals) {
41420
41749
  requestBody: requestBody2,
41421
41750
  oneShot
41422
41751
  });
41423
- const formal = startFormalPhase({ moment, files, root: frame.worktreeRoot, hookStartedAt, frame, range, auth: { serviceUrl: urlResult.data, token: tokenResult.data.token } });
41752
+ const projectConfig = moment === "pre-push" ? readProjectConfig() : null;
41753
+ const formal = projectConfig ? startFormalPhase({ moment, files, root: frame.worktreeRoot, hookStartedAt, frame, range, auth: { serviceUrl: urlResult.data, token: tokenResult.data.token }, config: projectConfig }) : null;
41424
41754
  const result = await oneShot;
41425
41755
  const agentOutcome = agent ? await agent : null;
41426
41756
  const formalOutcome = formal ? await formal : null;
@@ -41472,7 +41802,7 @@ async function runGuard(opts, globals) {
41472
41802
  const link2 = viewUrl ? ` \u2014 ${viewUrl}` : "";
41473
41803
  const covLine = `${coverageSummary(coverage)} in ${reviewSecs}s`;
41474
41804
  const agentLine = describeAgentOutcome(agentOutcome);
41475
- const formalLine = describeFormalOutcome(formalOutcome);
41805
+ const formalLine = describeFormalOutcome(formalOutcome) || (projectConfig ? formalOffUnverifiedLine(moment, projectConfig) : "");
41476
41806
  const covDetail = [coverageBlock(coverage), agentLine, formalLine].filter(Boolean).join("\n ");
41477
41807
  const witnessed = [...codeDelta.files.map((f) => f.path), ...agentVerdict?.witnessed_files ?? []];
41478
41808
  mergeFormalFindings(response, formalOutcome, files);
@@ -41713,7 +42043,7 @@ function registerIgnoreCommand(program2) {
41713
42043
 
41714
42044
  // src/commands/waive.ts
41715
42045
  var import_node_crypto23 = require("node:crypto");
41716
- var import_node_fs70 = require("node:fs");
42046
+ var import_node_fs71 = require("node:fs");
41717
42047
  function registerWaiveCommand(program2) {
41718
42048
  program2.command("waive <pattern-id>").description("Record an accepted-risk disposition for an open finding (voids when the file changes)").option("--file <path>", "File the finding is anchored to, REPO-RELATIVE (recommended \u2014 narrows the waive)").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action(async (patternId, opts) => {
41719
42049
  const globals = program2.opts();
@@ -41742,7 +42072,7 @@ function registerWaiveCommand(program2) {
41742
42072
  if (opts.file) {
41743
42073
  body2.file = opts.file;
41744
42074
  try {
41745
- body2.file_sha256 = (0, import_node_crypto23.createHash)("sha256").update((0, import_node_fs70.readFileSync)(opts.file)).digest("hex");
42075
+ body2.file_sha256 = (0, import_node_crypto23.createHash)("sha256").update((0, import_node_fs71.readFileSync)(opts.file)).digest("hex");
41746
42076
  } catch {
41747
42077
  printError(`Cannot read ${opts.file} \u2014 run from the repo root, or omit --file to waive by pattern.`);
41748
42078
  process.exit(1);
@@ -41768,15 +42098,15 @@ function registerWaiveCommand(program2) {
41768
42098
 
41769
42099
  // src/commands/init/index.ts
41770
42100
  var import_node_os15 = require("node:os");
41771
- var import_node_fs79 = require("node:fs");
42101
+ var import_node_fs80 = require("node:fs");
41772
42102
  var import_promises17 = require("node:fs/promises");
41773
- var import_node_path68 = require("node:path");
42103
+ var import_node_path69 = require("node:path");
41774
42104
 
41775
42105
  // src/lib/init-changes.ts
41776
42106
  var import_node_crypto24 = require("node:crypto");
41777
- var import_node_fs71 = require("node:fs");
42107
+ var import_node_fs72 = require("node:fs");
41778
42108
  var import_node_os12 = require("node:os");
41779
- var import_node_path59 = require("node:path");
42109
+ var import_node_path60 = require("node:path");
41780
42110
  var REPO_FILES = [
41781
42111
  ".gitignore",
41782
42112
  "CLAUDE.md",
@@ -41791,7 +42121,7 @@ var SKILLS_DIR = ".claude/skills";
41791
42121
  var MAX_HASH_BYTES = 2 * 1024 * 1024;
41792
42122
  var MAX_TREE_ENTRIES = 2e3;
41793
42123
  function userSettingsDir(env) {
41794
- return env.CLAUDE_CONFIG_DIR || (0, import_node_path59.join)(env.HOME || (0, import_node_os12.homedir)(), ".claude");
42124
+ return env.CLAUDE_CONFIG_DIR || (0, import_node_path60.join)(env.HOME || (0, import_node_os12.homedir)(), ".claude");
41795
42125
  }
41796
42126
  function fingerprint(root, rel) {
41797
42127
  const opened = openRegularInRoot(root, rel);
@@ -41799,7 +42129,7 @@ function fingerprint(root, rel) {
41799
42129
  if (opened.reason === "missing") return null;
41800
42130
  if (opened.reason === "symlink") {
41801
42131
  try {
41802
- return `link:${(0, import_node_fs71.readlinkSync)((0, import_node_path59.join)(root, rel))}`;
42132
+ return `link:${(0, import_node_fs72.readlinkSync)((0, import_node_path60.join)(root, rel))}`;
41803
42133
  } catch {
41804
42134
  return "link";
41805
42135
  }
@@ -41818,7 +42148,7 @@ function fingerprint(root, rel) {
41818
42148
  }
41819
42149
  function treeFingerprint(root, dirRel) {
41820
42150
  try {
41821
- if (!(0, import_node_fs71.lstatSync)((0, import_node_path59.join)(root, dirRel)).isDirectory()) return fingerprint(root, dirRel);
42151
+ if (!(0, import_node_fs72.lstatSync)((0, import_node_path60.join)(root, dirRel)).isDirectory()) return fingerprint(root, dirRel);
41822
42152
  } catch {
41823
42153
  return null;
41824
42154
  }
@@ -41826,7 +42156,7 @@ function treeFingerprint(root, dirRel) {
41826
42156
  const walk2 = (rel) => {
41827
42157
  let names2;
41828
42158
  try {
41829
- names2 = (0, import_node_fs71.readdirSync)((0, import_node_path59.join)(root, rel)).sort();
42159
+ names2 = (0, import_node_fs72.readdirSync)((0, import_node_path60.join)(root, rel)).sort();
41830
42160
  } catch {
41831
42161
  return;
41832
42162
  }
@@ -41835,7 +42165,7 @@ function treeFingerprint(root, dirRel) {
41835
42165
  const relPath = `${rel}/${name2}`;
41836
42166
  let st;
41837
42167
  try {
41838
- st = (0, import_node_fs71.lstatSync)((0, import_node_path59.join)(root, relPath));
42168
+ st = (0, import_node_fs72.lstatSync)((0, import_node_path60.join)(root, relPath));
41839
42169
  } catch {
41840
42170
  continue;
41841
42171
  }
@@ -41859,8 +42189,8 @@ function changedSince(before, after) {
41859
42189
 
41860
42190
  // src/lib/init-trace.ts
41861
42191
  var import_node_crypto25 = require("node:crypto");
41862
- var import_node_fs72 = require("node:fs");
41863
- var import_node_path60 = require("node:path");
42192
+ var import_node_fs73 = require("node:fs");
42193
+ var import_node_path61 = require("node:path");
41864
42194
  var INIT_STEPS = [
41865
42195
  "started",
41866
42196
  "prerequisites",
@@ -41906,9 +42236,9 @@ function traceDisabled(env = process.env) {
41906
42236
  }
41907
42237
  var PENDING_NAME = "init-trace.json";
41908
42238
  var MAX_PENDING_BYTES = 64 * 1024;
41909
- var verityHome2 = () => (0, import_node_path60.join)(process.env.HOME ?? "", ".verity");
42239
+ var verityHome2 = () => (0, import_node_path61.join)(process.env.HOME ?? "", ".verity");
41910
42240
  function pendingTracePath() {
41911
- return (0, import_node_path60.join)(verityHome2(), PENDING_NAME);
42241
+ return (0, import_node_path61.join)(verityHome2(), PENDING_NAME);
41912
42242
  }
41913
42243
  var RUN_ID = /^[0-9a-f]{16}$/;
41914
42244
  var CLI_VERSION = /^[0-9A-Za-z.+-]{1,40}$/;
@@ -41966,10 +42296,10 @@ function writePending(runs) {
41966
42296
  try {
41967
42297
  const kept = runs.filter((r) => r.events.length > 0).slice(-MAX_PENDING_RUNS);
41968
42298
  if (kept.length === 0) {
41969
- (0, import_node_fs72.rmSync)(pendingTracePath(), { force: true });
42299
+ (0, import_node_fs73.rmSync)(pendingTracePath(), { force: true });
41970
42300
  return;
41971
42301
  }
41972
- (0, import_node_fs72.mkdirSync)(verityHome2(), { recursive: true });
42302
+ (0, import_node_fs73.mkdirSync)(verityHome2(), { recursive: true });
41973
42303
  if (!replaceInRoot(verityHome2(), PENDING_NAME, JSON.stringify(kept))) {
41974
42304
  logEvent("init_trace_save_refused", {});
41975
42305
  }
@@ -42681,8 +43011,8 @@ var Rail = class {
42681
43011
  };
42682
43012
 
42683
43013
  // src/commands/init/ui.ts
42684
- var import_node_fs73 = require("node:fs");
42685
- var import_node_path61 = require("node:path");
43014
+ var import_node_fs74 = require("node:fs");
43015
+ var import_node_path62 = require("node:path");
42686
43016
  var import_node_os13 = require("node:os");
42687
43017
  var import_node_child_process18 = require("node:child_process");
42688
43018
  function createInitUi(opts) {
@@ -42702,10 +43032,10 @@ function installInterrupt(ui) {
42702
43032
  }
42703
43033
  var SEEN_FILE = ".init-seen";
42704
43034
  function firstInitOnMachine(home = process.env.HOME || (0, import_node_os13.homedir)()) {
42705
- return !(0, import_node_fs73.existsSync)((0, import_node_path61.join)(home, ".verity", SEEN_FILE));
43035
+ return !(0, import_node_fs74.existsSync)((0, import_node_path62.join)(home, ".verity", SEEN_FILE));
42706
43036
  }
42707
43037
  function markInitSeen(home = process.env.HOME || (0, import_node_os13.homedir)()) {
42708
- replaceInRoot((0, import_node_path61.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
43038
+ replaceInRoot((0, import_node_path62.join)(home, ".verity"), SEEN_FILE, `${(/* @__PURE__ */ new Date()).toISOString()}
42709
43039
  `);
42710
43040
  }
42711
43041
  function repoLabel() {
@@ -42715,12 +43045,12 @@ function repoLabel() {
42715
43045
  if (parsed) return terminalText(`${parsed.owner}/${parsed.repo}`, 120);
42716
43046
  } catch {
42717
43047
  }
42718
- return terminalText((0, import_node_path61.basename)(repoRoot()), 120);
43048
+ return terminalText((0, import_node_path62.basename)(repoRoot()), 120);
42719
43049
  }
42720
43050
 
42721
43051
  // src/lib/prereqs.ts
42722
43052
  var import_node_child_process19 = require("node:child_process");
42723
- var import_node_fs74 = require("node:fs");
43053
+ var import_node_fs75 = require("node:fs");
42724
43054
  var MIN_NODE_MAJOR = 20;
42725
43055
  function which(bin) {
42726
43056
  return whichSync(bin, { real: false });
@@ -42788,7 +43118,7 @@ function checkAnalysisCli(opts = {}) {
42788
43118
  }
42789
43119
  function sameFile(a, b) {
42790
43120
  try {
42791
- return (0, import_node_fs74.realpathSync)(a) === (0, import_node_fs74.realpathSync)(b);
43121
+ return (0, import_node_fs75.realpathSync)(a) === (0, import_node_fs75.realpathSync)(b);
42792
43122
  } catch {
42793
43123
  return false;
42794
43124
  }
@@ -42848,12 +43178,12 @@ async function checkPrereqs(opts = {}) {
42848
43178
  }
42849
43179
 
42850
43180
  // src/lib/telemetry-local.ts
42851
- var import_node_path63 = require("node:path");
43181
+ var import_node_path64 = require("node:path");
42852
43182
 
42853
43183
  // src/lib/telemetry-settings.ts
42854
43184
  var import_promises14 = require("node:fs/promises");
42855
43185
  var import_node_os14 = require("node:os");
42856
- var import_node_path62 = require("node:path");
43186
+ var import_node_path63 = require("node:path");
42857
43187
  var SETTINGS_LOCAL_FILE2 = ".claude/settings.local.json";
42858
43188
  var OTEL_HEADERS_HELPER_CMD = "verity telemetry headers";
42859
43189
  var LEGACY_HEADER_KEY = "OTEL_EXPORTER_OTLP_HEADERS";
@@ -42864,10 +43194,10 @@ var OTLP_ENDPOINT_KEYS = [
42864
43194
  "OTEL_EXPORTER_OTLP_LOGS_ENDPOINT"
42865
43195
  ];
42866
43196
  function claudeConfigDir2() {
42867
- return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path62.join)((0, import_node_os14.homedir)(), ".claude");
43197
+ return process.env.CLAUDE_CONFIG_DIR || (0, import_node_path63.join)((0, import_node_os14.homedir)(), ".claude");
42868
43198
  }
42869
43199
  function userSettingsPath() {
42870
- return (0, import_node_path62.join)(claudeConfigDir2(), "settings.json");
43200
+ return (0, import_node_path63.join)(claudeConfigDir2(), "settings.json");
42871
43201
  }
42872
43202
  function deriveOtlpEndpoint(serviceUrl) {
42873
43203
  return serviceUrl.replace(/\/+$/, "").replace(/\/v1$/, "") + "/v1/otlp";
@@ -42988,7 +43318,7 @@ async function healLocalTelemetry(root = repoRoot()) {
42988
43318
  if (settings.env && Object.keys(env).length === 0) delete settings.env;
42989
43319
  if (plan.helper) delete settings.otelHeadersHelper;
42990
43320
  const next = nextLocalBytes(local.raw, settings);
42991
- if (next !== null && !replaceInRoot((0, import_node_path63.join)(root, ".claude"), "settings.local.json", next)) {
43321
+ if (next !== null && !replaceInRoot((0, import_node_path64.join)(root, ".claude"), "settings.local.json", next)) {
42992
43322
  return { removed: [] };
42993
43323
  }
42994
43324
  return { removed: [...plan.envKeys, ...plan.helper ? ["otelHeadersHelper"] : []] };
@@ -42996,8 +43326,8 @@ async function healLocalTelemetry(root = repoRoot()) {
42996
43326
 
42997
43327
  // src/lib/telemetry-gate.ts
42998
43328
  var import_node_child_process20 = require("node:child_process");
42999
- var import_node_fs75 = require("node:fs");
43000
- var import_node_path64 = require("node:path");
43329
+ var import_node_fs76 = require("node:fs");
43330
+ var import_node_path65 = require("node:path");
43001
43331
  var PROJECT_MARKERS = ["setup.json", "config.json"];
43002
43332
  function writtenOnThisMachine(root, rel) {
43003
43333
  const r = (0, import_node_child_process20.spawnSync)("git", hardenedGitArgv(["ls-files", "--error-unmatch", "--", rel]), {
@@ -43014,10 +43344,10 @@ function writtenOnThisMachine(root, rel) {
43014
43344
  function isTelemetryProject() {
43015
43345
  for (const root of [repoRoot(), mainWorktreeRoot()]) {
43016
43346
  if (!root) continue;
43017
- const dir = (0, import_node_path64.join)(root, VERITY_DIR);
43347
+ const dir = (0, import_node_path65.join)(root, VERITY_DIR);
43018
43348
  if (isMachineStore(dir)) continue;
43019
43349
  for (const marker of PROJECT_MARKERS) {
43020
- if ((0, import_node_fs75.existsSync)((0, import_node_path64.join)(dir, marker)) && writtenOnThisMachine(root, `${VERITY_DIR}/${marker}`)) return true;
43350
+ if ((0, import_node_fs76.existsSync)((0, import_node_path65.join)(dir, marker)) && writtenOnThisMachine(root, `${VERITY_DIR}/${marker}`)) return true;
43021
43351
  }
43022
43352
  }
43023
43353
  return false;
@@ -43282,7 +43612,7 @@ function signInDetail(outcome) {
43282
43612
  }
43283
43613
  function formalDefault(previous, opts = {}) {
43284
43614
  const cfg = readProjectConfig();
43285
- if (previous?.init?.completed_at || opts.setUpBefore || cfg.formal !== void 0 || cfg.formal_refused) return false;
43615
+ if (previous?.init?.completed_at || opts.setUpBefore || cfg.formal !== void 0 || cfg.formal_refused || cfg.formal_off_refused) return false;
43286
43616
  writeProjectConfig({ formal: "push" });
43287
43617
  return true;
43288
43618
  }
@@ -43293,9 +43623,13 @@ function sayFormalOn(ui) {
43293
43623
  }
43294
43624
  function backgroundStep(ui, opts) {
43295
43625
  const { rail } = ui;
43296
- const formal = readProjectConfig().formal;
43297
- const said = opts.formalNew === true && formal === "push";
43298
- if (said) sayFormalOn(ui);
43626
+ const setting = formalSetting(readProjectConfig());
43627
+ const formal = setting.state;
43628
+ const said = formal === "push" && (opts.formalNew === true || setting.source === "default" && formalDefaultNoticeDue());
43629
+ if (said) {
43630
+ sayFormalOn(ui);
43631
+ recordFormalDefaultNotice();
43632
+ }
43299
43633
  const lean = shouldStartBackgroundInstall({ formal });
43300
43634
  const leanStarted = lean.start && startBackgroundInstall({ cli: opts.cli });
43301
43635
  const trivy = startTrivyPrewarm();
@@ -43382,8 +43716,8 @@ async function scaffoldStep(ui, given = {}) {
43382
43716
  rail.warn(".gitignore: could not write the Verity block");
43383
43717
  rail.line("Add it by hand: .verity/ holds copies of analyzed files, including any secret the gate flagged.");
43384
43718
  } else if (ignore === "conflict") {
43385
- rail.warn(".gitignore: git still ignores .verity/standard.yaml");
43386
- rail.line("A global or nested .gitignore covers it, so the Standard cannot be committed. Check: git check-ignore -v .verity/standard.yaml");
43719
+ rail.warn(".gitignore: git does not ignore the files under .verity/");
43720
+ rail.line("A nested .gitignore re-includes them, so copies of analyzed files, including any secret the gate flagged, can be committed. Check: git check-ignore -v .verity/.snapshot/x");
43387
43721
  } else if (ignore === "memory-tracked") {
43388
43722
  rail.warn("This project commits its knowledge graph");
43389
43723
  }
@@ -43398,15 +43732,25 @@ async function scaffoldStep(ui, given = {}) {
43398
43732
  rail.line("They show up in every diff. Stop with: verity memory untrack (or keep them on purpose: verity memory track)");
43399
43733
  }
43400
43734
  }
43735
+ if (committedStandardFile()) {
43736
+ if (given.untrackStandard) {
43737
+ const result = untrackStandard();
43738
+ if (result.ok) rail.line(`Untracked ${STANDARD_FILE} (staged). Commit to finish. Teammates get it back with: verity standard pull`);
43739
+ else rail.line(`Could not untrack ${STANDARD_FILE}. Run: verity standard untrack`, "warn");
43740
+ } else {
43741
+ rail.warn(`${STANDARD_FILE} is tracked in git`);
43742
+ rail.line("Verity keeps it on each machine now, and reviews read the Standard on the service. Stop tracking it with: verity standard untrack");
43743
+ }
43744
+ }
43401
43745
  const tracked = committedVerityState();
43402
43746
  if (tracked.length > 0) {
43403
43747
  if (given.untrackState || ui.asking && await askUntrackState(ui, tracked)) {
43404
43748
  const result = untrackVerityState();
43405
43749
  if (result === "untracked") rail.line("Untracked the Verity state files (staged). Commit to finish.");
43406
- else if (result === "failed") rail.line("Could not untrack them. Run: git rm -r --cached .verity && git add .verity/standard.yaml", "warn");
43750
+ else if (result === "failed") rail.line("Could not untrack them. Run: verity init --untrack-state", "warn");
43407
43751
  } else {
43408
43752
  rail.warn(`${tracked.length} Verity state file(s) are tracked in git`);
43409
- rail.line(`For example ${terminalText(tracked[0], 200)}. Fix: git rm -r --cached .verity && git add .verity/standard.yaml`);
43753
+ rail.line(`For example ${terminalText(tracked[0], 200)}. Fix: verity init --untrack-state`);
43410
43754
  }
43411
43755
  }
43412
43756
  retireCodacyConfig(ui);
@@ -43416,7 +43760,9 @@ async function scaffoldStep(ui, given = {}) {
43416
43760
  rail.line(`Could not update CLAUDE.md: ${terminalText(err2.message, 300)}`, "warn");
43417
43761
  }
43418
43762
  const cfg = readProjectConfig();
43419
- if (cfg.formal_refused) rail.line(`Formal verification: off \u2014 ${formalRefusalMessage(cfg.formal_refused)}`, "warn");
43763
+ if (cfg.formal_off_refused && formalSetting(cfg).state === "push") {
43764
+ rail.line(`Formal verification: on \u2014 ${formalOffRefusalMessage(cfg.formal_off_refused)}`, "warn");
43765
+ }
43420
43766
  rail.settle();
43421
43767
  }
43422
43768
  async function askUntrackState(ui, tracked) {
@@ -43454,9 +43800,9 @@ function retireCodacyConfig(ui) {
43454
43800
  }
43455
43801
 
43456
43802
  // src/commands/init/wiring.ts
43457
- var import_node_fs76 = require("node:fs");
43803
+ var import_node_fs77 = require("node:fs");
43458
43804
  var import_promises16 = require("node:fs/promises");
43459
- var import_node_path65 = require("node:path");
43805
+ var import_node_path66 = require("node:path");
43460
43806
  var DEFAULT_MOMENTS = ["stop", "pre-commit", "pre-push"];
43461
43807
  async function momentsFromInstalledHooks() {
43462
43808
  const status = await checkAllVerityHooks();
@@ -43529,12 +43875,12 @@ var SKILLS = [
43529
43875
  ];
43530
43876
  function resolveDataDir2() {
43531
43877
  const candidates2 = [
43532
- (0, import_node_path65.join)(__dirname, "..", "data"),
43533
- (0, import_node_path65.join)(__dirname, "..", "..", "data"),
43534
- (0, import_node_path65.join)(process.cwd(), "cli", "data")
43878
+ (0, import_node_path66.join)(__dirname, "..", "data"),
43879
+ (0, import_node_path66.join)(__dirname, "..", "..", "data"),
43880
+ (0, import_node_path66.join)(process.cwd(), "cli", "data")
43535
43881
  ];
43536
43882
  for (const candidate of candidates2) {
43537
- if ((0, import_node_fs76.existsSync)((0, import_node_path65.join)(candidate, "skills"))) return candidate;
43883
+ if ((0, import_node_fs77.existsSync)((0, import_node_path66.join)(candidate, "skills"))) return candidate;
43538
43884
  }
43539
43885
  throw new Error("Could not find Verity skill data. Reinstall: npm install -g @codacy/verity-cli");
43540
43886
  }
@@ -43542,9 +43888,9 @@ async function skillIsCurrent(src, dest) {
43542
43888
  const list2 = (dir) => {
43543
43889
  const out2 = [];
43544
43890
  const walk2 = (d, prefix) => {
43545
- for (const e of (0, import_node_fs76.readdirSync)(d, { withFileTypes: true })) {
43891
+ for (const e of (0, import_node_fs77.readdirSync)(d, { withFileTypes: true })) {
43546
43892
  const rel = prefix ? `${prefix}/${e.name}` : e.name;
43547
- if (e.isDirectory()) walk2((0, import_node_path65.join)(d, e.name), rel);
43893
+ if (e.isDirectory()) walk2((0, import_node_path66.join)(d, e.name), rel);
43548
43894
  else if (e.isFile()) out2.push(rel);
43549
43895
  }
43550
43896
  };
@@ -43555,7 +43901,7 @@ async function skillIsCurrent(src, dest) {
43555
43901
  const shipped = list2(src);
43556
43902
  if (JSON.stringify(shipped) !== JSON.stringify(list2(dest))) return false;
43557
43903
  for (const rel of shipped) {
43558
- if (await (0, import_promises16.readFile)((0, import_node_path65.join)(src, rel), "utf-8") !== await (0, import_promises16.readFile)((0, import_node_path65.join)(dest, rel), "utf-8")) return false;
43904
+ if (await (0, import_promises16.readFile)((0, import_node_path66.join)(src, rel), "utf-8") !== await (0, import_promises16.readFile)((0, import_node_path66.join)(dest, rel), "utf-8")) return false;
43559
43905
  }
43560
43906
  return true;
43561
43907
  } catch {
@@ -43563,16 +43909,16 @@ async function skillIsCurrent(src, dest) {
43563
43909
  }
43564
43910
  }
43565
43911
  async function installSkills(force) {
43566
- const source2 = (0, import_node_path65.join)(resolveDataDir2(), "skills");
43912
+ const source2 = (0, import_node_path66.join)(resolveDataDir2(), "skills");
43567
43913
  const missing = [];
43568
43914
  for (const skill of SKILLS) {
43569
- const src = (0, import_node_path65.join)(source2, skill);
43570
- const dest = (0, import_node_path65.join)(".claude/skills", skill);
43571
- if (!(0, import_node_fs76.existsSync)(src)) {
43915
+ const src = (0, import_node_path66.join)(source2, skill);
43916
+ const dest = (0, import_node_path66.join)(".claude/skills", skill);
43917
+ if (!(0, import_node_fs77.existsSync)(src)) {
43572
43918
  missing.push(skill);
43573
43919
  continue;
43574
43920
  }
43575
- if ((0, import_node_fs76.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
43921
+ if ((0, import_node_fs77.existsSync)(dest) && !force && await skillIsCurrent(src, dest)) continue;
43576
43922
  await (0, import_promises16.mkdir)(dest, { recursive: true });
43577
43923
  await (0, import_promises16.cp)(src, dest, { recursive: true, force: true });
43578
43924
  }
@@ -43612,8 +43958,8 @@ async function hooksStep(ui, ctx) {
43612
43958
  }
43613
43959
 
43614
43960
  // src/commands/init/standard.ts
43615
- var import_node_fs77 = require("node:fs");
43616
- var import_node_path66 = require("node:path");
43961
+ var import_node_fs78 = require("node:fs");
43962
+ var import_node_path67 = require("node:path");
43617
43963
  var import_yaml10 = __toESM2(require_dist());
43618
43964
 
43619
43965
  // src/lib/catalogue-client.ts
@@ -43686,10 +44032,8 @@ async function writeTeamStandard(team, opts) {
43686
44032
  serviceUrl: url.data,
43687
44033
  projectId: await projectLabel(token.data.token, url.data, opts.globals.verbose)
43688
44034
  });
43689
- if (opts.previous?.declinedStandardVersion != null) {
43690
- await writeSetupState({ declinedStandardVersion: void 0 }).catch(() => {
43691
- });
43692
- }
44035
+ await writeSetupState({ standardVersion: found.standard.version, declinedStandardVersion: void 0 }).catch(() => {
44036
+ });
43693
44037
  return { status: "adopted", version: found.standard.version, summary: describeRemote(found), notes: outcome.notes };
43694
44038
  } catch (err2) {
43695
44039
  return { status: "write-failed", reason: terminalText(err2.message, 300) };
@@ -43710,6 +44054,17 @@ async function askTeamChoice(ui, team) {
43710
44054
  rail.collapse();
43711
44055
  return k === "n" ? "new" : "use";
43712
44056
  }
44057
+ async function askKeepOrPull(ui, team) {
44058
+ const { rail, screen } = ui;
44059
+ rail.active(`${STANDARD_FILE} differs from your team's Standard`);
44060
+ rail.line(`On the service: ${describeRemote(team.found)}. Reviews use that one.`);
44061
+ rail.line(`Use it here too (a copy of your file is kept), or keep your file and share it over the team's later: verity standard push`);
44062
+ rail.gap();
44063
+ rail.keys([["Enter", "use the team's"], ["k", "keep yours"]]);
44064
+ const k = await screen.key(["enter", "k"], "enter", { freshMs: 300 });
44065
+ rail.collapse();
44066
+ return k === "k" ? "keep" : "use";
44067
+ }
43713
44068
  function sayAdopted(ui, team) {
43714
44069
  ui.rail.done(`Using your team's Standard v${team.version}`);
43715
44070
  ui.rail.line(terminalText(team.summary, 300));
@@ -43778,26 +44133,26 @@ async function deriveFromCatalogue(ui, globals, plan, expectedLatest) {
43778
44133
  return { ok: false, version: null };
43779
44134
  }
43780
44135
  }
43781
- function localStandardProblem() {
44136
+ function localStandard() {
43782
44137
  const raw = readRepoFile(STANDARD_FILE);
43783
- if (raw === null) return `${STANDARD_FILE} is a link, not a regular file, or larger than 512 KB.`;
44138
+ if (raw === null) return { ok: false, problem: `${STANDARD_FILE} is a link, not a regular file, or larger than 512 KB.` };
43784
44139
  let parsed;
43785
44140
  try {
43786
44141
  parsed = (0, import_yaml10.parse)(raw);
43787
44142
  } catch (err2) {
43788
- return `${STANDARD_FILE} is not valid YAML: ${terminalText(err2 instanceof Error ? err2.message : String(err2), 200)}`;
44143
+ return { ok: false, problem: `${STANDARD_FILE} is not valid YAML: ${terminalText(err2 instanceof Error ? err2.message : String(err2), 200)}` };
43789
44144
  }
43790
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return `${STANDARD_FILE} holds no Standard.`;
44145
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return { ok: false, problem: `${STANDARD_FILE} holds no Standard.` };
43791
44146
  const valid = validateStandardContent(parsed);
43792
- if (!valid.ok) return `${STANDARD_FILE} is not a Standard the service accepts: ${terminalText(valid.error, 200)}`;
43793
- return null;
44147
+ if (!valid.ok) return { ok: false, problem: `${STANDARD_FILE} is not a Standard the service accepts: ${terminalText(valid.error, 200)}` };
44148
+ return { ok: true, content: parsed };
43794
44149
  }
43795
44150
  function removeLocalStandard() {
43796
44151
  const path = projectPath(STANDARD_FILE);
43797
44152
  try {
43798
- const folder = (0, import_node_fs77.lstatSync)((0, import_node_path66.dirname)(path));
44153
+ const folder = (0, import_node_fs78.lstatSync)((0, import_node_path67.dirname)(path));
43799
44154
  if (!folder.isDirectory() || folder.isSymbolicLink()) return;
43800
- if ((0, import_node_fs77.lstatSync)(path).isFile()) (0, import_node_fs77.unlinkSync)(path);
44155
+ if ((0, import_node_fs78.lstatSync)(path).isFile()) (0, import_node_fs78.unlinkSync)(path);
43801
44156
  } catch {
43802
44157
  }
43803
44158
  }
@@ -43811,7 +44166,7 @@ function setLocalStandardAside(now = /* @__PURE__ */ new Date(), remove = remove
43811
44166
  return { ok: false, message: `Could not keep a copy of ${STANDARD_FILE} (${terminalText(err2 instanceof Error ? err2.message : String(err2), 200)}), so it was not set aside.` };
43812
44167
  }
43813
44168
  remove();
43814
- if ((0, import_node_fs77.existsSync)(projectPath(STANDARD_FILE))) return { ok: false, message: `Could not remove ${STANDARD_FILE} after copying it to ${path}.` };
44169
+ if ((0, import_node_fs78.existsSync)(projectPath(STANDARD_FILE))) return { ok: false, message: `Could not remove ${STANDARD_FILE} after copying it to ${path}.` };
43815
44170
  return { ok: true, path };
43816
44171
  }
43817
44172
  async function standardStep(ui, ctx) {
@@ -43873,7 +44228,7 @@ async function standardStep(ui, ctx) {
43873
44228
  rail.line(team2.status === "none" ? `There is no Standard on the service either, so reviews have no rules. Fix ${STANDARD_FILE}, or delete it, then run verity init again.` : team2.status === "unavailable" ? `The Verity service did not answer (${terminalText(team2.reason, 200)}), so no Standard is confirmed. Fix ${STANDARD_FILE}, then run verity init again.` : team2.status === "denied" && refusalOutcome(team2.code) === "denied" ? `Verity has no access to this repository (${terminalText(team2.reason, 200)}). Fix ${STANDARD_FILE}, give the Verity GitHub App access, then run verity login and verity init.` : team2.status === "denied" ? `The Verity service did not accept your sign-in (${terminalText(team2.reason, 200)}). Fix ${STANDARD_FILE}, then run verity login and verity init.` : `No Standard on the service was looked up. Fix ${STANDARD_FILE}, or sign in, then run verity init again.`);
43874
44229
  return end({ source: "none", version: null, profile: notAsked });
43875
44230
  };
43876
- if (ctx.standard === "new" && (0, import_node_fs77.lstatSync)(projectPath(STANDARD_FILE), { throwIfNoEntry: false })) {
44231
+ if (ctx.standard === "new" && (0, import_node_fs78.lstatSync)(projectPath(STANDARD_FILE), { throwIfNoEntry: false })) {
43877
44232
  const aside = setLocalStandardAside();
43878
44233
  if (!aside.ok) {
43879
44234
  rail.warn("Could not set your Standard aside for a new one");
@@ -43883,26 +44238,77 @@ async function standardStep(ui, ctx) {
43883
44238
  }
43884
44239
  rail.done(`Set your Standard aside ${s.gray}\xB7 a copy is in ${aside.path}${s.reset}`);
43885
44240
  }
43886
- if ((0, import_node_fs77.existsSync)(projectPath(STANDARD_FILE))) {
43887
- const problem = localStandardProblem();
43888
- if (problem !== null) return unusableHere(problem);
43889
- const sync = await pushLocalStandardIfMissing(ctx.globals);
43890
- rail.done("Kept your Standard");
43891
- rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is. Edit it there, then share the change: verity standard push`);
44241
+ const keptHints = async () => {
43892
44242
  if (!usesReviewPacks()) {
43893
44243
  rail.line("It does not use review packs. To move it to packs and keep your rules: verity standard push. To build a new one: verity init --standard new", "muted");
43894
44244
  }
43895
- if (sync.status === "pushed") {
43896
- rail.line(sync.version !== null ? `It was only on this machine. It is shared now as v${sync.version}.` : "It is only on this machine:", sync.version !== null ? "muted" : "warn");
43897
- for (const line2 of sync.lines) if (!line2.startsWith("Standard pushed")) rail.line(terminalText(line2, 600), "warn");
44245
+ if (!hasProjectRules() && await rulesOn(ctx.globals)) {
44246
+ rail.line("It has no project rules yet. Add them from a repo profile: verity standard generate");
44247
+ }
44248
+ };
44249
+ const keptHere = async (local) => {
44250
+ const team2 = await lookUpTeamStandard(ctx);
44251
+ if (team2.status === "found" || team2.status === "declined-before") {
44252
+ if (sameStandard(local, team2.found.standard.content)) {
44253
+ rail.done(`Kept your Standard ${s.gray}\xB7 it is the team's v${team2.version}${s.reset}`);
44254
+ rail.line(`Edit it in ${STANDARD_FILE}, then share the change: verity standard push`);
44255
+ await writeSetupState({ standardVersion: team2.version }).catch(() => {
44256
+ });
44257
+ await keptHints();
44258
+ return end({ source: "kept", version: team2.version, profile: notAsked });
44259
+ }
44260
+ const choice = ctx.standard === "use" ? "use" : team2.status === "declined-before" ? "keep" : ui.asking ? await askKeepOrPull(ui, team2) : "use";
44261
+ if (choice === "use") {
44262
+ const pulled = await pullStandard(ctx.globals);
44263
+ if (pulled.status === "pulled" || pulled.status === "same") {
44264
+ rail.done(`Using your team's Standard v${pulled.version}`);
44265
+ rail.line(terminalText(describeRemote(team2.found), 300));
44266
+ if (pulled.status === "pulled" && pulled.backup) rail.line(`Your file is kept as ${pulled.backup}`, "muted");
44267
+ rail.line(`Edit it in ${STANDARD_FILE}, then share the change: verity standard push`);
44268
+ if (pulled.status === "pulled") for (const note of pulled.notes) rail.line(terminalText(note, 300), "warn");
44269
+ return end({ source: "adopted", version: pulled.version, profile: notAsked });
44270
+ }
44271
+ rail.warn("Could not write the team's Standard here");
44272
+ rail.line(pulled.status === "failed" ? terminalText(pulled.message, 300) : `${STANDARD_FILE} changed while this ran.`);
44273
+ rail.line(`Reviews use the team's Standard v${team2.version} on the service. To write it here: verity standard pull`);
44274
+ return end({ source: "kept", version: team2.version, profile: notAsked });
44275
+ }
44276
+ rail.done(`Kept your Standard ${s.gray}\xB7 it differs from the team's v${team2.version}${s.reset}`);
44277
+ rail.line(team2.status === "declined-before" ? `This project turned down the team's Standard v${team2.version} before, so this run keeps yours. Reviews use the team's.` : "Reviews use the team's Standard on the service.");
44278
+ rail.line(`To share yours over the team's: verity standard push. To use the team's here: verity standard pull`);
44279
+ await writeSetupState({ standardVersion: team2.version }).catch(() => {
44280
+ });
44281
+ await keptHints();
44282
+ return end({ source: "kept", version: team2.version, profile: notAsked });
44283
+ }
44284
+ rail.done("Kept your Standard");
44285
+ rail.line(`${STANDARD_FILE} is already here, so this run leaves it as it is. Edit it there, then share the change: verity standard push`);
44286
+ let version = null;
44287
+ if (team2.status === "none") {
44288
+ const pushed = await pushStandard(ctx.globals, { expectedLatest: null });
44289
+ if (pushed.conflict) {
44290
+ rail.line("Someone shared a Standard for this repository while this ran. Reviews use theirs; to compare with yours, run verity init again, or verity standard pull.", "warn");
44291
+ } else if (pushed.standardVersion !== null) {
44292
+ version = pushed.standardVersion;
44293
+ rail.line(`It was only on this machine. It is shared now as v${version}.`, "muted");
44294
+ } else {
44295
+ rail.line("It is only on this machine:", "warn");
44296
+ }
44297
+ for (const line2 of pushed.lines) if (!line2.startsWith("Standard pushed")) rail.line(terminalText(line2, 600), "warn");
43898
44298
  }
43899
- if (sync.status === "skipped" && sync.reason === "unavailable") {
44299
+ if (team2.status === "unavailable") {
43900
44300
  rail.line(`It could not be shared: the Verity service did not answer. It is shared at the first review once the service answers, or now with: verity standard push`, "warn");
43901
44301
  }
43902
- if (!hasProjectRules() && await rulesOn(ctx.globals)) {
43903
- rail.line("It has no project rules yet. Add them from a repo profile: verity standard generate");
44302
+ if (team2.status === "denied") {
44303
+ rail.line(`It could not be compared with the team's: ${terminalText(team2.reason, 300)}`, "warn");
43904
44304
  }
43905
- return end({ source: "kept", version: sync.status === "pushed" ? sync.version : null, profile: notAsked });
44305
+ await keptHints();
44306
+ return end({ source: "kept", version, profile: notAsked });
44307
+ };
44308
+ if ((0, import_node_fs78.existsSync)(projectPath(STANDARD_FILE))) {
44309
+ const local = localStandard();
44310
+ if (!local.ok) return unusableHere(local.problem);
44311
+ return keptHere(local.content);
43906
44312
  }
43907
44313
  const team = await lookUpTeamStandard(ctx);
43908
44314
  let replaces;
@@ -44025,8 +44431,8 @@ function sayChanged(ui, before) {
44025
44431
  }
44026
44432
 
44027
44433
  // src/commands/migrate.ts
44028
- var import_node_fs78 = require("node:fs");
44029
- var import_node_path67 = require("node:path");
44434
+ var import_node_fs79 = require("node:fs");
44435
+ var import_node_path68 = require("node:path");
44030
44436
  var import_node_child_process22 = require("node:child_process");
44031
44437
  var LEGACY_NPM_PACKAGE = "@codacy/gate-cli";
44032
44438
  function defaultNpmRemover(pkg) {
@@ -44090,12 +44496,12 @@ async function runMigration(opts = {}) {
44090
44496
  return { actions, migrated: actions.length > 0 };
44091
44497
  }
44092
44498
  function migrateProjectDir(root, actions) {
44093
- const gateDir = (0, import_node_path67.join)(root, ".gate");
44094
- const verityDir = (0, import_node_path67.join)(root, ".verity");
44095
- if ((0, import_node_fs78.existsSync)(gateDir) && !(0, import_node_fs78.existsSync)(verityDir)) {
44499
+ const gateDir = (0, import_node_path68.join)(root, ".gate");
44500
+ const verityDir = (0, import_node_path68.join)(root, ".verity");
44501
+ if ((0, import_node_fs79.existsSync)(gateDir) && !(0, import_node_fs79.existsSync)(verityDir)) {
44096
44502
  return migrateProjectDirRename(root, gateDir, verityDir, actions);
44097
44503
  }
44098
- if ((0, import_node_fs78.existsSync)(gateDir) && (0, import_node_fs78.existsSync)(verityDir)) {
44504
+ if ((0, import_node_fs79.existsSync)(gateDir) && (0, import_node_fs79.existsSync)(verityDir)) {
44099
44505
  return migrateProjectDirCarry(gateDir, verityDir, actions);
44100
44506
  }
44101
44507
  return false;
@@ -44116,13 +44522,13 @@ function migrateProjectDirRename(root, gateDir, verityDir, actions) {
44116
44522
  }
44117
44523
  }
44118
44524
  if (moved) {
44119
- if ((0, import_node_fs78.existsSync)(gateDir)) {
44525
+ if ((0, import_node_fs79.existsSync)(gateDir)) {
44120
44526
  const carried = carryLegacyContents(gateDir, verityDir);
44121
44527
  if (carried > 0) {
44122
44528
  actions.push(`Carried ${carried} untracked legacy file(s) from .gate/ into .verity/`);
44123
44529
  }
44124
44530
  try {
44125
- (0, import_node_fs78.rmSync)(gateDir, { recursive: true, force: true });
44531
+ (0, import_node_fs79.rmSync)(gateDir, { recursive: true, force: true });
44126
44532
  } catch {
44127
44533
  }
44128
44534
  }
@@ -44138,18 +44544,18 @@ function migrateProjectDirCarry(gateDir, verityDir, actions) {
44138
44544
  actions.push(`Carried ${carried} legacy file(s) from .gate/ into .verity/`);
44139
44545
  }
44140
44546
  try {
44141
- (0, import_node_fs78.rmSync)(gateDir, { recursive: true, force: true });
44547
+ (0, import_node_fs79.rmSync)(gateDir, { recursive: true, force: true });
44142
44548
  } catch {
44143
44549
  }
44144
44550
  return carried > 0;
44145
44551
  }
44146
44552
  function migrateGlobalCredentials(home, actions) {
44147
44553
  if (!home) return;
44148
- const gateCreds = (0, import_node_path67.join)(home, ".gate", "credentials");
44149
- const verityCreds = (0, import_node_path67.join)(home, ".verity", "credentials");
44150
- if (!(0, import_node_fs78.existsSync)(gateCreds)) return;
44151
- if (!(0, import_node_fs78.existsSync)(verityCreds)) {
44152
- (0, import_node_fs78.mkdirSync)((0, import_node_path67.join)(home, ".verity"), { recursive: true });
44554
+ const gateCreds = (0, import_node_path68.join)(home, ".gate", "credentials");
44555
+ const verityCreds = (0, import_node_path68.join)(home, ".verity", "credentials");
44556
+ if (!(0, import_node_fs79.existsSync)(gateCreds)) return;
44557
+ if (!(0, import_node_fs79.existsSync)(verityCreds)) {
44558
+ (0, import_node_fs79.mkdirSync)((0, import_node_path68.join)(home, ".verity"), { recursive: true });
44153
44559
  moveFile(gateCreds, verityCreds);
44154
44560
  actions.push("Moved ~/.gate/credentials \u2192 ~/.verity/credentials");
44155
44561
  return;
@@ -44171,8 +44577,8 @@ async function migrateLegacyHooks(root, actions) {
44171
44577
  }
44172
44578
  }
44173
44579
  async function migrateClaudeMd(root, actions) {
44174
- const claudeMd = (0, import_node_path67.join)(root, "CLAUDE.md");
44175
- const hadLegacyBlock = (0, import_node_fs78.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
44580
+ const claudeMd = (0, import_node_path68.join)(root, "CLAUDE.md");
44581
+ const hadLegacyBlock = (0, import_node_fs79.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd));
44176
44582
  if (!hadLegacyBlock) return;
44177
44583
  try {
44178
44584
  await ensureClaudeMdPointer(root);
@@ -44182,9 +44588,9 @@ async function migrateClaudeMd(root, actions) {
44182
44588
  }
44183
44589
  }
44184
44590
  function migrateStandardFile(root, actions) {
44185
- const gateMd = (0, import_node_path67.join)(root, "GATE.md");
44186
- const verityMd = (0, import_node_path67.join)(root, "VERITY.md");
44187
- if (!(0, import_node_fs78.existsSync)(gateMd) || (0, import_node_fs78.existsSync)(verityMd)) return;
44591
+ const gateMd = (0, import_node_path68.join)(root, "GATE.md");
44592
+ const verityMd = (0, import_node_path68.join)(root, "VERITY.md");
44593
+ if (!(0, import_node_fs79.existsSync)(gateMd) || (0, import_node_fs79.existsSync)(verityMd)) return;
44188
44594
  let moved = false;
44189
44595
  if (isGitRepo(root) && isGitTracked(root, "GATE.md")) {
44190
44596
  try {
@@ -44196,7 +44602,7 @@ function migrateStandardFile(root, actions) {
44196
44602
  if (!moved) moveFile(gateMd, verityMd);
44197
44603
  const content = readFileSyncSafe(verityMd);
44198
44604
  const refreshed = content.split("GATE.md").join("VERITY.md");
44199
- if (refreshed !== content) (0, import_node_fs78.writeFileSync)(verityMd, refreshed);
44605
+ if (refreshed !== content) (0, import_node_fs79.writeFileSync)(verityMd, refreshed);
44200
44606
  actions.push("Renamed GATE.md \u2192 VERITY.md");
44201
44607
  }
44202
44608
  async function migrateLocalTelemetry(root, actions) {
@@ -44240,14 +44646,14 @@ function mergeGlobalCredentials(gateCreds, verityCreds) {
44240
44646
  }
44241
44647
  if (toAppend.length > 0) {
44242
44648
  const sep7 = verityContent.endsWith("\n") || verityContent === "" ? "" : "\n";
44243
- (0, import_node_fs78.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
44649
+ (0, import_node_fs79.writeFileSync)(verityCreds, verityContent + sep7 + toAppend.join("\n") + "\n");
44244
44650
  }
44245
- (0, import_node_fs78.rmSync)(gateCreds, { force: true });
44651
+ (0, import_node_fs79.rmSync)(gateCreds, { force: true });
44246
44652
  return toAppend.length;
44247
44653
  }
44248
44654
  function readFileSyncSafe(path) {
44249
44655
  try {
44250
- return (0, import_node_fs78.readFileSync)(path, "utf-8");
44656
+ return (0, import_node_fs79.readFileSync)(path, "utf-8");
44251
44657
  } catch {
44252
44658
  return "";
44253
44659
  }
@@ -44262,20 +44668,20 @@ function hasStagedChanges(root) {
44262
44668
  }
44263
44669
  function moveDir(from, to) {
44264
44670
  try {
44265
- (0, import_node_fs78.renameSync)(from, to);
44671
+ (0, import_node_fs79.renameSync)(from, to);
44266
44672
  } catch (err2) {
44267
44673
  if (err2.code !== "EXDEV") throw err2;
44268
- (0, import_node_fs78.cpSync)(from, to, { recursive: true });
44269
- (0, import_node_fs78.rmSync)(from, { recursive: true, force: true });
44674
+ (0, import_node_fs79.cpSync)(from, to, { recursive: true });
44675
+ (0, import_node_fs79.rmSync)(from, { recursive: true, force: true });
44270
44676
  }
44271
44677
  }
44272
44678
  function moveFile(from, to) {
44273
44679
  try {
44274
- (0, import_node_fs78.renameSync)(from, to);
44680
+ (0, import_node_fs79.renameSync)(from, to);
44275
44681
  } catch (err2) {
44276
44682
  if (err2.code !== "EXDEV") throw err2;
44277
- (0, import_node_fs78.cpSync)(from, to);
44278
- (0, import_node_fs78.rmSync)(from, { force: true });
44683
+ (0, import_node_fs79.cpSync)(from, to);
44684
+ (0, import_node_fs79.rmSync)(from, { force: true });
44279
44685
  }
44280
44686
  }
44281
44687
  function carryLegacyContents(gateDir, verityDir) {
@@ -44284,12 +44690,12 @@ function carryLegacyContents(gateDir, verityDir) {
44284
44690
  const walk2 = (relDir) => {
44285
44691
  let entries;
44286
44692
  try {
44287
- entries = (0, import_node_fs78.readdirSync)((0, import_node_path67.join)(gateDir, relDir), { withFileTypes: true });
44693
+ entries = (0, import_node_fs79.readdirSync)((0, import_node_path68.join)(gateDir, relDir), { withFileTypes: true });
44288
44694
  } catch {
44289
44695
  return;
44290
44696
  }
44291
44697
  for (const entry of entries) {
44292
- const rel = relDir ? (0, import_node_path67.join)(relDir, entry.name) : entry.name;
44698
+ const rel = relDir ? (0, import_node_path68.join)(relDir, entry.name) : entry.name;
44293
44699
  if (entry.isSymbolicLink()) continue;
44294
44700
  if (entry.isDirectory()) {
44295
44701
  walk2(rel);
@@ -44299,7 +44705,7 @@ function carryLegacyContents(gateDir, verityDir) {
44299
44705
  const opened = openRegularInRoot(gateDir, rel);
44300
44706
  if (!opened.ok) continue;
44301
44707
  try {
44302
- (0, import_node_fs78.writeFileSync)((0, import_node_path67.join)(verityDir, rel), readOpened(opened.fd, opened.size), { flag: "wx", mode: 384 });
44708
+ (0, import_node_fs79.writeFileSync)((0, import_node_path68.join)(verityDir, rel), readOpened(opened.fd, opened.size), { flag: "wx", mode: 384 });
44303
44709
  copied++;
44304
44710
  } catch {
44305
44711
  }
@@ -44309,19 +44715,19 @@ function carryLegacyContents(gateDir, verityDir) {
44309
44715
  return copied;
44310
44716
  }
44311
44717
  function hasLegacyStandard(root = repoRoot()) {
44312
- if (!isRealDir((0, import_node_path67.join)(root, ".gate"))) return false;
44718
+ if (!isRealDir((0, import_node_path68.join)(root, ".gate"))) return false;
44313
44719
  try {
44314
- if (!(0, import_node_fs78.lstatSync)((0, import_node_path67.join)(root, ".gate", "standard.yaml")).isFile()) return false;
44720
+ if (!(0, import_node_fs79.lstatSync)((0, import_node_path68.join)(root, ".gate", "standard.yaml")).isFile()) return false;
44315
44721
  } catch {
44316
44722
  return false;
44317
44723
  }
44318
- const verityDir = (0, import_node_path67.join)(root, ".verity");
44724
+ const verityDir = (0, import_node_path68.join)(root, ".verity");
44319
44725
  if (pathEntryExists(verityDir) && !isRealDir(verityDir)) return false;
44320
- return !pathEntryExists((0, import_node_path67.join)(verityDir, "standard.yaml"));
44726
+ return !pathEntryExists((0, import_node_path68.join)(verityDir, "standard.yaml"));
44321
44727
  }
44322
44728
  function pathEntryExists(path) {
44323
44729
  try {
44324
- (0, import_node_fs78.lstatSync)(path);
44730
+ (0, import_node_fs79.lstatSync)(path);
44325
44731
  return true;
44326
44732
  } catch {
44327
44733
  return false;
@@ -44329,7 +44735,7 @@ function pathEntryExists(path) {
44329
44735
  }
44330
44736
  function isRealDir(path) {
44331
44737
  try {
44332
- const st = (0, import_node_fs78.lstatSync)(path);
44738
+ const st = (0, import_node_fs79.lstatSync)(path);
44333
44739
  return st.isDirectory() && !st.isSymbolicLink();
44334
44740
  } catch {
44335
44741
  return false;
@@ -44338,9 +44744,9 @@ function isRealDir(path) {
44338
44744
  function ensureRealDirs(root, relDir) {
44339
44745
  let current = root;
44340
44746
  for (const segment of relDir.split(/[\\/]/).filter(Boolean)) {
44341
- current = (0, import_node_path67.join)(current, segment);
44747
+ current = (0, import_node_path68.join)(current, segment);
44342
44748
  try {
44343
- (0, import_node_fs78.mkdirSync)(current);
44749
+ (0, import_node_fs79.mkdirSync)(current);
44344
44750
  } catch (e) {
44345
44751
  if (e.code !== "EEXIST") return false;
44346
44752
  }
@@ -44349,23 +44755,23 @@ function ensureRealDirs(root, relDir) {
44349
44755
  return true;
44350
44756
  }
44351
44757
  async function needsMigration(root = repoRoot()) {
44352
- const gateDir = (0, import_node_path67.join)(root, ".gate");
44353
- const verityDir = (0, import_node_path67.join)(root, ".verity");
44354
- if ((0, import_node_fs78.existsSync)(gateDir) && !(0, import_node_fs78.existsSync)(verityDir)) return true;
44355
- if ((0, import_node_fs78.existsSync)(gateDir) && (0, import_node_fs78.existsSync)(verityDir)) {
44356
- if ((0, import_node_fs78.existsSync)((0, import_node_path67.join)(gateDir, "credentials")) && !(0, import_node_fs78.existsSync)((0, import_node_path67.join)(verityDir, "credentials"))) {
44758
+ const gateDir = (0, import_node_path68.join)(root, ".gate");
44759
+ const verityDir = (0, import_node_path68.join)(root, ".verity");
44760
+ if ((0, import_node_fs79.existsSync)(gateDir) && !(0, import_node_fs79.existsSync)(verityDir)) return true;
44761
+ if ((0, import_node_fs79.existsSync)(gateDir) && (0, import_node_fs79.existsSync)(verityDir)) {
44762
+ if ((0, import_node_fs79.existsSync)((0, import_node_path68.join)(gateDir, "credentials")) && !(0, import_node_fs79.existsSync)((0, import_node_path68.join)(verityDir, "credentials"))) {
44357
44763
  return true;
44358
44764
  }
44359
- if ((0, import_node_fs78.existsSync)((0, import_node_path67.join)(gateDir, "memory")) && !(0, import_node_fs78.existsSync)((0, import_node_path67.join)(verityDir, "memory"))) {
44765
+ if ((0, import_node_fs79.existsSync)((0, import_node_path68.join)(gateDir, "memory")) && !(0, import_node_fs79.existsSync)((0, import_node_path68.join)(verityDir, "memory"))) {
44360
44766
  return true;
44361
44767
  }
44362
44768
  if (hasLegacyStandard(root)) return true;
44363
44769
  }
44364
- const claudeMd = (0, import_node_path67.join)(root, "CLAUDE.md");
44365
- if ((0, import_node_fs78.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
44770
+ const claudeMd = (0, import_node_path68.join)(root, "CLAUDE.md");
44771
+ if ((0, import_node_fs79.existsSync)(claudeMd) && hasLegacyMemoryBlock(readFileSyncSafe(claudeMd))) {
44366
44772
  return true;
44367
44773
  }
44368
- if ((0, import_node_fs78.existsSync)((0, import_node_path67.join)(root, "GATE.md")) && !(0, import_node_fs78.existsSync)((0, import_node_path67.join)(root, "VERITY.md"))) {
44774
+ if ((0, import_node_fs79.existsSync)((0, import_node_path68.join)(root, "GATE.md")) && !(0, import_node_fs79.existsSync)((0, import_node_path68.join)(root, "VERITY.md"))) {
44369
44775
  return true;
44370
44776
  }
44371
44777
  if (await hasLegacyHooksAt(root)) return true;
@@ -44605,7 +45011,7 @@ async function recordAnswers(ui, momentIds, telemetry, completed) {
44605
45011
  moments: momentIds,
44606
45012
  ...telemetry ? { telemetry } : {},
44607
45013
  // A failed run also clears an earlier stamp: "done" describes this checkout now.
44608
- init: completed ? { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.3" : "dev" } : void 0
45014
+ init: completed ? { completed_at: (/* @__PURE__ */ new Date()).toISOString(), cli_version: true ? "0.35.5" : "dev" } : void 0
44609
45015
  });
44610
45016
  } catch (err2) {
44611
45017
  ui.rail.line(`Could not record the setup answers: ${terminalText(err2.message, 200)}`, "warn");
@@ -44658,7 +45064,7 @@ async function runInit(program2, opts) {
44658
45064
  const defaultsOnly = (opts.yes ?? false) || !interactive();
44659
45065
  const { givenMoments, givenTelemetry, profileAnswer, standardAnswer } = parseInitFlags(opts);
44660
45066
  const projectMarkers = [".git", "package.json", "pyproject.toml", "go.mod", "Cargo.toml", "Gemfile", "pom.xml", "build.gradle"];
44661
- if (!projectMarkers.some((m) => (0, import_node_fs79.existsSync)(m))) {
45067
+ if (!projectMarkers.some((m) => (0, import_node_fs80.existsSync)(m))) {
44662
45068
  printError('No project detected in the current directory. Run "verity init" from your project root.');
44663
45069
  process.exit(1);
44664
45070
  }
@@ -44684,12 +45090,12 @@ async function runInit(program2, opts) {
44684
45090
  process.exit(1);
44685
45091
  }
44686
45092
  trace.mark("prerequisites", "ok");
44687
- await scaffoldStep(ui, { untrackMemory: opts.untrackMemory === true, untrackState: opts.untrackState === true });
45093
+ await scaffoldStep(ui, { untrackMemory: opts.untrackMemory === true, untrackStandard: opts.untrackStandard === true, untrackState: opts.untrackState === true });
44688
45094
  trace.mark("scaffold", "ok");
44689
- await (0, import_promises17.mkdir)((0, import_node_path68.join)((0, import_node_os15.homedir)(), ".verity"), { recursive: true });
45095
+ await (0, import_promises17.mkdir)((0, import_node_path69.join)((0, import_node_os15.homedir)(), ".verity"), { recursive: true });
44690
45096
  await repoStep(ui, detectProject());
44691
45097
  const previous = await readSetupState();
44692
- const setUpBefore = (0, import_node_fs79.existsSync)(projectPath(STANDARD_FILE));
45098
+ const setUpBefore = (0, import_node_fs80.existsSync)(projectPath(STANDARD_FILE));
44693
45099
  const answers = await askSetupQuestions(defaultsOnly, previous, { moments: givenMoments, telemetry: givenTelemetry });
44694
45100
  trace.mark("questions", "ok", givenMoments || givenTelemetry !== void 0 || profileAnswer || standardAnswer ? "asked" : "defaults");
44695
45101
  const plan = planMoments(answers.moments, { given: givenMoments !== void 0, defaultsOnly });
@@ -44735,11 +45141,11 @@ async function runInit(program2, opts) {
44735
45141
  }
44736
45142
  }
44737
45143
  function registerInitCommand(program2) {
44738
- program2.command("init").alias("setup").description("Set up Verity in this project: sign in, telemetry, and the Standard; every other choice is a stated default").option("--force", "Reinstall the skills even when they are up to date (hooks are always reconciled)").option("-y, --yes", "Ask nothing: skip sign-in unless already signed in, and send the profile only with --profile send").option("--profile <send|skip>", "Answer the repo-profile question without asking (the plugin asks in the session)").option("--standard <use|new>", "When the team already has a Standard: use it, or create a new one (the plugin asks in the session; unattended runs use it)").option("--no-setup", "Ignored: init no longer hands off to /verity-setup (kept for old scripts)").option("--plugin-mode", "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has").option("--no-plugin", "Ignore any Claude Code plugin here and wire this project's own skills and hooks").option("--no-adopt", "Don't use this repository's Standard from the service; build a new one").option("--moments <list>", 'Set when Verity reviews without the default: stop,pre-commit,pre-push \u2014 or "none"').option("--intensity <level>", "Ignored: static analysis is Trivy only (kept so older plugin skills keep working)").option("--telemetry <on|off>", "Answer the telemetry question without asking: send cost & usage from this machine, or not").option("--untrack-memory", "Stop tracking the knowledge graph in git (init does not ask)").option("--untrack-state", "Untrack committed Verity state without asking (a terminal run asks when it finds some)").action((opts) => runInit(program2, opts));
45144
+ program2.command("init").alias("setup").description("Set up Verity in this project: sign in, telemetry, and the Standard; every other choice is a stated default").option("--force", "Reinstall the skills even when they are up to date (hooks are always reconciled)").option("-y, --yes", "Ask nothing: skip sign-in unless already signed in, and send the profile only with --profile send").option("--profile <send|skip>", "Answer the repo-profile question without asking (the plugin asks in the session)").option("--standard <use|new>", "When the team already has a Standard: use it, or create a new one (the plugin asks in the session; unattended runs use it)").option("--no-setup", "Ignored: init no longer hands off to /verity-setup (kept for old scripts)").option("--plugin-mode", "The Claude Code plugin owns the skills and hooks: install neither, and remove any this project already has").option("--no-plugin", "Ignore any Claude Code plugin here and wire this project's own skills and hooks").option("--no-adopt", "Don't use this repository's Standard from the service; build a new one").option("--moments <list>", 'Set when Verity reviews without the default: stop,pre-commit,pre-push \u2014 or "none"').option("--intensity <level>", "Ignored: static analysis is Trivy only (kept so older plugin skills keep working)").option("--telemetry <on|off>", "Answer the telemetry question without asking: send cost & usage from this machine, or not").option("--untrack-memory", "Stop tracking the knowledge graph in git (init does not ask)").option("--untrack-standard", "Stop tracking .verity/standard.yaml in git: Verity keeps it on each machine (init does not ask)").option("--untrack-state", "Untrack committed Verity state without asking (a terminal run asks when it finds some)").action((opts) => runInit(program2, opts));
44739
45145
  }
44740
45146
 
44741
45147
  // src/commands/doctor.ts
44742
- var import_node_fs80 = require("node:fs");
45148
+ var import_node_fs81 = require("node:fs");
44743
45149
  var LEGACY_STANDARD_FILE = ".gate/standard.yaml";
44744
45150
  async function buildReport() {
44745
45151
  const prereqs = await checkPrereqs({ install: false });
@@ -44749,11 +45155,11 @@ async function buildReport() {
44749
45155
  const provenance = cliProvenance();
44750
45156
  const telemetry = await checkTelemetry();
44751
45157
  const artifacts = {
44752
- standard: (0, import_node_fs80.existsSync)(projectPath(STANDARD_FILE)),
45158
+ standard: (0, import_node_fs81.existsSync)(projectPath(STANDARD_FILE)),
44753
45159
  // The migration's own answer, not a bare existsSync: /verity:init skips a
44754
45160
  // question on this, so it must be true only when init will carry the file.
44755
45161
  legacyStandard: hasLegacyStandard(repoRoot()),
44756
- verityMd: (0, import_node_fs80.existsSync)(projectPath(VERITY_MD_FILE))
45162
+ verityMd: (0, import_node_fs81.existsSync)(projectPath(VERITY_MD_FILE))
44757
45163
  };
44758
45164
  const next = [];
44759
45165
  for (const c of prereqs.checks) {
@@ -44764,17 +45170,23 @@ async function buildReport() {
44764
45170
  if (legacyOnly) {
44765
45171
  next.push(`Run "verity init" \u2014 it moves the old GATE.md install, with its Standard (${LEGACY_STANDARD_FILE}), to .verity/.`);
44766
45172
  }
45173
+ const standard = { tracked: committedStandardFile(), version: state?.standardVersion ?? null };
44767
45174
  const missing = [
44768
45175
  !artifacts.standard && !legacyOnly && ".verity/standard.yaml",
44769
45176
  !artifacts.verityMd && !legacyOnly && "VERITY.md"
44770
45177
  ].filter((x) => !!x);
44771
- if (missing.length === 2) {
44772
- next.push('Run "verity standard synthesize --push" \u2014 it derives the Standard and VERITY.md from this codebase.');
45178
+ if (!artifacts.standard && !legacyOnly) {
45179
+ next.push(standard.version !== null ? `Run "verity standard pull" \u2014 the team's Standard (v${standard.version}) is on the service, and this machine has no copy of it.` : `Run "verity init" \u2014 it writes the team's Standard from the service, or builds one when the team has none.`);
44773
45180
  } else if (missing.length > 0) {
44774
45181
  next.push(
44775
45182
  `Missing ${missing.join(" and ")}. Run "verity standard synthesize --force" to regenerate \u2014 note that replaces the Standard.`
44776
45183
  );
44777
45184
  }
45185
+ if (standard.tracked) {
45186
+ next.push(
45187
+ '.verity/standard.yaml is tracked in git. Verity keeps it on each machine now, and reviews read the Standard on the service. Run "verity standard untrack" to stop tracking it (the file stays on disk; teammates get it back with "verity standard pull").'
45188
+ );
45189
+ }
44778
45190
  const noAnalysisMoment = !hooks.stop && hooks.guardOn.length === 0;
44779
45191
  const configRead = readProjectConfig();
44780
45192
  if (configRead.refused) next.push(configRefusalMessage(configRead.refused));
@@ -44852,6 +45264,7 @@ async function buildReport() {
44852
45264
  artifacts,
44853
45265
  memory: memory2,
44854
45266
  trackedState,
45267
+ standard,
44855
45268
  next
44856
45269
  };
44857
45270
  }
@@ -44886,11 +45299,14 @@ function registerDoctorCommand(program2) {
44886
45299
  printInfo(` Git-moment gate: ${report.hooks.guardOn.length ? report.hooks.guardOn.join(", ") : "off"}`);
44887
45300
  {
44888
45301
  const cfg = readProjectConfig();
44889
- const formal = cfg.formal === "push";
45302
+ const setting = formalSetting(cfg);
45303
+ const formal = setting.state === "push";
44890
45304
  const tc = formal ? findToolchain() : null;
44891
45305
  const lean = tc ? "\u2713" : installProgressLine() ?? "MISSING \u2014 verity lean install";
44892
- printInfo(` Formal verification (push): ${formal ? `on, Lean ${lean} (ranking, model writing and replay run on the Verity service with your login)` : "off"}`);
44893
- if (cfg.formal_refused) printWarn(` ${formalRefusalMessage(cfg.formal_refused)}`);
45306
+ const how = setting.source === "default" ? " by default" : setting.source === "env" ? " (VERITY_GIT_MOMENT_FORMAL)" : "";
45307
+ printInfo(` Formal verification (push): ${formal ? `on${how}, Lean ${lean} (ranking, model writing and replay run on the Verity service with your login)` : `off${how}`}`);
45308
+ if (cfg.formal_off_refused && formal) printWarn(` ${formalOffRefusalMessage(cfg.formal_off_refused)}`);
45309
+ if (cfg.formal_off_unverified && !formal && setting.source === "config") printWarn(` ${formalOffUnverifiedNote()}`);
44894
45310
  }
44895
45311
  printInfo(` Infra (intent/baseline/compact/session-end): ${[report.hooks.intent, report.hooks.baseline, report.hooks.compact, report.hooks.sessionEnd].filter(Boolean).length}/4`);
44896
45312
  printInfo(
@@ -44899,7 +45315,7 @@ function registerDoctorCommand(program2) {
44899
45315
  if (process.platform === "win32") printWarn(WINDOWS_STATIC_NOTE);
44900
45316
  else printInfo("Static analysis: Trivy (dependencies, malicious packages, secrets)");
44901
45317
  printInfo("Artifacts:");
44902
- printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? "\u2713" : report.artifacts.legacyStandard ? `not yet (an old GATE.md Standard is at ${LEGACY_STANDARD_FILE}; verity init moves it)` : "missing"}`);
45318
+ printInfo(` .verity/standard.yaml: ${report.artifacts.standard ? report.standard.tracked ? "\u2713 (tracked in git \u2014 Verity keeps it machine-local now: verity standard untrack)" : "\u2713" : report.artifacts.legacyStandard ? `not yet (an old GATE.md Standard is at ${LEGACY_STANDARD_FILE}; verity init moves it)` : report.standard.version !== null ? `not on this machine (the team's v${report.standard.version} is on the service: verity standard pull)` : "missing"}`);
44903
45319
  printInfo(` VERITY.md: ${report.artifacts.verityMd ? "\u2713" : "missing"}`);
44904
45320
  printInfo(
44905
45321
  ` .verity/memory/: ${report.memory.trackedFiles > 0 ? `${report.memory.trackedFiles} file(s) tracked in git \u2014 they appear in every diff` : report.memory.posture === "ignored" ? "machine-local \u2713" : "not tracked"}`
@@ -44916,8 +45332,8 @@ function registerDoctorCommand(program2) {
44916
45332
  }
44917
45333
 
44918
45334
  // src/commands/uninstall.ts
44919
- var import_node_fs81 = require("node:fs");
44920
- var import_node_path69 = require("node:path");
45335
+ var import_node_fs82 = require("node:fs");
45336
+ var import_node_path70 = require("node:path");
44921
45337
  function registerUninstallCommand(program2) {
44922
45338
  program2.command("uninstall").description("Remove Verity from this project (skills, hooks, .verity/, VERITY.md)").option("--dry-run", "Show what would be removed without doing it").option("--purge-global", "Also remove ~/.verity/ (deletes saved tokens \u2014 reconnect requires re-registration)").option("--keep-verity-md", "Keep the project root VERITY.md file").action(async (opts) => {
44923
45339
  const dryRun = opts.dryRun ?? false;
@@ -44926,11 +45342,11 @@ function registerUninstallCommand(program2) {
44926
45342
  const actions = [];
44927
45343
  const skillsRoot = projectPath(".claude/skills");
44928
45344
  for (const name2 of PROJECT_SKILL_NAMES) {
44929
- const dir = (0, import_node_path69.join)(skillsRoot, name2);
44930
- if ((0, import_node_fs81.existsSync)(dir)) {
45345
+ const dir = (0, import_node_path70.join)(skillsRoot, name2);
45346
+ if ((0, import_node_fs82.existsSync)(dir)) {
44931
45347
  actions.push({
44932
45348
  label: `Remove .claude/skills/${name2}/`,
44933
- apply: () => (0, import_node_fs81.rmSync)(dir, { recursive: true, force: true })
45349
+ apply: () => (0, import_node_fs82.rmSync)(dir, { recursive: true, force: true })
44934
45350
  });
44935
45351
  }
44936
45352
  }
@@ -44944,24 +45360,24 @@ function registerUninstallCommand(program2) {
44944
45360
  });
44945
45361
  }
44946
45362
  const verityDir = projectPath(VERITY_DIR);
44947
- if ((0, import_node_fs81.existsSync)(verityDir)) {
45363
+ if ((0, import_node_fs82.existsSync)(verityDir)) {
44948
45364
  actions.push({
44949
45365
  label: `Remove ${VERITY_DIR}/`,
44950
- apply: () => (0, import_node_fs81.rmSync)(verityDir, { recursive: true, force: true })
45366
+ apply: () => (0, import_node_fs82.rmSync)(verityDir, { recursive: true, force: true })
44951
45367
  });
44952
45368
  }
44953
45369
  if (!keepVerityMd) {
44954
45370
  const verityMd = projectPath(VERITY_MD_FILE);
44955
- if ((0, import_node_fs81.existsSync)(verityMd)) {
45371
+ if ((0, import_node_fs82.existsSync)(verityMd)) {
44956
45372
  actions.push({
44957
45373
  label: `Remove ${VERITY_MD_FILE}`,
44958
- apply: () => (0, import_node_fs81.rmSync)(verityMd, { force: true })
45374
+ apply: () => (0, import_node_fs82.rmSync)(verityMd, { force: true })
44959
45375
  });
44960
45376
  }
44961
45377
  }
44962
45378
  const cleanupEmptyDir = (path) => {
44963
- if ((0, import_node_fs81.existsSync)(path) && (0, import_node_fs81.statSync)(path).isDirectory() && (0, import_node_fs81.readdirSync)(path).length === 0) {
44964
- (0, import_node_fs81.rmdirSync)(path);
45379
+ if ((0, import_node_fs82.existsSync)(path) && (0, import_node_fs82.statSync)(path).isDirectory() && (0, import_node_fs82.readdirSync)(path).length === 0) {
45380
+ (0, import_node_fs82.rmdirSync)(path);
44965
45381
  }
44966
45382
  };
44967
45383
  actions.push({
@@ -44972,11 +45388,11 @@ function registerUninstallCommand(program2) {
44972
45388
  }
44973
45389
  });
44974
45390
  const home = process.env.HOME ?? "";
44975
- const globalVerityDir = (0, import_node_path69.join)(home, ".verity");
44976
- if (purgeGlobal && (0, import_node_fs81.existsSync)(globalVerityDir)) {
45391
+ const globalVerityDir = (0, import_node_path70.join)(home, ".verity");
45392
+ if (purgeGlobal && (0, import_node_fs82.existsSync)(globalVerityDir)) {
44977
45393
  actions.push({
44978
45394
  label: `Remove ~/.verity/ (global credentials \u2014 reconnect requires re-registration)`,
44979
- apply: () => (0, import_node_fs81.rmSync)(globalVerityDir, { recursive: true, force: true })
45395
+ apply: () => (0, import_node_fs82.rmSync)(globalVerityDir, { recursive: true, force: true })
44980
45396
  });
44981
45397
  }
44982
45398
  if (actions.length === 0) {
@@ -45195,8 +45611,8 @@ function registerTaskCommands(program2) {
45195
45611
  }
45196
45612
 
45197
45613
  // src/commands/reset.ts
45198
- var import_node_fs82 = require("node:fs");
45199
- var import_node_path70 = require("node:path");
45614
+ var import_node_fs83 = require("node:fs");
45615
+ var import_node_path71 = require("node:path");
45200
45616
  function registerResetCommand(program2) {
45201
45617
  program2.command("reset").description("Close the current task and clear transient state").option("--keep-task", "Only purge caches; leave the current task open").option("--all", "Also purge diagnostic logs (.verity/.logs/)").action(async (opts) => {
45202
45618
  const globals = program2.opts();
@@ -45236,18 +45652,18 @@ function registerResetCommand(program2) {
45236
45652
  } catch {
45237
45653
  }
45238
45654
  }
45239
- const purged = purgeVerityDir((0, import_node_path70.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
45655
+ const purged = purgeVerityDir((0, import_node_path71.basename)(CACHE_DIR), (name2) => name2.startsWith("pending-"));
45240
45656
  if (purged === null) printError(`Did not purge ${CACHE_DIR}: it is not a directory inside ${VERITY_DIR}.`);
45241
45657
  const stateDir2 = projectPath(VERITY_DIR);
45242
45658
  const filesToClear = [
45243
- (0, import_node_path70.basename)(INTENT_FILE),
45244
- (0, import_node_path70.basename)(HASH_FILE),
45245
- (0, import_node_path70.basename)(BASELINE_SHA_FILE),
45659
+ (0, import_node_path71.basename)(INTENT_FILE),
45660
+ (0, import_node_path71.basename)(HASH_FILE),
45661
+ (0, import_node_path71.basename)(BASELINE_SHA_FILE),
45246
45662
  ".iteration-count",
45247
45663
  ".last-analysis"
45248
45664
  ];
45249
45665
  for (const name2 of filesToClear) {
45250
- if (lstatOrNull((0, import_node_path70.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
45666
+ if (lstatOrNull((0, import_node_path71.join)(stateDir2, name2)) && !replaceInRoot(stateDir2, name2, "")) {
45251
45667
  printError(`Did not clear ${VERITY_DIR}/${name2}: it is not a regular file inside ${VERITY_DIR}.`);
45252
45668
  }
45253
45669
  }
@@ -45262,29 +45678,29 @@ function registerResetCommand(program2) {
45262
45678
  }
45263
45679
  function lstatOrNull(path) {
45264
45680
  try {
45265
- return (0, import_node_fs82.lstatSync)(path);
45681
+ return (0, import_node_fs83.lstatSync)(path);
45266
45682
  } catch {
45267
45683
  return null;
45268
45684
  }
45269
45685
  }
45270
45686
  function purgeVerityDir(child, select) {
45271
45687
  const verityDir = projectPath(VERITY_DIR);
45272
- const dir = (0, import_node_path70.join)(verityDir, child);
45688
+ const dir = (0, import_node_path71.join)(verityDir, child);
45273
45689
  const parent = lstatOrNull(verityDir);
45274
45690
  const st = lstatOrNull(dir);
45275
45691
  if (!st) return 0;
45276
45692
  if (!parent || parent.isSymbolicLink() || !parent.isDirectory()) return null;
45277
45693
  if (st.isSymbolicLink() || !st.isDirectory()) return null;
45278
45694
  try {
45279
- if ((0, import_node_fs82.realpathSync)(dir) !== (0, import_node_path70.join)((0, import_node_fs82.realpathSync)(verityDir), child)) return null;
45695
+ if ((0, import_node_fs83.realpathSync)(dir) !== (0, import_node_path71.join)((0, import_node_fs83.realpathSync)(verityDir), child)) return null;
45280
45696
  } catch {
45281
45697
  return null;
45282
45698
  }
45283
45699
  let removed = 0;
45284
- for (const entry of (0, import_node_fs82.readdirSync)(dir, { withFileTypes: true })) {
45700
+ for (const entry of (0, import_node_fs83.readdirSync)(dir, { withFileTypes: true })) {
45285
45701
  if (entry.isDirectory() || !select(entry.name)) continue;
45286
45702
  try {
45287
- (0, import_node_fs82.unlinkSync)((0, import_node_path70.join)(dir, entry.name));
45703
+ (0, import_node_fs83.unlinkSync)((0, import_node_path71.join)(dir, entry.name));
45288
45704
  removed++;
45289
45705
  } catch {
45290
45706
  }
@@ -45293,7 +45709,7 @@ function purgeVerityDir(child, select) {
45293
45709
  }
45294
45710
 
45295
45711
  // src/commands/reflect.ts
45296
- var import_node_fs83 = require("node:fs");
45712
+ var import_node_fs84 = require("node:fs");
45297
45713
 
45298
45714
  // src/lib/reflection-globs.ts
45299
45715
  var MAX_GLOBS = 6;
@@ -45331,7 +45747,7 @@ async function writeNodeToDisk(args2) {
45331
45747
  { treePaths: listTrackedFiles(), recordBaseline: false }
45332
45748
  );
45333
45749
  if (written === 0) return false;
45334
- return (0, import_node_fs83.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
45750
+ return (0, import_node_fs84.existsSync)(projectPath(`${VERITY_DIR}/memory/${args2.filePath}`));
45335
45751
  } catch {
45336
45752
  return false;
45337
45753
  }
@@ -45768,7 +46184,7 @@ function registerRunCommand(program2) {
45768
46184
  }
45769
46185
 
45770
46186
  // src/commands/telemetry.ts
45771
- var import_node_fs84 = require("node:fs");
46187
+ var import_node_fs85 = require("node:fs");
45772
46188
  var SCOPE = "every Claude Code session on this machine";
45773
46189
  function declineHeaders(why) {
45774
46190
  process.stderr.write(`Verity telemetry is off here: ${why} Nothing is exported from this session.
@@ -45776,7 +46192,7 @@ function declineHeaders(why) {
45776
46192
  process.exitCode = 1;
45777
46193
  }
45778
46194
  async function recordTelemetryChoice(choice) {
45779
- if (!(0, import_node_fs84.existsSync)(projectPath(SETUP_STATE_FILE))) return;
46195
+ if (!(0, import_node_fs85.existsSync)(projectPath(SETUP_STATE_FILE))) return;
45780
46196
  try {
45781
46197
  if ((await readSetupState())?.telemetry === choice) return;
45782
46198
  await writeSetupState({ telemetry: choice });
@@ -45884,12 +46300,12 @@ function registerTelemetryCommands(program2) {
45884
46300
  }
45885
46301
 
45886
46302
  // src/commands/prove.ts
45887
- var import_node_fs86 = require("node:fs");
45888
- var import_node_path72 = require("node:path");
46303
+ var import_node_fs87 = require("node:fs");
46304
+ var import_node_path73 = require("node:path");
45889
46305
 
45890
46306
  // src/lib/prove/on-demand-run.ts
45891
- var import_node_fs85 = require("node:fs");
45892
- var import_node_path71 = require("node:path");
46307
+ var import_node_fs86 = require("node:fs");
46308
+ var import_node_path72 = require("node:path");
45893
46309
  var ON_DEMAND_RUN = `${PROOFS_DIR}/.results/.formal-run.json`;
45894
46310
  var ON_DEMAND_RUN_REUSE_MS = 18 * 6e4;
45895
46311
  function isLocalState2(root) {
@@ -45911,7 +46327,7 @@ function forgetOnDemandRun(root) {
45911
46327
  setFormalRun(null);
45912
46328
  if (!isLocalState2(root)) return;
45913
46329
  try {
45914
- (0, import_node_fs85.unlinkSync)((0, import_node_path71.join)(root, ON_DEMAND_RUN));
46330
+ (0, import_node_fs86.unlinkSync)((0, import_node_path72.join)(root, ON_DEMAND_RUN));
45915
46331
  } catch {
45916
46332
  }
45917
46333
  }
@@ -46088,7 +46504,7 @@ function registerProveCommand(program2) {
46088
46504
  process.exit(2);
46089
46505
  }
46090
46506
  const limit = Math.max(1, Math.min(50, Number(opts.limit) || 10));
46091
- let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path72.relative)(root, (0, import_node_path72.resolve)(p)) || ".") });
46507
+ let signals = collectRiskSignals(root, { since: opts.since, limit, paths: paths.map((p) => (0, import_node_path73.relative)(root, (0, import_node_path73.resolve)(p)) || ".") });
46092
46508
  if (opts.hint && paths.length === 0) {
46093
46509
  const hit = hintMatches(root, opts.hint);
46094
46510
  if (hit.files.length > 0) {
@@ -46142,7 +46558,7 @@ function registerProveCommand(program2) {
46142
46558
  });
46143
46559
  prove.command("check [model]").description("Elaborate a model (or every model) with the local Lean toolchain and report each property").option("--timeout <ms>", "Per-model timeout in ms", "120000").option("--json", "Machine-readable output").action((model, opts) => {
46144
46560
  const root = repoRoot();
46145
- if (!(0, import_node_fs86.existsSync)((0, import_node_path72.join)(root, PROOFS_DIR, "lakefile.toml"))) {
46561
+ if (!(0, import_node_fs87.existsSync)((0, import_node_path73.join)(root, PROOFS_DIR, "lakefile.toml"))) {
46146
46562
  printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
46147
46563
  process.exit(2);
46148
46564
  }
@@ -46166,7 +46582,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
46166
46582
  const reports = [];
46167
46583
  for (const f of files) {
46168
46584
  const report = checkModel(tc, root, f, { timeoutMs: Math.max(1e3, Number(opts.timeout) || 12e4) });
46169
- report.file = (0, import_node_path72.relative)(root, f);
46585
+ report.file = (0, import_node_path73.relative)(root, f);
46170
46586
  reports.push(report);
46171
46587
  try {
46172
46588
  writeResult(root, report);
@@ -46204,7 +46620,7 @@ ${build.stderr.split("\n").filter((l) => !l.startsWith("trace:")).slice(0, 20).j
46204
46620
  printError("Not logged in \u2014 replays run on the Verity service. Run `verity login` first.");
46205
46621
  process.exit(2);
46206
46622
  }
46207
- if (!(0, import_node_fs86.existsSync)((0, import_node_path72.join)(root, PROOFS_DIR, "lakefile.toml"))) {
46623
+ if (!(0, import_node_fs87.existsSync)((0, import_node_path73.join)(root, PROOFS_DIR, "lakefile.toml"))) {
46208
46624
  printError(`No ${PROOFS_DIR}/ project here. Run \`verity prove init\` first.`);
46209
46625
  process.exit(2);
46210
46626
  }
@@ -46243,7 +46659,7 @@ ${report.error ?? ""}`);
46243
46659
  process.exit(2);
46244
46660
  }
46245
46661
  }
46246
- const modelSource = readRegularFileInRoot(root, (0, import_node_path72.relative)(root, file), 512 * 1024);
46662
+ const modelSource = readRegularFileInRoot(root, (0, import_node_path73.relative)(root, file), 512 * 1024);
46247
46663
  if (!opts.json) printInfo(`replaying ${targets.length} counterexample(s) of ${model} in network-less containers (${Math.min(targets.length, REPLAY_CONCURRENCY)} at a time)\u2026`);
46248
46664
  const outcomes = await mapPool(targets, REPLAY_CONCURRENCY, async (p) => {
46249
46665
  const brief = buildReplayBrief(root, report, p, modelSource.ok ? modelSource.content : "");
@@ -46264,7 +46680,7 @@ ${report.error ?? ""}`);
46264
46680
  });
46265
46681
  prove.command("auto [files...]").description("The formal phase the pre-push guard runs: select one risky symbol among changed files, generate a model, check it, replay counterexamples (default files: the commits ahead of upstream)").option("--deadline <ms>", "Budget for the whole phase", "170000").option("--json", "Machine-readable output").action(async (paths, opts) => {
46266
46682
  const root = repoRoot();
46267
- let files = paths.map((p) => (0, import_node_path72.relative)(root, (0, import_node_path72.resolve)(p)));
46683
+ let files = paths.map((p) => (0, import_node_path73.relative)(root, (0, import_node_path73.resolve)(p)));
46268
46684
  if (files.length === 0) {
46269
46685
  const ahead = runGitSync(root, ["diff", "--name-only", "@{upstream}...HEAD"]);
46270
46686
  const fallback = ahead.ok ? ahead : runGitSync(root, ["diff", "--name-only", "HEAD~1..HEAD"]);
@@ -46338,7 +46754,7 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
46338
46754
  let revise;
46339
46755
  if (opts.revise) {
46340
46756
  const existing = resolveModelFile(root, opts.revise);
46341
- const src = existing ? readRegularFileInRoot(root, (0, import_node_path72.relative)(root, existing), 64 * 1024) : null;
46757
+ const src = existing ? readRegularFileInRoot(root, (0, import_node_path73.relative)(root, existing), 64 * 1024) : null;
46342
46758
  if (!src || !src.ok || !opts.note) {
46343
46759
  printError("--revise needs an existing model under Models/ and --note - with what the replay showed on stdin");
46344
46760
  process.exit(2);
@@ -46387,8 +46803,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
46387
46803
  });
46388
46804
  prove.command("waive <file>").description("Dismiss the remembered formal finding(s) on a file for its current bytes \u2014 the accepted-risk path for a formal block").requiredOption("--reason <text>", "The human disposition this records (reviewer finding, ADR, \u2026)").action((file, opts) => {
46389
46805
  const root = repoRoot();
46390
- const rel0 = (0, import_node_path72.relative)(root, (0, import_node_path72.isAbsolute)(file) ? file : (0, import_node_path72.join)(root, file)).replace(/\\/g, "/");
46391
- const rel = rel0.startsWith("..") || (0, import_node_path72.isAbsolute)(rel0) ? null : rel0;
46806
+ const rel0 = (0, import_node_path73.relative)(root, (0, import_node_path73.isAbsolute)(file) ? file : (0, import_node_path73.join)(root, file)).replace(/\\/g, "/");
46807
+ const rel = rel0.startsWith("..") || (0, import_node_path73.isAbsolute)(rel0) ? null : rel0;
46392
46808
  if (!rel) {
46393
46809
  printError(`${file} is not inside this repository`);
46394
46810
  process.exit(2);
@@ -46404,8 +46820,8 @@ ${f.reason ? ` ${terminalText(f.reason, 2e3)}
46404
46820
  });
46405
46821
  prove.command("hash <file>").description("Print the sha256 of a file, for a model header (Verity.model \u2026 sha256). Path is REPO-relative, as in the header").action((file) => {
46406
46822
  const root = repoRoot();
46407
- const rel = (0, import_node_path72.relative)(root, (0, import_node_path72.isAbsolute)(file) ? file : (0, import_node_path72.join)(root, file));
46408
- const digest = rel.startsWith("..") || (0, import_node_path72.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
46823
+ const rel = (0, import_node_path73.relative)(root, (0, import_node_path73.isAbsolute)(file) ? file : (0, import_node_path73.join)(root, file));
46824
+ const digest = rel.startsWith("..") || (0, import_node_path73.isAbsolute)(rel) ? null : sha256InRoot(root, rel);
46409
46825
  if (!digest) {
46410
46826
  printError(`Cannot hash ${file}: it must be a regular file inside this repository (not a symlink)`);
46411
46827
  process.exit(2);
@@ -46439,10 +46855,10 @@ function refreshScaffold(root) {
46439
46855
  }
46440
46856
 
46441
46857
  // src/commands/lean.ts
46442
- var import_node_fs87 = require("node:fs");
46858
+ var import_node_fs88 = require("node:fs");
46443
46859
  var import_node_child_process24 = require("node:child_process");
46444
46860
  var import_node_os16 = require("node:os");
46445
- var import_node_path73 = require("node:path");
46861
+ var import_node_path74 = require("node:path");
46446
46862
  function registerLeanCommand(program2) {
46447
46863
  const lean = program2.command("lean").description("The Lean 4 toolchain formal verification needs (managed download, pinned by this CLI)");
46448
46864
  lean.command("status").option("--json", "Machine-readable output").description("Which toolchain would run, and whether it matches the pinned version").action((opts) => {
@@ -46461,7 +46877,7 @@ function registerLeanCommand(program2) {
46461
46877
  const bg = readInstallState();
46462
46878
  const background = bg ? { ...bg, running: bg.state === "running" && installRunning() } : null;
46463
46879
  if (opts.json) {
46464
- printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs87.existsSync)((0, import_node_path73.join)(managed, "bin")), asset: leanAssetName(), background });
46880
+ printJson({ pinned: LEAN_TOOLCHAIN, found: tc?.lake ?? null, version, matches_pin: matches, managed_dir: managed, managed_installed: (0, import_node_fs88.existsSync)((0, import_node_path74.join)(managed, "bin")), asset: leanAssetName(), background });
46465
46881
  return;
46466
46882
  }
46467
46883
  printInfo(`pinned: ${LEAN_TOOLCHAIN}`);
@@ -46482,7 +46898,7 @@ function registerLeanCommand(program2) {
46482
46898
  process.exit(2);
46483
46899
  }
46484
46900
  const dir = pinnedVersionDir();
46485
- if ((0, import_node_fs87.existsSync)((0, import_node_path73.join)(dir, "bin", "lake")) && !opts.force) {
46901
+ if ((0, import_node_fs88.existsSync)((0, import_node_path74.join)(dir, "bin", "lake")) && !opts.force) {
46486
46902
  printInfo(`already installed at ${dir}`);
46487
46903
  return;
46488
46904
  }
@@ -46518,20 +46934,20 @@ function registerLeanCommand(program2) {
46518
46934
  });
46519
46935
  lean.command("remove").description("Delete the Verity-managed toolchain for the pinned version").action(() => {
46520
46936
  const dir = pinnedVersionDir();
46521
- if (!(0, import_node_fs87.existsSync)(dir)) {
46937
+ if (!(0, import_node_fs88.existsSync)(dir)) {
46522
46938
  printInfo("nothing to remove");
46523
46939
  return;
46524
46940
  }
46525
- (0, import_node_fs87.rmSync)(dir, { recursive: true, force: true });
46941
+ (0, import_node_fs88.rmSync)(dir, { recursive: true, force: true });
46526
46942
  printInfo(`removed ${dir}`);
46527
46943
  });
46528
46944
  }
46529
46945
 
46530
46946
  // src/cli.ts
46531
- program.name("verity").description("CLI for Verity quality gate service").version("0.35.3").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
46947
+ program.name("verity").description("CLI for Verity quality gate service").version("0.35.5").option("--token <token>", "Override authentication token").option("--service-url <url>", "Override service URL").option("--verbose", "Log HTTP requests/responses to stderr").hook("preAction", async (_thisCommand, actionCommand) => {
46532
46948
  const isTelemetryHelper = actionCommand.name() === "headers" && actionCommand.parent?.name() === "telemetry";
46533
46949
  if (!isTelemetryHelper) {
46534
- installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.3");
46950
+ installStderrLog(actionCommand.name(), process.argv.slice(2), "0.35.5");
46535
46951
  }
46536
46952
  setUserNamedServiceUrl(program.opts().serviceUrl);
46537
46953
  try {