termstate-core 0.0.1

no_std, allocation-free parser, encoder and stream scanner for OSC 7501 (Program Status Protocol)
docs.rs failed to build termstate-core-0.0.1
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

termstate

A conformance-tested implementation of OSC 7501, the Program Status Protocol: a terminal escape sequence that lets a program tell the terminal whether it is idle, working, done, blocked on the user, or failed, and why.

ESC ] 7501 ; state=blocked:kind=permission:app=terraform:msg=QXBwbHkgMyB0byBhZGQsIDEgdG8gY2hhbmdlLCAwIHRvIGRlc3Ryb3k/ ESC \

Implements spec revision 0.4 (2026-10-10).

Crates

crate for
termstate-core both sides no_std, no allocation, no dependencies. Report builder and encoder, body parser, streaming scanner that extracts or strips sequences from terminal output, feature-detection query and reply.
termstate-store terminals, multiplexers no_std + alloc. The record set one terminal keeps: replacement, subtree clear, app inheritance, lifetime events, 64–256 record cap with LRU eviction.

Program side:

use termstate_core::{Report, State, Kind};

let report = Report::builder(State::Blocked)
    .kind(Kind::Permission)
    .app("terraform")
    .msg("Apply 3 to add, 1 to change, 0 to destroy?")
    .build()?;            // refuses anything a terminal would discard
print!("{report}");       // ESC ] 7501 ; ... ESC \

Terminal or multiplexer side:

use termstate_core::scan::{Event, Scanner};
use termstate_store::Store;

let mut scanner = Scanner::new();
let mut store = Store::new();
scanner.feed(&pty_output, |event| match event {
    Event::Passthrough(bytes) => forward(bytes),
    Event::Sequence { message: Ok(m), .. } => { store.apply(&m); }
    Event::Sequence { message: Err(_), .. } => {} // discarded, per spec
});

How conformance is verified

The goal is that every behaviour traces to a sentence in the spec, and that nothing drifts silently. Run everything with scripts/check.sh.

  1. Requirement trace. spec-tests/requirements.json quotes all 54 normative statements of the spec, verbatim, with ids. scripts/check-trace.py fails if a quote no longer appears in the live spec, or if a testable requirement has no covering test.
  2. Spec lock. scripts/fetch-spec.py hashes the live spec and compares it with spec-tests/SPEC.lock; CI runs it weekly.
  3. Language-neutral vectors. spec-tests/vectors: 145 parse, 21 scan and 19 store cases in plain JSON, each naming the requirements it covers. Any implementation in any language can load them (format).
  4. Written interpretations. Where the spec leaves room, the choice is documented in docs/interpretations.md (INT-1…17) and the vectors that depend on it say so.
  5. Differential testing against Ghostty. tools/ghostty-diff/run.sh feeds every vector to libghostty-vt's parser (by the spec's author) and compares. At ghostty 0b1729e9: 0 unexpected differences on terminal-side cases.
  6. Properties. Scanner output does not depend on how input is split into reads; the parser never panics; every accepted report re-encodes to itself.
  7. Coverage floor. cargo llvm-cov must report ≥ 95% of lines.

Status

Pre-release (0.0.x). Planned: a std facade with tty output and feature detection, a termstate CLI, a C ABI for other languages, and WASM.

License

MIT or Apache-2.0, at your option.