@volter/supercode-orchestrator 0.5.39 → 0.5.40
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/board/dispatch.mjs +9 -4
- package/board/engine.mjs +5 -0
- package/package.json +1 -1
package/board/dispatch.mjs
CHANGED
|
@@ -2237,7 +2237,11 @@ async function notify(board, fleet, report, entry) {
|
|
|
2237
2237
|
// and answers, each its own message, in full. A notice the board already recorded as taken by this address is never sent again.
|
|
2238
2238
|
// only events that would be told count: a stale one or the recipient's own act never supersedes one that would be
|
|
2239
2239
|
const sendable = (ev) => { const p = json(ev.payload, {}) ?? {}; return !(maxAge && now() - Number(ev.created_at) > maxAge) && !(p.by && p.by === sub.chat_id); };
|
|
2240
|
-
|
|
2240
|
+
// News is coalesced by its subject, not its kind alone: a later `assigned` says what an earlier one did, but each
|
|
2241
|
+
// subtask added, dependency linked or unlinked is its own fact, keyed by the card it names; a comment or an
|
|
2242
|
+
// answer is always its own message.
|
|
2243
|
+
const subjectOf = (ev) => { const p = json(ev.payload, {}) ?? {}; return ['commented', 'answered'].includes(ev.kind) ? `${ev.kind}:${ev.id}` : `${ev.kind}:${p.subtask ?? p.parent ?? p.child ?? ''}`; };
|
|
2244
|
+
const latest = new Map(events.filter((ev) => sendable(ev)).map((ev) => [subjectOf(ev), ev.id]));
|
|
2241
2245
|
const sentBefore = board.db.prepare("SELECT 1 FROM task_events WHERE task_id = ? AND kind IN ('notice_delivered', 'notice_filed') AND json_extract(payload, '$.source_event') = ? AND json_extract(payload, '$.address') = ? LIMIT 1");
|
|
2242
2246
|
for (const ev of events) {
|
|
2243
2247
|
const p = json(ev.payload, {}) ?? {};
|
|
@@ -2246,7 +2250,7 @@ async function notify(board, fleet, report, entry) {
|
|
|
2246
2250
|
if (sentBefore.get(sub.task_id, ev.id, sub.chat_id)) { cursor = ev.id; continue; }
|
|
2247
2251
|
// nobody is told of their own act
|
|
2248
2252
|
if (p.by && p.by === sub.chat_id) { cursor = ev.id; continue; }
|
|
2249
|
-
if (latest.has(ev
|
|
2253
|
+
if (latest.has(subjectOf(ev)) && latest.get(subjectOf(ev)) !== ev.id) { cursor = ev.id; (report.superseded_notices_passed ??= 0); report.superseded_notices_passed += 1; continue; }
|
|
2250
2254
|
let text;
|
|
2251
2255
|
if (ev.kind === 'commented') {
|
|
2252
2256
|
if (p.author === sub.chat_id) { cursor = ev.id; continue; }
|
|
@@ -2305,13 +2309,14 @@ async function notify(board, fleet, report, entry) {
|
|
|
2305
2309
|
// attempt it is abandoned (its terminal state, `notice_abandoned`, recorded once) and the cursor moves past it;
|
|
2306
2310
|
// the subscription stays, so the notices after it are still told (dropping it lost them when the round
|
|
2307
2311
|
// subscribed the session again from the card's newest event).
|
|
2308
|
-
|
|
2312
|
+
// failures are counted per notice: a failure of a later notice starts its own count
|
|
2313
|
+
const failures = failed ? (Number(sub.delivery_metadata?.failing_event) === trying ? Number(sub.delivery_metadata?.failures ?? 0) : 0) + 1 : 0;
|
|
2309
2314
|
if (failed && failures >= NOTICE_ATTEMPTS && trying) {
|
|
2310
2315
|
event(db, sub.task_id, 'notice_abandoned', { source_event: trying, address: sub.chat_id, attempts: failures, error: failed });
|
|
2311
2316
|
db.prepare(`UPDATE kanban_notify_subs SET last_event_id = ?, delivery_metadata = ? ${where} AND last_event_id < ?`).run(trying, JSON.stringify({ ...sub.delivery_metadata, failures: 0, next_at: 0 }), ...identity, trying);
|
|
2312
2317
|
return;
|
|
2313
2318
|
}
|
|
2314
|
-
if (failures !== Number(sub.delivery_metadata?.failures ?? 0)) db.prepare(`UPDATE kanban_notify_subs SET delivery_metadata = ? ${where}`).run(JSON.stringify({ ...sub.delivery_metadata, failures, next_at: failed ? now() + Math.min(1800, 30 * 2 ** (failures - 1)) : 0 }), ...identity);
|
|
2319
|
+
if (failures !== Number(sub.delivery_metadata?.failures ?? 0) || (failed && Number(sub.delivery_metadata?.failing_event) !== trying)) db.prepare(`UPDATE kanban_notify_subs SET delivery_metadata = ? ${where}`).run(JSON.stringify({ ...sub.delivery_metadata, failures, failing_event: failed ? trying : null, next_at: failed ? now() + Math.min(1800, 30 * 2 ** (failures - 1)) : 0 }), ...identity);
|
|
2315
2320
|
if (task?.status === 'archived' && cursor >= events.at(-1).id) db.prepare(`DELETE FROM kanban_notify_subs ${where}`).run(...identity);
|
|
2316
2321
|
});
|
|
2317
2322
|
});
|
package/board/engine.mjs
CHANGED
|
@@ -974,6 +974,11 @@ export function diagnose(db, task, { failureLimit = DEFAULT_FAILURE_LIMIT, stran
|
|
|
974
974
|
? { kind: 'effect_failed', severity: 'warning', title: `its ${kind} failed for good after ${row.attempts} attempts: ${row.last_error ?? 'no reason recorded'}`, data: { effect: row.id, kind, attempts: row.attempts }, actions: [] }
|
|
975
975
|
: { kind: 'effect_stalled', severity: 'error', title: `its ${kind} has failed ${row.attempts} times and is still tried: ${row.last_error ?? 'no reason recorded'}`, data: { effect: row.id, kind, attempts: row.attempts }, actions: [] });
|
|
976
976
|
}
|
|
977
|
+
// notices the board gave up on: each one, with the event it was about and who was not told
|
|
978
|
+
for (const row of all(db, "SELECT payload FROM task_events WHERE task_id = ? AND kind = 'notice_abandoned' ORDER BY id DESC LIMIT 20", task.id)) {
|
|
979
|
+
const p = json(row.payload, {}) ?? {};
|
|
980
|
+
out.push({ kind: 'notice_abandoned', severity: 'warning', title: `the notice of event ${p.source_event} to ${p.address} was abandoned after ${p.attempts} attempts: ${p.error ?? 'no reason recorded'}`, data: { event: p.source_event, address: p.address, attempts: p.attempts }, actions: [] });
|
|
981
|
+
}
|
|
977
982
|
const order = ['critical', 'error', 'warning'];
|
|
978
983
|
return out.sort((a, b) => order.indexOf(a.severity) - order.indexOf(b.severity));
|
|
979
984
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@volter/supercode-orchestrator",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.40",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "The orchestrator runtime over the Volter Harness ontology: one typed operational model whose folder is its serialization, read and written through the harness orchestration doors (docs/ORCHESTRATOR-IR.md)",
|
|
6
6
|
"exports": {
|