trantor 0.17.78 → 0.17.79

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.17.78",
3
+ "version": "0.17.79",
4
4
  "description": "Trantor — the hub-world for AI agent crews: live message bus, presence, project Kanban/flow board + crew orchestration for independent AI coding agents (Claude, Codex, Gemini, Kimi, DeepSeek)",
5
5
  "mcpServers": {
6
6
  "relay": {
package/README.md CHANGED
@@ -283,7 +283,8 @@ Crew output is gated mechanically, too: `bin/slop-gate.mjs` runs the vendored
283
283
  [anti-slop](https://github.com/dmmulroy/anti-slop) Oxlint rules over an agent's **changed files
284
284
  only** — unexplained type assertions, `unknown` laundering, runtime `typeof`, Reflect tricks and
285
285
  friends fail the card before it can reach done, while legacy debt burns down on its own card
286
- instead of blocking everyone.
286
+ instead of blocking everyone. A surface that has paid its debt off stays paid: `desktop/src` is at
287
+ zero and `npm test` now lints it in full (`node bin/slop-gate.mjs --surface desktop/src`).
287
288
 
288
289
  ## The brain — plan-aware economics
289
290
 
package/bin/models.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  import { execSync } from "node:child_process";
8
8
  import { dirname, join } from "node:path";
9
9
  import { homedir } from "node:os";
10
- import { existsSync } from "node:fs";
10
+ import { existsSync, statSync } from "node:fs";
11
11
  import { pathToFileURL, fileURLToPath } from "node:url";
12
12
  import { buildRoster, loadWorld } from "./advise.mjs";
13
13
 
@@ -57,7 +57,10 @@ function detail(name) {
57
57
  if (existsSync(SCROOGE) || has("scrooge")) {
58
58
  console.log(`\n${C.gold}router picks (code task):${C.off}`);
59
59
  for (const d of ["easy", "medium", "hard"]) console.log(` ${d.padEnd(7)} → ${routePick(models, d)}`);
60
- console.log(`${C.dim}(run scrooge-capabilities to (re)score the catalog for accurate difficulty routing)${C.off}`);
60
+ const caps = join(H, ".token-scrooge", "capabilities.json");
61
+ // only nag when the scores are missing or older than the weekly refresh allows for
62
+ const ageDays = existsSync(caps) ? (Date.now() - statSync(caps).mtimeMs) / 86400e3 : Infinity;
63
+ if (ageDays > 9) console.log(`${C.dim}(scores ${ageDays === Infinity ? "missing" : `${Math.floor(ageDays)}d old`} — run scrooge-capabilities to (re)score the catalog for accurate difficulty routing)${C.off}`);
61
64
  }
62
65
  }
63
66
 
package/bin/slop-gate.mjs CHANGED
@@ -3,6 +3,7 @@
3
3
  //
4
4
  // node bin/slop-gate.mjs # lint changed + untracked lintable files (the crew gate)
5
5
  // node bin/slop-gate.mjs --all # lint the whole configured surface (advisory audit)
6
+ // node bin/slop-gate.mjs --surface desktop/src # lint ONE paid-off surface in full (hard gate in npm test)
6
7
  //
7
8
  // The rules are the vendored dmmulroy/anti-slop set (tools/oxlint/anti-slop — ours to tune):
8
9
  // they mechanically reject low-evidence AI patterns — unexplained type assertions, unknown
@@ -13,6 +14,10 @@
13
14
  import { execSync, spawnSync } from "node:child_process";
14
15
 
15
16
  const ALL = process.argv.includes("--all");
17
+ // A surface that has burned its debt down to zero (desktop/src, #4798) is gated in FULL, not by
18
+ // diff: the whole point of paying it off is that it stays paid. npm test runs this.
19
+ const SURFACE_AT = process.argv.indexOf("--surface");
20
+ const SURFACE = SURFACE_AT > -1 ? process.argv[SURFACE_AT + 1] : null;
16
21
  const LINTABLE = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
17
22
 
18
23
  function sh(cmd) {
@@ -21,7 +26,9 @@ function sh(cmd) {
21
26
  }
22
27
 
23
28
  let args;
24
- if (ALL) {
29
+ if (SURFACE) {
30
+ args = [SURFACE];
31
+ } else if (ALL) {
25
32
  args = ["."];
26
33
  } else {
27
34
  const changed = sh("git diff --name-only HEAD");
@@ -40,9 +47,10 @@ const hits = out.split("\n").filter(l => /error\s+anti-slop\(/.test(l));
40
47
 
41
48
  if (hits.length) {
42
49
  console.log(hits.join("\n"));
43
- console.log(`\nslop-gate: ${hits.length} anti-slop error(s) in your changes fix them (or state the SAFETY invariant) before moving the card to testing.`);
50
+ const where = SURFACE ? `in ${SURFACE} (a zero-debt surfacekeep it at zero)` : "in your changes";
51
+ console.log(`\nslop-gate: ${hits.length} anti-slop error(s) ${where} — fix them (or state the SAFETY invariant) before moving the card to testing.`);
44
52
  process.exit(1);
45
53
  }
46
54
  const warnings = out.split("\n").filter(l => /\bwarning\b/.test(l)).length;
47
- console.log(`slop-gate: clean${warnings ? ` (${warnings} advisory warning(s) — not gating)` : ""}.`);
55
+ console.log(`slop-gate${SURFACE ? ` (${SURFACE})` : ""}: clean${warnings ? ` (${warnings} advisory warning(s) — not gating)` : ""}.`);
48
56
  process.exit(0);
@@ -22,7 +22,7 @@ Usage: scrooge-capabilities # refresh from AA (+OpenRouter), show a
22
22
  scrooge-capabilities --dry-run # fetch + match, print, but don't write
23
23
  Exit: 0 updated · 1 nothing fetched (no key / network) · 2 error.
24
24
  """
25
- import sys, os, json, argparse, math, urllib.request, urllib.error
25
+ import sys, os, json, argparse, math, time, urllib.request, urllib.error
26
26
 
27
27
  HOME = os.path.expanduser("~")
28
28
  SCROOGE_DIR = os.environ.get("SCROOGE_HOME", os.path.join(HOME, ".token-scrooge"))
@@ -56,10 +56,21 @@ def load_env_file(path):
56
56
  except Exception:
57
57
  pass
58
58
 
59
- def http_get_json(url, headers, timeout=30):
59
+ def http_get_json(url, headers, timeout=30, retries=(5, 20, 60)):
60
+ # the weekly launchd run can land while the Mac has no network yet (2026-08-17: DNS error,
61
+ # scores went a week stale) -- back off and retry on connection failures, not on HTTP errors
60
62
  req = urllib.request.Request(url, headers=headers, method="GET")
61
- with urllib.request.urlopen(req, timeout=timeout) as r:
62
- return json.loads(r.read().decode())
63
+ for i, wait in enumerate(list(retries) + [None]):
64
+ try:
65
+ with urllib.request.urlopen(req, timeout=timeout) as r:
66
+ return json.loads(r.read().decode())
67
+ except urllib.error.HTTPError:
68
+ raise
69
+ except (urllib.error.URLError, OSError) as e:
70
+ if wait is None:
71
+ raise
72
+ sys.stderr.write(WARN(" network: %s -- retry %d in %ds\n" % (str(e)[:60], i + 1, wait)))
73
+ time.sleep(wait)
63
74
 
64
75
  def norm(s):
65
76
  return s.lower().replace(".", "-").replace("_", "-")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trantor",
3
- "version": "0.17.78",
3
+ "version": "0.17.79",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "trantor": "bin/cli.mjs"
@@ -11,7 +11,7 @@
11
11
  "zod": "^4.4.3"
12
12
  },
13
13
  "scripts": {
14
- "test": "node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-handoff.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-balances.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
14
+ "test": "node bin/slop-gate.mjs --surface desktop/src && node test.mjs && node test-scenarios.mjs && node test-failure.mjs && node test-redelivery.mjs && node test-handoff.mjs && node test-agents.mjs && node test-update.mjs && node test-handoff-guard.mjs && node test-balances.mjs && node test-subagent-cost.mjs && node test-inbox.mjs && node test-inflight.mjs && node test-notify.mjs && node test-focus.mjs && node test-cardlog.mjs && node test-relay-note.mjs && node test-reaper.mjs && node test-events.mjs && node test-proposals.mjs && node test-scrub.mjs && node test-store-delta.mjs && node test-claims.mjs && node test-adopt.mjs && node test-summarize.mjs && node test-identity-core.mjs && node test-identity.mjs && node test-identity-instances.mjs && node test-duty.mjs && node test-discovery.mjs && node test-doctor.mjs && node test-splitbrain.mjs && node test-overseer.mjs && node test-overseer-lib.mjs && node test-overseer-warn.mjs && node test-provider-keys.mjs && node test-inbox-delivery.mjs && node test-hub-routing.mjs && node test-hook-routing.mjs && node test-relay-wait.mjs && node test-dsh-seat.mjs && node test-kimi-bridge.mjs && node test-kimi-events.mjs && python3 engine/test-routing.py && node test-reconcile.mjs && node test-resources.mjs && node test-patrol.mjs && node test-bridge.mjs && bash test-crew.sh"
15
15
  },
16
16
  "description": "The hub-world for AI agent crews \u2014 orchestrate Claude Code, Codex, GLM, Kimi, DeepSeek & any OpenRouter model as live crews with a plan-aware Advisor, a Kanban/flow command center, a testing gate, and an economics brain (Scrooge).",
17
17
  "files": [