sf snapshot: uncommitted changes after 38m inactivity

This commit is contained in:
Mikael Hugo 2026-05-06 14:48:15 +02:00
parent 7b0b346928
commit d8570d059e
7 changed files with 92 additions and 35 deletions

1
.gitignore vendored
View file

@ -29,6 +29,7 @@ Thumbs.db
*~ *~
.idea/ .idea/
.vscode/ .vscode/
.vtcode/
*.code-workspace *.code-workspace
.env .env
.env.* .env.*

View file

@ -8,6 +8,7 @@
"files": { "files": {
"includes": [ "includes": [
"**/*.{js,cjs,mjs,ts,tsx,json,jsonc,css,html}", "**/*.{js,cjs,mjs,ts,tsx,json,jsonc,css,html}",
"!!.vtcode",
"!!.sf", "!!.sf",
"!!.omg", "!!.omg",
"!!**/dist", "!!**/dist",

View file

@ -4115,9 +4115,9 @@ export function getGateCircuitBreaker(gateId) {
*/ */
export function updateGateCircuitBreaker(gateId, updates) { export function updateGateCircuitBreaker(gateId, updates) {
if (!currentDb) return; if (!currentDb) return;
currentDb currentDb
.prepare( .prepare(
`INSERT INTO gate_circuit_breakers ( `INSERT INTO gate_circuit_breakers (
gate_id, state, failure_streak, last_failure_at, opened_at, half_open_attempts, updated_at gate_id, state, failure_streak, last_failure_at, opened_at, half_open_attempts, updated_at
) VALUES ( ) VALUES (
:gate_id, :state, :failure_streak, :last_failure_at, :opened_at, :half_open_attempts, :updated_at :gate_id, :state, :failure_streak, :last_failure_at, :opened_at, :half_open_attempts, :updated_at
@ -4129,23 +4129,25 @@ export function updateGateCircuitBreaker(gateId, updates) {
opened_at = COALESCE(excluded.opened_at, gate_circuit_breakers.opened_at), opened_at = COALESCE(excluded.opened_at, gate_circuit_breakers.opened_at),
half_open_attempts = excluded.half_open_attempts, half_open_attempts = excluded.half_open_attempts,
updated_at = excluded.updated_at`, updated_at = excluded.updated_at`,
) )
.run({ .run({
":gate_id": gateId, ":gate_id": gateId,
":state": updates.state ?? "closed", ":state": updates.state ?? "closed",
":failure_streak": updates.failureStreak ?? 0, ":failure_streak": updates.failureStreak ?? 0,
":last_failure_at": updates.lastFailureAt ?? null, ":last_failure_at": updates.lastFailureAt ?? null,
":opened_at": updates.openedAt ?? null, ":opened_at": updates.openedAt ?? null,
":half_open_attempts": updates.halfOpenAttempts ?? 0, ":half_open_attempts": updates.halfOpenAttempts ?? 0,
":updated_at": new Date().toISOString(), ":updated_at": new Date().toISOString(),
}); });
return { total: 0, avgMs: 0, p50Ms: 0, p95Ms: 0, maxMs: 0 }; return { total: 0, avgMs: 0, p50Ms: 0, p95Ms: 0, maxMs: 0 };
} }
export function getGateLatencyStats(gateId, windowHours = 24) { export function getGateLatencyStats(gateId, windowHours = 24) {
if (!currentDb) { if (!currentDb) {
return { total: 0, avgMs: 0, p50Ms: 0, p95Ms: 0, maxMs: 0 }; return { total: 0, avgMs: 0, p50Ms: 0, p95Ms: 0, maxMs: 0 };
} }
const cutoff = new Date(Date.now() - windowHours * 60 * 60 * 1000).toISOString(); const cutoff = new Date(
Date.now() - windowHours * 60 * 60 * 1000,
).toISOString();
try { try {
const row = currentDb const row = currentDb
.prepare( .prepare(

View file

@ -228,7 +228,11 @@ test("run_records_every_attempt_to_gate_runs", async () => {
type: "verification", type: "verification",
execute: async () => { execute: async () => {
calls++; calls++;
return { outcome: "fail", failureClass: "execution", rationale: `attempt ${calls}` }; return {
outcome: "fail",
failureClass: "execution",
rationale: `attempt ${calls}`,
};
}, },
}); });
await runner.run("audit-gate", makeCtx()); await runner.run("audit-gate", makeCtx());
@ -311,6 +315,7 @@ test("circuitBreaker_when_fails_incrementally_opens_after_threshold", async () =
assert.equal(breaker.state, "open"); assert.equal(breaker.state, "open");
assert.equal(breaker.failureStreak, 5); assert.equal(breaker.failureStreak, 5);
assert.ok(breaker.openedAt != null); assert.ok(breaker.openedAt != null);
assert.equal(calls, 10);
}); });
test("circuitBreaker_when_open_blocks_execution", async () => { test("circuitBreaker_when_open_blocks_execution", async () => {
@ -366,7 +371,11 @@ test("circuitBreaker_half_open_fail_reopens", async () => {
runner.register({ runner.register({
id: "cb-reopen", id: "cb-reopen",
type: "verification", type: "verification",
execute: async () => ({ outcome: "fail", failureClass: "execution", rationale: "nope" }), execute: async () => ({
outcome: "fail",
failureClass: "execution",
rationale: "nope",
}),
}); });
updateGateCircuitBreaker("cb-reopen", { updateGateCircuitBreaker("cb-reopen", {
state: "half-open", state: "half-open",
@ -425,7 +434,11 @@ test("validateGate_when_missing_id_returns_false", () => {
}); });
test("validateGate_when_empty_id_returns_false", () => { test("validateGate_when_empty_id_returns_false", () => {
const result = validateGate({ id: "", type: "policy", execute: async () => ({}) }); const result = validateGate({
id: "",
type: "policy",
execute: async () => ({}),
});
assert.equal(result.valid, false); assert.equal(result.valid, false);
assert.ok(result.reason.includes("id")); assert.ok(result.reason.includes("id"));
}); });
@ -450,8 +463,15 @@ test("validateGate_when_execute_not_function_returns_false", () => {
test("runner_register_when_invalid_gate_throws", () => { test("runner_register_when_invalid_gate_throws", () => {
const runner = new UokGateRunner(); const runner = new UokGateRunner();
assert.throws(() => runner.register({ id: "", type: "policy", execute: async () => ({}) }), /id/); assert.throws(
assert.throws(() => runner.register({ id: "x", type: "", execute: async () => ({}) }), /type/); () =>
runner.register({ id: "", type: "policy", execute: async () => ({}) }),
/id/,
);
assert.throws(
() => runner.register({ id: "x", type: "", execute: async () => ({}) }),
/type/,
);
assert.throws(() => runner.register({ id: "x", type: "policy" }), /execute/); assert.throws(() => runner.register({ id: "x", type: "policy" }), /execute/);
assert.throws(() => runner.register(null), /object/); assert.throws(() => runner.register(null), /object/);
}); });

View file

@ -160,6 +160,16 @@ test("decide_when_stale_with_budget_returns_retry", () => {
assert.equal(d.action, "retry"); assert.equal(d.action, "retry");
}); });
test("decide_when_runaway_recovered_requires_explicit_reset", () => {
const d = decideUnitRuntimeDispatch({
status: "runaway-recovered",
retryCount: 0,
maxRetries: 1,
});
assert.equal(d.action, "block");
assert.equal(d.reasonCode, "runaway-recovery-reset-required");
});
test("decide_when_blocked_returns_notify", () => { test("decide_when_blocked_returns_notify", () => {
const d = decideUnitRuntimeDispatch({ status: "blocked" }); const d = decideUnitRuntimeDispatch({ status: "blocked" });
assert.equal(d.action, "notify"); assert.equal(d.action, "notify");

View file

@ -41,16 +41,32 @@ function collectGateMetrics() {
const lines = []; const lines = [];
for (const gateId of GATE_NAMES) { for (const gateId of GATE_NAMES) {
const stats = getGateRunStats(gateId, 24); const stats = getGateRunStats(gateId, 24);
lines.push(fmtCounter("uok_gate_runs_total", stats.total, { gate_id: gateId })); lines.push(
lines.push(fmtCounter("uok_gate_runs_passed_total", stats.pass, { gate_id: gateId })); fmtCounter("uok_gate_runs_total", stats.total, { gate_id: gateId }),
lines.push(fmtCounter("uok_gate_runs_failed_total", stats.fail, { gate_id: gateId })); );
lines.push(fmtCounter("uok_gate_runs_retry_total", stats.retry, { gate_id: gateId })); lines.push(
fmtCounter("uok_gate_runs_passed_total", stats.pass, { gate_id: gateId }),
);
lines.push(
fmtCounter("uok_gate_runs_failed_total", stats.fail, { gate_id: gateId }),
);
lines.push(
fmtCounter("uok_gate_runs_retry_total", stats.retry, { gate_id: gateId }),
);
const latency = getGateLatencyStats(gateId, 24); const latency = getGateLatencyStats(gateId, 24);
lines.push(fmtGauge("uok_gate_latency_avg_ms", latency.avgMs, { gate_id: gateId })); lines.push(
lines.push(fmtGauge("uok_gate_latency_p50_ms", latency.p50Ms, { gate_id: gateId })); fmtGauge("uok_gate_latency_avg_ms", latency.avgMs, { gate_id: gateId }),
lines.push(fmtGauge("uok_gate_latency_p95_ms", latency.p95Ms, { gate_id: gateId })); );
lines.push(fmtGauge("uok_gate_latency_max_ms", latency.maxMs, { gate_id: gateId })); lines.push(
fmtGauge("uok_gate_latency_p50_ms", latency.p50Ms, { gate_id: gateId }),
);
lines.push(
fmtGauge("uok_gate_latency_p95_ms", latency.p95Ms, { gate_id: gateId }),
);
lines.push(
fmtGauge("uok_gate_latency_max_ms", latency.maxMs, { gate_id: gateId }),
);
const breaker = getGateCircuitBreaker(gateId); const breaker = getGateCircuitBreaker(gateId);
const stateMap = { closed: 0, "half-open": 1, open: 2 }; const stateMap = { closed: 0, "half-open": 1, open: 2 };
@ -61,9 +77,13 @@ function collectGateMetrics() {
}), }),
); );
lines.push( lines.push(
fmtGauge("uok_gate_circuit_breaker_failure_streak", breaker.failureStreak, { fmtGauge(
gate_id: gateId, "uok_gate_circuit_breaker_failure_streak",
}), breaker.failureStreak,
{
gate_id: gateId,
},
),
); );
} }
return lines; return lines;

View file

@ -98,11 +98,7 @@ export const UNIT_RUNTIME_TRANSITIONS = {
notified: ["queued"], notified: ["queued"],
}; };
const DEFAULT_UNIT_RUNTIME_MAX_RETRIES = 1; const DEFAULT_UNIT_RUNTIME_MAX_RETRIES = 1;
const RETRYABLE_TERMINAL_STATUSES = new Set([ const RETRYABLE_TERMINAL_STATUSES = new Set(["failed", "stale"]);
"failed",
"stale",
"runaway-recovered",
]);
function hasUpdate(updates, key) { function hasUpdate(updates, key) {
return Object.hasOwn(updates, key); return Object.hasOwn(updates, key);
} }
@ -242,6 +238,13 @@ export function decideUnitRuntimeDispatch(record, options = {}) {
...common, ...common,
}; };
} }
if (state.status === "runaway-recovered") {
return {
action: "block",
reasonCode: "runaway-recovery-reset-required",
...common,
};
}
if (RETRYABLE_TERMINAL_STATUSES.has(state.status)) { if (RETRYABLE_TERMINAL_STATUSES.has(state.status)) {
if (remaining > 0) { if (remaining > 0) {
return { return {