instar 1.3.994 → 1.3.996
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/channelRegistry.d.ts +17 -0
- package/dist/core/channelRegistry.d.ts.map +1 -1
- package/dist/core/channelRegistry.js +7 -1
- package/dist/core/channelRegistry.js.map +1 -1
- package/dist/core/instarChannels.d.ts.map +1 -1
- package/dist/core/instarChannels.js +4 -0
- package/dist/core/instarChannels.js.map +1 -1
- package/dist/core/userChannels.d.ts +73 -0
- package/dist/core/userChannels.d.ts.map +1 -0
- package/dist/core/userChannels.js +138 -0
- package/dist/core/userChannels.js.map +1 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +58 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.995.md +75 -0
- package/upgrades/1.3.996.md +38 -0
- package/upgrades/side-effects/patch-actions-strict.md +131 -0
- package/upgrades/side-effects/user-channel-liveness.md +116 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# Side-effects review — user-channel liveness in the channel registry
|
|
2
|
+
|
|
3
|
+
**Change:** the two direct USER channels (Telegram, Slack) join the peer channel registry, with
|
|
4
|
+
liveness probes that read live adapter state instead of configuration. `ChannelDefinition` and
|
|
5
|
+
`ChannelReport` gain an `audience: 'peer' | 'user'` field.
|
|
6
|
+
|
|
7
|
+
**Decision point touched:** none that blocks. This is a read surface — it informs a routing choice a
|
|
8
|
+
caller makes, and holds no authority over it. `advisory: true` is already on the response.
|
|
9
|
+
|
|
10
|
+
## 1. Over-block — what legitimate inputs does this reject that it shouldn't?
|
|
11
|
+
|
|
12
|
+
Nothing is rejected; nothing is gated. The nearest analogue is reporting a *usable* channel as
|
|
13
|
+
unusable, which would cause a caller to avoid a working path.
|
|
14
|
+
|
|
15
|
+
One case deliberately risks that and should be named: a Telegram that is **not polling with no
|
|
16
|
+
recorded reason** reports `unknown`, not `working`. If the adapter was stopped deliberately and could
|
|
17
|
+
still send, this understates it. That direction is chosen on purpose — over-reporting liveness is the
|
|
18
|
+
defect being fixed, and `unknown` carries its reason rather than pretending to a verdict.
|
|
19
|
+
|
|
20
|
+
Transient errors do NOT downgrade a live channel: still-polling with a non-zero consecutive error
|
|
21
|
+
count stays `working`, with the count named in the detail. Marking that broken would be the opposite
|
|
22
|
+
over-block.
|
|
23
|
+
|
|
24
|
+
## 2. Under-block — what does it still miss?
|
|
25
|
+
|
|
26
|
+
- **A live reading is not a promise.** `working` means the loop was polling when asked. It can die a
|
|
27
|
+
second later. No probe can fix that; the response carries `generatedAt`.
|
|
28
|
+
- **No round-trip is attempted.** Telegram `working` means the poll loop is up, not that a message to
|
|
29
|
+
a specific topic would land (a topic could be deleted, a user could have blocked the bot). The
|
|
30
|
+
peer registry's `mutual-ssh` entry already makes the same distinction in its own detail text, and
|
|
31
|
+
this follows that precedent rather than overstating.
|
|
32
|
+
- **Slack is one workspace.** `isConnected()` is per-adapter; a multi-workspace setup is not modelled.
|
|
33
|
+
- **Other user surfaces are absent** — WhatsApp and iMessage adapters exist in the tree. They are not
|
|
34
|
+
in this change, and the registry will therefore not claim anything about them. Under the registry's
|
|
35
|
+
own "absence is impossible" property this is a real limit: a channel with no row cannot report that
|
|
36
|
+
it is missing. Called out rather than quietly scoped away.
|
|
37
|
+
|
|
38
|
+
## 3. Level-of-abstraction fit
|
|
39
|
+
|
|
40
|
+
The user definitions live in a NEW file (`src/core/userChannels.ts`) rather than being added to
|
|
41
|
+
`src/core/instarChannels.ts`, whose header declares an explicit "PEER-TO-PEER only" scope discipline
|
|
42
|
+
and justifies two prior exclusions. Widening that file would have silently discarded a deliberate
|
|
43
|
+
decision by its author.
|
|
44
|
+
|
|
45
|
+
The two lists are composed at the route and resolved by the same `resolveChannels`, so the registry's
|
|
46
|
+
invariants (one row per definition, bounded probes, `unknown` on failure) apply identically to both
|
|
47
|
+
without being reimplemented.
|
|
48
|
+
|
|
49
|
+
`audience` is data on the channel, not two registries, because the peer-vs-user choice is itself a
|
|
50
|
+
routing decision a caller must be able to weigh. Two surfaces would require the caller to already
|
|
51
|
+
know which to consult — the arbitrariness the registry exists to remove.
|
|
52
|
+
|
|
53
|
+
## 4. Signal vs authority
|
|
54
|
+
|
|
55
|
+
Pure signal. The registry reports; it never routes, blocks, or sends. The response is already flagged
|
|
56
|
+
`advisory: true`. The mapping functions (`telegramStateFrom`, `slackStateFrom`) are exported and pure
|
|
57
|
+
precisely so the verdict logic can be pinned by tests without constructing an adapter — the mapping
|
|
58
|
+
is where a wrong verdict would originate.
|
|
59
|
+
|
|
60
|
+
## 5. Interactions
|
|
61
|
+
|
|
62
|
+
- **`/capabilities`** — unchanged. It keeps reporting `telegram: { configured: true }`, which remains
|
|
63
|
+
correct for what it measures (configuration). This adds the missing state reading; it does not
|
|
64
|
+
correct or replace the config reading, and the two answer different questions.
|
|
65
|
+
- **Peer channels** — behaviour unchanged; they gain an `audience: 'peer'` tag. Because `audience` is
|
|
66
|
+
required on `ChannelDefinition`, a future channel cannot be added untagged: it is a compile error,
|
|
67
|
+
not a silent default.
|
|
68
|
+
- **No double-fire / no races** — read-only, no writes, no timers of its own. Probes are bounded by
|
|
69
|
+
the registry's existing 3s timeout.
|
|
70
|
+
|
|
71
|
+
## 6. External surfaces
|
|
72
|
+
|
|
73
|
+
`GET /channels` gains two rows and every row gains an `audience` field. Additive: existing consumers
|
|
74
|
+
reading `id`/`state`/`detail` are unaffected. No new route, no config key, no user-visible string.
|
|
75
|
+
|
|
76
|
+
## 7. Multi-machine posture
|
|
77
|
+
|
|
78
|
+
**Machine-local BY DESIGN**, `machine-local-justification: physical-credential-locality` — a Telegram
|
|
79
|
+
bot token and its long-poll loop, and a Slack Socket Mode connection, live in one process on one
|
|
80
|
+
machine. "Is my Telegram polling?" is only meaningful about the machine asked; replicating another
|
|
81
|
+
machine's answer would assert liveness this process cannot observe. This matches the peer registry,
|
|
82
|
+
which is machine-local for the same reason (its relay/SSH probes read local runtime state).
|
|
83
|
+
|
|
84
|
+
## 8. Rollback cost
|
|
85
|
+
|
|
86
|
+
Low. One new file, one required field on two interfaces, one route composing two lists. No data
|
|
87
|
+
migration, no config, no persisted state. Reverting restores the prior behaviour exactly — including
|
|
88
|
+
the gap.
|
|
89
|
+
|
|
90
|
+
## Refusals demonstrated (command + output)
|
|
91
|
+
|
|
92
|
+
Falsification 1 — make the Telegram probe read EXISTENCE instead of liveness (`if (status !== null)`
|
|
93
|
+
in place of `if (status.started)`), i.e. exactly what `/capabilities` does:
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
× THE FIX: a configured-but-DEAD Telegram is never reported as working
|
|
97
|
+
× a missing bot token is a credential verdict, not a network one
|
|
98
|
+
× a network death is broken — distinct from a credential problem
|
|
99
|
+
× stopped for NO recorded reason is unknown — never working, never a confident broken
|
|
100
|
+
Tests 4 failed | 12 passed (16)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Falsification 2 — give Slack "ever connected" semantics (`if (enabled)` in place of
|
|
104
|
+
`if (connected)`), the exact trap its own source warns about:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
× THE TRAP: enabled with the socket DOWN is broken, not working
|
|
108
|
+
Tests 1 failed | 15 passed (16)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Restored: `Tests 35 passed (35)` across `channel-registry`, `channel-registry-claims` and
|
|
112
|
+
`user-channel-liveness`; `npx tsc --noEmit` exit 0.
|
|
113
|
+
|
|
114
|
+
Two source ratchets are included so the probes cannot drift back: one asserts `userChannels.ts` never
|
|
115
|
+
reads a `.configured` property or a `config.*` value, the other asserts the route wiring uses
|
|
116
|
+
`isConnected()` and never `slackAdapter.started`.
|