Session-Engine Probe Memo#
myform.Session is the smallest reusable in-process boundary for a future repeated caller.
It is not a daemon, transport API, console command, background service, or LSP.
1. Decision#
Session(config=..., target_path=...) first normalizes every configuration input to MyformConfig, then takes an independent Pydantic deep snapshot that preserves model_fields_set.
That keeps nested compatibility mappings from aliasing callers while retaining the explicit-section provenance that drives non-fatal applicability warnings.
The public readers, writers, lowering registries, extension identities, and potentially stateful writer objects are process-global. One fail-fast process-wide guard therefore spans Session construction and extension registration plus every Session or disposable public conversion. Concurrent work must be serialized by the caller; creating separate concurrent Sessions is not safe advice. A failed setup or conversion releases the guard for a later sequential call.
The existing myform.convert* functions construct a disposable Session, so the typed reader -> degradation -> writer pipeline has one implementation.
convert, convert_with_report, and format preserve the public one-shot contract while accepting explicit per-call options.
2. Launcher-Side Harness Data#
Note
The following numbers are launcher-side harness data, not a portable performance guarantee.
They use the benchmark’s default MyST-to-Typst fixture with ten samples per path.
Both disposable workers exited with status 0.
Path |
Median latency |
Evidence |
|---|---|---|
Cold public conversion |
250.105 ms |
Launcher-side harness, 10 samples |
Warm public |
0.974 ms |
Launcher-side harness, 10 samples |
Warm |
0.436 ms |
Launcher-side harness, 10 samples |
Persistent stdio NDJSON |
0.531 ms |
Launcher-side harness, 10 samples; worker exit |
Disposable Unix-socket NDJSON |
0.579 ms |
Launcher-side harness, 10 samples; worker exit |
2.1. Earlier Card-Ledger Baseline#
import myformmeasured 1.254 ms median and 1.448 ms p95 across 20 fresh interpreter samples.A tiny MyST-to-Typst conversion measured 176.830 ms cold median and 185.011 ms p95 across 12 fresh interpreter samples.
The same tiny conversion measured 0.737 ms per warm in-process call across 200 calls.
The differing fixture, sample counts, and launch environment mean the ledger and launcher harness are complementary evidence, not a single comparable time series.
3. Disposable Measurement Boundary#
benchmarks/session_engine.py is an uninstalled developer probe, deliberately limited to Pydantic plus the standard library until a child actually imports myform.
Its normal invocation writes one JSON document to stdout and all diagnostics to stderr.
The stdio and Unix-domain-socket workers are child processes for one probe run, require explicit shutdown, and use bounded terminate/kill/reap cleanup.
uv run python benchmarks/session_engine.py --samples 5
Unix-socket support receives a direct bind preflight.
Only an EPERM preflight denial is an environment skip in tests; child startup, import, protocol, or worker failures remain ProbeError failures with bounded diagnostics.
After bind, the worker records the socket device/inode pair and unlinks only that same socket object, never a replacement pathname.
4. Decision Table#
Path |
Latency / evidence |
Lifecycle owner |
Authentication boundary |
Crash recovery |
Version handshake |
Fallback |
|---|---|---|---|---|---|---|
Direct |
Cold 250.105 ms; warm public 0.974 ms, launcher 10-sample data |
Caller / process |
Python call boundary |
Caller retries a new call |
Package version at install/import |
Direct call is the baseline |
In-process |
Warm 0.436 ms, launcher 10-sample data |
Caller / process; all calls serialize process-wide |
Python call boundary |
Failed call releases guard |
Package version at install/import |
Disposable |
Disposable stdio fixture |
0.531 ms, launcher 10-sample data; worker exit |
Benchmark parent |
Parent-owned stdin/stdout only |
Explicit shutdown, then bounded terminate/reap |
None: private same-run fixture |
Parent uses direct conversion |
Disposable Unix socket fixture |
0.579 ms, launcher 10-sample data; worker exit |
Benchmark parent |
Filesystem path in one temporary directory |
Explicit shutdown, bounded terminate/reap, identity-safe unlink |
None: private same-run fixture |
Parent uses direct conversion |
No fixture is a durable protocol.
No named caller currently exists.
A future opt-in service would need a versioned request/response handshake, compatible-version behavior, authenticated local ownership, restart policy, and direct myform.convert fallback before it could become product surface.
5. Daemon and LSP Gates#
Reopen daemon design only when one named external workflow trace satisfies every condition:
It records at least 100 conversions.
Its active bursts reach at least 10 requests per minute.
It demonstrates at least 50 ms median end-to-end benefit against process-per-call.
Lifecycle, authentication, versioning, and crash-recovery owners have explicitly accepted their responsibilities.
Any auto-start, service installation, socket persistence, network listener, or systemd unit remains a separate human-gated capability decision. This probe creates none.
An LSP remains gated on cross-format editor value not already supplied by Tinymist.
myform translates and formats across document formats; Tinymist is a Typst-native editor/LSP tool.
Neither role subsumes the other, and this probe implements no LSP request surface.
The accepted Rust/Python integration remains the typst-syntax parser bridge packaged through PyO3/Maturin as an ABI3 extension.
ttt remains unsuitable as a dependency because it is an unlicensed binary application, not a parser library.
Those are unchanged, already-landed decisions; this memo adds no live-source claim to them.
6. Problem space#
Probe terminus — No further daemon, LSP, listener, or service work belongs in this bounded probe.
⭐ Named external workflow trace — The keystone: capture the 100-conversion, 10-request/minute, 50-ms-benefit evidence before reopening daemon design.
Accepted operational owners — A future proposal needs explicit lifecycle, authentication, versioning, and crash-recovery ownership.
Cross-format editor value — Revisit LSP scope only if a concrete need exceeds Tinymist’s Typst-native remit.
No next steps — The current probe campaign is closed at its no-service terminus; a qualified external caller reopens the decision rather than extending this implementation.