metheus-governance-mcp-cli 0.2.283 → 0.2.284

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/cli.mjs CHANGED
@@ -145,10 +145,10 @@ import {
145
145
  findEarlierProcessableArchiveDuplicate,
146
146
  findRecentTelegramMessageEnvelope,
147
147
  isTelegramLocalInboundEnvelopeForRoute,
148
- isInboundArchiveKind,
149
- normalizeTelegramMessageEnvelope as normalizeRunnerTelegramMessageEnvelope,
150
- normalizeArchiveCommentRecord,
151
- selectPendingArchiveComments,
148
+ isInboundArchiveKind,
149
+ normalizeTelegramMessageEnvelope as normalizeRunnerTelegramMessageEnvelope,
150
+ normalizeArchiveCommentRecord,
151
+ selectPendingArchiveComments,
152
152
  printRunnerResult,
153
153
  } from "./lib/runner-helpers.mjs";
154
154
  import {
@@ -6430,10 +6430,7 @@ function markRunnerRequestLifecycle({
6430
6430
  const authoritativeDecisionBundle = resolvedDecisionBundleValidation.ok === true
6431
6431
  ? safeObject(resolvedDecisionBundleValidation.bundle)
6432
6432
  : runnerRequestAuthoritativeDecisionBundle(existing);
6433
- const effectiveReplyToMessageID = intFromRawAllowZero(
6434
- replyToMessageID,
6435
- intFromRawAllowZero(existing.last_reply_to_message_id, 0),
6436
- );
6433
+ const effectiveReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
6437
6434
  const lastReplyMessageEnvelope = buildTelegramBotReplyEnvelope({
6438
6435
  sourceEnvelope: sourceMessageEnvelope,
6439
6436
  chatID: existing.chat_id,
@@ -6454,14 +6451,14 @@ function markRunnerRequestLifecycle({
6454
6451
  senderUsername: normalizedCurrentBotSelector,
6455
6452
  body: aiReplyPreview,
6456
6453
  });
6457
- const shouldRefreshAttemptedDeliveryEnvelope = (
6458
- aiReplyGenerated === true
6459
- || String(aiReplyPreview || "").trim().length > 0
6460
- || String(deliveryStatus || "").trim().length > 0
6461
- || String(transportError || "").trim().length > 0
6462
- || intFromRawAllowZero(replyToMessageID, 0) > 0
6463
- || intFromRawAllowZero(lastReplyMessageThreadID, 0) > 0
6464
- );
6454
+ const shouldRefreshAttemptedDeliveryEnvelope = (
6455
+ aiReplyGenerated === true
6456
+ || String(aiReplyPreview || "").trim().length > 0
6457
+ || String(deliveryStatus || "").trim().length > 0
6458
+ || String(transportError || "").trim().length > 0
6459
+ || intFromRawAllowZero(replyToMessageID, 0) > 0
6460
+ || intFromRawAllowZero(lastReplyMessageThreadID, 0) > 0
6461
+ );
6465
6462
  const rootEffectiveExecutionContractTargets = uniqueOrderedStrings(
6466
6463
  [
6467
6464
  ...ensureArray(authoritativeDecisionBundle.execution_contract_targets),
@@ -6504,6 +6501,13 @@ function markRunnerRequestLifecycle({
6504
6501
  const normalizedOutcome = String(outcome || "").trim().toLowerCase();
6505
6502
  const normalizedFailureReplyClassification = String(failureReplyClassification || "").trim().toLowerCase();
6506
6503
  const normalizedFailureFacts = safeObject(failureFacts);
6504
+ const shouldPersistReplyAnchor = (
6505
+ aiReplyGenerated === true
6506
+ || intFromRawAllowZero(lastReplyMessageID, 0) > 0
6507
+ || ["delivered", "dry_run", "archive_error", "failed_transport"].includes(normalizedDeliveryStatus)
6508
+ || String(transportError || "").trim().length > 0
6509
+ || ["replied", "delivery_failed_after_generation"].includes(normalizedOutcome)
6510
+ );
6507
6511
  const shouldRemainRunningAfterReply = authoritativeDecisionBundle.should_close_after_reply === true
6508
6512
  ? false
6509
6513
  : authoritativeDecisionBundle.should_close_after_reply === false
@@ -6907,7 +6911,9 @@ function markRunnerRequestLifecycle({
6907
6911
  last_source_message_thread_id: intFromRawAllowZero(parsed.messageThreadID, 0) || existing.last_source_message_thread_id,
6908
6912
  last_reply_message_id: intFromRawAllowZero(lastReplyMessageID, 0) || existing.last_reply_message_id,
6909
6913
  last_reply_message_thread_id: intFromRawAllowZero(lastReplyMessageThreadID, 0) || existing.last_reply_message_thread_id,
6910
- last_reply_to_message_id: intFromRawAllowZero(replyToMessageID, 0) || existing.last_reply_to_message_id,
6914
+ last_reply_to_message_id: shouldPersistReplyAnchor
6915
+ ? effectiveReplyToMessageID
6916
+ : existing.last_reply_to_message_id,
6911
6917
  last_reply_message_envelope: persistSuccessfulReplyEnvelope
6912
6918
  ? lastReplyMessageEnvelope
6913
6919
  : safeObject(existing.last_reply_message_envelope),
@@ -13,6 +13,14 @@ function intFromRawAllowZero(value, fallback = 0) {
13
13
  return Number.isFinite(parsed) ? parsed : fallback;
14
14
  }
15
15
 
16
+ function buildReplyAnchorMismatchError(expectedReplyToMessageID, observedReplyToMessageID) {
17
+ return `reply anchor mismatch: expected ${String(expectedReplyToMessageID || 0)}, observed ${String(observedReplyToMessageID || 0)}`;
18
+ }
19
+
20
+ function buildMessageThreadMismatchError(expectedMessageThreadID, observedMessageThreadID) {
21
+ return `message thread mismatch: expected ${String(expectedMessageThreadID || 0)}, observed ${String(observedMessageThreadID || 0)}`;
22
+ }
23
+
16
24
  function requireArchiveDependency(deps, key) {
17
25
  const candidate = deps?.[key];
18
26
  if (typeof candidate !== "function") {
@@ -83,11 +91,41 @@ export async function finalizeLocalBotDeliveryArchive({
83
91
  deliveredResult.message_id ?? deliveredBody.message_id ?? deliveredBody.ts,
84
92
  0,
85
93
  );
86
- const deliveredMessageThreadID = intFromRawAllowZero(
94
+ const observedMessageThreadID = intFromRawAllowZero(
87
95
  deliveredResult.message_thread_id ?? deliveredBody.message_thread_id ?? delivery.effectiveMessageThreadID,
88
96
  intFromRawAllowZero(messageThreadID, 0),
89
97
  );
90
- const archiveReplyToMessageID = intFromRawAllowZero(delivery.effectiveReplyToMessageID, replyToMessageID);
98
+ const expectedMessageThreadID = intFromRawAllowZero(messageThreadID, 0);
99
+ const expectedReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
100
+ const observedReplyToMessageID = intFromRawAllowZero(delivery.effectiveReplyToMessageID, 0);
101
+ if (
102
+ (expectedReplyToMessageID > 0 || observedReplyToMessageID > 0)
103
+ && observedReplyToMessageID !== expectedReplyToMessageID
104
+ ) {
105
+ return {
106
+ ok: false,
107
+ error: buildReplyAnchorMismatchError(expectedReplyToMessageID, observedReplyToMessageID),
108
+ reply_anchor_mismatch: true,
109
+ expected_reply_to_message_id: expectedReplyToMessageID,
110
+ observed_reply_to_message_id: observedReplyToMessageID,
111
+ thread_id: thread.threadID,
112
+ work_item_id: thread.workItemID,
113
+ };
114
+ }
115
+ if (
116
+ (expectedMessageThreadID > 0 || observedMessageThreadID > 0)
117
+ && observedMessageThreadID !== expectedMessageThreadID
118
+ ) {
119
+ return {
120
+ ok: false,
121
+ error: buildMessageThreadMismatchError(expectedMessageThreadID, observedMessageThreadID),
122
+ message_thread_mismatch: true,
123
+ expected_message_thread_id: expectedMessageThreadID,
124
+ observed_message_thread_id: observedMessageThreadID,
125
+ thread_id: thread.threadID,
126
+ work_item_id: thread.workItemID,
127
+ };
128
+ }
91
129
  if (archiveDedupeOutbound && deliveredMessageID > 0) {
92
130
  const existingComments = await listThreadCommentsTail({
93
131
  siteBaseURL,
@@ -119,8 +157,8 @@ export async function finalizeLocalBotDeliveryArchive({
119
157
  destination,
120
158
  replyText: text,
121
159
  messageID: deliveredMessageID,
122
- messageThreadID: deliveredMessageThreadID,
123
- replyToMessageID: archiveReplyToMessageID,
160
+ messageThreadID: expectedMessageThreadID,
161
+ replyToMessageID: expectedReplyToMessageID,
124
162
  conversation: archiveConversation,
125
163
  });
126
164
  const createdComment = await createThreadComment({
@@ -314,6 +314,27 @@ export function buildTelegramMessageEnvelopeFromParsedArchive(parsedArchiveRaw,
314
314
  });
315
315
  }
316
316
 
317
+ export function resolveTelegramReplyAnchorMessageID({
318
+ replyToMessageID = 0,
319
+ sourceEnvelope: sourceEnvelopeRaw = {},
320
+ fallbackReplyToMessageID = 0,
321
+ } = {}) {
322
+ const explicitReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
323
+ if (explicitReplyToMessageID > 0) {
324
+ return explicitReplyToMessageID;
325
+ }
326
+ const sourceEnvelope = normalizeTelegramMessageEnvelope(sourceEnvelopeRaw);
327
+ const sourceMessageID = intFromRawAllowZero(sourceEnvelope.message_id, 0);
328
+ if (sourceMessageID > 0) {
329
+ return sourceMessageID;
330
+ }
331
+ const sourceReplyToMessageID = intFromRawAllowZero(sourceEnvelope.reply_to_message_id, 0);
332
+ if (sourceReplyToMessageID > 0) {
333
+ return sourceReplyToMessageID;
334
+ }
335
+ return intFromRawAllowZero(fallbackReplyToMessageID, 0);
336
+ }
337
+
317
338
  export function buildTelegramBotReplyEnvelope({
318
339
  sourceEnvelope: sourceEnvelopeRaw = {},
319
340
  chatID = "",
@@ -1,3 +1,7 @@
1
+ import {
2
+ resolveTelegramReplyAnchorMessageID,
3
+ } from "./runner-helpers.mjs";
4
+
1
5
  function safeObject(value) {
2
6
  if (!value || typeof value !== "object" || Array.isArray(value)) {
3
7
  return {};
@@ -154,9 +158,20 @@ export function prepareRunnerSelectedRecordIngress({
154
158
  currentBotSelector,
155
159
  });
156
160
  const replyMessageThreadID = intFromRawAllowZero(sourceMessageEnvelope.message_thread_id, 0);
157
- const replyToMessageID = intFromRawAllowZero(sourceMessageEnvelope.message_id, 0);
161
+ const sourceMessageID = intFromRawAllowZero(sourceMessageEnvelope.message_id, 0);
162
+ const sourceReplyToMessageID = intFromRawAllowZero(sourceMessageEnvelope.reply_to_message_id, 0);
163
+ const replyToMessageID = resolveTelegramReplyAnchorMessageID({
164
+ replyToMessageID: sourceMessageID,
165
+ sourceEnvelope: sourceMessageEnvelope,
166
+ });
158
167
  const replyAnchorSource = String(sourceMessageEnvelope.source_origin || "").trim()
159
- || (replyToMessageID > 0 ? "source_message_envelope" : "");
168
+ || (replyToMessageID > 0
169
+ ? sourceMessageID > 0
170
+ ? "source_message_envelope"
171
+ : sourceReplyToMessageID > 0
172
+ ? "source_message_envelope_reply_to"
173
+ : ""
174
+ : "");
160
175
 
161
176
  return {
162
177
  handledResult: null,
@@ -29,6 +29,10 @@ function buildConversationSummaryDetail({
29
29
  ].filter(Boolean).join(" | ");
30
30
  }
31
31
 
32
+ function buildMessageThreadMismatchError(expectedMessageThreadID, observedMessageThreadID) {
33
+ return `message thread mismatch: expected ${String(expectedMessageThreadID || 0)}, observed ${String(observedMessageThreadID || 0)}`;
34
+ }
35
+
32
36
  function buildSelectedRecordReplyOutcomeBase({
33
37
  routeKey,
34
38
  normalizedRoute,
@@ -354,11 +358,19 @@ export async function finalizeRunnerSelectedRecordReplyOutcome({
354
358
  deliveryBody.result?.message_id ?? deliveryBody.message_id,
355
359
  0,
356
360
  );
357
- const effectiveReplyMessageThreadID = intFromRawAllowZero(
361
+ const expectedReplyMessageThreadID = intFromRawAllowZero(replyMessageThreadID, 0);
362
+ const observedReplyMessageThreadID = intFromRawAllowZero(
358
363
  deliveryResult?.delivery?.effectiveMessageThreadID,
359
- replyMessageThreadID,
364
+ 0,
365
+ );
366
+ const messageThreadMismatch = (
367
+ (expectedReplyMessageThreadID > 0 || observedReplyMessageThreadID > 0)
368
+ && observedReplyMessageThreadID !== expectedReplyMessageThreadID
360
369
  );
361
- const effectiveReplyToMessageID = intFromRawAllowZero(deliveryResult?.delivery?.effectiveReplyToMessageID, replyToMessageID);
370
+ const messageThreadError = messageThreadMismatch
371
+ ? buildMessageThreadMismatchError(expectedReplyMessageThreadID, observedReplyMessageThreadID)
372
+ : "";
373
+ const effectiveReplyToMessageID = intFromRawAllowZero(replyToMessageID, 0);
362
374
  const conversationDetail = buildConversationSummaryDetail({
363
375
  effectiveConversationContext,
364
376
  executionContract,
@@ -371,7 +383,7 @@ export async function finalizeRunnerSelectedRecordReplyOutcome({
371
383
  ...buildRunnerRouteStateFromComment(selectedRecord, {
372
384
  last_action: "replied",
373
385
  last_reply_message_id: replyMessageID,
374
- last_reply_message_thread_id: effectiveReplyMessageThreadID,
386
+ last_reply_message_thread_id: expectedReplyMessageThreadID,
375
387
  last_reply_anchor_source: replyAnchorSource,
376
388
  last_contract_validation_status: String(responseContractValidation?.status || "").trim(),
377
389
  last_contract_validation_reason: String(responseContractValidation?.reason || "").trim(),
@@ -445,17 +457,21 @@ export async function finalizeRunnerSelectedRecordReplyOutcome({
445
457
  evidence_ids: ensureArray(aiResult?.evidenceItems).map((item) => String(safeObject(item).id || "").trim()).filter(Boolean),
446
458
  evidence_paths: ensureArray(aiResult?.evidenceItems).map((item) => String(safeObject(item).path || "").trim()).filter(Boolean),
447
459
  last_reply_message_id: replyMessageID,
448
- last_reply_message_thread_id: effectiveReplyMessageThreadID,
460
+ last_reply_message_thread_id: expectedReplyMessageThreadID,
449
461
  reply_to_message_id: effectiveReplyToMessageID,
450
462
  last_reply_message_envelope: buildTelegramBotReplyEnvelope({
451
463
  sourceEnvelope: sourceMessageEnvelope,
452
464
  messageID: replyMessageID,
453
- messageThreadID: effectiveReplyMessageThreadID,
465
+ messageThreadID: expectedReplyMessageThreadID,
454
466
  replyToMessageID: effectiveReplyToMessageID,
455
467
  sender: bot?.username ? `@${String(bot.username || "").trim().replace(/^@+/, "")}` : String(bot?.name || "bot").trim(),
456
468
  senderUsername: normalizeMentionSelector(bot?.username || bot?.name),
457
469
  body: sanitizedReplyText,
458
470
  }),
471
+ message_thread_mismatch: messageThreadMismatch,
472
+ expected_message_thread_id: expectedReplyMessageThreadID || undefined,
473
+ observed_message_thread_id: observedReplyMessageThreadID || undefined,
474
+ message_thread_error: messageThreadError,
459
475
  reply_anchor_source: replyAnchorSource,
460
476
  reply_fallback_used: deliveryResult?.delivery?.replyFallbackUsed === true,
461
477
  delivery_status: deliveryResult?.delivery?.dryRun ? "dry_run" : "delivered",
@@ -36,14 +36,8 @@ export function prepareRunnerFailureReplyDeliveryHandoff({
36
36
  const sourceMessageEnvelope = Object.keys(safeObject(authoritativeSourceMessageEnvelope)).length > 0
37
37
  ? safeObject(authoritativeSourceMessageEnvelope)
38
38
  : safeObject(result.source_message_envelope);
39
- const replyToMessageID = intFromRawAllowZero(
40
- result.reply_to_message_id,
41
- intFromRawAllowZero(sourceMessageEnvelope.message_id, 0),
42
- );
43
- const messageThreadID = intFromRawAllowZero(
44
- result.reply_message_thread_id,
45
- intFromRawAllowZero(sourceMessageEnvelope.message_thread_id, 0),
46
- );
39
+ const replyToMessageID = intFromRawAllowZero(result.reply_to_message_id, 0);
40
+ const messageThreadID = intFromRawAllowZero(result.reply_message_thread_id, 0);
47
41
  return {
48
42
  siteBaseURL: normalizedRuntime.baseURL,
49
43
  token: normalizedRuntime.token,
@@ -11,6 +11,14 @@ function intFromRawAllowZero(value, fallback = 0) {
11
11
  return Number.isFinite(parsed) ? parsed : fallback;
12
12
  }
13
13
 
14
+ function buildReplyAnchorMismatchError(expectedReplyToMessageID, observedReplyToMessageID) {
15
+ return `reply anchor mismatch: expected ${String(expectedReplyToMessageID || 0)}, observed ${String(observedReplyToMessageID || 0)}`;
16
+ }
17
+
18
+ function buildMessageThreadMismatchError(expectedMessageThreadID, observedMessageThreadID) {
19
+ return `message thread mismatch: expected ${String(expectedMessageThreadID || 0)}, observed ${String(observedMessageThreadID || 0)}`;
20
+ }
21
+
14
22
  export function finalizeRunnerFailureReplyDeliveryOutcome({
15
23
  processed = {},
16
24
  routeKey = "",
@@ -50,6 +58,24 @@ export function finalizeRunnerFailureReplyDeliveryOutcome({
50
58
  const delivery = safeObject(normalizedDeliveryResult.delivery);
51
59
  const archive = safeObject(normalizedDeliveryResult.archive);
52
60
  const handoff = safeObject(deliveryHandoff);
61
+ const expectedReplyToMessageID = intFromRawAllowZero(handoff.replyToMessageID, 0);
62
+ const observedReplyToMessageID = intFromRawAllowZero(delivery.effectiveReplyToMessageID, 0);
63
+ const expectedMessageThreadID = intFromRawAllowZero(handoff.messageThreadID, 0);
64
+ const observedMessageThreadID = intFromRawAllowZero(delivery.effectiveMessageThreadID, 0);
65
+ const replyAnchorMismatch = (
66
+ (expectedReplyToMessageID > 0 || observedReplyToMessageID > 0)
67
+ && observedReplyToMessageID !== expectedReplyToMessageID
68
+ );
69
+ const replyAnchorError = replyAnchorMismatch
70
+ ? buildReplyAnchorMismatchError(expectedReplyToMessageID, observedReplyToMessageID)
71
+ : "";
72
+ const messageThreadMismatch = (
73
+ (expectedMessageThreadID > 0 || observedMessageThreadID > 0)
74
+ && observedMessageThreadID !== expectedMessageThreadID
75
+ );
76
+ const messageThreadError = messageThreadMismatch
77
+ ? buildMessageThreadMismatchError(expectedMessageThreadID, observedMessageThreadID)
78
+ : "";
53
79
  const mergedResult = {
54
80
  ...result,
55
81
  failure_reply_sent: true,
@@ -69,14 +95,16 @@ export function finalizeRunnerFailureReplyDeliveryOutcome({
69
95
  safeObject(delivery.body).result?.message_id ?? safeObject(delivery.body).message_id,
70
96
  intFromRawAllowZero(result.last_reply_message_id, 0),
71
97
  ),
72
- last_reply_message_thread_id: intFromRawAllowZero(
73
- delivery.effectiveMessageThreadID,
74
- intFromRawAllowZero(handoff.messageThreadID, 0),
75
- ),
76
- reply_to_message_id: intFromRawAllowZero(
77
- delivery.effectiveReplyToMessageID,
78
- intFromRawAllowZero(handoff.replyToMessageID, 0),
79
- ),
98
+ last_reply_message_thread_id: expectedMessageThreadID,
99
+ reply_to_message_id: expectedReplyToMessageID,
100
+ reply_anchor_mismatch: replyAnchorMismatch,
101
+ expected_reply_to_message_id: expectedReplyToMessageID || undefined,
102
+ observed_reply_to_message_id: observedReplyToMessageID || undefined,
103
+ reply_anchor_error: replyAnchorError,
104
+ message_thread_mismatch: messageThreadMismatch,
105
+ expected_message_thread_id: expectedMessageThreadID || undefined,
106
+ observed_message_thread_id: observedMessageThreadID || undefined,
107
+ message_thread_error: messageThreadError,
80
108
  };
81
109
  if (normalizedRouteKey && typeof saveRunnerRouteState === "function") {
82
110
  const currentRouteState = typeof loadRouteState === "function"
@@ -18930,7 +18930,7 @@ export async function runSelftestRunnerScenarios(push, deps) {
18930
18930
  next_expected_responders: ["beta_bot"],
18931
18931
  },
18932
18932
  authoritativeSourceMessageEnvelope: {
18933
- message_id: 4401,
18933
+ message_id: 5501,
18934
18934
  source_origin: "local_receipt",
18935
18935
  },
18936
18936
  buildRunnerDeliveryDeps: () => ({
@@ -18940,12 +18940,13 @@ export async function runSelftestRunnerScenarios(push, deps) {
18940
18940
  push(
18941
18941
  "runner_recorder_failure_delivery_handoff_stays_within_5_to_6_boundary",
18942
18942
  String(handoff.text || "") === "I could not finish this request."
18943
+ && String(handoff.replyToMessageID || "") === "4401"
18943
18944
  && String(handoff.archiveConversationContext?.id || "") === "conversation-failure-handoff-1"
18944
18945
  && String(handoff.archiveExecutionContract?.type || "") === "delegation"
18945
18946
  && !Object.prototype.hasOwnProperty.call(handoff, "archiveConversation")
18946
18947
  && !Object.prototype.hasOwnProperty.call(handoff, "requestKey")
18947
18948
  && !Object.prototype.hasOwnProperty.call(handoff, "status"),
18948
- `text=${String(handoff.text || "(none)")} conversation=${String(handoff.archiveConversationContext?.id || "(none)")} contract=${String(handoff.archiveExecutionContract?.type || "(none)")} archiveConversation=${String(handoff.archiveConversation || "(none)")}`,
18949
+ `text=${String(handoff.text || "(none)")} replyTo=${String(handoff.replyToMessageID || "(none)")} conversation=${String(handoff.archiveConversationContext?.id || "(none)")} contract=${String(handoff.archiveExecutionContract?.type || "(none)")} archiveConversation=${String(handoff.archiveConversation || "(none)")}`,
18949
18950
  );
18950
18951
  } catch (err) {
18951
18952
  push("runner_recorder_failure_delivery_handoff_stays_within_5_to_6_boundary", false, String(err?.message || err));
@@ -19003,6 +19004,96 @@ export async function runSelftestRunnerScenarios(push, deps) {
19003
19004
  push("runner_recorder_failure_delivery_outcome_handoff_stays_within_module_5_boundary", false, String(err?.message || err));
19004
19005
  }
19005
19006
 
19007
+ try {
19008
+ const finalizedFailureDelivery = finalizeRunnerFailureReplyDeliveryOutcome({
19009
+ processed: {
19010
+ kind: "error",
19011
+ result: {
19012
+ failure_reply_text: "I could not finish this request.",
19013
+ last_reply_message_id: 11,
19014
+ },
19015
+ },
19016
+ routeKey: "route-failure-delivery-outcome-mismatch-1",
19017
+ deliveryResult: {
19018
+ delivery: {
19019
+ dryRun: false,
19020
+ effectiveMessageThreadID: 22,
19021
+ effectiveReplyToMessageID: 99,
19022
+ body: {
19023
+ result: {
19024
+ message_id: 44,
19025
+ },
19026
+ },
19027
+ },
19028
+ archive: {
19029
+ ok: true,
19030
+ },
19031
+ },
19032
+ deliveryHandoff: {
19033
+ messageThreadID: 22,
19034
+ replyToMessageID: 33,
19035
+ },
19036
+ loadRouteState: () => ({}),
19037
+ saveRunnerRouteState: () => {},
19038
+ });
19039
+ push(
19040
+ "runner_failure_delivery_outcome_preserves_module_4_reply_anchor_on_mismatch",
19041
+ intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).reply_to_message_id, 0) === 33
19042
+ && safeSelftestObject(finalizedFailureDelivery.result).reply_anchor_mismatch === true
19043
+ && intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).expected_reply_to_message_id, 0) === 33
19044
+ && intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).observed_reply_to_message_id, 0) === 99
19045
+ && /expected 33, observed 99/i.test(String(safeSelftestObject(finalizedFailureDelivery.result).reply_anchor_error || "")),
19046
+ `reply_to=${String(safeSelftestObject(finalizedFailureDelivery.result).reply_to_message_id || "(none)")} mismatch=${String(safeSelftestObject(finalizedFailureDelivery.result).reply_anchor_mismatch || false)} expected=${String(safeSelftestObject(finalizedFailureDelivery.result).expected_reply_to_message_id || "(none)")} observed=${String(safeSelftestObject(finalizedFailureDelivery.result).observed_reply_to_message_id || "(none)")}`,
19047
+ );
19048
+ } catch (err) {
19049
+ push("runner_failure_delivery_outcome_preserves_module_4_reply_anchor_on_mismatch", false, String(err?.message || err));
19050
+ }
19051
+
19052
+ try {
19053
+ const finalizedFailureDelivery = finalizeRunnerFailureReplyDeliveryOutcome({
19054
+ processed: {
19055
+ kind: "error",
19056
+ result: {
19057
+ failure_reply_text: "I could not finish this request.",
19058
+ last_reply_message_id: 11,
19059
+ },
19060
+ },
19061
+ routeKey: "route-failure-delivery-thread-mismatch-1",
19062
+ deliveryResult: {
19063
+ delivery: {
19064
+ dryRun: false,
19065
+ effectiveMessageThreadID: 99,
19066
+ effectiveReplyToMessageID: 33,
19067
+ body: {
19068
+ result: {
19069
+ message_id: 44,
19070
+ },
19071
+ },
19072
+ },
19073
+ archive: {
19074
+ ok: true,
19075
+ },
19076
+ },
19077
+ deliveryHandoff: {
19078
+ messageThreadID: 22,
19079
+ replyToMessageID: 33,
19080
+ },
19081
+ loadRouteState: () => ({}),
19082
+ saveRunnerRouteState: () => {},
19083
+ });
19084
+ push(
19085
+ "runner_failure_delivery_outcome_preserves_module_4_message_thread_on_mismatch",
19086
+ intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).last_reply_message_thread_id, 0) === 22
19087
+ && safeSelftestObject(finalizedFailureDelivery.result).message_thread_mismatch === true
19088
+ && intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).expected_message_thread_id, 0) === 22
19089
+ && intFromRawAllowZero(safeSelftestObject(finalizedFailureDelivery.result).observed_message_thread_id, 0) === 99
19090
+ && /expected 22, observed 99/i.test(String(safeSelftestObject(finalizedFailureDelivery.result).message_thread_error || "")),
19091
+ `thread=${String(safeSelftestObject(finalizedFailureDelivery.result).last_reply_message_thread_id || "(none)")} mismatch=${String(safeSelftestObject(finalizedFailureDelivery.result).message_thread_mismatch || false)} expected=${String(safeSelftestObject(finalizedFailureDelivery.result).expected_message_thread_id || "(none)")} observed=${String(safeSelftestObject(finalizedFailureDelivery.result).observed_message_thread_id || "(none)")}`,
19092
+ );
19093
+ } catch (err) {
19094
+ push("runner_failure_delivery_outcome_preserves_module_4_message_thread_on_mismatch", false, String(err?.message || err));
19095
+ }
19096
+
19006
19097
  try {
19007
19098
  const activePatch = buildRunnerRootWorkItemRoutePatch({
19008
19099
  syncedRequest: {
@@ -20260,6 +20351,137 @@ export async function runSelftestRunnerScenarios(push, deps) {
20260
20351
  push("runner_selected_record_delivery_context_stays_within_module_4_boundary", false, String(err?.message || err));
20261
20352
  }
20262
20353
 
20354
+ try {
20355
+ const savedStates = [];
20356
+ const ingress = prepareRunnerSelectedRecordIngress({
20357
+ routeKey: "route-ingress-reply-fallback-1",
20358
+ routeState: {},
20359
+ selectedRecord: {
20360
+ id: "comment-ingress-reply-fallback-1",
20361
+ parsedArchive: {
20362
+ kind: "telegram_message",
20363
+ senderIsBot: false,
20364
+ messageID: 4703,
20365
+ },
20366
+ },
20367
+ persistedHumanIntentRequest: null,
20368
+ precomputedHumanInboundVisibility: {
20369
+ applies: true,
20370
+ executable: true,
20371
+ visibilityMode: "reply_chain",
20372
+ visibilityStatus: "visible",
20373
+ visibilityReason: "",
20374
+ visibilitySource: "local_receipt",
20375
+ candidateBotUsernames: ["alpha_bot"],
20376
+ sourceMessageEnvelope: {
20377
+ reply_to_message_id: 4702,
20378
+ message_thread_id: 82,
20379
+ source_origin: "local_receipt",
20380
+ },
20381
+ },
20382
+ currentBotSelector: "alpha_bot",
20383
+ triggerDecision: {
20384
+ shouldRespond: true,
20385
+ trigger: "reply_to_bot",
20386
+ candidateBotUsernames: ["alpha_bot"],
20387
+ },
20388
+ saveRunnerRouteState: (_routeKey, patch) => {
20389
+ savedStates.push(patch);
20390
+ },
20391
+ deps: {
20392
+ hasPrecomputedRunnerTriggerDecision: (decision) => typeof decision?.shouldRespond === "boolean",
20393
+ buildRunnerTriggerStatePatch: () => ({ last_trigger_candidate_bot_usernames: ["alpha_bot"] }),
20394
+ buildRunnerRouteStateFromComment: (_comment, patch) => patch,
20395
+ intFromRawAllowZero: (raw, fallback = 0) => {
20396
+ const numeric = Number.parseInt(String(raw ?? ""), 10);
20397
+ return Number.isFinite(numeric) ? numeric : fallback;
20398
+ },
20399
+ shouldRunnerTriggerProceed: (decision) => decision?.shouldRespond === true,
20400
+ resolveRunnerHumanInboundVisibility: () => {
20401
+ throw new Error("ingress helper should use provided visibility before recomputing");
20402
+ },
20403
+ resolveRunnerDeliverySourceMessageEnvelope: () => {
20404
+ throw new Error("ingress helper should not build fallback source when visibility already supplies one");
20405
+ },
20406
+ },
20407
+ });
20408
+ push(
20409
+ "runner_selected_record_ingress_resolves_reply_anchor_once_from_source_reply_to",
20410
+ !ingress.handledResult
20411
+ && ingress.replyToMessageID === 4702
20412
+ && ingress.replyMessageThreadID === 82
20413
+ && ingress.replyAnchorSource === "local_receipt"
20414
+ && savedStates.length === 0,
20415
+ `replyTo=${String(ingress.replyToMessageID || 0)} thread=${String(ingress.replyMessageThreadID || 0)} anchor=${String(ingress.replyAnchorSource || "(none)")} savedStates=${savedStates.length}`,
20416
+ );
20417
+ } catch (err) {
20418
+ push("runner_selected_record_ingress_resolves_reply_anchor_once_from_source_reply_to", false, String(err?.message || err));
20419
+ }
20420
+
20421
+ try {
20422
+ const prepared = await prepareRunnerSelectedRecordDeliveryContext({
20423
+ aiResult: {
20424
+ reply: "reply without explicit anchor",
20425
+ },
20426
+ bot: {
20427
+ name: "RyoAI_bot",
20428
+ username: "RyoAI_bot",
20429
+ },
20430
+ selectedRecord: {
20431
+ id: "comment-delivery-context-anchor-fallback",
20432
+ },
20433
+ currentBotSelector: "ryoai_bot",
20434
+ executionContract: {
20435
+ type: "direct_result",
20436
+ assignments: [],
20437
+ nextResponders: [],
20438
+ },
20439
+ effectiveTriggerDecision: {
20440
+ trigger: "mention",
20441
+ },
20442
+ effectiveConversationContext: {
20443
+ mode: "single_bot",
20444
+ stage: "human_opening",
20445
+ intentMode: "single_bot",
20446
+ leadBotUsername: "ryoai_bot",
20447
+ },
20448
+ effectiveResolvedIntentMode: "single_bot",
20449
+ directHumanPeerMap: new Map(),
20450
+ responseContractValidation: {
20451
+ ok: true,
20452
+ status: "valid",
20453
+ reason: "",
20454
+ targets: [],
20455
+ },
20456
+ sourceMessageEnvelope: {
20457
+ chat_id: "-1001",
20458
+ message_id: 7001,
20459
+ source_origin: "local_telegram_inbound",
20460
+ },
20461
+ replyToMessageID: 6123,
20462
+ replyMessageThreadID: 0,
20463
+ deps: {
20464
+ normalizeMentionSelector: normalizeSelftestMentionSelector,
20465
+ sanitizeForcedDirectReplyText: ({ replyText }) => String(replyText || "").trim(),
20466
+ buildTelegramBotReplyEnvelope: ({ sourceEnvelope, replyToMessageID, body }) => ({
20467
+ chat_id: sourceEnvelope?.chat_id || "",
20468
+ message_id: sourceEnvelope?.message_id || 0,
20469
+ reply_to_message_id: replyToMessageID,
20470
+ body,
20471
+ }),
20472
+ collectExecutionContractNextResponders: () => [],
20473
+ extractManagedPeerMentionSelectors: () => [],
20474
+ },
20475
+ });
20476
+ push(
20477
+ "runner_selected_record_delivery_context_uses_module_4_resolved_reply_anchor",
20478
+ String(prepared.attemptedDeliveryEnvelope?.reply_to_message_id || "") === "6123",
20479
+ `replyTo=${String(prepared.attemptedDeliveryEnvelope?.reply_to_message_id || "(none)")}`,
20480
+ );
20481
+ } catch (err) {
20482
+ push("runner_selected_record_delivery_context_uses_module_4_resolved_reply_anchor", false, String(err?.message || err));
20483
+ }
20484
+
20263
20485
  try {
20264
20486
  const prepared = await prepareRunnerSelectedRecordDeliveryContext({
20265
20487
  aiResult: {
@@ -20395,6 +20617,65 @@ export async function runSelftestRunnerScenarios(push, deps) {
20395
20617
  push("runner_selected_record_delivery_handoff_stays_within_4_to_6_boundary", false, String(err?.message || err));
20396
20618
  }
20397
20619
 
20620
+ try {
20621
+ const handoff = prepareRunnerSelectedRecordDeliveryHandoff({
20622
+ runtime: {
20623
+ baseURL: "https://example.test",
20624
+ token: "selftest-token",
20625
+ timeoutSeconds: 5,
20626
+ actor: { user_id: "user-delivery-handoff-anchor-fallback" },
20627
+ },
20628
+ bot: {
20629
+ id: "bot-delivery-handoff-anchor-fallback",
20630
+ name: "RyoAI_bot",
20631
+ username: "RyoAI_bot",
20632
+ role: "monitor",
20633
+ provider: "telegram",
20634
+ },
20635
+ normalizedRoute: {
20636
+ projectID: "project-delivery-handoff-anchor-fallback",
20637
+ provider: "telegram",
20638
+ dryRunDelivery: true,
20639
+ archivePolicy: {
20640
+ mirrorReplies: true,
20641
+ dedupeOutbound: true,
20642
+ },
20643
+ },
20644
+ destination: {
20645
+ id: "dest-delivery-handoff-anchor-fallback",
20646
+ label: "Main Room",
20647
+ },
20648
+ sanitizedReplyText: "Anchor from source envelope.",
20649
+ replyMessageThreadID: 0,
20650
+ replyToMessageID: 7788,
20651
+ sourceMessageEnvelope: {
20652
+ chat_id: "-2002",
20653
+ message_id: 7002,
20654
+ source_origin: "local_telegram_inbound",
20655
+ },
20656
+ archiveThread: {
20657
+ threadID: "thread-delivery-handoff-anchor-fallback",
20658
+ workItemID: "workitem-delivery-handoff-anchor-fallback",
20659
+ },
20660
+ effectiveConversationContext: {
20661
+ id: "conversation-delivery-handoff-anchor-fallback",
20662
+ mode: "single_bot",
20663
+ stage: "human_opening",
20664
+ },
20665
+ executionContract: {
20666
+ type: "direct_result",
20667
+ },
20668
+ buildRunnerDeliveryDeps: () => ({ test: true }),
20669
+ });
20670
+ push(
20671
+ "runner_selected_record_delivery_handoff_uses_module_4_resolved_reply_anchor",
20672
+ String(handoff.replyToMessageID || "") === "7788",
20673
+ `replyTo=${String(handoff.replyToMessageID || "(none)")}`,
20674
+ );
20675
+ } catch (err) {
20676
+ push("runner_selected_record_delivery_handoff_uses_module_4_resolved_reply_anchor", false, String(err?.message || err));
20677
+ }
20678
+
20398
20679
  try {
20399
20680
  const savedStates = [];
20400
20681
  const finalized = await finalizeRunnerSelectedRecordReplyOutcome({
@@ -20545,6 +20826,152 @@ export async function runSelftestRunnerScenarios(push, deps) {
20545
20826
  push("runner_selected_record_reply_outcome_stays_within_module_4_boundary", false, String(err?.message || err));
20546
20827
  }
20547
20828
 
20829
+ try {
20830
+ const savedStates = [];
20831
+ const finalized = await finalizeRunnerSelectedRecordReplyOutcome({
20832
+ routeKey: "route-reply-outcome-thread-mismatch-1",
20833
+ normalizedRoute: {
20834
+ name: "Reply Outcome Thread Mismatch Route",
20835
+ },
20836
+ archiveThread: {
20837
+ threadID: "thread-reply-outcome-thread-mismatch-1",
20838
+ },
20839
+ selectedRecord: {
20840
+ id: "comment-reply-outcome-thread-mismatch-1",
20841
+ parsedArchive: {
20842
+ senderIsBot: false,
20843
+ },
20844
+ },
20845
+ effectiveTriggerDecision: {
20846
+ trigger: "precomputed_human_intent_contract",
20847
+ },
20848
+ effectiveConversationContext: {
20849
+ id: "conversation-reply-outcome-thread-mismatch-1",
20850
+ stage: "lead_kickoff",
20851
+ intentMode: "multi_bot_collab",
20852
+ leadBotUsername: "leadbot",
20853
+ summaryBotUsername: "",
20854
+ participantSelectors: ["leadbot", "peerbot"],
20855
+ initialResponderSelectors: ["leadbot"],
20856
+ allowedResponderSelectors: ["leadbot", "peerbot"],
20857
+ allowBotToBot: true,
20858
+ replyExpectation: "public_reply",
20859
+ },
20860
+ executionContract: {
20861
+ type: "delegation",
20862
+ actionable: true,
20863
+ },
20864
+ normalizedExecutionContractType: "delegation",
20865
+ normalizedExecutionTargets: ["peerbot"],
20866
+ normalizedExecutionNextResponders: ["peerbot"],
20867
+ artifactValidation: {
20868
+ status: "none",
20869
+ },
20870
+ aiResult: {},
20871
+ authoritativeDecisionBundle: {
20872
+ schema_version: "conversation_decision.v1",
20873
+ decision_type: "reply_outcome",
20874
+ },
20875
+ projectContextSuggestion: {},
20876
+ bot: {
20877
+ name: "Lead Bot",
20878
+ username: "leadbot",
20879
+ },
20880
+ sourceMessageEnvelope: {
20881
+ source_origin: "local_telegram_inbound",
20882
+ message_id: 4900,
20883
+ message_thread_id: 93,
20884
+ },
20885
+ attemptedDeliveryEnvelope: {
20886
+ message_id: 4900,
20887
+ },
20888
+ humanInboundVisibility: {
20889
+ visibilityStatus: "local",
20890
+ visibilityReason: "",
20891
+ visibilitySource: "local_receipt",
20892
+ },
20893
+ replyToMessageID: 4900,
20894
+ replyMessageThreadID: 93,
20895
+ replyAnchorSource: "local_receipt",
20896
+ responseContractValidation: {
20897
+ status: "valid",
20898
+ reason: "",
20899
+ targets: [],
20900
+ },
20901
+ assignmentExecutionValidation: {
20902
+ status: "direct_execution",
20903
+ reason: "",
20904
+ assignmentModes: [],
20905
+ },
20906
+ responderStatePatch: {},
20907
+ intentStatePatch: {},
20908
+ visibilityStatePatch: {},
20909
+ effectiveExecutionPlan: {
20910
+ mode: "lookup_only",
20911
+ roleProfileName: "monitor",
20912
+ workspaceDir: "C:\\selftest",
20913
+ usedCommandFallback: false,
20914
+ },
20915
+ executedRolePlan: {},
20916
+ sanitizedReplyText: "bot replied",
20917
+ aiReplyGeneratedAt: "2026-03-30T00:00:00.000Z",
20918
+ aiReplyPreview: "bot replied",
20919
+ deliveryResult: {
20920
+ delivery: {
20921
+ dryRun: false,
20922
+ effectiveMessageThreadID: 199,
20923
+ effectiveReplyToMessageID: 4900,
20924
+ body: {
20925
+ result: {
20926
+ message_id: 4901,
20927
+ },
20928
+ },
20929
+ },
20930
+ archive: {
20931
+ ok: true,
20932
+ },
20933
+ },
20934
+ saveRunnerRouteState: (_routeKey, patch) => {
20935
+ savedStates.push(patch);
20936
+ },
20937
+ emitRunnerStage: () => {},
20938
+ deps: {
20939
+ buildRunnerRouteStateFromComment: (_comment, patch) => patch,
20940
+ summarizeValidatedArtifactPaths: () => [],
20941
+ intFromRawAllowZero: (raw, fallback = 0) => {
20942
+ const numeric = Number.parseInt(String(raw ?? ""), 10);
20943
+ return Number.isFinite(numeric) ? numeric : fallback;
20944
+ },
20945
+ normalizeMentionSelector: normalizeSelftestMentionSelector,
20946
+ formatConversationContractSummary: () => "contract=delegation",
20947
+ maybeCreateSuggestedProjectContext: async () => ({ created: false, reason: "message_is_interrogative" }),
20948
+ buildConversationSessionsPatch: (_routeState, _conversationContext, patch) => patch,
20949
+ normalizeConversationReplyFingerprint: (text) => String(text || "").trim(),
20950
+ buildTelegramBotReplyEnvelope: ({ messageID, messageThreadID, replyToMessageID, body }) => ({
20951
+ message_id: messageID,
20952
+ message_thread_id: messageThreadID,
20953
+ reply_to_message_id: replyToMessageID,
20954
+ body,
20955
+ }),
20956
+ publicMultiBotDefaultMaxTurns: 6,
20957
+ publicMultiBotDefaultTtlMs: 300000,
20958
+ },
20959
+ });
20960
+ push(
20961
+ "runner_selected_record_reply_outcome_preserves_module_4_message_thread_on_mismatch",
20962
+ finalized.kind === "replied"
20963
+ && savedStates.length === 1
20964
+ && intFromRawAllowZero(finalized.result?.last_reply_message_thread_id, 0) === 93
20965
+ && finalized.result?.message_thread_mismatch === true
20966
+ && intFromRawAllowZero(finalized.result?.expected_message_thread_id, 0) === 93
20967
+ && intFromRawAllowZero(finalized.result?.observed_message_thread_id, 0) === 199
20968
+ && /expected 93, observed 199/i.test(String(finalized.result?.message_thread_error || "")),
20969
+ `thread=${String(finalized.result?.last_reply_message_thread_id || "(none)")} mismatch=${String(finalized.result?.message_thread_mismatch || false)} expected=${String(finalized.result?.expected_message_thread_id || "(none)")} observed=${String(finalized.result?.observed_message_thread_id || "(none)")}`,
20970
+ );
20971
+ } catch (err) {
20972
+ push("runner_selected_record_reply_outcome_preserves_module_4_message_thread_on_mismatch", false, String(err?.message || err));
20973
+ }
20974
+
20548
20975
  try {
20549
20976
  const finalized = finalizeExecutionPlanSteps(
20550
20977
  {
@@ -21113,6 +21540,154 @@ export async function runSelftestRunnerScenarios(push, deps) {
21113
21540
  push("runner_delivery_archive_handoff_stays_within_module_6_boundary", false, String(err?.message || err));
21114
21541
  }
21115
21542
 
21543
+ try {
21544
+ let createCommentCalls = 0;
21545
+ const archivedReply = await finalizeLocalBotDeliveryArchive({
21546
+ siteBaseURL: "https://example.test",
21547
+ token: "token-delivery-archive-mismatch-1",
21548
+ timeoutSeconds: 10,
21549
+ actorUserID: "actor-delivery-archive-mismatch-1",
21550
+ projectID: "project-delivery-archive-mismatch-1",
21551
+ provider: "telegram",
21552
+ destination: {
21553
+ id: "dest-delivery-archive-mismatch-1",
21554
+ provider: "telegram",
21555
+ label: "Delivery Archive Mismatch",
21556
+ chatID: "-100558",
21557
+ },
21558
+ bot: {
21559
+ id: "bot-delivery-archive-mismatch-1",
21560
+ name: "DeliveryArchiveMismatchBot",
21561
+ username: "deliveryarchivemismatchbot",
21562
+ role: "monitor",
21563
+ },
21564
+ text: "archive mismatch delivery",
21565
+ messageThreadID: 77,
21566
+ replyToMessageID: 55,
21567
+ archiveReplies: true,
21568
+ archiveDedupeOutbound: true,
21569
+ archiveThreadID: "thread-delivery-archive-mismatch-1",
21570
+ archiveWorkItemID: "workitem-delivery-archive-mismatch-1",
21571
+ archiveConversation: {
21572
+ id: "delivery-archive-mismatch-conversation",
21573
+ },
21574
+ dryRun: false,
21575
+ delivery: {
21576
+ body: {
21577
+ result: {
21578
+ message_id: 502,
21579
+ message_thread_id: 77,
21580
+ },
21581
+ },
21582
+ effectiveReplyToMessageID: 66,
21583
+ effectiveMessageThreadID: 77,
21584
+ },
21585
+ formatBotReplyArchiveComment,
21586
+ findArchivedBotReplyRecord,
21587
+ deps: {
21588
+ discoverArchiveThreadForDestination: async () => ({
21589
+ threadID: "thread-delivery-archive-mismatch-1",
21590
+ workItemID: "workitem-delivery-archive-mismatch-1",
21591
+ }),
21592
+ listThreadComments: async () => [],
21593
+ listThreadCommentsTail: async () => [],
21594
+ createThreadComment: async () => {
21595
+ createCommentCalls += 1;
21596
+ return {
21597
+ id: "comment-delivery-archive-mismatch-1",
21598
+ };
21599
+ },
21600
+ parseArchivedChatComment,
21601
+ },
21602
+ });
21603
+ push(
21604
+ "runner_delivery_archive_reveals_reply_anchor_mismatch_instead_of_masking",
21605
+ archivedReply.ok === false
21606
+ && archivedReply.reply_anchor_mismatch === true
21607
+ && intFromRawAllowZero(archivedReply.expected_reply_to_message_id, 0) === 55
21608
+ && intFromRawAllowZero(archivedReply.observed_reply_to_message_id, 0) === 66
21609
+ && createCommentCalls === 0
21610
+ && /expected 55, observed 66/i.test(String(archivedReply.error || "")),
21611
+ `ok=${String(archivedReply.ok)} mismatch=${String(archivedReply.reply_anchor_mismatch || false)} expected=${String(archivedReply.expected_reply_to_message_id || "(none)")} observed=${String(archivedReply.observed_reply_to_message_id || "(none)")} created=${createCommentCalls}`,
21612
+ );
21613
+ } catch (err) {
21614
+ push("runner_delivery_archive_reveals_reply_anchor_mismatch_instead_of_masking", false, String(err?.message || err));
21615
+ }
21616
+
21617
+ try {
21618
+ let createCommentCalls = 0;
21619
+ const archivedReply = await finalizeLocalBotDeliveryArchive({
21620
+ siteBaseURL: "https://example.test",
21621
+ token: "token-delivery-thread-mismatch-1",
21622
+ timeoutSeconds: 10,
21623
+ actorUserID: "actor-delivery-thread-mismatch-1",
21624
+ projectID: "project-delivery-thread-mismatch-1",
21625
+ provider: "telegram",
21626
+ destination: {
21627
+ id: "dest-delivery-thread-mismatch-1",
21628
+ provider: "telegram",
21629
+ label: "Delivery Thread Mismatch",
21630
+ chatID: "-100559",
21631
+ },
21632
+ bot: {
21633
+ id: "bot-delivery-thread-mismatch-1",
21634
+ name: "DeliveryThreadMismatchBot",
21635
+ username: "deliverythreadmismatchbot",
21636
+ role: "monitor",
21637
+ },
21638
+ text: "archive thread mismatch delivery",
21639
+ messageThreadID: 77,
21640
+ replyToMessageID: 55,
21641
+ archiveReplies: true,
21642
+ archiveDedupeOutbound: true,
21643
+ archiveThreadID: "thread-delivery-thread-mismatch-1",
21644
+ archiveWorkItemID: "workitem-delivery-thread-mismatch-1",
21645
+ archiveConversation: {
21646
+ id: "delivery-thread-mismatch-conversation",
21647
+ },
21648
+ dryRun: false,
21649
+ delivery: {
21650
+ body: {
21651
+ result: {
21652
+ message_id: 503,
21653
+ message_thread_id: 88,
21654
+ },
21655
+ },
21656
+ effectiveReplyToMessageID: 55,
21657
+ effectiveMessageThreadID: 88,
21658
+ },
21659
+ formatBotReplyArchiveComment,
21660
+ findArchivedBotReplyRecord,
21661
+ deps: {
21662
+ discoverArchiveThreadForDestination: async () => ({
21663
+ threadID: "thread-delivery-thread-mismatch-1",
21664
+ workItemID: "workitem-delivery-thread-mismatch-1",
21665
+ }),
21666
+ listThreadComments: async () => [],
21667
+ listThreadCommentsTail: async () => [],
21668
+ createThreadComment: async () => {
21669
+ createCommentCalls += 1;
21670
+ return {
21671
+ id: "comment-delivery-thread-mismatch-1",
21672
+ };
21673
+ },
21674
+ parseArchivedChatComment,
21675
+ },
21676
+ });
21677
+ push(
21678
+ "runner_delivery_archive_reveals_message_thread_mismatch_instead_of_masking",
21679
+ archivedReply.ok === false
21680
+ && archivedReply.message_thread_mismatch === true
21681
+ && intFromRawAllowZero(archivedReply.expected_message_thread_id, 0) === 77
21682
+ && intFromRawAllowZero(archivedReply.observed_message_thread_id, 0) === 88
21683
+ && createCommentCalls === 0
21684
+ && /expected 77, observed 88/i.test(String(archivedReply.error || "")),
21685
+ `ok=${String(archivedReply.ok)} mismatch=${String(archivedReply.message_thread_mismatch || false)} expected=${String(archivedReply.expected_message_thread_id || "(none)")} observed=${String(archivedReply.observed_message_thread_id || "(none)")} created=${createCommentCalls}`,
21686
+ );
21687
+ } catch (err) {
21688
+ push("runner_delivery_archive_reveals_message_thread_mismatch_instead_of_masking", false, String(err?.message || err));
21689
+ }
21690
+
21116
21691
  try {
21117
21692
  const finalizedDeliveryResult = finalizeLocalBotDeliveryResult({
21118
21693
  provider: "telegram",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metheus-governance-mcp-cli",
3
- "version": "0.2.283",
3
+ "version": "0.2.284",
4
4
  "description": "Metheus Governance MCP CLI (setup + stdio proxy)",
5
5
  "type": "module",
6
6
  "files": [