@geonosis/ratchet 2.5.2 → 2.6.0
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 +34 -0
- package/dist/{chunk-WENVCC67.js → chunk-Z2T6MHHQ.js} +47 -5
- package/dist/cli.js +1 -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,39 @@
|
|
|
1
1
|
# @geonosis/ratchet
|
|
2
2
|
|
|
3
|
+
## 2.6.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 58565d7: Plan 029, the factory floor: the gate learns to run things at once, and the heavy budget stops
|
|
8
|
+
being one laptop's number.
|
|
9
|
+
|
|
10
|
+
`@geonosis/verify` — `TierStep` carries `group`, the number that says which steps run at once, so a
|
|
11
|
+
tier can declare `{ "parallel": [ … ] }` groups and `{ "command": "… {workspace} …", "per":
|
|
12
|
+
"workspace" }` steps. New: `affected.ts` (`affected`, `affectedOf`, `workspacesIn`, `changedSince`,
|
|
13
|
+
`mergeBase`, `ROOT_SHAPED`, types `Affected` and `Workspace`), `PerWorkspace`, `ParallelStep`,
|
|
14
|
+
`CommandStep` on `config`, and `planSteps`, `runGroups`, `shellRunAsync`, `PlannedStep`,
|
|
15
|
+
`AsyncStepRun` on `run`. `GateReport` gains an optional `affected` block. `runSteps`, `shellRun` and
|
|
16
|
+
`stepIds` are unchanged. The bin gains `--affected`, `--since <ref>` and `--jobs <n>`.
|
|
17
|
+
|
|
18
|
+
`@geonosis/ratchet` and `@geonosis/release` — `heavySlots()` and `DEFAULT_HEAVY_SLOTS` beside
|
|
19
|
+
`HEAVY_SLOTS`, which is no longer the constant 2: the budget is `GEONOSIS_HEAVY_SLOTS` or
|
|
20
|
+
`availableParallelism() - 1`, read on every claim rather than at module load (D-073).
|
|
21
|
+
|
|
22
|
+
`@geonosis/ledger` — `shape.ts`: `shapeCheck`, `readManifest`, `exportsOf`, `formatShape` and the
|
|
23
|
+
types `BriefManifest`, `ShapeRefusal`, `ShapeOutcome` — the lock that refuses an engineer diff
|
|
24
|
+
touching a locked test, renaming an export or adding a file the brief did not name (D-072). Not yet
|
|
25
|
+
on the package's public entry: the CLI door lands with W115's dispatch unit.
|
|
26
|
+
|
|
27
|
+
`geonosis` — the plugin's post-edit hook takes its formatter from `geonosis.json → stack.format`
|
|
28
|
+
(D-070) and the stop hook reads the machine's heavy budget.
|
|
29
|
+
- 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.
|
|
30
|
+
|
|
31
|
+
### Patch Changes
|
|
32
|
+
|
|
33
|
+
- 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.
|
|
34
|
+
|
|
35
|
+
## 2.5.3
|
|
36
|
+
|
|
3
37
|
## 2.5.2
|
|
4
38
|
|
|
5
39
|
## 2.5.1
|
|
@@ -72,7 +72,7 @@ var versionOf = (moduleUrl) => {
|
|
|
72
72
|
|
|
73
73
|
// src/core/heavy-slot.ts
|
|
74
74
|
import { readFileSync as readFileSync2, rmSync as rmSync2 } from "fs";
|
|
75
|
-
import { tmpdir } from "os";
|
|
75
|
+
import { availableParallelism, freemem, tmpdir } from "os";
|
|
76
76
|
import { join as join2 } from "path";
|
|
77
77
|
|
|
78
78
|
// src/core/file-claim.ts
|
|
@@ -104,7 +104,42 @@ var claimFile = (path, holder) => {
|
|
|
104
104
|
};
|
|
105
105
|
|
|
106
106
|
// src/core/heavy-slot.ts
|
|
107
|
-
var
|
|
107
|
+
var availableMemoryMb = () => {
|
|
108
|
+
try {
|
|
109
|
+
const found = /^MemAvailable:\s+(\d+) kB/m.exec(readFileSync2("/proc/meminfo", "utf8"));
|
|
110
|
+
if (found?.[1] !== void 0) return Math.floor(Number(found[1]) / 1024);
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
return Math.floor(freemem() / 1048576);
|
|
114
|
+
};
|
|
115
|
+
var DEFAULT_MEMORY_MB_PER_SLOT = 512;
|
|
116
|
+
var DEFAULT_HEAVY_SLOTS = Math.max(1, availableParallelism() - 1);
|
|
117
|
+
var perSlotMbDeclaredIn = (root) => {
|
|
118
|
+
try {
|
|
119
|
+
const parsed = JSON.parse(readFileSync2(join2(root, "geonosis.json"), "utf8"));
|
|
120
|
+
const declared = parsed.stack?.memoryMbPerSlot;
|
|
121
|
+
return typeof declared === "number" && Number.isFinite(declared) && declared > 0 ? declared : void 0;
|
|
122
|
+
} catch {
|
|
123
|
+
return void 0;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var heavyBudget = (input = {}) => {
|
|
127
|
+
const declared = Number(process.env.GEONOSIS_HEAVY_SLOTS ?? "");
|
|
128
|
+
const spendable = Number(process.env.GEONOSIS_HEAVY_MEMORY_MB ?? "");
|
|
129
|
+
const reading = {
|
|
130
|
+
availableMemoryMb: input.availableMemoryMb ?? (Number.isFinite(spendable) && spendable > 0 ? spendable : availableMemoryMb()),
|
|
131
|
+
cores: input.cores ?? availableParallelism(),
|
|
132
|
+
perSlotMb: input.perSlotMb ?? perSlotMbDeclaredIn(input.root ?? process.cwd()) ?? DEFAULT_MEMORY_MB_PER_SLOT
|
|
133
|
+
};
|
|
134
|
+
if (Number.isInteger(declared) && declared > 0)
|
|
135
|
+
return { ...reading, by: "declared", slots: declared };
|
|
136
|
+
const byCores = Math.max(1, reading.cores - 1);
|
|
137
|
+
const byMemory = Math.max(1, Math.floor(reading.availableMemoryMb / reading.perSlotMb));
|
|
138
|
+
return byMemory < byCores ? { ...reading, by: "memory", slots: byMemory } : { ...reading, by: "cores", slots: byCores };
|
|
139
|
+
};
|
|
140
|
+
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`;
|
|
141
|
+
var heavySlots = () => heavyBudget().slots;
|
|
142
|
+
var HEAVY_SLOTS = heavySlots();
|
|
108
143
|
var DEFAULT_WAIT_MS = 20 * 6e4;
|
|
109
144
|
var heavySlotDir = () => {
|
|
110
145
|
const named = process.env.GEONOSIS_HEAVY_SLOT_DIR;
|
|
@@ -130,7 +165,7 @@ var reapIfDead = (path) => {
|
|
|
130
165
|
if (holder !== void 0 && !alive(holder.pid)) rmSync2(path, { force: true });
|
|
131
166
|
};
|
|
132
167
|
var claim = (path, holder) => claimFile(path, holder);
|
|
133
|
-
var namedHolders = (dir) => Array.from({ length:
|
|
168
|
+
var namedHolders = (dir) => Array.from({ length: heavySlots() }, (_unused, at) => holderOf(slotPath(dir, at))).filter(
|
|
134
169
|
(holder) => holder !== void 0
|
|
135
170
|
);
|
|
136
171
|
var describeHolders = (holders) => holders.map((one) => `${one.pid} ${one.command} since ${one.startedAt}`).join(", ");
|
|
@@ -148,7 +183,7 @@ var takeSlot = async ({
|
|
|
148
183
|
const until = Date.now() + waitMs;
|
|
149
184
|
let told = false;
|
|
150
185
|
for (; ; ) {
|
|
151
|
-
for (let at = 0; at <
|
|
186
|
+
for (let at = 0; at < heavySlots(); at += 1) {
|
|
152
187
|
const path = slotPath(dir, at);
|
|
153
188
|
reapIfDead(path);
|
|
154
189
|
const mine = {
|
|
@@ -1168,13 +1203,15 @@ var UNIX_FINDING = /^\S[^\n]*:\d+:\d+: .*\[(Error|Warning)\/[^\]\n]+\]$/gm;
|
|
|
1168
1203
|
var UNIX_SUMMARY = /^(\d+) problems?$/m;
|
|
1169
1204
|
var AGENT_FINDING = /^\S[^\n]*:\d+:\d+: (error|warning) /gm;
|
|
1170
1205
|
var DEFAULT_SUMMARY = /^Found (\d+) warnings? and (\d+) errors?\.$/m;
|
|
1171
|
-
var DEFAULT_FINDING = /^\s*([x
|
|
1206
|
+
var DEFAULT_FINDING = /^\s*([x!×⚠]) [^\s(]+\([^)\n]+\): /m;
|
|
1172
1207
|
var FINDING_SHAPES = [UNIX_FINDING, AGENT_FINDING, DEFAULT_FINDING].map(
|
|
1173
1208
|
(shape) => new RegExp(shape.source)
|
|
1174
1209
|
);
|
|
1175
1210
|
var RULE_TOKEN = /\([^()\n]+\)/;
|
|
1176
1211
|
var SEVERITY_BY_TOKEN = {
|
|
1177
1212
|
"!": "warning",
|
|
1213
|
+
"\xD7": "error",
|
|
1214
|
+
"\u26A0": "warning",
|
|
1178
1215
|
Error: "error",
|
|
1179
1216
|
error: "error",
|
|
1180
1217
|
Warning: "warning",
|
|
@@ -2184,6 +2221,11 @@ export {
|
|
|
2184
2221
|
UnbalancedEnvelope,
|
|
2185
2222
|
writeEnvelope,
|
|
2186
2223
|
versionOf,
|
|
2224
|
+
availableMemoryMb,
|
|
2225
|
+
DEFAULT_MEMORY_MB_PER_SLOT,
|
|
2226
|
+
heavyBudget,
|
|
2227
|
+
describeBudget,
|
|
2228
|
+
heavySlots,
|
|
2187
2229
|
HEAVY_SLOTS,
|
|
2188
2230
|
heavySlotDir,
|
|
2189
2231
|
describeHolders,
|
package/dist/cli.js
CHANGED
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-Z2T6MHHQ.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.0",
|
|
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
|
}
|