instar 1.3.335 → 1.3.336
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/HttpLiveTailTransport.d.ts +19 -0
- package/dist/core/HttpLiveTailTransport.d.ts.map +1 -1
- package/dist/core/HttpLiveTailTransport.js +25 -4
- package/dist/core/HttpLiveTailTransport.js.map +1 -1
- package/dist/core/ReplyMarkerTransport.d.ts +19 -0
- package/dist/core/ReplyMarkerTransport.d.ts.map +1 -1
- package/dist/core/ReplyMarkerTransport.js +25 -4
- package/dist/core/ReplyMarkerTransport.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +2 -2
- package/upgrades/1.3.336.md +45 -0
- package/upgrades/side-effects/sibling-transport-brakes.md +43 -0
|
@@ -59,6 +59,20 @@ export interface HttpLiveTailTransportDeps {
|
|
|
59
59
|
fetchImpl?: typeof fetch;
|
|
60
60
|
/** How recent a successful broadcast counts as "reachable". Default = 60s. */
|
|
61
61
|
reachabilityWindowMs?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Per-request abort timeout (P19 brake — a hung socket must not hold a
|
|
64
|
+
* flush open indefinitely). Default 30s: sized ABOVE the fleet's documented
|
|
65
|
+
* 5–40s receiver-stall envelope (the #874 reviewer's sizing rationale) so a
|
|
66
|
+
* slow-but-alive standby is not converted into "unreachable".
|
|
67
|
+
*/
|
|
68
|
+
requestTimeoutMs?: number;
|
|
69
|
+
/**
|
|
70
|
+
* Coarse-reminder interval for the per-peer failure log gate (P19 brake).
|
|
71
|
+
* Default 360: with #867's per-topic backoff the attempt rate is bounded,
|
|
72
|
+
* but N topics against one down peer still meant N lines per backoff window
|
|
73
|
+
* — the gate collapses that to first/Nth/recovery per peer.
|
|
74
|
+
*/
|
|
75
|
+
failureLogEveryN?: number;
|
|
62
76
|
now?: () => number;
|
|
63
77
|
logger?: (msg: string) => void;
|
|
64
78
|
}
|
|
@@ -73,7 +87,12 @@ export declare class HttpLiveTailTransport {
|
|
|
73
87
|
private readonly d;
|
|
74
88
|
private lastBroadcastOkAt;
|
|
75
89
|
private readonly windowMs;
|
|
90
|
+
private readonly requestTimeoutMs;
|
|
91
|
+
/** State-change failure logging (first/Nth/recovery) — never per-attempt. */
|
|
92
|
+
private readonly logGate;
|
|
76
93
|
constructor(deps: HttpLiveTailTransportDeps);
|
|
94
|
+
private logFailure;
|
|
95
|
+
private logSuccess;
|
|
77
96
|
private now;
|
|
78
97
|
private log;
|
|
79
98
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpLiveTailTransport.d.ts","sourceRoot":"","sources":["../../src/core/HttpLiveTailTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;
|
|
1
|
+
{"version":3,"file":"HttpLiveTailTransport.d.ts","sourceRoot":"","sources":["../../src/core/HttpLiveTailTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAKH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE/D,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;IACZ,6EAA6E;IAC7E,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,qFAAqF;AACrF,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,sBAAsB,CAAC;IAC5B,2EAA2E;IAC3E,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,yBAAyB;IACxC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,yFAAyF;IACzF,KAAK,EAAE,MAAM,YAAY,EAAE,CAAC;IAC5B,yEAAyE;IACzE,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B;;;OAGG;IACH,UAAU,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,KAAK,sBAAsB,CAAC;IACpF,iDAAiD;IACjD,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,gFAAgF;IAChF,GAAG,EAAE,MAAM,CAAC;IACZ,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAA4B;IAC9C,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,IAAI,EAAE,yBAAyB;IAO3C,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAKlB,OAAO,CAAC,GAAG;IAGX,OAAO,CAAC,GAAG;IAIX;;;;OAIG;IACG,SAAS,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IA4C5D,uFAAuF;IACvF,WAAW,IAAI,OAAO;CAGvB"}
|
|
@@ -28,14 +28,30 @@
|
|
|
28
28
|
* redact→encrypt→post logic is unit-testable without a network or real keys.
|
|
29
29
|
*/
|
|
30
30
|
import { signRequest } from '../server/machineAuth.js';
|
|
31
|
+
import { PeerFailureLogGate } from './PeerFailureLogGate.js';
|
|
31
32
|
import { redactForLiveTail } from './liveTailRedaction.js';
|
|
32
33
|
export class HttpLiveTailTransport {
|
|
33
34
|
d;
|
|
34
35
|
lastBroadcastOkAt = 0;
|
|
35
36
|
windowMs;
|
|
37
|
+
requestTimeoutMs;
|
|
38
|
+
/** State-change failure logging (first/Nth/recovery) — never per-attempt. */
|
|
39
|
+
logGate;
|
|
36
40
|
constructor(deps) {
|
|
37
41
|
this.d = deps;
|
|
38
42
|
this.windowMs = deps.reachabilityWindowMs ?? 60_000;
|
|
43
|
+
this.requestTimeoutMs = deps.requestTimeoutMs ?? 30_000;
|
|
44
|
+
this.logGate = new PeerFailureLogGate(deps.failureLogEveryN ?? 360);
|
|
45
|
+
}
|
|
46
|
+
logFailure(key, detail) {
|
|
47
|
+
const line = this.logGate.failed(key, detail);
|
|
48
|
+
if (line)
|
|
49
|
+
this.log(line);
|
|
50
|
+
}
|
|
51
|
+
logSuccess(key) {
|
|
52
|
+
const line = this.logGate.succeeded(key);
|
|
53
|
+
if (line)
|
|
54
|
+
this.log(line);
|
|
39
55
|
}
|
|
40
56
|
now() {
|
|
41
57
|
return (this.d.now ?? Date.now)();
|
|
@@ -73,14 +89,19 @@ export class HttpLiveTailTransport {
|
|
|
73
89
|
method: 'POST',
|
|
74
90
|
headers: { 'Content-Type': 'application/json', ...headers },
|
|
75
91
|
body: JSON.stringify(body),
|
|
92
|
+
// P19 brake: a hung socket aborts instead of holding the flush open.
|
|
93
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
76
94
|
});
|
|
77
|
-
if (res && res.ok)
|
|
95
|
+
if (res && res.ok) {
|
|
78
96
|
anyOk = true;
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
this.logSuccess(`live-tail to ${peer.machineId}`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.logFailure(`live-tail to ${peer.machineId}`, `status ${res?.status} (seq=${flush.seq})`);
|
|
101
|
+
}
|
|
81
102
|
}
|
|
82
103
|
catch (err) {
|
|
83
|
-
this.
|
|
104
|
+
this.logFailure(`live-tail to ${peer.machineId}`, err instanceof Error ? err.message : String(err));
|
|
84
105
|
}
|
|
85
106
|
}));
|
|
86
107
|
if (anyOk)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HttpLiveTailTransport.js","sourceRoot":"","sources":["../../src/core/HttpLiveTailTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"HttpLiveTailTransport.js","sourceRoot":"","sources":["../../src/core/HttpLiveTailTransport.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,12 +40,31 @@ export interface ReplyMarkerTransportDeps {
|
|
|
40
40
|
/** Monotonic per-request sequence for machine-auth replay protection. */
|
|
41
41
|
nextSequence: () => number;
|
|
42
42
|
fetchImpl?: typeof fetch;
|
|
43
|
+
/**
|
|
44
|
+
* Per-request abort timeout (P19 brake — a hung socket must not hold the
|
|
45
|
+
* reply-commit path open). Default 30s: sized ABOVE the fleet's documented
|
|
46
|
+
* 5–40s receiver-stall envelope (the #874 reviewer's sizing rationale — a
|
|
47
|
+
* slow-but-alive peer must not read as unreachable).
|
|
48
|
+
*/
|
|
49
|
+
requestTimeoutMs?: number;
|
|
50
|
+
/**
|
|
51
|
+
* Coarse-reminder interval for the per-peer failure log gate (P19 brake).
|
|
52
|
+
* Default 50 — markers flow at REPLY cadence (not a fixed 5s tick), so the
|
|
53
|
+
* reminder lands roughly every 50 failed replies; first-failure and
|
|
54
|
+
* recovery lines carry the state changes regardless.
|
|
55
|
+
*/
|
|
56
|
+
failureLogEveryN?: number;
|
|
43
57
|
logger?: (msg: string) => void;
|
|
44
58
|
}
|
|
45
59
|
export declare class ReplyMarkerTransport {
|
|
46
60
|
private readonly d;
|
|
61
|
+
private readonly requestTimeoutMs;
|
|
62
|
+
/** State-change failure logging (first/Nth/recovery) — never per-attempt. */
|
|
63
|
+
private readonly logGate;
|
|
47
64
|
constructor(deps: ReplyMarkerTransportDeps);
|
|
48
65
|
private log;
|
|
66
|
+
private logFailure;
|
|
67
|
+
private logSuccess;
|
|
49
68
|
/**
|
|
50
69
|
* POST the marker to every standby peer over the signed channel. Resolves true
|
|
51
70
|
* if at least one peer accepted (a live medium carried it); false if none was
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplyMarkerTransport.d.ts","sourceRoot":"","sources":["../../src/core/ReplyMarkerTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;
|
|
1
|
+
{"version":3,"file":"ReplyMarkerTransport.d.ts","sourceRoot":"","sources":["../../src/core/ReplyMarkerTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAKH,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,4DAA4D;IAC5D,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,KAAK,EAAE,MAAM,eAAe,EAAE,CAAC;IAC/B,yEAAyE;IACzE,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,CAAC,CAA2B;IAC7C,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;gBAEjC,IAAI,EAAE,wBAAwB;IAM1C,OAAO,CAAC,GAAG;IAIX,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAKlB;;;;;;OAMG;IACG,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;CA8BvD"}
|
|
@@ -21,14 +21,30 @@
|
|
|
21
21
|
* injected for testability.
|
|
22
22
|
*/
|
|
23
23
|
import { signRequest } from '../server/machineAuth.js';
|
|
24
|
+
import { PeerFailureLogGate } from './PeerFailureLogGate.js';
|
|
24
25
|
export class ReplyMarkerTransport {
|
|
25
26
|
d;
|
|
27
|
+
requestTimeoutMs;
|
|
28
|
+
/** State-change failure logging (first/Nth/recovery) — never per-attempt. */
|
|
29
|
+
logGate;
|
|
26
30
|
constructor(deps) {
|
|
27
31
|
this.d = deps;
|
|
32
|
+
this.requestTimeoutMs = deps.requestTimeoutMs ?? 30_000;
|
|
33
|
+
this.logGate = new PeerFailureLogGate(deps.failureLogEveryN ?? 50);
|
|
28
34
|
}
|
|
29
35
|
log(m) {
|
|
30
36
|
this.d.logger?.(`[reply-marker] ${m}`);
|
|
31
37
|
}
|
|
38
|
+
logFailure(key, detail) {
|
|
39
|
+
const line = this.logGate.failed(key, detail);
|
|
40
|
+
if (line)
|
|
41
|
+
this.log(line);
|
|
42
|
+
}
|
|
43
|
+
logSuccess(key) {
|
|
44
|
+
const line = this.logGate.succeeded(key);
|
|
45
|
+
if (line)
|
|
46
|
+
this.log(line);
|
|
47
|
+
}
|
|
32
48
|
/**
|
|
33
49
|
* POST the marker to every standby peer over the signed channel. Resolves true
|
|
34
50
|
* if at least one peer accepted (a live medium carried it); false if none was
|
|
@@ -50,14 +66,19 @@ export class ReplyMarkerTransport {
|
|
|
50
66
|
method: 'POST',
|
|
51
67
|
headers: { 'Content-Type': 'application/json', ...headers },
|
|
52
68
|
body: JSON.stringify(body),
|
|
69
|
+
// P19 brake: a hung socket aborts instead of holding the reply-commit path.
|
|
70
|
+
signal: AbortSignal.timeout(this.requestTimeoutMs),
|
|
53
71
|
});
|
|
54
|
-
if (res && res.ok)
|
|
72
|
+
if (res && res.ok) {
|
|
55
73
|
anyOk = true;
|
|
56
|
-
|
|
57
|
-
|
|
74
|
+
this.logSuccess(`marker to ${peer.machineId}`);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
this.logFailure(`marker to ${peer.machineId}`, `status ${res?.status} (marker ${marker.dedupeKey})`);
|
|
78
|
+
}
|
|
58
79
|
}
|
|
59
80
|
catch (err) {
|
|
60
|
-
this.
|
|
81
|
+
this.logFailure(`marker to ${peer.machineId}`, err instanceof Error ? err.message : String(err));
|
|
61
82
|
}
|
|
62
83
|
}));
|
|
63
84
|
return anyOk;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReplyMarkerTransport.js","sourceRoot":"","sources":["../../src/core/ReplyMarkerTransport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"ReplyMarkerTransport.js","sourceRoot":"","sources":["../../src/core/ReplyMarkerTransport.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "./builtin-manifest.schema.json",
|
|
3
3
|
"schemaVersion": 1,
|
|
4
|
-
"generatedAt": "2026-06-06T03:
|
|
5
|
-
"instarVersion": "1.3.
|
|
4
|
+
"generatedAt": "2026-06-06T03:58:05.940Z",
|
|
5
|
+
"instarVersion": "1.3.336",
|
|
6
6
|
"entryCount": 199,
|
|
7
7
|
"entries": {
|
|
8
8
|
"hook:session-start": {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Audit fix #5 under "No Unbounded Loops" (P19), completing the mesh-transport
|
|
9
|
+
brake set #874 started: `HttpLiveTailTransport` (conversation-tail streaming)
|
|
10
|
+
and `ReplyMarkerTransport` (cross-machine "already answered" markers) get
|
|
11
|
+
`AbortSignal.timeout` on their fetches (30s default — the #874 reviewer's
|
|
12
|
+
stall-envelope sizing, applied from the start) and `PeerFailureLogGate`
|
|
13
|
+
state-change failure logging (first / every-Nth / recovery; previously one
|
|
14
|
+
line per failed attempt, and silent for non-ok responses). Verified: neither
|
|
15
|
+
transport feeds lease renewal/suspend (`isReachable()`'s only consumer is the
|
|
16
|
+
lease wire), so the timeout carries zero authority coupling. The reviewer
|
|
17
|
+
confirmed the marker-drop trade-off under a 30s abort is pre-accepted by the
|
|
18
|
+
marker's documented design (fire-and-forget; provider redelivery + dedup gate
|
|
19
|
+
+ synced ledger are the backstops).
|
|
20
|
+
|
|
21
|
+
## What to Tell Your User
|
|
22
|
+
|
|
23
|
+
If you run me on more than one machine: the last two machine-to-machine sync
|
|
24
|
+
channels now have the same protections as the rest — a stuck network
|
|
25
|
+
connection can't silently jam them, and a down machine produces a handful of
|
|
26
|
+
meaningful log lines instead of one per failed attempt. All four cross-machine
|
|
27
|
+
wires now follow the loop-safety standard.
|
|
28
|
+
|
|
29
|
+
## Summary of New Capabilities
|
|
30
|
+
|
|
31
|
+
- Hung-socket protection + bounded failure logging on the live-tail and
|
|
32
|
+
reply-marker transports (no configuration needed; recovery and
|
|
33
|
+
rejecting-peer visibility included).
|
|
34
|
+
|
|
35
|
+
## Evidence
|
|
36
|
+
|
|
37
|
+
CMT-1109 audit, mechanical completion of the #874 pattern (line-level
|
|
38
|
+
CONCUR-reviewed tonight) on two lower-blast-radius transports. Focused
|
|
39
|
+
adversarial second-pass: CONCUR on all probes (Two-Generals widening
|
|
40
|
+
pre-accepted by design — broadcast is void-discarded at its only callsite;
|
|
41
|
+
gate memory peer-bounded; alternating ok/fail chatter is diagnostic at reply
|
|
42
|
+
cadence; local encryptFor throws disambiguate via error detail). Tests: 6 new
|
|
43
|
+
green (AbortSignal pins both transports; P19 bounds — 25 failed flushes → 3
|
|
44
|
+
lines, 12 rejected markers → 2 lines; recovery-once; steady-success silence)
|
|
45
|
+
+ pre-existing transport suites untouched and green; tsc clean.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Side-Effects Review — Sibling-Transport Brakes
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `sibling-transport-brakes`
|
|
4
|
+
**Date:** `2026-06-06`
|
|
5
|
+
**Author:** `Echo (instar-dev agent, autonomous session per Justin's direction)`
|
|
6
|
+
**Second-pass reviewer:** `focused adversarial reviewer subagent — CONCUR on all four probes (the #874 pattern itself carries tonight's line-level CONCUR)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
The #874 brake pattern applied verbatim to the two remaining mesh transports: `HttpLiveTailTransport` and `ReplyMarkerTransport` get `AbortSignal.timeout` (30s default, the #874-corrected sizing above the fleet's 5–40s receiver-stall envelope) on their fetches, and `PeerFailureLogGate` state-change failure logging (live-tail N=360; reply-marker N=50 for reply cadence) replacing per-attempt lines, with non-ok responses now gated-logged. Files: both transports, one test file.
|
|
11
|
+
|
|
12
|
+
## Decision-point inventory
|
|
13
|
+
|
|
14
|
+
- Both transports' `broadcast()` — **modify (bounded)** — same requests, same return semantics; failures abort at the timeout instead of hanging; logging gated.
|
|
15
|
+
|
|
16
|
+
## 1. Over-block / 2. Under-block
|
|
17
|
+
|
|
18
|
+
(a) A 30s abort can drop a reply-marker a slower wait might have delivered — widening the marker's documented Two-Generals residual. Reviewer-traced: `broadcast()`'s return is void-discarded at its ONLY callsite (fire-and-forget by design); provider redelivery + the dedup gate + git-committed ledger state are the explicit backstops. Pre-accepted, not new risk. (b) Perfectly alternating ok/fail logs one transition pair per cycle — at reply cadence this is bounded and is diagnostic signal for a genuinely unstable peer (reviewer: not a blocker; an across-resets suppressor would mask real instability). (c) A local `encryptFor` throw logs under the peer's key — imprecise label, but the error detail (the real exception message) disambiguates and the operational meaning ("this peer's flush did not deliver") is identical. (d) Remaining audit item is the SessionRouter design <!-- tracked: CMT-1109 -->.
|
|
19
|
+
|
|
20
|
+
## 3. Level-of-abstraction fit / 4. Signal vs authority
|
|
21
|
+
|
|
22
|
+
Brakes in the transports that generate the cost; cadence stays with callers; reuses the canonical gate class rather than re-implementing. **Signal-only** per `docs/signal-vs-authority.md`: log shaping + failure-timing bounds. Critically verified: neither transport feeds lease renewal/suspend (`.isReachable()`'s only src consumer is `LeaseCoordinator` via the LEASE transport) — the #874 reviewer's false-self-suspend concern structurally cannot apply here.
|
|
23
|
+
|
|
24
|
+
## 5. Interactions
|
|
25
|
+
|
|
26
|
+
- **Exactly-once (the marker's job):** unchanged — the timeout affects only the best-effort fast path; the dedup gate and ledger sync carry correctness (reviewer probe 1).
|
|
27
|
+
- **#867's per-topic backoff:** composes — backoff bounds live-tail attempt RATE per topic; the gate bounds LOG volume per peer across topics.
|
|
28
|
+
- **Old format pins:** none — pre-existing transport suites pass untouched.
|
|
29
|
+
- **Node engines / AbortSignal-vs-test-clocks:** identical to #874 (verified there; same package.json).
|
|
30
|
+
|
|
31
|
+
## 6. External surfaces / 7. Rollback
|
|
32
|
+
|
|
33
|
+
Logs only; no API/schema/config/persistent state; no migration (in-code defaults). Rollback = revert; the hang exposure and per-attempt logging return.
|
|
34
|
+
|
|
35
|
+
## Conclusion
|
|
36
|
+
|
|
37
|
+
Completes the mesh-transport brake set: all four cross-machine wires (lease #874, heartbeat #881, live-tail and reply-marker here) now carry P19's brakes. Mechanical extension of a twice-validated pattern, with the one novel risk (marker-drop under timeout) traced to a documented, backstopped design acceptance.
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Phase 5 — Second-pass review (cross-machine exactly-once adjacency → performed)
|
|
42
|
+
|
|
43
|
+
A focused adversarial pass probed only what is novel beyond #874's reviewed pattern: (1) the Two-Generals widening from a 30s marker abort — traced `broadcast()`'s only callsite (void-discarded, fire-and-forget, comment-documented backstops): pre-accepted by design; (2) gate key granularity + alternating ok/fail behavior — peer-bounded memory, transition chatter acceptable-and-diagnostic at reply cadence; (3) local `encryptFor` throws misattributed to the peer — disambiguated by the error detail, operationally equivalent; (4) ran the new + pre-existing transport suites (15/15) and tsc (clean). **Verdict: CONCUR.**
|