Repository navigation
Measure Pele render time on a phone and set the default diagram limits #522
Description
Part of #521. Read the tracker first: its §Shared contract binds this issue. Work on main.
Summary
A throwaway measurement, outside the tracked tree, that sets the default limits for the new package (D10). It times Pele render plus DOMPurify sanitize across a size ladder. The maintainer then confirms on a real mid-range phone. The public API freezes only at this issue's checkpoint.
Rulings: 1, 10, 11, 12 (#521 §Maintainer rulings).
Files and interfaces
Files (all gitignored; H is REPO/Notes/local-docs/mermaid-measure)
- Create:
H/package.json,H/index.html,H/bench.ts,H/examples.md(a copy ofhttps://pele.run/examples.md). - Create: the measure note,
REPO/Notes/local-docs/<measure date>-mermaid-render-measure.md(<measure date>is this run's date,YYYY-MM-DD).
Interfaces
- Produces:
DEFAULT_LIMITS = { characters: N, edges: M }for Scaffold @docs.plus/extension-mermaid with its types and the insertMermaid command #523; thestarterrow laptop p95 for the README (Write the extension-mermaid README, docs, assets and StackBlitz example #526); a check thatPeleError.linereports editor lines. If L1 or L2 does not, record a finding (Add the Mermaid renderer, default view, styles and node view #524 adds the offset); the sanitize survival result fordocs/security.md.
Measurement rules
- One sample is
performance.now()around one synchronousmount(el, source, OPTIONS)on a fresh connecteddiv(widthmin(720px, 100%)).OPTIONS.sanitizeruns insidemount, so a sample is render plus sanitize, likedurationMs. Destroy and remove the element after each sample. - Before the first sample of a fixture, run
detectTypein try/catch andawait load(type). Loading is never timed. - One warm-up sample, then 50 timed samples. Yield with
setTimeout(r, 0)between samples, and keep the tab in the foreground. - Percentiles are nearest-rank. With 50 samples, p99 is the maximum.
OPTIONS:{ limit: 20000, maxEdges: 1000, outputLimit: 1000000, links: false, images: false, zoom: matchMedia('(pointer: coarse)').matches ? false : 'auto', sanitize, now: Date.UTC(2026, 0, 1) }, wheresanitizeis the D8 config ([Tracking] Mermaid diagrams in docs.plus through a headless Pele extension #521 §Shared contract → Behaviour) withdompurify3.4.15.- Ladder levels (
characters/edges):20000/1000,15000/750,10000/500,5000/250. - Fixtures:
starter: the [Tracking] Mermaid diagrams in docs.plus through a headless Pele extension #521 §Commands starter.- Seven grown fixtures (sequence, class, state, er, gantt, gitGraph, mindmap). Each grows one valid line at a time to just under the level's
characters. The builder writes the templates. flowchart-edges:flowchart TDplus exactlyedgesdistinct edges.- Every
```mermaidblock inexamples.md, plus the sourceinfo.
- A grown fixture that does not reach
readyat the top level is a harness defect. Fix its template. The one exception iserror:limit(output), a real finding: the characters cap is above whatoutputLimitallows. - Line probe: L0
flowchart LR\n A --> B\n B -->(error on editor line 3); L1 the same after a 3-line---frontmatter (editor line 6); L2 the same after%%{init: {}}%%(editor line 4). RecordPeleError.lineandcolumn. - Sanitize probe: run
mountwith a capturingsanitizethat keeps Pele's raw SVG string and the sanitized fragment. Use everyexamples.mdblock,info, andflowchart LR\n accTitle: Order flow\n accDescr: Two steps\n A --> B. In both trees, keep one count for each attribute name (id,style,class,role,aria-roledescription,aria-labelledby,aria-describedby), plus the<title>and<desc>element counts, and compare the root text. A mismatch is any single count that differs. It is a finding for the maintainer. Do not change the D8 config here.
Implementation steps
-
Step 1: Install the harness and prove the repo is untouched.
- Run
curl -s https://registry.npmjs.org/pele/latest | grep -o '"version":"[^"]*"'. If it is newer than1.6.0, use that version everywhere ([Tracking] Mermaid diagrams in docs.plus through a headless Pele extension #521 §Shared contract → Pele). H/package.json:{ "name": "mermaid-measure", "private": true, "type": "module", "dependencies": { "dompurify": "3.4.15", "pele": "1.6.0" } }.- First save
git -C REPO status --porcelainto<scratchpad>/status-before.txt. - Run
mkdir -p REPO/Notes/local-docs/mermaid-measure && cd REPO/Notes/local-docs/mermaid-measure && bun install, thencurl -fsS https://pele.run/examples.md -o examples.md. - Run
git -C REPO status --porcelainandgit -C REPO diff --stat bun.lock. Expected: the status matchesstatus-before.txt, and the diff prints nothing. Otherwise stop: Bun treatedHas a workspace member.
- Run
-
Step 2: Write
H/index.htmlandH/bench.ts.index.htmlholds<div id="host" style="width:min(720px,100%)">,<pre id="out">and the module script./bench.ts.bench.tsimports onlypele/lazyanddompurify, andexamples.mdas text:import examples from './examples.md' with { type: 'text' }. It has two modes:?mode=time&label=<label>and?mode=probe.- Time mode prints one line
label=<label> zoom=<zoom> host=<px> viewport=<px>, then a table| fixture | level | chars | edges | first ms | p50 | p95 | p99 | state |, thenDONE <label> <rows> rows.stateisreadyorerror:<code>. Printerror:limit(output)when the message starts with "Diagram output is longer". - Probe mode prints the line-probe table and the sanitize table (
fixture | state | mismatches | text equal), thenDONE probes.
-
Step 3: Run the laptop timing. Run
bun ./index.htmlinH. Open?mode=time&label=laptopin desktop Chrome, with no throttle and DevTools closed. Expected:DONE laptopandzoom=auto. -
Step 4: Run the phone proxy. In DevTools, choose device mode
Moto G Powerand CPU6× slowdown, then load?mode=time&label=6x. Expected:DONE 6x,zoom=false, and a viewport near 412 px. -
Step 5: Run the probes. Load
?mode=probewith no throttle. Expected:DONE probes, no sanitize mismatch, and everytext equaltrue. Record the actual values either way. -
Step 6: Apply the D10 rule.
- A ladder row passes when its 6x
p99 <= 200. A level passes when all its rows pass. charactersis the highest level where all seven grown fixtures pass.edgesis the highest level whereflowchart-edgespasses.- A failing
starteror example row is a finding. Lowering the limits does not fix it. - List every laptop p95 over 16 ms under "Spec gap: laptop gate scope". It never cuts the limits.
- If no level passes, stop and ask the maintainer. D10 rules out a worker.
- A ladder row passes when its 6x
-
Step 7: Write the measure note and post its verdict. Sections: Setup (machine, Chrome and Bun versions, the rules), Results (the two tables), D10 verdict (
DEFAULT_LIMITS = { characters: N, edges: M }and thestarterrow laptop p95), Probes, Findings, and "Phone confirmation: Pending maintainer." Give the maintainer the Setup, D10 verdict, Probes and Findings sections to post on this issue. -
Step 8: Review checkpoint and maintainer gate.
- Run
git -C REPO status --porcelain. Expected: it matchesstatus-before.txt. - The maintainer confirms on a real mid-range Android phone: serve with
bun ./index.html, runadb reverse tcp:<port> tcp:<port>, and openhttp://localhost:<port>/?mode=time&label=phonein Chrome. Apply the Step 6 rule to the phone rows. If the phone gives lower limits, the phone wins. - Stop. Scaffold @docs.plus/extension-mermaid with its types and the insertMermaid command #523 starts after the maintainer confirms the limits and rules on the laptop gap (R1).
- Run
Decisions
- R1. Do the phone numbers win when they give lower limits, and are laptop p95 rows over 16 ms findings only? Recommended: yes to both. The design names no consequence for the laptop number.
Acceptance criteria
- The posted comment holds the Setup, D10 verdict and Findings sections, with
NandM. - The maintainer has posted the phone confirmation and the R1 ruling on this issue.
git -C REPO status --porcelainmatchedstatus-before.txtat the checkpoint.
Activity
HMarzban commentedon Oct 19, 2026
Maintainer ruling, 2026-10-19
R1 is confirmed: phone numbers win when they give lower limits, and laptop p95 rows over 16 ms are findings only. The headless 6x CPU stand-in is accepted in place of Step 4. 2.2.0 does not ship until the Step 8 phone run confirms or lowers
characters: 15000andedges: 500.feat(extension-mermaid): add the Mermaid diagram extension
HMarzban commentedon Oct 21, 2026
Step 8 phone run, 2026-10-21: the limits come down to 10000 characters and 250 edges
The maintainer ran the harness on three devices over Wi-Fi (
bun ./index.html --hostname=0.0.0.0), withpele1.7.0, the version that ships. The harness now prints a Step 6 verdict box afterDONE. Each device finished all 92 rows. The table shows the worst p99 for each level (rule: p99 ≤ 200 ms).Bold cells fail. Under R1, the phone wins when it gives lower limits, so:
DEFAULT_LIMITS = { characters: 10000, edges: 250 }, down from the provisional 15000 / 500.starterrow and every example pass with a wide margin on all three devices.Next: lower
DEFAULT_LIMITSinextensions/extension-mermaid/src/constants.tsandLIMITSinapps/hocuspocus.server/src/modules/mcp/domain/checkDiagrams.tstogether, because they must match. Then update the tests and every doc that states 15000 or 500.HMarzban commentedon Oct 21, 2026
Live on production on 2026-10-21 (
5c28ca8b7, deploy tip282747489). The webapp bundle carriescharacters: 10000, edges: 250, and no bundle keeps the old 15000 / 500. The MCP check inrest-apicarriesLIMITS = { limit: 10000, maxEdges: 250 }, so the two still match. Docs, both CHANGELOGs and the extension README name the phone-measured numbers (docs/pele.md§Measured on real phones).