instar 1.3.988 → 1.3.989
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/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +9 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/channelRegistry.d.ts +104 -0
- package/dist/core/channelRegistry.d.ts.map +1 -0
- package/dist/core/channelRegistry.js +86 -0
- package/dist/core/channelRegistry.js.map +1 -0
- package/dist/core/instarChannels.d.ts +32 -0
- package/dist/core/instarChannels.d.ts.map +1 -0
- package/dist/core/instarChannels.js +91 -0
- package/dist/core/instarChannels.js.map +1 -0
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +6 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +40 -0
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.989.md +55 -0
- package/upgrades/side-effects/channel-registry.md +199 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
# Side-Effects Review — a channel registry whose defining property is that nothing can vanish from it
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `channel-registry`
|
|
4
|
+
**Date:** `2026-07-26`
|
|
5
|
+
**Author:** `Echo (instar-dev agent)`
|
|
6
|
+
**Second-pass reviewer:** `see Phase 5`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
Operator request (topic 29723): inter-agent channel choice "feels arbitrary"; there is no registry of
|
|
11
|
+
what channels exist and when each is appropriate. Evidence: when the relay died I stopped and reported
|
|
12
|
+
that I could not reach the peer. I did not evaluate alternatives — I had no way to ask what they were
|
|
13
|
+
or which were alive.
|
|
14
|
+
|
|
15
|
+
Adds `src/core/channelRegistry.ts` (pure resolver + state vocabulary), `src/core/instarChannels.ts`
|
|
16
|
+
(this agent's four peer channels with injected probes), and `GET /channels`.
|
|
17
|
+
|
|
18
|
+
**The design is NOT the list.** A hand-built list, tried first, was wrong three times in one hour —
|
|
19
|
+
each time by classifying something by its label rather than its consumer. The load-bearing properties:
|
|
20
|
+
|
|
21
|
+
1. **Absence is impossible.** The channel set is code-defined, never derived from what constructed
|
|
22
|
+
successfully. Row count == definition count, whatever probes do.
|
|
23
|
+
2. **A channel that cannot determine liveness says so.** `unknown` carries a reason; it is never a
|
|
24
|
+
synonym for healthy and is counted separately from `unusable`.
|
|
25
|
+
3. **The vocabulary matches reality:** `working | broken | half-built | reachable-no-credential |
|
|
26
|
+
not-configured | unknown`. Every value was observed on a real channel; none is speculative.
|
|
27
|
+
|
|
28
|
+
## Refusal evidence (constraint 2)
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
REFUSAL 1 — drop channels whose probe failed (the incident's exact shape)
|
|
32
|
+
× REGRESSION: a channel whose probe THROWS still gets a row
|
|
33
|
+
→ expected [ { id: 'healthy', …(7) } ] to have a length of 2 but got 1
|
|
34
|
+
Tests 5 failed | 7 passed (12)
|
|
35
|
+
|
|
36
|
+
REFUSAL 2 — treat an undetermined probe as healthy (the mirror error)
|
|
37
|
+
× UNKNOWN is never counted as unusable — "could not tell" is not "broken"
|
|
38
|
+
× discriminates — it is not stuck on one verdict, and it reports the healthy case
|
|
39
|
+
Tests 5 failed | 7 passed (12)
|
|
40
|
+
|
|
41
|
+
REFUSAL 3 — empty the ROUTE's registry
|
|
42
|
+
× REGRESSION: the route serves EVERY code-defined channel — an emptied registry fails here
|
|
43
|
+
→ expected [] to deeply equal [ 'a2a-telegram', 'mutual-ssh', …(2) ]
|
|
44
|
+
Tests 4 failed | 2 passed (6)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Restored: **25 passed (25)**, `tsc --noEmit` exit 0.
|
|
48
|
+
|
|
49
|
+
**Refusal 3 is the finding.** I ran it BEFORE writing the integration test: the route served zero
|
|
50
|
+
channels and **all 19 unit tests passed.** The module was thoroughly guarded; the wiring was not. That
|
|
51
|
+
is this feature's own defect one layer up — a surface reporting nothing wrong because nothing asked it
|
|
52
|
+
anything — and without the mandatory refusal step I would have shipped it as covered.
|
|
53
|
+
|
|
54
|
+
## Decision-point inventory
|
|
55
|
+
|
|
56
|
+
| point | classification | note |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| per-channel probe → state | `invariant` per channel | Deterministic reads of runtime state. No model call. |
|
|
59
|
+
| probe failure → `unknown` | `invariant` | Fails toward "undetermined", never toward healthy. |
|
|
60
|
+
| malformed probe result → `unknown` | `invariant` | An unrecognised shape is a failed probe, not a healthy channel. |
|
|
61
|
+
| 3s probe timeout | `invariant` | One wedged channel must not make the surface unanswerable. |
|
|
62
|
+
| `unknown` excluded from `unusable` | `invariant` | Deliberate: "could not tell" ≠ "broken". |
|
|
63
|
+
| a2a-telegram = `half-built` | **asserted, guarded** | Build-time fact; see §2. |
|
|
64
|
+
|
|
65
|
+
No judgment points. No LLM. Nothing gates or blocks.
|
|
66
|
+
|
|
67
|
+
## 1. Over-block
|
|
68
|
+
|
|
69
|
+
Nothing is blocked — this is a read surface with no authority over any send. The available harm is
|
|
70
|
+
**misinforming a reader**, and the direction that matters is a channel reported healthier than it is.
|
|
71
|
+
Every failure path resolves to `unknown` or a specific unusable state; no path resolves to `working`
|
|
72
|
+
without a probe explicitly saying so (asserted by the garbage/nullish/throw/hang tests).
|
|
73
|
+
|
|
74
|
+
The opposite over-block — reporting a working channel as unusable — would cost me a usable path
|
|
75
|
+
mid-outage. Guarded by the discrimination test asserting all four verdict classes occur and by the
|
|
76
|
+
route test asserting a connected relay reports `working`.
|
|
77
|
+
|
|
78
|
+
## 2. Under-block
|
|
79
|
+
|
|
80
|
+
**One entry is asserted, not measured.** `a2a-telegram` is reported `half-built / receive-only`
|
|
81
|
+
because its send function has no executing caller — a build-time property no runtime probe can see.
|
|
82
|
+
Mitigated by a source-scan test that fails the moment a caller appears, forcing the entry to be
|
|
83
|
+
corrected. Honest limit: it detects a caller in `src/`, not one added in scripts or templates.
|
|
84
|
+
|
|
85
|
+
**Liveness is construction, not round-trip.** `mutual-ssh` reports `working` when its runtime
|
|
86
|
+
constructed. That is NOT proof a peer was reached, and the detail string says so verbatim (asserted by
|
|
87
|
+
test). Real round-trip probing is a bigger change and is deliberately not bundled. <!-- tracked: CMT-1044 -->
|
|
88
|
+
|
|
89
|
+
**`peer-http` is honest but inert here.** No peer HTTP endpoint is configured, so it reports exactly
|
|
90
|
+
that rather than probing. On an agent that configures one, this needs a real probe.
|
|
91
|
+
|
|
92
|
+
**Not wired into the operator dashboard.** The data is available at `GET /channels`; nothing renders it.
|
|
93
|
+
Deliberate — the same separation I applied in #1656, and stated here rather than left for a reader to
|
|
94
|
+
notice. <!-- tracked: CMT-1044 -->
|
|
95
|
+
|
|
96
|
+
**It does not fix a single channel.** Two of three peer channels remain unusable. This makes their state
|
|
97
|
+
visible, which is a precondition for choosing between them, not a repair.
|
|
98
|
+
|
|
99
|
+
## 3. Level-of-abstraction fit
|
|
100
|
+
|
|
101
|
+
The resolver is pure (`fs`-free, network-free, clock injected) and testable without a server; the
|
|
102
|
+
definitions own the runtime coupling; the route owns transport. Probes are injected, which is what
|
|
103
|
+
made the incident state reproducible in tests.
|
|
104
|
+
|
|
105
|
+
**A smarter thing already exists and is deliberately NOT duplicated.** `/capability-registry`
|
|
106
|
+
(`scanState: "never-observed"`) and `/capabilities` (`autoDispatch: false` rather than an omitted key)
|
|
107
|
+
and a discovery adapter's breaker-aware `isAvailable()` all already implement honest-absence. This is
|
|
108
|
+
the same idiom applied to channels — universality, not invention. A future consolidation into
|
|
109
|
+
`capability-registry` as host is reasonable; it is not attempted here because that surface has its own
|
|
110
|
+
projection/scan lifecycle this data does not share.
|
|
111
|
+
|
|
112
|
+
## 4. Signal vs authority compliance
|
|
113
|
+
|
|
114
|
+
Pure signal, marked `advisory: true` in the response. It cannot refuse a send, select a channel, or
|
|
115
|
+
influence routing; nothing consumes it programmatically. `docs/signal-vs-authority.md` is satisfied
|
|
116
|
+
trivially. The one risk an observer carries — taking down what it watches — is closed by the probe
|
|
117
|
+
timeout and by the route returning a self-describing error object rather than a 500.
|
|
118
|
+
|
|
119
|
+
## 4b. Judgment-point check (Judgment Within Floors standard)
|
|
120
|
+
|
|
121
|
+
None introduced. Every branch is a deterministic read.
|
|
122
|
+
|
|
123
|
+
## 5. Interactions
|
|
124
|
+
|
|
125
|
+
- **`/threadline/status`** — reads the same `ctx.threadlineRelayClient.connectionState`; unchanged, not
|
|
126
|
+
wrapped. Two surfaces over one source, deliberately: that one is threadline-specific, this one is
|
|
127
|
+
cross-channel.
|
|
128
|
+
- **App-level auth** — `/channels` carries no per-handler auth check, matching `/capability-registry`.
|
|
129
|
+
I asserted 401 first and got 200; on checking, auth is app-level middleware absent from the test
|
|
130
|
+
harness by construction. Recorded in the test rather than deleted (§6b).
|
|
131
|
+
- **`globalThis.__instarMutualSshRuntime`** — read only. Set by `server.ts` after successful construction.
|
|
132
|
+
- **Excluded by design:** the upstream dispatcher and the reputation/discovery client. Both were on my
|
|
133
|
+
first list; both are asserted absent by test so the category error cannot be re-introduced quietly.
|
|
134
|
+
|
|
135
|
+
## 6. External surfaces
|
|
136
|
+
|
|
137
|
+
One new authenticated read route. No config key, no persisted state, no message to any user, nothing
|
|
138
|
+
installed into an agent home. Response contains channel ids, purposes, verdicts and evidence strings —
|
|
139
|
+
no credentials, no peer identifiers, no message content.
|
|
140
|
+
|
|
141
|
+
## 6b. Operator-surface quality
|
|
142
|
+
|
|
143
|
+
Each row carries purpose / when-preferred / cost / verdict / evidence — the operator's four questions
|
|
144
|
+
plus the reason. Asserted non-empty for every row by test.
|
|
145
|
+
|
|
146
|
+
Wording carries two deliberate hedges earned tonight: `mutual-ssh` says construction "is not a
|
|
147
|
+
completed round-trip", and `not-configured` reads as a decision rather than a fault, so switched-off
|
|
148
|
+
infrastructure does not manufacture alarms.
|
|
149
|
+
|
|
150
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
151
|
+
|
|
152
|
+
**Machine-local BY DESIGN, and this is a real limitation rather than a neutral choice.** Every probe
|
|
153
|
+
reads this process's own state; a channel healthy here may be dead on a peer machine. No replication,
|
|
154
|
+
no lease interaction, no generated URL. A pool-scope merge (`?scope=pool`, dark-peer tolerant) is the
|
|
155
|
+
obvious extension and is not attempted here. <!-- tracked: CMT-1044 -->
|
|
156
|
+
|
|
157
|
+
## 8. Rollback cost
|
|
158
|
+
|
|
159
|
+
Low. Two new modules, one route, three test files. No migration, no persisted state, no config default.
|
|
160
|
+
Reverting removes a read surface and nothing else; no caller depends on it.
|
|
161
|
+
|
|
162
|
+
## Phase 5 — Second-pass review
|
|
163
|
+
|
|
164
|
+
Not a gate, sentinel, guard or watchdog; holds no block/allow authority; touches no session lifecycle
|
|
165
|
+
or trust level. The high-risk trigger list is not engaged. Author-applied lenses, disclosed:
|
|
166
|
+
|
|
167
|
+
**Adversarial — "how would I make this useless?"** Two ways, both closed and asserted: let a failed
|
|
168
|
+
probe delete its channel (refusal 1), or let it report healthy (refusal 2). A third — let the route
|
|
169
|
+
stop asking — was open until refusal 3 found it.
|
|
170
|
+
|
|
171
|
+
**"Would it have caught the incident?"** It would have shown, in one read: relay `broken`, a2a
|
|
172
|
+
`half-built/receive-only`, mutual-ssh `broken`, peer-http `reachable-no-credential`. Summary: zero
|
|
173
|
+
working. That is the answer I needed and could not get — and notably it would ALSO have stopped me
|
|
174
|
+
telling the operator a fallback existed, because the row says it cannot send.
|
|
175
|
+
|
|
176
|
+
**"Symptom or cause?"** Neither: it makes the state legible. The channels are still broken.
|
|
177
|
+
|
|
178
|
+
**Weakest point:** the asserted `half-built` entry. It is guarded, but a guard scoped to `src/` is
|
|
179
|
+
narrower than the claim it protects, and the entry is the single place where this registry could
|
|
180
|
+
become the confident-but-stale label it exists to prevent.
|
|
181
|
+
|
|
182
|
+
## Post-CI addendum — three awareness registries I did not know existed
|
|
183
|
+
|
|
184
|
+
I updated `templates.ts` + `PostUpdateMigrator` by hand and believed the Agent Awareness Standard was
|
|
185
|
+
satisfied. CI disagreed, three times:
|
|
186
|
+
|
|
187
|
+
1. **`feature-delivery-completeness`** — the CLAUDE.md section must be listed in `featureSections`, or
|
|
188
|
+
the template↔migrator parity assertion cannot see it at all.
|
|
189
|
+
2. **`capabilities-discoverability`** — a new route prefix must be classified in `CapabilityIndex`:
|
|
190
|
+
surfaced in `/capabilities` or explicitly `INTERNAL_PREFIXES`. Its message is exactly right —
|
|
191
|
+
*"The lint refuses to assume; the author makes the call."* Surfaced, since agents need it.
|
|
192
|
+
3. **`migrateFrameworkShadowCapabilities`** — without a marker, a Codex/Gemini agent never learns the
|
|
193
|
+
capability and "will improvise a weaker workaround". That is this feature's own failure mode
|
|
194
|
+
reproduced one layer out, and I would have shipped it.
|
|
195
|
+
|
|
196
|
+
**This is the Structure-over-Willpower case restated by accident.** I was deliberately doing the
|
|
197
|
+
awareness work and still missed three of five required registries. No amount of care would have closed
|
|
198
|
+
that gap; only the gates did. Recorded here rather than quietly fixed, because the ratio (2 found by
|
|
199
|
+
intent, 3 by machinery) is the useful number.
|