@evident-ai/runner-synchroniser 3.4.1-dev.31006db → 3.4.1-dev.38181ff

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +11 -11
  2. package/dist/cli.js +27 -4
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -56,7 +56,7 @@ build time.
56
56
  | `env` | resolved config as shell-eval'able vars | `0` = ran; non-zero = tool broken |
57
57
  | `litestream-config` | the generated `litestream.yml` | `0` = ran; non-zero = tool broken |
58
58
  | `restore <claude\|opencode>` | — | `0` = ran; non-zero = tool broken |
59
- | `sync-once <claude\|opencode>` | — | `0` = ran; non-zero = tool broken |
59
+ | `sync-once <claude\|opencode>` | — | `0` = uploaded/unchanged/absent/disabled; `40` = not persisted; other = tool broken |
60
60
  | `model-auth-ready` | — | `0` = ready, `10` = not ready; other = tool broken |
61
61
  | `self-stop` | — | `0` = stopped, `20` = keep the task; other = tool broken |
62
62
  | `session-db-classify <litestream-restore-exit-code> <attempt> [--on-unusable-replica=<prune\|leave\|clear\|crash>] [--recovery-occurred] [--fresh-db-fallback]` | — | `0` = restored/no-replica/disabled, `32` = re-run and ask again, `31` = unusable (booted fresh, replicates into the existing prefix), `30` = fatal; other = usage/tool broken |
@@ -250,8 +250,7 @@ and it can never repair corruption at a higher compaction level.
250
250
 
251
251
  `EVIDENT_ON_UNUSABLE_REPLICA` is a `runner/docker-images/fargate` (entrypoint) variable translated into the flag above at boot — this CLI never reads it, so it has no row in this README's Configuration table below; see [the Fargate image README](../docker-images/fargate/README.md#when-the-replica-is-unusable-evident_on_unusable_replica). An unrecognised value (including wrong casing, e.g. `Prune`) exits `2`, outside `entrypoint.sh`'s `0|10|20|30|31|32|33` allow-list — a typo **crash-loops the task on attempt 1** rather than falling back to `prune`, and also logs the misleading "credential persistence is DEGRADED" ERROR.
252
252
 
253
- See `specs/local-runner.feature`'s "Recovering session history at startup" scenarios for
254
- the behavioural anchor (31 comes online, 30 does not).
253
+ The behavioural anchor is explicit: **31 comes online, 30 does not**.
255
254
 
256
255
  ### `session-db-verify`
257
256
 
@@ -327,15 +326,16 @@ same way it already reuses `SESSION-DB-REPLICA-UNUSABLE` for `session-db-classif
327
326
 
328
327
  ### Why the contract is asymmetric
329
328
 
330
- `restore` and `sync-once` **never** report a domain outcome through the exit code. A
331
- missing remote object, a corrupt local file, a failed upload, an IAM denial — all of
332
- those are logged and still exit `0`. None of them should abort a boot: a runner with no
333
- credentials yet is a runner a human can still log into.
329
+ `restore` logs domain outcomes and still exits `0`: a missing remote object or a
330
+ restore failure should not abort a boot, because a runner with no credentials yet
331
+ is a runner a human can still log into. `sync-once` returns `40` when the current
332
+ credential file is not persisted (`failed`, `hashFailed`, or `localInvalid`), while
333
+ `absent` and `disabled` remain legitimate `0` outcomes.
334
334
 
335
- The consequence is the point: **any non-zero status from `restore`/`sync-once` means the
336
- tool itself broke** — bad arguments (`2`), an uncaught throw (`1`), or a bundle that
337
- would not run at all. The shell needs no case analysis to know something is wrong, so
338
- its "credential persistence is DEGRADED" error lives in exactly one helper.
335
+ The consequence is the point: **any non-zero status from `restore` means the tool itself
336
+ broke** — and `sync-once` uses `40` for its typed persistence answer; other non-zero
337
+ statuses still mean bad arguments (`2`), an uncaught throw (`1`), or a bundle that would
338
+ not run at all. The shell keeps that distinction in one helper.
339
339
 
340
340
  The **two predicates** — `model-auth-ready` and `self-stop` — each need to distinguish
341
341
  "the answer is no" from "the tool is broken", so each answers no with its own dedicated
package/dist/cli.js CHANGED
@@ -61819,7 +61819,14 @@ async function verifySessionDb(params) {
61819
61819
  if (stat2 === null) return { kind: "skipped", localDb: "absent" };
61820
61820
  if (stat2.size === 0) return { kind: "skipped", localDb: "empty" };
61821
61821
  const result = await sqlite.check(dbPath);
61822
- if (result.ok) return { kind: "healthy", bytes: stat2.size };
61822
+ if (result.ok) {
61823
+ if (result.migrationSummary !== void 0 && result.migrationSummary !== null) {
61824
+ log(
61825
+ `INFO: SESSION-DB-SCHEMA-PROVENANCE: ${result.migrationSummary.migrationCount} migrations, newest ${result.migrationSummary.newestMigrationId ?? "(none)"}`
61826
+ );
61827
+ }
61828
+ return { kind: "healthy", bytes: stat2.size };
61829
+ }
61823
61830
  log(
61824
61831
  `WARNING: SESSION-DB-INTEGRITY-FAILED: opencode.db at ${dbPath} failed its integrity check: ${result.detail}`
61825
61832
  );
@@ -62062,6 +62069,19 @@ var MAX_DETAIL_LENGTH = 500;
62062
62069
  function truncate(text) {
62063
62070
  return text.length > MAX_DETAIL_LENGTH ? `${text.slice(0, MAX_DETAIL_LENGTH)}\u2026` : text;
62064
62071
  }
62072
+ function readMigrationSummary(db) {
62073
+ try {
62074
+ const row = db.prepare("SELECT COUNT(*) AS migration_count, MAX(id) AS newest_migration_id FROM migration").get();
62075
+ const count = row?.migration_count;
62076
+ const newestId = row?.newest_migration_id;
62077
+ if (typeof count !== "number" || !Number.isSafeInteger(count) || count < 0) return null;
62078
+ if (newestId !== null && typeof newestId !== "string") return null;
62079
+ return { migrationCount: count, newestMigrationId: newestId ?? null };
62080
+ } catch (error) {
62081
+ void error;
62082
+ return null;
62083
+ }
62084
+ }
62065
62085
  var nodeSqliteIntegrity = {
62066
62086
  async check(path) {
62067
62087
  let db;
@@ -62070,7 +62090,7 @@ var nodeSqliteIntegrity = {
62070
62090
  const row = db.prepare("PRAGMA integrity_check").get();
62071
62091
  const value = row?.integrity_check;
62072
62092
  if (value === "ok") {
62073
- return { ok: true };
62093
+ return { ok: true, migrationSummary: readMigrationSummary(db) };
62074
62094
  }
62075
62095
  return { ok: false, detail: truncate(String(value ?? "no result row")) };
62076
62096
  } catch (err) {
@@ -62144,6 +62164,7 @@ var EXIT_SESSION_DB_UNUSABLE = 31;
62144
62164
  var EXIT_SESSION_DB_RETRY = 32;
62145
62165
  var EXIT_SESSION_DB_UNVERIFIABLE = 33;
62146
62166
  var EXIT_SESSION_DB_UNSEPARATED = 34;
62167
+ var EXIT_SYNC_NOT_PERSISTED = 40;
62147
62168
  var STATE_SUFFIX = ".synchash";
62148
62169
  var COMMANDS = shell_contract_default.commands;
62149
62170
  var USAGE = `Usage: runner-synchroniser <command> [args]
@@ -62294,6 +62315,7 @@ async function commandSyncOnce(name, config, deps) {
62294
62315
  if (result.hash !== null && result.hash !== lastHash) {
62295
62316
  await writeLastHash(fileOps, statePath, result.hash, log);
62296
62317
  }
62318
+ return result.outcome.kind === "failed" || result.outcome.kind === "hashFailed" || result.outcome.kind === "localInvalid" ? EXIT_SYNC_NOT_PERSISTED : 0;
62297
62319
  }
62298
62320
  async function commandSessionDbClassify(args, config, deps) {
62299
62321
  const { fileOps, log } = deps;
@@ -62431,10 +62453,10 @@ ${USAGE}`);
62431
62453
  }
62432
62454
  if (command12 === "restore") {
62433
62455
  await commandRestore(name, config, deps);
62456
+ return 0;
62434
62457
  } else {
62435
- await commandSyncOnce(name, config, deps);
62458
+ return commandSyncOnce(name, config, deps);
62436
62459
  }
62437
- return 0;
62438
62460
  }
62439
62461
  function isEntryPoint() {
62440
62462
  const entry = process.argv[1];
@@ -62467,6 +62489,7 @@ export {
62467
62489
  EXIT_SESSION_DB_UNSEPARATED,
62468
62490
  EXIT_SESSION_DB_UNUSABLE,
62469
62491
  EXIT_SESSION_DB_UNVERIFIABLE,
62492
+ EXIT_SYNC_NOT_PERSISTED,
62470
62493
  EXIT_USAGE,
62471
62494
  main
62472
62495
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evident-ai/runner-synchroniser",
3
- "version": "3.4.1-dev.31006db",
3
+ "version": "3.4.1-dev.38181ff",
4
4
  "description": "Restores and syncs the Evident runner's OpenCode credential stores (and litestream config) to an object store, so a runner survives task replacement with almost no state loss.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",
@@ -11,7 +11,7 @@
11
11
  "dist"
12
12
  ],
13
13
  "scripts": {
14
- "build": "tsup",
14
+ "build": "tsup && node -e 'const m=require(\"./package.json\").main; if (!require(\"node:fs\").existsSync(m)) { console.error(`build produced no ${m}`); process.exit(1); }'",
15
15
  "typecheck": "tsc --noEmit",
16
16
  "lint": "eslint 'src/**/*.ts'",
17
17
  "test": "vitest run",