<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[WeMAIde Engineering Notes]]></title><description><![CDATA[WeMAIde Engineering Notes]]></description><link>https://wemaide-engineering.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Sun, 01 Nov 2026 13:09:32 GMT</lastBuildDate><atom:link href="https://wemaide-engineering.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Spreadsheet Write Succeeded. The Confirmation Failed.]]></title><description><![CDATA[Separate confirmed submissions, spreadsheet writes and Telegram acknowledgements. A tested example explains safe recovery after a timeout.]]></description><link>https://wemaide-engineering.hashnode.dev/the-spreadsheet-write-succeeded-the-confirmation-failed</link><guid isPermaLink="true">https://wemaide-engineering.hashnode.dev/the-spreadsheet-write-succeeded-the-confirmation-failed</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[api integration]]></category><dc:creator><![CDATA[WM Info]]></dc:creator><pubDate>Sat, 24 Oct 2026 16:35:20 GMT</pubDate><content:encoded><![CDATA[<p>A person confirms an enquiry in a Telegram bot. The application appends a row to Google Sheets, sends a confirmation and marks the job complete. The happy path fits in one sentence.</p>
<p>Now interrupt the response after the spreadsheet accepts the append. The worker sees a timeout. If it retries the entire job, the operator may receive two enquiries. If it assumes success, it may acknowledge a request whose write never completed. Neither conclusion follows from the timeout alone.</p>
<p>We built a small reference implementation to make that boundary inspectable. It uses synthetic enquiries and an offline demonstration; it is not a client incident report or a production reliability benchmark. The default run calls no external service and needs no credentials.</p>
<h2>The submission is not the Telegram user</h2>
<p>A chat identifier tells the bot where a conversation happens. It does not identify a single business request. One person can legitimately submit two enquiries. Using their chat ID as the deduplication key would suppress the second one.</p>
<p>Give each confirmed submission a stable reference and preserve its payload. A repeated confirmation should refer to the existing submission. A new enquiry should receive a new reference. If an existing reference arrives with different material fields, stop and inspect the conflict rather than silently treating the changed payload as a replay.</p>
<p>The reference keeps request type, summary and chat identity with the submission. An intake layer still needs to show the summary and obtain confirmation. The downloadable module starts after that boundary; it is not a complete conversation bot.</p>
<h2>Persist the intention before attempting the write</h2>
<p>A useful set of write states is:</p>
<pre><code class="language-text">pending → attempting → confirmed
                    ↘ uncertain → reconciliation or review
</code></pre>
<p>Persisting attempting before the external request lets a later worker recognise that an interrupted attempt may have crossed the boundary. It should not restart as if no write had been attempted.</p>
<p>The example serialises work inside one processor. That handles concurrent calls in that process. It is not a substitute for transactional storage and worker claims when several processes operate on the same queue. The included file adapter is a teaching convenience, not a crash-proof distributed transaction.</p>
<h2>Append does not enforce your business identity</h2>
<p>Google Sheets values.append finds a logical table in the supplied range and adds values after it. The application supplies its own submission reference as a column. The append method does not make that reference unique.</p>
<p>The reference uses RAW input for enquiry text and expects a fixed six-column layout. Both choices are explicit parts of the adapter contract. Renaming a tab or rearranging columns is a configuration change that needs a test, not an invisible spreadsheet housekeeping task.</p>
<p>API behaviour: <a href="https://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets.values/append">https://developers.google.com/workspace/sheets/api/reference/rest/v4/spreadsheets.values/append</a></p>
<p>When an earlier append is uncertain, the processor looks for the submission reference and compares the stored request fields. Exactly one matching row can reconcile that write. Multiple rows, conflicting content or no matching row leave a review task.</p>
<p>An empty lookup immediately after a timeout is not treated as proof that it is safe to append again. That conservative choice can require an operator. It avoids turning incomplete evidence into an automatic second side effect.</p>
<h2>Acknowledgement is a separate operation</h2>
<p>Suppose the spreadsheet write is confirmed and only the Telegram response is lost. Repeating the spreadsheet append cannot fix the acknowledgement. The write already has its own result.</p>
<p>Keep confirmation state separately:</p>
<pre><code class="language-text">write: confirmed
acknowledgement: uncertain
</code></pre>
<p>On a successful sendMessage response, the Bot API returns a Message. Retain the returned identifier as evidence of API acceptance. It does not establish that the recipient read the message. After an unknown result, choose a deliberate recovery policy rather than treating another send as harmless.</p>
<p>API reference: <a href="https://core.telegram.org/bots/api#sendmessage">https://core.telegram.org/bots/api#sendmessage</a></p>
<p>In the teaching reference, an uncertain acknowledgement goes to review and is not automatically repeated. A production product could choose a different tradeoff, but it should name that tradeoff and make the outcome visible to its operator.</p>
<h2>Test the boundary, not just the success message</h2>
<p>The offline demo submits one enquiry, replays it and then submits a different enquiry from the same synthetic chat. The result is two recorded rows and two acknowledgements. That is an observation from the demo, not an exactly-once claim about real external APIs.</p>
<p>The automated checks also interrupt the append after acceptance, restart the processor, supply conflicting row content, return multiple matching rows and lose an acknowledgement response. They check that the processor preserves the distinction between confirmed work and an unknown outcome.</p>
<p>Adapter tests use synthetic request/response data. We did not connect real Google or Telegram accounts for this exercise. A deployment still needs authenticated intake, a supported credential flow, production storage, quotas and bounded retries, monitoring, retention and a recovery owner.</p>
<p>The source and setup notes are linked from the WeMAIde Telegram/Sheets integration guide: <a href="https://wemaide.com/insights/telegram-bot-google-sheets-integration?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=telegram-sheets-reference">https://wemaide.com/insights/telegram-bot-google-sheets-integration?utm_source=hashnode&amp;utm_medium=referral&amp;utm_campaign=telegram-sheets-reference</a></p>
<h2>Keep the operator's question answerable</h2>
<p>The useful question is not merely whether the workflow ran. It is which confirmed request was recorded, whether the external write is known to exist, whether its acknowledgement completed and which uncertainty still needs attention.</p>
<p>Separating those records makes a small integration easier to recover. It also keeps the implementation honest: local coordination, external acceptance and a person's confirmation are related events, not one transaction.</p>
<p>Disclosure: Prepared with AI assistance and reviewed against the accompanying reference code and its local tests. All demonstration enquiries are synthetic. No client results, production uptime or performance savings are claimed.</p>
]]></content:encoded></item></channel></rss>