Skip to content

fix(agent): ground replies in tool receipts and repair delegation delivery - #496

Merged
anconina merged 206 commits into
mainfrom
test/real-user-telegram-local-20260810
Aug 12, 2026
Merged

fix(agent): ground replies in tool receipts and repair delegation delivery#496
anconina merged 206 commits into
mainfrom
test/real-user-telegram-local-20260810

Conversation

@anconina

Copy link
Copy Markdown
Contributor

Intent

Ship the completed local real-user Telegram campaign changes: preserve Comis as a generic domain-neutral runtime, include every test-first product, observability, harness, and documentation fix produced by the campaign, retain strict security and no-backward-compatibility contracts, and publish the committed feature branch as a pull request. The campaign itself remains honestly PARTIAL because safety-suspended and stopped-before-drive corpus rows were not claimed as passes, while all encountered COMIS failures were fixed and validated. Push the branch, create the PR, and require the configured review, test, documentation, lint, and CI gates; do not weaken checks or discard pipeline-generated fixes.

What Changed

  • Executor grounding guards now require authoritative receipts before a reply asserts a fact. packages/agent/src/executor/response-grounding.ts gains guards for outbound audio/image/delivery-status claims (a backgrounded spawn or a runtime voice route is distinguished from a synthesis receipt), and enforceRuntimeSelfReportEvidence now rejects unsupported cost/duration/outage-receipt answers using new observabilityEvidenceLimits (cost: runtime_estimate, providerInvoice: unverified, crossExecutionDurationRanking: unavailable) instead of only checking that an obs_query call happened. Prompt-skill routing (prompt-skill-request-routing.ts, request-tool-nudge.ts) no longer arms off recalled history, backgrounded completions, extracted content, or generic stopwords; citation evidence prefers the current turn's receipts; degraded/input-guard replies pick up i18n coverage; and recall (rag/relevance-scorer.ts, memory-recall.ts) keeps space-free prose searchable while blocking opaque-payload inheritance.
  • Graph/sub-agent delegation carries an explicit completion contract end-to-end. New packages/daemon/src/api/graph-handlers/graph-dispatch-hint.ts returns a per-route dispatch hint that only promises notification when an announcement route and durable delivery exist; graph completion routes retain scoped caller turn/session identity; IncidentGraphRun gains required announcementDelivery (not-requested/unavailable/committed/retained/failed) plus optional sessionKey; and obs.explain picks up a request.clarification_required trajectory event, recallDisposition, route_validation_failed skip reason, and reworked completion-evidence/tool-invocation verdicts. Config posture adds an execSandboxDisabled signal for skills.execSandbox.enabled: "never".
  • Delivery, skills, harness, and docs fixes across the rest of the runtime. New setup-channels/attachment-delivery-hooks.ts publishes every successful ChannelPort.sendAttachment through after_delivery hooks (with delivery authority and a dependency warn on hook failure); bare-link parsing keeps IPv6 hosts intact; the secret entropy backstop swaps its delimiter check for a structure check so source expressions are excluded while password punctuation stays covered; web_search names the exact missing provider secret/knob; the skill manifest adds comis.min-distinct-web-fetch-urls / comis.min-distinct-web-search-queries with defensive parsing that ignores malformed values instead of hiding the skill. Live Telegram/self-driving harness scripts and 20 docs files (json-rpc, channel delivery, skills manifest, environment variables, CLI) are updated to match.

Risk Assessment

⚠️ Medium: The fix round correctly resolved three of the five round-1 items and its new pi-executor probes are a genuine improvement, but the evidence-floor arming fix is a threshold tweak that source evidence shows still discards correct answers on generic prose, the fix's own new event-reason branch has no test that fails without it, and a diagnosability regression in graph completion plus two unaddressed items remain — all safe to merge with follow-ups except the floor-arming product decision, which needs the author's call.

Testing

I ran every changed test file in the change set across all five tiers it touches — the 68 changed package unit files (4350 tests), the changed architecture and contracts tests, the self-driving live-kit project, the credential-free Stage-B legs of the six changed live Telegram/Signal scenarios, and the new real-daemon config-posture integration test — plus the intent-named generic-runtime-boundary gate, the regenerated web-contract drift/size/parity gates, and the bundled deep-research skill mirror gates; everything passes, and the only skips are the COMIS_LIVE Stage-C legs that need real Telegram and model credentials this environment does not have (consistent with the intent's own "honestly PARTIAL" statement). Beyond green tests I produced three product-level artifacts: a run of the daemon's real attachment-delivery + mirror wiring showing the emulator's sendPhoto/sendVoice wire record next to the two persisted delivery_mirror rows with distinct idempotency hashes and a failed send that records nothing; a transcript of the exact chat copy a user receives when an image/voice/delivery-status/delegation claim lacks a receipt versus when the receipt exists; and a real comis explain --graph --offline CLI transcript rendering notification=retained / notification=unavailable and the graph→caller session join, read back from a record written by the product's own writeRunMetadata. No screenshots apply: the change has no rendered UI surface — the web-facing part is generated API contract JSON/TS, and the user-visible surfaces are chat text and CLI output, both captured as transcripts. I created no files in the worktree (git status and git clean -nxd are clean apart from pre-existing gitignored build output); all evidence lives in the dedicated evidence directory.

Evidence: Outbound Telegram attachment: what the chat received and what the runtime persisted

AGENT SENDS two captionless attachments to Telegram chat 424242 adapter results : ok, ok CHANNEL ORACLE — what the Telegram user received (recorded by the emulator): sendPhoto mediaKind=photo caption="" sendVoice mediaKind=voice caption="" MIRROR ORACLE — what the runtime persisted in delivery_mirror (real product schema): origin=channel:attachment channel=telegram/424242 status=pending text="" media_urls=[".../screenshot.png"] idempotency_key=cv_1FsgHETFOjN85Cor7SVtgwzEI9ThBfK4i2zmJnQsmiY:a84882638a615cd3:1786516124 origin=channel:attachment channel=telegram/424242 status=pending text="" media_urls=[".../briefing.ogg"] idempotency_key=cv_1FsgHETFOjN85Cor7SVtgwzEI9ThBfK4i2zmJnQsmiY:363ce1d2c1ec59c6:1786516124 distinct idempotency hashes for the two captionless files: 2 of 2 FAILED SEND — the emulator rejects sendPhoto; the mirror must claim nothing: adapter result : error (send refused by Telegram) delivery_mirror row count: 2 (unchanged — no phantom delivery recorded)

AGENT SENDS two captionless attachments to Telegram chat 424242
  adapter results          : ok, ok

CHANNEL ORACLE — what the Telegram user received (recorded by the emulator):
  sendPhoto  mediaKind=photo  caption=""
  sendVoice  mediaKind=voice  caption=""

MIRROR ORACLE — what the runtime persisted in delivery_mirror (real product schema):
  origin=channel:attachment channel=telegram/424242 status=pending
    text=""  media_urls=["/tmp/attach-mirror-evidence-5ax3Zp/screenshot.png"]
    idempotency_key=cv_1FsgHETFOjN85Cor7SVtgwzEI9ThBfK4i2zmJnQsmiY:586103a2cfc70822:1786516136
  origin=channel:attachment channel=telegram/424242 status=pending
    text=""  media_urls=["/tmp/attach-mirror-evidence-5ax3Zp/briefing.ogg"]
    idempotency_key=cv_1FsgHETFOjN85Cor7SVtgwzEI9ThBfK4i2zmJnQsmiY:17f0dbf156d6181c:1786516136
  distinct idempotency hashes for the two captionless files: 2 of 2

FAILED SEND — the emulator rejects sendPhoto; the mirror must claim nothing:
  adapter result           : error (send refused by Telegram)
  delivery_mirror row count: 2 (unchanged — no phantom delivery recorded)
Evidence: Reply-honesty transcript: the chat copy a user receives with and without a receipt

── image claim, NO generation or attach receipt user sent : Can you create a picture of the office layout and send it here? model drafted : I created the picture and sent it to you. user receives : I could not verify creation or delivery of the requested image in this turn because there is no successful current-turn generation or trusted completion receipt. I cannot confirm that it was created or delivered; please retry. replaced : true (missing_outbound_image_evidence) ── image claim, WITH a successful message.attach receipt — kept verbatim user receives : I created the picture and sent it to you. replaced : false ── voice-note claim, NO tts receipt and NO configured voice route user sent : can you please reply with a voice message? model drafted : Done — the voice message is sent. user receives : I could not verify delivery of the requested audio in this turn because there is no successful current-turn synthesis or trusted completion receipt. I cannot confirm that it was delivered; please check for a voice message or retry. replaced : true (missing_outbound_audio_evidence) ── delivery-status follow-up, NO current delivery/observability receipt user sent : did it send? model drafted : Yes, it was sent successfully. user receives : I could not verify whether the prior outbound item was delivered in this turn because there is no current delivery or observability receipt. I cannot confirm delivery yet. replaced : true (missing_outbound_delivery_status_evidence) ── answer after a SUCCESSFUL spawn whose delegated result is not disclosed model drafted : Today is 11 August 2026. user receives : I successfully started the requested sub-agent. Its result has not been verified in this turn yet. replaced : true (successful_spawn_response_ungrounded)

Outbound-evidence guards — what the chat user actually receives
(honest-replacement copy comes from the runtime's own locale catalog)

── image claim, NO generation or attach receipt
   user sent        : Can you create a picture of the office layout and send it here?
   model drafted    : I created the picture and sent it to you.
   user receives    : I could not verify creation or delivery of the requested image in this turn because there is no successful current-turn generation or trusted completion receipt. I cannot confirm that it was created or delivered; please retry.
   replaced         : true (missing_outbound_image_evidence)

── image claim, WITH a successful message.attach receipt — kept verbatim
   user sent        : Can you create a picture of the office layout and send it here?
   model drafted    : I created the picture and sent it to you.
   user receives    : I created the picture and sent it to you.
   replaced         : false

── voice-note claim, NO tts receipt and NO configured voice route
   user sent        : can you please reply with a voice message?
   model drafted    : Done — the voice message is sent.
   user receives    : I could not verify delivery of the requested audio in this turn because there is no successful current-turn synthesis or trusted completion receipt. I cannot confirm that it was delivered; please check for a voice message or retry.
   replaced         : true (missing_outbound_audio_evidence)

── voice-note claim, WITH the runtime's own voice route speaking this turn — kept verbatim
   user sent        : can you please reply with a voice message?
   model drafted    : Done — the voice message is sent.
   user receives    : Done — the voice message is sent.
   replaced         : false

── delivery-status follow-up, NO current delivery/observability receipt
   user sent        : did it send?
   model drafted    : Yes, it was sent successfully.
   user receives    : I could not verify whether the prior outbound item was delivered in this turn because there is no current delivery or observability receipt. I cannot confirm delivery yet.
   replaced         : true (missing_outbound_delivery_status_evidence)

── delivery-status follow-up, WITH an obs_query receipt — kept verbatim
   user sent        : did it send?
   model drafted    : Yes, it was sent successfully.
   user receives    : Yes, it was sent successfully.
   replaced         : false

── delegated-result claim, NO successful current-turn spawn
   user sent        : can u get someone to check if this lasts 7 hrs and make them ask someone else too
   model drafted    : Two independent checks reached the same conclusion: qualified yes.
   user receives    : I did not successfully start the requested sub-agent in this turn, so I cannot claim a new independent check. Please retry the request.
   replaced         : true (missing_current_turn_spawn)

── answer after a SUCCESSFUL spawn whose delegated result is not disclosed
   user sent        : delegate the research to a background helper, and also tell me today's date
   model drafted    : Today is 11 August 2026.
   user receives    : I successfully started the requested sub-agent. Its result has not been verified in this turn yet.
   replaced         : true (successful_spawn_response_ungrounded)
Evidence: `comis explain --graph --offline` — graph notification disposition + caller-session join

Session: acme:telegram:chat-4471 Outcome: ok (endReason=unknown — rollup unresolved (no session-end record found), degraded=false) Graph: completed · graph-nightly-digest-1 · 2/2 succeeded · 0 failed · 0 skipped · notification=retained gather: completed · 0ms · attempts=1 · run=run-gather summarize: completed · 0ms · attempts=1 · run=run-summarize Session: acme:telegram:chat-4471 Graph: completed · graph-durability-off-2 · 2/2 succeeded · 0 failed · 0 skipped · notification=unavailable

  Session:    acme:telegram:chat-4471
  Trace:      
  Outcome:    ok (endReason=unknown — rollup unresolved (no session-end record found), degraded=false)
  Cost:       $0 · 0 tok (incl cache reads)
  Timing:     0 ms · 0 turns
  Summary:    0 tool failures across 0 turns; endReason=unknown
  Graph:      completed · graph-nightly-digest-1 · 2/2 succeeded · 0 failed · 0 skipped · notification=retained
    gather: completed · 0ms · attempts=1 · run=run-gather
    summarize: completed · 0ms · attempts=1 · run=run-summarize
  Session:    acme:telegram:chat-4471
  Trace:      
  Outcome:    ok (endReason=unknown — rollup unresolved (no session-end record found), degraded=false)
  Cost:       $0 · 0 tok (incl cache reads)
  Timing:     0 ms · 0 turns
  Summary:    0 tool failures across 0 turns; endReason=unknown
  Graph:      completed · graph-durability-off-2 · 2/2 succeeded · 0 failed · 0 skipped · notification=unavailable
    gather: completed · 0ms · attempts=1 · run=run-gather
    summarize: completed · 0ms · attempts=1 · run=run-summarize
Evidence: Stage-B live Telegram delivery + attachment-mirror scenario run

 RUN  v4.1.10 /home/ubuntu/.no-mistakes/worktrees/87e8ad14dbc6/01KZT7E7CH69P982R352SZ9CY2

 ✓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-B — both delivery oracles + the HARD cross-check + dedupe (no COMIS_LIVE) > keys the delivery tables on the DURABLE identity the product writes — conversation_ref, never a session_key column (the drift that hid a broken live leg) 1198ms
 ✓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-B — both delivery oracles + the HARD cross-check + dedupe (no COMIS_LIVE) > asserts the delivery_queue (status + attempt_count) and delivery_mirror (idempotency_key + pending->acknowledged) SEPARATELY (the two tables use distinct status lifecycles) 1200ms
 ✓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-B — both delivery oracles + the HARD cross-check + dedupe (no COMIS_LIVE) > the HARD dual-oracle cross-check (assertChannelTrace) PASSES on wire==mirror and THROWS on a mismatch (ORACLE-02) 1197ms
 ✓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-B — both delivery oracles + the HARD cross-check + dedupe (no COMIS_LIVE) > a same-second identical-text replay does NOT add a second delivery_mirror row (dedupe via the unique idempotency index) 1196ms
 ✓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-B — both delivery oracles + the HARD cross-check + dedupe (no COMIS_LIVE) > runDbOracle confirms the delivery store survived uncorrupted with exactly the expected delta (persistence oracle) 1212ms
 ↓ test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts > DELIV-01/ORACLE-02 Stage-C — delivery round-trip on BOTH oracles + dedupe (COMIS_LIVE) > a send round-trips through the real adapter->delivery path writing delivery_queue + delivery_mirror, the dual-oracle cross-check holds, and a replay dedupes
 ✓ test/live/scenarios/channels/telegram-attachment-mirror.test.ts > DELIV-02 Stage-B — outbound attachments reach the chat AND the delivery mirror > mirrors a captionless screenshot and voice note the user received, without deduping one away 129ms
 ✓ test/live/scenarios/channels/telegram-attachment-mirror.test.ts > DELIV-02 Stage-B — outbound attachments reach the chat AND the delivery mirror > records nothing when the attachment never reached the chat 66ms

 Test Files  2 passed (2)
      Tests  7 passed | 1 skipped (8)
   Start at  06:20:03
   Duration  13.04s (transform 2.23s, setup 26ms, import 6.52s, tests 6.20s, environment 0ms)
Evidence: Changed-package unit test run (68 files, 4350 tests)

 RUN  v4.1.10 /home/ubuntu/.no-mistakes/worktrees/87e8ad14dbc6/01KZT7E7CH69P982R352SZ9CY2

······································································································································································································································stdout | src/bridge/pi-event-bridge.test.ts > createPiEventBridge > compaction_end > memoryPort.store() rejection does not throw (fire-and-forget)
Suppressed error (compaction memory flush): DB write failed

·····························································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································stdout | src/background/auto-background-middleware.test.ts > wrapToolForAutoBackground > rejects immediately and aborts the tool when background capacity is exhausted
Suppressed error (auto-background rejected-admission cleanup): underlying tool aborted

·································································································································································stdout | src/delivery/delivery-service.test.ts > createDeliveryService — factory contract (smoke-level) > runAfterDelivery rejection does NOT corrupt the request (suppressError wrap preserved)
Suppressed error (after_delivery hook failed): hook bug

··························································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································································

 Test Files  68 passed (68)
      Tests  4350 passed (4350)
   Start at  06:17:36
   Duration  52.12s (transform 48.42s, setup 203ms, import 150.87s, tests 39.87s, environment 8ms)
Evidence: Real-daemon config-posture integration run (obs.system.health surfaces exec-sandbox relaxation)

 RUN  v4.1.10 /home/ubuntu/.no-mistakes/worktrees/87e8ad14dbc6/01KZT7E7CH69P982R352SZ9CY2

 ✓ |integration| test/integration/config-posture-wiring.test.ts > daemon config-posture composition > surfaces an agent exec-sandbox relaxation through system health 11ms

 Test Files  1 passed (1)
      Tests  1 passed (1)
   Start at  06:20:30
   Duration  15.03s (transform 7.23s, setup 19ms, import 55ms, tests 14.78s, environment 0ms)
Evidence: Evidence harnesses used for the manual verification runs
// Evidence harness (manual verification): the outbound-attachment path exactly as the
// daemon wires it — real grammy Telegram adapter -> loopback Telegram emulator, and
// adapter.sendAttachment -> after_delivery hook -> the delivery-mirror plugin -> a
// production-schema SQLite db. Prints what the chat received and what the runtime
// persisted, so both halves are reviewer-visible.
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { createRequire } from "node:module";

const ROOT = "/home/ubuntu/.no-mistakes/worktrees/87e8ad14dbc6/01KZT7E7CH69P982R352SZ9CY2";
// Resolve the native sqlite binding from the repo's installed tree (this harness lives in /tmp).
const Database = createRequire(`${ROOT}/package.json`)("better-sqlite3") as any;
const { createTelegramPlugin } = await import(`${ROOT}/packages/channels/dist/index.js`) as any;
const { createHookRunner, createPluginRegistry, runWithContext } = await import(
  `${ROOT}/packages/core/dist/index.js`
) as any;
const { initSchema, openSqliteDatabase } = await import(`${ROOT}/packages/memory/dist/index.js`) as any;
const { setupDeliveryMirror } = await import(
  `${ROOT}/packages/daemon/dist/wiring/setup-delivery.js`
) as any;
const { instrumentAttachmentDeliveries } = await import(
  `${ROOT}/packages/daemon/dist/wiring/setup-channels/attachment-delivery-hooks.js`
) as any;
const { createTgEmulator } = await import(`${ROOT}/test/live/emulators/telegram/tg-emulator.ts`) as any;

const noop = () => undefined;
const logger: any = {
  info: noop, warn: noop, error: noop, debug: noop, trace: noop, fatal: noop,
  child: () => logger,
};

const CHAT = { chatId: 424242 };
const BOT_TOKEN = "12345:test";
const dir = mkdtempSync(join(tmpdir(), "attach-mirror-evidence-"));
const dbPath = join(dir, "memory.db");
const db = openSqliteDatabase({
  dbPath,
  initSchema: (handle: any) => { initSchema(handle, 1536); },
});

const pluginRegistry = createPluginRegistry();
const mirror = await setupDeliveryMirror({
  db,
  config: {
    deliveryMirror: {
      enabled: true,
      retentionMs: 86_400_000,
      pruneIntervalMs: 300_000,
      maxEntriesPerInjection: 10,
      maxCharsPerInjection: 4000,
    },
  },
  pluginRegistry,
  logger,
});
const hookRunner = createHookRunner(pluginRegistry, { catchErrors: false });

const emulator = createTgEmulator({ botToken: BOT_TOKEN });
const handle = await emulator.start();
const adapter = createTelegramPlugin({
  getBotToken: () => BOT_TOKEN,
  apiRoot: handle.apiRoot,
  logger,
}).adapter;
const started = await adapter.start();
if (!started.ok) throw started.error;
instrumentAttachmentDeliveries(new Map([["telegram", adapter]]), {
  hookRunner,
  logger,
  clock: { now: () => Date.now() },
});

const endpoint = {
  channelType: "telegram",
  channelInstanceId: adapter.channelId,
  conversationId: String(CHAT.chatId),
  conversationKind: "direct" as const,
};
const inTurnScope = <T>(fn: () => Promise<T>): Promise<T> => runWithContext({
  tenantId: "default",
  userId: "user_a",
  sessionKey: "default:agent:default:user_a:telegram:peer:user_a",
  agentId: "default",
  turnScope: {
    conversation: {
      tenantId: "default",
      agentId: "default",
      partition: { kind: "endpoint-conversation-principal", endpoint, principalId: "user_a" },
    },
    principal: { principalId: "user_a" },
    endpoint,
  },
  traceId: "550e8400-e29b-41d4-a716-446655440000",
  startedAt: Date.now(),
  trustLevel: "admin",
}, fn);

const screenshot = join(dir, "screenshot.png");
const voiceNote = join(dir, "briefing.ogg");
writeFileSync(screenshot, Buffer.from("89504e470d0a1a0a0000000d49484452", "hex"));
writeFileSync(voiceNote, Buffer.from("4f676753000200000000000000000000", "hex"));

const sends = await inTurnScope(async () => [
  await adapter.sendAttachment(String(CHAT.chatId), { type: "image", url: screenshot }),
  await adapter.sendAttachment(String(CHAT.chatId), {
    type: "audio", url: voiceNote, isVoiceNote: true, durationSecs: 3,
  }),
]);

const line = (s = "") => process.stdout.write(`${s}\n`);
line("AGENT SENDS two captionless attachments to Telegram chat 424242");
line(`  adapter results          : ${sends.map((s: any) => (s.ok ? "ok" : `err:${s.error}`)).join(", ")}`);
line();
line("CHANNEL ORACLE — what the Telegram user received (recorded by the emulator):");
for (const out of emulator.outbound(CHAT)) {
  line(`  ${out.method}  mediaKind=${out.mediaKind ?? "-"}  caption=${JSON.stringify(out.caption ?? "")}`);
}
line();
line("MIRROR ORACLE — what the runtime persisted in delivery_mirror (real product schema):");
const reader = new Database(dbPath, { readonly: true });
const rows = reader.prepare(
  "SELECT origin, channel_type, channel_id, text, media_urls, idempotency_key, status FROM delivery_mirror ORDER BY created_at ASC, rowid ASC",
).all() as any[];
for (const row of rows) {
  line(`  origin=${row.origin} channel=${row.channel_type}/${row.channel_id} status=${row.status}`);
  line(`    text=${JSON.stringify(row.text)}  media_urls=${row.media_urls}`);
  line(`    idempotency_key=${row.idempotency_key}`);
}
const hash = (k: string) => k.split(":").slice(-2, -1)[0];
line(`  distinct idempotency hashes for the two captionless files: ${
  new Set(rows.map((r) => hash(r.idempotency_key))).size
} of ${rows.length}`);
line();

line("FAILED SEND — the emulator rejects sendPhoto; the mirror must claim nothing:");
emulator.fail("sendPhoto", { error_code: 400, description: "Bad Request: chat not found" });
const missing = join(dir, "never-sent.png");
writeFileSync(missing, Buffer.from("89504e470d0a1a0a", "hex"));
const failed = await inTurnScope(() => adapter.sendAttachment(String(CHAT.chatId), {
  type: "image", url: missing,
}));
const after = reader.prepare("SELECT COUNT(*) AS n FROM delivery_mirror").get() as { n: number };
line(`  adapter result           : ${failed.ok ? "ok" : "error (send refused by Telegram)"}`);
line(`  delivery_mirror row count: ${after.n} (unchanged — no phantom delivery recorded)`);

reader.close();
await adapter.stop().catch(() => undefined);
await emulator.stop().catch(() => undefined);
mirror.shutdown();
db.close();
rmSync(dir, { recursive: true, force: true });

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

⚠️ **Review** - 6 issues (4 warnings, 2 infos)
  • ⚠️ packages/agent/src/executor/prompt-skill-request-routing.ts:142 - Prompt-skill routing was broadened from nano-only to every capability class (executor-tool-assembly.ts:690 removed the capabilityClass !== &#34;nano&#34; gate), and the shipped deep-research skill now declares min-distinct-web-fetch-urls: 3 / min-distinct-web-search-queries: 3. Routing still admits a skill on only 2 shared content terms (prompt-skill-request-routing.ts:142, MIN_SHARED_TERMS=2) against deep-research's new ~65-content-term "MANDATORY…" description (explain, understand, research, information, current, online, source, answer, memory, search, reports, documentation, …). Once routed on a read-shaped turn, runRequestToolNudge arms the floor (request-tool-nudge.ts:535-539), runs up to 4 extra bounded continuations, and on failure returns recovered:false, which promoteToolInvocationStall (executor-post-execution.ts:934) turns into tool_invocation_stall — executor-post-execution.ts:2377 then REPLACES the model's answer with a deterministic "could not verify" reply and the rollup records degraded:true. Concrete path: user asks "explain the deployment reports we generated" on an agent with web_search/web_fetch available → 2+ shared terms route deep-research → floor armed → the model answers correctly from local context, or fetches only 2 distinct URLs because the third 404s → the correct answer is discarded. The docs acknowledge the design (docs/skills/manifest.mdx), but the combination of a 2-term routing bar, the new all-capability-class routing, and an answer-destroying completion gate on the default shipped skill is a product decision worth explicit sign-off — e.g. raising MIN_SHARED_TERMS for gate-bearing skills, or degrading an unmet floor to an appended sourcing caveat instead of discarding the reply.
  • ⚠️ packages/core/src/security/secret-detection.ts:145 - The entropy backstop gate changed from a delimiter denylist /[\s:\/?&amp;=@,]/ to an allowlist CREDENTIAL_BODY_RE = /^[A-Za-z0-9_.+-]+$/, so looksLikeSecretValue now short-circuits to false for any value containing characters outside [A-Za-z0-9_.+-]. The false positive this fixed — the added corpus line const name = document.getElementById(&#39;runName&#39;).value.trim(); — is scrubbed per whitespace-delimited token (NON_WHITESPACE_TOKEN_RE = /\S+/g in secret-egress-guard.ts), and the offending token is excluded by (, ), &#39; under either regex, so the new allowlist is far stricter than the fix required. Failure scenario: a bare 44+ char high-entropy credential containing ordinary password punctuation not present in the old set — e.g. a Django-style SECRET_KEY drawn from a-z0-9!#$%^*(-_=+) that happens to contain no @, &amp;, =, :, /, ?, or , (~13% of 50-char keys) — was previously redacted by the entropy backstop and now passes scrubSecretsFromText unredacted into agent output, memory writes, and graph announcements. The labelled-assignment paths still cover password=… shapes, so this is the last-resort net only. This also bears on the intent's REQUIRED criterion "retain strict security and no-backward-compatibility contracts … do not weaken checks". A narrower exclusion (keeping the old delimiters and adding only source-expression characters such as ()[]{}&#39;&#34;) would fix the reported false positive without shrinking the net.
  • ⚠️ packages/agent/src/executor/executor-post-execution.ts:1758 - enforceRuntimeSelfReportEvidence now returns three reasons, but the execution:recovery_attempted emit only branches on unsupportedOutageReceipt. A correction with reason unsupported_runtime_self_report_evidence (the receipt existed but could not support a comparative-latency or provider-billed-cost claim) is published on the event bus as missing_runtime_self_report_evidence — the opposite claim, since a successful obs_query receipt did exist. events-messaging.ts added unsupported_outage_receipt_evidence to the reason union but not this third value. Failure scenario: a user asks "why was the slowest execution slow?", obs_query succeeds with crossExecutionDurationRanking: &#34;unavailable&#34;, the guard replaces the answer with the unsupported-evidence reply, and comis explain / the trajectory report "lacked a successful current-turn obs_query receipt" — sending the operator to check obs_query admission for a turn where obs_query worked fine. The precise reason survives only in the audit-event metadata.
  • ⚠️ packages/agent/src/executor/executor-post-execution.test.ts:1197 - Three newly added tests ("source-grep — outbound audio completion requires current delivery evidence", "— outbound image completion…", "— elliptical delivery status…") read executor-post-execution.ts as text and assert regex matches on function names (enforceOutboundAudioEvidence\(), string literals (response.outbound_audio_evidence_guard), array-literal shapes (acceptedEvidence: [ &#34;tts_synthesize&#34;, … ]), and indexOf call ordering. Existing runtime-self-report and citation source-greps also gained new source-content lines. This is the flagged anti-pattern: a behavior-preserving refactor (renaming the local, extracting the emit, reordering an object literal) breaks these while the behavior is intact, and a commented-out or dead call site would satisfy them. The guards themselves already have real behavior probes in executor-response-filter.test.ts and degraded-reply.test.ts; what the source-greps uniquely claim — that postExecution actually invokes each guard, emits the audit/recovery events, and does so before synchronizeFinalAssistantResponse — is directly observable by driving postExecution with a stub eventBus and asserting the emitted events and final result.response. Replace these three with that probe rather than extending the grep suite; no repo-wide cleanup of the ~28 pre-existing source-greps is being requested.
  • ℹ️ packages/agent/src/executor/response-grounding.ts:527 - enforceOutboundAudioEvidence fully short-circuits when runtimeAudioDelivery === true, which orchestrator/execution-execute.ts:206-215 computes by pre-running autoVoiceDeliveryActive — the auto-TTS decision, not the delivery outcome. executeVoiceResponse deliberately returns ok({ voiceSent: false }) on every synthesis/conversion error so text delivery can proceed. Failure scenario: user says "say that out loud", the voice route is configured so the guard passes an "I've recorded that for you" reply unchecked, then TTS synthesis or the ffmpeg conversion fails and the pipeline falls back to text — the user receives a text message asserting audio was sent, which is exactly the claim this guard exists to prevent. Not a regression (the guard is new on this branch) and the prediction matches executeVoiceResponse's own hasMediaUrl: false input, so the hole is bounded to delivery-time failures; closing it would mean re-checking the claim after the voice pipeline settles rather than before execution returns.
  • ℹ️ packages/agent/src/executor/citation-evidence.ts:370 - citationEvidenceDigestsForTurn changes the allowed-digest set from the union current ∪ relayed ∪ historical to fresh.length &gt; 0 ? fresh : historical, so durable receipts from earlier turns in the same session are excluded the moment this turn produces any fresh fetch or relayed digest. Failure scenario: a follow-up where the assistant fetches one new source and legitimately re-cites two URLs it fetched (and journalled) two turns earlier — enforceCitationEvidence now strips those two prior citations from the reply even though durable receipts back them, degrading a correct sourced answer. The comment states this is intentional ("durable receipts fill only an evidence-free follow-up"), and the shipped deep-research procedure now instructs re-fetching every candidate citation, which mitigates it in the routed case; flagging so the tradeoff is a conscious one for non-deep-research turns.
  • ℹ️ packages/daemon/src/wiring/setup-delivery.ts:663 - The delivery-mirror after_delivery hook's missing-authority branch was demoted from logger.warn with errorKind: &#34;precondition&#34; and an actionable hint to logger.debug. The demotion is justified for the new origin: &#34;channel:attachment&#34; path (attachments delivered outside a resolved turn are routine and correctly skipped), but it also silences the pre-existing text-delivery case: if a regression breaks ctx.deliveryAuthority/ctx.destinationEndpoint binding for ordinary replies, mirroring silently stops and the agent loses its own-utterance continuity across restarts with no evidence at the default log level. This runs against the CLAUDE.md rule that diagnosability must not depend on logLevel: debug having been set before the incident. Consider branching on event.origin — DEBUG for channel:attachment, WARN for a turn-bound text delivery.

🔧 Fix: narrow evidence-floor arming, restore secret entropy net, replace source-greps
6 issues (4 warnings, 2 infos) still open:

  • ⚠️ packages/agent/src/executor/prompt-skill-request-routing.ts:198 - The fix raises floor-arming from 2 to 3 shared terms, but the answer-discarding path is still reachable on ordinary local-context prose. Verified against the SHIPPED skills/deep-research/SKILL.md description (63 content terms after tokenize()/ROUTING_STOPWORDS, including explain, information, reports, documentation, source, answer, memory, search, current, online, topic, short, paragraph, skill, load, instead): "explain what information those reports actually contain" scores exactly 3 (explain, information, reports) and "search my memory for the current status" scores 3 (search, memory, current). Both arm min-distinct-web-fetch-urls:3 + min-distinct-web-search-queries:3. Neither is a conversation-history lookup, so CONVERSATION_HISTORY_RECALL_PATTERN (line 143) does not exclude them. Once armed, runRequestToolNudge (request-tool-nudge.ts:447) sets webEvidenceGateActive, returns recovered:false when the receipts never arrive, promoteToolInvocationStall raises tool_invocation_stall, and executor-post-execution replaces the model's correct answer with the deterministic "could not verify" reply while the rollup records degraded:true. The fix round's own new docs guidance (docs/skills/manifest.mdx:144, "Write a narrow, distinctive description on any skill that declares a floor") is contradicted by the only shipped skill that declares floors — its description is deliberately broad ("MANDATORY: … any request to understand or explain a topic …"). A threshold bump against a 63-term generic description does not establish the invariant; the durable boundary is either (a) narrowing the shipped description to distinctive terms, or (b) degrading an unmet floor to an appended sourcing caveat instead of discarding the reply. Both change product behavior, so this needs the author's call rather than another threshold tweak.
  • ⚠️ packages/agent/src/executor/executor-post-execution.ts:1760 - The fix round added the unsupportedEvidence ? &#34;unsupported_runtime_self_report_evidence&#34; : … branch to the execution:recovery_attempted emit, but nothing fails if that branch is wrong. response-grounding.test.ts:228/289 only proves the guard RETURNS that reason; events-messaging.test.ts:33 only proves the bus type carries the string; and the source-grep at executor-post-execution.test.ts:1223 uses lazy [\s\S]*? between "unsupported_outage_receipt_evidence" and "missing_runtime_self_report_evidence", so it matched before the fix and still matches with the new branch deleted. The mapping the fix exists to correct — guard reason -> emitted event reason inside postExecution — has no assertion. The harness the same commit added (pi-executor.test.ts recoveryReasons(deps)) can assert it directly: drive execute() with an obs_query result carrying observabilityEvidenceLimits.crossExecutionDurationRanking: &#34;unavailable&#34; on a "why was the slowest execution slow" request and assert the emitted reason is unsupported_runtime_self_report_evidence, not missing_runtime_self_report_evidence.
  • ⚠️ packages/agent/src/executor/executor-post-execution.test.ts:1222 - The fix round removed the three new source-grep tests but left the source-content-only assertions this branch ADDED to two existing greps: line 1222 expect(stripped).toMatch(/unsupported_outage_receipt_evidence/) and lines 1223-1225's emit-shape regex, plus line 1145 expect(stripped).toMatch(/citationEvidenceDigestsForTurn\(\{/). Each asserts only that a literal or a call spelling appears in executor-post-execution.ts text: renaming the local, hoisting the emit into a helper, or commenting the call out satisfies or breaks them independently of behavior — and as noted above, line 1223's lazy regex already matched the pre-fix source. Per the test-quality rule these newly added assertions should be removed or refined; the pi-executor.test.ts probe pattern the same commit introduced (assert result.response + recoveryReasons/auditActionTypes from a driven execute()) is the semantic replacement. No cleanup of the ~28 pre-existing greps is being requested.
  • ⚠️ packages/daemon/src/graph/graph-completion.ts:288 - When a graph has an announcement route but deps.sendGovernedAnnouncement is undefined, handleGraphCompletion now records announcementDelivery: &#34;unavailable&#34;, skips delivery entirely (line 296 gates on !== undefined), writes run metadata, and returns ok(undefined). Before this branch the same state fell into sendGoverned, which logged logger.error with missingPrerequisites: [&#34;sendBoundary&#34;] and an actionable repair hint (graph-announcement-delivery.ts:59-66) and returned err. The new path emits nothing above INFO — the only evidence is one field on the "Graph execution complete" INFO line — so a wiring regression that drops the governed sender would silently swallow every graph completion announcement while the coordinator reports success. This is currently unreachable in production (createAnnouncementDelivery always returns sendGovernedAnnouncement, and daemon.ts:502 only spreads a truthy value), which is precisely why the missing WARN matters: nothing else would surface the regression. Emit a logger.warn with errorKind: &#34;precondition&#34; and a hint naming the governed-announcement boundary on the &#34;unavailable&#34; branch. Contrast the sibling branches at lines 292-295 and 298-301, which both keep the err + WARN contract.
  • ℹ️ packages/daemon/src/wiring/setup-delivery.ts:663 - This round-1 finding was selected for fixing but the fix round did not touch this file — the missing-authority branch is still a single logger.debug for every origin. The demotion is correct for the new origin: &#34;channel:attachment&#34; path (attachments delivered outside a resolved turn are routinely skipped), but it also silences the pre-existing turn-bound text-delivery case: if ctx.deliveryAuthority/ctx.destinationEndpoint binding regresses for ordinary replies, mirroring stops and the agent loses own-utterance continuity across restarts with zero evidence at the default log level, against CLAUDE.md's rule that diagnosability must not depend on logLevel: debug having been set before the incident. Branch on event.origin: DEBUG for channel:attachment, WARN with the prior errorKind: &#34;precondition&#34; + hint for a turn-bound text delivery.
  • ℹ️ packages/core/src/security/secret-detection.ts:148 - Noting the residual delta for the record, not asking for a change. NON_CREDENTIAL_STRUCTURE_RE restores the base's denylist form but widens it from [\s:/?&amp;=@,] to also exclude ; &#39; &#34; ( ) [ ] { } < > \ |. Verified that the minimum needed for the motivating false positive (document.getElementById('runName').value.trim();— 48 chars, H=4.34, not excluded under the base regex) is only(, ), '; the extra characters shrink the last-resort entropy net relative to base f8c4401 for an unlabelled 44+ char credential that happens to contain a semicolon, quote, bracket, angle bracket, backslash, or pipe. Password punctuation (! # $ % ^ * ~ + - _ .`) is back inside the net, and the labelled-assignment paths are unaffected, so the tradeoff is narrow and the code documents it — recorded so the intent's "do not weaken checks" criterion is evaluated against a known, bounded delta rather than an unexamined one.
✅ **Test** - passed

✅ No issues found.

  • CI=true npx vitest run &lt;68 changed packages/**/*.test.ts&gt; — every changed package unit test file (4350 tests)
  • CI=true npx vitest run test/architecture/locale-pack-ids-doc-parity.test.ts scripts/contracts/real-user-target-contract.test.ts
  • cd test/live/self-driving/scripts &amp;&amp; CI=true npx vitest run --config vitest.config.ts — live-kit driver/oracle project (16 files, 173 tests)
  • CI=true npx vitest run -c test/live/vitest.config.ts test/live/scenarios/channels/telegram-attachment-mirror.test.ts test/live/scenarios/channels/telegram-delivery-roundtrip.test.ts — Stage-B real grammy adapter → loopback Telegram emulator → production-schema delivery_mirror (Stage-C skipped: needs COMIS_LIVE credentials)
  • CI=true npx vitest run -c test/live/vitest.config.ts test/live/assert/channel-trace.test.ts test/live/harness/rig.test.ts test/live/scenarios/channels/{signal-foundation-proof,telegram-acceptance-vl,telegram-groups,telegram-reaction-learning}.test.ts
  • CI=true npx vitest run -c test/vitest.config.ts test/integration/config-posture-wiring.test.ts — boots a real daemon, asserts the exec-sandbox relaxation reaches obs.system.health
  • CI=true npx vitest run -c test/vitest.config.ts test/live/self-driving/scripts/remote-root.test.ts
  • CI=true npx vitest run test/architecture/generic-runtime-boundary.test.ts — the domain-neutral runtime gate named in the intent
  • CI=true npx vitest run test/architecture/{contract-codegen-drift,contract-bundle-size,contract-handler-parity}.test.ts — gates over the regenerated web API contracts
  • CI=true npx vitest run test/architecture/{autonomy-skill-no-drift,bundled-skills-declare-requirements}.test.ts — bundled deep-research SKILL.md mirror parity
  • pnpm build (needed by the integration/live dist-aliased tiers)
  • Manual evidence run: npx tsx /tmp/no-mistakes-evidence/01KZT7E7CH69P982R352SZ9CY2/attachment-mirror-evidence.mts — drove the daemon's own instrumentAttachmentDeliveries + setupDeliveryMirror wiring against the Telegram emulator and dumped the persisted delivery_mirror rows
  • Manual evidence run: node /tmp/no-mistakes-evidence/01KZT7E7CH69P982R352SZ9CY2/graph-record-writer.mjs (real writeRunMetadata from packages/daemon/dist) then COMIS_DATA_DIR=… node packages/cli/dist/cli.js explain &lt;graphId&gt; --graph --offline
  • Manual evidence run: node /tmp/no-mistakes-evidence/01KZT7E7CH69P982R352SZ9CY2/reply-transcript.mjs — rendered the chat text a user receives from the built outbound-evidence guards and locale catalog
⚠️ **Document** - 1 info
  • ⚠️ packages/daemon/bundled-skills/deep-research/SKILL.md:1 - Out-of-scope consolidation worth a follow-up: skills/&lt;name&gt;/SKILL.md and packages/daemon/bundled-skills/&lt;name&gt;/SKILL.md are two byte-identical hand-maintained copies of the same 7 shipped skills, and this change edited both copies of deep-research/SKILL.md in lockstep (+17 each) to add the min-distinct-web-* floors and the receipt-ledger procedure. No gate enforces parity between the trees (test/architecture/ has no deep-research or skills-tree check; packages/daemon/package.json just ships bundled-skills verbatim), so the next edit to one copy silently ships a stale bundled skill while the repo-visible one reads correct — and docs/skills/manifest.mdx cites skills/deep-research/SKILL.md as the authoritative example while the runtime seeds from the bundled copy. Fixing this properly means either generating/copying the bundled tree from skills/ at pack time or adding an architecture parity gate; both are build/packaging changes beyond a documentation pass. All 7 overlapping pairs are currently in sync, so nothing is stale today.

🔧 Fix: name the shipping skill tree as single owner
1 info still open:

  • ℹ️ packages/daemon/bundled-skills/deep-research/SKILL.md:1 - Follow-up proposal, doc half resolved: AGENTS.md §6.5 now names packages/daemon/bundled-skills/&lt;name&gt;/SKILL.md as the only tree that ships and seeds, records that the repo-root skills/ tree is a non-shipping mirror of seven of those skills, and states the edit-both rule — so the misdirection that let this change touch two hand-maintained copies in lockstep is documented at its owner. The enforcement half remains open and cannot be done in a documentation phase: byte parity is gated for deep-research only (scripts/contracts/real-user-target-contract.test.ts:88), leaving chart-visualization, find-skills, image-generation, log-troubleshooting, podcast-generation, and video-generation free to drift silently. Closing it means either an architecture parity test over all seven mirrored pairs or generating/copying the mirror from the shipped tree at build time — both are test or build changes this phase is forbidden to make. All seven pairs are byte-identical today (verified with diff -rq), so nothing is stale right now.
✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@github-actions

Copy link
Copy Markdown

PR description incomplete

Please fill in all required sections before this PR can be reviewed.

Required sections: Description, Related Issue, Type of Change, Checklist, RED Test Proof.

For code changes in packages/*/src/**, paste the failing test output (test name + assertion error) from before the production patch in the RED Test Proof section, or write EXEMPT: <reason> for docs/CI/config-only PRs.

See CONTRIBUTING.md for the full contribution bar.

@mintlify

mintlify Bot commented Aug 12, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
comis 🟢 Ready View Preview Aug 12, 2026, 6:52 AM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@anconina
anconina merged commit af2b0fc into main Aug 12, 2026
18 checks passed
@anconina
anconina deleted the test/real-user-telegram-local-20260810 branch August 12, 2026 08:26
@anconina anconina mentioned this pull request Aug 12, 2026
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant