@geonosis/ratchet 2.5.3 → 2.6.1
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/CHANGELOG.md +48 -0
- package/dist/{chunk-WENVCC67.js → chunk-GJD2676B.js} +114 -62
- package/dist/cli.js +3 -1
- package/dist/index.d.ts +27 -16
- package/dist/index.js +11 -1
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,53 @@
|
|
|
1
1
|
# @geonosis/ratchet
|
|
2
2
|
|
|
3
|
+
## 2.6.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e1db50f: `geonosis-ratchet` reads `geonosis.ratchet.json` BEFORE it queues for a heavy slot. The slot
|
|
8
|
+
directory is machine-wide — it bounds heavy work on the box, not per repo — so a second repo's
|
|
9
|
+
`geonosis-verify` holds it legitimately, and a run with nothing to count was spending the resource
|
|
10
|
+
that bounds heavy work in order to discover it had no heavy work, waiting `GEONOSIS_HEAVY_WAIT_MS`
|
|
11
|
+
(20 minutes by default) to then exit 2. Measured 2026-09-07: with during.day's verify holding the
|
|
12
|
+
only slot on a memory-clamped one-slot box, this kit's own
|
|
13
|
+
`registry.test.ts > passes the child bin exit code through verbatim` — which spawns a ratchet into a
|
|
14
|
+
config-less directory expecting an instant refusal — sat for 979,800 ms against a 60,000 ms timeout,
|
|
15
|
+
three runs running. It now refuses in about a second with every slot held, and a bin case holds it
|
|
16
|
+
there: a config-less run must name `geonosis.ratchet.json` and must never say it is waiting.
|
|
17
|
+
- db89b0b: Two Stop-hook defects, both caught by making the instrument go red once. A heavy slot is inherited down a process tree: the holder marks its environment with the slot's path, and a descendant whose mark names a living ancestor's slot takes none of its own — a ratchet inside a verify waited on the slot its parent held, for ever, on a one-slot runner (CI's), and now waits on nobody; a tool that takes twice in one process still gets two. The Stop hook judges a run over a dirty tree: a tree's identity is HEAD plus every change on it, the hook's own files aside; a run launched over that identity is read at the next turn end and not re-run while the tree stands, where before an uncommitted edit meant every turn end launched the tier again and none was judged. The kit's own fast tier now builds first, so the hook never judges bins whose `dist` is older than the `src` under test.
|
|
18
|
+
|
|
19
|
+
## 2.6.0
|
|
20
|
+
|
|
21
|
+
### Minor Changes
|
|
22
|
+
|
|
23
|
+
- 58565d7: Plan 029, the factory floor: the gate learns to run things at once, and the heavy budget stops
|
|
24
|
+
being one laptop's number.
|
|
25
|
+
|
|
26
|
+
`@geonosis/verify` — `TierStep` carries `group`, the number that says which steps run at once, so a
|
|
27
|
+
tier can declare `{ "parallel": [ … ] }` groups and `{ "command": "… {workspace} …", "per":
|
|
28
|
+
"workspace" }` steps. New: `affected.ts` (`affected`, `affectedOf`, `workspacesIn`, `changedSince`,
|
|
29
|
+
`mergeBase`, `ROOT_SHAPED`, types `Affected` and `Workspace`), `PerWorkspace`, `ParallelStep`,
|
|
30
|
+
`CommandStep` on `config`, and `planSteps`, `runGroups`, `shellRunAsync`, `PlannedStep`,
|
|
31
|
+
`AsyncStepRun` on `run`. `GateReport` gains an optional `affected` block. `runSteps`, `shellRun` and
|
|
32
|
+
`stepIds` are unchanged. The bin gains `--affected`, `--since <ref>` and `--jobs <n>`.
|
|
33
|
+
|
|
34
|
+
`@geonosis/ratchet` and `@geonosis/release` — `heavySlots()` and `DEFAULT_HEAVY_SLOTS` beside
|
|
35
|
+
`HEAVY_SLOTS`, which is no longer the constant 2: the budget is `GEONOSIS_HEAVY_SLOTS` or
|
|
36
|
+
`availableParallelism() - 1`, read on every claim rather than at module load (D-073).
|
|
37
|
+
|
|
38
|
+
`@geonosis/ledger` — `shape.ts`: `shapeCheck`, `readManifest`, `exportsOf`, `formatShape` and the
|
|
39
|
+
types `BriefManifest`, `ShapeRefusal`, `ShapeOutcome` — the lock that refuses an engineer diff
|
|
40
|
+
touching a locked test, renaming an export or adding a file the brief did not name (D-072). Not yet
|
|
41
|
+
on the package's public entry: the CLI door lands with W115's dispatch unit.
|
|
42
|
+
|
|
43
|
+
`geonosis` — the plugin's post-edit hook takes its formatter from `geonosis.json → stack.format`
|
|
44
|
+
(D-070) and the stop hook reads the machine's heavy budget.
|
|
45
|
+
- 58565d7: The heavy budget is memory-aware (D-081, amending D-073). `heavyBudget()` — on `@geonosis/ratchet`'s door, copied into `@geonosis/release` and the plugin's Stop hook and held to one answer by a test — is the smaller of one less than the cores and the memory the machine can spend divided by what one run holds: `geonosis.json → stack.memoryMbPerSlot`, 512 MB when nothing is declared. `GEONOSIS_HEAVY_SLOTS` still wins outright; `GEONOSIS_HEAVY_MEMORY_MB` is the memory to spend instead of the machine's. `heavySlots()` is the budget's number read fresh, and `describeBudget()` names the number and both inputs. `geonosis-verify --jobs` defaults to it, its report carries it as `heavy`, and the run prints it; the kit's own vitest workers read it too. Measured on a 4-core, 3.4 GB box: three slots over 1.4 GB swapped; two do not.
|
|
46
|
+
|
|
47
|
+
### Patch Changes
|
|
48
|
+
|
|
49
|
+
- 58565d7: The oxlint counters read the graphical format's Unicode markers — `×` for an error, `⚠` for a warning — beside the ASCII `x` and `!` a pipe gets. CI's terminal printed the Unicode pair, `oxlintRule` saw two findings it could attribute to no rule, and refused as its contract says; a laptop's vitest child never printed them.
|
|
50
|
+
|
|
3
51
|
## 2.5.3
|
|
4
52
|
|
|
5
53
|
## 2.5.2
|
|
@@ -1,6 +1,51 @@
|
|
|
1
|
+
// src/core/config.ts
|
|
2
|
+
import { existsSync, readFileSync } from "fs";
|
|
3
|
+
import { resolve } from "path";
|
|
4
|
+
var CONFIG_FILE = "geonosis.ratchet.json";
|
|
5
|
+
var keyOf = (entry) => entry.key ?? entry.counter;
|
|
6
|
+
var loadConfig = (cwd) => {
|
|
7
|
+
const path = resolve(cwd, CONFIG_FILE);
|
|
8
|
+
if (!existsSync(path)) {
|
|
9
|
+
throw new Error(`no ${CONFIG_FILE} in ${cwd} \u2014 the ratchet has nothing to count`);
|
|
10
|
+
}
|
|
11
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
12
|
+
if (!Array.isArray(parsed.counters)) {
|
|
13
|
+
throw new Error(`${CONFIG_FILE} has no "counters" array`);
|
|
14
|
+
}
|
|
15
|
+
for (const entry of parsed.counters) {
|
|
16
|
+
if (typeof entry.counter !== "string") {
|
|
17
|
+
throw new Error(`${CONFIG_FILE}: every counter entry needs a "counter" id`);
|
|
18
|
+
}
|
|
19
|
+
if (entry.tiers !== void 0) {
|
|
20
|
+
const named = Array.isArray(entry.tiers) && entry.tiers.length > 0 && entry.tiers.every((tier) => typeof tier === "string" && tier.length > 0);
|
|
21
|
+
if (!named) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`${CONFIG_FILE}: "${keyOf(entry)}" has "tiers" that is not a non-empty list of tier names`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const keys = parsed.counters.map(keyOf);
|
|
29
|
+
const duplicate = keys.find((key, at) => keys.indexOf(key) !== at);
|
|
30
|
+
if (duplicate !== void 0) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`${CONFIG_FILE}: two counters both write "${duplicate}" \u2014 give one of them a distinct "key"`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
return { baseline: parsed.baseline ?? "gate-baseline.json", counters: parsed.counters };
|
|
36
|
+
};
|
|
37
|
+
var resolveBaseline = (cwd) => {
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(readFileSync(resolve(cwd, CONFIG_FILE), "utf8"));
|
|
40
|
+
return typeof parsed.baseline === "string" ? parsed.baseline : "gate-baseline.json";
|
|
41
|
+
} catch {
|
|
42
|
+
return "gate-baseline.json";
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
1
46
|
// src/core/envelope.ts
|
|
2
|
-
import { mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
3
|
-
import { dirname, join, resolve } from "path";
|
|
47
|
+
import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
48
|
+
import { dirname, join, resolve as resolve2 } from "path";
|
|
4
49
|
import { fileURLToPath } from "url";
|
|
5
50
|
var ENVELOPES_DIR = ".geonosis/envelopes";
|
|
6
51
|
var envelopePath = (root, tool) => join(root, ENVELOPES_DIR, `${tool}.json`);
|
|
@@ -15,7 +60,7 @@ var unbalancedMessage = (envelope, next) => `${envelope.tool}: considered ${enve
|
|
|
15
60
|
var FORBIDDEN_ROOT = "GEONOSIS_ENVELOPES_FORBIDDEN_ROOT";
|
|
16
61
|
var refuseForbiddenRoot = (root) => {
|
|
17
62
|
const forbidden = process.env[FORBIDDEN_ROOT];
|
|
18
|
-
if (forbidden === void 0 ||
|
|
63
|
+
if (forbidden === void 0 || resolve2(forbidden) !== resolve2(root)) return;
|
|
19
64
|
throw new UnbalancedEnvelope(
|
|
20
65
|
`${root} is off limits to envelope writers in this process (${FORBIDDEN_ROOT}) \u2014 a run that writes one into a shared root races every other run reading it, and leaves a file the next one takes for real. Point this at a scratch root of its own: tooling/scratch-dir.ts.`
|
|
21
66
|
);
|
|
@@ -53,7 +98,7 @@ var writeEnvelope = ({
|
|
|
53
98
|
var UNKNOWN = "unknown";
|
|
54
99
|
var versionIn = (dir) => {
|
|
55
100
|
try {
|
|
56
|
-
const manifest = JSON.parse(
|
|
101
|
+
const manifest = JSON.parse(readFileSync2(join(dir, "package.json"), "utf8"));
|
|
57
102
|
return typeof manifest.version === "string" ? manifest.version : void 0;
|
|
58
103
|
} catch {
|
|
59
104
|
return void 0;
|
|
@@ -71,8 +116,8 @@ var versionOf = (moduleUrl) => {
|
|
|
71
116
|
};
|
|
72
117
|
|
|
73
118
|
// src/core/heavy-slot.ts
|
|
74
|
-
import { readFileSync as
|
|
75
|
-
import { tmpdir } from "os";
|
|
119
|
+
import { readFileSync as readFileSync3, rmSync as rmSync2 } from "fs";
|
|
120
|
+
import { availableParallelism, freemem, tmpdir } from "os";
|
|
76
121
|
import { join as join2 } from "path";
|
|
77
122
|
|
|
78
123
|
// src/core/file-claim.ts
|
|
@@ -104,7 +149,42 @@ var claimFile = (path, holder) => {
|
|
|
104
149
|
};
|
|
105
150
|
|
|
106
151
|
// src/core/heavy-slot.ts
|
|
107
|
-
var
|
|
152
|
+
var availableMemoryMb = () => {
|
|
153
|
+
try {
|
|
154
|
+
const found = /^MemAvailable:\s+(\d+) kB/m.exec(readFileSync3("/proc/meminfo", "utf8"));
|
|
155
|
+
if (found?.[1] !== void 0) return Math.floor(Number(found[1]) / 1024);
|
|
156
|
+
} catch {
|
|
157
|
+
}
|
|
158
|
+
return Math.floor(freemem() / 1048576);
|
|
159
|
+
};
|
|
160
|
+
var DEFAULT_MEMORY_MB_PER_SLOT = 512;
|
|
161
|
+
var DEFAULT_HEAVY_SLOTS = Math.max(1, availableParallelism() - 1);
|
|
162
|
+
var perSlotMbDeclaredIn = (root) => {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(readFileSync3(join2(root, "geonosis.json"), "utf8"));
|
|
165
|
+
const declared = parsed.stack?.memoryMbPerSlot;
|
|
166
|
+
return typeof declared === "number" && Number.isFinite(declared) && declared > 0 ? declared : void 0;
|
|
167
|
+
} catch {
|
|
168
|
+
return void 0;
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
var heavyBudget = (input = {}) => {
|
|
172
|
+
const declared = Number(process.env.GEONOSIS_HEAVY_SLOTS ?? "");
|
|
173
|
+
const spendable = Number(process.env.GEONOSIS_HEAVY_MEMORY_MB ?? "");
|
|
174
|
+
const reading = {
|
|
175
|
+
availableMemoryMb: input.availableMemoryMb ?? (Number.isFinite(spendable) && spendable > 0 ? spendable : availableMemoryMb()),
|
|
176
|
+
cores: input.cores ?? availableParallelism(),
|
|
177
|
+
perSlotMb: input.perSlotMb ?? perSlotMbDeclaredIn(input.root ?? process.cwd()) ?? DEFAULT_MEMORY_MB_PER_SLOT
|
|
178
|
+
};
|
|
179
|
+
if (Number.isInteger(declared) && declared > 0)
|
|
180
|
+
return { ...reading, by: "declared", slots: declared };
|
|
181
|
+
const byCores = Math.max(1, reading.cores - 1);
|
|
182
|
+
const byMemory = Math.max(1, Math.floor(reading.availableMemoryMb / reading.perSlotMb));
|
|
183
|
+
return byMemory < byCores ? { ...reading, by: "memory", slots: byMemory } : { ...reading, by: "cores", slots: byCores };
|
|
184
|
+
};
|
|
185
|
+
var describeBudget = (budget) => `${budget.slots} heavy slot(s), bound by ${budget.by === "declared" ? "GEONOSIS_HEAVY_SLOTS" : budget.by} \u2014 ${budget.cores} cores, ${budget.availableMemoryMb} MB available, ${budget.perSlotMb} MB per slot`;
|
|
186
|
+
var heavySlots = () => heavyBudget().slots;
|
|
187
|
+
var HEAVY_SLOTS = heavySlots();
|
|
108
188
|
var DEFAULT_WAIT_MS = 20 * 6e4;
|
|
109
189
|
var heavySlotDir = () => {
|
|
110
190
|
const named = process.env.GEONOSIS_HEAVY_SLOT_DIR;
|
|
@@ -114,7 +194,7 @@ var heavySlotDir = () => {
|
|
|
114
194
|
var slotPath = (dir, at) => join2(dir, `slot-${at}.json`);
|
|
115
195
|
var holderOf = (path) => {
|
|
116
196
|
try {
|
|
117
|
-
const parsed = JSON.parse(
|
|
197
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
118
198
|
return typeof parsed.pid === "number" ? {
|
|
119
199
|
command: parsed.command ?? "unknown",
|
|
120
200
|
cwd: parsed.cwd ?? "somewhere",
|
|
@@ -130,12 +210,19 @@ var reapIfDead = (path) => {
|
|
|
130
210
|
if (holder !== void 0 && !alive(holder.pid)) rmSync2(path, { force: true });
|
|
131
211
|
};
|
|
132
212
|
var claim = (path, holder) => claimFile(path, holder);
|
|
133
|
-
var namedHolders = (dir) => Array.from({ length:
|
|
213
|
+
var namedHolders = (dir) => Array.from({ length: heavySlots() }, (_unused, at) => holderOf(slotPath(dir, at))).filter(
|
|
134
214
|
(holder) => holder !== void 0
|
|
135
215
|
);
|
|
136
216
|
var describeHolders = (holders) => holders.map((one) => `${one.pid} ${one.command} since ${one.startedAt}`).join(", ");
|
|
137
217
|
var HeavySlotTimeout = class extends Error {
|
|
138
218
|
};
|
|
219
|
+
var HEAVY_SLOT_HELD = "GEONOSIS_HEAVY_SLOT_HELD";
|
|
220
|
+
var inherited = () => {
|
|
221
|
+
const named = process.env[HEAVY_SLOT_HELD];
|
|
222
|
+
if (named === void 0 || named === "") return false;
|
|
223
|
+
const holder = holderOf(named);
|
|
224
|
+
return holder !== void 0 && holder.pid !== process.pid && alive(holder.pid);
|
|
225
|
+
};
|
|
139
226
|
var takeSlot = async ({
|
|
140
227
|
command,
|
|
141
228
|
cwd = process.cwd(),
|
|
@@ -145,10 +232,11 @@ var takeSlot = async ({
|
|
|
145
232
|
`),
|
|
146
233
|
waitMs = Number(process.env.GEONOSIS_HEAVY_WAIT_MS ?? "") || DEFAULT_WAIT_MS
|
|
147
234
|
}) => {
|
|
235
|
+
if (inherited()) return () => void 0;
|
|
148
236
|
const until = Date.now() + waitMs;
|
|
149
237
|
let told = false;
|
|
150
238
|
for (; ; ) {
|
|
151
|
-
for (let at = 0; at <
|
|
239
|
+
for (let at = 0; at < heavySlots(); at += 1) {
|
|
152
240
|
const path = slotPath(dir, at);
|
|
153
241
|
reapIfDead(path);
|
|
154
242
|
const mine = {
|
|
@@ -158,8 +246,10 @@ var takeSlot = async ({
|
|
|
158
246
|
startedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
159
247
|
};
|
|
160
248
|
if (claim(path, mine)) {
|
|
249
|
+
process.env[HEAVY_SLOT_HELD] = path;
|
|
161
250
|
return () => {
|
|
162
251
|
if (holderOf(path)?.pid === process.pid) rmSync2(path, { force: true });
|
|
252
|
+
if (process.env[HEAVY_SLOT_HELD] === path) delete process.env[HEAVY_SLOT_HELD];
|
|
163
253
|
};
|
|
164
254
|
}
|
|
165
255
|
}
|
|
@@ -178,7 +268,7 @@ var takeSlot = async ({
|
|
|
178
268
|
};
|
|
179
269
|
|
|
180
270
|
// src/core/lock.ts
|
|
181
|
-
import { readFileSync as
|
|
271
|
+
import { readFileSync as readFileSync4, rmSync as rmSync3 } from "fs";
|
|
182
272
|
import { homedir } from "os";
|
|
183
273
|
import { isAbsolute, join as join3 } from "path";
|
|
184
274
|
var heavyLockPath = () => {
|
|
@@ -190,7 +280,7 @@ var heavyLockPath = () => {
|
|
|
190
280
|
};
|
|
191
281
|
var holderOf2 = (path) => {
|
|
192
282
|
try {
|
|
193
|
-
const parsed = JSON.parse(
|
|
283
|
+
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
194
284
|
return typeof parsed.pid === "number" ? {
|
|
195
285
|
cwd: parsed.cwd ?? "somewhere",
|
|
196
286
|
pid: parsed.pid,
|
|
@@ -245,51 +335,6 @@ var acquireExclusive = async ({
|
|
|
245
335
|
}
|
|
246
336
|
};
|
|
247
337
|
|
|
248
|
-
// src/core/config.ts
|
|
249
|
-
import { existsSync, readFileSync as readFileSync4 } from "fs";
|
|
250
|
-
import { resolve as resolve2 } from "path";
|
|
251
|
-
var CONFIG_FILE = "geonosis.ratchet.json";
|
|
252
|
-
var keyOf = (entry) => entry.key ?? entry.counter;
|
|
253
|
-
var loadConfig = (cwd) => {
|
|
254
|
-
const path = resolve2(cwd, CONFIG_FILE);
|
|
255
|
-
if (!existsSync(path)) {
|
|
256
|
-
throw new Error(`no ${CONFIG_FILE} in ${cwd} \u2014 the ratchet has nothing to count`);
|
|
257
|
-
}
|
|
258
|
-
const parsed = JSON.parse(readFileSync4(path, "utf8"));
|
|
259
|
-
if (!Array.isArray(parsed.counters)) {
|
|
260
|
-
throw new Error(`${CONFIG_FILE} has no "counters" array`);
|
|
261
|
-
}
|
|
262
|
-
for (const entry of parsed.counters) {
|
|
263
|
-
if (typeof entry.counter !== "string") {
|
|
264
|
-
throw new Error(`${CONFIG_FILE}: every counter entry needs a "counter" id`);
|
|
265
|
-
}
|
|
266
|
-
if (entry.tiers !== void 0) {
|
|
267
|
-
const named = Array.isArray(entry.tiers) && entry.tiers.length > 0 && entry.tiers.every((tier) => typeof tier === "string" && tier.length > 0);
|
|
268
|
-
if (!named) {
|
|
269
|
-
throw new Error(
|
|
270
|
-
`${CONFIG_FILE}: "${keyOf(entry)}" has "tiers" that is not a non-empty list of tier names`
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
const keys = parsed.counters.map(keyOf);
|
|
276
|
-
const duplicate = keys.find((key, at) => keys.indexOf(key) !== at);
|
|
277
|
-
if (duplicate !== void 0) {
|
|
278
|
-
throw new Error(
|
|
279
|
-
`${CONFIG_FILE}: two counters both write "${duplicate}" \u2014 give one of them a distinct "key"`
|
|
280
|
-
);
|
|
281
|
-
}
|
|
282
|
-
return { baseline: parsed.baseline ?? "gate-baseline.json", counters: parsed.counters };
|
|
283
|
-
};
|
|
284
|
-
var resolveBaseline = (cwd) => {
|
|
285
|
-
try {
|
|
286
|
-
const parsed = JSON.parse(readFileSync4(resolve2(cwd, CONFIG_FILE), "utf8"));
|
|
287
|
-
return typeof parsed.baseline === "string" ? parsed.baseline : "gate-baseline.json";
|
|
288
|
-
} catch {
|
|
289
|
-
return "gate-baseline.json";
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
|
|
293
338
|
// src/core/types.ts
|
|
294
339
|
var CounterError = class extends Error {
|
|
295
340
|
constructor(counter, message) {
|
|
@@ -1168,13 +1213,15 @@ var UNIX_FINDING = /^\S[^\n]*:\d+:\d+: .*\[(Error|Warning)\/[^\]\n]+\]$/gm;
|
|
|
1168
1213
|
var UNIX_SUMMARY = /^(\d+) problems?$/m;
|
|
1169
1214
|
var AGENT_FINDING = /^\S[^\n]*:\d+:\d+: (error|warning) /gm;
|
|
1170
1215
|
var DEFAULT_SUMMARY = /^Found (\d+) warnings? and (\d+) errors?\.$/m;
|
|
1171
|
-
var DEFAULT_FINDING = /^\s*([x
|
|
1216
|
+
var DEFAULT_FINDING = /^\s*([x!×⚠]) [^\s(]+\([^)\n]+\): /m;
|
|
1172
1217
|
var FINDING_SHAPES = [UNIX_FINDING, AGENT_FINDING, DEFAULT_FINDING].map(
|
|
1173
1218
|
(shape) => new RegExp(shape.source)
|
|
1174
1219
|
);
|
|
1175
1220
|
var RULE_TOKEN = /\([^()\n]+\)/;
|
|
1176
1221
|
var SEVERITY_BY_TOKEN = {
|
|
1177
1222
|
"!": "warning",
|
|
1223
|
+
"\xD7": "error",
|
|
1224
|
+
"\u26A0": "warning",
|
|
1178
1225
|
Error: "error",
|
|
1179
1226
|
error: "error",
|
|
1180
1227
|
Warning: "warning",
|
|
@@ -2179,11 +2226,20 @@ var formatProve = ({ proofs, proven }) => {
|
|
|
2179
2226
|
};
|
|
2180
2227
|
|
|
2181
2228
|
export {
|
|
2229
|
+
CONFIG_FILE,
|
|
2230
|
+
keyOf,
|
|
2231
|
+
loadConfig,
|
|
2232
|
+
resolveBaseline,
|
|
2182
2233
|
ENVELOPES_DIR,
|
|
2183
2234
|
envelopePath,
|
|
2184
2235
|
UnbalancedEnvelope,
|
|
2185
2236
|
writeEnvelope,
|
|
2186
2237
|
versionOf,
|
|
2238
|
+
availableMemoryMb,
|
|
2239
|
+
DEFAULT_MEMORY_MB_PER_SLOT,
|
|
2240
|
+
heavyBudget,
|
|
2241
|
+
describeBudget,
|
|
2242
|
+
heavySlots,
|
|
2187
2243
|
HEAVY_SLOTS,
|
|
2188
2244
|
heavySlotDir,
|
|
2189
2245
|
describeHolders,
|
|
@@ -2191,10 +2247,6 @@ export {
|
|
|
2191
2247
|
takeSlot,
|
|
2192
2248
|
heavyLockPath,
|
|
2193
2249
|
acquireExclusive,
|
|
2194
|
-
CONFIG_FILE,
|
|
2195
|
-
keyOf,
|
|
2196
|
-
loadConfig,
|
|
2197
|
-
resolveBaseline,
|
|
2198
2250
|
CounterError,
|
|
2199
2251
|
runCommand,
|
|
2200
2252
|
runProve,
|
package/dist/cli.js
CHANGED
|
@@ -3,12 +3,13 @@ import {
|
|
|
3
3
|
acquireExclusive,
|
|
4
4
|
formatProve,
|
|
5
5
|
formatReport,
|
|
6
|
+
loadConfig,
|
|
6
7
|
runProve,
|
|
7
8
|
runRatchet,
|
|
8
9
|
takeSlot,
|
|
9
10
|
versionOf,
|
|
10
11
|
writeEnvelope
|
|
11
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-GJD2676B.js";
|
|
12
13
|
|
|
13
14
|
// src/cli.ts
|
|
14
15
|
import process from "process";
|
|
@@ -132,6 +133,7 @@ var measure = async () => {
|
|
|
132
133
|
` : formatProve(proof));
|
|
133
134
|
return proof.proven ? 0 : 2;
|
|
134
135
|
}
|
|
136
|
+
loadConfig(cwd);
|
|
135
137
|
releaseHeavySlot = await takeSlot({
|
|
136
138
|
command: `geonosis-ratchet${tier === void 0 ? "" : ` --tier ${tier}`}`,
|
|
137
139
|
cwd
|
package/dist/index.d.ts
CHANGED
|
@@ -288,33 +288,44 @@ declare const versionOf: (moduleUrl: string) => string;
|
|
|
288
288
|
declare const COUNTERS: Counter[];
|
|
289
289
|
declare const counterById: (id: string) => Counter;
|
|
290
290
|
|
|
291
|
-
/** Who is spending a heavy slot, running what, since when. */
|
|
292
291
|
type HeavySlotHolder = {
|
|
293
292
|
command: string;
|
|
294
293
|
cwd: string;
|
|
295
294
|
pid: number;
|
|
296
295
|
startedAt: string;
|
|
297
296
|
};
|
|
297
|
+
/** The memory the machine can spend now: `MemAvailable` where the kernel says it, `os.freemem()` where it does not. */
|
|
298
|
+
declare const availableMemoryMb: () => number;
|
|
299
|
+
/** What one heavy run is budgeted to hold when the repo declares nothing: a vitest worker measured 334 MB and a tsc 224 MB (plan 030). */
|
|
300
|
+
declare const DEFAULT_MEMORY_MB_PER_SLOT = 512;
|
|
301
|
+
type HeavyBudget = {
|
|
302
|
+
availableMemoryMb: number;
|
|
303
|
+
/** What bound the number: a declared `GEONOSIS_HEAVY_SLOTS`, the cores, or the memory. */
|
|
304
|
+
by: 'cores' | 'declared' | 'memory';
|
|
305
|
+
cores: number;
|
|
306
|
+
perSlotMb: number;
|
|
307
|
+
slots: number;
|
|
308
|
+
};
|
|
298
309
|
/**
|
|
299
|
-
*
|
|
300
|
-
*
|
|
301
|
-
*
|
|
302
|
-
*
|
|
310
|
+
* The heavy budget, read fresh: the smaller of one less than the cores and the memory divided by
|
|
311
|
+
* what one run holds (D-081, amending D-073). A declared `GEONOSIS_HEAVY_SLOTS` wins over both;
|
|
312
|
+
* `GEONOSIS_HEAVY_MEMORY_MB` is the memory to spend instead of the machine's; the per-slot number
|
|
313
|
+
* is `geonosis.json → stack.memoryMbPerSlot`, 512 when nothing is declared.
|
|
303
314
|
*/
|
|
304
|
-
declare const
|
|
315
|
+
declare const heavyBudget: (input?: {
|
|
316
|
+
availableMemoryMb?: number;
|
|
317
|
+
cores?: number;
|
|
318
|
+
perSlotMb?: number;
|
|
319
|
+
root?: string;
|
|
320
|
+
}) => HeavyBudget;
|
|
321
|
+
/** One line a report can carry: the number, and both inputs it was read from. */
|
|
322
|
+
declare const describeBudget: (budget: HeavyBudget) => string;
|
|
323
|
+
declare const heavySlots: () => number;
|
|
324
|
+
declare const HEAVY_SLOTS: number;
|
|
305
325
|
declare const heavySlotDir: () => string;
|
|
306
326
|
declare const describeHolders: (holders: HeavySlotHolder[]) => string;
|
|
307
|
-
/** A wait that ran out. The message names every holder, because the caller's own refusal is this text. */
|
|
308
327
|
declare class HeavySlotTimeout extends Error {
|
|
309
328
|
}
|
|
310
|
-
/**
|
|
311
|
-
* Claims one of the two machine-wide heavy slots, waiting for whoever holds them.
|
|
312
|
-
*
|
|
313
|
-
* A dead holder is reaped before a slot is tried, so a killed run never wedges the budget for
|
|
314
|
-
* whoever asks next. Both held is announced ONCE — not on every poll, which would turn a half-hour
|
|
315
|
-
* wait into a half hour of identical lines — and the wait is bounded, so a taker that can never get
|
|
316
|
-
* in refuses instead of hanging the process that called it.
|
|
317
|
-
*/
|
|
318
329
|
declare const takeSlot: ({ command, cwd, dir, pollMs, say, waitMs, }: {
|
|
319
330
|
command: string;
|
|
320
331
|
cwd?: string;
|
|
@@ -413,4 +424,4 @@ declare const formatProve: ({ proofs, proven }: ProveResult) => string;
|
|
|
413
424
|
*/
|
|
414
425
|
declare const runCommand: (cwd: string, counterId: string, env?: NodeJS.ProcessEnv) => (command: string) => CommandResult;
|
|
415
426
|
|
|
416
|
-
export { CONFIG_FILE, COUNTERS, type CommandResult, type Counter, type CounterConfig, type CounterContext, CounterError, type CounterProbe, ENVELOPES_DIR, type EnvelopeEntry, HEAVY_SLOTS, type HeavySlotHolder, HeavySlotTimeout, type Holder, type Measurement, type Proof, type ProveResult, type RatchetConfig, type RatchetResult, type Refusal, type ReportEnvelope, UnbalancedEnvelope, type Verdict, acquireExclusive, counterById, describeHolders, envelopePath, formatProve, formatReport, heavyLockPath, heavySlotDir, keyOf, loadConfig, resolveBaseline, runCommand, runProve, runRatchet, takeSlot, versionOf, writeEnvelope };
|
|
427
|
+
export { CONFIG_FILE, COUNTERS, type CommandResult, type Counter, type CounterConfig, type CounterContext, CounterError, type CounterProbe, DEFAULT_MEMORY_MB_PER_SLOT, ENVELOPES_DIR, type EnvelopeEntry, HEAVY_SLOTS, type HeavyBudget, type HeavySlotHolder, HeavySlotTimeout, type Holder, type Measurement, type Proof, type ProveResult, type RatchetConfig, type RatchetResult, type Refusal, type ReportEnvelope, UnbalancedEnvelope, type Verdict, acquireExclusive, availableMemoryMb, counterById, describeBudget, describeHolders, envelopePath, formatProve, formatReport, heavyBudget, heavyLockPath, heavySlotDir, heavySlots, keyOf, loadConfig, resolveBaseline, runCommand, runProve, runRatchet, takeSlot, versionOf, writeEnvelope };
|
package/dist/index.js
CHANGED
|
@@ -2,18 +2,23 @@ import {
|
|
|
2
2
|
CONFIG_FILE,
|
|
3
3
|
COUNTERS,
|
|
4
4
|
CounterError,
|
|
5
|
+
DEFAULT_MEMORY_MB_PER_SLOT,
|
|
5
6
|
ENVELOPES_DIR,
|
|
6
7
|
HEAVY_SLOTS,
|
|
7
8
|
HeavySlotTimeout,
|
|
8
9
|
UnbalancedEnvelope,
|
|
9
10
|
acquireExclusive,
|
|
11
|
+
availableMemoryMb,
|
|
10
12
|
counterById,
|
|
13
|
+
describeBudget,
|
|
11
14
|
describeHolders,
|
|
12
15
|
envelopePath,
|
|
13
16
|
formatProve,
|
|
14
17
|
formatReport,
|
|
18
|
+
heavyBudget,
|
|
15
19
|
heavyLockPath,
|
|
16
20
|
heavySlotDir,
|
|
21
|
+
heavySlots,
|
|
17
22
|
keyOf,
|
|
18
23
|
loadConfig,
|
|
19
24
|
resolveBaseline,
|
|
@@ -23,23 +28,28 @@ import {
|
|
|
23
28
|
takeSlot,
|
|
24
29
|
versionOf,
|
|
25
30
|
writeEnvelope
|
|
26
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-GJD2676B.js";
|
|
27
32
|
export {
|
|
28
33
|
CONFIG_FILE,
|
|
29
34
|
COUNTERS,
|
|
30
35
|
CounterError,
|
|
36
|
+
DEFAULT_MEMORY_MB_PER_SLOT,
|
|
31
37
|
ENVELOPES_DIR,
|
|
32
38
|
HEAVY_SLOTS,
|
|
33
39
|
HeavySlotTimeout,
|
|
34
40
|
UnbalancedEnvelope,
|
|
35
41
|
acquireExclusive,
|
|
42
|
+
availableMemoryMb,
|
|
36
43
|
counterById,
|
|
44
|
+
describeBudget,
|
|
37
45
|
describeHolders,
|
|
38
46
|
envelopePath,
|
|
39
47
|
formatProve,
|
|
40
48
|
formatReport,
|
|
49
|
+
heavyBudget,
|
|
41
50
|
heavyLockPath,
|
|
42
51
|
heavySlotDir,
|
|
52
|
+
heavySlots,
|
|
43
53
|
keyOf,
|
|
44
54
|
loadConfig,
|
|
45
55
|
resolveBaseline,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geonosis/ratchet",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.6.1",
|
|
4
4
|
"types": "./dist/index.d.ts",
|
|
5
5
|
"description": "Debt as a number that may only shrink — one ratchet, pluggable counters.",
|
|
6
6
|
"keywords": [
|
|
@@ -43,6 +43,10 @@
|
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup",
|
|
46
|
-
"typecheck": "tsc --noEmit"
|
|
46
|
+
"typecheck": "tsc --noEmit",
|
|
47
|
+
"lint": "oxlint --config ../../.oxlintrc.json .",
|
|
48
|
+
"test:unit": "vitest run --project unit --root ../.. --passWithNoTests \"$PWD\"",
|
|
49
|
+
"test:bin": "vitest run --project bin --root ../.. --passWithNoTests \"$PWD\"",
|
|
50
|
+
"test:exam": "vitest run --project exam --root ../.. --passWithNoTests \"$PWD\""
|
|
47
51
|
}
|
|
48
52
|
}
|