Skip to content

Release 1.0.0: release-readiness review, API hardening, and pipeline gates - #26

Merged
StevenTCramer merged 22 commits into
masterfrom
dev
Jul 3, 2026
Merged

Release 1.0.0: release-readiness review, API hardening, and pipeline gates#26
StevenTCramer merged 22 commits into
masterfrom
dev

Conversation

@StevenTCramer

Copy link
Copy Markdown
Contributor

Promotes TimeWarp.Terminal from 1.0.0-beta.13 to the first stable release. Everything here came out of the full release-readiness code review (task 022: 4 blockers, 28 majors, all minors fixed or explicitly accepted), executed across 22 commits.

Breaking changes vs 1.0.0-beta.13

  • Format culture: Terminal.Write/WriteLine(format, ...) now use the current culture (Console parity) instead of InvariantCulture. Set Terminal.FormatProvider = CultureInfo.InvariantCulture to restore deterministic output.
  • ReadKey() moved from IConsole to ITerminal: key-by-key input is interactive-terminal functionality; TestConsole.ReadKey() (which threw NotSupportedException) and TimeWarpConsole.ReadKey() are removed.
  • Legacy conhost members removed from ITerminal/implementations/static facade: MoveBufferArea, CursorSize, SetWindowSize, SetWindowPosition, SetBufferSize, WindowLeft, WindowTop, LargestWindowWidth, LargestWindowHeight. WindowWidth/WindowHeight/BufferWidth/BufferHeight are now get-only on the interface and facade (TestTerminal keeps setters for test configuration). These APIs don't work in Windows Terminal, let alone elsewhere.
  • AnsiHyperlinks.CreateLink(url, displayText): parameter order aligned with WriteLink.
  • TableBuilder.Build() returns a snapshot: building twice yields independent tables; post-Build builder calls no longer mutate built tables.
  • ConsoleColor SGR mapping corrected: Dark* colors map to SGR 30-37 and normal colors to bright 90-97 (previously dark/normal collided) — visible output changes for ConsoleColor overload users.
  • WritePanel(string, string?) overload removed: it made Terminal.WritePanel("content") ambiguous (CS0121); the remaining overload accepts every previously-compilable call.

Highlights

  • Capability-aware output: ConsoleColor overloads, WritePanel/WriteTable colors, and WriteLink/WriteLinkLine degrade to plain text when SupportsColor/SupportsHyperlinks is false (honors non-empty NO_COLOR, TERM=dumb, redirection).
  • Parallel-safe testing: Terminal.Instance resolves through the async-local TestTerminalContext, making the documented parallel test isolation actually true; the context never mutates the global.
  • Rendering correctness: Unicode 16 EAW-accurate width measurement (decomposed accents, emoji blocks, Tangut/Kana), ANSI handling for all CSI/OSC/ESC forms, mid-word styling no longer breaks word wrap, ANSI-preserving cell truncation, panel WordWrap(false) truncates instead of breaking the border.
  • Security: OSC 8 hyperlink URLs are sanitized (C0/DEL percent-encoded), closing a terminal escape-injection vector.
  • Cross-platform fixes: CursorVisible/Title setters and Beep() now work on Linux/macOS (the BCL gates only their getters/overloads); KeyAvailable no longer throws under redirected stdin; platform contracts documented on every ITerminal member.
  • Test doubles match System.Console: Read/ReadLine/ReadKey share one input source, KeyAvailable reflects all key sources, QueueKey honors shift/ctrl.
  • Verified AOT compatibility: the library builds clean under full trim/AOT analysis (suppressions removed), proven by native AOT publish.
  • Release pipeline hardening: release runs verify samples + the full runfile test suite before packing, cross-check the release tag against the version, publish readme + snupkg symbols with deterministic builds, and dispatch a timewarp-software site rebuild via the org GitHub App after push.

Verification

Solution builds 0 warnings / 0 errors under the strict analyzer set, all 33 test runfiles pass, all 5 samples verify, ganda repo audit 19/19, native AOT publish succeeds.

🤖 Generated with Claude Code

StevenTCramer and others added 22 commits July 2, 2026 23:57
ganda repo audit flagged missing kanban stage directories, the memsearch
scaffold (config + git hooks), and VS Code window-title/icon configuration.
All generated by the audit fixer so the repo passes the compliance profile.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- TimeWarp.Nuru/DevCli 3.0.0-beta.71: audit requires latest Nuru; DevCli
  must match for the shared dev-cli endpoint sources
- TimeWarp.Jaribu 1.0.0-beta.13 + TimeWarp.Amuru 1.0.0-beta.34: Jaribu
  beta.12 pulled Amuru beta.21 -> StreamJsonRpc -> MessagePack 2.5.198,
  which trips NU1902/NU1903 vulnerability errors under
  TreatWarningsAsErrors; the new pair drops that chain
- TimeWarp.Build.Tasks 1.0.0: required by the assembly-metadata audit check
- TimeWarp.Builder 1.0.0-beta.3, NetAnalyzers 10.0.301: routine updates

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The analyzer bump surfaced ~250 latent style violations (IDE0011 braces,
IDE0072 switch exhaustiveness, formatting) that blocked every build,
which in turn blocked installing bin/dev. Fixes are mechanical with no
behavior change: braces added, switch arms named explicitly with
identical results, doc-comment XML repaired.

Root causes addressed alongside:
- .editorconfig had csharp_prefer_braces = when-multiline (hyphen), an
  invalid value the analyzer silently ignores; corrected to
  when_multiline:warning so the intended rule is actually enforced
- GenerateDocumentationFile is now on (required for IDE0005 to run on
  build) with CS1591 suppressed
- dev-cli suppresses style rules violated by TimeWarp.Nuru.DevCli content
  files compiled from the NuGet cache, where .editorconfig cannot reach
- runfiles (tests/samples/dev-cli) use the portable env -S shebang per
  audit policy and IDE0211 is suppressed for them since file-based apps
  require top-level statements

Verified: solution builds 0/0, all 33 test runfiles pass, all 5 samples
verify, ganda audit 19/19.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The integration itself landed in 1e930f5 but the card was never updated.
All checklist items verified working (self-install, clean, check-version
via the shared TimeWarp.Nuru.DevCli beta.71 endpoints); Results section
records where the final shape differs from the original plan.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 022

Full library review (25 files, ~7,200 lines) across six subsystem passes
ahead of the beta -> 1.0.0 move. 4 blockers (ambiguous WritePanel overload,
colored-rule crash on long titles, Unix CursorVisible no-op, prerelease
TimeWarp.Builder dependency), 22 majors, ~28 minors. Majors emphasize
behavioral contracts that become breaking changes once 1.0 ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Terminal.WritePanel("content") and Terminal.WritePanel("content", "header")
were CS0121 ambiguous for consumers because both the 2-param and 4-param
string overloads required default-argument substitution, so neither was
better. The library never noticed: no in-repo code used the positional
forms (tests used named args or the ITerminal extension) and the only
occurrence was an uncompiled XML doc example.

The surviving 4-param overload accepts every previously-compilable call,
so this is source-compatible. Adds a positional-call regression test whose
compilation is the assertion.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Rule.Render: colored rules with titles longer than the width crashed
  with ArgumentOutOfRangeException because the color branch recomputed
  the layout from scratch, bypassing the title-only fallback. Layout is
  now computed once and color only wraps the segments; width is clamped
  to >= 0. Regression test added.
- CursorVisible setter: was Windows-gated so hide/show cursor silently
  no-oped on Linux/macOS, but only the BCL getter is Windows-only.
  Gate removed; verified ESC[?25l/h are emitted under a Linux TTY.
- TimeWarp.Builder: bumped prerelease beta.3 to the stable 1.0.0 so a
  stable TimeWarp.Terminal 1.0.0 won't depend on a prerelease (NU5104).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Cross-platform gating corrected to match the BCL: Title setter and
parameterless Beep() work on Unix (only their getter/overload
counterparts are Windows-only), so their gates are removed.
KeyAvailable now also catches InvalidOperationException, which Console
throws under redirected stdin — the exact scenario its fallback exists
for.

Packaging: PackageReadmeFile so nuget.org renders the readme, snupkg
symbol packages, and ContinuousIntegrationBuild on release pack.

Pipeline: the test step (dev test and both workflow paths) ran
`dotnet test` against a solution with zero VSTest projects — a false
green. It now runs the tests/*.cs runfiles and fails on any failure,
and the release path gains verify-samples + test before pack, so 1.0
cannot publish from an untested commit.

README: removed nonexistent .Shrink() from the table quickstart.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Test doubles now match System.Console semantics: Read() consumes
constructor input when the key queue is empty (Read/ReadLine share one
stream), KeyAvailable reflects every source ReadKey draws from, QueueKey
honors shift/ctrl when computing KeyChar, and TestTerminalContext.
ClearCurrent can no longer leave a disposed TestTerminal installed as
the global Instance.

Table contracts: Grow columns floor at max(4, MinWidth) instead of
collapsing to zero width, Expand respects MaxWidth caps, AddRow(null)
throws eagerly, and TableBuilder.Build() returns an independent
snapshot so post-Build builder calls cannot mutate built tables.

Security/parity: OSC 8 hyperlink URLs are sanitized (C0 controls and
DEL percent-encoded) closing a terminal escape-injection vector, and
the static Terminal facade gains the CancelKeyPress event it was
missing relative to ITerminal and System.Console.

Docs: ITerminal members that platform-gate or swallow exceptions now
say so in <remarks> — the silent-failure contract ships documented
rather than frozen implicitly at 1.0.

All changes covered by new regression tests; full suite 33/33, samples
verified, audit 19/19.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The format overloads now use FormatProvider ?? CurrentCulture resolved
per call, matching System.Console (and TextWriter.FormatProvider
semantics) instead of silently diverging for code migrated from
Console. Null default resolves lazily so cultures set after startup
(or per-request) are honored; set InvariantCulture for deterministic
output. Typed IFormatProvider per BCL convention and placed on
Terminal itself for discoverability. TestTerminalContext snapshots
and restores it alongside Instance.

Release note: format overloads previously used InvariantCulture;
they now default to the current culture.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All ConsoleColor-parameter paths — facade Write/WriteLine/WriteErrorLine,
static WritePanel/WriteTable, and the ITerminal panel/table extension
helpers — now degrade to plain text when the terminal reports
SupportsColor false, so NO_COLOR and redirected output no longer receive
raw ANSI escapes. Caller-embedded ANSI (string.Cyan(), BorderColor)
remains the caller's choice.

Release note: colored overloads previously emitted ANSI unconditionally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Static Terminal.WriteLink now checks SupportsHyperlinks and writes the
plain display text when unsupported, matching the ITerminal extension —
same philosophy as the SupportsColor gating.

Terminal.Instance resolution is now "AsyncLocal wins": the getter
returns TestTerminalContext.Current ?? the process-global field, and
SetCurrent/Use/ClearCurrent only touch the AsyncLocal — the global is
never mutated by the context. This makes the documented parallel test
isolation actually true (regression test runs two concurrent Use
scopes), moots the disposed-terminal leak path, and keeps direct
Terminal.Instance assignment working for serial tests. FormatProvider
snapshot/restore is retained.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unicode width now follows Unicode 16 EAW/emoji-presentation data: the
blanket 0x1F000-0x1FAFF wide range made playing cards, alchemical
symbols, and dingbats wide and hid the regional-indicator branch, while
Tangut/Kana/Nushu and BMP tails were missing. Grapheme measurement no
longer hardcodes multi-rune clusters to 2, so NFD combining sequences
(decomposed accents) measure 1 while emoji ZWJ/VS16/flag/skin-tone
clusters stay 2.

ANSI handling covers real streams: the strip/measure regex now matches
all CSI finals, all OSC commands, and two-byte ESC sequences instead of
SGR-only; WrapText tokenizes on visible whitespace so mid-word styling
no longer splits words; hyperlink and SGR are independent wrap carry
channels (closed links stay closed across line breaks).

Truncation preserves styling: new ANSI/grapheme-aware TruncateVisible
helpers back both panel WordWrap(false) lines (which previously
overflowed through the right border) and table cell End/Start/Middle
truncation (which previously stripped all color). Style at a cut is
reset before ellipsis/border; style opened before a Start/Middle cut
replays onto the kept tail.

All behavior covered by new regression tests; suite 33/33, samples
verified, audit 19/19.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ReadKey() lived on IConsole, forcing stream-oriented implementations
like TestConsole to throw NotSupportedException in violation of the
interface docs. Key-by-key input is interactive-terminal functionality,
so it now lives on ITerminal alongside ReadKey(bool); TestConsole's
throwing member and TimeWarpConsole's implementation are removed, and a
contract test pins the placement. BREAKING for IConsole consumers that
called ReadKey (intentional pre-1.0 correction).

The IsAotCompatible claim is now verified rather than asserted: the
blanket IL* NoWarn is removed from the root props, the library builds
clean under full trim/AOT analysis, and the dev-cli native AOT publish
consuming it succeeds. IL suppressions remain only in tools/dev-cli,
scoped and documented, for TimeWarp.Nuru.DevCli package-content files
that use reflection-based JsonSerializer.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…5.6.0

No new diagnostics under the newer analyzer; full build, all 33 test
runfiles, sample verification, AOT dev-cli publish, and audit all green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Core terminal: NO_COLOR honored per spec (non-empty only) plus
TERM=dumb; GetCursorPosition atomic; TreatControlCAsInput follows the
swallow-and-default policy; IsInteractive requires both stdin and
stdout unredirected.

Color/facade: ConsoleColor SGR mapping corrected (Dark* = 30-37,
normal = bright 90-97 — visible-output release note); null colored
messages write plain per docs; symmetry overloads added (Write and
WriteErrorLine fg+bg, WriteLinkLine); CreateLink parameter order
aligned to (url, displayText) matching WriteLink (breaking vs beta);
widget fg/bg colors survive embedded resets from BorderColor/styled
cells; Pad*/Center clamp negative widths.

Test double: EOF sentinel, [CLEAR] marker, and single-threaded design
documented publicly; SimulateCancelKeyPress throws instead of silently
no-oping; Dispose no longer disposes consumer-assigned streams.

Pipeline/docs: release runs fail when the GitHub tag mismatches the
props version; PackageProjectUrl/RepositoryType/PackageReleaseNotes
added; README leads with TestTerminalContext; Expand/BorderStyle.None
interaction documented.

Five judgment items accepted with rationale on the card (Windows-legacy
interface members, unambiguous overload pairs, net10.0-only TFM,
Terminal type name, grow-overhead moot after earlier fix).

Task 022 checklist is now fully resolved: 4 blockers, 28 majors,
all minors fixed or explicitly accepted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All 56 findings resolved across 12 commits: 4 blockers and 28 majors
fixed, minors fixed or explicitly accepted with rationale recorded
per item. The library is release-ready pending the version bump to
1.0.0 and release notes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
MoveBufferArea, CursorSize, SetWindowSize, SetWindowPosition,
SetBufferSize, WindowLeft, WindowTop, LargestWindowWidth, and
LargestWindowHeight are legacy Windows console-host features that do
not work in Windows Terminal (the Windows 11 default) let alone
anywhere else. Keeping them in ITerminal would freeze a permanent stub
burden into the 1.0 contract for every implementer, for behavior
nobody can rely on. Removed from ITerminal, TimeWarpTerminal,
TestTerminal, and the static Terminal facade.

WindowWidth/WindowHeight/BufferWidth/BufferHeight are demoted to
get-only on the interface and facade (setters were conhost-only);
TestTerminal keeps public setters so tests can configure geometry.

Reverses the earlier "accepted for Console parity" triage decision on
task 022 — parity with APIs that no modern terminal honors is tech
debt, not compatibility. BREAKING vs beta (intentional, pre-1.0).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The 1.0 readiness sweep changed behavior the skill still described:
CreateLink's example used the pre-1.0 reversed argument order (it
would have linked to the URL "text"), MinWidth's property default is
null (4 is the layout floor, not the stored default), and the static
testing section taught the raw Instance swap instead of the
parallel-safe TestTerminalContext.Use scope.

Also documents the new surface: FormatProvider culture contract,
static WriteLinkLine, CancelKeyPress, fg+bg overload symmetry,
get-only geometry properties, ReadKey's home on ITerminal, and the
self-gating color/hyperlink behavior (pitfall updated to scope the
manual SupportsColor check to embedded ANSI only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
timewarp-software's rebuild.yml already listens for repository_dispatch
(type: rebuild) from library repos; this implements the sending side.
After a successful NuGet push, the release workflow fires the dispatch
with the package id and version in the client payload so the site
reflects the new release immediately instead of waiting for the
nightly cron.

Best effort by design: a dispatch failure warns but never fails a
release that already pushed, since the nightly rebuild is the
documented backstop.

Auth: locally gh's stored credentials suffice. In Actions the default
GITHUB_TOKEN cannot reach other repos (the recent Copilot-CLI PAT
changelog does not change this), so workflow.yml passes GH_TOKEN from
a REBUILD_DISPATCH_TOKEN secret — a fine-grained PAT or GitHub App
token with write access to timewarp-software; absent secret = skipped
dispatch with a warning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replaces the fine-grained-PAT secret approach with a short-lived
installation token minted per release run via
actions/create-github-app-token, using org-level configuration
(REBUILD_APP_ID variable + REBUILD_APP_PRIVATE_KEY secret) so every
library repo can adopt the same pattern unchanged. No long-lived PAT,
nothing expires annually, and the token is scoped to exactly
timewarp-software for the duration of one run.

The mint step is guarded: unconfigured app = skipped step = empty
GH_TOKEN = the dev-cli dispatch warns non-fatally and the nightly
rebuild remains the backstop.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
First stable release, following the full release-readiness review
(task 022). Release notes accompany the GitHub release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@StevenTCramer
StevenTCramer merged commit 93800ea into master Jul 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant