instar 1.3.382 → 1.3.384

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.
@@ -0,0 +1,32 @@
1
+ # ELI16 — Duplicate-Message Suppression
2
+
3
+ ## The problem
4
+
5
+ You flagged it and the logs proved it: the same status message went out to you
6
+ THREE times — two of them byte-for-byte identical, 13.5 minutes apart. That's me
7
+ sending the same thing twice, which is exactly as annoying as it sounds.
8
+
9
+ Why did the existing safeguards miss it? There was a guard that catches "the
10
+ exact same send retried" — but these were two SEPARATE sends with the same
11
+ words, 13 minutes apart, so it didn't count them as the same. And the smarter
12
+ content-check was being skipped on the very paths these went through (status
13
+ relays, cross-machine).
14
+
15
+ ## What this fixes
16
+
17
+ I added a dead-simple, reliable check right where messages go out: if I'm about
18
+ to send you the SAME message text in the SAME topic that I already sent in the
19
+ last ~15 minutes, the repeat is dropped — you just don't get pinged twice. The
20
+ first one still goes through normally.
21
+
22
+ It's deliberately careful so it never over-corrects:
23
+ - **Short acks are exempt.** If you send me two things and I reply "Got it" to
24
+ both, you still see both — only longer, substantial messages get deduped.
25
+ - **Per-conversation.** The same text in a different topic still sends.
26
+ - **I can force a repeat** when I genuinely mean to (rare) with an explicit flag.
27
+ - **A failed send isn't remembered**, so a real retry of something that didn't
28
+ go through is never swallowed.
29
+
30
+ Tested both ways: identical status 13 min apart → second one dropped; two
31
+ different messages → both sent; two short acks → both sent; cross-topic → sent.
32
+ Run through the real send path, not just in isolation.
@@ -0,0 +1,79 @@
1
+ # Side-Effects Review — Outbound Content-Dedup
2
+
3
+ **Version / slug:** `outbound-content-dedup`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `Echo (instar-dev agent — Justin: "We really need to work on not sending duplicate messages. Let's make this much more robust.")`
6
+ **Second-pass reviewer:** `self-adversarial pass over the one real risk — suppressing a message the user actually needed`
7
+
8
+ ## Summary of the change
9
+
10
+ A pure `OutboundContentDedup` (per-topic, windowed, length-gated content
11
+ fingerprint) wired at the `/telegram/reply` route, after the delivery-id LRU and
12
+ before the tone gate. An identical long message to the same topic within ~15min
13
+ is suppressed (200, not re-sent); the first send still goes. Files:
14
+ `OutboundContentDedup.ts` (new), `routes.ts` (instance + check/record),
15
+ `PostUpdateMigrator.ts` (CLAUDE.md note), 3 test files.
16
+
17
+ ## Decision-point inventory
18
+
19
+ - content-dedup check — **add (suppress)** — before the send.
20
+ - length floor (minLength 40) — **add** — brief acks always pass.
21
+ - `allowDuplicate` bypass — **reuse (existing metadata)** — caller can force a repeat.
22
+ - record-after-success — **deliberate** — a failed send's retry isn't lost.
23
+
24
+ ## 1. Over-block (the real risk — suppressing a wanted message)
25
+
26
+ The danger is dropping a message the user needed. Defenses, each tested:
27
+ - **Length floor:** brief acks (the most common legitimate repeat — two "Got it"
28
+ for two user messages) are exempt (test: "brief acks never suppressed").
29
+ - **Per-topic:** the same text to a different topic sends (tested).
30
+ - **`allowDuplicate` escape hatch:** a caller that means to repeat bypasses it
31
+ (tested).
32
+ - **Record-after-success:** a send that throws is never recorded, so its
33
+ legitimate retry (same content, new id) is not suppressed.
34
+ - **Scope = `/telegram/reply` only:** command responses (bot command handler) and
35
+ sentinel/standby sends go through other paths and are untouched, so a user
36
+ re-running a command still gets fresh output.
37
+ Residual: a caller that legitimately re-sends the EXACT same ≥40-char text to the
38
+ SAME topic within 15min without `allowDuplicate` is suppressed — which is exactly
39
+ the reported bug, and the escape hatch covers the rare intentional case.
40
+
41
+ ## 2. Under-block
42
+
43
+ Near-duplicates (reworded status, e.g. the 21:15 variant in the incident) are
44
+ NOT caught — only byte-identical (whitespace-normalized) text. Catching
45
+ semantic near-dups would require an LLM and risks false suppression; the
46
+ deterministic exact-match is the safe, robust core. The reworded-variant case is
47
+ a separate, lower-frequency concern.
48
+
49
+ ## 3. Level-of-abstraction fit
50
+
51
+ The dedup is a pure module in `messaging/`, instantiated once per route
52
+ construction beside the existing delivery-id LRU (same lifecycle, same
53
+ chokepoint). It reuses the route's existing `allowDuplicate` metadata contract.
54
+
55
+ ## 4. Signal vs authority compliance
56
+
57
+ **Required reference:** `docs/signal-vs-authority.md`
58
+
59
+ - [x] Deterministic guard, no LLM. It removes a redundant send; it never alters
60
+ content and never blocks a non-duplicate. The `allowDuplicate` hatch preserves
61
+ caller authority for intentional repeats. Strictly subtractive on exact dups.
62
+
63
+ ## 5. Interactions
64
+
65
+ - **Delivery-id LRU:** complementary — id-dedup catches a re-POST of the same id;
66
+ content-dedup catches a fresh-id re-send of the same text. Runs after it.
67
+ - **Tone gate:** the content-dedup runs BEFORE it (so a duplicate skips the LLM
68
+ call entirely) and independent of it (covers the proxy/relay paths the gate
69
+ skips).
70
+ - **Tokenless-standby relay:** unaffected — the dedup decides before the send;
71
+ the relay still carries the real messageId on a non-duplicate.
72
+
73
+ ## 6. External surfaces / 7. Rollback
74
+
75
+ New response field `suppressedDuplicate: true` on a suppressed `/telegram/reply`
76
+ (additive; callers `.catch`/ignore the body today). Optional config
77
+ `outboundContentDedup` (window/minLength/maxPerTopic/enabled) with safe
78
+ defaults; absent ⇒ defaults. One idempotent CLAUDE.md note. Rollback = revert;
79
+ duplicates flow again.
@@ -0,0 +1,62 @@
1
+ # Side-Effects Review — Quota collector polling independence (finding A1)
2
+
3
+ **Version / slug:** `quota-collector-polling-independence`
4
+ **Date:** `2026-06-06`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required`
7
+
8
+ ## Summary of the change
9
+
10
+ The QuotaManager/collector/accountSwitcher pipeline was hoisted out of the
11
+ `if (telegramConfig && !skipTelegram && !isStandbyTelegram && !lifelineOwnsPolling)`
12
+ block to top scope (after the scheduler exists), gated only on quotaTracker.
13
+ On lifeline-owns-polling agents the collector never started → fail-open
14
+ placement (finding A1).
15
+
16
+ ## Decision-point inventory
17
+
18
+ One: WHERE the pipeline runs. Now: top scope, after scheduler, gated on
19
+ quotaTracker. Before: inside the server-owns-polling block.
20
+
21
+ ## 1. Over-block
22
+
23
+ None. The pipeline now runs in MORE cases (send-only servers too), never fewer.
24
+
25
+ ## 2. Under-block
26
+
27
+ The collector still no-ops for non-claude-code frameworks (unchanged) and when
28
+ quotaTracking is off (unchanged). A missing OAuth credential still degrades to
29
+ JSONL estimation inside QuotaCollector (unchanged).
30
+
31
+ ## 3. Level-of-abstraction fit
32
+
33
+ Quota collection is independent of Telegram-polling ownership; placing it with
34
+ the other top-scope monitoring setup (after scheduler, beside telemetry
35
+ heartbeat) is the correct layer. accountSwitcher moves with it; its later
36
+ consumer (wireTelegramCallbacks, inside the polling block) resolves it from the
37
+ enclosing scope — verified by tsc.
38
+
39
+ ## 4. Signal vs authority compliance
40
+
41
+ **Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
42
+
43
+ No authority change. quota-aware placement remains fail-open on absent data;
44
+ this fix simply lets the data exist.
45
+
46
+ ## 5. Interactions
47
+
48
+ - Scheduler: the pipeline now runs after `scheduler` is constructed, so
49
+ setScheduler() wires a real scheduler (was already conditional on `scheduler`).
50
+ - wireTelegramCallbacks (polling block): consumes the hoisted accountSwitcher
51
+ from the enclosing scope — only runs in the polling path, harmless otherwise.
52
+ - notify(): defined at outer scope (function decl), called lazily on threshold
53
+ crossings — safe to construct the notifier earlier.
54
+ - Double-start: the old in-block copy was REMOVED (test asserts a single
55
+ QuotaManager construction) — no risk of two pollers.
56
+ - guard-posture tripwire: already observed quotaTracking re-enabled; this fix
57
+ makes that enablement actually produce data.
58
+
59
+ ## 6. External surfaces
60
+
61
+ No new routes/config/notifications. `GET /quota` now returns real data on
62
+ lifeline-driven agents instead of `no_data`.