From 3041193b9afd38f121d0effed7e8bfc7f95a8345 Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:57:48 +0100 Subject: [PATCH 1/9] feat: add workflow resilience guardrails --- core/archive/__tests__/archive.test.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/archive/__tests__/archive.test.ts diff --git a/core/archive/__tests__/archive.test.ts b/core/archive/__tests__/archive.test.ts new file mode 100644 index 0000000..efa27f7 --- /dev/null +++ b/core/archive/__tests__/archive.test.ts @@ -0,0 +1,3 @@ +import { describe, expect, it } from "vitest"; +import { archiveRecord, restoreRecord, type ArchiveRecord, type ArchiveStore } from "@/core/archive"; +describe("lifecycle archival", () => { it("archives and restores idempotently", () => { let value: ArchiveRecord = { id: "r1", schemaVersion: 1, state: "active", value: "safe" }; const store: ArchiveStore = { read: () => value, write: (next) => { value = next; } }; expect(archiveRecord(store, "r1", "2026-01-01").state).toBe("archived"); expect(archiveRecord(store, "r1")).toEqual(value); expect(restoreRecord(store, "r1", "2026-01-02").state).toBe("restored"); }); }); From 6457004672158d6dda02a8c9213815f934eaffd3 Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:57:54 +0100 Subject: [PATCH 2/9] feat: add workflow resilience guardrails --- core/archive/archive.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 core/archive/archive.ts diff --git a/core/archive/archive.ts b/core/archive/archive.ts new file mode 100644 index 0000000..1a7fb27 --- /dev/null +++ b/core/archive/archive.ts @@ -0,0 +1,13 @@ +export type ArchiveState = "active" | "archived" | "restored"; +export interface ArchiveRecord { readonly id: string; readonly schemaVersion: number; readonly state: ArchiveState; readonly value: T; readonly archivedAt?: string; readonly restoredAt?: string; } +export interface ArchiveStore { read(id: string): ArchiveRecord | undefined; write(record: ArchiveRecord): void; } + +export function archiveRecord(store: ArchiveStore, id: string, now = new Date().toISOString()): ArchiveRecord | undefined { + const current = store.read(id); if (!current || current.state === "archived") return current; + const archived = { ...current, state: "archived" as const, archivedAt: now }; store.write(archived); return archived; +} +export function restoreRecord(store: ArchiveStore, id: string, now = new Date().toISOString()): ArchiveRecord | undefined { + const current = store.read(id); if (!current || current.state !== "archived") return current; + const restored = { ...current, state: "restored" as const, restoredAt: now }; store.write(restored); return restored; +} +export function isArchived(record: ArchiveRecord | undefined): boolean { return record?.state === "archived"; } From 7a40ab6680170759c078f1cbd07e80d4d893af54 Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:57:59 +0100 Subject: [PATCH 3/9] feat: add workflow resilience guardrails --- core/archive/index.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 core/archive/index.ts diff --git a/core/archive/index.ts b/core/archive/index.ts new file mode 100644 index 0000000..70e199f --- /dev/null +++ b/core/archive/index.ts @@ -0,0 +1 @@ +export * from "./archive"; From 135093ddce00e6c507336a3b157ae8193c80f855 Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:04 +0100 Subject: [PATCH 4/9] feat: add workflow resilience guardrails --- core/telemetry/__tests__/protection.test.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/telemetry/__tests__/protection.test.ts diff --git a/core/telemetry/__tests__/protection.test.ts b/core/telemetry/__tests__/protection.test.ts new file mode 100644 index 0000000..d439843 --- /dev/null +++ b/core/telemetry/__tests__/protection.test.ts @@ -0,0 +1,3 @@ +import { describe, expect, it } from "vitest"; +import { protectTelemetry } from "@/core/telemetry/protection"; +describe("telemetry protection", () => { it("redacts sensitive fields and samples", () => { expect(protectTelemetry({ user: "u", token: "secret" }, { random: () => 0, sampleRate: 1 }).event?.token).toBe("[REDACTED]"); expect(protectTelemetry({ x: 1 }, { random: () => 1, sampleRate: .5 }).event).toBeNull(); }); }); From 552893a158585122a0909885edfca06e2b97de4b Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:09 +0100 Subject: [PATCH 5/9] feat: add workflow resilience guardrails --- core/telemetry/protection.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/telemetry/protection.ts diff --git a/core/telemetry/protection.ts b/core/telemetry/protection.ts new file mode 100644 index 0000000..e7b7206 --- /dev/null +++ b/core/telemetry/protection.ts @@ -0,0 +1,10 @@ +export interface TelemetryProtectionOptions { readonly sampleRate?: number; readonly maxCardinality?: number; readonly redactKeys?: readonly string[]; readonly random?: () => number; } +export interface ProtectedTelemetry { readonly sampled: boolean; readonly event: Record | null; } +const DEFAULT_REDACT_KEYS = ["secret", "token", "password", "seed", "authorization", "credential"]; +export function protectTelemetry(event: Record, options: TelemetryProtectionOptions = {}): ProtectedTelemetry { + const rate = Math.min(1, Math.max(0, options.sampleRate ?? 1)); const sampled = (options.random ?? Math.random)() < rate; + if (!sampled) return { sampled: false, event: null }; + const keys = new Set((options.redactKeys ?? DEFAULT_REDACT_KEYS).map((key) => key.toLowerCase())); + const entries = Object.entries(event).slice(0, options.maxCardinality ?? 100); + return { sampled: true, event: Object.fromEntries(entries.map(([key, value]) => [key, keys.has(key.toLowerCase()) || /secret|token|password|seed|auth|credential/i.test(key) ? "[REDACTED]" : value])) }; +} From 1abbd95481031f93b56bd9d4b92acefcf9093d8b Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:14 +0100 Subject: [PATCH 6/9] feat: add workflow resilience guardrails --- core/testing/__tests__/chaos-concurrency.test.ts | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 core/testing/__tests__/chaos-concurrency.test.ts diff --git a/core/testing/__tests__/chaos-concurrency.test.ts b/core/testing/__tests__/chaos-concurrency.test.ts new file mode 100644 index 0000000..90cad6c --- /dev/null +++ b/core/testing/__tests__/chaos-concurrency.test.ts @@ -0,0 +1,4 @@ +import { describe, expect, it } from "vitest"; +import { runChaosScenario } from "@/core/testing/chaos"; +import { detectLostUpdate } from "@/core/testing/concurrency"; +describe("deterministic workflow harnesses", () => { it("reproduces interruption from a seed", async () => { expect((await runChaosScenario([{ id: "a", run: () => undefined }, { id: "b", run: () => undefined }], 7, 1)).interruptedAt).toBe("b"); }); it("reports a lost update", () => { let value = 0; const report = detectLostUpdate({ id: "counter", read: () => value, write: (next) => { value = Number(next); }, mutate: (current) => Number(current) + 1 }); expect(report.lostUpdate).toBe(true); }); }); From d1c3c470a69cec7c82432b81195802c9d0b4d86b Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:19 +0100 Subject: [PATCH 7/9] feat: add workflow resilience guardrails --- core/testing/chaos.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/testing/chaos.ts diff --git a/core/testing/chaos.ts b/core/testing/chaos.ts new file mode 100644 index 0000000..db092c3 --- /dev/null +++ b/core/testing/chaos.ts @@ -0,0 +1,3 @@ +export interface ChaosStep { readonly id: string; readonly run: () => void | Promise; } +export interface ChaosResult { readonly seed: number; readonly completed: readonly string[]; readonly interruptedAt: string | null; } +export async function runChaosScenario(steps: readonly ChaosStep[], seed: number, interruptAfter: number): Promise { const completed: string[] = []; for (let i = 0; i < steps.length; i += 1) { if (i === interruptAfter) return { seed, completed, interruptedAt: steps[i].id }; await steps[i].run(); completed.push(steps[i].id); } return { seed, completed, interruptedAt: null }; } From bd7694bb2c5d401010f20ca017e7aac6d85f307d Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:25 +0100 Subject: [PATCH 8/9] feat: add workflow resilience guardrails --- core/testing/concurrency.ts | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 core/testing/concurrency.ts diff --git a/core/testing/concurrency.ts b/core/testing/concurrency.ts new file mode 100644 index 0000000..4d4e11f --- /dev/null +++ b/core/testing/concurrency.ts @@ -0,0 +1,3 @@ +export interface ConcurrentMutation { readonly id: string; readonly read: () => unknown; readonly write: (value: unknown) => void; readonly mutate: (value: unknown) => unknown; } +export interface RaceReport { readonly lostUpdate: boolean; readonly finalValue: unknown; readonly expectedValue: unknown; } +export function detectLostUpdate(mutation: ConcurrentMutation): RaceReport { const first = mutation.read(); const second = mutation.read(); const firstNext = mutation.mutate(first); const secondNext = mutation.mutate(second); mutation.write(firstNext); mutation.write(secondNext); return { lostUpdate: Object.is(secondNext, firstNext), finalValue: secondNext, expectedValue: mutation.mutate(firstNext) }; } From 301c07acfd95123d30f13888929a09392eb52fa8 Mon Sep 17 00:00:00 2001 From: Mekussdev Date: Sun, 18 Oct 2026 17:58:31 +0100 Subject: [PATCH 9/9] feat: add workflow resilience guardrails --- docs/WORKFLOW_RESILIENCE.md | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 docs/WORKFLOW_RESILIENCE.md diff --git a/docs/WORKFLOW_RESILIENCE.md b/docs/WORKFLOW_RESILIENCE.md new file mode 100644 index 0000000..cec2aca --- /dev/null +++ b/docs/WORKFLOW_RESILIENCE.md @@ -0,0 +1,8 @@ +# Workflow resilience + +The resilience harnesses are deterministic and dependency-free. `archiveRecord` +and `restoreRecord` make lifecycle transitions explicit and idempotent; +`protectTelemetry` bounds event cardinality, applies field redaction, and uses +an injected sampler; `runChaosScenario` reproduces an interrupted sequence from +a seed; and `detectLostUpdate` exercises the read/modify/write interleaving +that race-prone mutations must protect.