@volter/twin-telegram 1.0.0

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.
Files changed (92) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +53 -0
  3. package/dist/generated/pack-facts.json +53 -0
  4. package/dist/journeys/coverage.json +138 -0
  5. package/dist/journeys/customer-life.json +1229 -0
  6. package/dist/journeys/customer-life.outline.md +99 -0
  7. package/dist/journeys/customer-life.outline.verdict.json +76 -0
  8. package/dist/journeys/customer-life.verdict.json +134 -0
  9. package/dist/journeys/decisions.json +95 -0
  10. package/dist/journeys/demand.json +152 -0
  11. package/dist/journeys/score.json +163 -0
  12. package/dist/journeys/unreachable.json +29 -0
  13. package/dist/journeys/vendor-examples.json +1554 -0
  14. package/dist/journeys/vendor-examples.verdict.json +14 -0
  15. package/dist/spec/SOURCE.md +51 -0
  16. package/dist/spec/bot-api.html.gz +0 -0
  17. package/dist/spec/doc-examples.json +344 -0
  18. package/dist/spec/grouping.json +1 -0
  19. package/dist/spec/openapi.json.gz +0 -0
  20. package/dist/spec/page-to-openapi.ts +258 -0
  21. package/dist/spec/patches.json +289 -0
  22. package/dist/spec/sources.json +416 -0
  23. package/dist/src/cli.d.ts +2 -0
  24. package/dist/src/cli.js +21 -0
  25. package/dist/src/engine/media.d.ts +17 -0
  26. package/dist/src/engine/media.js +75 -0
  27. package/dist/src/engine/text.d.ts +45 -0
  28. package/dist/src/engine/text.js +512 -0
  29. package/dist/src/fetch.d.ts +2 -0
  30. package/dist/src/fetch.js +17 -0
  31. package/dist/src/generated/surface.gen.json +1 -0
  32. package/dist/src/generated/ui.gen.json +1 -0
  33. package/dist/src/index.d.ts +5 -0
  34. package/dist/src/index.js +9 -0
  35. package/dist/src/manifest.d.ts +2 -0
  36. package/dist/src/manifest.js +104 -0
  37. package/dist/src/semantics/around.d.ts +2 -0
  38. package/dist/src/semantics/around.js +32 -0
  39. package/dist/src/semantics/available-methods.d.ts +12 -0
  40. package/dist/src/semantics/available-methods.js +266 -0
  41. package/dist/src/semantics/doors.d.ts +18 -0
  42. package/dist/src/semantics/doors.js +292 -0
  43. package/dist/src/semantics/getting-updates.d.ts +3 -0
  44. package/dist/src/semantics/getting-updates.js +68 -0
  45. package/dist/src/semantics/index.d.ts +2 -0
  46. package/dist/src/semantics/index.js +19 -0
  47. package/dist/src/semantics/shared.d.ts +112 -0
  48. package/dist/src/semantics/shared.js +424 -0
  49. package/dist/src/semantics/states.d.ts +18 -0
  50. package/dist/src/semantics/states.js +37 -0
  51. package/dist/src/semantics/updating-messages.d.ts +2 -0
  52. package/dist/src/semantics/updating-messages.js +48 -0
  53. package/dist/src/server.d.ts +7 -0
  54. package/dist/src/server.js +24 -0
  55. package/generated/pack-facts.json +53 -0
  56. package/journeys/coverage.json +138 -0
  57. package/journeys/customer-life.json +1229 -0
  58. package/journeys/customer-life.outline.md +99 -0
  59. package/journeys/customer-life.outline.verdict.json +76 -0
  60. package/journeys/customer-life.verdict.json +134 -0
  61. package/journeys/decisions.json +95 -0
  62. package/journeys/demand.json +152 -0
  63. package/journeys/score.json +163 -0
  64. package/journeys/unreachable.json +29 -0
  65. package/journeys/vendor-examples.json +1554 -0
  66. package/journeys/vendor-examples.verdict.json +14 -0
  67. package/package.json +37 -0
  68. package/spec/SOURCE.md +51 -0
  69. package/spec/bot-api.html.gz +0 -0
  70. package/spec/doc-examples.json +344 -0
  71. package/spec/grouping.json +1 -0
  72. package/spec/openapi.json.gz +0 -0
  73. package/spec/page-to-openapi.ts +258 -0
  74. package/spec/patches.json +289 -0
  75. package/spec/sources.json +416 -0
  76. package/src/cli.ts +22 -0
  77. package/src/engine/media.ts +67 -0
  78. package/src/engine/text.ts +409 -0
  79. package/src/fetch.ts +18 -0
  80. package/src/generated/surface.gen.json +1 -0
  81. package/src/generated/ui.gen.json +1 -0
  82. package/src/index.ts +11 -0
  83. package/src/manifest.ts +114 -0
  84. package/src/semantics/around.ts +33 -0
  85. package/src/semantics/available-methods.ts +253 -0
  86. package/src/semantics/doors.ts +277 -0
  87. package/src/semantics/getting-updates.ts +64 -0
  88. package/src/semantics/index.ts +23 -0
  89. package/src/semantics/shared.ts +428 -0
  90. package/src/semantics/states.ts +38 -0
  91. package/src/semantics/updating-messages.ts +41 -0
  92. package/src/server.ts +17 -0
@@ -0,0 +1,99 @@
1
+ # Tide Labs on Telegram: the outline
2
+
3
+ **The customer.** Tide Labs, a two-person software studio in Lisbon. Ana Ribeiro, its founder, runs an agent on Volter
4
+ Harness that she and the team talk to on Telegram, and publishes the studio's news to its Telegram channel with
5
+ Postiz. Bruno Costa, its engineer, is on the team chat. The story runs from Monday 5 January to Friday 27 February
6
+ 2026: two bots made, the agent at work in a private chat and a group, a news channel connected to Postiz and posted to
7
+ for six weeks, the agent's group retired and, at the end, the agent's token revoked. The channel and its bot stay.
8
+
9
+ ## Who acts, and with what
10
+
11
+ - **Ana** and **Bruno**: Telegram accounts, acting in the Telegram app (the World's doors stand in for the app: an
12
+ account, a chat made, a member added or removed, a message posted). An account holds no API credential.
13
+ - **BotFather**, Telegram's bot for making bots (https://core.telegram.org/bots/features#botfather): Ana makes each
14
+ bot there and is given its token; at the end she makes one again there, which revokes the one before (the World's
15
+ bot doors stand in for /newbot and /token).
16
+ - **The agent bot**, @tidelabs_agent_bot, token A: Volter Harness's Telegram channel (the Chat SDK's adapter, polling).
17
+ Its grant is the bot's: it reads the updates sent to it and writes in chats it is in. Privacy mode is on, as every
18
+ bot's is by default, so in a group where it is a plain member it receives only commands meant for it, replies to it
19
+ and service messages (https://core.telegram.org/bots/features#privacy-mode); an administrator bot is sent every
20
+ message, which is why Ana, once the agent misses the team's messages, makes it an administrator of the team group.
21
+ Harness's board, where the agent files work as cards, tells a chat subscribed to a card when it is done, the card's
22
+ files attached; the Chat SDK sends a message with files as MarkdownV2 (supercode board/dispatch.mjs notifyChats,
23
+ @chat-adapter/telegram's resolveParseMode).
24
+ - **The news bot**, @tidelabs_news_bot, token N: the bot Postiz posts with. Postiz's Telegram integration calls with
25
+ the one bot its deployment configures, its TELEGRAM_TOKEN (postiz-app telegram.provider.ts), so Ana runs Postiz
26
+ herself (docker compose, local storage) and sets its TELEGRAM_TOKEN to token N. She makes the bot an administrator of
27
+ the channel allowed to post and to delete messages.
28
+
29
+ A bot can write to a person in private only after the person has started it, and to a group or channel only while it
30
+ is a member; a bot deletes another's message in a channel only with the right to delete messages.
31
+
32
+ ## Acts
33
+
34
+ 1. **Monday 5 January, 10:00. Two people, one bot.** Ana and Bruno have Telegram accounts. Ana makes the agent bot
35
+ with BotFather and is given token A. In Telegram she opens the bot, presses Start (/start) and writes: "Hi! I'm Ana
36
+ from Tide Labs. Can you keep notes of our standups?" (doors: people, bots, a private message twice).
37
+ 2. **10:05. The agent comes up.** Ana puts token A in her Harness profile and starts Harness. Its adapter reads the
38
+ bot itself (getMe), removes any webhook so it can poll (deleteWebhook, keeping pending updates), and polls
39
+ (getUpdates, timeout 30): Ana's /start and her message come back at once. For her message it shows the bot typing in
40
+ her private chat (sendChatAction) and posts the agent's answer there (sendMessage, plain text).
41
+ 3. **11:00. The team chat.** Ana makes the group "Tide Labs team" with Bruno and the agent bot, which the app adds as
42
+ a plain member (door). Harness's poll, from the update after the last it read (offset), receives the bot's own
43
+ membership change (my_chat_member) and the service message that the group was created, which privacy mode lets
44
+ through. At 11:10 Bruno tells the team standups are at 9:00 on Tuesdays from tomorrow, items posted in the group, and
45
+ asks "@tidelabs_agent_bot what did Ana ask you to track?"; privacy mode sends a plain member neither (a plain
46
+ mention is no command and no reply to it), so no answer comes. At 11:25 Ana reads why and makes the agent an
47
+ administrator of the group (door), and Bruno asks again with the standup time. Harness's next poll receives the
48
+ bot's promotion and Bruno's second question, and nothing of his first two messages; the agent answers in the group
49
+ that it will post the digest at 9:15 (no typing indicator outside a private chat). Ana makes the group the
50
+ profile's home chat in Harness (no Telegram call).
51
+ 4. **Tuesday 6 January, 09:00. The first standup and its digest.** Ana posts her items in the group, and Harness's poll,
52
+ held open, returns with them as she posts; at 09:02 Bruno posts his (blocked on staging credentials) and Harness's
53
+ next poll, from the update after hers, returns with them. At 09:15 Harness's scheduled job posts the digest of
54
+ exactly those items to the home chat (sendMessage to the group).
55
+ 5. **Wednesday 7 January, 14:00. The news channel.** Ana makes the public channel "Tide Labs News" (@tidelabs_news)
56
+ with the studio's logo as its photo (door). She makes the news bot with BotFather (token N) and adds it to the
57
+ channel as an administrator that may post and delete messages (door). In the Postiz she runs herself (docker compose,
58
+ local storage) she sets TELEGRAM_TOKEN to token N, and then chooses to add Telegram; Postiz
59
+ shows the command to post, "/connect" and a word, and she posts it in the channel (door). Postiz's connect screen
60
+ polls (getUpdates, its library sending allowed_updates as the fields allowed_updates[0]=message and
61
+ allowed_updates[1]=channel_post, which the Bot API does not read as its JSON-serialized list, so every update type
62
+ comes back): the bot's own membership change and her post come back. Postiz reads its bot (getMe), checks the bot
63
+ may delete messages there (getChatMember: an administrator with can_delete_messages), deletes her /connect post
64
+ (deleteMessage), posts "Connection Successful. Message will be deleted in 10 seconds." (sendMessage) and, ten
65
+ seconds later, deletes that too (deleteMessage). It then reads the channel (getChat), the photo's file (getFile), and copies the photo into its
66
+ storage from the file's link (the file download).
67
+ 6. **Thursday 8 January, 10:00. The launch post, and the release notes.** Postiz publishes Ana's announcement, bold title and two paragraphs
68
+ (sendMessage, parse_mode HTML).
69
+ At 11:00 Ana asks the agent in her private chat for the 0.9 release notes by Friday; Harness's poll receives it,
70
+ it shows the bot typing, and the agent answers that the work is card t_7c1e9a04 on its board and that this chat
71
+ will get the notes when it is done (it subscribes the chat to the card on the board; no Telegram call). **Friday 9
72
+ January, 15:00**: the card is done with its notes file, and Harness's board tells the chat: the notice "card … done
73
+ — Draft the 0.9 release notes" and its summary as the caption of the file (sendDocument, the file uploaded, the
74
+ caption in MarkdownV2 as the Chat SDK escapes it).
75
+ 7. **Monday 12 January, 10:00. A release with screenshots.** Postiz publishes the release post with three screenshots
76
+ as one album, the text on the first (sendMediaGroup, the images uploaded). At 11:00 it publishes the post's comment,
77
+ the changelog, as a reply to the album's first message (sendMessage with reply_to_message_id).
78
+ 8. **Monday 12 January, 18:00. The standups move.** The team moves its standups to a video call and Ana removes the
79
+ agent bot from "Tide Labs team" (door), but leaves the digest job on. Tuesday 13 January at 09:15 the job posts its
80
+ digest to the home chat, which says no standup items were posted this week (that morning's standup was on the
81
+ call), and Telegram refuses: "Forbidden: bot was kicked from the group chat". Ana deletes the job in Harness (no
82
+ Telegram call).
83
+ 9. **Wednesday 14 January, 16:00. A caption too long.** Ana's next post is one product photo with a 1,091-character
84
+ story. Postiz sends it (sendPhoto) and Telegram refuses it: a photo's caption holds at most 1,024 characters
85
+ ("Bad Request: message caption is too long"); Postiz shows "Telegram rejected the post". At 16:20 she cuts the story
86
+ to a paragraph and Postiz sends it again, which posts.
87
+ 10. **Monday 19 January, 11:00. A demo and a paper.** Postiz publishes the product demo video (sendVideo, uploaded) and,
88
+ at 15:00, the white paper as a PDF (sendDocument, uploaded).
89
+ 11. **Friday 27 February, 17:00. The agent retired.** Ana stops using the agent on Telegram and, in BotFather, makes a new
90
+ token for it (/token), which revokes token A. Harness, still running on her laptop, polls with it and is refused ("Unauthorized"); she stops it. The
91
+ news channel, its posts and the news bot stay with Postiz.
92
+
93
+ ## What the story reaches
94
+
95
+ getMe (both bots), deleteWebhook, getUpdates (held, a long poll; with allowed_updates sent as form fields the Bot API does not read; with offset),
96
+ privacy mode (a plain member bot sent a service message and not a plain mention; an administrator bot sent every
97
+ message), sendChatAction, sendMessage (plain, HTML, a reply; refused for a removed bot), getChatMember, deleteMessage
98
+ (twice), getChat, getFile, the file download, sendPhoto (refused for its caption, then sent), sendVideo, sendDocument
99
+ (Postiz's PDF; Harness's notes with a MarkdownV2 caption), sendMediaGroup; and a revoked token's refusal.
@@ -0,0 +1,76 @@
1
+ {
2
+ "judge": {
3
+ "agent": "Claude Code (Claude Agent SDK), model claude-opus-5-5",
4
+ "name": "Contributor 106"
5
+ },
6
+ "authors": [
7
+ "Contributor 173"
8
+ ],
9
+ "bar": "829132787d94a2d4",
10
+ "sha256": "267e4ae00d8d6bd435831be1a5b9dc741a7003bd0a43641ea7c4c000ee974e20",
11
+ "judgedAt": "2026-10-02",
12
+ "plausible": true,
13
+ "rulings": [
14
+ {
15
+ "id": "three-tuesdays-skipped",
16
+ "steps": [
17
+ "act 3",
18
+ "act 4",
19
+ "act 7",
20
+ "act 8",
21
+ "act 9",
22
+ "act 10"
23
+ ],
24
+ "barItem": "a stated cause that never happens, and a promise never kept",
25
+ "reason": "Act 3 sets the standup weekly on Tuesdays and makes the agent an administrator, which is sent every group message (https://core.telegram.org/bots/features#privacy-mode). Act 4 runs the first standup and digest on 6 January. The standups move to the call only on Monday 2 February (act 10). Tuesdays 13, 20 and 27 January fall within acts 7-9 with no standup, no poll and no digest. Act 10's digest then says that no items were posted \"this week\", as if earlier weeks had them.",
26
+ "correction": "Move act 10 to Monday 12 January, 18:00, so that the refused digest is Tuesday 13 January's. Or tell the three January Tuesdays' standups and digests."
27
+ },
28
+ {
29
+ "id": "postiz-never-given-token-n",
30
+ "steps": [
31
+ "Who acts",
32
+ "act 5"
33
+ ],
34
+ "barItem": "the wrong actor or credential for an act",
35
+ "reason": "Postiz posts to Telegram through the one bot its deployment configures, process.env.TELEGRAM_TOKEN (postiz-app e0a08a7, libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:19). In hosted Postiz, that bot is Postiz's own. The outline calls @tidelabs_news_bot, token N, \"Postiz's Telegram integration\", and in act 5 Ana makes the bot and Postiz then calls with it. The outline never says that she runs Postiz herself or that token N becomes its TELEGRAM_TOKEN.",
36
+ "correction": "In 'Who acts' and act 5, say that Ana self-hosts Postiz (docker compose, local storage) and sets its TELEGRAM_TOKEN to token N before she adds Telegram."
37
+ }
38
+ ],
39
+ "applied": {
40
+ "from": "7f1802b57b256e32061a52ede0d2972060c725a7550b068ac1ae23f131c63c6c",
41
+ "wasPlausible": false,
42
+ "steps": [
43
+ "0",
44
+ "5",
45
+ "8",
46
+ "9",
47
+ "10"
48
+ ],
49
+ "rulings": [
50
+ {
51
+ "id": "three-tuesdays-skipped",
52
+ "steps": [
53
+ "act 3",
54
+ "act 4",
55
+ "act 7",
56
+ "act 8",
57
+ "act 9",
58
+ "act 10"
59
+ ],
60
+ "barItem": "a stated cause that never happens, and a promise never kept",
61
+ "reason": "Act 3 sets the standup weekly on Tuesdays and makes the agent an administrator, which is sent every group message (https://core.telegram.org/bots/features#privacy-mode). Act 4 runs the first standup and digest on 6 January. The standups move to the call only on Monday 2 February (act 10). Tuesdays 13, 20 and 27 January fall within acts 7-9 with no standup, no poll and no digest. Act 10's digest then says that no items were posted \"this week\", as if earlier weeks had them.",
62
+ "correction": "Move act 10 to Monday 12 January, 18:00, so that the refused digest is Tuesday 13 January's. Or tell the three January Tuesdays' standups and digests."
63
+ },
64
+ {
65
+ "id": "postiz-never-given-token-n",
66
+ "steps": [
67
+ "Who acts",
68
+ "act 5"
69
+ ],
70
+ "barItem": "the wrong actor or credential for an act",
71
+ "reason": "Postiz posts to Telegram through the one bot its deployment configures, process.env.TELEGRAM_TOKEN (postiz-app e0a08a7, libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:19). In hosted Postiz, that bot is Postiz's own. The outline calls @tidelabs_news_bot, token N, \"Postiz's Telegram integration\", and in act 5 Ana makes the bot and Postiz then calls with it. The outline never says that she runs Postiz herself or that token N becomes its TELEGRAM_TOKEN.",
72
+ "correction": "In 'Who acts' and act 5, say that Ana self-hosts Postiz (docker compose, local storage) and sets its TELEGRAM_TOKEN to token N before she adds Telegram."
73
+ }
74
+ ]
75
+ }
76
+ }
@@ -0,0 +1,134 @@
1
+ {
2
+ "judge": {
3
+ "agent": "Claude Code (Claude Agent SDK), model claude-opus-5-5",
4
+ "name": "Contributor 106"
5
+ },
6
+ "authors": [
7
+ "Contributor 173"
8
+ ],
9
+ "bar": "829132787d94a2d4",
10
+ "sha256": "f58ee4db9e9c90c0f87f6018cf370f00963898ae16e0d4e7069e6afca17a6cf3",
11
+ "judgedAt": "2026-10-02",
12
+ "plausible": true,
13
+ "rulings": [
14
+ {
15
+ "id": "three-tuesdays-skipped",
16
+ "steps": [
17
+ 16,
18
+ 20,
19
+ 22,
20
+ 30,
21
+ 58,
22
+ 62,
23
+ 66,
24
+ 70,
25
+ 73
26
+ ],
27
+ "barItem": "a stated cause that never happens, and a promise never kept",
28
+ "reason": "Steps 16, 20 and 22 set the standup for 9:00 every Tuesday, with items posted in the group, and the agent promises the digest at 9:15. From step 19 the agent is an administrator, and an administrator is sent every group message (https://core.telegram.org/bots/features#privacy-mode, \"bot admins always receive all messages\"). The job runs once, Tuesday 6 January (step 30). The team keeps the group standup until Monday 2 February (step 70: \"the standups move to a video call\"), yet Tuesdays 13, 20 and 27 January pass inside steps 58-70 with no items posted, no poll and no digest. Step 73's digest then says \"No standup items were posted this week\", which implies the earlier weeks had items. Three of the four weekly runs the story promises never happen, and nothing in the story explains why.",
29
+ "correction": "Move the standups' move and the bot's removal to Monday 12 January, 18:00, after step 61. The job's next run, Tuesday 13 January at 09:15, is then the refused one, and the waits to Wednesday 14 January 16:00 and onward are adjusted. Alternatively, show the Tuesday standups of 13, 20 and 27 January: items posted, a poll, and a digest each week."
30
+ },
31
+ {
32
+ "id": "postiz-never-given-token-n",
33
+ "steps": [
34
+ 33,
35
+ 37,
36
+ 40,
37
+ 41,
38
+ 42,
39
+ 43,
40
+ 45,
41
+ 46,
42
+ 47,
43
+ 48,
44
+ 50,
45
+ 59,
46
+ 61,
47
+ 63,
48
+ 65,
49
+ 67,
50
+ 69
51
+ ],
52
+ "barItem": "the wrong actor or credential for an act",
53
+ "reason": "Postiz's Telegram provider calls with one bot, the one its deployment configures: `new TelegramBot(process.env.TELEGRAM_TOKEN!)` (libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:19, postiz-app e0a08a7; demand.json, Postiz's client: \"TELEGRAM_TOKEN\"). In hosted Postiz, that is Postiz's own bot and not one a customer makes. Step 33 has Ana make @tidelabs_news_bot \"for Postiz\", and from step 37 on Postiz calls with {news_token}. But no step or name says that Ana runs Postiz herself, or that token N is set as its TELEGRAM_TOKEN. The life never shows how the credential Postiz uses reaches it, and as written it reads as the hosted product calling with a bot that belongs to the customer.",
54
+ "correction": "In step 33 or a step beside it, say that Ana runs Postiz herself (docker compose, local storage, as demand.json reads it) and sets TELEGRAM_TOKEN to the news bot's token before she adds Telegram. Use the same wording in the outline."
55
+ },
56
+ {
57
+ "id": "running-harness-polls-without-offset",
58
+ "steps": [
59
+ 76
60
+ ],
61
+ "barItem": "content that does not bear out the step's name",
62
+ "reason": "Step 76 is Harness \"still running on her laptop\". The adapter it runs keeps its offset in its polling loop and sends it on every poll: `offset = update.update_id + 1` (@chat-adapter/telegram 4.39.0, dist/index.js:3327-3345, pollingLoop; demand.json: \"confirming the last with offset\"). The life last confirmed one at step 53. Step 76's body, {\"limit\": 100, \"timeout\": 30}, is the request a freshly started adapter sends (step 9), not the request of the process the step names.",
63
+ "correction": "Step 76's body: {\"offset\": \"{agent_next}\", \"limit\": 100, \"timeout\": 30}."
64
+ }
65
+ ],
66
+ "applied": {
67
+ "from": "1248e944117c27955453a626f8ed5a44ff07ad520f525feaa0a7a6dc53399963",
68
+ "wasPlausible": false,
69
+ "steps": [
70
+ "33",
71
+ "36",
72
+ "62",
73
+ "70",
74
+ "71",
75
+ "72",
76
+ "73",
77
+ "74",
78
+ "76"
79
+ ],
80
+ "rulings": [
81
+ {
82
+ "id": "three-tuesdays-skipped",
83
+ "steps": [
84
+ 16,
85
+ 20,
86
+ 22,
87
+ 30,
88
+ 58,
89
+ 62,
90
+ 66,
91
+ 70,
92
+ 73
93
+ ],
94
+ "barItem": "a stated cause that never happens, and a promise never kept",
95
+ "reason": "Steps 16, 20 and 22 set the standup for 9:00 every Tuesday, with items posted in the group, and the agent promises the digest at 9:15. From step 19 the agent is an administrator, and an administrator is sent every group message (https://core.telegram.org/bots/features#privacy-mode, \"bot admins always receive all messages\"). The job runs once, Tuesday 6 January (step 30). The team keeps the group standup until Monday 2 February (step 70: \"the standups move to a video call\"), yet Tuesdays 13, 20 and 27 January pass inside steps 58-70 with no items posted, no poll and no digest. Step 73's digest then says \"No standup items were posted this week\", which implies the earlier weeks had items. Three of the four weekly runs the story promises never happen, and nothing in the story explains why.",
96
+ "correction": "Move the standups' move and the bot's removal to Monday 12 January, 18:00, after step 61. The job's next run, Tuesday 13 January at 09:15, is then the refused one, and the waits to Wednesday 14 January 16:00 and onward are adjusted. Alternatively, show the Tuesday standups of 13, 20 and 27 January: items posted, a poll, and a digest each week."
97
+ },
98
+ {
99
+ "id": "postiz-never-given-token-n",
100
+ "steps": [
101
+ 33,
102
+ 37,
103
+ 40,
104
+ 41,
105
+ 42,
106
+ 43,
107
+ 45,
108
+ 46,
109
+ 47,
110
+ 48,
111
+ 50,
112
+ 59,
113
+ 61,
114
+ 63,
115
+ 65,
116
+ 67,
117
+ 69
118
+ ],
119
+ "barItem": "the wrong actor or credential for an act",
120
+ "reason": "Postiz's Telegram provider calls with one bot, the one its deployment configures: `new TelegramBot(process.env.TELEGRAM_TOKEN!)` (libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:19, postiz-app e0a08a7; demand.json, Postiz's client: \"TELEGRAM_TOKEN\"). In hosted Postiz, that is Postiz's own bot and not one a customer makes. Step 33 has Ana make @tidelabs_news_bot \"for Postiz\", and from step 37 on Postiz calls with {news_token}. But no step or name says that Ana runs Postiz herself, or that token N is set as its TELEGRAM_TOKEN. The life never shows how the credential Postiz uses reaches it, and as written it reads as the hosted product calling with a bot that belongs to the customer.",
121
+ "correction": "In step 33 or a step beside it, say that Ana runs Postiz herself (docker compose, local storage, as demand.json reads it) and sets TELEGRAM_TOKEN to the news bot's token before she adds Telegram. Use the same wording in the outline."
122
+ },
123
+ {
124
+ "id": "running-harness-polls-without-offset",
125
+ "steps": [
126
+ 76
127
+ ],
128
+ "barItem": "content that does not bear out the step's name",
129
+ "reason": "Step 76 is Harness \"still running on her laptop\". The adapter it runs keeps its offset in its polling loop and sends it on every poll: `offset = update.update_id + 1` (@chat-adapter/telegram 4.39.0, dist/index.js:3327-3345, pollingLoop; demand.json: \"confirming the last with offset\"). The life last confirmed one at step 53. Step 76's body, {\"limit\": 100, \"timeout\": 30}, is the request a freshly started adapter sends (step 9), not the request of the process the step names.",
130
+ "correction": "Step 76's body: {\"offset\": \"{agent_next}\", \"limit\": 100, \"timeout\": 30}."
131
+ }
132
+ ]
133
+ }
134
+ }
@@ -0,0 +1,95 @@
1
+ {
2
+ "about": "telegram's decision table (the P3 procedure: demand, then this table, then the code): each method the pack serves, the demand it answers (journeys/demand.json, or the manifest's refresh scope) and the mechanism that serves it. The Bot API is an RPC wire (POST /bot<token>/<method>, every answer {ok, result}), so no method is a create, read, update or delete the derived core could serve: each is a handler over the bot the token names (./src/semantics/around.ts resolves it, before routing, as the Bot API server does). Every other method of the page answers the server's unknown method (the manifest's gap), the URL-media sends only an opt-in Postiz storage makes (demand.json's optional) included: a URL the World cannot reach is refused as Telegram refuses one it cannot download. People, bots and chats are made through the World's doors (the Telegram app and BotFather, which no Bot API method stands for).",
3
+ "decisions": [
4
+ {
5
+ "operation": "getMe",
6
+ "demand": "Volter Harness's adapter reads its bot as it starts; Postiz reads its bot's id when the /connect message is found",
7
+ "mechanism": "handler",
8
+ "why": "a computed read: the bot the token names, its User with the bot-only fields (can_join_groups, can_read_all_group_messages for privacy mode, supports_inline_queries, …)"
9
+ },
10
+ {
11
+ "operation": "deleteWebhook",
12
+ "demand": "Volter Harness's adapter removes any webhook before it polls (drop_pending_updates false)",
13
+ "mechanism": "handler",
14
+ "why": "an action on the bot: its webhook URL cleared (none was set: 'Webhook is already deleted'), and its waiting updates dropped when asked"
15
+ },
16
+ {
17
+ "operation": "getUpdates",
18
+ "demand": "Volter Harness's adapter polls in a loop (timeout 30, limit 100); Postiz's connect screen polls with allowed_updates [message, channel_post] and an offset",
19
+ "mechanism": "handler",
20
+ "why": "an action: the bot's queue read from the offset (confirming, and so deleting, every update below it), filtered by the allowed_updates in force when each was made, at most limit; with nothing waiting and a timeout, the read held until an update is written (the kernel's held read)"
21
+ },
22
+ {
23
+ "operation": "sendChatAction",
24
+ "demand": "Volter Harness's adapter shows its bot typing when a person writes in private",
25
+ "mechanism": "handler",
26
+ "why": "an action that stores nothing: the chat's access checked as the server checks it, True"
27
+ },
28
+ {
29
+ "operation": "sendMessage",
30
+ "demand": "Volter Harness posts its answers and its scheduled reports; Postiz posts a text (HTML), its connect notices and a comment as a reply",
31
+ "mechanism": "handler",
32
+ "why": "a message made in a chat the bot may write in: its message_id counted per chat, its text parsed for the parse_mode's entities (HTML, MarkdownV2 or Markdown), checked for length (1-4096 after parsing), a reply resolved; it writes no update for its own bot (bots do not receive their own messages)",
33
+ "sourceRules": [
34
+ {
35
+ "source": "https://raw.githubusercontent.com/tdlib/telegram-bot-api/e3e9dd8e5b3d7ab8537cd5a10dc31d5ffa8f82d1/telegram-bot-api/Client.cpp",
36
+ "means": "Unsupported parse_mode"
37
+ }
38
+ ]
39
+ },
40
+ {
41
+ "operation": "sendPhoto",
42
+ "demand": "Postiz posts one image with its caption (HTML), uploaded",
43
+ "mechanism": "handler",
44
+ "why": "a message with a photo: the upload's bytes stored as a file (its width and height read from the image), the caption parsed and checked (0-1024); a URL the World cannot reach is refused as the server refuses one it cannot download"
45
+ },
46
+ {
47
+ "operation": "sendVideo",
48
+ "demand": "Postiz posts one video with its caption, uploaded",
49
+ "mechanism": "handler",
50
+ "why": "a message with a video: the bytes stored, its width, height and duration read from the file's own header where it is an MP4"
51
+ },
52
+ {
53
+ "operation": "sendDocument",
54
+ "demand": "Postiz posts one file that is neither an image nor a video, uploaded; Volter Harness's board sends a done card's artifact with its notice as a MarkdownV2 caption",
55
+ "mechanism": "handler",
56
+ "why": "a message with a document: the bytes stored with its name and media type; its caption parsed as sendMessage's text is (HTML, MarkdownV2 or Markdown) and checked (0-1024)"
57
+ },
58
+ {
59
+ "operation": "sendMediaGroup",
60
+ "demand": "Postiz posts several images or videos as one album, the caption on the first, uploaded (attach://<name>); Volter Harness's board sends a done card's several artifacts as one album of documents",
61
+ "mechanism": "handler",
62
+ "why": "2-10 messages sharing one media_group_id, each its own file; the array of sent messages answered"
63
+ },
64
+ {
65
+ "operation": "deleteMessage",
66
+ "demand": "Postiz deletes the /connect message and its own success notice",
67
+ "mechanism": "handler",
68
+ "why": "a message deleted where the bot may delete it: its own, or another's in a channel or group where it is an administrator with can_delete_messages; 'message to delete not found' for one that is not there"
69
+ },
70
+ {
71
+ "operation": "getChat",
72
+ "demand": "Postiz reads the channel it is connecting (id, title, username, photo); the vendor-backed refresh reads each chat again (the manifest's refresh of chat)",
73
+ "mechanism": "handler",
74
+ "why": "a computed read: the chat as ChatFullInfo, its access checked as the server checks a read"
75
+ },
76
+ {
77
+ "operation": "getChatMember",
78
+ "demand": "Postiz checks its bot is an administrator that can delete messages in the channel",
79
+ "mechanism": "handler",
80
+ "why": "a computed read: the member's status and rights with its user"
81
+ },
82
+ {
83
+ "operation": "getFile",
84
+ "demand": "Postiz reads the chat photo's file to make its link",
85
+ "mechanism": "handler",
86
+ "why": "an action: the file's download path issued (file_path), valid for the bot that asked"
87
+ },
88
+ {
89
+ "operation": "downloadFile",
90
+ "demand": "Postiz copies the chat photo into its storage from the file's link",
91
+ "mechanism": "handler",
92
+ "why": "the stored bytes of the file a getFile path names, for the bot whose token is in the path"
93
+ }
94
+ ]
95
+ }
@@ -0,0 +1,152 @@
1
+ {
2
+ "measured": "Application calls and optional settings are recorded below at the cited commits.",
3
+ "applications": [
4
+ {
5
+ "name": "volter-ai/supercode (Volter Harness)",
6
+ "commit": "62620578c014",
7
+ "client": "the Chat SDK's Telegram adapter (@chat-adapter/telegram 4.39.0 under chat 4.39.0, fetch POST https://api.telegram.org/bot<token>/<method> with a JSON body, multipart form data for a file; rows marked @chat-adapter/telegram@4.39.0: are its published dist), botToken TELEGRAM_BOT_TOKEN from the Hermes profile's .env, mode polling; configuration read at sdk/orchestrator/adapters/chat-sdk.mjs:10,80,209",
8
+ "backend": [
9
+ {
10
+ "call": "the sidecar starts the Chat SDK: the adapter reads the bot's own user (its id and username, which a mention names)",
11
+ "operation": "getMe",
12
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:331",
13
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:1088"
14
+ },
15
+ {
16
+ "call": "...and, polling, removes any webhook first (pending updates kept)",
17
+ "operation": "deleteWebhook",
18
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:80",
19
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:1293"
20
+ },
21
+ {
22
+ "call": "...then asks for updates in a loop, each request held up to 30 seconds, confirming the last with offset",
23
+ "operation": "getUpdates",
24
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:80",
25
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:3327"
26
+ },
27
+ {
28
+ "call": "a person writes to the bot in private: the adapter shows it typing",
29
+ "operation": "sendChatAction",
30
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:323",
31
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:1945"
32
+ },
33
+ {
34
+ "call": "the agent's answer posted in the chat it came from, in parts of 4096 characters at most",
35
+ "operation": "sendMessage",
36
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:449",
37
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:3246"
38
+ },
39
+ {
40
+ "call": "a scheduled job's report to the profile's home chat (TELEGRAM_HOME_CHANNEL)",
41
+ "operation": "sendMessage",
42
+ "evidence": "sdk/orchestrator/adapters/chat-sdk.mjs:453",
43
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:1790"
44
+ },
45
+ {
46
+ "call": "a card the chat is subscribed to is done: the board's notice with the card's artifact, the notice the file's caption in MarkdownV2",
47
+ "operation": "sendDocument",
48
+ "evidence": "sdk/orchestrator/board/dispatch.mjs:1663",
49
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:2645"
50
+ },
51
+ {
52
+ "call": "...a card done with several artifacts: the files as one album of documents, the notice the first's caption in MarkdownV2",
53
+ "operation": "sendMediaGroup",
54
+ "evidence": "sdk/orchestrator/board/dispatch.mjs:1663",
55
+ "client": "@chat-adapter/telegram@4.39.0:dist/index.js:2802"
56
+ }
57
+ ]
58
+ },
59
+ {
60
+ "commit": "e0a08a7d9592fdc1ef1e82cb7cb781489fceade3",
61
+ "client": "node-telegram-bot-api 0.66.0 (POST https://api.telegram.org/bot<token>/<method>, form-urlencoded, multipart for a file; rows marked node-telegram-bot-api@0.66.0: are its published src), TELEGRAM_TOKEN",
62
+ "backend": [
63
+ {
64
+ "call": "the connect screen polls for the person's /connect message (allowed_updates message and channel_post, from the last offset)",
65
+ "operation": "getUpdates",
66
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:87",
67
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:849"
68
+ },
69
+ {
70
+ "call": "found: the bot's own id",
71
+ "operation": "getMe",
72
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:105",
73
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:931"
74
+ },
75
+ {
76
+ "call": "...whether it is an administrator that can delete messages there",
77
+ "operation": "getChatMember",
78
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:356",
79
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1966"
80
+ },
81
+ {
82
+ "call": "an administrator: the /connect message deleted, a success notice posted and deleted ten seconds later",
83
+ "operation": "deleteMessage",
84
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:120",
85
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:3093"
86
+ },
87
+ {
88
+ "call": "the success notice (or, without the right to delete, the notice asking the person to remove the messages)",
89
+ "operation": "sendMessage",
90
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:122",
91
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
92
+ },
93
+ {
94
+ "call": "the channel added: its chat read (id, title, username, photo)",
95
+ "operation": "getChat",
96
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:62",
97
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1924"
98
+ },
99
+ {
100
+ "call": "...and its photo's file, whose path makes the channel's picture link",
101
+ "operation": "getFile",
102
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:71",
103
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1522"
104
+ },
105
+ {
106
+ "call": "...and Postiz copies the picture into its own storage from that link",
107
+ "operation": "downloadFile",
108
+ "evidence": "libraries/nestjs-libraries/src/database/prisma/integrations/integration.service.ts:119",
109
+ "client": "libraries/nestjs-libraries/src/upload/local.storage.ts:62"
110
+ },
111
+ {
112
+ "call": "a post with no media: its text, parse_mode HTML",
113
+ "operation": "sendMessage",
114
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:224",
115
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
116
+ },
117
+ {
118
+ "call": "a post with one image: the photo uploaded with the text as its caption",
119
+ "operation": "sendPhoto",
120
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:247",
121
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1075"
122
+ },
123
+ {
124
+ "call": "a post with one video",
125
+ "operation": "sendVideo",
126
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:240",
127
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1164"
128
+ },
129
+ {
130
+ "call": "a post with one file that is neither",
131
+ "operation": "sendDocument",
132
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:253",
133
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1136"
134
+ },
135
+ {
136
+ "call": "a post with several media: an album of up to ten, the text on the first",
137
+ "operation": "sendMediaGroup",
138
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:272",
139
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:1296"
140
+ },
141
+ {
142
+ "call": "a comment: the next post sent as a reply to the last (reply_to_message_id)",
143
+ "operation": "sendMessage",
144
+ "evidence": "libraries/nestjs-libraries/src/integrations/social/telegram.provider.ts:226",
145
+ "client": "node-telegram-bot-api@0.66.0:src/telegram.js:970"
146
+ }
147
+ ],
148
+ "name": "gitroomhq/postiz-app"
149
+ }
150
+ ],
151
+ "optional": []
152
+ }