Skip to content

Commit 2168d98

Browse files
lesnik512claude
andauthored
docs: make retry demo failure count the visible payoff (#110)
The retry page's whole point is that httpware surfaces fewer failures to the caller, but the failed count was rendered in the muted label style, the blip stops spotlighted the in-flight counters (while the copy said 'compare the ✗ counts'), and the 1.2s blip left httpware recovering only ~5 of ~14 — a weak contrast. - ✗ failed is now a prominent stat (bold number, red when >0), wrapped in a spotlightable element, on all pages - retry blip stops spotlight the ✗ counts (plain climbing vs httpware ~0) - tighten the blip to 0.4s so httpware recovers all of it: plain ✗6, httpware ✗0 Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
1 parent 10b9e8c commit 2168d98

4 files changed

Lines changed: 57 additions & 13 deletions

File tree

docs/demos/demos.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@
9696
.hw-demo .score .k { color: var(--hw-muted); }
9797
.hw-demo .stat { padding: 2px 6px; border-radius: 6px; }
9898
.hw-demo .inflight-big { font-size: 1.15rem; font-weight: 700; transition: color .3s; }
99+
.hw-demo .fail-big { font-size: 1.15rem; font-weight: 700; transition: color .3s; }
99100
.hw-demo .big { font-weight: 700; }
100101
.hw-demo .note { color: var(--hw-muted); font-size: .76rem; font-style: italic; margin-top: 6px; }
101102

docs/demos/engine.js

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ window.HttpwareDemo = (function () {
213213
<div class="score">
214214
<span class="stat"><span class="k">in-flight</span> <span class="inflight-big" data-el="ifA">0</span></span>
215215
<span class="k">&#10003; <span data-el="okA">0</span></span>
216-
<span class="k">&#10007; <span data-el="badA">0</span></span>
216+
<span class="stat" data-el="badWrapA"><span class="k">&#10007; failed</span> <span class="fail-big" data-el="badA">0</span></span>
217217
<span class="stat"><span class="k">p99</span> <span class="big" data-el="latA">40ms</span></span>
218218
</div>
219219
</div>
@@ -229,7 +229,7 @@ window.HttpwareDemo = (function () {
229229
<div class="score">
230230
<span class="stat"><span class="k">in-flight</span> <span class="inflight-big" data-el="ifB">0</span></span>
231231
<span class="k">&#10003; <span data-el="okB">0</span></span>
232-
<span class="k">&#10007; <span data-el="badB">0</span></span>
232+
<span class="stat" data-el="badWrapB"><span class="k">&#10007; failed</span> <span class="fail-big" data-el="badB">0</span></span>
233233
<span class="k">&#9211; fast-failed <span data-el="rejB">0</span></span>
234234
<span class="stat" data-el="poolWrap" style="display:none"><span class="k">pool</span> <span class="big" data-el="poolB">&mdash;</span></span>
235235
<span class="stat" data-el="elapsedWrap" style="display:none"><span class="k">elapsed</span> <span class="big" data-el="elapsedB">&mdash;</span></span>
@@ -262,16 +262,16 @@ window.HttpwareDemo = (function () {
262262
scenarios: $('scenarios'), play: $('play'), replay: $('replay'), scenLabel: $('scenLabel'),
263263
timeline: $('timeline'), outage: $('outage'), outageLabel: $('outageLabel'), playhead: $('playhead'),
264264
laneA: $('laneA'), badgeA: $('badgeA'), srvA: $('srvA'), trackA: $('trackA'),
265-
ifA: $('ifA'), okA: $('okA'), badA: $('badA'), latA: $('latA'),
265+
ifA: $('ifA'), okA: $('okA'), badA: $('badA'), badWrapA: $('badWrapA'), latA: $('latA'),
266266
laneB: $('laneB'), badgeB: $('badgeB'), srvB: $('srvB'), trackB: $('trackB'), brkB: $('brkB'),
267-
ifB: $('ifB'), okB: $('okB'), badB: $('badB'), rejB: $('rejB'), latB: $('latB'),
267+
ifB: $('ifB'), okB: $('okB'), badB: $('badB'), badWrapB: $('badWrapB'), rejB: $('rejB'), latB: $('latB'),
268268
poolWrap: $('poolWrap'), poolB: $('poolB'),
269269
elapsedWrap: $('elapsedWrap'), elapsedB: $('elapsedB'),
270270
note: $('note'),
271271
dimT: $('dimT'), dimB: $('dimB'), dimL: $('dimL'), dimR: $('dimR'), ring: $('ring'),
272272
coach: $('coach'), cArrow: $('cArrow'), cStep: $('cStep'), cTitle: $('cTitle'), cBody: $('cBody'), cGo: $('cGo'),
273273
};
274-
const ELS = { ifA: els.ifA, latA: els.latA, ifB: els.ifB, latB: els.latB, brkB: els.brkB, poolB: els.poolB, elapsedB: els.elapsedB };
274+
const ELS = { ifA: els.ifA, latA: els.latA, badWrapA: els.badWrapA, ifB: els.ifB, latB: els.latB, badWrapB: els.badWrapB, brkB: els.brkB, poolB: els.poolB, elapsedB: els.elapsedB };
275275

276276
// Built fresh inside run() from the selected scenario (STOPS is never read before a
277277
// scenario is played, so it's safe to leave empty until then) — pages with one
@@ -401,7 +401,7 @@ window.HttpwareDemo = (function () {
401401
els.ifA.textContent = '0'; els.okA.textContent = '0'; els.badA.textContent = '0';
402402
els.ifB.textContent = '0'; els.okB.textContent = '0'; els.badB.textContent = '0'; els.rejB.textContent = '0';
403403
els.latA.textContent = '40ms'; els.latB.textContent = '40ms';
404-
[els.latA, els.latB, els.ifA, els.ifB].forEach((n) => { n.style.color = ''; });
404+
[els.latA, els.latB, els.ifA, els.ifB, els.badA, els.badB].forEach((n) => { n.style.color = ''; });
405405
// Flow-diagram boxes reflect the selected scenario's CHAIN config, not the runtime
406406
// middleware objects (which aren't constructed until run()). Otherwise the breaker
407407
// box reads "no breaker" on a circuit-breaker page until the first Play.
@@ -425,6 +425,8 @@ window.HttpwareDemo = (function () {
425425
els.ifB.textContent = B.if; els.okB.textContent = B.ok; els.badB.textContent = B.bad; els.rejB.textContent = B.rej;
426426
els.ifA.style.color = A.if > 10 ? 'var(--hw-bad)' : '';
427427
els.ifB.style.color = B.if <= 6 ? 'var(--hw-ok)' : '';
428+
els.badA.style.color = A.bad > 0 ? 'var(--hw-bad)' : '';
429+
els.badB.style.color = B.bad > 0 ? 'var(--hw-bad)' : '';
428430
if (bulk) els.poolB.textContent = bulk.inUse + '/' + bulk.max;
429431
let maxElapsed = 0;
430432
if (tmoCfg) {

docs/demos/retry.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ document.addEventListener('DOMContentLoaded', function () {
1212
HttpwareDemo.mount('#retry-demo', {
1313
scenarios: [
1414
{ id: 'blip', label: 'Brief blip (recovers)', dur: 12.5,
15-
fault: (now, rnd) => (now >= 2.0 && now < 3.2)
15+
fault: (now, rnd) => (now >= 2.0 && now < 2.4)
1616
? { ok: false, ms: 0.05, label: 'blip' } : { ok: true, ms: 0.05 },
1717
chainB: { retry: { maxAttempts: 3, baseDelay: 0.1, maxDelay: 5.0 },
1818
budget: { ttl: 10.0, minRetriesPerSec: 10.0, percentCanRetry: 0.2 } } },
@@ -32,12 +32,12 @@ document.addEventListener('DOMContentLoaded', function () {
3232
{ when: (s) => s.now >= 10.0, spot: ['ifA', 'ifB'], title: 'Blip: recovered. Outage: contained',
3333
body: 'Retry rescues transient errors without turning a real outage into a storm. That cap is the whole reason the budget exists.' },
3434
] : [
35-
{ when: (s) => s.now >= 1.2, spot: ['ifA', 'ifB'], title: 'A backend blip appears',
36-
body: 'Both clients hit the same transient errors. Watch how each responds.' },
37-
{ when: (s) => s.now >= 2.4, spot: ['ifB'], title: 'httpware retries the blip',
38-
body: 'The plain client surfaces the error immediately. httpware retries with backoff most of these recover on attempt 2 or 3, invisibly to the caller.' },
39-
{ when: (s) => s.now >= 10.0, spot: ['ifA', 'ifB'], title: 'Blip: recovered',
40-
body: 'The backend healed and so did both clients — but compare the ✗ counts: httpware surfaced far fewer failures to the caller. That is what retry buys you on a transient blip.' },
35+
{ when: (s) => s.now >= 1.2, spot: ['badWrapA', 'badWrapB'], title: 'A backend blip appears',
36+
body: 'Both clients are about to hit the same transient errors. Keep your eye on the ✗ failed counts — they start equal at zero.' },
37+
{ when: (s) => s.now >= 2.4, spot: ['badWrapA', 'badWrapB'], title: 'Plain surfaces every error; httpware retries',
38+
body: 'The plain client surfaces each error straight to the caller — its ✗ is climbing. httpware retries with backoff, so most of these recover on attempt 2 or 3 and its ✗ barely moves.' },
39+
{ when: (s) => s.now >= 10.0, spot: ['badWrapA', 'badWrapB'], title: 'Blip over — mind the ✗ gap',
40+
body: 'The backend healed. Compare the ✗ failed counts: the plain client surfaced far more failures than httpware. That gap is exactly what retry buys you on a transient blip.' },
4141
],
4242
});
4343
});
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
---
2+
summary: Make the retry demo's failure count the visible payoff — prominent ✗ stat, spotlighted at the blip stops, with a punchier recover-vs-surface contrast.
3+
---
4+
5+
# Change: Make retry demo failures count obvious
6+
7+
**Lane:** lightweight — docs demo only, no capability contract moves.
8+
9+
## Goal
10+
11+
On the retry demo the whole point is that httpware surfaces *fewer failures* to
12+
the caller than a plain client. But the ✗ failure count is rendered in the same
13+
muted style as every label, the blip stops spotlight the in-flight counters
14+
(while the copy says "compare the ✗ counts"), and the 1.2 s blip lets httpware
15+
recover only ~5 of ~14 failures — a weak contrast. Result: the failure count,
16+
the metric that matters, is not obvious.
17+
18+
## Approach
19+
20+
- Make the ✗ failure count a prominent stat (larger number, red when > 0),
21+
wrapped in a spotlightable element, on all pages.
22+
- Register the failure-stat wrappers in the engine's spotlight lookup so stops
23+
can point at them.
24+
- Retry blip stops spotlight the ✗ counts (plain rising vs httpware staying
25+
low) instead of in-flight.
26+
- Tighten the blip window so httpware recovers nearly all of it, making the
27+
recover-vs-surface contrast dramatic (verified by trace).
28+
29+
## Files
30+
31+
- `docs/demos/engine.js` — failure-stat wrappers in the template + ELS lookup;
32+
colour ✗ count red when > 0.
33+
- `docs/demos/demos.css` — prominent failure-number style.
34+
- `docs/demos/retry.md` — blip stops spotlight the ✗ counts; tighten the blip.
35+
36+
## Verification
37+
38+
- [ ] `node --check docs/demos/engine.js`.
39+
- [ ] `just docs-build` clean; scratchpad `verify-real-page.js` + `verify-demos.js` green.
40+
- [ ] Trace the blip: plain ✗ high, httpware ✗ low (dramatic gap); sustained still
41+
hits `budgetExhausted`; every stop reachable.

0 commit comments

Comments
 (0)