@mark1russell7/kind 0.2.1 → 0.2.3

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.
@@ -261,6 +261,36 @@ const deletionsOf = (run) => run.replicas.flatMap((replica) => {
261
261
  }).sort((a, b) => a.deletion - b.deletion);
262
262
  /** This function tells if a prediction is a window for each deleted replica. */
263
263
  const eachOf = (prediction) => prediction.kind === "window" && prediction.each === true;
264
+ /**
265
+ * This function gives how long a run observes after the end reference of its last window, or null
266
+ * when its prediction sets no such time. The judge closes a window `end.band.hi` + δ after its end
267
+ * reference. A request that starts by then ends at most the timeout of the workload later: the
268
+ * client cancels it then. A prediction that is not a window, or a window with no end, sets none.
269
+ */
270
+ const observationAfter = (cell) => {
271
+ const prediction = cell.prediction;
272
+ if (prediction.kind !== "window" || prediction.end === null) return null;
273
+ return seconds(sec(valueIn(prediction.end.band.hi, "ms") + valueIn(cell.workload.timeout, "ms") + NOISE));
274
+ };
275
+ /**
276
+ * This function gives the earliest end of the observation of a run, as a stamp, or null when its
277
+ * prediction sets none (`observationAfter`). It is that time after the latest end reference of a
278
+ * window: of the target, or with `each`, of each replica that got a deletion. A run that ends before
279
+ * it can miss its own failures. In a rolling update of gRPC, a call waits in the queue of its channel
280
+ * for a stalled connect. It fails only at its timeout (kind run 40128267098). A reference that is not
281
+ * in the lines yet does not count.
282
+ */
283
+ const observationEnd = (cell, run) => {
284
+ const after = observationAfter(cell);
285
+ const prediction = cell.prediction;
286
+ if (after === null || prediction.kind !== "window" || prediction.end === null) return null;
287
+ const from = prediction.end.from;
288
+ const references = (prediction.each === true ? run.replicas.map((replica) => replica.pod) : [run.target?.pod]).flatMap((pod) => {
289
+ const at = referenceOf(run, from, pod);
290
+ return at === null ? [] : [at];
291
+ });
292
+ return references.length === 0 ? null : Math.max(...references) + valueIn(after, "ms");
293
+ };
264
294
  /** This function gives the facts of a run that each table reads: its failures by outcome and node, and the times of its target. */
265
295
  const factsOf = (cell, run) => {
266
296
  const lines = run.lines;
@@ -328,15 +358,27 @@ const checkRun = (cell, run) => {
328
358
  if (start === null || prediction.end !== null && end === null) return done(false, `the run has no ${start === null ? prediction.start.from : prediction.end.from} reference${each ? ` for ${pod}` : ""}`, null);
329
359
  const origin = each ? referenceOf(run, "deletion", pod) : facts.deletion ?? (cell.trigger.kind === "crash" ? run.acts.fault : null);
330
360
  const ip = run.replicas.find((replica) => replica.pod === pod)?.ip;
361
+ const propagation = each ? propagationOf(run, origin, ip) : facts.propagation;
331
362
  windows.push({
332
363
  pod,
333
364
  start,
334
365
  end,
335
366
  lo: start + opens.lo * 1e3 - NOISE,
336
367
  hi: end === null ? Number.POSITIVE_INFINITY : end + closes.hi * 1e3 + NOISE,
337
- dropped: droppedAfter(origin, each ? propagationOf(run, origin, ip) : facts.propagation)
368
+ origin,
369
+ propagation,
370
+ dropped: droppedAfter(origin, propagation)
338
371
  });
339
372
  }
373
+ const bound = prediction.propagation === void 0 ? null : secondsOf(prediction.propagation).hi * 1e3 + NOISE;
374
+ const slow = bound === null ? [] : windows.flatMap(({ pod, origin, propagation }) => origin === null ? [] : Object.entries(propagation).flatMap(([node, propagated]) => {
375
+ const by = propagated === null ? null : valueIn(propagated.by, "ms");
376
+ return (by === null ? run.end - origin > bound : by > bound) ? [{
377
+ pod,
378
+ node,
379
+ by
380
+ }] : [];
381
+ }));
340
382
  const relevant = prediction.nodes === "paused" ? failures.filter((request) => request.n === run.paused) : failures;
341
383
  const others = prediction.nodes === "paused" ? failures.filter((request) => request.n !== run.paused) : [];
342
384
  const reuse = prediction.afterReset;
@@ -364,12 +406,22 @@ const checkRun = (cell, run) => {
364
406
  const where = each ? `the windows of ${windows.length} deleted replicas` : "the window";
365
407
  const agrees = reused.length === 0 ? `${relevant.length} failed requests, all in ${where} (span ${span} s)` : `${inside.length} failed requests in ${where} (span ${span} s), and ${reused.length} on a port that a reset closed`;
366
408
  const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join("; "), relevant.length);
367
- if (problems.length > 0 || tail.length === 0) return checked;
368
- const last = tail.at(-1);
369
- const after = tailOf(last);
370
- return {
409
+ if (problems.length > 0) return checked;
410
+ const notes = [];
411
+ if (tail.length > 0) {
412
+ const last = tail.at(-1);
413
+ const after = tailOf(last);
414
+ notes.push(each ? `${tail.length} failed requests after a window, the last ${sec(last.t - after.hi + NOISE)} s after the window of ${after.pod} on ${last.n}, before the rules of ${last.n} dropped ${after.pod}` : `${tail.length} failed requests after the window, the last ${sec(last.t - after.hi + NOISE)} s after it on ${last.n}, before the rules of ${last.n} dropped the target`);
415
+ }
416
+ if (slow.length > 0) {
417
+ const latest = slow.find((late) => late.by === null) ?? slow.reduce((a, b) => b.by > a.by ? b : a);
418
+ const what = each ? latest.pod : "the target";
419
+ const drop = latest.by === null ? `kept ${what} to the end of the run` : `dropped ${what} ${sec(latest.by)} s after its deletion`;
420
+ notes.push(`${slow.length} drops after the propagation of ${secondsOf(prediction.propagation).hi} s at the most, the latest: the rules of ${latest.node} ${drop}`);
421
+ }
422
+ return notes.length === 0 ? checked : {
371
423
  ...checked,
372
- tail: each ? `${tail.length} failed requests after a window, the last ${sec(last.t - after.hi + NOISE)} s after the window of ${after.pod} on ${last.n}, before the rules of ${last.n} dropped ${after.pod}` : `${tail.length} failed requests after the window, the last ${sec(last.t - after.hi + NOISE)} s after it on ${last.n}, before the rules of ${last.n} dropped the target`
424
+ tail: notes.join("; ")
373
425
  };
374
426
  }
375
427
  case "removal": {
@@ -607,7 +659,7 @@ const upgradeTrigger = (trigger) => {
607
659
  const upgradePrediction = (prediction) => {
608
660
  switch (prediction.kind) {
609
661
  case "window": {
610
- const { afterReset, ...rest } = prediction;
662
+ const { afterReset, propagation, ...rest } = prediction;
611
663
  return {
612
664
  ...rest,
613
665
  start: {
@@ -621,7 +673,8 @@ const upgradePrediction = (prediction) => {
621
673
  ...afterReset === void 0 ? {} : { afterReset: {
622
674
  life: seconds(afterReset.life),
623
675
  outcome: afterReset.outcome
624
- } }
676
+ } },
677
+ ...propagation === void 0 ? {} : { propagation: band(propagation) }
625
678
  };
626
679
  }
627
680
  case "removal":
@@ -726,7 +779,7 @@ const atBand = (from, band) => {
726
779
  const predictionText = (prediction) => {
727
780
  switch (prediction.kind) {
728
781
  case "none": return prediction.outcomes === void 0 ? "no failure" : `no ${prediction.outcomes.join(", ")}`;
729
- case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? ", of each deleted replica" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}`;
782
+ case "window": return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? "" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? ", of each deleted replica" : ""}${prediction.nodes === "paused" ? ", on the paused node" : ""}${prediction.certain ? ", at least one" : ""}${prediction.afterReset === void 0 ? "" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === void 0 ? "" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}`;
730
783
  case "removal": return `Service empty in ${showBand(prediction.band)}`;
731
784
  case "leaves": return `the replica leaves in ${showBand(prediction.band)}`;
732
785
  case "restarts": return prediction.band === null ? "no restart" : `each replica SIGTERM in ${showBand(prediction.band)}`;
@@ -984,6 +1037,7 @@ const recordOf = (a, main) => {
984
1037
  outcome: z.string()
985
1038
  }).optional(),
986
1039
  each: z.boolean().optional(),
1040
+ propagation: band.optional(),
987
1041
  why: z.string()
988
1042
  }),
989
1043
  z.object({
@@ -1131,6 +1185,6 @@ const readRecord = (data) => {
1131
1185
  throw new Error(`a kind cell record has the format ${FORMAT} or ${FORMAT_V1}, not ${String(format)}`);
1132
1186
  };
1133
1187
  //#endregion
1134
- export { HEALTH_PORT as $, byClients as A, podsOf as B, reusesReset as C, middle as D, medianInterval as E, failuresOf as F, signalOf as G, requestsOf as H, intakeOf as I, sliceEmptyOf as J, signalsOf as K, kernelCounts as L, deletionOf as M, dropOf as N, quantileOf as O, emptyOf as P, CLIENT as Q, outcomesOf as R, referenceOf as S, RELATIVE as T, restartsAt as U, recordsOf as V, rulesOf as W, stoppingOf as X, slicesOf as Y, transitionOf as Z, checkRun as _, calibrationOf as a, seconds as at, judgeKind as b, predictionText as c, FORMAT_V1 as d, LOG_LABEL as et, judge as f, LAST_SLACK as g, KIND_NOISE as h, readRecord as i, bandOf as it, byNode as j, backendsAt as k, timeSpread as l, upgradeCell as m, kindCellRecordSchema as n, REDIS as nt, cellsTable as o, secondsOf as ot, upgrade as p, sliceDropOf as q, kindCellRecordV1Schema as r, SERVER as rt, medianText as s, showBand as st, STAGES as t, PORT as tt, FORMAT as u, deletionsOf as v, unitOf as w, propagationOf as x, factsOf as y, podsAt as z };
1188
+ export { transitionOf as $, quantileOf as A, outcomesOf as B, propagationOf as C, RELATIVE as D, unitOf as E, dropOf as F, restartsAt as G, podsOf as H, emptyOf as I, signalsOf as J, rulesOf as K, failuresOf as L, byClients as M, byNode as N, medianInterval as O, deletionOf as P, stoppingOf as Q, intakeOf as R, observationEnd as S, reusesReset as T, recordsOf as U, podsAt as V, requestsOf as W, sliceEmptyOf as X, sliceDropOf as Y, slicesOf as Z, checkRun as _, calibrationOf as a, SERVER as at, judgeKind as b, predictionText as c, secondsOf as ct, FORMAT_V1 as d, CLIENT as et, judge as f, LAST_SLACK as g, KIND_NOISE as h, readRecord as i, REDIS as it, backendsAt as j, middle as k, timeSpread as l, showBand as lt, upgradeCell as m, kindCellRecordSchema as n, LOG_LABEL as nt, cellsTable as o, bandOf as ot, upgrade as p, signalOf as q, kindCellRecordV1Schema as r, PORT as rt, medianText as s, seconds as st, STAGES as t, HEALTH_PORT as tt, FORMAT as u, deletionsOf as v, referenceOf as w, observationAfter as x, factsOf as y, kernelCounts as z };
1135
1189
 
1136
- //# sourceMappingURL=judge-reWPMVr4.js.map
1190
+ //# sourceMappingURL=judge-B3CHoiTW.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"judge-B3CHoiTW.js","names":["time","time"],"sources":["../src/bands.ts","../src/names.ts","../src/observe.ts","../src/stats.ts","../src/verdict.ts","../src/record.ts","../src/report.ts","../src/schema.ts"],"sourcesContent":["import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport type { Band } from \"./cell.ts\";\n\n/** This module makes and prints the bands of the predictions. It imports only the amounts. */\n\n/** This function gives a time in seconds as a quantity. */\nexport const seconds = (value: number): Quantity => ({ value, unit: \"s\" });\n\n/** This function gives a band from its two ends in seconds. */\nexport const bandOf = (lo: number, hi: number): Band => ({ lo: seconds(lo), hi: seconds(hi) });\n\n/** This function gives the two ends of a band as numbers of seconds, for arithmetic. */\nexport const secondsOf = (band: Band): { readonly lo: number; readonly hi: number } => ({\n lo: valueIn(band.lo, \"s\"),\n hi: valueIn(band.hi, \"s\"),\n});\n\n/** This function gives a band in a short form for a name or a table: `[28, 49] s`. */\nexport const showBand = (band: Band): string => {\n const { lo, hi } = secondsOf(band);\n return `[${lo}, ${hi}] s`;\n};\n","// The names and the ports of the objects of a cell. The judge reads them, thus this module imports\n// nothing.\n\n/** These are the ports of the app: its server, and its health server for the probes. */\nexport const PORT = 8080;\nexport const HEALTH_PORT = 9000;\n/** This label makes the watcher follow the log of a pod. */\nexport const LOG_LABEL = \"lab.systems/log\";\n/** These are the names of the objects of a cell. */\nexport const SERVER = \"lab\";\nexport const CLIENT = \"client\";\nexport const REDIS = \"redis\";\n","/**\n * This module reads a run from its lines. Each time is in milliseconds on CLOCK_MONOTONIC. All the\n * containers of the VM share that clock: the clients, the service, the agents, the injector, the\n * watcher and the prober.\n */\n\n/**\n * A line is one JSON line of a source of a run, with its stamp `t` in milliseconds on\n * CLOCK_MONOTONIC. It is a request of a client, a record of the service, a line of grpc-go or a probe answer. It is\n * also a pod, an EndpointSlice or an event of the API. Or it is the backends of the rules of a\n * node, a kernel record, or an act of the lab. A record keeps each line as its source wrote it.\n */\nexport interface Line {\n readonly ev: string;\n readonly t: number;\n readonly [field: string]: unknown;\n}\n\n/**\n * A request is one request of a client: its start, its duration in ms, its outcome, its client and\n * the node of the client. The backend (`b`) is the pod that answered. The local port (`lp`) is on\n * each request from 0.1.3, and on the failed ones before it. A conntrack entry is per port, thus the\n * requests on one port show which connection a stale entry came from.\n */\nexport interface Request {\n readonly t: number;\n readonly d: number;\n readonly o: string;\n readonly p: string;\n readonly n: string;\n readonly b?: string;\n readonly lp?: number | null;\n}\n\n/** A pod line is one state of a pod, as the watcher received it. */\nexport interface PodLine {\n readonly t: number;\n readonly type: string;\n readonly name: string;\n readonly node: string | null;\n readonly ip: string | null;\n readonly del: boolean;\n readonly ready: boolean;\n readonly phase: string | null;\n readonly cs: ReadonlyArray<{ readonly n: string; readonly r: boolean; readonly rc: number; readonly st: string; readonly id: string | null }>;\n readonly labels: Readonly<Record<string, string>>;\n}\n\n/** An endpoint is one endpoint of an EndpointSlice: its address, its conditions and its pod. */\nexport interface Endpoint {\n readonly ip: string | null;\n readonly r: boolean | null;\n readonly s: boolean | null;\n readonly x: boolean | null;\n readonly pod: string | null;\n readonly node: string | null;\n}\n\n/** A slice line is one state of an EndpointSlice. */\nexport interface SliceLine {\n readonly t: number;\n readonly type: string;\n readonly name: string;\n readonly svc: string | null;\n readonly eps: readonly Endpoint[];\n}\n\n/** A rules line is the backends of the Service in the rules of one node, after a change. `prev` is the start of the poll before it. */\nexport interface RulesLine {\n readonly t: number;\n readonly prev: number | null;\n readonly node: string;\n readonly b: readonly string[];\n}\n\n/** A record line is one transition of a machine of the service, with its own stamp `at`. */\nexport interface RecordLine {\n readonly t: number;\n readonly at: number;\n readonly p: string;\n readonly m: string;\n readonly i: string;\n readonly from: string;\n readonly to: string;\n readonly on: string;\n readonly d: string | null;\n}\n\nconst of = <T>(lines: readonly Line[], ev: string): T[] => lines.filter((line) => line.ev === ev) as unknown as T[];\n\nexport const requestsOf = (lines: readonly Line[]): Request[] => of<Request>(lines, \"req\");\nexport const podsOf = (lines: readonly Line[]): PodLine[] => of<PodLine>(lines, \"pod\").sort((a, b) => a.t - b.t);\nexport const slicesOf = (lines: readonly Line[], svc: string): SliceLine[] =>\n of<SliceLine>(lines, \"slice\").filter((line) => line.svc === svc);\nexport const rulesOf = (lines: readonly Line[]): RulesLine[] => of<RulesLine>(lines, \"rules\").sort((a, b) => a.t - b.t);\nexport const recordsOf = (lines: readonly Line[]): RecordLine[] => of<RecordLine>(lines, \"rec\");\n\n/** This function gives the requests that did not succeed, in sequence of their start. */\nexport const failuresOf = (lines: readonly Line[]): Request[] =>\n requestsOf(lines)\n .filter((request) => request.o !== \"ok\")\n .sort((a, b) => a.t - b.t);\n\n/** This function counts the outcomes of the requests, by outcome. */\nexport const outcomesOf = (requests: readonly Request[]): Record<string, number> => {\n const out: Record<string, number> = {};\n for (const request of requests) out[request.o] = (out[request.o] ?? 0) + 1;\n return out;\n};\n\n/**\n * This function orders the replicas by how many clients each one answered last, the most first,\n * then by name. `answers` gives the backend of the last answer of each client, by client. A client\n * of gRPC holds its connection to one replica, thus the first replica is the one whose stop the\n * most clients see.\n */\nexport const byClients = <T extends { readonly pod: string }>(replicas: readonly T[], answers: ReadonlyMap<string, string>): T[] => {\n const count = (pod: string): number => [...answers.values()].filter((backend) => backend === pod).length;\n return [...replicas].sort((a, b) => count(b.pod) - count(a.pod) || a.pod.localeCompare(b.pod));\n};\n\n/** This function gives the requests of each node, by the name of the node. */\nexport const byNode = (requests: readonly Request[]): Map<string, Request[]> => {\n const out = new Map<string, Request[]>();\n for (const request of requests) out.set(request.n, [...(out.get(request.n) ?? []), request]);\n return out;\n};\n\n/** This function gives the time that the watcher first received a deletion timestamp of one of the pods, after a time. */\nexport const deletionOf = (lines: readonly Line[], pods: readonly string[], after: number): number | null =>\n podsOf(lines).find((pod) => pods.includes(pod.name) && pod.del && pod.t >= after)?.t ?? null;\n\n/** This function gives the stamp of a transition of the service in a pod: the first matching record of a machine, after a time. */\nexport const transitionOf = (\n lines: readonly Line[],\n pod: string,\n machine: string,\n match: (record: RecordLine) => boolean,\n after: number,\n): number | null =>\n recordsOf(lines).find((record) => record.p === pod && record.m === machine && record.at >= after && match(record))?.at ?? null;\n\n/**\n * This function gives the line `stopping` of grpc-go in a pod, after a time. The server writes it at\n * SIGTERM. Then GracefulStop closes its listener and sends GOAWAY on each connection.\n */\nexport const stoppingOf = (lines: readonly Line[], pod: string, after: number): number | null =>\n lines.find((line) => line.ev === \"stopping\" && line[\"p\"] === pod && line.t >= after)?.t ?? null;\n\n/**\n * This function gives when the server of a pod stopped its intake. That is the start of the phase\n * `stop intake` of the service, or the line `stopping` of grpc-go.\n */\nexport const intakeOf = (lines: readonly Line[], pod: string, after: number): number | null =>\n transitionOf(lines, pod, \"drain\", (record) => record.to === \"stop intake\", after) ?? stoppingOf(lines, pod, after);\n\n/** This function gives when the server of a pod received its exit signal: a record of the service, or the line `stopping` of grpc-go. */\nexport const signalOf = (lines: readonly Line[], pod: string, after: number): number | null =>\n transitionOf(lines, pod, \"exit\", (record) => record.on === \"exit.request\", after) ?? stoppingOf(lines, pod, after);\n\n/**\n * This function gives when the rules of a node stopped the selection of a backend: the first poll\n * after a time without it. The change came after `prev`, the poll before it, and at the latest at `t`.\n */\nexport const dropOf = (lines: readonly Line[], node: string, backend: string, after: number): RulesLine | null =>\n rulesOf(lines).find((line) => line.node === node && line.t >= after && !line.b.includes(backend)) ?? null;\n\n/** This function gives when the rules of a node had no backend of the Service, after a time. */\nexport const emptyOf = (lines: readonly Line[], node: string, after: number): RulesLine | null =>\n rulesOf(lines).find((line) => line.node === node && line.t >= after && line.b.length === 0) ?? null;\n\n/** This function gives when an EndpointSlice of the Service first had no ready endpoint, after a time. */\nexport const sliceEmptyOf = (lines: readonly Line[], svc: string, after: number): number | null =>\n slicesOf(lines, svc).find((slice) => slice.t >= after && !slice.eps.some((endpoint) => endpoint.r === true))?.t ?? null;\n\n/** This function gives when the endpoint of an address was first not ready in the EndpointSlice of the Service, after a time. */\nexport const sliceDropOf = (lines: readonly Line[], svc: string, ip: string, after: number): number | null =>\n slicesOf(lines, svc).find((slice) => slice.t >= after && !slice.eps.some((endpoint) => endpoint.ip === ip && endpoint.r === true))?.t ?? null;\n\n/** This function gives the latest state of each pod at a time, by its name. A deleted pod is not in it. */\nexport const podsAt = (lines: readonly Line[], at: number): Map<string, PodLine> => {\n const out = new Map<string, PodLine>();\n for (const pod of podsOf(lines)) {\n if (pod.t > at) break;\n if (pod.type === \"DELETED\") out.delete(pod.name);\n else out.set(pod.name, pod);\n }\n return out;\n};\n\n/** This function gives the latest backends of each node at a time, by node. */\nexport const backendsAt = (lines: readonly Line[], at: number): Map<string, readonly string[]> => {\n const out = new Map<string, readonly string[]>();\n for (const line of rulesOf(lines)) {\n if (line.t > at) break;\n out.set(line.node, line.b);\n }\n return out;\n};\n\n/** This function gives the kernel records of the kinds that the lab keeps, counted by kind. */\nexport const kernelCounts = (lines: readonly Line[]): Record<string, number> => {\n const out: Record<string, number> = {};\n for (const line of lines) if (line.ev.startsWith(\"kernel.\")) out[line.ev.slice(7)] = (out[line.ev.slice(7)] ?? 0) + 1;\n return out;\n};\n\n/** This function gives the SIGTERM of each pod in a set after a time, by pod. A pod without one is not in it. */\nexport const signalsOf = (lines: readonly Line[], pods: readonly string[], after: number): Map<string, number> => {\n const out = new Map<string, number>();\n for (const pod of pods) {\n const at = signalOf(lines, pod, after);\n if (at !== null) out.set(pod, at);\n }\n return out;\n};\n\n/** This function gives the restart count of each container of each pod at a time, summed by pod. */\nexport const restartsAt = (lines: readonly Line[], at: number): Map<string, number> => {\n const out = new Map<string, number>();\n for (const [name, pod] of podsAt(lines, at)) out.set(name, pod.cs.reduce((sum, status) => sum + status.rc, 0));\n return out;\n};\n","// The statistics of a kind verdict: the median, the interval of the median and a quantile. They are\n// the statistics of the verdict of the harness, and a test holds them to it. The judge reads a\n// record without the harness, because the harness loads its Docker lab.\n\n/** This fraction, δ_rel, is how far the median of a cell can be outside its band and stay equivalent. */\nexport const RELATIVE = 0.05;\n\n/** This function gives the median of sorted values: the middle value, or the mean of the two middle values. */\nexport const middle = (sorted: readonly number[]): number => {\n const half = Math.floor(sorted.length / 2);\n return sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n};\n\n/** This function gives the value at quantile p of sorted values, by nearest rank: x_(⌈p·n⌉). */\nexport const quantileOf = (sorted: readonly number[], p: number): number =>\n sorted[Math.min(sorted.length, Math.max(1, Math.ceil(p * sorted.length))) - 1]!;\n\n/** This function gives P(X ≤ j) for X ~ Binomial(n, 1/2), exactly. */\nconst below = (n: number, j: number): number => {\n let term = 1;\n let sum = 0;\n for (let i = 0; i <= j; i += 1) {\n sum += term;\n term = (term * (n - i)) / (i + 1);\n }\n return sum / 2 ** n;\n};\n\n/**\n * This function gives the distribution-free interval of a median: [x_(k), x_(n+1−k)], for the\n * largest k whose two tails each hold 2.5 % or less. For eleven runs, it gives [x_(2), x_(10)],\n * at 98.8 %.\n */\nexport const medianInterval = (sorted: readonly number[]): [number, number] => {\n const n = sorted.length;\n let k = 0;\n while (k + 1 <= n / 2 && below(n, k) <= 0.025) k += 1;\n return k === 0 ? [sorted[0]!, sorted[n - 1]!] : [sorted[k - 1]!, sorted[n - k]!];\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { valueIn } from \"@mark1russell7/quantity\";\nimport { seconds, secondsOf } from \"./bands.ts\";\nimport type { KindCell, KindRun, Prediction, Reference, ResetReuse } from \"./cell.ts\";\nimport { PORT, SERVER } from \"./names.ts\";\nimport type { Line, Request } from \"./observe.ts\";\nimport {\n byNode,\n deletionOf,\n dropOf,\n emptyOf,\n failuresOf,\n intakeOf,\n outcomesOf,\n podsOf,\n signalsOf,\n sliceDropOf,\n sliceEmptyOf,\n} from \"./observe.ts\";\nimport { RELATIVE, medianInterval, middle } from \"./stats.ts\";\n\n/**\n * This time is δ for kind: each time of a run lands in its band ± δ. Its parts are the interval of\n * the poll of the rules (50 ms) and the delay of the watcher. The watcher receives each change after\n * the API server writes it.\n */\nexport const KIND_NOISE: Quantity = { value: 100, unit: \"ms\" };\n/**\n * This time is how much earlier than the end of its window the last failed request can start. A\n * request goes to the closed pod by the choice of the rules, thus the last ones before the end can\n * go to another pod. At 50 requests a second and two pods, 12 requests in sequence miss the closed\n * pod with a chance of 1 in 4096.\n */\nexport const LAST_SLACK: Quantity = { value: 250, unit: \"ms\" };\n\n/** These are δ and the slack in milliseconds, the unit of the stamps. */\nconst NOISE = valueIn(KIND_NOISE, \"ms\");\nconst SLACK = valueIn(LAST_SLACK, \"ms\");\n\n/** A propagated change is when the rules of a node first had it: after the poll `after`, and by the poll `by`. */\nexport interface Propagated<A = Quantity> {\n readonly after: A | null;\n readonly by: A;\n}\n\n/**\n * The facts of a run are what each table reads: its failures by outcome and node, and the times of\n * its target. A count stays a number, and so does a stamp: `first`, `last` and `deletion` are\n * readings of CLOCK_MONOTONIC in milliseconds. Each other time is an amount after a reference. The\n * parameter `A` is the form of an amount: a quantity in the format `/2`, seconds in the format `/1`.\n */\nexport interface RunFacts<A = Quantity> {\n readonly failures: number;\n readonly outcomes: Readonly<Record<string, number>>;\n readonly byNode: Readonly<Record<string, number>>;\n readonly first: number | null;\n readonly last: number | null;\n readonly deletion: number | null;\n readonly intakeAfterDeletion: A | null;\n readonly sliceAfter: A | null;\n /** This record is the propagation of the target to the rules of each node, by node. */\n readonly propagation: Readonly<Record<string, Propagated<A> | null>>;\n /** This time is when the EndpointSlice had no ready endpoint, after the fault (`removal`). */\n readonly sliceEmpty?: A | undefined;\n /** These times are when the rules of each node had no backend, after the fault (`removal`). */\n readonly rules?: Readonly<Record<string, A | null>> | undefined;\n /** This time is when the target left the EndpointSlice, after the fault (`leaves`). */\n readonly left?: A | undefined;\n /** These times are the SIGTERM of each replica, after the fault (`restarts`). */\n readonly signals?: ReadonlyArray<A | null> | undefined;\n /**\n * These are the replicas of a window for each deleted replica (`each`). Each one is a replica of the\n * run with a deletion after its start, with the stamps of its intake and of its deletion.\n */\n readonly deletions?: readonly Deleted[] | undefined;\n}\n\n/** A deleted replica is a replica of a run that got a deletion. It gives its pod, and the stamps of its intake (or null) and of its deletion. */\nexport interface Deleted {\n readonly pod: string;\n readonly intake: number | null;\n readonly deletion: number;\n}\n\n/**\n * A run check is the judgement of one run: if it agrees with the prediction, why, its main value,\n * and its facts. The main value is a time, or a count of requests or of replicas (`unitOf`).\n */\nexport interface RunCheck<A = Quantity> {\n readonly ok: boolean;\n readonly why: string;\n readonly value: A | null;\n readonly facts: RunFacts<A>;\n /**\n * This text tells why a run that agrees with the law is past its calibrated band. A request failed\n * after the window, but before the rules of its node dropped the target. Such a run makes its cell\n * `extend`, and the cell does not hold (D-EXTEND).\n */\n readonly tail?: string | undefined;\n}\n\n/** A kind decision is the result of a kind cell. */\nexport type KindDecision = \"holds\" | \"fails\" | \"extend\" | \"measured\" | \"calibration\" | \"void\";\n\n/** A kind verdict is the judgement of a cell on its counted runs. */\nexport interface KindVerdict<A = Quantity> {\n readonly decision: KindDecision;\n readonly counted: number;\n readonly voided: number;\n readonly failed: number;\n /** This amount is the median of the main value of the counted runs, or null. */\n readonly median: A | null;\n readonly why: string;\n readonly checks: ReadonlyArray<RunCheck<A>>;\n}\n\n/**\n * This function gives the unit of the main value of a cell, from its prediction:\n * - `s` for a time: the removal, the departure or the restart of a band, and the end of a stall.\n * - `{request}` for the failed requests: no failure, a window, a fleet that stays, and a measurement.\n * - `{replica}` for the replicas that received SIGTERM, when no replica must restart.\n */\nexport const unitOf = (prediction: { readonly kind: Prediction[\"kind\"]; readonly band?: unknown }): string => {\n switch (prediction.kind) {\n case \"removal\":\n case \"leaves\":\n case \"stalls\":\n return \"s\";\n case \"restarts\":\n return prediction.band === null ? \"{replica}\" : \"s\";\n default:\n return \"{request}\";\n }\n};\n\nconst sec = (ms: number): number => Math.round(ms) / 1000;\nconst inBand = (value: number, band: { readonly lo: number; readonly hi: number }): boolean =>\n value >= band.lo - NOISE / 1000 && value <= band.hi + NOISE / 1000;\nconst time = (value: number | null): Quantity | null => (value === null ? null : seconds(value));\n\n/**\n * This function tells if a failed request is the reuse of a port that a reset closed. The same\n * client had a reset on the same port, up to `life` before the request. The request ends with\n * `outcome`.\n */\nexport const reusesReset = (request: Request, requests: readonly Request[], reuse: ResetReuse): boolean => {\n if (request.o !== reuse.outcome || request.lp === undefined || request.lp === null) return false;\n const life = valueIn(reuse.life, \"ms\") + NOISE;\n return requests.some(\n (earlier) =>\n earlier.o === \"reset\" &&\n earlier.p === request.p &&\n earlier.lp === request.lp &&\n earlier.t + earlier.d <= request.t &&\n request.t - (earlier.t + earlier.d) <= life,\n );\n};\n\n/**\n * This function gives a reference event of a run, as a stamp, or null when the run does not have it.\n * The references `deletion` and `intake` are those of the target, or of the replica `pod`.\n */\nexport const referenceOf = (run: Pick<KindRun, \"start\" | \"acts\" | \"target\" | \"lines\">, from: Reference, pod: string | undefined = run.target?.pod): number | null => {\n const lines = run.lines as readonly Line[];\n if (from === \"fault\") return run.acts.fault;\n if (from === \"resume\") return run.acts.resume;\n if (pod === undefined) return null;\n if (from === \"deletion\") return deletionOf(lines, [pod], run.start);\n return intakeOf(lines, pod, run.start);\n};\n\n/**\n * This function gives the propagation of each node for the target of a run, or for the replica of\n * the address `ip`. It is the time from its deletion (or its crash) to the first poll of the rules\n * of the node without it. The change came between the poll before (`after`) and that poll (`by`).\n */\nexport const propagationOf = (run: KindRun, from: number | null, ip: string | undefined = run.target?.ip): Record<string, Propagated | null> => {\n const out: Record<string, Propagated | null> = {};\n if (ip === undefined || from === null) return out;\n const nodes = [...new Set((run.lines as readonly Line[]).filter((line) => line.ev === \"rules\").map((line) => String(line[\"node\"])))].sort();\n for (const node of nodes) {\n const drop = dropOf(run.lines as readonly Line[], node, `${ip}:${PORT}`, from);\n out[node] = drop === null ? null : { after: drop.prev === null ? null : seconds(sec(Math.max(0, drop.prev - from))), by: seconds(sec(drop.t - from)) };\n }\n return out;\n};\n\n/**\n * This function gives each replica of a run that got a deletion after the start of the run, in the\n * sequence of the deletions. A rolling update deletes each old replica in turn.\n */\nexport const deletionsOf = (run: KindRun): Deleted[] =>\n run.replicas\n .flatMap((replica) => {\n const deletion = referenceOf(run, \"deletion\", replica.pod);\n return deletion === null ? [] : [{ pod: replica.pod, intake: referenceOf(run, \"intake\", replica.pod), deletion }];\n })\n .sort((a, b) => a.deletion - b.deletion);\n\n/** This function tells if a prediction is a window for each deleted replica. */\nconst eachOf = (prediction: Prediction): boolean => prediction.kind === \"window\" && prediction.each === true;\n\n/**\n * A span is one window of a run, as stamps. It gives the replica of its references (or none), and its\n * two references. It also gives its ends with δ, and the deletion of its replica (`origin`). Then it\n * gives the propagation of that deletion to each node, and when the rules of a node dropped its replica.\n */\ninterface Span {\n readonly pod: string | undefined;\n readonly start: number;\n readonly end: number | null;\n readonly lo: number;\n readonly hi: number;\n readonly origin: number | null;\n readonly propagation: RunFacts[\"propagation\"];\n readonly dropped: (node: string) => number | null;\n}\n\n/**\n * This function gives how long a run observes after the end reference of its last window, or null\n * when its prediction sets no such time. The judge closes a window `end.band.hi` + δ after its end\n * reference. A request that starts by then ends at most the timeout of the workload later: the\n * client cancels it then. A prediction that is not a window, or a window with no end, sets none.\n */\nexport const observationAfter = (cell: Pick<KindCell, \"prediction\" | \"workload\">): Quantity | null => {\n const prediction = cell.prediction;\n if (prediction.kind !== \"window\" || prediction.end === null) return null;\n return seconds(sec(valueIn(prediction.end.band.hi, \"ms\") + valueIn(cell.workload.timeout, \"ms\") + NOISE));\n};\n\n/**\n * This function gives the earliest end of the observation of a run, as a stamp, or null when its\n * prediction sets none (`observationAfter`). It is that time after the latest end reference of a\n * window: of the target, or with `each`, of each replica that got a deletion. A run that ends before\n * it can miss its own failures. In a rolling update of gRPC, a call waits in the queue of its channel\n * for a stalled connect. It fails only at its timeout (kind run 40128267098). A reference that is not\n * in the lines yet does not count.\n */\nexport const observationEnd = (cell: KindCell, run: Pick<KindRun, \"start\" | \"acts\" | \"target\" | \"replicas\" | \"lines\">): number | null => {\n const after = observationAfter(cell);\n const prediction = cell.prediction;\n if (after === null || prediction.kind !== \"window\" || prediction.end === null) return null;\n const from = prediction.end.from;\n const pods: ReadonlyArray<string | undefined> = prediction.each === true ? run.replicas.map((replica) => replica.pod) : [run.target?.pod];\n const references = pods.flatMap((pod) => {\n const at = referenceOf(run, from, pod);\n return at === null ? [] : [at];\n });\n return references.length === 0 ? null : Math.max(...references) + valueIn(after, \"ms\");\n};\n\n/** This function gives the facts of a run that each table reads: its failures by outcome and node, and the times of its target. */\nexport const factsOf = (cell: KindCell, run: KindRun): RunFacts => {\n const lines = run.lines as readonly Line[];\n const failures = failuresOf(lines).filter((request) => request.t >= run.start && request.t <= run.end);\n const deletion = referenceOf(run, \"deletion\");\n const intake = referenceOf(run, \"intake\");\n const crash = cell.trigger.kind === \"crash\" ? run.acts.fault : null;\n const from = deletion ?? crash;\n return {\n failures: failures.length,\n outcomes: outcomesOf(failures),\n byNode: Object.fromEntries([...byNode(failures)].map(([node, list]) => [node, list.length])),\n first: failures.length === 0 ? null : failures[0]!.t,\n last: failures.length === 0 ? null : failures.at(-1)!.t,\n deletion,\n intakeAfterDeletion: deletion === null || intake === null ? null : seconds(sec(intake - deletion)),\n sliceAfter: run.target === null || from === null ? null : time((() => {\n const at = sliceDropOf(lines, SERVER, run.target!.ip, from);\n return at === null ? null : sec(at - from);\n })()),\n propagation: propagationOf(run, from),\n ...(eachOf(cell.prediction) ? { deletions: deletionsOf(run) } : {}),\n };\n};\n\n/** This function judges one run against the prediction of its cell. */\nexport const checkRun = (cell: KindCell, run: KindRun): RunCheck => {\n const lines = run.lines as readonly Line[];\n const facts = factsOf(cell, run);\n const failures = failuresOf(lines).filter((request) => request.t >= run.start && request.t <= run.end);\n const prediction = cell.prediction;\n const unit = unitOf(prediction);\n const done = (ok: boolean, why: string, value: number | null, more: Partial<RunFacts> = {}): RunCheck => ({\n ok,\n why,\n value: value === null ? null : { value, unit },\n facts: { ...facts, ...more },\n });\n switch (prediction.kind) {\n case \"none\": {\n const excluded = prediction.outcomes;\n const counted = excluded === undefined ? failures : failures.filter((request) => excluded.includes(request.o));\n const what = excluded === undefined ? \"requests failed\" : `requests ended ${excluded.join(\", \")}`;\n return done(counted.length === 0, counted.length === 0 ? `no ${excluded === undefined ? \"request failed\" : `request ended ${excluded.join(\", \")}`}` : `${counted.length} ${what}: ${JSON.stringify(facts.outcomes)}`, counted.length);\n }\n case \"window\": {\n const each = prediction.each === true;\n const opens = secondsOf(prediction.start.band);\n const closes = prediction.end === null ? null : secondsOf(prediction.end.band);\n // a request after a window that started before the rules of its node dropped the replica of that\n // window agrees with the law: only the calibrated band is short (D-EXTEND). A request after the drop is still late\n const droppedAfter = (origin: number | null, propagation: RunFacts[\"propagation\"]) => (node: string): number | null => {\n const propagated = propagation[node];\n return origin === null || propagated === undefined || propagated === null ? null : origin + valueIn(propagated.by, \"ms\");\n };\n // the window of the target, or with `each` the window of each replica that got a deletion\n const pods: ReadonlyArray<string | undefined> = each ? (facts.deletions ?? []).map((deleted) => deleted.pod) : [run.target?.pod];\n if (pods.length === 0) return done(false, \"no replica of the run got a deletion\", null);\n const windows: Span[] = [];\n for (const pod of pods) {\n const start = referenceOf(run, prediction.start.from, pod);\n const end = prediction.end === null ? null : referenceOf(run, prediction.end.from, pod);\n if (start === null || (prediction.end !== null && end === null))\n return done(false, `the run has no ${start === null ? prediction.start.from : prediction.end!.from} reference${each ? ` for ${pod}` : \"\"}`, null);\n const origin = each ? referenceOf(run, \"deletion\", pod) : (facts.deletion ?? (cell.trigger.kind === \"crash\" ? run.acts.fault : null));\n const ip = run.replicas.find((replica) => replica.pod === pod)?.ip;\n const propagation = each ? propagationOf(run, origin, ip) : facts.propagation;\n windows.push({\n pod,\n start,\n end,\n lo: start + opens.lo * 1000 - NOISE,\n hi: end === null ? Number.POSITIVE_INFINITY : end + closes!.hi * 1000 + NOISE,\n origin,\n propagation,\n dropped: droppedAfter(origin, propagation),\n });\n }\n // a node that dropped the replica of a window after the propagation of the prediction, or kept it\n // past that bound to the end of the run: the run agrees with the law, but the band is short (D-EXTEND)\n const bound = prediction.propagation === undefined ? null : secondsOf(prediction.propagation).hi * 1000 + NOISE;\n const slow =\n bound === null\n ? []\n : windows.flatMap(({ pod, origin, propagation }) =>\n origin === null\n ? []\n : Object.entries(propagation).flatMap(([node, propagated]) => {\n const by = propagated === null ? null : valueIn(propagated.by, \"ms\");\n const late = by === null ? run.end - origin > bound : by > bound;\n return late ? [{ pod, node, by }] : [];\n }),\n );\n const relevant: Request[] = prediction.nodes === \"paused\" ? failures.filter((request) => request.n === run.paused) : failures;\n const others = prediction.nodes === \"paused\" ? failures.filter((request) => request.n !== run.paused) : [];\n const reuse = prediction.afterReset;\n const late = relevant.filter((request) => windows.every((window) => request.t < window.lo || request.t > window.hi));\n // a request outside the window that reuses the port of a reset is the CLOSE entry of that reset\n const reused = reuse === undefined ? [] : late.filter((request) => reusesReset(request, failures, reuse));\n const tailOf = (request: Request): Span | undefined =>\n reused.includes(request)\n ? undefined\n : windows.find((window) => {\n const drop = request.t > window.hi ? window.dropped(request.n) : null;\n return drop !== null && request.t <= drop + NOISE;\n });\n const tail = late.filter((request) => tailOf(request) !== undefined);\n const outside = late.filter((request) => !reused.includes(request) && !tail.includes(request));\n const bounds = (window: Span): string => `[${sec(window.lo - window.start)}, ${sec(window.hi - window.start)}] s`;\n const problems: string[] = [];\n if (outside.length > 0)\n problems.push(\n each\n ? `${outside.length} failed requests outside the window of each deleted replica: ${windows.map((window) => `${window.pod} ${bounds(window)}`).join(\", \")} after its ${prediction.start.from}`\n : `${outside.length} failed requests outside ${bounds(windows[0]!)} after ${prediction.start.from}`,\n );\n if (others.length > 0) problems.push(`${others.length} failed requests on nodes whose kube-proxy did not pause`);\n if (prediction.certain && relevant.length === 0) problems.push(\"no request failed in a window that the law opens\");\n // the window that ends last: the last failed request comes near its end\n const final = windows.reduce((a, b) => ((b.end ?? Number.NEGATIVE_INFINITY) > (a.end ?? Number.NEGATIVE_INFINITY) ? b : a));\n if (prediction.certain && final.end !== null && relevant.length > 0) {\n const last = relevant.at(-1)!.t;\n const earliest = final.end + closes!.lo * 1000 - NOISE - SLACK;\n if (last < earliest) problems.push(`the last failed request started ${sec(earliest - last)} s before the window can end`);\n }\n const inside = relevant.filter((request) => !late.includes(request));\n const span = inside.length === 0 ? 0 : sec(inside.at(-1)!.t - inside[0]!.t);\n const where = each ? `the windows of ${windows.length} deleted replicas` : \"the window\";\n const agrees =\n reused.length === 0\n ? `${relevant.length} failed requests, all in ${where} (span ${span} s)`\n : `${inside.length} failed requests in ${where} (span ${span} s), and ${reused.length} on a port that a reset closed`;\n const checked = done(problems.length === 0, problems.length === 0 ? agrees : problems.join(\"; \"), relevant.length);\n if (problems.length > 0) return checked;\n const notes: string[] = [];\n if (tail.length > 0) {\n const last = tail.at(-1)!;\n const after = tailOf(last)!;\n notes.push(\n each\n ? `${tail.length} failed requests after a window, the last ${sec(last.t - after.hi + NOISE)} s after the window of ${after.pod} on ${last.n}, before the rules of ${last.n} dropped ${after.pod}`\n : `${tail.length} failed requests after the window, the last ${sec(last.t - after.hi + NOISE)} s after it on ${last.n}, before the rules of ${last.n} dropped the target`,\n );\n }\n if (slow.length > 0) {\n // a node that kept its replica to the end of the run is the latest\n const latest = slow.find((late) => late.by === null) ?? slow.reduce((a, b) => (b.by! > a.by! ? b : a));\n const what = each ? latest.pod : \"the target\";\n const drop = latest.by === null ? `kept ${what} to the end of the run` : `dropped ${what} ${sec(latest.by)} s after its deletion`;\n notes.push(`${slow.length} drops after the propagation of ${secondsOf(prediction.propagation!).hi} s at the most, the latest: the rules of ${latest.node} ${drop}`);\n }\n return notes.length === 0 ? checked : { ...checked, tail: notes.join(\"; \") };\n }\n case \"removal\": {\n const fault = run.acts.fault;\n if (fault === null) return done(false, \"the run has no fault stamp\", null);\n const empty = sliceEmptyOf(lines, SERVER, fault);\n if (empty === null) return done(false, \"the Service kept a ready endpoint\", null);\n const value = sec(empty - fault);\n const band = secondsOf(prediction.band);\n const problems: string[] = [];\n if (!inBand(value, band)) problems.push(`the EndpointSlice had no ready endpoint at ${value} s`);\n const nodes = [...new Set(lines.filter((line) => line.ev === \"rules\").map((line) => String(line[\"node\"])))].sort();\n const rules: Record<string, Quantity | null> = {};\n for (const node of nodes) {\n const drop = emptyOf(lines, node, fault);\n rules[node] = drop === null ? null : seconds(sec(drop.t - fault));\n if (drop === null) problems.push(`the rules of ${node} kept a backend`);\n else if (!inBand(sec(drop.t - fault), { lo: band.lo, hi: band.hi + 0.05 }))\n problems.push(`the rules of ${node} had no backend at ${sec(drop.t - fault)} s`);\n }\n return done(problems.length === 0, problems.length === 0 ? `the Service was empty at ${value} s` : problems.join(\"; \"), value, { sliceEmpty: seconds(value), rules });\n }\n case \"leaves\": {\n const fault = run.acts.fault;\n if (fault === null || run.target === null) return done(false, \"the run has no fault stamp or no target\", null);\n const at = sliceDropOf(lines, SERVER, run.target.ip, fault);\n if (at === null) return done(false, `${run.target.pod} stayed ready in the EndpointSlice`, null);\n const value = sec(at - fault);\n const band = secondsOf(prediction.band);\n const problems: string[] = [];\n if (!inBand(value, band)) problems.push(`${run.target.pod} left the EndpointSlice at ${value} s`);\n const propagation = propagationOf(run, fault);\n for (const [node, drop] of Object.entries(propagation)) {\n const by = drop === null ? null : valueIn(drop.by, \"s\");\n if (by === null) problems.push(`the rules of ${node} kept ${run.target.pod}`);\n else if (!inBand(by, { lo: band.lo, hi: band.hi + 0.05 })) problems.push(`the rules of ${node} dropped it at ${by} s`);\n }\n return done(problems.length === 0, problems.length === 0 ? `${run.target.pod} left at ${value} s` : problems.join(\"; \"), value, { left: seconds(value), propagation });\n }\n case \"restarts\": {\n const fault = run.acts.fault;\n if (fault === null) return done(false, \"the run has no fault stamp\", null);\n const until = run.acts.heal ?? run.end;\n const pods = run.replicas.map((replica) => replica.pod);\n const signals = signalsOf(lines, pods, fault);\n const inside = [...signals].filter(([, at]) => at <= until);\n if (prediction.band === null)\n return done(inside.length === 0, inside.length === 0 ? \"no replica received SIGTERM\" : `${inside.length} replicas received SIGTERM`, inside.length);\n const band = secondsOf(prediction.band);\n const times = pods.map((pod) => {\n const at = signals.get(pod);\n return at === undefined ? null : sec(at - fault);\n });\n const missing = times.filter((each) => each === null).length;\n const off = times.filter((each): each is number => each !== null && !inBand(each, band));\n const known = times.filter((each): each is number => each !== null).sort((a, b) => a - b);\n const ok = missing === 0 && off.length === 0;\n return done(ok, ok ? `each replica received SIGTERM, at ${known.join(\", \")} s` : `${missing} replicas without SIGTERM, ${off.length} outside the band (${known.join(\", \")} s)`, known.length === 0 ? null : middle(known), { signals: times.map(time) });\n }\n case \"stays\": {\n const fault = run.acts.fault;\n if (fault === null) return done(false, \"the run has no fault stamp\", null);\n const until = run.end;\n const dropped = run.replicas.filter((replica) => {\n const at = sliceDropOf(lines, SERVER, replica.ip, fault);\n return at !== null && at <= until;\n });\n const refused = failures.filter((request) => request.o === \"refused\" && request.t >= fault);\n const problems: string[] = [];\n if (dropped.length > 0) problems.push(`${dropped.length} replicas left the EndpointSlice`);\n if (refused.length > 0) problems.push(`${refused.length} requests were refused`);\n return done(problems.length === 0, problems.length === 0 ? `the replicas stayed ready, ${failures.length} requests failed by their dependency` : problems.join(\"; \"), failures.length);\n }\n case \"stalls\": {\n const fault = run.acts.fault;\n const heal = run.acts.heal;\n if (fault === null || heal === null) return done(false, \"the run has no fault or heal stamp\", null);\n const old = new Set(run.replicas.map((replica) => replica.pod));\n // only the replicas of the service: the pod of Redis is not a part of the rollout\n const servers = podsOf(lines).filter((pod) => pod.labels[\"app\"] === SERVER);\n const pods = servers.filter((pod) => pod.t >= fault && pod.t <= heal);\n const deleted = pods.filter((pod) => old.has(pod.name) && pod.del);\n const newReady = pods.filter((pod) => !old.has(pod.name) && pod.ready);\n const after = servers.filter((pod) => pod.t > heal);\n const firstReady = after.find((pod) => !old.has(pod.name) && pod.ready);\n const finished = firstReady !== undefined && after.some((pod) => old.has(pod.name) && pod.type === \"DELETED\");\n const problems: string[] = [];\n if (deleted.length > 0) problems.push(`${new Set(deleted.map((pod) => pod.name)).size} old pods were deleted while Redis was down`);\n if (newReady.length > 0) problems.push(\"a new pod became ready while Redis was down\");\n if (!finished) problems.push(\"the rollout did not continue after Redis came back\");\n if (problems.length === 0)\n return done(true, `the rollout waited for Redis, and its first new pod was ready ${sec(firstReady!.t - heal)} s after Redis came back`, sec(firstReady!.t - heal));\n return done(false, problems.join(\"; \"), null);\n }\n case \"measured\":\n return done(true, \"measured\", facts.failures);\n }\n};\n\n/** This function judges a cell on its runs: each counted run must agree, and a median in a band must be equivalent to it. */\nexport const judgeKind = (cell: KindCell, runs: readonly KindRun[]): KindVerdict => {\n const counted = runs.filter((run) => !run.warmup && run.void === null);\n const voided = runs.filter((run) => !run.warmup && run.void !== null).length;\n const checks = counted.map((run) => checkRun(cell, run));\n // the main values of one cell share the unit of its prediction\n const values = checks.flatMap((check) => (check.value === null ? [] : [check.value.value])).sort((a, b) => a - b);\n const median: Quantity | null = values.length === 0 ? null : { value: middle(values), unit: unitOf(cell.prediction) };\n const failed = checks.filter((check) => !check.ok).length;\n if (counted.length === 0) return { decision: \"void\", counted: 0, voided, failed: 0, median: null, why: \"no counted run\", checks };\n if (cell.calibrates) return { decision: \"calibration\", counted: counted.length, voided, failed, median, why: \"a calibration of the propagation\", checks };\n if (cell.prediction.kind === \"measured\") return { decision: \"measured\", counted: counted.length, voided, failed: 0, median, why: cell.prediction.why, checks };\n const prediction = cell.prediction;\n const band = prediction.kind === \"removal\" || prediction.kind === \"leaves\" || prediction.kind === \"restarts\" ? prediction.band : null;\n if (failed > 0) {\n const first = checks.find((check) => !check.ok)!;\n return { decision: \"fails\", counted: counted.length, voided, failed, median, why: `${failed} of ${counted.length} runs disagree, the first: ${first.why}`, checks };\n }\n // a run past its calibrated band agrees with the law, but the band is short: the cell needs a longer one\n const tails = checks.filter((check) => check.tail !== undefined);\n if (tails.length > 0)\n return { decision: \"extend\", counted: counted.length, voided, failed, median, why: `${tails.length} of ${counted.length} runs are past the calibrated band, the first: ${tails[0]!.tail}`, checks };\n if (band !== null && values.length > 0) {\n const ends = secondsOf(band);\n const [from, to] = medianInterval(values);\n const wide = { lo: ends.lo * (1 - RELATIVE), hi: ends.hi * (1 + RELATIVE) };\n if (to < wide.lo || from > wide.hi)\n return { decision: \"fails\", counted: counted.length, voided, failed, median, why: `the median interval [${from}, ${to}] s is outside the band`, checks };\n if (from < wide.lo || to > wide.hi)\n return { decision: \"extend\", counted: counted.length, voided, failed, median, why: `the median interval [${from}, ${to}] s crosses the edge of the band`, checks };\n }\n return { decision: \"holds\", counted: counted.length, voided, failed, median, why: `${counted.length} of ${counted.length} runs agree`, checks };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { seconds } from \"./bands.ts\";\nimport type { Band, KindCell, KindRun, Prediction, Trigger } from \"./cell.ts\";\nimport type { KindScenario } from \"./scenario.ts\";\nimport type { KindVerdict, Propagated, RunCheck, RunFacts } from \"./verdict.ts\";\nimport { judgeKind, unitOf } from \"./verdict.ts\";\n\n/**\n * This module is the raw record of a kind cell: its formats, and the upgrade from the format `/1`\n * to the format `/2`. The lab writes `/2`, and it does not write `/1` again. A reader reads a record\n * of `/1` through `upgrade`, and the meaning of each value stays the same.\n */\n\n/** This format is the format that the lab writes. Each amount in it is a quantity. */\nexport const FORMAT = \"systems.kind-cell-record/2\";\n/** This format is the first format. Its bands, its medians and its times are numbers. */\nexport const FORMAT_V1 = \"systems.kind-cell-record/1\";\n\n/** A load sample is what the VM did in 4 s before a run: when (a stamp of the wall clock in milliseconds), its idle share, and its busiest processes. */\nexport interface LoadSample {\n readonly t: number;\n readonly idle: number;\n readonly top: readonly string[];\n readonly foreign: readonly string[];\n}\n\n/** A cluster manifest is what a run records about its cluster: the versions, the images, the nodes and the mode as read back. */\nexport interface ClusterManifest {\n readonly kind: string;\n readonly nodeImage: string;\n readonly server: string;\n readonly mode: string;\n readonly docker: string;\n readonly nodes: ReadonlyArray<{\n readonly name: string;\n readonly kubelet: string;\n readonly kernel: string;\n readonly runtime: string;\n readonly os: string;\n }>;\n}\n\n/** An environment manifest is one cluster of a cell: its scenario, what the cluster was, and the images of the lab. */\nexport interface EnvironmentManifest {\n readonly env: number;\n readonly scenario: KindScenario;\n readonly cluster: ClusterManifest;\n readonly images: Readonly<Record<string, string>>;\n}\n\n/**\n * A kind cell result is a cell, its runs over its environments, the manifest of each environment,\n * and its verdict. The parameter `A` is the form of an amount.\n */\nexport interface KindCellResult<A = Quantity> {\n readonly cell: KindCell<A>;\n readonly runs: readonly KindRun[];\n readonly manifests: readonly EnvironmentManifest[];\n readonly loads: readonly LoadSample[];\n readonly verdict: KindVerdict<A>;\n /** This text is the time of the verdict, in ISO 8601. */\n readonly at: string;\n}\n\n/** A kind cell record is the raw record of a cell in the format `/2`: a result with its format. */\nexport interface KindCellRecord extends KindCellResult {\n readonly format: typeof FORMAT;\n}\n\n/** A kind cell record of the format `/1` gives each band, median, main value and time of a fact as a number. */\nexport interface KindCellRecordV1 extends KindCellResult<number> {\n readonly format: typeof FORMAT_V1;\n}\n\n/** These are the parts of the format `/1`, with numbers for amounts. */\nexport type BandV1 = Band<number>;\nexport type PredictionV1 = Prediction<number>;\nexport type TriggerV1 = Trigger<number>;\nexport type KindCellV1 = KindCell<number>;\nexport type KindVerdictV1 = KindVerdict<number>;\nexport type RunCheckV1 = RunCheck<number>;\nexport type RunFactsV1 = RunFacts<number>;\n\nconst time = (value: number | null): Quantity | null => (value === null ? null : seconds(value));\nconst band = (old: BandV1): Band => ({ lo: seconds(old.lo), hi: seconds(old.hi) });\n/** The format `/1` gives each part of `cpu.max` in microseconds. */\nconst microseconds = (value: number): Quantity => ({ value, unit: \"us\" });\n\nconst upgradeTrigger = (trigger: TriggerV1): Trigger => {\n const { cpuMax, ...rest } = trigger;\n return cpuMax === undefined ? rest : { ...rest, cpuMax: { quota: microseconds(cpuMax.quota), period: microseconds(cpuMax.period) } };\n};\n\nconst upgradePrediction = (prediction: PredictionV1): Prediction => {\n switch (prediction.kind) {\n case \"window\": {\n const { afterReset, propagation, ...rest } = prediction;\n return {\n ...rest,\n start: { from: prediction.start.from, band: band(prediction.start.band) },\n end: prediction.end === null ? null : { from: prediction.end.from, band: band(prediction.end.band) },\n ...(afterReset === undefined ? {} : { afterReset: { life: seconds(afterReset.life), outcome: afterReset.outcome } }),\n ...(propagation === undefined ? {} : { propagation: band(propagation) }),\n };\n }\n case \"removal\":\n case \"leaves\":\n return { ...prediction, band: band(prediction.band) };\n case \"restarts\":\n return { ...prediction, band: prediction.band === null ? null : band(prediction.band) };\n default:\n return prediction;\n }\n};\n\n/** This function gives a cell of the format `/1` in the format `/2`: its bands in seconds, and its `cpu.max` in microseconds. */\nexport const upgradeCell = (cell: KindCellV1): KindCell => ({\n ...cell,\n trigger: upgradeTrigger(cell.trigger),\n prediction: upgradePrediction(cell.prediction),\n});\n\nconst upgradeFacts = (facts: RunFactsV1): RunFacts => {\n const propagated = (old: Propagated<number> | null): Propagated | null => (old === null ? null : { after: time(old.after), by: seconds(old.by) });\n const { sliceEmpty, rules, left, signals, ...rest } = facts;\n return {\n ...rest,\n intakeAfterDeletion: time(facts.intakeAfterDeletion),\n sliceAfter: time(facts.sliceAfter),\n propagation: Object.fromEntries(Object.entries(facts.propagation).map(([node, old]) => [node, propagated(old)])),\n ...(sliceEmpty === undefined ? {} : { sliceEmpty: seconds(sliceEmpty) }),\n ...(rules === undefined ? {} : { rules: Object.fromEntries(Object.entries(rules).map(([node, old]) => [node, time(old)])) }),\n ...(left === undefined ? {} : { left: seconds(left) }),\n ...(signals === undefined ? {} : { signals: signals.map((old) => time(old)) }),\n };\n};\n\n/**\n * This function gives a record in the format `/2`. A record of `/2` comes back as it is. A record\n * of `/1` gets each amount as a quantity, and each value stays the same:\n * - Each band and each time of a fact is in seconds, as `/1` gives it.\n * - The `cpu.max` of a throttle is in microseconds, as `/1` gives it.\n * - The median and the main value of each run get the unit of the prediction (`unitOf`): seconds,\n * failed requests (`{request}`) or replicas (`{replica}`).\n */\nexport const upgrade = (record: KindCellRecordV1 | KindCellRecord): KindCellRecord => {\n if (record.format === FORMAT) return record;\n const unit = unitOf(record.cell.prediction);\n const amount = (value: number | null): Quantity | null => (value === null ? null : { value, unit });\n const verdict = record.verdict;\n return {\n format: FORMAT,\n cell: upgradeCell(record.cell),\n runs: record.runs,\n manifests: record.manifests,\n loads: record.loads,\n verdict: {\n ...verdict,\n median: amount(verdict.median),\n checks: verdict.checks.map((check) => ({ ...check, value: amount(check.value), facts: upgradeFacts(check.facts) })),\n },\n at: record.at,\n };\n};\n\n/**\n * This function judges a record again, from its cell and its runs, with the judge of this version.\n * It reads a record of `/1` through `upgrade`. The verdict is in the format `/2`.\n */\nexport const judge = (record: KindCellRecordV1 | KindCellRecord): KindVerdict => {\n const current = upgrade(record);\n return judgeKind(current.cell, current.runs);\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { format, valueIn } from \"@mark1russell7/quantity\";\nimport { seconds, secondsOf, showBand } from \"./bands.ts\";\nimport type { Band, KindCell, KindRun, Prediction } from \"./cell.ts\";\nimport { quantileOf } from \"./stats.ts\";\nimport type { KindVerdict } from \"./verdict.ts\";\nimport { factsOf, propagationOf, referenceOf } from \"./verdict.ts\";\n\n/** A judged cell is a cell, its runs and its verdict: the part of a result that a report reads. */\nexport interface Judged {\n readonly cell: KindCell;\n readonly runs: readonly KindRun[];\n readonly verdict: KindVerdict;\n}\n\n/** A spread is the count, the extremes, the median and the 99th percentile of a set of times. */\nexport interface TimeSpread {\n readonly count: number;\n readonly min: Quantity;\n readonly median: Quantity;\n readonly p99: Quantity;\n readonly max: Quantity;\n}\n\n/** This function gives the spread of a set of times, or null for none. The percentile is by nearest rank. */\nexport const timeSpread = (times: readonly Quantity[]): TimeSpread | null => {\n if (times.length === 0) return null;\n const sorted = times.map((each) => valueIn(each, \"s\")).sort((a, b) => a - b);\n const half = Math.floor(sorted.length / 2);\n const median = sorted.length % 2 === 1 ? sorted[half]! : (sorted[half - 1]! + sorted[half]!) / 2;\n return {\n count: sorted.length,\n min: seconds(sorted[0]!),\n median: seconds(Math.round(median * 1000) / 1000),\n p99: seconds(quantileOf(sorted, 0.99)),\n max: seconds(sorted.at(-1)!),\n };\n};\n\n/** This function gives a reference with its band, and only the reference when the band is [0, 0] s. */\nconst atBand = (from: string, band: Band): string => {\n const { lo, hi } = secondsOf(band);\n return lo === 0 && hi === 0 ? `the ${from}` : `the ${from} + ${showBand(band)}`;\n};\n\n/** This function gives the prediction of a cell in a short form for a table. */\nexport const predictionText = (prediction: Prediction): string => {\n switch (prediction.kind) {\n case \"none\":\n return prediction.outcomes === undefined ? \"no failure\" : `no ${prediction.outcomes.join(\", \")}`;\n case \"window\":\n // a window with `each` is a window for each replica that got a deletion, from the references of that replica.\n // A window can open before its reference: a stale SYN_SENT entry fails the paused node up to its life before the intake\n return `failures only from ${atBand(prediction.start.from, prediction.start.band)}${prediction.end === null ? \"\" : ` to ${atBand(prediction.end.from, prediction.end.band)}`}${prediction.each === true ? \", of each deleted replica\" : \"\"}${prediction.nodes === \"paused\" ? \", on the paused node\" : \"\"}${prediction.certain ? \", at least one\" : \"\"}${prediction.afterReset === undefined ? \"\" : `, or a ${prediction.afterReset.outcome} up to ${format(prediction.afterReset.life)} after a reset on its port`}${prediction.propagation === undefined ? \"\" : `, the rules drop the replica in ${showBand(prediction.propagation)}`}`;\n case \"removal\":\n return `Service empty in ${showBand(prediction.band)}`;\n case \"leaves\":\n return `the replica leaves in ${showBand(prediction.band)}`;\n case \"restarts\":\n return prediction.band === null ? \"no restart\" : `each replica SIGTERM in ${showBand(prediction.band)}`;\n case \"stays\":\n return \"stays ready, no refusal\";\n case \"stalls\":\n return \"the rollout waits for Redis\";\n case \"measured\":\n return \"measured\";\n }\n};\n\n/**\n * This function gives the main measurement of a cell in a short form: its median and what it\n * counts. For failed requests, it is the middle count of the counted runs.\n */\nexport const medianText = (judged: Judged): string => {\n const { cell, verdict } = judged;\n if (verdict.median === null) return \"\";\n if (verdict.median.unit === \"{request}\") {\n const counted = judged.runs.filter((run) => !run.warmup && run.void === null);\n const failures = counted.map((run) => factsOf(cell, run).failures).sort((a, b) => a - b);\n return `${failures.length === 0 ? 0 : failures[Math.floor(failures.length / 2)]} failed requests`;\n }\n return format(verdict.median, { digits: 4 });\n};\n\n/** This function gives the table of the cells in Markdown. Each row has the cell, its plan cell, its mode, its trigger, the prediction, the verdict, its runs and its median. */\nexport const cellsTable = (judged: readonly Judged[]): string =>\n [\n \"| Cell | Plan id | Mode | Trigger | Prediction | Verdict | Runs | Median |\",\n \"| --- | --- | --- | --- | --- | --- | --- | --- |\",\n ...judged.map((each) => {\n const { cell, verdict } = each;\n const runs = verdict.voided === 0 ? `${verdict.counted}` : `${verdict.counted} (+${verdict.voided} void)`;\n return `| ${cell.name} | ${cell.id} | ${cell.mode} | ${cell.trigger.kind} | ${predictionText(cell.prediction)} | ${verdict.decision} | ${runs} | ${medianText(each)} |`;\n }),\n ].join(\"\\n\");\n\n/**\n * This function gives the propagation of each calibration, by node. It is the time from the\n * deletion to the poll of the rules of each node without the pod. It also gives the time to the\n * EndpointSlice and to the stop of the intake.\n */\nexport const calibrationOf = (judged: Judged): {\n readonly nodes: Readonly<Record<string, TimeSpread | null>>;\n readonly all: TimeSpread | null;\n readonly slice: TimeSpread | null;\n readonly intake: TimeSpread | null;\n} => {\n const byNode: Record<string, Quantity[]> = {};\n const slice: Quantity[] = [];\n const intake: Quantity[] = [];\n for (const run of judged.runs.filter((each) => !each.warmup && each.void === null)) {\n const deletion = referenceOf(run, \"deletion\");\n for (const [node, drop] of Object.entries(propagationOf(run, deletion))) if (drop !== null) (byNode[node] ??= []).push(drop.by);\n const facts = factsOf(judged.cell, run);\n if (facts.sliceAfter !== null) slice.push(facts.sliceAfter);\n if (facts.intakeAfterDeletion !== null) intake.push(facts.intakeAfterDeletion);\n }\n const nodes = Object.fromEntries(Object.entries(byNode).map(([node, values]) => [node, timeSpread(values)]));\n return { nodes, all: timeSpread(Object.values(byNode).flat()), slice: timeSpread(slice), intake: timeSpread(intake) };\n};\n","import type { Quantity } from \"@mark1russell7/quantity\";\nimport { extentSchema, quantitySchema } from \"@mark1russell7/quantity/schema\";\nimport type { Stage } from \"@mark1russell7/service\";\nimport { z } from \"zod\";\nimport type { KindRun, TriggerKind } from \"./cell.ts\";\nimport type { KindCellRecord, KindCellRecordV1 } from \"./record.ts\";\nimport { FORMAT, FORMAT_V1, upgrade } from \"./record.ts\";\nimport type { KubeProxyMode } from \"./scenario.ts\";\nimport { unitOf } from \"./verdict.ts\";\n\n/**\n * This module holds the zod schemas of a kind cell record, in the formats `/1` and `/2`. It names\n * the stages of the shutdown itself, thus it does not load the service. A test holds the names to\n * the service.\n */\n\n/** These are the stages of the shutdown of the service, in their sequence. */\nexport const STAGES: readonly [Stage, ...Stage[]] = [\n \"fail readiness\",\n \"stop intake\",\n \"drain\",\n \"close transports\",\n \"stop the poller\",\n \"tear down dependencies\",\n \"close the health server\",\n \"flush the output\",\n];\n\nconst TRIGGERS = [\n \"pod-deletion\",\n \"rolling-update\",\n \"node-drain\",\n \"scale-in\",\n \"crash\",\n \"liveness-restart\",\n \"freeze-process\",\n \"rst\",\n \"blackhole\",\n \"peer-gone-without-close\",\n \"app-stall\",\n \"rollout\",\n \"cpu-throttle\",\n] as const satisfies readonly TriggerKind[];\nconst MODES = [\"iptables\", \"ipvs\", \"nftables\"] as const satisfies readonly KubeProxyMode[];\n\nconst time = quantitySchema(\"time\");\nconst extent = extentSchema(\"time\");\nconst count = z.number().int().nonnegative();\nconst gate = z.enum([\"readiness\", \"liveness\"]);\nconst reference = z.enum([\"deletion\", \"intake\", \"fault\", \"resume\"]);\n\nconst probe = z.object({ period: time, threshold: count, timeout: time });\nconst timing = z.discriminatedUnion(\"type\", [\n z.object({ type: z.literal(\"debounce\"), interval: time, failAfter: count, recoverAfter: count }),\n z.object({ type: z.literal(\"window\"), failAfter: time, recoverAfter: time.optional() }),\n]);\nconst knobs = z.object({\n connect_bound: time,\n request_timeout: time,\n driver_writeoff_time: extent,\n keepalive_idle: extent,\n heartbeat_interval: extent,\n heartbeat_timeout: time,\n client_retry_window: extent,\n retry_base: time,\n retry_cap: time,\n close_bound: time,\n max_connection_age: extent,\n retrans_giveup_time: extent,\n});\nconst declaration = z.object({\n poll: z.object({ interval: time, jitter: z.number().min(0).lt(1), timeout: time }),\n readiness: timing,\n liveness: timing,\n startup: time,\n phases: z.array(z.tuple([z.enum(STAGES), time])),\n cooperativeWindow: extent,\n couplings: z.array(z.object({ from: gate, to: gate, effect: z.literal(\"fails\") })),\n redis: z.object({ gates: gate.nullable(), knobs, misses: count }).nullable(),\n flag: gate.nullable(),\n watchdog: z.object({ tick: time, threshold: time }).nullable(),\n});\n\nconst replica = z.object({ pod: z.string(), node: z.string(), ip: z.string(), version: z.string() });\n/** A line keeps each field that its source wrote. The judge reads `ev`, `t` and the fields of its kind. */\nconst line = z.object({ ev: z.string(), t: z.number() }).catchall(z.unknown());\nconst run: z.ZodType<KindRun> = z.object({\n env: count,\n index: z.number().int(),\n warmup: z.boolean(),\n start: z.number(),\n end: z.number(),\n acts: z.object({ fault: z.number().nullable(), heal: z.number().nullable(), resume: z.number().nullable() }),\n target: replica.nullable(),\n paused: z.string().nullable(),\n replicas: z.array(replica),\n void: z.string().nullable(),\n lines: z.array(line),\n dropped: z.record(z.string(), count),\n notes: z.array(z.string()).optional(),\n});\nconst manifest = z.object({\n env: count,\n scenario: z.object({\n name: z.string(),\n kubernetes: z.string(),\n workers: count,\n kubeProxy: z.object({ mode: z.enum(MODES), minSyncPeriod: time, syncPeriod: time }),\n cni: z.literal(\"kindnet\"),\n podSubnet: z.string(),\n serviceSubnet: z.string(),\n }),\n cluster: z.object({\n kind: z.string(),\n nodeImage: z.string(),\n server: z.string(),\n mode: z.string(),\n docker: z.string(),\n nodes: z.array(z.object({ name: z.string(), kubelet: z.string(), kernel: z.string(), runtime: z.string(), os: z.string() })),\n }),\n images: z.record(z.string(), z.string()),\n});\nconst load = z.object({ t: z.number(), idle: z.number(), top: z.array(z.string()), foreign: z.array(z.string()) });\n\n/**\n * This function gives the schema of a record whose amounts have one form. The schema `a` is for a\n * time. The schema `main` is for the main value of a run: a time or a count.\n */\nconst recordOf = <A>(a: z.ZodType<A>, main: z.ZodType<A>) => {\n const band = z.object({ lo: a, hi: a });\n const prediction = z.discriminatedUnion(\"kind\", [\n z.object({ kind: z.literal(\"none\"), outcomes: z.array(z.string()).optional(), why: z.string() }),\n z.object({\n kind: z.literal(\"window\"),\n nodes: z.enum([\"all\", \"paused\"]),\n start: z.object({ from: reference, band }),\n end: z.object({ from: reference, band }).nullable(),\n certain: z.boolean(),\n afterReset: z.object({ life: a, outcome: z.string() }).optional(),\n each: z.boolean().optional(),\n propagation: band.optional(),\n why: z.string(),\n }),\n z.object({ kind: z.literal(\"removal\"), band, why: z.string() }),\n z.object({ kind: z.literal(\"leaves\"), band, why: z.string() }),\n z.object({ kind: z.literal(\"restarts\"), band: band.nullable(), why: z.string() }),\n z.object({ kind: z.literal(\"stays\"), why: z.string() }),\n z.object({ kind: z.literal(\"stalls\"), why: z.string() }),\n z.object({ kind: z.literal(\"measured\"), why: z.string() }),\n ]);\n const cell = z.object({\n id: z.string(),\n name: z.string(),\n template: z.enum([\"T07\", \"T18\"]),\n stack: z.enum([\"service\", \"grpc\"]).optional(),\n substitutes: z.string().nullable(),\n mode: z.enum(MODES),\n workers: count,\n trigger: z.object({\n kind: z.enum(TRIGGERS),\n hold: time.optional(),\n lead: time.optional(),\n cpuMax: z.object({ quota: a, period: a }).optional(),\n }),\n pod: z.object({ replicas: count, grace: time, preStop: time, readiness: probe, liveness: probe, startup: probe.nullable() }),\n declaration: declaration.nullable(),\n workload: z.object({ every: time, timeout: time }),\n prediction,\n calibrates: z.boolean(),\n runs: count,\n });\n const facts = z.object({\n failures: count,\n outcomes: z.record(z.string(), count),\n byNode: z.record(z.string(), count),\n first: z.number().nullable(),\n last: z.number().nullable(),\n deletion: z.number().nullable(),\n intakeAfterDeletion: a.nullable(),\n sliceAfter: a.nullable(),\n propagation: z.record(z.string(), z.object({ after: a.nullable(), by: a }).nullable()),\n sliceEmpty: a.optional(),\n rules: z.record(z.string(), a.nullable()).optional(),\n left: a.optional(),\n signals: z.array(a.nullable()).optional(),\n windows: z.array(z.object({ pod: z.string(), intake: z.number().nullable(), deletion: z.number() })).optional(),\n });\n const verdict = z.object({\n decision: z.enum([\"holds\", \"fails\", \"extend\", \"measured\", \"calibration\", \"void\"]),\n counted: count,\n voided: count,\n failed: count,\n median: main.nullable(),\n why: z.string(),\n checks: z.array(z.object({ ok: z.boolean(), why: z.string(), value: main.nullable(), facts, tail: z.string().optional() })),\n });\n return { cell, verdict, runs: z.array(run), manifests: z.array(manifest), loads: z.array(load), at: z.string() };\n};\n\nconst v2 = recordOf<Quantity>(time, quantitySchema([\"time\", \"count\"]));\nconst v1 = recordOf<number>(z.number(), z.number());\n\n/**\n * This schema accepts a record in the format `/2`. The median and the main value of each run must\n * have the unit of the prediction of the cell (`unitOf`).\n */\nexport const kindCellRecordSchema: z.ZodType<KindCellRecord> = z\n .object({ format: z.literal(FORMAT), ...v2 })\n .superRefine((record, ctx) => {\n const unit = unitOf(record.cell.prediction);\n const values = [record.verdict.median, ...record.verdict.checks.map((check) => check.value)];\n for (const value of values) {\n if (value !== null && value.unit !== unit)\n ctx.addIssue({ code: \"custom\", message: `the main value is in ${value.unit}, and the prediction ${record.cell.prediction.kind} gives ${unit}` });\n }\n });\n\n/** This schema accepts a record in the format `/1`. */\nexport const kindCellRecordV1Schema: z.ZodType<KindCellRecordV1> = z.object({ format: z.literal(FORMAT_V1), ...v1 });\n\n/** This function reads a record of either format from parsed JSON, and gives it in the format `/2`. */\nexport const readRecord = (data: unknown): KindCellRecord => {\n const format = typeof data === \"object\" && data !== null ? (data as { format?: unknown }).format : undefined;\n if (format === FORMAT_V1) return upgrade(kindCellRecordV1Schema.parse(data));\n if (format === FORMAT) return kindCellRecordSchema.parse(data);\n throw new Error(`a kind cell record has the format ${FORMAT} or ${FORMAT_V1}, not ${String(format)}`);\n};\n"],"mappings":"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"}
@@ -245,6 +245,14 @@ type Prediction<A = Quantity> = {
245
245
  * rolling update deletes each old replica in turn. Without the flag, the window is that of the target.
246
246
  */
247
247
  readonly each?: boolean | undefined;
248
+ /**
249
+ * This band is the propagation that the window assumes. It starts at the deletion of the replica
250
+ * of a window. It ends at the first poll of the rules of each node without that replica. The
251
+ * judge holds each such poll to `hi` ± δ, and it does not test a window without the band. A
252
+ * later poll agrees with the law, but the band is short, thus its cell is `extend` (D-EXTEND).
253
+ * An earlier drop only closes the window earlier.
254
+ */
255
+ readonly propagation?: Band<A> | undefined;
248
256
  readonly why: string;
249
257
  } | {
250
258
  readonly kind: "removal";
@@ -610,7 +618,7 @@ declare const reusesReset: (request: Request, requests: readonly Request[], reus
610
618
  * This function gives a reference event of a run, as a stamp, or null when the run does not have it.
611
619
  * The references `deletion` and `intake` are those of the target, or of the replica `pod`.
612
620
  */
613
- declare const referenceOf: (run: KindRun, from: Reference, pod?: string | undefined) => number | null;
621
+ declare const referenceOf: (run: Pick<KindRun, "start" | "acts" | "target" | "lines">, from: Reference, pod?: string | undefined) => number | null;
614
622
  /**
615
623
  * This function gives the propagation of each node for the target of a run, or for the replica of
616
624
  * the address `ip`. It is the time from its deletion (or its crash) to the first poll of the rules
@@ -622,6 +630,22 @@ declare const propagationOf: (run: KindRun, from: number | null, ip?: string | u
622
630
  * sequence of the deletions. A rolling update deletes each old replica in turn.
623
631
  */
624
632
  declare const deletionsOf: (run: KindRun) => Deleted[];
633
+ /**
634
+ * This function gives how long a run observes after the end reference of its last window, or null
635
+ * when its prediction sets no such time. The judge closes a window `end.band.hi` + δ after its end
636
+ * reference. A request that starts by then ends at most the timeout of the workload later: the
637
+ * client cancels it then. A prediction that is not a window, or a window with no end, sets none.
638
+ */
639
+ declare const observationAfter: (cell: Pick<KindCell, "prediction" | "workload">) => Quantity | null;
640
+ /**
641
+ * This function gives the earliest end of the observation of a run, as a stamp, or null when its
642
+ * prediction sets none (`observationAfter`). It is that time after the latest end reference of a
643
+ * window: of the target, or with `each`, of each replica that got a deletion. A run that ends before
644
+ * it can miss its own failures. In a rolling update of gRPC, a call waits in the queue of its channel
645
+ * for a stalled connect. It fails only at its timeout (kind run 40128267098). A reference that is not
646
+ * in the lines yet does not count.
647
+ */
648
+ declare const observationEnd: (cell: KindCell, run: Pick<KindRun, "start" | "acts" | "target" | "replicas" | "lines">) => number | null;
625
649
  /** This function gives the facts of a run that each table reads: its failures by outcome and node, and the times of its target. */
626
650
  declare const factsOf: (cell: KindCell, run: KindRun) => RunFacts;
627
651
  /** This function judges one run against the prediction of its cell. */
@@ -784,5 +808,5 @@ declare const quantileOf: (sorted: readonly number[], p: number) => number;
784
808
  */
785
809
  declare const medianInterval: (sorted: readonly number[]) => [number, number];
786
810
  //#endregion
787
- export { RecordLine as $, KindConfig as $t, TriggerV1 as A, REDIS as At, RunCheck as B, KubeletProbe as Bt, KindCellResult as C, slicesOf as Ct, PredictionV1 as D, HEALTH_PORT as Dt, LoadSample as E, CLIENT as Et, KIND_NOISE as F, showBand as Ft, judgeKind as G, Replica as Gt, checkRun as H, Prediction as Ht, KindDecision as I, Band as It, reusesReset as J, Template as Jt, propagationOf as K, ResetReuse as Kt, KindVerdict as L, Declaration as Lt, upgrade as M, bandOf as Mt, upgradeCell as N, seconds as Nt, RunCheckV1 as O, LOG_LABEL as Ot, Deleted as P, secondsOf as Pt, PodLine as Q, KUBE_PROXY_MODES as Qt, LAST_SLACK as R, KindCell as Rt, KindCellRecordV1 as S, sliceEmptyOf as St, KindVerdictV1 as T, transitionOf as Tt, deletionsOf as U, RedisDeclaration as Ut, RunFacts as V, PodSettings as Vt, factsOf as W, Reference as Wt, Endpoint as X, TriggerKind as Xt, unitOf as Y, Trigger as Yt, Line as Z, Workload as Zt, ClusterManifest as _, restartsAt as _t, STAGES as a, cidrRange as an, byNode as at, FORMAT_V1 as b, signalsOf as bt, readRecord as c, kubeProxyPatch as cn, emptyOf as ct, calibrationOf as d, scenarioProblems as dn, kernelCounts as dt, KindScenario as en, Request as et, cellsTable as f, outcomesOf as ft, BandV1 as g, requestsOf as gt, timeSpread as h, recordsOf as ht, quantileOf as i, cidrOverlap as in, byClients as it, judge as j, SERVER as jt, RunFactsV1 as k, PORT as kt, Judged as l, nodeNames as ln, failuresOf as lt, predictionText as m, podsOf as mt, medianInterval as n, NODE_IMAGES as nn, SliceLine as nt, kindCellRecordSchema as o, goDuration as on, deletionOf as ot, medianText as p, podsAt as pt, referenceOf as q, Stack as qt, middle as r, NodeImage as rn, backendsAt as rt, kindCellRecordV1Schema as s, kindConfig as sn, dropOf as st, RELATIVE as t, KubeProxyMode as tn, RulesLine as tt, TimeSpread as u, scenario as un, intakeOf as ut, EnvironmentManifest as v, rulesOf as vt, KindCellV1 as w, stoppingOf as wt, KindCellRecord as x, sliceDropOf as xt, FORMAT as y, signalOf as yt, Propagated as z, KindRun as zt };
788
- //# sourceMappingURL=judge-Dwa6MqP4.d.ts.map
811
+ export { Line as $, Workload as $t, TriggerV1 as A, LOG_LABEL as At, RunCheck as B, KindCell as Bt, KindCellResult as C, sliceDropOf as Ct, PredictionV1 as D, transitionOf as Dt, LoadSample as E, stoppingOf as Et, KIND_NOISE as F, seconds as Ft, judgeKind as G, RedisDeclaration as Gt, checkRun as H, KubeletProbe as Ht, KindDecision as I, secondsOf as It, propagationOf as J, ResetReuse as Jt, observationAfter as K, Reference as Kt, KindVerdict as L, showBand as Lt, upgrade as M, REDIS as Mt, upgradeCell as N, SERVER as Nt, RunCheckV1 as O, CLIENT as Ot, Deleted as P, bandOf as Pt, Endpoint as Q, TriggerKind as Qt, LAST_SLACK as R, Band as Rt, KindCellRecordV1 as S, signalsOf as St, KindVerdictV1 as T, slicesOf as Tt, deletionsOf as U, PodSettings as Ut, RunFacts as V, KindRun as Vt, factsOf as W, Prediction as Wt, reusesReset as X, Template as Xt, referenceOf as Y, Stack as Yt, unitOf as Z, Trigger as Zt, ClusterManifest as _, recordsOf as _t, STAGES as a, NodeImage as an, backendsAt as at, FORMAT_V1 as b, rulesOf as bt, readRecord as c, goDuration as cn, deletionOf as ct, calibrationOf as d, nodeNames as dn, failuresOf as dt, KUBE_PROXY_MODES as en, PodLine as et, cellsTable as f, scenario as fn, intakeOf as ft, BandV1 as g, podsOf as gt, timeSpread as h, podsAt as ht, quantileOf as i, NODE_IMAGES as in, SliceLine as it, judge as j, PORT as jt, RunFactsV1 as k, HEALTH_PORT as kt, Judged as l, kindConfig as ln, dropOf as lt, predictionText as m, outcomesOf as mt, medianInterval as n, KindScenario as nn, Request as nt, kindCellRecordSchema as o, cidrOverlap as on, byClients as ot, medianText as p, scenarioProblems as pn, kernelCounts as pt, observationEnd as q, Replica as qt, middle as r, KubeProxyMode as rn, RulesLine as rt, kindCellRecordV1Schema as s, cidrRange as sn, byNode as st, RELATIVE as t, KindConfig as tn, RecordLine as tt, TimeSpread as u, kubeProxyPatch as un, emptyOf as ut, EnvironmentManifest as v, requestsOf as vt, KindCellV1 as w, sliceEmptyOf as wt, KindCellRecord as x, signalOf as xt, FORMAT as y, restartsAt as yt, Propagated as z, Declaration as zt };
812
+ //# sourceMappingURL=judge-CctoUEo8.d.ts.map
package/build/judge.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as RecordLine, A as TriggerV1, At as REDIS, B as RunCheck, Bt as KubeletProbe, C as KindCellResult, Ct as slicesOf, D as PredictionV1, Dt as HEALTH_PORT, E as LoadSample, Et as CLIENT, F as KIND_NOISE, Ft as showBand, G as judgeKind, Gt as Replica, H as checkRun, Ht as Prediction, I as KindDecision, It as Band, J as reusesReset, Jt as Template, K as propagationOf, Kt as ResetReuse, L as KindVerdict, Lt as Declaration, M as upgrade, Mt as bandOf, N as upgradeCell, Nt as seconds, O as RunCheckV1, Ot as LOG_LABEL, P as Deleted, Pt as secondsOf, Q as PodLine, R as LAST_SLACK, Rt as KindCell, S as KindCellRecordV1, St as sliceEmptyOf, T as KindVerdictV1, Tt as transitionOf, U as deletionsOf, Ut as RedisDeclaration, V as RunFacts, Vt as PodSettings, W as factsOf, Wt as Reference, X as Endpoint, Xt as TriggerKind, Y as unitOf, Yt as Trigger, Z as Line, Zt as Workload, _ as ClusterManifest, _t as restartsAt, a as STAGES, at as byNode, b as FORMAT_V1, bt as signalsOf, c as readRecord, ct as emptyOf, d as calibrationOf, dt as kernelCounts, et as Request, f as cellsTable, ft as outcomesOf, g as BandV1, gt as requestsOf, h as timeSpread, ht as recordsOf, i as quantileOf, it as byClients, j as judge, jt as SERVER, k as RunFactsV1, kt as PORT, l as Judged, lt as failuresOf, m as predictionText, mt as podsOf, n as medianInterval, nt as SliceLine, o as kindCellRecordSchema, ot as deletionOf, p as medianText, pt as podsAt, q as referenceOf, qt as Stack, r as middle, rt as backendsAt, s as kindCellRecordV1Schema, st as dropOf, t as RELATIVE, tn as KubeProxyMode, tt as RulesLine, u as TimeSpread, ut as intakeOf, v as EnvironmentManifest, vt as rulesOf, w as KindCellV1, wt as stoppingOf, x as KindCellRecord, xt as sliceDropOf, y as FORMAT, yt as signalOf, z as Propagated, zt as KindRun } from "./judge-Dwa6MqP4.js";
2
- export { type Band, type BandV1, CLIENT, type ClusterManifest, type Declaration, type Deleted, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, HEALTH_PORT, type Judged, KIND_NOISE, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindDecision, type KindRun, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, Line, type LoadSample, PORT, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, SERVER, STAGES, SliceLine, type Stack, type Template, type TimeSpread, type Trigger, type TriggerKind, type TriggerV1, type Workload, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, medianInterval, medianText, middle, outcomesOf, podsAt, podsOf, predictionText, propagationOf, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartsAt, reusesReset, rulesOf, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stoppingOf, timeSpread, transitionOf, unitOf, upgrade, upgradeCell };
1
+ import { $ as Line, $t as Workload, A as TriggerV1, At as LOG_LABEL, B as RunCheck, Bt as KindCell, C as KindCellResult, Ct as sliceDropOf, D as PredictionV1, Dt as transitionOf, E as LoadSample, Et as stoppingOf, F as KIND_NOISE, Ft as seconds, G as judgeKind, Gt as RedisDeclaration, H as checkRun, Ht as KubeletProbe, I as KindDecision, It as secondsOf, J as propagationOf, Jt as ResetReuse, K as observationAfter, Kt as Reference, L as KindVerdict, Lt as showBand, M as upgrade, Mt as REDIS, N as upgradeCell, Nt as SERVER, O as RunCheckV1, Ot as CLIENT, P as Deleted, Pt as bandOf, Q as Endpoint, Qt as TriggerKind, R as LAST_SLACK, Rt as Band, S as KindCellRecordV1, St as signalsOf, T as KindVerdictV1, Tt as slicesOf, U as deletionsOf, Ut as PodSettings, V as RunFacts, Vt as KindRun, W as factsOf, Wt as Prediction, X as reusesReset, Xt as Template, Y as referenceOf, Yt as Stack, Z as unitOf, Zt as Trigger, _ as ClusterManifest, _t as recordsOf, a as STAGES, at as backendsAt, b as FORMAT_V1, bt as rulesOf, c as readRecord, ct as deletionOf, d as calibrationOf, dt as failuresOf, et as PodLine, f as cellsTable, ft as intakeOf, g as BandV1, gt as podsOf, h as timeSpread, ht as podsAt, i as quantileOf, it as SliceLine, j as judge, jt as PORT, k as RunFactsV1, kt as HEALTH_PORT, l as Judged, lt as dropOf, m as predictionText, mt as outcomesOf, n as medianInterval, nt as Request, o as kindCellRecordSchema, ot as byClients, p as medianText, pt as kernelCounts, q as observationEnd, qt as Replica, r as middle, rn as KubeProxyMode, rt as RulesLine, s as kindCellRecordV1Schema, st as byNode, t as RELATIVE, tt as RecordLine, u as TimeSpread, ut as emptyOf, v as EnvironmentManifest, vt as requestsOf, w as KindCellV1, wt as sliceEmptyOf, x as KindCellRecord, xt as signalOf, y as FORMAT, yt as restartsAt, z as Propagated, zt as Declaration } from "./judge-CctoUEo8.js";
2
+ export { type Band, type BandV1, CLIENT, type ClusterManifest, type Declaration, type Deleted, Endpoint, type EnvironmentManifest, FORMAT, FORMAT_V1, HEALTH_PORT, type Judged, KIND_NOISE, type KindCell, type KindCellRecord, type KindCellRecordV1, type KindCellResult, type KindCellV1, type KindDecision, type KindRun, type KindVerdict, type KindVerdictV1, type KubeProxyMode, type KubeletProbe, LAST_SLACK, LOG_LABEL, Line, type LoadSample, PORT, PodLine, type PodSettings, type Prediction, type PredictionV1, type Propagated, REDIS, RELATIVE, RecordLine, type RedisDeclaration, type Reference, type Replica, Request, type ResetReuse, RulesLine, type RunCheck, type RunCheckV1, type RunFacts, type RunFactsV1, SERVER, STAGES, SliceLine, type Stack, type Template, type TimeSpread, type Trigger, type TriggerKind, type TriggerV1, type Workload, backendsAt, bandOf, byClients, byNode, calibrationOf, cellsTable, checkRun, deletionOf, deletionsOf, dropOf, emptyOf, factsOf, failuresOf, intakeOf, judge, judgeKind, kernelCounts, kindCellRecordSchema, kindCellRecordV1Schema, medianInterval, medianText, middle, observationAfter, observationEnd, outcomesOf, podsAt, podsOf, predictionText, propagationOf, quantileOf, readRecord, recordsOf, referenceOf, requestsOf, restartsAt, reusesReset, rulesOf, seconds, secondsOf, showBand, signalOf, signalsOf, sliceDropOf, sliceEmptyOf, slicesOf, stoppingOf, timeSpread, transitionOf, unitOf, upgrade, upgradeCell };
package/build/judge.js CHANGED
@@ -1 +1 @@
1
- export { Q as CLIENT, u as FORMAT, d as FORMAT_V1, $ as HEALTH_PORT, h as KIND_NOISE, g as LAST_SLACK, et as LOG_LABEL, tt as PORT, nt as REDIS, T as RELATIVE, rt as SERVER, t as STAGES, k as backendsAt, it as bandOf, A as byClients, j as byNode, a as calibrationOf, o as cellsTable, _ as checkRun, M as deletionOf, v as deletionsOf, N as dropOf, P as emptyOf, y as factsOf, F as failuresOf, I as intakeOf, f as judge, b as judgeKind, L as kernelCounts, n as kindCellRecordSchema, r as kindCellRecordV1Schema, E as medianInterval, s as medianText, D as middle, R as outcomesOf, z as podsAt, B as podsOf, c as predictionText, x as propagationOf, O as quantileOf, i as readRecord, V as recordsOf, S as referenceOf, H as requestsOf, U as restartsAt, C as reusesReset, W as rulesOf, at as seconds, ot as secondsOf, st as showBand, G as signalOf, K as signalsOf, q as sliceDropOf, J as sliceEmptyOf, Y as slicesOf, X as stoppingOf, l as timeSpread, Z as transitionOf, w as unitOf, p as upgrade, m as upgradeCell } from "./judge-reWPMVr4.js";
1
+ export { et as CLIENT, u as FORMAT, d as FORMAT_V1, tt as HEALTH_PORT, h as KIND_NOISE, g as LAST_SLACK, nt as LOG_LABEL, rt as PORT, it as REDIS, D as RELATIVE, at as SERVER, t as STAGES, j as backendsAt, ot as bandOf, M as byClients, N as byNode, a as calibrationOf, o as cellsTable, _ as checkRun, P as deletionOf, v as deletionsOf, F as dropOf, I as emptyOf, y as factsOf, L as failuresOf, R as intakeOf, f as judge, b as judgeKind, z as kernelCounts, n as kindCellRecordSchema, r as kindCellRecordV1Schema, O as medianInterval, s as medianText, k as middle, x as observationAfter, S as observationEnd, B as outcomesOf, V as podsAt, H as podsOf, c as predictionText, C as propagationOf, A as quantileOf, i as readRecord, U as recordsOf, w as referenceOf, W as requestsOf, G as restartsAt, T as reusesReset, K as rulesOf, st as seconds, ct as secondsOf, lt as showBand, q as signalOf, J as signalsOf, Y as sliceDropOf, X as sliceEmptyOf, Z as slicesOf, Q as stoppingOf, l as timeSpread, $ as transitionOf, E as unitOf, p as upgrade, m as upgradeCell } from "./judge-B3CHoiTW.js";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package",
3
3
  "name": "@mark1russell7/kind",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "description": "Kubernetes in Docker as a lab for connection timers: a kind cluster from a scenario, faults in the namespaces of its pods, observations on the one monotonic clock of the VM, and predictions from the ordering law. Its judge entry reads and judges a raw record without Docker.",
6
6
  "keywords": [
7
7
  "kubernetes",
@@ -61,7 +61,7 @@
61
61
  "node": ">=22.19"
62
62
  },
63
63
  "dependencies": {
64
- "@mark1russell7/harness": "^0.12.0",
64
+ "@mark1russell7/harness": "^0.12.2",
65
65
  "@mark1russell7/quantity": "^0.2.0",
66
66
  "@mark1russell7/service": "^0.5.8",
67
67
  "testcontainers": "12.2.0"