Repository navigation
Stability candidate and 1.0 readiness criteria #335
Description
0.11 is the stability candidate. It is the release after which no promised public item of termlens changes incompatibly before 1.0. This issue holds the criteria 1.0 must meet, in one place, so they can be argued about here and then pinned. Proposed wording — finalise it in this issue, then pin.
What 0.11 ships
- Correctness first: Screen::unsupported() reports the four SGR parameters the attribute shadow implements #320 (
unsupported()names sequences the attribute shadow implements) — can ship as a 0.10.x patch before the candidate. - The contract: STABILITY.md exempts every feature-gated API from termlens's own versioning, which is not a promise a consumer can use #328 (what is promised, in which feature configurations, and what is not), The JSON a Screen serialises to has no format version, yet termlens diff and render read it back as a saved artifact #329 (a versioned JSON shape).
- Enforcement that can fail: The semver gate runs zero checks on a 0.x minor bump, and --all-features cannot see an item moved behind a feature #323 (a forced, per-configuration semver gate on every PR, with a self-test), cargo test --workspace --no-default-features still enables serde through termlens-cli, so the minimal library build is never tested #324 (feature tests isolated from workspace unification), The MSRV job checks the default feature set only, so decode, regex and serde have no evidence at 1.85 #325 (MSRV over every feature), install.yml verifies cargo add termlens but never cargo install termlens-cli, so a broken CLI publish is invisible until a user hits it #326 (the published CLI installed and exercised), No test pins that a 0.10 saved screen still parses: the text format, the styles block and the JSON have no frozen corpus #327 (a frozen saved-screen corpus).
- The one breaking change: Screen::unsupported() exposes its Arc<str> storage and keeps the overflow count in a second accessor #330 —
Screen::unsupported()returns a view instead of&[Arc<str>]. - Additive API: Screen::cursor() packs visibility into a positional bool; add cursor_visible() #331
cursor_visible(), ScreenWithStyles is returned by Screen::with_styles but cannot be named outside the crate #332ScreenWithStylesre-export, Location has no accessors, so asking "screen or history?" needs a match #306, ScreenDiff computes which rows and style runs changed, then exposes neither #308, Color has no Display, so its snapshot token is hand-built at the one place that writes it #315. - Documentation and process: README: link Ratatui's snapshot-testing recipe, and say what the renderers are for #333, RELEASING, CONTRIBUTING, CHANGELOG and the skill need the stability-candidate process #334.
Everything else in the tracker is welcome and unaffected by the freeze: additive flags, fixture hygiene, docs polish.
What "frozen" means
From 0.11.0, the items docs/STABILITY.md names as promised do not change incompatibly. A change that must break one ships as a new candidate (0.12.0) with a migration table, and restarts the observation window below. A patch release does not restart it. The _for wait methods, wait_idle, Recording, frame_timings and the renderers stay as they are.
1.0 readiness criteria
1.0 is tagged when all of the following hold, and not before. No calendar date is a criterion.
- Contract published — STABILITY.md states the promised API, feature configurations, formats, CLI contract, platform limits and MSRV policy, and every statement names the job or test that checks it.
- Enforcement proven — the PR gate has been seen to fail on a deliberate break and on an item hidden behind a feature (the self-test), and has been green on every PR since the candidate.
- Consumers migrated — every maintained downstream repository depends on the candidate from crates.io (no path or patch overrides) and has been green on it for the whole observation window.
- An external pilot — at least one repository the maintainer does not own has tests on a published release candidate (
1.0.0-rc.N) covering one real user flow, has caught at least one regression with them, and has run them in its own CI repeatedly. Internal green runs do not count. - No known correctness blocker in the promised scope.
- Eight weeks of stable pilot usage, counted from the external pilot's first green run on the candidate — not from the candidate's publication date. Recorded here: pilot start, installed version, consumer SHA, runs, blockers, upgrades.
- Fresh-install evidence — the daily
install.yml(library and CLI) green throughout the window.
If a criterion cannot be met, this issue records why, and 0.11 stays the current release. A candidate that is never promoted is a smaller failure than a 1.0 that is broken by 1.1.
Activity
vyncint commentedon Sep 10, 2026
The candidate is published. termlens 0.11.0 and termlens-cli 0.11.0 went to crates.io on 2026-09-10T21:35Z from tag
v0.11.0=b8af0fa, through Trusted Publishing (release run 34532570645: tag check, full CI, semver gate, publish, GitHub Release, and the registry-consumer check — library in both feature shapes and the installed CLI — all green on Linux and macOS). The stress workflow ran on that exact commit first: 100 iterations per OS across five--test-threadsshards, Linux, macOS and Windows, all green (34530320216).What 0.11 shipped, against the list above. Correctness: #320 (in 0.10.2). The contract: #328, #329. Enforcement that can fail: #323 (with its self-test, and seen to fail on a real removal), #324, #325, #326, #327 (a 0.10.1 corpus written by the published 0.10.1, and a 0.11.0 one frozen from the release tree). The one breaking change: #330, landed alone under the
breakinglabel with the diagnostics pasted and the migration table in the CHANGELOG. Additive API: #331, #332, #306, #308, #315. Documentation and process: #333, #334. Plus #340, found while writing the CLI contract:inspect's stdout is now a saved screen. The docs carry the criteria above indocs/STABILITY.md, and the README, CHANGELOG header and STABILITY state the candidate in the same words, checked by CI.What the freeze now means in practice. Every pull request is compared against the published 0.11.0 (once #350 moves the baseline) with the release type forced to
patchin three feature views; the gate has been seen to fail on a removed item and on a gated one. One known blind spot, recorded in STABILITY.md: a method re-typed under the same name is not a lint cargo-semver-checks 0.50.0 has — it reported only the removedunsupported_overflow()beside #330's return-type change.For the criteria. 1 (contract published) and 2 (enforcement proven to fail; green on every PR since — from now) hold as of this release. 3 (consumers migrated) starts now: six maintained repositories move to
termlens = "0.11"from crates.io, never a path. 4–7 begin when an external pilot's first green run is recorded here; nothing here sets a date.vyncint commentedon Sep 11, 2026
Criterion 3 — consumers migrated — is met. All six maintained repositories now depend on
termlens = "0.11"from crates.io (never a path or[patch.crates-io]), each merged with its own CI green:survey_0_11.rsThe observation window for criterion 6 does not start here — it starts at an external pilot's first green run, and there is no pilot yet.
What the migration measured, which is the point of doing it rather than asserting the table was right.
The
unsupported()break was mechanical everywhere, and in every repository it made the suite stricter rather than merely different: the pinned list and "the record is not truncated" collapse into one assertion, because the view is equal to a slice only when the retained shapes match and nothing overflowed. Five repositories deleted aVec<String>helper that existed only to make the old comparison typecheck.Two things were found by a test failing, both in termlens-demo, which is what that tier is for. Its
unsupported()probe pinned #320 as a defect and said in the test body that it should invert once fixed — against 0.11.0 it does, so the fix is now confirmed from outside against a real ratatui application. And its CLI test assertedinspect's trailer was in stdout, which #340 moved to stderr; it now asserts the split and the round trip.One gap in my own verification, worth recording. Five repositories keep their
termlens-clitests#[ignore]d because they install from crates.io, so a localcargo test— and reconverge'sjust ci, which says so explicitly — skips them. All five asserted the trailer from stdout and all five went red on CI after a locally green run. The#[ignore]is right (a local gate should not reach the network), but it means "green locally" is not evidence about the CLI contract in these repositories, and the next termlens CLI change will meet the same shape.A follow-up available but not taken:
cursor().2still appears at ten call sites across five repositories wherecursor_visible()would read better. It compiles identically, so mixing that sweep into a dependency bump would only have made the diffs harder to review.vyncint commentedon Sep 16, 2026
Criteria checked against
main, 2026-09-16A status pass over the seven criteria, since this issue is where the
evidence is supposed to live. Nothing here changes the criteria — the
wording above is unchanged and the issue stays open, which is what it says
it should do until criterion 4 is met.
docs/STABILITY.md§§1–3 each carry a decision and the measurement behind it (Windows #149, backend #150, styled history #146); "What the promise covers" splits Promised / Third-party boundaries / Not promisedtools/semver-gate-selftest/run.shpasses today, 7/7, asserting both holes as holes — including "forced minor still fails on 0.x", which is why the gate usesmajor. It runs in ci.yml'ssemverjob, andci.ymlonmainis green on every run since the candidatetermlens = "0.11"from crates.io, no path or patch override anywhere: reconverge (×2 manifests), mossaic, launchbound, oxmera, oxidelake, termlens-demo1.0.0-rc.N. Nothing else is waiting on anythinginstall.ymlgreen every day 2026-09-11 → 2026-09-16. The one failure in the window, 2026-09-10, predates the candidate's publication later that daySo: six of seven hold, and the seventh has not been started. Criterion 6
cannot begin until criterion 4 does, so the distance to 1.0 is measured
entirely by finding a pilot — at least eight weeks after that, and no
sooner. Worth saying plainly because five green boxes can read like
progress toward a date, and criterion 6 explicitly is not one.
The honest reading of criterion 4 is that it is the one criterion this
repository cannot satisfy by working harder on itself. It may be worth its
own issue: who the candidate pilots are, what "one real user flow" means for
them, and what termlens offers in return for the trouble.
vyncint commentedon Sep 16, 2026
Closing this as the discussion it was opened for, which has concluded:
the criteria were argued, finalised and pinned, and the status pass above
records each one against
main.Closed does not mean met. Reading it that way would be wrong, so for
anyone who arrives here from the README,
CHANGELOG.md,docs/STABILITY.mdor
docs/RELEASING.md:outside the maintainer's own has tests on a
1.0.0-rc.N. It is the onecriterion this project cannot satisfy by working harder on itself.
criterion 4 does. It counts from the pilot's first green run, not from
a calendar date and not from the candidate's publication.
above.
So termlens is not 1.0-ready, and the distance to 1.0 is still measured
entirely by finding a pilot plus at least eight weeks after that.
The criteria themselves are unchanged and remain readable here. 0.11.0 stays
the current release and the freeze it declares is unaffected: from 0.11.0 no
promised item changes incompatibly before 1.0, and a change that must break
one ships as a new candidate.
docs/RELEASING.mdstill names this issue as the gate for av1.0.0tag —"every criterion in #335 to be recorded as met, with its evidence". That
remains the rule; this issue is where the evidence is, closed or not.
docs: the 1.0 criteria in STABILITY.md, and the skill tables caught up
docs: the 1.0 criteria in STABILITY.md, and the skill tables caught up (
vyncint commentedon Sep 23, 2026
The seven criteria in this issue are now a section of
docs/STABILITY.md: The 1.0 readiness criteria (#472, landed in #492). They are copied word for word, except that "this issue" now reads "this section". The candidate statement in the README, the CHANGELOG and STABILITY.md, anddocs/RELEASING.md'sv1.0.0gate now link that section instead of this closed issue. That section is the live record, and a criterion is recorded as met there. Nothing about the criteria themselves changed: criterion 4, the external pilot, has still not started.docs: the 1.0 criteria in STABILITY.md, and the skill tables caught up (