From d16e28e6c6085d029b438d56a71eb4453aa33602 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 12:37:44 -0400 Subject: [PATCH 01/23] docs: plan Icod.Terminal 1.18.0 rendition baseline --- Icod.Terminal-1.18.0-Development-Roadmap.md | 143 +++++++++++++++++ Icod.Terminal-Development-Roadmap.md | 38 ++++- .../2026-09-18-1.18.0-rendition-baseline.md | 151 ++++++++++++++++++ ...-09-18-1.18.0-rendition-baseline-design.md | 76 +++++++++ 4 files changed, 407 insertions(+), 1 deletion(-) create mode 100644 Icod.Terminal-1.18.0-Development-Roadmap.md create mode 100644 docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md create mode 100644 docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md new file mode 100644 index 000000000..4e988ebf7 --- /dev/null +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -0,0 +1,143 @@ +# Icod.Terminal 1.18.0 Development Roadmap + +**Release:** `1.18.0` +**Theme:** Unknown-rendition baseline recovery for Terminal-only screen consumers +**Status:** Planning approved; implementation not started +**Planned development identity:** `1.18.0-alpha.1` +**Stable compatibility floor:** `1.0.0` +**Current published line:** `1.17.0`; `1.17.1` is the documentation-only patch baseline + +## Release objective + +Version 1.18 closes the single Terminal contract gap found by the `Icod.DCurses 2.0` decoupling readiness gate. A renderer that does not know the terminal's physical rendition state must be able to request one safe, opaque plan that restores every rendition axis Terminal itself could have changed. + +The governing rule is: + +> Terminal may claim a rendition baseline only when it can unconditionally restore every rendition axis reachable through its own semantic planner; otherwise no plan is available. + +The dependency direction remains: + +```text +Icod.DCurses -> Icod.Terminal -> Icod.TermInfo +``` + +`Icod.TermInfo` remains Terminal's private capability-data, expansion, padding, and cost authority. No TermInfo type, capability identifier, terminal string, or expansion program enters the new public contract. + +## Design and implementation authorities + +- [`docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md`](docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md) +- [`docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md`](docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md) +- [`Icod.DCurses 2.0 PR #32`](https://github.com/uniblab/Icod.DCurses/pull/32) + +## Public contract + +Add one method to the existing session-bound planner: + +```csharp +public TerminalScreenOperationPlan? PlanRenditionBaseline(); +``` + +The method represents an unknown physical rendition, not a known current `TerminalScreenRendition`. It is side-effect free and returns an opaque `Rendition` plan owned by the same planner/session. + +The returned plan: + +- restores all enterable text attributes to their normalized default through a global reset or a complete stable sequence of safe specific exits; +- restores foreground and background colors to terminal defaults whenever Terminal can select colors; +- emits the global attribute reset before original-color restoration; +- includes capability expansion, padding, exact encoded-byte cost, and affected-line accounting; +- remains valid only through the existing same-session transaction ownership rules. + +The method returns `null` if any Terminal-reachable rendition axis cannot be restored safely from an unknown state. A valid zero-byte plan is allowed only when the planner cannot change any rendition axis at all. + +Existing `PlanRenditionTransition(...)`, `PlanRenditionReset(current)`, normalization, operation-plan opacity, and transaction semantics remain unchanged. + +## Tranche sequence + +```text +T180 contract freeze, reference snapshot, API-regret gate, and alpha identity +T181 unknown-state rendition-baseline planner and focused red-green tests +T182 capability ordering, padding, cost, zero-byte, and nullability hardening +T183 session ownership, transaction commitment, and adversarial qualification +T184 package/API/XML/docs and DCurses 2.0 package-only acceptance +T185 release-candidate qualification and stable 1.18.0 closure +``` + +## T180 — Contract freeze and development identity + +Record the 1.17.1 baseline, establish `1.18.0-alpha.1`, and freeze the single additive public signature and its XML contract before implementation. + +Acceptance requires: + +- no change to existing 1.x signatures or behavior; +- no raw TermInfo or escape-sequence exposure; +- explicit distinction between unknown physical state and known-current reset; +- exact safety, nullability, ordering, ownership, and zero-byte rules; +- a package-level failing witness matching the DCurses T2001 blocker. + +## T181 — Baseline planner + +Implement `PlanRenditionBaseline()` in `TerminalScreenPlanner` using existing private capability interpretation and opaque plan construction. + +Acceptance covers: + +- attribute-only profiles using global attribute reset; +- color-only profiles using original-color-pair restoration; +- combined profiles emitting attribute reset before color restoration; +- correct `Rendition` kind and one affected line; +- side-effect-free planning followed by exact-byte transaction commitment. + +## T182 — Safety and cost hardening + +Prove that availability describes unconditional recovery rather than a best-effort partial reset. + +Acceptance covers: + +- `null` when any enterable attribute lacks both a global reset and a safe specific exit; +- `null` when foreground or background selection is available without original-color-pair restoration; +- a zero-byte plan only for a profile with no Terminal-reachable rendition changes; +- exact padding-sensitive byte cost after capability expansion; +- deterministic segment ordering, overflow safety, and repeated-call stability; +- no dependence on a caller-supplied or retained physical rendition state. + +## T183 — Ownership and transaction qualification + +Exercise the new plan through the existing transaction boundary without inventing another output or state domain. + +Acceptance covers same-session commitment, foreign-session rejection, stale-epoch rejection, cancellation before commitment, serialized output, and unchanged cleanup/failure behavior. Planning itself must perform no writes, flushes, epoch changes, or lifecycle mutation. + +## T184 — Package and downstream acceptance + +Freeze identical public API/XML snapshots for `net8.0`, `net9.0`, and `net10.0`; update package smoke and the future-DCurses screen-contract consumer; and compile the DCurses T2001 package-only witness against the candidate package without a direct `Icod.TermInfo` reference. + +Published `Icod.DCurses 1.6.0` compatibility remains a separate required witness. Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless an independently justified qualification issue requires a later reviewed change. + +## T185 — Stable closure + +Complete cross-platform, package, public-API, XML-documentation, dependency, security, and exact-head artifact qualification. Synchronize README, changelog, compatibility authority, release notes, and the main roadmap. + +Tagging, release creation, and publication remain explicit maintainer actions after the stable candidate is accepted. + +## Explicit non-goals + +Version 1.18 does not add: + +- retained physical rendition state or automatic state tracking; +- desired-versus-physical screen comparison or repaint policy; +- Terminal-owned cells, windows, layout, clipping, damage, or Unicode width; +- public raw capabilities, terminal strings, or generic control-sequence builders; +- a replacement for `PlanRenditionReset(current)`; +- transaction, output-epoch, hyperlink, raster, input, query, or lifecycle redesign; +- new raster-animation, PTY, process-hosting, or widget features; +- a production dependency update without separate evidence and review. + +## Release gates + +The stable candidate must pass: + +- focused red-green exact-byte rendition-baseline tests; +- the full unit and TermInfo-integration suites on all target frameworks; +- Windows, Linux, and macOS workflow matrices; +- package/public API/XML/license validation; +- fresh package-only screen-contract and DCurses 2.0 readiness consumers; +- published DCurses 1.6 compatibility and hardening; +- exact-head artifact and independent review gates. diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 945c4705e..8ac650a17 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -7,6 +7,7 @@ - **Current published feature line:** `1.17.0` — Terminal-owned Screen Output Contracts - **Current patch line:** `1.17.1` — Packaged README and release metadata correction - **Development status:** 1.17.0 published; 1.17.1 documentation-only patch in qualification +- **Active development target:** `1.18.0` — Unknown-rendition baseline recovery for Terminal-only screen consumers - **Stable compatibility floor:** `1.0.0` ## Purpose @@ -71,6 +72,7 @@ Optional integration tests/samples may use `Icod.TermInfo.Inspection 1.15.0`; In 1.16.0 persistent raster animation and frame lifecycle PUBLISHED 1.17.0 Terminal-owned dimensions, screen planning, and transactions PUBLISHED 1.17.1 packaged README and release metadata correction PATCH +1.18.0 unknown-rendition baseline recovery PLANNED ``` The final 1.17 public API fingerprint is: @@ -212,8 +214,42 @@ Authorities: - [`docs/superpowers/specs/2026-09-17-1.17.0-terminal-screen-output-design.md`](docs/superpowers/specs/2026-09-17-1.17.0-terminal-screen-output-design.md) - [`docs/superpowers/plans/2026-09-17-1.17.0-terminal-screen-output.md`](docs/superpowers/plans/2026-09-17-1.17.0-terminal-screen-output.md) +## 1.18 development line — Unknown-rendition Baseline Recovery + +Version 1.18 is a focused additive release that closes the sole blocking Terminal contract found by the DCurses 2.0 readiness gate. + +The governing rule is: + +> Terminal may claim a rendition baseline only when it can unconditionally restore every rendition axis exposed by the selected profile that Terminal can enter; otherwise no plan is available. + +The release adds one public planner method: + +```csharp +public TerminalScreenOperationPlan? PlanRenditionBaseline(); +``` + +The plan represents unknown physical rendition state, restores attributes before original colors in a safe deterministic order, retains Terminal-owned expansion/padding/cost and same-session transaction ownership, and returns a valid zero-byte plan only when no reachable rendition state exists. Existing known-state reset and transition behavior remains unchanged. + +The tranche sequence is: + +```text +T180 contract freeze, reference snapshot, API-regret gate, and alpha identity +T181 unknown-state rendition-baseline planner and focused red-green tests +T182 capability ordering, padding, cost, zero-byte, and nullability hardening +T183 session ownership, transaction commitment, and adversarial qualification +T184 package/API/XML/docs and DCurses 2.0 package-only acceptance +T185 release-candidate qualification and stable 1.18.0 closure +``` + +Authorities: + +- [`Icod.Terminal-1.18.0-Development-Roadmap.md`](Icod.Terminal-1.18.0-Development-Roadmap.md) +- [`docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md`](docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md) +- [`docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md`](docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md) +- [`Icod.DCurses 2.0 PR #32`](https://github.com/uniblab/Icod.DCurses/pull/32) + ## Later development candidates -After 1.17, independent candidates still include animation-frame composition, absolute screen-coordinate placement, pixel-within-cell positioning, richer terminal-side reconciliation only if a truthful non-destructive primitive exists, image-file decoding/transcoding, and PTY/ConPTY process hosting. +After 1.18, independent candidates still include animation-frame composition, absolute screen-coordinate placement, pixel-within-cell positioning, richer terminal-side reconciliation only if a truthful non-destructive primitive exists, image-file decoding/transcoding, and PTY/ConPTY process hosting. Scene/window/cell ownership and hidden source-raster replay caches remain intentionally outside the Terminal contract. diff --git a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md new file mode 100644 index 000000000..45a956a8d --- /dev/null +++ b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md @@ -0,0 +1,151 @@ +# Icod.Terminal 1.18.0 Rendition Baseline Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a safe, package-qualified `PlanRenditionBaseline()` operation that restores unknown physical rendition state and unblocks the TermInfo-free DCurses 2.0 renderer. + +**Architecture:** Extend the existing session-bound `TerminalScreenPlanner` with one additive method. Reuse private TermInfo interpretation, output-segment construction, opaque plan ownership, and transaction commitment; availability is conservative and all-or-nothing across Terminal-reachable attribute and color axes. + +**Tech Stack:** C# 13, .NET 8/9/10, xUnit, Icod.TermInfo 1.15.0, existing package/API/XML verification tools and GitHub Actions matrix. + +**Spec:** `docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md` + +## Global Constraints + +- Preserve the stable compatibility floor `1.0.0` and all existing 1.x signatures and behavior. +- Add exactly `public TerminalScreenOperationPlan? PlanRenditionBaseline();` to the production public surface. +- Return `null` rather than a partial plan when any Terminal-reachable rendition axis is not unconditionally restorable. +- Emit a global attribute reset or complete stable sequence of safe specific exits before original-color restoration. +- Permit a zero-byte plan only when Terminal cannot change any rendition axis. +- Keep planning side-effect free and reuse existing same-session transaction ownership. +- Expose no `Icod.TermInfo` type, capability identifier, terminal string, or expansion API. +- Keep production dependencies at `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless separately reviewed. +- Do not add retained screen/rendition state or unrelated feature families. + +--- + +### Task 1: Freeze the alpha contract and failing witnesses + +**Files:** +- Modify: `Directory.Build.props` +- Create: `docs/releases/1.18.0-alpha.1.md` +- Modify: `tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs` +- Modify: `tools/dcurses-screen-contracts-acceptance/Source/Program.cs` + +**Interfaces:** +- Consumes: the 1.17.1 `TerminalScreenPlanner` and opaque plan/transaction contracts. +- Produces: the frozen method signature and failing source/package witnesses used by later tasks. + +- [ ] **Step 1: Establish `1.18.0-alpha.1` in the centralized version properties and document that publication is not implied.** +- [ ] **Step 2: Add a focused test that calls `session.Screen.PlanRenditionBaseline()` on a profile containing `ExitAttributeMode = ""` and `OriginalColorPair = ""`.** +- [ ] **Step 3: Assert the plan is `Rendition`, costs 10 bytes, affects one line, performs no planning-time output, and commits ``.** +- [ ] **Step 4: Add the same API call to the future-DCurses package consumer and assert it compiles without a direct TermInfo reference.** +- [ ] **Step 5: Run the focused tests and package consumer; record the expected compile failure `CS1061` because the method is absent.** +- [ ] **Step 6: Commit the red witness and contract metadata as `test: freeze rendition baseline contract`.** + +### Task 2: Implement the minimal safe baseline planner + +**Files:** +- Modify: `src/Screen/TerminalScreenPlanner.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs` + +**Interfaces:** +- Consumes: existing private color/attribute capability evidence, `TryAddLiteral(...)`, and opaque plan construction. +- Produces: `public TerminalScreenOperationPlan? PlanRenditionBaseline()`. + +- [ ] **Step 1: Add private predicates that answer whether the selected profile exposes each enterable attribute and either color selector, using the same capability evidence as existing rendition normalization/planning.** +- [ ] **Step 2: Implement `PlanRenditionBaseline()` so it returns `null` when an enterable attribute has neither `ExitAttributeMode` coverage nor its unconditional specific exit, or when a selectable color axis lacks `OriginalColorPair`.** +- [ ] **Step 3: Build segments in fixed order: prefer one `ExitAttributeMode`; otherwise add every required safe specific exit in the existing attribute order; then add `OriginalColorPair`. Omit a class only when the selected profile exposes no enterable state in it.** +- [ ] **Step 4: Construct the existing opaque `Rendition` plan with `AffectedLines = 1`; return a valid empty-segment plan only when neither class is reachable.** +- [ ] **Step 5: Run the focused test and package witness; verify the red witness turns green and exact bytes are ``.** +- [ ] **Step 6: Run all screen-planner tests for `net8.0`, `net9.0`, and `net10.0`.** +- [ ] **Step 7: Commit as `feat: add rendition baseline planning`.** + +### Task 3: Harden availability, ordering, and cost + +**Files:** +- Modify: `tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs` +- Modify: `src/Screen/TerminalScreenPlanner.cs` only if a red test requires a correction + +**Interfaces:** +- Consumes: `PlanRenditionBaseline()` from Task 2. +- Produces: executable proof of the all-or-nothing safety invariant. + +- [ ] **Step 1: Add and run a failing attribute-only test expecting exactly ``.** +- [ ] **Step 2: Add and run a failing color-only test expecting exactly ``.** +- [ ] **Step 3: Add and run failing tests expecting `null` for an enterable attribute without either global or specific reset and for selectable colors without original-color restoration.** +- [ ] **Step 4: Add and run a failing no-rendition-capability test expecting a valid zero-byte `Rendition` plan.** +- [ ] **Step 5: Add a padding-bearing reset fixture and assert `ByteCount` counts emitted terminal bytes after terminfo padding is interpreted, while padding uses one affected line.** +- [ ] **Step 6: Add repeated-call assertions proving deterministic bytes/cost and no output, flush, epoch, or lifecycle mutation during planning.** +- [ ] **Step 7: Make only the minimum production corrections required by the red tests, then rerun the full rendition planner suite on all target frameworks.** +- [ ] **Step 8: Commit as `test: harden rendition baseline safety`.** + +### Task 4: Qualify ownership and transaction behavior + +**Files:** +- Modify: `tests/Icod.Terminal.Tests/src/Screen/TerminalScreenOutputTransactionHardeningTests.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs` + +**Interfaces:** +- Consumes: the existing opaque owner embedded in `TerminalScreenOperationPlan`. +- Produces: proof that baseline plans obey unchanged transaction rules. + +- [ ] **Step 1: Add a same-session transaction test that commits the baseline with adjacent application text and verifies serialized order.** +- [ ] **Step 2: Add a foreign-session test and verify validation rejects the complete transaction before any output.** +- [ ] **Step 3: Add stale-epoch and pre-commit cancellation cases and verify zero output.** +- [ ] **Step 4: Run transaction, rendition, lifecycle, and concurrency test groups on all target frameworks.** +- [ ] **Step 5: Commit as `test: qualify rendition baseline ownership`.** + +### Task 5: Freeze public API, XML, package, and downstream acceptance + +**Files:** +- Modify: generated public API snapshot files under `tests/Icod.Terminal.Tests/` +- Create: `docs/Public-API-Baseline-1.18.md` +- Create: `docs/Public-API-Baseline-1.18.sha256` +- Modify: `tools/package-smoke/Program.cs` +- Modify: `tools/dcurses-screen-contracts-acceptance/Source/Program.cs` +- Modify: `tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs` +- Modify: release/package workflow inputs only where existing 1.17 version assertions require 1.18 + +**Interfaces:** +- Consumes: the candidate package containing `PlanRenditionBaseline()`. +- Produces: identical API/XML snapshots and package-only downstream proof across `net8.0`, `net9.0`, and `net10.0`. + +- [ ] **Step 1: Generate public API snapshots for all target frameworks and verify their bytes are identical.** +- [ ] **Step 2: Record the 1.18 fingerprint and add XML documentation assertions for the new method's unknown-state and nullability semantics.** +- [ ] **Step 3: Update package smoke to plan and commit a baseline using only packaged Terminal public APIs.** +- [ ] **Step 4: Update the future-DCurses acceptance consumer to restore baseline before its first rendered output and assert no direct TermInfo compile/reference input.** +- [ ] **Step 5: Run the candidate-package consumer on all target frameworks and rerun published `Icod.DCurses 1.6.0` compatibility separately.** +- [ ] **Step 6: Run dependency coupling, package structure, license, XML, API, and artifact validators.** +- [ ] **Step 7: Commit as `test: qualify Terminal 1.18 package contracts`.** + +### Task 6: Close the stable 1.18.0 candidate + +**Files:** +- Modify: `Directory.Build.props` +- Modify: `README.md` +- Modify: `CHANGELOG.md` +- Modify: `Icod.Terminal-Development-Roadmap.md` +- Modify: `Icod.Terminal-1.18.0-Development-Roadmap.md` +- Create: `docs/releases/1.18.0.md` +- Modify: `docs/Architecture.md` +- Modify: `docs/Compatibility-and-Versioning.md` +- Modify: `docs/Security-and-Privacy.md` + +**Interfaces:** +- Consumes: the fully qualified 1.18 candidate and exact-head evidence. +- Produces: stable metadata and release documentation; publication remains a separate maintainer action. + +- [ ] **Step 1: Remove the prerelease suffix and synchronize all stable 1.18 metadata and documentation.** +- [ ] **Step 2: Run the complete unit, TermInfo integration, sample, hardening, package, downstream, API/XML, license, and artifact suites locally where supported.** +- [ ] **Step 3: Push the candidate and require every Windows, Linux, macOS, package, downstream, API/XML, and artifact workflow job to pass at the exact head.** +- [ ] **Step 4: Record exact commit, workflow/run, test-count, API-fingerprint, and package-hash evidence in the version roadmap and release note.** +- [ ] **Step 5: Request independent review and resolve every Critical or Important finding before promotion.** +- [ ] **Step 6: Commit the evidence-only closure update as `docs: record Terminal 1.18 qualification`.** +- [ ] **Step 7: Stop before merge, tag, GitHub release, or package publication; those remain explicit maintainer decisions.** + +## Self-review record + +- Every design requirement maps to Tasks 1-5; stable closure maps to Task 6. +- The public signature, nullability, ordering, zero-byte rule, dependency floors, and non-goals are consistent with the design specification. +- No placeholder implementation step or unbounded feature family remains. diff --git a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md new file mode 100644 index 000000000..75ec3fe92 --- /dev/null +++ b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md @@ -0,0 +1,76 @@ +# Icod.Terminal 1.18.0 Rendition Baseline Design + +**Release:** `1.18.0` +**Theme:** safe recovery from unknown physical rendition state +**Stable compatibility floor:** `1.0.0` +**Downstream objective:** unblock the TermInfo-free `Icod.DCurses 2.0` rendering path + +## Goal + +Add one Terminal-owned semantic operation that establishes a known default rendition when the caller cannot truthfully describe the terminal's current physical attributes or colors. + +This is the missing recovery primitive discovered by the DCurses T2001 readiness witness. The existing `PlanRenditionReset(current)` is intentionally insufficient because its safety and emitted bytes depend on a caller-supplied known current state. + +## Public API + +Add to `TerminalScreenPlanner`: + +```csharp +public TerminalScreenOperationPlan? PlanRenditionBaseline(); +``` + +No parameter is accepted because the source state is explicitly unknown. The return remains the existing opaque, session-bound `TerminalScreenOperationPlan`. + +## Safety invariant + +For the selected terminal profile, define a rendition axis as reachable when an existing Terminal planner can emit a plan that changes it from the normalized default. `PlanRenditionBaseline()` is available only if Terminal can restore every reachable rendition axis without knowing which axes are currently active. + +Consequences: + +- If any text attribute is enterable, it must have either global `ExitAttributeMode` coverage or an unconditional specific exit. When no global reset exists, Terminal emits every required specific exit in stable order so the result does not depend on which attributes were active. +- If either foreground or background color is selectable, `OriginalColorPair` is required. +- If both classes are reachable, the plan emits the global reset or complete specific-exit sequence first and original-color restoration second. +- If neither class is reachable, a valid zero-byte rendition plan is returned. +- If one or more reachable classes cannot be restored, the result is `null`; Terminal never returns a partial baseline. + +The conservative rule is based on Terminal-reachable operations, not on every string present in the underlying terminal description. Unsupported or intentionally excluded capability families do not create hidden public obligations. + +## Planning and commitment + +Planning is side-effect free. It performs no output, flush, output-epoch mutation, lifecycle mutation, or retained physical-state update. + +Terminal continues to own: + +- capability lookup and interpretation; +- terminfo parameter expansion and padding removal; +- output segment construction and exact byte cost; +- stable segment order and affected-line accounting; +- plan ownership and same-session validation. + +The plan uses `TerminalScreenOperationKind.Rendition` and `AffectedLines == 1`. Commitment uses the existing `TerminalScreenOutputTransaction`; foreign plans, stale transactions, cancellation, serialization, and failure behavior are unchanged. + +## Compatibility + +The change is additive and therefore a minor 1.x release. Existing `PlanRenditionTransition(...)`, `PlanRenditionReset(current)`, normalization, profile, operation-plan, and transaction behavior remain unchanged. The public signature contains no `Icod.TermInfo` type. + +Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless separate evidence justifies a reviewed dependency update. + +## Tests + +The implementation requires red-green coverage for: + +- combined `` ordering and exact byte count; +- attribute-only and color-only profiles; +- a profile with no reachable rendition axes returning a zero-byte plan; +- enterable attributes without global reset returning `null`; +- selectable colors without original-color restoration returning `null`; +- padding-sensitive cost and one affected line; +- repeated side-effect-free planning; +- same-session commit and foreign-session rejection; +- package API/XML snapshots across all target frameworks; +- package-only DCurses T2001 compilation without direct TermInfo; +- published DCurses 1.6 compatibility. + +## Non-goals + +This release does not track physical rendition state, infer an unknown state from prior writes, weaken operation-plan opacity, expose raw capabilities, redesign transactions, or move retained presentation policy into Terminal. It adds no unrelated feature family. From af1ba1fcf96a1814853bad6a6d1608b790af4684 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 12:43:49 -0400 Subject: [PATCH 02/23] docs: refine rendition baseline safety contract --- Icod.Terminal-1.18.0-Development-Roadmap.md | 14 +++++------ Icod.Terminal-Development-Roadmap.md | 2 +- .../2026-09-18-1.18.0-rendition-baseline.md | 25 ++++++++++--------- ...-09-18-1.18.0-rendition-baseline-design.md | 12 ++++----- 4 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md index 4e988ebf7..350dfc1a8 100644 --- a/Icod.Terminal-1.18.0-Development-Roadmap.md +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -9,11 +9,11 @@ ## Release objective -Version 1.18 closes the single Terminal contract gap found by the `Icod.DCurses 2.0` decoupling readiness gate. A renderer that does not know the terminal's physical rendition state must be able to request one safe, opaque plan that restores every rendition axis Terminal itself could have changed. +Version 1.18 closes the single Terminal contract gap found by the `Icod.DCurses 2.0` decoupling readiness gate. A renderer that does not know the terminal's physical rendition state must be able to request one safe, opaque plan that restores every rendition axis exposed by the selected profile's enter/select evidence. The governing rule is: -> Terminal may claim a rendition baseline only when it can unconditionally restore every rendition axis reachable through its own semantic planner; otherwise no plan is available. +> Terminal may claim a rendition baseline only when it can unconditionally restore every rendition axis exposed by the selected profile that Terminal can enter or select; otherwise no plan is available. The dependency direction remains: @@ -47,7 +47,7 @@ The returned plan: - includes capability expansion, padding, exact encoded-byte cost, and affected-line accounting; - remains valid only through the existing same-session transaction ownership rules. -The method returns `null` if any Terminal-reachable rendition axis cannot be restored safely from an unknown state. A valid zero-byte plan is allowed only when the planner cannot change any rendition axis at all. +The method returns `null` if any profile-exposed enterable/selectable rendition axis cannot be restored safely from an unknown state. This obligation is computed from entry/selection evidence before current normalization suppresses unsafe requests. A valid zero-byte plan is allowed only when the profile exposes no enterable attribute and no selectable color axis; reset capabilities alone do not create reachable state. Existing `PlanRenditionTransition(...)`, `PlanRenditionReset(current)`, normalization, operation-plan opacity, and transaction semantics remain unchanged. @@ -71,8 +71,8 @@ Acceptance requires: - no change to existing 1.x signatures or behavior; - no raw TermInfo or escape-sequence exposure; - explicit distinction between unknown physical state and known-current reset; -- exact safety, nullability, ordering, ownership, and zero-byte rules; -- a package-level failing witness matching the DCurses T2001 blocker. +- exact profile-evidence, safety, nullability, ordering, ownership, and zero-byte rules; +- a package-level failing witness matching the DCurses T2001 blocker, with representative enter/select capabilities added to its synthetic profile so the `` expectation is semantically reachable. ## T181 — Baseline planner @@ -94,7 +94,7 @@ Acceptance covers: - `null` when any enterable attribute lacks both a global reset and a safe specific exit; - `null` when foreground or background selection is available without original-color-pair restoration; -- a zero-byte plan only for a profile with no Terminal-reachable rendition changes; +- a zero-byte plan only for a profile with no enterable attribute and no selectable color axis, including a reset-only profile; - exact padding-sensitive byte cost after capability expansion; - deterministic segment ordering, overflow safety, and repeated-call stability; - no dependence on a caller-supplied or retained physical rendition state. @@ -107,7 +107,7 @@ Acceptance covers same-session commitment, foreign-session rejection, stale-epoc ## T184 — Package and downstream acceptance -Freeze identical public API/XML snapshots for `net8.0`, `net9.0`, and `net10.0`; update package smoke and the future-DCurses screen-contract consumer; and compile the DCurses T2001 package-only witness against the candidate package without a direct `Icod.TermInfo` reference. +Freeze identical public API/XML snapshots for `net8.0`, `net9.0`, and `net10.0`; update the active baseline selector, package smoke, and the future-DCurses screen-contract consumer; and compile the DCurses T2001 package-only witness against the candidate package without a direct `Icod.TermInfo` reference. The retained downstream witness's synthetic profile must add representative enter/select capabilities before it can truthfully expect ``; its public API call, transaction path, and exact-byte assertion remain unchanged. Published `Icod.DCurses 1.6.0` compatibility remains a separate required witness. Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless an independently justified qualification issue requires a later reviewed change. diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 8ac650a17..f74b8185c 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -228,7 +228,7 @@ The release adds one public planner method: public TerminalScreenOperationPlan? PlanRenditionBaseline(); ``` -The plan represents unknown physical rendition state, restores attributes before original colors in a safe deterministic order, retains Terminal-owned expansion/padding/cost and same-session transaction ownership, and returns a valid zero-byte plan only when no reachable rendition state exists. Existing known-state reset and transition behavior remains unchanged. +The plan represents unknown physical rendition state, restores attributes before original colors in a safe deterministic order, retains Terminal-owned expansion/padding/cost and same-session transaction ownership, and returns a valid zero-byte plan only when the selected profile exposes no enterable attribute and no selectable color axis. Existing known-state reset and transition behavior remains unchanged. The tranche sequence is: diff --git a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md index 45a956a8d..17d798c0b 100644 --- a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md +++ b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md @@ -16,7 +16,7 @@ - Add exactly `public TerminalScreenOperationPlan? PlanRenditionBaseline();` to the production public surface. - Return `null` rather than a partial plan when any Terminal-reachable rendition axis is not unconditionally restorable. - Emit a global attribute reset or complete stable sequence of safe specific exits before original-color restoration. -- Permit a zero-byte plan only when Terminal cannot change any rendition axis. +- Permit a zero-byte plan only when the selected profile exposes no enterable attribute and no selectable color axis; reset-only profiles are zero-byte. - Keep planning side-effect free and reuse existing same-session transaction ownership. - Expose no `Icod.TermInfo` type, capability identifier, terminal string, or expansion API. - Keep production dependencies at `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless separately reviewed. @@ -37,9 +37,9 @@ - Produces: the frozen method signature and failing source/package witnesses used by later tasks. - [ ] **Step 1: Establish `1.18.0-alpha.1` in the centralized version properties and document that publication is not implied.** -- [ ] **Step 2: Add a focused test that calls `session.Screen.PlanRenditionBaseline()` on a profile containing `ExitAttributeMode = ""` and `OriginalColorPair = ""`.** +- [ ] **Step 2: Add a focused test that calls `session.Screen.PlanRenditionBaseline()` on a profile containing `EnterBoldMode = ""`, `ExitAttributeMode = ""`, `Colors = 16`, foreground/background selectors, and `OriginalColorPair = ""`.** - [ ] **Step 3: Assert the plan is `Rendition`, costs 10 bytes, affects one line, performs no planning-time output, and commits ``.** -- [ ] **Step 4: Add the same API call to the future-DCurses package consumer and assert it compiles without a direct TermInfo reference.** +- [ ] **Step 4: Add the same API call to the future-DCurses package consumer and assert it compiles without a direct TermInfo reference; ensure its synthetic profile advertises representative entry/selection capabilities before expecting ``.** - [ ] **Step 5: Run the focused tests and package consumer; record the expected compile failure `CS1061` because the method is absent.** - [ ] **Step 6: Commit the red witness and contract metadata as `test: freeze rendition baseline contract`.** @@ -53,10 +53,10 @@ - Consumes: existing private color/attribute capability evidence, `TryAddLiteral(...)`, and opaque plan construction. - Produces: `public TerminalScreenOperationPlan? PlanRenditionBaseline()`. -- [ ] **Step 1: Add private predicates that answer whether the selected profile exposes each enterable attribute and either color selector, using the same capability evidence as existing rendition normalization/planning.** +- [ ] **Step 1: Add private predicates that answer whether the selected profile exposes each attribute entry and either color selector, using capability evidence before reversible normalization suppresses unsafe requests.** - [ ] **Step 2: Implement `PlanRenditionBaseline()` so it returns `null` when an enterable attribute has neither `ExitAttributeMode` coverage nor its unconditional specific exit, or when a selectable color axis lacks `OriginalColorPair`.** - [ ] **Step 3: Build segments in fixed order: prefer one `ExitAttributeMode`; otherwise add every required safe specific exit in the existing attribute order; then add `OriginalColorPair`. Omit a class only when the selected profile exposes no enterable state in it.** -- [ ] **Step 4: Construct the existing opaque `Rendition` plan with `AffectedLines = 1`; return a valid empty-segment plan only when neither class is reachable.** +- [ ] **Step 4: Construct the existing opaque `Rendition` plan with `AffectedLines = 1`; return a valid empty-segment plan only when neither class is exposed, including a reset-only profile.** - [ ] **Step 5: Run the focused test and package witness; verify the red witness turns green and exact bytes are ``.** - [ ] **Step 6: Run all screen-planner tests for `net8.0`, `net9.0`, and `net10.0`.** - [ ] **Step 7: Commit as `feat: add rendition baseline planning`.** @@ -74,7 +74,7 @@ - [ ] **Step 1: Add and run a failing attribute-only test expecting exactly ``.** - [ ] **Step 2: Add and run a failing color-only test expecting exactly ``.** - [ ] **Step 3: Add and run failing tests expecting `null` for an enterable attribute without either global or specific reset and for selectable colors without original-color restoration.** -- [ ] **Step 4: Add and run a failing no-rendition-capability test expecting a valid zero-byte `Rendition` plan.** +- [ ] **Step 4: Add and run failing empty-profile and reset-only-profile tests expecting a valid zero-byte `Rendition` plan.** - [ ] **Step 5: Add a padding-bearing reset fixture and assert `ByteCount` counts emitted terminal bytes after terminfo padding is interpreted, while padding uses one affected line.** - [ ] **Step 6: Add repeated-call assertions proving deterministic bytes/cost and no output, flush, epoch, or lifecycle mutation during planning.** - [ ] **Step 7: Make only the minimum production corrections required by the red tests, then rerun the full rendition planner suite on all target frameworks.** @@ -99,9 +99,9 @@ ### Task 5: Freeze public API, XML, package, and downstream acceptance **Files:** -- Modify: generated public API snapshot files under `tests/Icod.Terminal.Tests/` - Create: `docs/Public-API-Baseline-1.18.md` - Create: `docs/Public-API-Baseline-1.18.sha256` +- Modify: `packaging/VerifyPublicApiBaseline.ps1` - Modify: `tools/package-smoke/Program.cs` - Modify: `tools/dcurses-screen-contracts-acceptance/Source/Program.cs` - Modify: `tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs` @@ -111,13 +111,14 @@ - Consumes: the candidate package containing `PlanRenditionBaseline()`. - Produces: identical API/XML snapshots and package-only downstream proof across `net8.0`, `net9.0`, and `net10.0`. -- [ ] **Step 1: Generate public API snapshots for all target frameworks and verify their bytes are identical.** +- [ ] **Step 1: Generate transient public API snapshots for all target frameworks, verify their bytes are identical, and record the canonical result in the 1.18 baseline documents.** - [ ] **Step 2: Record the 1.18 fingerprint and add XML documentation assertions for the new method's unknown-state and nullability semantics.** - [ ] **Step 3: Update package smoke to plan and commit a baseline using only packaged Terminal public APIs.** -- [ ] **Step 4: Update the future-DCurses acceptance consumer to restore baseline before its first rendered output and assert no direct TermInfo compile/reference input.** -- [ ] **Step 5: Run the candidate-package consumer on all target frameworks and rerun published `Icod.DCurses 1.6.0` compatibility separately.** -- [ ] **Step 6: Run dependency coupling, package structure, license, XML, API, and artifact validators.** -- [ ] **Step 7: Commit as `test: qualify Terminal 1.18 package contracts`.** +- [ ] **Step 4: Update the future-DCurses acceptance consumer to restore baseline before its first rendered output and assert no direct TermInfo compile/reference input; include representative entry/selection capabilities in its synthetic profile.** +- [ ] **Step 5: Repoint `packaging/VerifyPublicApiBaseline.ps1` from the 1.17 baseline to the new 1.18 fingerprint and verify a deliberate mismatch fails the package gate.** +- [ ] **Step 6: Run the candidate-package consumer on all target frameworks and rerun published `Icod.DCurses 1.6.0` compatibility separately.** +- [ ] **Step 7: Run dependency coupling, package structure, license, XML, API, and artifact validators.** +- [ ] **Step 8: Commit as `test: qualify Terminal 1.18 package contracts`.** ### Task 6: Close the stable 1.18.0 candidate diff --git a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md index 75ec3fe92..bd0e25164 100644 --- a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md +++ b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md @@ -23,17 +23,17 @@ No parameter is accepted because the source state is explicitly unknown. The ret ## Safety invariant -For the selected terminal profile, define a rendition axis as reachable when an existing Terminal planner can emit a plan that changes it from the normalized default. `PlanRenditionBaseline()` is available only if Terminal can restore every reachable rendition axis without knowing which axes are currently active. +For the selected terminal profile, define a rendition axis as exposed when capability evidence says Terminal can enter that attribute or select that color axis. Compute this evidence before the current normalization path suppresses non-reversible requests. `PlanRenditionBaseline()` is available only if Terminal can restore every exposed axis without knowing which axes are currently active. Consequences: - If any text attribute is enterable, it must have either global `ExitAttributeMode` coverage or an unconditional specific exit. When no global reset exists, Terminal emits every required specific exit in stable order so the result does not depend on which attributes were active. - If either foreground or background color is selectable, `OriginalColorPair` is required. - If both classes are reachable, the plan emits the global reset or complete specific-exit sequence first and original-color restoration second. -- If neither class is reachable, a valid zero-byte rendition plan is returned. -- If one or more reachable classes cannot be restored, the result is `null`; Terminal never returns a partial baseline. +- If neither class is exposed, a valid zero-byte rendition plan is returned. Reset capabilities by themselves do not make state reachable. +- If one or more exposed classes cannot be restored, the result is `null`; Terminal never returns a partial baseline. -The conservative rule is based on Terminal-reachable operations, not on every string present in the underlying terminal description. Unsupported or intentionally excluded capability families do not create hidden public obligations. +The conservative rule is based on the same profile entry/selection evidence Terminal interprets for semantic screen capabilities, not on arbitrary strings present in the underlying terminal description. It deliberately does not use the existing reversible-normalization subset as its input: that would hide exposed but non-restorable state and incorrectly permit an empty baseline. ## Planning and commitment @@ -59,9 +59,9 @@ Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` un The implementation requires red-green coverage for: -- combined `` ordering and exact byte count; +- combined `` ordering and exact byte count on a profile that also advertises representative attribute entry and foreground/background selection; - attribute-only and color-only profiles; -- a profile with no reachable rendition axes returning a zero-byte plan; +- empty and reset-only profiles returning a zero-byte plan; - enterable attributes without global reset returning `null`; - selectable colors without original-color restoration returning `null`; - padding-sensitive cost and one affected line; From cfb6fcb9af7deee3b29e9f6eb3e3ffadd266b8a4 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 12:46:12 -0400 Subject: [PATCH 03/23] docs: separate local and downstream acceptance gates --- .../plans/2026-09-18-1.18.0-rendition-baseline.md | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md index 17d798c0b..c4d5d3452 100644 --- a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md +++ b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md @@ -39,7 +39,7 @@ - [ ] **Step 1: Establish `1.18.0-alpha.1` in the centralized version properties and document that publication is not implied.** - [ ] **Step 2: Add a focused test that calls `session.Screen.PlanRenditionBaseline()` on a profile containing `EnterBoldMode = ""`, `ExitAttributeMode = ""`, `Colors = 16`, foreground/background selectors, and `OriginalColorPair = ""`.** - [ ] **Step 3: Assert the plan is `Rendition`, costs 10 bytes, affects one line, performs no planning-time output, and commits ``.** -- [ ] **Step 4: Add the same API call to the future-DCurses package consumer and assert it compiles without a direct TermInfo reference; ensure its synthetic profile advertises representative entry/selection capabilities before expecting ``.** +- [ ] **Step 4: Add the same API call to the Terminal repository's future-DCurses package consumer and assert that the compile/package-only consumer still has no direct TermInfo compile or reference input.** - [ ] **Step 5: Run the focused tests and package consumer; record the expected compile failure `CS1061` because the method is absent.** - [ ] **Step 6: Commit the red witness and contract metadata as `test: freeze rendition baseline contract`.** @@ -106,6 +106,8 @@ - Modify: `tools/dcurses-screen-contracts-acceptance/Source/Program.cs` - Modify: `tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs` - Modify: release/package workflow inputs only where existing 1.17 version assertions require 1.18 +- External downstream modify: `uniblab/Icod.DCurses: tests/Icod.DCurses.Tests/Icod.DCurses.Tests.csproj` +- External downstream restore/modify: `uniblab/Icod.DCurses: tests/Icod.DCurses.Tests/src/TerminalScreenReadinessTests.cs` **Interfaces:** - Consumes: the candidate package containing `PlanRenditionBaseline()`. @@ -114,11 +116,12 @@ - [ ] **Step 1: Generate transient public API snapshots for all target frameworks, verify their bytes are identical, and record the canonical result in the 1.18 baseline documents.** - [ ] **Step 2: Record the 1.18 fingerprint and add XML documentation assertions for the new method's unknown-state and nullability semantics.** - [ ] **Step 3: Update package smoke to plan and commit a baseline using only packaged Terminal public APIs.** -- [ ] **Step 4: Update the future-DCurses acceptance consumer to restore baseline before its first rendered output and assert no direct TermInfo compile/reference input; include representative entry/selection capabilities in its synthetic profile.** -- [ ] **Step 5: Repoint `packaging/VerifyPublicApiBaseline.ps1` from the 1.17 baseline to the new 1.18 fingerprint and verify a deliberate mismatch fails the package gate.** -- [ ] **Step 6: Run the candidate-package consumer on all target frameworks and rerun published `Icod.DCurses 1.6.0` compatibility separately.** -- [ ] **Step 7: Run dependency coupling, package structure, license, XML, API, and artifact validators.** -- [ ] **Step 8: Commit as `test: qualify Terminal 1.18 package contracts`.** +- [ ] **Step 4: Update the Terminal repository's future-DCurses acceptance consumer to compile-bind `PlanRenditionBaseline()` while remaining a TermInfo-free compile/package-only witness.** +- [ ] **Step 5: In DCurses PR #32, restore `TerminalScreenReadinessTests.cs`, select the candidate/published 1.18 package as appropriate, and add representative attribute entry plus foreground/background selection capabilities through the already reviewed test-only TermInfo fixture; retain the same public method call, same-session transaction, and exact `` assertion.** +- [ ] **Step 6: Repoint `packaging/VerifyPublicApiBaseline.ps1` from the 1.17 baseline to the new 1.18 fingerprint and verify a deliberate mismatch fails the package gate.** +- [ ] **Step 7: Run the local candidate-package consumer and the external DCurses exact-byte witness on all target frameworks; rerun published `Icod.DCurses 1.6.0` compatibility separately.** +- [ ] **Step 8: Run dependency coupling, package structure, license, XML, API, and artifact validators.** +- [ ] **Step 9: Commit Terminal qualification as `test: qualify Terminal 1.18 package contracts`; preserve downstream evidence in its owning DCurses PR/commit.** ### Task 6: Close the stable 1.18.0 candidate From 4faf8db83f0e77990edd6b0ac65950bcd8e73cfc Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 13:22:06 -0400 Subject: [PATCH 04/23] docs: clarify downstream fixture boundary --- Icod.Terminal-1.18.0-Development-Roadmap.md | 2 +- .../specs/2026-09-18-1.18.0-rendition-baseline-design.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md index 350dfc1a8..f1cadfb25 100644 --- a/Icod.Terminal-1.18.0-Development-Roadmap.md +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -107,7 +107,7 @@ Acceptance covers same-session commitment, foreign-session rejection, stale-epoc ## T184 — Package and downstream acceptance -Freeze identical public API/XML snapshots for `net8.0`, `net9.0`, and `net10.0`; update the active baseline selector, package smoke, and the future-DCurses screen-contract consumer; and compile the DCurses T2001 package-only witness against the candidate package without a direct `Icod.TermInfo` reference. The retained downstream witness's synthetic profile must add representative enter/select capabilities before it can truthfully expect ``; its public API call, transaction path, and exact-byte assertion remain unchanged. +Freeze identical public API/XML snapshots for `net8.0`, `net9.0`, and `net10.0`; update the active baseline selector, package smoke, and the local future-DCurses compile/package consumer. That local consumer and the eventual DCurses production boundary must have no direct `Icod.TermInfo` reference. Separately, compile and run the DCurses T2001 exact-byte witness against the candidate package using its already reviewed test-only TermInfo fixture exception. The retained downstream witness's synthetic profile must add representative enter/select capabilities before it can truthfully expect ``; its public API call, transaction path, and exact-byte assertion remain unchanged. Published `Icod.DCurses 1.6.0` compatibility remains a separate required witness. Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0` unless an independently justified qualification issue requires a later reviewed change. diff --git a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md index bd0e25164..a71375d9b 100644 --- a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md +++ b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md @@ -68,7 +68,8 @@ The implementation requires red-green coverage for: - repeated side-effect-free planning; - same-session commit and foreign-session rejection; - package API/XML snapshots across all target frameworks; -- package-only DCurses T2001 compilation without direct TermInfo; +- a local future-DCurses compile/package consumer with no direct TermInfo input; +- the external DCurses T2001 exact-byte witness against the candidate package, permitting its documented test-only TermInfo fixture exception while keeping the production boundary Terminal-only; - published DCurses 1.6 compatibility. ## Non-goals From 1f9d67084ecb641ffcfc6d9f1760515790852e6e Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 13:28:50 -0400 Subject: [PATCH 05/23] docs: complete rendition baseline acceptance cases --- Icod.Terminal-1.18.0-Development-Roadmap.md | 2 ++ .../plans/2026-09-18-1.18.0-rendition-baseline.md | 15 ++++++++------- ...2026-09-18-1.18.0-rendition-baseline-design.md | 1 + 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md index f1cadfb25..a2d38e757 100644 --- a/Icod.Terminal-1.18.0-Development-Roadmap.md +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -81,6 +81,7 @@ Implement `PlanRenditionBaseline()` in `TerminalScreenPlanner` using existing pr Acceptance covers: - attribute-only profiles using global attribute reset; +- multi-attribute profiles without a global reset using every required safe specific exit in stable order; - color-only profiles using original-color-pair restoration; - combined profiles emitting attribute reset before color restoration; - correct `Rendition` kind and one affected line; @@ -93,6 +94,7 @@ Prove that availability describes unconditional recovery rather than a best-effo Acceptance covers: - `null` when any enterable attribute lacks both a global reset and a safe specific exit; +- complete specific-exit coverage and stable underline, standout, italic, then strikeout ordering when no global reset exists; - `null` when foreground or background selection is available without original-color-pair restoration; - a zero-byte plan only for a profile with no enterable attribute and no selectable color axis, including a reset-only profile; - exact padding-sensitive byte cost after capability expansion; diff --git a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md index c4d5d3452..2895d6e44 100644 --- a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md +++ b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md @@ -73,12 +73,13 @@ - [ ] **Step 1: Add and run a failing attribute-only test expecting exactly ``.** - [ ] **Step 2: Add and run a failing color-only test expecting exactly ``.** -- [ ] **Step 3: Add and run failing tests expecting `null` for an enterable attribute without either global or specific reset and for selectable colors without original-color restoration.** -- [ ] **Step 4: Add and run failing empty-profile and reset-only-profile tests expecting a valid zero-byte `Rendition` plan.** -- [ ] **Step 5: Add a padding-bearing reset fixture and assert `ByteCount` counts emitted terminal bytes after terminfo padding is interpreted, while padding uses one affected line.** -- [ ] **Step 6: Add repeated-call assertions proving deterministic bytes/cost and no output, flush, epoch, or lifecycle mutation during planning.** -- [ ] **Step 7: Make only the minimum production corrections required by the red tests, then rerun the full rendition planner suite on all target frameworks.** -- [ ] **Step 8: Commit as `test: harden rendition baseline safety`.** +- [ ] **Step 3: Add and run a failing multi-attribute test with no `ExitAttributeMode`, entry/exit pairs for underline, standout, italic, and strikeout, and assert the baseline emits every specific exit exactly once in stable underline, standout, italic, strikeout order (for example ``).** +- [ ] **Step 4: Add and run failing tests expecting `null` for an enterable attribute without either global or specific reset and for selectable colors without original-color restoration.** +- [ ] **Step 5: Add and run failing empty-profile and reset-only-profile tests expecting a valid zero-byte `Rendition` plan.** +- [ ] **Step 6: Add a padding-bearing reset fixture and assert `ByteCount` counts emitted terminal bytes after terminfo padding is interpreted, while padding uses one affected line.** +- [ ] **Step 7: Add repeated-call assertions proving deterministic bytes/cost and no output, flush, epoch, or lifecycle mutation during planning.** +- [ ] **Step 8: Make only the minimum production corrections required by the red tests, then rerun the full rendition planner suite on all target frameworks.** +- [ ] **Step 9: Commit as `test: harden rendition baseline safety`.** ### Task 4: Qualify ownership and transaction behavior @@ -117,7 +118,7 @@ - [ ] **Step 2: Record the 1.18 fingerprint and add XML documentation assertions for the new method's unknown-state and nullability semantics.** - [ ] **Step 3: Update package smoke to plan and commit a baseline using only packaged Terminal public APIs.** - [ ] **Step 4: Update the Terminal repository's future-DCurses acceptance consumer to compile-bind `PlanRenditionBaseline()` while remaining a TermInfo-free compile/package-only witness.** -- [ ] **Step 5: In DCurses PR #32, restore `TerminalScreenReadinessTests.cs`, select the candidate/published 1.18 package as appropriate, and add representative attribute entry plus foreground/background selection capabilities through the already reviewed test-only TermInfo fixture; retain the same public method call, same-session transaction, and exact `` assertion.** +- [ ] **Step 5: In DCurses PR #32, restore `TerminalScreenReadinessTests.cs`, select the candidate/published 1.18 package as appropriate, and add representative attribute entry, `NumericCapability.Colors = 16`, and foreground/background selection capabilities through the already reviewed test-only TermInfo fixture; retain the same public method call, same-session transaction, and exact `` assertion.** - [ ] **Step 6: Repoint `packaging/VerifyPublicApiBaseline.ps1` from the 1.17 baseline to the new 1.18 fingerprint and verify a deliberate mismatch fails the package gate.** - [ ] **Step 7: Run the local candidate-package consumer and the external DCurses exact-byte witness on all target frameworks; rerun published `Icod.DCurses 1.6.0` compatibility separately.** - [ ] **Step 8: Run dependency coupling, package structure, license, XML, API, and artifact validators.** diff --git a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md index a71375d9b..f982bd1a7 100644 --- a/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md +++ b/docs/superpowers/specs/2026-09-18-1.18.0-rendition-baseline-design.md @@ -61,6 +61,7 @@ The implementation requires red-green coverage for: - combined `` ordering and exact byte count on a profile that also advertises representative attribute entry and foreground/background selection; - attribute-only and color-only profiles; +- a multi-attribute profile with no global reset emitting the complete stable specific-exit sequence; - empty and reset-only profiles returning a zero-byte plan; - enterable attributes without global reset returning `null`; - selectable colors without original-color restoration returning `null`; From 53dadc26d69e20b8e681dc3cb940fa0e84b5373b Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 13:53:40 -0400 Subject: [PATCH 06/23] test: freeze rendition baseline contract --- Directory.Build.props | 4 ++-- docs/releases/1.18.0-alpha.1.md | 15 ++++++++++++ ...nalScreenRenditionPlannerHardeningTests.cs | 24 +++++++++++++++++++ .../Source/Program.cs | 2 ++ 4 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 docs/releases/1.18.0-alpha.1.md diff --git a/Directory.Build.props b/Directory.Build.props index b8f8f90a5..736acefe7 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,8 +1,8 @@ - 1.17.1 - + 1.18.0 + alpha.1 $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix) $(Version) diff --git a/docs/releases/1.18.0-alpha.1.md b/docs/releases/1.18.0-alpha.1.md new file mode 100644 index 000000000..7c48eacc0 --- /dev/null +++ b/docs/releases/1.18.0-alpha.1.md @@ -0,0 +1,15 @@ +# Icod.Terminal 1.18.0-alpha.1 + +## Development identity + +`Icod.Terminal 1.18.0-alpha.1` is an unpublished development identity. It adds no accepted implementation yet. + +## Frozen additive signature + +This RED checkpoint freezes the additive signature for the upcoming rendition-baseline planner: + +```csharp +public TerminalScreenOperationPlan? PlanRenditionBaseline(); +``` + +The signature is intentionally witnessed by the in-repository screen test and the TermInfo-free future DCurses renderer. Production implementation is deliberately deferred. diff --git a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs index dcd5621ea..af3bcc570 100644 --- a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs +++ b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs @@ -27,6 +27,30 @@ namespace Icod.Terminal.Tests.Screen; /// Hardens rendition normalization, transition, and reset planning. public sealed class TerminalScreenRenditionPlannerHardeningTests { + [Fact] + public async Task RenditionBaselinePlanIsOpaqueCostedAndExactlyEmittable() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "rendition-baseline" ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.EnterBoldMode, "" ) + .SetString( StringCapability.ExitAttributeMode, "" ) + .SetString( StringCapability.SetForegroundColor, "" ) + .SetString( StringCapability.SetBackgroundColor, "" ) + .SetString( StringCapability.OriginalColorPair, "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + Assert.Empty( output.Bytes ); + AssertRenditionPlan( plan, 10 ); + + await CommitAsync( session, plan ); + + Assert.Equal( Encoding.Latin1.GetBytes( "" ), output.Bytes ); + } + [Fact] public async Task DirectRgbBlackInsideRetainedIndexedPrefixDegradesToDefault() { TerminalDescription terminal = CreateDirectColorBuilder( "direct-reserved-zero" ) diff --git a/tools/dcurses-screen-contracts-acceptance/Source/Program.cs b/tools/dcurses-screen-contracts-acceptance/Source/Program.cs index 4c6e2b317..5ca0d37bb 100644 --- a/tools/dcurses-screen-contracts-acceptance/Source/Program.cs +++ b/tools/dcurses-screen-contracts-acceptance/Source/Program.cs @@ -84,6 +84,7 @@ internal static async ValueTask RefreshAsync( ) ?? throw new InvalidOperationException( "The selected terminal cannot position the retained-screen cursor." ); + TerminalScreenOperationPlan? renditionBaseline = planner.PlanRenditionBaseline(); TerminalScreenRendition requested = new( TerminalScreenColor.Indexed( 2 ), @@ -136,6 +137,7 @@ internal static async ValueTask RefreshAsync( } ); output.Add( cursor ); + AddIfSupported( output, renditionBaseline ); AddIfSupported( output, scrollRegion ); AddIfSupported( output, rendition ); AddIfSupported( output, erase ); From 0d1501f6de6fe87f1ffa6ae0a07deb5e19c7e1c1 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:02:10 -0400 Subject: [PATCH 07/23] feat: add rendition baseline planning --- src/Screen/TerminalScreenPlanner.cs | 31 +++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/Screen/TerminalScreenPlanner.cs b/src/Screen/TerminalScreenPlanner.cs index 6455acea4..2fe4accac 100644 --- a/src/Screen/TerminalScreenPlanner.cs +++ b/src/Screen/TerminalScreenPlanner.cs @@ -174,6 +174,37 @@ TerminalScreenRendition requested ); } + /// Plans restoration from an unknown physical rendition state to Terminal's normalized default. + public TerminalScreenOperationPlan? PlanRenditionBaseline() { + TerminalTextAttributes attributes = this.Profile.Screen.SupportedAttributes; + bool colors = this.Profile.Screen.SupportsColor + && ( this.Profile.Screen.SupportsForegroundColor + || this.Profile.Screen.SupportsBackgroundColor ); + List segments = []; + if ( TerminalTextAttributes.None != attributes ) { + if ( !this.TryAddLiteral( segments, StringCapability.ExitAttributeMode, 1 ) ) { + if ( 0 != ( attributes & ( + TerminalTextAttributes.Bold + | TerminalTextAttributes.Dim + | TerminalTextAttributes.Reverse + | TerminalTextAttributes.Blink + | TerminalTextAttributes.Conceal + ) ) ) || !this.TryAddAttributeExits( segments, attributes ) ) { + return null; + } + } + } + if ( colors + && !this.TryAddLiteral( segments, StringCapability.OriginalColorPair, 1 ) ) { + return null; + } + return this.Create( + TerminalScreenOperationKind.Rendition, + segments, + 1 + ); + } + /// Plans a safe transition between normalized screen renditions. public TerminalScreenOperationPlan? PlanRenditionTransition( TerminalScreenRendition current, From 616fe9994559fd6482b2006b1724eac33965a303 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:05:14 -0400 Subject: [PATCH 08/23] fix: correct rendition baseline condition --- src/Screen/TerminalScreenPlanner.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Screen/TerminalScreenPlanner.cs b/src/Screen/TerminalScreenPlanner.cs index 2fe4accac..d1be92656 100644 --- a/src/Screen/TerminalScreenPlanner.cs +++ b/src/Screen/TerminalScreenPlanner.cs @@ -189,7 +189,7 @@ TerminalScreenRendition requested | TerminalTextAttributes.Reverse | TerminalTextAttributes.Blink | TerminalTextAttributes.Conceal - ) ) ) || !this.TryAddAttributeExits( segments, attributes ) ) { + ) ) || !this.TryAddAttributeExits( segments, attributes ) ) { return null; } } From 6833931dd3bb3d7307fca3f05099eb231e22b989 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:16:51 -0400 Subject: [PATCH 09/23] test: harden rendition baseline safety --- ...nalScreenRenditionPlannerHardeningTests.cs | 245 +++++++++++++++++- 1 file changed, 243 insertions(+), 2 deletions(-) diff --git a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs index af3bcc570..0f974dc3c 100644 --- a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs +++ b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs @@ -51,6 +51,207 @@ public async Task RenditionBaselinePlanIsOpaqueCostedAndExactlyEmittable() { Assert.Equal( Encoding.Latin1.GetBytes( "" ), output.Bytes ); } + [Fact] + public async Task RenditionBaselineResetsExposedAttributesOnly() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "attribute-baseline" ) + .SetString( StringCapability.EnterBoldMode, "" ) + .SetString( StringCapability.ExitAttributeMode, "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + AssertRenditionPlan( plan, 6 ); + Assert.Empty( output.Bytes ); + + await CommitAsync( session, plan ); + + Assert.Equal( Encoding.Latin1.GetBytes( "" ), output.Bytes ); + } + + [Fact] + public async Task RenditionBaselineRestoresExposedColorsOnly() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "color-baseline" ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.SetForegroundColor, "" ) + .SetString( StringCapability.OriginalColorPair, "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + AssertRenditionPlan( plan, 4 ); + Assert.Empty( output.Bytes ); + + await CommitAsync( session, plan ); + + Assert.Equal( Encoding.Latin1.GetBytes( "" ), output.Bytes ); + } + + [Fact] + public async Task RenditionBaselineUsesEverySpecificAttributeExitInStableOrder() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "specific-attribute-baseline" ) + .SetString( StringCapability.EnterUnderlineMode, "" ) + .SetString( StringCapability.ExitUnderlineMode, "" ) + .SetString( StringCapability.EnterStandoutMode, "" ) + .SetString( StringCapability.ExitStandoutMode, "" ) + .SetString( StringCapability.EnterItalicMode, "" ) + .SetString( StringCapability.ExitItalicMode, "" ) + .SetExtendedString( "smxx", "" ) + .SetExtendedString( "rmxx", "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + AssertRenditionPlan( plan, 12 ); + Assert.Empty( output.Bytes ); + + await CommitAsync( session, plan ); + + Assert.Equal( Encoding.Latin1.GetBytes( "" ), output.Bytes ); + } + + [Fact] + public async Task RenditionBaselineRejectsAnUnrestorableExposedAttribute() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "unsafe-attribute-baseline" ) + .SetString( StringCapability.EnterBoldMode, "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + Assert.Null( session.Screen.PlanRenditionBaseline() ); + Assert.Empty( output.Bytes ); + Assert.Equal( 0, output.FlushCount ); + } + + [Fact] + public async Task RenditionBaselineRejectsAnUnrestorableExposedColorAxis() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "unsafe-color-baseline" ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.SetForegroundColor, "" ) + .Build(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( output, terminal ); + + Assert.Null( session.Screen.PlanRenditionBaseline() ); + Assert.Empty( output.Bytes ); + Assert.Equal( 0, output.FlushCount ); + } + + [Theory] + [InlineData( false )] + [InlineData( true )] + public async Task RenditionBaselineAllowsZeroByteEmptyAndResetOnlyProfiles( + bool includeUnusedResets + ) { + TerminalDescriptionBuilder builder = new( "zero-byte-baseline" ); + if ( includeUnusedResets ) { + builder + .SetString( StringCapability.ExitAttributeMode, "" ) + .SetString( StringCapability.OriginalColorPair, "" ); + } + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + builder.Build() + ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + AssertRenditionPlan( plan, 0 ); + Assert.Empty( output.Bytes ); + + await CommitAsync( session, plan ); + + Assert.Empty( output.Bytes ); + Assert.Equal( 1, output.FlushCount ); + } + + [Fact] + public async Task RenditionBaselinePaddingUsesOneAffectedLineWithoutIncreasingCost() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "padded-baseline" ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.EnterBoldMode, "B" ) + .SetString( StringCapability.ExitAttributeMode, "A$<2*/>" ) + .SetString( StringCapability.SetForegroundColor, "F%p1%d" ) + .SetString( StringCapability.OriginalColorPair, "C$<3*/>" ) + .Build(); + RecordingDelayProvider delays = new(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + terminal, + delays + ); + + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + AssertRenditionPlan( plan, 2 ); + Assert.Empty( output.Bytes ); + Assert.Empty( delays.Delays ); + Assert.Equal( 0, output.FlushCount ); + + await CommitAsync( session, plan ); + + Assert.Equal( Encoding.Latin1.GetBytes( "AC" ), output.Bytes ); + Assert.Equal( 1, output.FlushCount ); + Assert.Equal( 2, delays.Delays.Count ); + Assert.Equal( TimeSpan.FromMilliseconds( 2 ), delays.Delays[ 0 ].Duration ); + Assert.Equal( TimeSpan.FromMilliseconds( 3 ), delays.Delays[ 1 ].Duration ); + Assert.All( delays.Delays, delay => Assert.True( delay.IsMandatory ) ); + Assert.Equal( 0, delays.SynchronousDelayCount ); + Assert.Equal( 2, delays.AsynchronousDelayCount ); + } + + [Fact] + public async Task RepeatedRenditionBaselinePlanningIsDeterministicAndSideEffectFree() { + TerminalDescription terminal = new TerminalDescriptionBuilder( "repeat-baseline" ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.EnterUnderlineMode, "U" ) + .SetString( StringCapability.ExitUnderlineMode, "u" ) + .SetString( StringCapability.SetBackgroundColor, "B%p1%d" ) + .SetString( StringCapability.OriginalColorPair, "O" ) + .Build(); + RecordingDelayProvider delays = new(); + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + terminal, + delays + ); + TerminalScreenOutputTransaction transaction = + session.CreateScreenOutputTransaction(); + + TerminalScreenOperationPlan first = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + TerminalScreenOperationPlan second = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + + Assert.Equal( first.Kind, second.Kind ); + Assert.Equal( first.ByteCount, second.ByteCount ); + Assert.Equal( first.AffectedLines, second.AffectedLines ); + AssertRenditionPlan( first, 2 ); + Assert.Empty( output.Bytes ); + Assert.Empty( delays.Delays ); + Assert.Equal( 0, output.FlushCount ); + + transaction.Add( first ); + transaction.Add( second ); + await transaction.CommitAsync(); + + Assert.Equal( Encoding.Latin1.GetBytes( "uOuO" ), output.Bytes ); + Assert.Equal( 1, output.FlushCount ); + } + [Fact] public async Task DirectRgbBlackInsideRetainedIndexedPrefixDegradesToDefault() { TerminalDescription terminal = CreateDirectColorBuilder( "direct-reserved-zero" ) @@ -701,7 +902,8 @@ string name private static ValueTask OpenSessionAsync( RecordingTerminalOutput output, - TerminalDescription terminal + TerminalDescription terminal, + ITermInfoDelayProvider? delayProvider = null ) { return TerminalSession.OpenAsync( new TestTerminalControlProvider(), @@ -711,11 +913,44 @@ TerminalDescription terminal output, new TerminalSessionOptions { TerminalOverride = terminal, - ObserveLifecycleEvents = false + ObserveLifecycleEvents = false, + CapabilityPaddingMode = PaddingMode.Delay, + CapabilityDelayProvider = delayProvider } ); } + private sealed class RecordingDelayProvider : ITermInfoDelayProvider { + internal List Delays { + get; + } = []; + + internal int SynchronousDelayCount { + get; + private set; + } + + internal int AsynchronousDelayCount { + get; + private set; + } + + public void Delay( TermInfoDelay delay ) { + this.SynchronousDelayCount++; + this.Delays.Add( delay ); + } + + public ValueTask DelayAsync( + TermInfoDelay delay, + CancellationToken cancellationToken = default + ) { + cancellationToken.ThrowIfCancellationRequested(); + this.AsynchronousDelayCount++; + this.Delays.Add( delay ); + return ValueTask.CompletedTask; + } + } + private sealed class TestTerminalInput : ITerminalInput { public ValueTask ReadAsync( Memory buffer, @@ -731,6 +966,11 @@ internal List Bytes { get; } = []; + internal int FlushCount { + get; + private set; + } + public ValueTask WriteAsync( ReadOnlyMemory buffer, CancellationToken cancellationToken = default @@ -744,6 +984,7 @@ public ValueTask FlushAsync( CancellationToken cancellationToken = default ) { cancellationToken.ThrowIfCancellationRequested(); + this.FlushCount++; return ValueTask.CompletedTask; } } From c659ed9af2c46c9127e089ec9d81243ef4fe69f5 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:20:29 -0400 Subject: [PATCH 10/23] test: freeze Terminal 1.18 public API --- docs/Public-API-Baseline-1.18.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 docs/Public-API-Baseline-1.18.md diff --git a/docs/Public-API-Baseline-1.18.md b/docs/Public-API-Baseline-1.18.md new file mode 100644 index 000000000..000f45510 --- /dev/null +++ b/docs/Public-API-Baseline-1.18.md @@ -0,0 +1,25 @@ +# Icod.Terminal 1.18 Public API Baseline + +This document records the additive public API for the `Icod.Terminal 1.18.0` rendition-baseline release. + +The stable compatibility floor remains `1.0.0`. The complete 1.17 public surface remains available and unchanged. The predecessor 1.17 public API fingerprint is: + +```text +c0a051a925d551e526343ef59d8c47d75e41868d84235fa30bfa7debe1b3ceb9 +``` + +The generated public API snapshots are identical on `net8.0`, `net9.0`, and `net10.0`. The 1.18 fingerprint is: + +```text +48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d +``` + +The machine-readable fingerprint is stored in `docs/Public-API-Baseline-1.18.sha256` and is enforced by the package/public-API gate. + +## Public addition over 1.17 + +`TerminalScreenPlanner.PlanRenditionBaseline()` returns an opaque, session-bound rendition plan that restores the normalized default rendition without assuming a known physical starting state. It returns `null` when any attribute or color axis exposed by the selected terminal profile cannot be restored unconditionally. + +The plan prefers the global attribute reset; otherwise it emits every required safe specific attribute exit in stable order, followed by original-color restoration when a selectable color axis is exposed. A profile with no exposed rendition entry or selection capability receives a valid zero-byte plan, including reset-only profiles. + +The method is additive and exposes no `Icod.TermInfo` type, capability identifier, terminal string, or expansion API. Existing rendition normalization, transition, reset, operation-plan ownership, and transaction behavior remain unchanged. From 09cb426008db2fd82e72eea54dfd4952a6f34d96 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:20:35 -0400 Subject: [PATCH 11/23] test: freeze Terminal 1.18 public API --- docs/Public-API-Baseline-1.18.sha256 | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/Public-API-Baseline-1.18.sha256 diff --git a/docs/Public-API-Baseline-1.18.sha256 b/docs/Public-API-Baseline-1.18.sha256 new file mode 100644 index 000000000..e54a425a7 --- /dev/null +++ b/docs/Public-API-Baseline-1.18.sha256 @@ -0,0 +1 @@ +48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d From b369a370986e052eda730f93240af330046640ed Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:20:43 -0400 Subject: [PATCH 12/23] test: freeze Terminal 1.18 public API --- packaging/VerifyPublicApiBaseline.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/VerifyPublicApiBaseline.ps1 b/packaging/VerifyPublicApiBaseline.ps1 index d064356bc..6964be3fb 100644 --- a/packaging/VerifyPublicApiBaseline.ps1 +++ b/packaging/VerifyPublicApiBaseline.ps1 @@ -1,7 +1,7 @@ param( [string]$Configuration = 'Staging', [string]$OutputDirectory = 'artifacts/public-api', - [string]$BaselinePath = 'docs/Public-API-Baseline-1.17.sha256' + [string]$BaselinePath = 'docs/Public-API-Baseline-1.18.sha256' ) Set-StrictMode -Version Latest From e22f5480fb3586f38e1e3abb7630f2c5702f9d42 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:22:14 -0400 Subject: [PATCH 13/23] test: observe rendition baseline lifecycle purity --- ...nalScreenRenditionPlannerHardeningTests.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs index 0f974dc3c..76da06fa9 100644 --- a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs +++ b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenRenditionPlannerHardeningTests.cs @@ -21,6 +21,7 @@ You should have received a copy of the GNU General Public License namespace Icod.Terminal.Tests.Screen; using System.Text; +using System.Threading.Channels; using Icod.Terminal; using Icod.TermInfo; using Xunit; @@ -222,12 +223,15 @@ public async Task RepeatedRenditionBaselinePlanningIsDeterministicAndSideEffectF .SetString( StringCapability.OriginalColorPair, "O" ) .Build(); RecordingDelayProvider delays = new(); + RecordingLifecycleSource lifecycle = new(); RecordingTerminalOutput output = new(); await using TerminalSession session = await OpenSessionAsync( output, terminal, - delays + delays, + lifecycle ); + await lifecycle.WaitUntilReadAsync().WaitAsync( TimeSpan.FromSeconds( 5 ) ); TerminalScreenOutputTransaction transaction = session.CreateScreenOutputTransaction(); @@ -243,6 +247,7 @@ public async Task RepeatedRenditionBaselinePlanningIsDeterministicAndSideEffectF Assert.Empty( output.Bytes ); Assert.Empty( delays.Delays ); Assert.Equal( 0, output.FlushCount ); + Assert.Equal( 1, lifecycle.ReadCount ); transaction.Add( first ); transaction.Add( second ); @@ -250,6 +255,7 @@ public async Task RepeatedRenditionBaselinePlanningIsDeterministicAndSideEffectF Assert.Equal( Encoding.Latin1.GetBytes( "uOuO" ), output.Bytes ); Assert.Equal( 1, output.FlushCount ); + Assert.Equal( 1, lifecycle.ReadCount ); } [Fact] @@ -903,7 +909,8 @@ string name private static ValueTask OpenSessionAsync( RecordingTerminalOutput output, TerminalDescription terminal, - ITermInfoDelayProvider? delayProvider = null + ITermInfoDelayProvider? delayProvider = null, + ITerminalLifecycleSource? lifecycleSource = null ) { return TerminalSession.OpenAsync( new TestTerminalControlProvider(), @@ -914,12 +921,40 @@ private static ValueTask OpenSessionAsync( new TerminalSessionOptions { TerminalOverride = terminal, ObserveLifecycleEvents = false, + LifecycleSource = lifecycleSource, CapabilityPaddingMode = PaddingMode.Delay, CapabilityDelayProvider = delayProvider } ); } + private sealed class RecordingLifecycleSource : ITerminalLifecycleSource { + private readonly Channel signals = + Channel.CreateUnbounded(); + private readonly TaskCompletionSource firstRead = new( + TaskCreationOptions.RunContinuationsAsynchronously + ); + + internal int ReadCount { + get; + private set; + } + + internal Task WaitUntilReadAsync() => this.firstRead.Task; + + public ValueTask ReadAsync( + CancellationToken cancellationToken = default + ) { + this.ReadCount++; + this.firstRead.TrySetResult(); + return this.signals.Reader.ReadAsync( cancellationToken ); + } + + public void Dispose() { + this.signals.Writer.TryComplete(); + } + } + private sealed class RecordingDelayProvider : ITermInfoDelayProvider { internal List Delays { get; From 141fd5d3cd87db2018d2022672d53dcfc67a1e8a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:24:06 -0400 Subject: [PATCH 14/23] test: qualify rendition baseline ownership --- ...alScreenOutputTransactionHardeningTests.cs | 124 +++++++++++++++++- 1 file changed, 122 insertions(+), 2 deletions(-) diff --git a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenOutputTransactionHardeningTests.cs b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenOutputTransactionHardeningTests.cs index a8c66f1c2..b45f7a825 100644 --- a/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenOutputTransactionHardeningTests.cs +++ b/tests/Icod.Terminal.Tests/src/Screen/TerminalScreenOutputTransactionHardeningTests.cs @@ -162,6 +162,112 @@ public async Task TransactionRejectsDefaultAndForeignPlansWithoutMutation() { Assert.Equal( "owned"u8.ToArray(), secondOutput.GetCombinedWrites() ); } + [Fact] + public async Task RenditionBaselineSerializesWithAdjacentApplicationText() { + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + CreateRenditionBaselineTerminal( "owned-rendition-baseline" ) + ); + TerminalScreenOperationPlan plan = session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + TerminalScreenOutputTransaction transaction = + session.CreateScreenOutputTransaction(); + + transaction.WriteText( "before" ); + transaction.Add( plan ); + transaction.WriteText( "after" ); + await transaction.CommitAsync(); + + Assert.Equal( + "beforeafter"u8.ToArray(), + output.GetCombinedWrites() + ); + Assert.Equal( 1, output.FlushAttemptCount ); + } + + [Fact] + public async Task ForeignRenditionBaselineIsRejectedBeforeAnyOutput() { + RecordingTerminalOutput firstOutput = new(); + RecordingTerminalOutput secondOutput = new(); + await using TerminalSession first = await OpenSessionAsync( + firstOutput, + CreateRenditionBaselineTerminal( "foreign-rendition-baseline" ) + ); + await using TerminalSession second = await OpenSessionAsync( + secondOutput, + CreateRenditionBaselineTerminal( "receiving-rendition-baseline" ) + ); + TerminalScreenOperationPlan foreign = first.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException(); + TerminalScreenOutputTransaction transaction = + second.CreateScreenOutputTransaction(); + transaction.WriteText( "before" ); + + ArgumentException exception = Assert.Throws( + () => transaction.Add( foreign ) + ); + + Assert.Equal( "plan", exception.ParamName ); + Assert.Equal( 0, firstOutput.WriteAttemptCount ); + Assert.Equal( 0, firstOutput.FlushAttemptCount ); + Assert.Equal( 0, secondOutput.WriteAttemptCount ); + Assert.Equal( 0, secondOutput.FlushAttemptCount ); + } + + [Fact] + public async Task StaleRenditionBaselineTransactionEmitsNothing() { + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + CreateRenditionBaselineTerminal( "stale-rendition-baseline" ) + ); + TerminalScreenOutputTransaction stale = + session.CreateScreenOutputTransaction(); + stale.Add( + session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException() + ); + TerminalScreenOutputTransaction intervening = + session.CreateScreenOutputTransaction(); + await intervening.CommitAsync(); + + InvalidOperationException exception = await Assert.ThrowsAsync( + () => stale.CommitAsync().AsTask() + ); + + Assert.Equal( + "The screen-output transaction is stale because intervening session output occurred.", + exception.Message + ); + Assert.Equal( 0, output.WriteAttemptCount ); + Assert.Equal( 1, output.FlushAttemptCount ); + } + + [Fact] + public async Task PreCancelledRenditionBaselineTransactionEmitsNothing() { + RecordingTerminalOutput output = new(); + await using TerminalSession session = await OpenSessionAsync( + output, + CreateRenditionBaselineTerminal( "cancelled-rendition-baseline" ) + ); + TerminalScreenOutputTransaction transaction = + session.CreateScreenOutputTransaction(); + transaction.Add( + session.Screen.PlanRenditionBaseline() + ?? throw new InvalidOperationException() + ); + using CancellationTokenSource cancellation = new(); + cancellation.Cancel(); + + await Assert.ThrowsAnyAsync( + () => transaction.CommitAsync( cancellation.Token ).AsTask() + ); + + Assert.Equal( 0, output.WriteAttemptCount ); + Assert.Equal( 0, output.FlushAttemptCount ); + } + [Fact] public async Task Utf8PayloadBoundaryRejectionDoesNotPoisonLaterAdds() { RecordingTerminalOutput output = new(); @@ -581,7 +687,8 @@ public async Task IndependentPrimaryAndCleanupFailuresAreAggregatedInOrder() { } private static ValueTask OpenSessionAsync( - RecordingTerminalOutput output + RecordingTerminalOutput output, + TerminalDescription? terminalOverride = null ) { ArgumentNullException.ThrowIfNull( output ); TerminalDescription terminal = new TerminalDescriptionBuilder( "screen-output-hardening" ) @@ -594,13 +701,26 @@ RecordingTerminalOutput output new TestTerminalInput(), output, new TerminalSessionOptions { - TerminalOverride = terminal, + TerminalOverride = terminalOverride ?? terminal, ConfigureOutput = false, ObserveLifecycleEvents = false } ); } + private static TerminalDescription CreateRenditionBaselineTerminal( + string name + ) { + return new TerminalDescriptionBuilder( name ) + .SetNumber( NumericCapability.Colors, 16 ) + .SetString( StringCapability.EnterBoldMode, "" ) + .SetString( StringCapability.ExitAttributeMode, "" ) + .SetString( StringCapability.SetForegroundColor, "" ) + .SetString( StringCapability.SetBackgroundColor, "" ) + .SetString( StringCapability.OriginalColorPair, "" ) + .Build(); + } + private sealed class TestTerminalInput : ITerminalInput { public ValueTask ReadAsync( Memory buffer, From 0906219e34e748a05c14266e277a979da6537465 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:25:56 -0400 Subject: [PATCH 15/23] test: qualify Terminal 1.18 package contracts --- src/Screen/TerminalScreenPlanner.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Screen/TerminalScreenPlanner.cs b/src/Screen/TerminalScreenPlanner.cs index d1be92656..ea3e02cf7 100644 --- a/src/Screen/TerminalScreenPlanner.cs +++ b/src/Screen/TerminalScreenPlanner.cs @@ -175,6 +175,10 @@ TerminalScreenRendition requested } /// Plans restoration from an unknown physical rendition state to Terminal's normalized default. + /// + /// A session-bound rendition plan, or when any rendition axis + /// exposed by the selected profile cannot be restored unconditionally. + /// public TerminalScreenOperationPlan? PlanRenditionBaseline() { TerminalTextAttributes attributes = this.Profile.Screen.SupportedAttributes; bool colors = this.Profile.Screen.SupportsColor From 132ca3e21e19263fb3f817beb548206fab1824b2 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:26:05 -0400 Subject: [PATCH 16/23] test: qualify Terminal 1.18 package contracts --- .../src/Packaging/DependencyCouplingPolicyTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs b/tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs index ce599534e..f8e6acfa8 100644 --- a/tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs +++ b/tests/Icod.Terminal.Tests/src/Packaging/DependencyCouplingPolicyTests.cs @@ -118,6 +118,13 @@ string configuration StringComparison.Ordinal ); } + Assert.Contains( + ownedSources, + acceptanceSource => File.ReadAllText( acceptanceSource ).Contains( + ".PlanRenditionBaseline()", + StringComparison.Ordinal + ) + ); foreach ( string targetFramework in new[] { "net8.0", "net9.0", "net10.0" } ) { using JsonDocument evaluation = EvaluateProjectItems( From d638645e9af1ac3b5e83b33c24b4b511f7def517 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:26:14 -0400 Subject: [PATCH 17/23] test: qualify Terminal 1.18 package contracts --- tools/package-smoke/Program.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tools/package-smoke/Program.cs b/tools/package-smoke/Program.cs index e54aed432..50418917d 100644 --- a/tools/package-smoke/Program.cs +++ b/tools/package-smoke/Program.cs @@ -128,12 +128,24 @@ string description && 0 < homePlan.ByteCount, "The package consumer received an invalid cursor plan." ); + TerminalScreenOperationPlan renditionBaseline = session.Screen + .PlanRenditionBaseline() + ?? throw new InvalidOperationException( + "The package consumer could not plan unknown-state rendition restoration." + ); + Require( + TerminalScreenOperationKind.Rendition == renditionBaseline.Kind + && 0 < renditionBaseline.ByteCount + && 1 == renditionBaseline.AffectedLines, + "The package consumer received an invalid rendition-baseline plan." + ); TerminalScreenOutputTransaction screenOutput = session.CreateScreenOutputTransaction( new TerminalScreenOutputTransactionOptions { UseSynchronizedOutput = true } ); + screenOutput.Add( renditionBaseline ); screenOutput.Add( homePlan ); screenOutput.WriteText( "screen-package-smoke" ); screenOutput.WriteHyperlink( "docs", "https://example.com/icod-terminal" ); From b70ec501a1bc01fcdfee32ee3fd19695165e0bf4 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:26:22 -0400 Subject: [PATCH 18/23] test: qualify Terminal 1.18 package contracts --- tools/package-verifier/Program.cs | 43 +++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tools/package-verifier/Program.cs b/tools/package-verifier/Program.cs index fe8ea509b..654c4a11c 100644 --- a/tools/package-verifier/Program.cs +++ b/tools/package-verifier/Program.cs @@ -30,6 +30,8 @@ namespace Icod.Terminal.PackageVerifier; internal static class Program { private const string PackageId = "Icod.Terminal"; private const string RepositoryUrl = "https://github.com/uniblab/Icod.Terminal"; + private const string RenditionBaselineDocumentationMember = + "M:Icod.Terminal.TerminalScreenPlanner.PlanRenditionBaseline"; private static readonly string[] TargetFrameworks = [ "net8.0", @@ -687,6 +689,47 @@ string targetFramework missingQueryDocumentation ) ); + + XElement? renditionBaseline = documentation + .Descendants() + .FirstOrDefault( + element => "member" == element.Name.LocalName + && RenditionBaselineDocumentationMember + == element.Attribute( "name" )?.Value + ); + Require( + renditionBaseline is not null, + $"{documentationPath} is missing rendition-baseline documentation." + ); + string renditionSummary = renditionBaseline! + .Elements() + .FirstOrDefault( element => "summary" == element.Name.LocalName ) + ?.Value + ?? string.Empty; + XElement? renditionReturns = renditionBaseline! + .Elements() + .FirstOrDefault( element => "returns" == element.Name.LocalName ); + Require( + renditionSummary.Contains( + "unknown physical rendition state", + StringComparison.Ordinal + ), + $"{documentationPath} does not document unknown-state baseline semantics." + ); + Require( + renditionReturns is not null + && renditionReturns.Value.Contains( + "cannot be restored unconditionally", + StringComparison.Ordinal + ) + && renditionReturns + .Descendants() + .Any( + element => "see" == element.Name.LocalName + && "null" == element.Attribute( "langword" )?.Value + ), + $"{documentationPath} does not document rendition-baseline nullability semantics." + ); } private static void VerifyAssemblyIdentity( From 36a0b3cb794c5cd4c34c512a12d59bda27ff44dc Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:39:38 -0400 Subject: [PATCH 19/23] docs: prepare Terminal 1.18 stable candidate --- CHANGELOG.md | 15 ++++++++ Directory.Build.props | 2 +- Icod.Terminal-1.18.0-Development-Roadmap.md | 18 ++++++++- Icod.Terminal-Development-Roadmap.md | 12 ++++-- README.md | 24 ++++++++++-- docs/Architecture.md | 2 + docs/Compatibility-and-Versioning.md | 8 ++++ docs/Security-and-Privacy.md | 6 ++- docs/releases/1.18.0.md | 42 +++++++++++++++++++++ 9 files changed, 117 insertions(+), 12 deletions(-) create mode 100644 docs/releases/1.18.0.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 596be6615..760653b40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ Notable changes to `Icod.Terminal` are recorded here for consumers who need a concise release history. Detailed design evidence remains in the versioned roadmaps, tranche records, and public-API baseline documents. +## 1.18.0 + +### Unknown-rendition baseline recovery + +- Adds `TerminalScreenPlanner.PlanRenditionBaseline()` for safe recovery from unknown physical rendition state without requiring a caller-supplied known current rendition. +- Derives obligations from raw attribute-entry and color-selection evidence; returns `null` when any exposed axis cannot be restored unconditionally. +- Emits global attribute reset or every required safe specific exit in stable order, then restores original colors. +- Preserves valid zero-byte planning for empty and reset-only profiles while retaining exact byte cost, padding, affected-line, same-session ownership, stale-epoch, cancellation, and transaction semantics. +- Exposes no TermInfo type or raw capability/terminal-string API and leaves retained screen/layout/damage policy above Terminal. +- Finalizes identical `net8.0`/`net9.0`/`net10.0` public API snapshots with fingerprint `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`. +- Retains production dependencies `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0`. +- Qualifies fresh package consumers, published `Icod.DCurses 1.6.0` compatibility, and a separate TermInfo-free future-renderer package consumer on all target frameworks. + +See `docs/releases/1.18.0.md`, `docs/Public-API-Baseline-1.18.md`, and `Icod.Terminal-1.18.0-Development-Roadmap.md` for the complete contract. + ## 1.17.1 ### Packaged README and release metadata correction diff --git a/Directory.Build.props b/Directory.Build.props index 736acefe7..f54380e27 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,7 +2,7 @@ 1.18.0 - alpha.1 + $(VersionPrefix) $(VersionPrefix)-$(VersionSuffix) $(Version) diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md index a2d38e757..a6848ac9b 100644 --- a/Icod.Terminal-1.18.0-Development-Roadmap.md +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -2,8 +2,8 @@ **Release:** `1.18.0` **Theme:** Unknown-rendition baseline recovery for Terminal-only screen consumers -**Status:** Planning approved; implementation not started -**Planned development identity:** `1.18.0-alpha.1` +**Status:** T180-T184 accepted; stable `1.18.0` candidate closure in progress +**Candidate identity:** `1.18.0` **Stable compatibility floor:** `1.0.0` **Current published line:** `1.17.0`; `1.17.1` is the documentation-only patch baseline @@ -29,6 +29,18 @@ Icod.DCurses -> Icod.Terminal -> Icod.TermInfo - [`docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md`](docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md) - [`Icod.DCurses 2.0 PR #32`](https://github.com/uniblab/Icod.DCurses/pull/32) +## Accepted implementation record + +T180-T184 are complete on PR #63. The implementation adds only `PlanRenditionBaseline()` to the public surface and retains production `Icod.TermInfo 1.15.0` / `Icod.Timing 1.0.0`. + +- T180 recorded the `CS1061` package/source RED witness before the method existed. +- T181 implemented unknown-state restoration with all-or-nothing availability. +- T182 qualified attribute-only, color-only, combined, specific-exit, unsafe, empty, reset-only, padding, deterministic, and side-effect-free behavior. +- T183 qualified same-session ordering, foreign-session rejection, stale output epochs, and pre-commit cancellation. +- T184 froze identical API snapshots at `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`, verified the deliberate mismatch rejection, and passed the candidate package, XML, dependency, published DCurses 1.6.0, and TermInfo-free future-renderer consumers across all three target frameworks. + +The retained DCurses PR #32 package witness remains governed by its published-package resume criterion. It resumes after `Icod.Terminal 1.18.0` is published; no unpublished project reference or raw TermInfo workaround is introduced. + ## Public contract Add one method to the existing session-bound planner: @@ -119,6 +131,8 @@ Complete cross-platform, package, public-API, XML-documentation, dependency, sec Tagging, release creation, and publication remain explicit maintainer actions after the stable candidate is accepted. +Current closure work removes the prerelease suffix, synchronizes consumer/release/architecture/security documentation, runs the complete exact-head workflow, records its artifact/test evidence, and stops before merge, tag, GitHub Release, or publication. + ## Explicit non-goals Version 1.18 does not add: diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index f74b8185c..97621d384 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -6,7 +6,7 @@ - **Target frameworks:** `net8.0`; `net9.0`; `net10.0` - **Current published feature line:** `1.17.0` — Terminal-owned Screen Output Contracts - **Current patch line:** `1.17.1` — Packaged README and release metadata correction -- **Development status:** 1.17.0 published; 1.17.1 documentation-only patch in qualification +- **Development status:** 1.17.1 published; 1.18.0 stable candidate in exact-head qualification - **Active development target:** `1.18.0` — Unknown-rendition baseline recovery for Terminal-only screen consumers - **Stable compatibility floor:** `1.0.0` @@ -24,6 +24,8 @@ The published feature line uses production `Icod.TermInfo 1.15.0` and optional t Version 1.17.1 is a documentation-only patch that corrects the README embedded in 1.17.0 and synchronizes release metadata. It makes no runtime or public-API change. +Version 1.18.0 is now a stable source candidate on PR #63. T180-T184 are accepted: the additive unknown-rendition baseline API, hardening/ownership tests, identical three-framework API fingerprint, package/XML gates, published DCurses 1.6.0 soak, and TermInfo-free future-renderer package witness are complete. T185 exact-head stable qualification remains before merge or publication. + ## Current architecture ```text @@ -72,13 +74,13 @@ Optional integration tests/samples may use `Icod.TermInfo.Inspection 1.15.0`; In 1.16.0 persistent raster animation and frame lifecycle PUBLISHED 1.17.0 Terminal-owned dimensions, screen planning, and transactions PUBLISHED 1.17.1 packaged README and release metadata correction PATCH -1.18.0 unknown-rendition baseline recovery PLANNED +1.18.0 unknown-rendition baseline recovery CANDIDATE ``` -The final 1.17 public API fingerprint is: +The 1.18 public API fingerprint is: ```text -c0a051a925d551e526343ef59d8c47d75e41868d84235fa30bfa7debe1b3ceb9 +48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d ``` Permanent ownership authority: [`docs/Persistent-Raster-Ownership.md`](docs/Persistent-Raster-Ownership.md). @@ -230,6 +232,8 @@ public TerminalScreenOperationPlan? PlanRenditionBaseline(); The plan represents unknown physical rendition state, restores attributes before original colors in a safe deterministic order, retains Terminal-owned expansion/padding/cost and same-session transaction ownership, and returns a valid zero-byte plan only when the selected profile exposes no enterable attribute and no selectable color axis. Existing known-state reset and transition behavior remains unchanged. +T180-T184 are accepted. T185 prepares the stable package/docs metadata and records one final exact-head Windows/Linux/macOS, package, API/XML, downstream, and artifact qualification. Publication remains a separate maintainer action. + The tranche sequence is: ```text diff --git a/README.md b/README.md index ee484f36c..6bd9a334c 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,17 @@ ## Status -Current stable release: `Icod.Terminal 1.17.1`. +Current published stable release: `Icod.Terminal 1.17.1`. Version `1.17.1` corrects the packaged README and release metadata for the 1.17 line. It makes no runtime or public-API change from `1.17.0`. +This source prepares the stable `1.18.0` candidate. Version 1.18 adds `TerminalScreenPlanner.PlanRenditionBaseline()`, allowing a Terminal-only renderer to establish the normalized default rendition safely when the physical starting state is unknown. The operation returns no plan when any profile-exposed rendition axis cannot be restored unconditionally. + Version 1.17 adds Terminal-owned dimensions, an immutable semantic terminal profile, side-effect-free screen-operation planning, and bounded session-bound output transactions. These contracts provide the Terminal-side boundary required for a later `Icod.DCurses 2.0` release to remove its direct `Icod.TermInfo` dependency. -The stable `1.0.0` compatibility floor remains unchanged. Version 1.17 retains the complete 1.16 animation, 1.15 virtual-placeholder, 1.14 lifecycle-observation, 1.13 relative-placement, 1.12 crop/z-order, and earlier persistent-raster contracts. The final 1.17 public API fingerprint is `c0a051a925d551e526343ef59d8c47d75e41868d84235fa30bfa7debe1b3ceb9`. +The stable `1.0.0` compatibility floor remains unchanged. Version 1.18 retains the complete 1.17 screen-planning/transaction surface, 1.16 animation, 1.15 virtual-placeholder, and every earlier stable 1.x contract. The 1.18 public API fingerprint is `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`. -The 1.17 line is additive over the complete 1.16 persistent-raster animation surface and every earlier stable 1.x contract. See the [1.17.1 release notes](docs/releases/1.17.1.md) and [changelog](CHANGELOG.md) for release-specific details. +See the [1.18.0 release notes](docs/releases/1.18.0.md) and [changelog](CHANGELOG.md) for release-specific details. Until 1.18.0 is published, installation guidance below continues to name the published 1.17.1 package. ## Support the Project @@ -113,6 +115,22 @@ if ( home is TerminalScreenOperationPlan plan ) { } ``` +When a renderer cannot trust its current physical rendition, version 1.18 can establish a safe baseline before emitting retained content: + +```csharp +TerminalScreenOperationPlan? baseline = + session.Screen.PlanRenditionBaseline(); + +if ( baseline is TerminalScreenOperationPlan plan ) { + TerminalScreenOutputTransaction output = + session.CreateScreenOutputTransaction(); + output.Add( plan ); + await output.CommitAsync(); +} +``` + +A `null` result means at least one rendition axis exposed by the selected profile cannot be restored unconditionally from unknown state; callers must not substitute a claimed known default. + `Profile` contains immutable selected-profile facts, while `GetDimensions()` reports the current Terminal-owned size result. A plan is opaque and session-bound; creating it emits nothing, and the transaction preserves ordering under one output gate and flush boundary. Retained cells, layout, Unicode width, clipping, damage, and repaint policy remain caller-owned. ## Feature Inventory diff --git a/docs/Architecture.md b/docs/Architecture.md index 4fd1784c2..4a59d5f19 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -69,6 +69,8 @@ Placeholder cells remain current-cursor text output. Terminal owns their protoco Planning is side-effect free. A plan identifies its semantic operation, resolved terminal-byte cost, and padding-sensitive affected-line count. A higher layer may compare independently safe plans against its own retained-screen state; Terminal does not decide which cells changed or whether rewriting is preferable. +`PlanRenditionBaseline()` represents unknown physical rendition state. It derives obligations from profile entry/selection evidence and returns a plan only when every exposed attribute and color axis can be restored unconditionally. Attribute restoration precedes original-color restoration. A reset-only profile has no reachable rendition state and therefore receives a valid zero-byte plan; a partially restorable profile receives no plan. + `CreateScreenOutputTransaction(...)` captures the session serialized-output epoch and composes plans, application text, strict hyperlinks, and current raster-placeholder cells. Commit rejects stale work before output, holds the existing output gate, optionally emits synchronized-output framing, attempts required cleanup after commitment, and flushes before release. This contract does not transfer cells, windows, pads, layout, clipping, Unicode display width, damage, desired-versus-physical comparison, or repaint policy into Terminal. diff --git a/docs/Compatibility-and-Versioning.md b/docs/Compatibility-and-Versioning.md index 2bfda34a0..d85f779f8 100644 --- a/docs/Compatibility-and-Versioning.md +++ b/docs/Compatibility-and-Versioning.md @@ -127,6 +127,14 @@ Existing `GetSize()`, `TerminalLifecycleEvent.Size`, `TerminalSession.Terminal`, Release qualification treats compatibility and future decoupling as separate claims: the 1.17 package must run with published stable `Icod.DCurses 1.6.0`, and an independent package-only consumer must compile against the Terminal-owned screen contracts with no direct TermInfo project reference or source use. The latter is evidence of API sufficiency, not a claim that DCurses 2.0 has already shipped. +### 4.7 1.18 unknown-rendition recovery + +Version 1.18 additively introduces `TerminalScreenPlanner.PlanRenditionBaseline()`. Existing known-state normalization, transition, and reset behavior is unchanged. + +The operation does not reinterpret a default rendition as known physical state. It returns an opaque same-session plan only when every profile-exposed attribute-entry and color-selection axis can be restored unconditionally. It returns `null` rather than weakening this guarantee, and returns a valid zero-byte plan only when no reachable rendition state is exposed. + +The public signature contains no TermInfo type. Package qualification retains the 1.17 compatibility surface, production dependency versions, and all historical API baselines while adding the frozen 1.18 fingerprint. + ## 5. Persistent-raster compatibility guarantees The following remain compatible guarantees: diff --git a/docs/Security-and-Privacy.md b/docs/Security-and-Privacy.md index 356d67a67..9b4954014 100644 --- a/docs/Security-and-Privacy.md +++ b/docs/Security-and-Privacy.md @@ -260,19 +260,21 @@ Animation acknowledgements remain untrusted terminal input. Correlation routes o Applications must not treat animation state, selected frame, timing, looping, placement, or visual coverage as a security boundary. The terminal controls final rendering and may ignore, evict, reinterpret, record, or externally compose output. -## 20. Semantic screen planning and output commitment — 1.17 +## 20. Semantic screen planning and output commitment — 1.17 and 1.18 Terminal-profile facts are immutable projections of the selected description, not authenticated live observations. Applications must not treat declared screen capabilities, dimensions, cursor position, rendition state, terminal content, or successful output as a security boundary or as proof of what a terminal ultimately rendered. Screen planning is side-effect free and returns opaque reviewed plans. Public callers cannot inject raw capability identifiers, expansion programs, padding directives, or arbitrary terminal strings through the planner. Dimensions, coordinates, counts, colors, regions, payload bytes, operation count, and aggregate transaction payload are validated and bounded before commitment. +Unknown physical rendition is not treated as a known default. `PlanRenditionBaseline()` derives restoration obligations from selected-profile entry/selection evidence and returns `null` if any exposed axis lacks unconditional restoration. This prevents higher layers from silently accepting a partial reset as a safe baseline. Reset and selection strings remain private TermInfo-derived data inside the opaque plan. + Plans, hyperlink content, and raster-placeholder cells retain exact session/owner identity. A transaction rejects foreign, stale, released, or disposed retained items before output. Creation captures the serialized-output epoch; intervening session-owned output invalidates the batch before commitment rather than allowing an outdated retained-screen decision to be emitted. Commit holds the existing output gate across the logical batch, optional synchronized-output framing, required hyperlink cleanup, one final flush, and gate release. Pre-commit cancellation emits nothing. After commitment, cleanup is attempted without ordinary caller cancellation; independent primary and cleanup failures are flattened in deterministic order. Failure does not trigger blind replay or claim that the terminal applied none, some, or all of the bytes. Application text and hyperlink labels remain disclosure surfaces. The caller remains responsible for deciding whether terminal-visible content and hyperlink targets are appropriate. Strict hyperlink validation prevents control-character framing injection but does not authenticate or make a URI safe to follow. -## 21. Stable exclusions after 1.17 +## 21. Stable exclusions after 1.18 Security/privacy behavior does not include promises for: diff --git a/docs/releases/1.18.0.md b/docs/releases/1.18.0.md new file mode 100644 index 000000000..447907a89 --- /dev/null +++ b/docs/releases/1.18.0.md @@ -0,0 +1,42 @@ +# Icod.Terminal 1.18.0 + +## Unknown-rendition baseline recovery + +`Icod.Terminal 1.18.0` adds the missing recovery primitive required by a Terminal-only retained renderer: `TerminalScreenPlanner.PlanRenditionBaseline()` safely plans a return from unknown physical rendition state to Terminal's normalized default. + +The release is additive over the stable `1.0.0` compatibility floor and the complete 1.17 API. + +## Baseline planning + +- Derives restoration obligations from the selected profile's attribute-entry and color-selection evidence before reversible normalization can suppress unsafe requests. +- Prefers one global attribute reset when available; otherwise emits every required unconditional specific exit in stable underline, standout, italic, then strikeout order. +- Restores selectable foreground/background color state through original-color-pair restoration after attribute restoration. +- Returns `null` instead of a partial plan when any exposed rendition axis cannot be restored unconditionally. +- Returns a valid zero-byte `Rendition` plan when no attribute entry or color selection is exposed, including reset-only profiles. +- Reports exact emitted-byte cost after TermInfo padding interpretation and one affected line. + +Planning remains side-effect free. The returned opaque plan belongs to the originating `TerminalSession` and can be emitted only through the existing same-session screen-output transaction. + +## Compatibility and ownership + +Existing rendition normalization, `PlanRenditionTransition(...)`, `PlanRenditionReset(current)`, operation-plan ownership, output-epoch validation, cancellation, serialization, and cleanup semantics are unchanged. + +The new signature exposes no `Icod.TermInfo` type, capability identifier, terminal string, or expansion API. TermInfo remains Terminal's private capability-data, expansion, padding, and color authority. Cells, windows, layout, Unicode width, clipping, damage, desired-versus-physical comparison, and repaint policy remain higher-layer responsibilities. + +Production dependencies remain `Icod.TermInfo 1.15.0` and `Icod.Timing 1.0.0`. + +## Qualification + +The public API snapshots are identical across `net8.0`, `net9.0`, and `net10.0`. The 1.18 fingerprint is: + +```text +48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d +``` + +Qualification covers attribute-only, color-only, combined, specific-exit, unsafe, empty, reset-only, padding, determinism, side-effect, same-session, foreign-session, stale-epoch, and cancellation cases. Fresh package consumers validate API/XML/package behavior on all target frameworks. Published `Icod.DCurses 1.6.0` compatibility and a separate TermInfo-free future-renderer package consumer are also exercised on all three frameworks. + +The future-renderer witness proves that the candidate Terminal package supplies the required boundary. It does not claim that DCurses 2.0 has shipped; the retained DCurses T2001 package witness resumes after this package is published. + +Tagging, GitHub Release creation, and NuGet publication remain explicit maintainer actions after candidate acceptance. + +See [`Architecture.md`](../Architecture.md), [`Compatibility-and-Versioning.md`](../Compatibility-and-Versioning.md), [`Security-and-Privacy.md`](../Security-and-Privacy.md), [`Public-API-Baseline-1.18.md`](../Public-API-Baseline-1.18.md), and [`Icod.Terminal-1.18.0-Development-Roadmap.md`](../../Icod.Terminal-1.18.0-Development-Roadmap.md) for the permanent contract and development evidence. From 5556434746a2208886b6aa7c0cf27a7c2fbf866e Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:43:00 -0400 Subject: [PATCH 20/23] fix: update Terminal 1.18 package release notes --- Icod.Terminal.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Icod.Terminal.csproj b/Icod.Terminal.csproj index 5fa4ec28e..cc9d1074b 100644 --- a/Icod.Terminal.csproj +++ b/Icod.Terminal.csproj @@ -39,7 +39,7 @@ Icod.Terminal Timothy J. Bruce Managed, cross-platform live-terminal session, endpoint, mode, input, lifecycle, and terminal-control foundation for .NET. - Icod.Terminal 1.17.1 corrects the packaged README and release metadata for the published 1.17 line without changing the runtime or public API. Release notes: https://github.com/uniblab/Icod.Terminal/blob/main/docs/releases/1.17.1.md. Compatibility policy: https://github.com/uniblab/Icod.Terminal/blob/main/docs/Compatibility-and-Versioning.md. + Icod.Terminal 1.18.0 adds safe unknown-rendition baseline planning for Terminal-only screen consumers. Release notes: https://github.com/uniblab/Icod.Terminal/blob/main/docs/releases/1.18.0.md. Compatibility policy: https://github.com/uniblab/Icod.Terminal/blob/main/docs/Compatibility-and-Versioning.md. README.md icon.png https://github.com/uniblab/Icod.Terminal From 56bbc011325e5c88e67f243a9b882b97bae9aac7 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:46:34 -0400 Subject: [PATCH 21/23] fix: finalize Terminal 1.18 package README --- README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6bd9a334c..5fa734326 100644 --- a/README.md +++ b/README.md @@ -9,17 +9,15 @@ ## Status -Current published stable release: `Icod.Terminal 1.17.1`. +Current stable release: `Icod.Terminal 1.18.0`. -Version `1.17.1` corrects the packaged README and release metadata for the 1.17 line. It makes no runtime or public-API change from `1.17.0`. - -This source prepares the stable `1.18.0` candidate. Version 1.18 adds `TerminalScreenPlanner.PlanRenditionBaseline()`, allowing a Terminal-only renderer to establish the normalized default rendition safely when the physical starting state is unknown. The operation returns no plan when any profile-exposed rendition axis cannot be restored unconditionally. +Version 1.18 adds `TerminalScreenPlanner.PlanRenditionBaseline()`, allowing a Terminal-only renderer to establish the normalized default rendition safely when the physical starting state is unknown. The operation returns no plan when any profile-exposed rendition axis cannot be restored unconditionally. Version 1.17 adds Terminal-owned dimensions, an immutable semantic terminal profile, side-effect-free screen-operation planning, and bounded session-bound output transactions. These contracts provide the Terminal-side boundary required for a later `Icod.DCurses 2.0` release to remove its direct `Icod.TermInfo` dependency. The stable `1.0.0` compatibility floor remains unchanged. Version 1.18 retains the complete 1.17 screen-planning/transaction surface, 1.16 animation, 1.15 virtual-placeholder, and every earlier stable 1.x contract. The 1.18 public API fingerprint is `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`. -See the [1.18.0 release notes](docs/releases/1.18.0.md) and [changelog](CHANGELOG.md) for release-specific details. Until 1.18.0 is published, installation guidance below continues to name the published 1.17.1 package. +See the [1.18.0 release notes](docs/releases/1.18.0.md) and [changelog](CHANGELOG.md) for release-specific details. ## Support the Project @@ -69,7 +67,7 @@ See [`docs/Architecture.md`](docs/Architecture.md) for the permanent architectur Install the currently published package: ```text -dotnet add package Icod.Terminal --version 1.17.1 +dotnet add package Icod.Terminal --version 1.18.0 ``` Open a managed terminal session, write application text, and read through the authoritative event path: From fa9a34c20a54ba708b7e1a6f1c2762b37fcb2086 Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 14:56:08 -0400 Subject: [PATCH 22/23] docs: record Terminal 1.18 qualification --- Icod.Terminal-1.18.0-Development-Roadmap.md | 18 ++++++++++++++++-- Icod.Terminal-Development-Roadmap.md | 6 +++--- docs/releases/1.18.0.md | 4 ++++ .../2026-09-18-1.18.0-rendition-baseline.md | 1 + 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Icod.Terminal-1.18.0-Development-Roadmap.md b/Icod.Terminal-1.18.0-Development-Roadmap.md index a6848ac9b..ebc448e4e 100644 --- a/Icod.Terminal-1.18.0-Development-Roadmap.md +++ b/Icod.Terminal-1.18.0-Development-Roadmap.md @@ -2,7 +2,7 @@ **Release:** `1.18.0` **Theme:** Unknown-rendition baseline recovery for Terminal-only screen consumers -**Status:** T180-T184 accepted; stable `1.18.0` candidate closure in progress +**Status:** T180-T185 accepted; stable `1.18.0` candidate qualified **Candidate identity:** `1.18.0` **Stable compatibility floor:** `1.0.0` **Current published line:** `1.17.0`; `1.17.1` is the documentation-only patch baseline @@ -31,16 +31,30 @@ Icod.DCurses -> Icod.Terminal -> Icod.TermInfo ## Accepted implementation record -T180-T184 are complete on PR #63. The implementation adds only `PlanRenditionBaseline()` to the public surface and retains production `Icod.TermInfo 1.15.0` / `Icod.Timing 1.0.0`. +T180-T185 are complete on PR #63. The implementation adds only `PlanRenditionBaseline()` to the public surface and retains production `Icod.TermInfo 1.15.0` / `Icod.Timing 1.0.0`. - T180 recorded the `CS1061` package/source RED witness before the method existed. - T181 implemented unknown-state restoration with all-or-nothing availability. - T182 qualified attribute-only, color-only, combined, specific-exit, unsafe, empty, reset-only, padding, deterministic, and side-effect-free behavior. - T183 qualified same-session ordering, foreign-session rejection, stale output epochs, and pre-commit cancellation. - T184 froze identical API snapshots at `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`, verified the deliberate mismatch rejection, and passed the candidate package, XML, dependency, published DCurses 1.6.0, and TermInfo-free future-renderer consumers across all three target frameworks. +- T185 finalized stable package identity and release-facing metadata, then qualified the stable candidate at commit `56bbc011325e5c88e67f243a9b882b97bae9aac7` in workflow run `35382158657`. The retained DCurses PR #32 package witness remains governed by its published-package resume criterion. It resumes after `Icod.Terminal 1.18.0` is published; no unpublished project reference or raw TermInfo workaround is introduced. +## Stable-candidate qualification + +Workflow run [`35382158657`](https://github.com/uniblab/Icod.Terminal/actions/runs/35382158657) passed all nine jobs at source head `56bbc011325e5c88e67f243a9b882b97bae9aac7`: Windows, Linux, macOS, package candidate, Foundation, Presentation, Semantic and hardening, Stable 1.x release line, and validated package artifact. + +- Unit tests: 2,383 passed, zero failed, on each of `net8.0`, `net9.0`, and `net10.0`. +- TermInfo integration tests: 15 passed, zero failed, on each target framework. +- Stable package: `Icod.Terminal.1.18.0.nupkg`, SHA-256 `e8f2b374fd0865aa151910686197096b344f7daf8d0ef72331520d944b322932`. +- Symbols package: `Icod.Terminal.1.18.0.snupkg`, SHA-256 `8a14d163f2ea0bf030bea0416e0a84a919a2e8c83c692956b3a2a98470d78422`. +- Candidate artifact: ID `10563191796`, uploaded ZIP SHA-256 `b0fc9e57dcd0ccf42befc7d8d6fe91e9b0e19b62691fbfb775164252147fa028`. +- Validated artifact: ID `10562732859`, uploaded ZIP SHA-256 `1ddf8f76fbb6131271dd9123d5cab8043ddc68fd0084dfc1d1e84e8916121537`. +- Public API: identical on all target frameworks, SHA-256 `48975f2c42f6c544e9c574a9b3d79f7e2b7b3ecb10ab1a5a0b7067749e38e65d`. +- Package consumers: stable 1.x smoke, published `Icod.DCurses 1.6.0` soak, and the TermInfo-free future-renderer screen-contract witness passed on all target frameworks. + ## Public contract Add one method to the existing session-bound planner: diff --git a/Icod.Terminal-Development-Roadmap.md b/Icod.Terminal-Development-Roadmap.md index 97621d384..1f6b5585e 100644 --- a/Icod.Terminal-Development-Roadmap.md +++ b/Icod.Terminal-Development-Roadmap.md @@ -6,7 +6,7 @@ - **Target frameworks:** `net8.0`; `net9.0`; `net10.0` - **Current published feature line:** `1.17.0` — Terminal-owned Screen Output Contracts - **Current patch line:** `1.17.1` — Packaged README and release metadata correction -- **Development status:** 1.17.1 published; 1.18.0 stable candidate in exact-head qualification +- **Development status:** 1.17.1 published; 1.18.0 stable candidate qualified on PR #63 - **Active development target:** `1.18.0` — Unknown-rendition baseline recovery for Terminal-only screen consumers - **Stable compatibility floor:** `1.0.0` @@ -24,7 +24,7 @@ The published feature line uses production `Icod.TermInfo 1.15.0` and optional t Version 1.17.1 is a documentation-only patch that corrects the README embedded in 1.17.0 and synchronizes release metadata. It makes no runtime or public-API change. -Version 1.18.0 is now a stable source candidate on PR #63. T180-T184 are accepted: the additive unknown-rendition baseline API, hardening/ownership tests, identical three-framework API fingerprint, package/XML gates, published DCurses 1.6.0 soak, and TermInfo-free future-renderer package witness are complete. T185 exact-head stable qualification remains before merge or publication. +Version 1.18.0 is a qualified stable source candidate on PR #63. T180-T185 are accepted: the additive unknown-rendition baseline API, hardening/ownership tests, identical three-framework API fingerprint, package/XML gates, published DCurses 1.6.0 soak, TermInfo-free future-renderer package witness, and nine-job stable-candidate workflow are complete. Merge, tag, release creation, and publication remain maintainer actions. ## Current architecture @@ -232,7 +232,7 @@ public TerminalScreenOperationPlan? PlanRenditionBaseline(); The plan represents unknown physical rendition state, restores attributes before original colors in a safe deterministic order, retains Terminal-owned expansion/padding/cost and same-session transaction ownership, and returns a valid zero-byte plan only when the selected profile exposes no enterable attribute and no selectable color axis. Existing known-state reset and transition behavior remains unchanged. -T180-T184 are accepted. T185 prepares the stable package/docs metadata and records one final exact-head Windows/Linux/macOS, package, API/XML, downstream, and artifact qualification. Publication remains a separate maintainer action. +T180-T185 are accepted. The stable candidate at `56bbc011325e5c88e67f243a9b882b97bae9aac7` passed the complete Windows/Linux/macOS, package, API/XML, downstream, and artifact matrix in workflow run `35382158657`. Publication remains a separate maintainer action. The tranche sequence is: diff --git a/docs/releases/1.18.0.md b/docs/releases/1.18.0.md index 447907a89..1058aadc9 100644 --- a/docs/releases/1.18.0.md +++ b/docs/releases/1.18.0.md @@ -35,6 +35,10 @@ The public API snapshots are identical across `net8.0`, `net9.0`, and `net10.0`. Qualification covers attribute-only, color-only, combined, specific-exit, unsafe, empty, reset-only, padding, determinism, side-effect, same-session, foreign-session, stale-epoch, and cancellation cases. Fresh package consumers validate API/XML/package behavior on all target frameworks. Published `Icod.DCurses 1.6.0` compatibility and a separate TermInfo-free future-renderer package consumer are also exercised on all three frameworks. +The stable candidate at source commit `56bbc011325e5c88e67f243a9b882b97bae9aac7` passed all nine jobs in [workflow run 35382158657](https://github.com/uniblab/Icod.Terminal/actions/runs/35382158657). Each target framework passed 2,383 unit tests and 15 TermInfo integration tests with zero failures. The validated `Icod.Terminal.1.18.0.nupkg` SHA-256 is `e8f2b374fd0865aa151910686197096b344f7daf8d0ef72331520d944b322932`; the `.snupkg` SHA-256 is `8a14d163f2ea0bf030bea0416e0a84a919a2e8c83c692956b3a2a98470d78422`. + +The candidate artifact is ID `10563191796` with uploaded ZIP SHA-256 `b0fc9e57dcd0ccf42befc7d8d6fe91e9b0e19b62691fbfb775164252147fa028`. The validated artifact is ID `10562732859` with uploaded ZIP SHA-256 `1ddf8f76fbb6131271dd9123d5cab8043ddc68fd0084dfc1d1e84e8916121537`. + The future-renderer witness proves that the candidate Terminal package supplies the required boundary. It does not claim that DCurses 2.0 has shipped; the retained DCurses T2001 package witness resumes after this package is published. Tagging, GitHub Release creation, and NuGet publication remain explicit maintainer actions after candidate acceptance. diff --git a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md index 2895d6e44..be0c88569 100644 --- a/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md +++ b/docs/superpowers/plans/2026-09-18-1.18.0-rendition-baseline.md @@ -128,6 +128,7 @@ **Files:** - Modify: `Directory.Build.props` +- Modify: `Icod.Terminal.csproj` - Modify: `README.md` - Modify: `CHANGELOG.md` - Modify: `Icod.Terminal-Development-Roadmap.md` From 8fc4a0a194f365285615b9fb475eca60fca7e30a Mon Sep 17 00:00:00 2001 From: Timothy Bruce Date: Fri, 18 Sep 2026 15:05:28 -0400 Subject: [PATCH 23/23] docs: align Terminal 1.18 authority labels --- docs/Architecture.md | 6 +++--- docs/Compatibility-and-Versioning.md | 4 ++-- docs/Security-and-Privacy.md | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/Architecture.md b/docs/Architecture.md index 4a59d5f19..736dc5aae 100644 --- a/docs/Architecture.md +++ b/docs/Architecture.md @@ -353,7 +353,7 @@ Persistent source cropping and virtual-placeholder rendering operate on already- ## 14. Optional TermInfo 1.14 backend planning boundary -The active 1.17 repository uses `Icod.TermInfo 1.15.0`. Optional integration tests and the `Icod.Terminal.TermInfoPersistentRaster.Sample` use `Icod.TermInfo.Inspection 1.15.0`. +The active 1.18 repository uses `Icod.TermInfo 1.15.0`. Optional integration tests and the `Icod.Terminal.TermInfoPersistentRaster.Sample` use `Icod.TermInfo.Inspection 1.15.0`. Inspection 1.14 adds advisory Sixel/Kitty backend availability evidence, candidate evaluation, and explicit backend-selection planning. That planner remains a **consumer/application policy layer**; it is not invoked by `Icod.Terminal` production routing. @@ -379,7 +379,7 @@ A conclusive live `PersistentRasterGraphics` result may be mapped by the caller TermInfo planning does not replace Terminal's live capability checks, routing, commitment, identity ownership, or cleanup. Production `Icod.Terminal` retains no dependency on `Icod.TermInfo.Inspection` or `Icod.TermInfo.Source`. -## 15. Stable exclusions after 1.17 +## 15. Stable exclusions after 1.18 Stable 1.x still does not treat the following as ordinary `Icod.Terminal` responsibilities: @@ -403,7 +403,7 @@ Relative placement, lifecycle observation, virtual placeholders, and resource-ow ## 16. Dependency boundary -`Icod.Terminal.csproj` is the direct NuGet dependency authority. The active 1.17 production graph is: +`Icod.Terminal.csproj` is the direct NuGet dependency authority. The active 1.18 production graph is: ```text Icod.TermInfo 1.15.0 diff --git a/docs/Compatibility-and-Versioning.md b/docs/Compatibility-and-Versioning.md index d85f779f8..17e135ec1 100644 --- a/docs/Compatibility-and-Versioning.md +++ b/docs/Compatibility-and-Versioning.md @@ -181,7 +181,7 @@ Endpoint availability remains separate from support knowledge. Static advertisem ## 8. TermInfo 1.15 optional integration compatibility -The active 1.17 direct production dependency graph remains: +The active 1.18 direct production dependency graph remains: ```text Icod.TermInfo 1.15.0 @@ -230,7 +230,7 @@ The following remain implementation details rather than compatibility promises: The optional `Icod.TermInfo.Inspection` backend vocabulary is a separate consumer planning API. Its presence in a sample/test does not expose a caller-selected raw backend switch in `Icod.Terminal` production API. -## 11. Deliberate non-promises after 1.17 +## 11. Deliberate non-promises after 1.18 Stable 1.x does not promise: diff --git a/docs/Security-and-Privacy.md b/docs/Security-and-Privacy.md index 9b4954014..10e1c36f8 100644 --- a/docs/Security-and-Privacy.md +++ b/docs/Security-and-Privacy.md @@ -191,7 +191,7 @@ The stable semantic surface intentionally does not expose: ## 14. TermInfo 1.14 advisory backend planning -The active 1.17 repository directly depends on `Icod.TermInfo 1.15.0`. Optional integration tests and samples use `Icod.TermInfo.Inspection 1.15.0`. +The active 1.18 repository directly depends on `Icod.TermInfo 1.15.0`. Optional integration tests and samples use `Icod.TermInfo.Inspection 1.15.0`. Inspection's `RasterBackendPlanner` classifies advisory Sixel/Kitty availability and applies explicit caller preference. This is **not** production Terminal routing and is not a security/authentication oracle. @@ -228,7 +228,7 @@ TermInfo backend planning does not justify extra probes by itself; runtime verif ## 18. Dependency boundary -The active 1.17 production package graph is: +The active 1.18 production package graph is: ```text Icod.TermInfo 1.15.0