C3: the forge module reads failed-steps' fill through the bundle's state call mesh/state.info

Builds: design 47 G §623, A-214

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-12 22:19:49 +01:00
parent 0aa5d6637c
commit f112011727
2 changed files with 17 additions and 8 deletions
+14 -5
View File
@@ -14,6 +14,7 @@
// judged on each `branch-moved` and each pull request's close (Heads, below).
import type { State } from "@novox/mesh-sdk/state";
import { askRuntime } from "@novox/mesh-sdk/runtime";
import {
failedStepsByModuleFingerprintStepKey as failedKey, type FailedStep, type PullClosedEvent, type PullMergedEvent, type PullUpdatedEvent,
} from "@novox/mesh-delivery-contracts-git";
@@ -21,8 +22,14 @@ import type { StepEndedEvent } from "@novox/mesh-delivery-contracts-mesh-pipelin
/** The key prefix of the commits' fingerprints (§487). */
export const SEEN = "seen.";
/** The template state's cap (design 47 *The runner's kept data*): the fill is shown against it. */
export const CAP_BYTES = 64 * 1024 * 1024;
/** The bucket's fill against its caps as the bundle's state call `mesh/state.info` answers it (design 47 G §623). */
export interface StateFill {
bytes: number;
keys: number;
max_bytes: number;
}
/** The fill of the state `failed-steps`, asked of the runtime (§623). */
export const askFill = (): Promise<StateFill> => askRuntime("mesh/state.info", { state: "failed-steps" }) as Promise<StateFill>;
/** How long a `seen` key is kept after its `step-ended`, and how many go on one event (§581). */
export const SEEN_DAYS = 30;
@@ -57,7 +64,9 @@ export class FailedSteps {
constructor(private readonly st: State<unknown>, private readonly log: (line: string) => void = () => {},
/** Whether a commit is now a watched branch's head or an open pull request's head. */
private readonly current: (commit: string) => boolean = () => false) {}
private readonly current: (commit: string) => boolean = () => false,
/** The state's fill against its caps: the bundle's state call (§623). */
private readonly fillOf: () => Promise<StateFill> = askFill) {}
private turn<T>(act: () => Promise<T>): Promise<T> {
const r = this.chain.then(act, act);
@@ -166,9 +175,9 @@ export class FailedSteps {
held(commit?: string): Promise<{ failed: FailedStep[]; seen: Seen[]; fill: { keys: number; bytes: number; cap: number } }> {
return this.turn(async () => {
const { failed, seen } = await this.all();
const bytes = [...failed.values(), ...seen.values()].reduce((n, v) => n + JSON.stringify(v).length, 0);
const info = await this.fillOf();
return { failed: [...failed.values()].filter((f) => !commit || f.commits.includes(commit)).sort((a, b) => (a.run < b.run ? -1 : 1)),
seen: [...seen.values()].filter((s) => !commit || s.commit === commit), fill: { keys: failed.size + seen.size, bytes, cap: CAP_BYTES } };
seen: [...seen.values()].filter((s) => !commit || s.commit === commit), fill: { keys: info.keys, bytes: info.bytes, cap: info.max_bytes } };
});
}
+3 -3
View File
@@ -82,14 +82,14 @@ test("git_failed_steps shows what is held and its fill, and git_failed_steps_dro
const { failedStepsTools } = await import("../tools/failedsteps.js");
const { runAs } = await import("@novox/mesh-sdk/callers");
const { st } = memory();
const f = new FailedSteps(st as any);
const f = new FailedSteps(st as any, () => {}, () => false, async () => ({ bytes: 4321, keys: 2, max_bytes: 64 * 1024 * 1024 }));
await f.stepEnded(ended(C2, "failed"));
const acts: any[] = [];
let recorded = true;
const [read, drop] = failedStepsTools(f, async (a) => { if (!recorded) throw new Error("no hand-act log"); acts.push(a); });
const shown: any = await read.run({});
assert.deepEqual([shown.failed.map((x: any) => [x.key, x.run]), shown.seen.length, shown.fill.keys, shown.fill.cap],
[[["nats.fp-1.unit", "main.7.nats-22"]], 1, 2, 64 * 1024 * 1024]);
assert.deepEqual([shown.failed.map((x: any) => [x.key, x.run]), shown.seen.length, shown.fill.keys, shown.fill.bytes, shown.fill.cap],
[[["nats.fp-1.unit", "main.7.nats-22"]], 1, 2, 4321, 64 * 1024 * 1024], "the fill is the state call's answer, not counted from the keys");
assert.equal(((await read.run({ commit: C1 })) as any).failed.length, 0, "a commit asked for filters");
recorded = false;
await assert.rejects(runAs({ "mesh/caller": "operator" }, {}, () => drop.run({ key: "nats.fp-1.unit", why: "stale" })), /hand act was not recorded, so nothing was dropped/);